001    /**
002     * Copyright (C) 2010 The Roslin Institute <contact andy.law@roslin.ed.ac.uk>
003     *
004     * This file is part of the Ensembl Java API demonstration project developed by the
005     * Bioinformatics Group at The Roslin Institute, The Royal (Dick) School of
006     * Veterinary Studies, University of Edinburgh.
007     *
008     * This is free software: you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (version 3) as published by
010     * the Free Software Foundation.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015     * GNU General Public License for more details.
016     *
017     * You should have received a copy of the GNU General Public License
018     * in this software distribution. If not, see <http://www.gnu.org/licenses/gpl-3.0.html/>.
019     */
020    
021    package uk.ac.roslin.ensembl.model;
022    
023    import java.io.Serializable;
024    import java.util.Comparator;
025    import uk.ac.roslin.ensembl.model.core.CoordinateSystem;
026    
027    /**
028     * Used to be abstract
029     * @author paterson
030     */
031    public class Mapping implements Serializable {
032    
033        protected MappableObject source=null;
034        protected MappableObject target=null;
035        protected Coordinate sourceCoordinates=null;
036        protected Coordinate targetCoordinates=null;
037        protected CoordinateSystem sourceCoordSystem=null;
038        protected CoordinateSystem targetCoordSystem=null;
039        protected Mapping reverseMapping=null;
040    
041        public Mapping() {
042        }
043    
044        public static boolean addReverseMapping(Mapping originalMapping) {
045            try {
046                //don't add the reverse mapping if we already have it
047    //            for (Mapping mapping : ((MappingSet) originalMapping.getTarget().getMappings())) {
048    //                if (mapping.getTarget().getId().equals(originalMapping.getSource().getId())) {
049    //                    return;
050    //                }
051    //            }
052    
053                Mapping reverseMapping = new Mapping();
054                reverseMapping.setReverseMapping(originalMapping);
055    
056                reverseMapping.setSource(originalMapping.getTarget());
057                reverseMapping.setTarget(originalMapping.getSource());
058    
059                reverseMapping.setSourceCoordinates(originalMapping.getTargetCoordinates());
060                reverseMapping.setTargetCoordinates(originalMapping.getSourceCoordinates());
061    
062                reverseMapping.getSource().addMapping(reverseMapping);
063                originalMapping.setReverseMapping(reverseMapping);
064    
065            } catch (Exception e) {
066                return false;
067            }
068            return true;
069        }
070    
071        public void setSource(MappableObject source) {
072            this.source = source;
073        }
074    
075        public void setTarget(MappableObject target) {
076            this.target = target;
077        }
078    
079        public MappableObject getSource() {
080            return source;
081        }
082    
083        public MappableObject getTarget() {
084            return target;
085        }
086    
087        public ObjectType getTargetType() {
088            return (this.getTarget()!=null) ? this.getTarget().getType() : null;
089            }
090    
091        public ObjectType getSourceType() {
092            return (this.getSource()!=null) ? this.getSource().getType() : null;
093            }
094    
095        public String getTargetHashID() {
096            //make it an empty string if there is no target set
097            return (this.getTarget()!=null) ? this.getTarget().getHashID() : "";
098        }
099    
100        public String getSourceHashID() {
101            //make it an empty string if there is no source set
102            return (this.getSource()!=null) ? this.getSource().getHashID() : "";
103        }
104    
105        public Coordinate getSourceCoordinates() {
106            return this.sourceCoordinates;
107        }
108    
109        public Coordinate getTargetCoordinates() {
110            return this.targetCoordinates;
111        }
112    
113        public void setSourceCoordinates(Coordinate coord) {
114            this.sourceCoordinates = coord;
115        }
116    
117        public void setTargetCoordinates(Coordinate coord) {
118            this.targetCoordinates = coord;
119        }
120    
121        public void setSourceCoordinates(Integer start, Integer end, Coordinate.Strand strand) {
122            this.sourceCoordinates = new Coordinate(start, end, strand);
123        }
124        public void setSourceCoordinates(Integer start, Integer end, Integer strand) {
125            this.sourceCoordinates = new Coordinate(start, end, strand);
126        }
127    
128        public void setTargetCoordinates(Integer start, Integer end, Coordinate.Strand strand) {
129            this.targetCoordinates = new Coordinate(start, end, strand);
130        }
131        public void setTargetCoordinates(Integer start, Integer end, Integer strand) {
132            this.targetCoordinates = new Coordinate(start, end, strand);
133        }
134    
135        @Override
136        public boolean equals(Object mapping) {
137    
138            if (this == mapping ) {
139                return true;
140            }
141    
142            //this checks for null too..
143            if ( ! (mapping instanceof Mapping )) {
144                return false;
145            }
146    
147            Mapping m = (Mapping) mapping;
148    
149            boolean out = true;
150    
151            //this checks for identity if source and target are set on both mappings
152            //and each pair has the same hashcode
153            //the hashcode method will be based on objectID, objectType and probably SpeciesID
154    
155            if ( !( !this.getSourceHashID().equals("")
156                    &&  !m.getSourceHashID().equals("")
157                    && !this.getTargetHashID().equals("")
158                    &&  !m.getTargetHashID().equals("")
159                    && this.getSourceHashID().equals(m.getSourceHashID())
160                    && this.getTargetHashID().equals(m.getTargetHashID())
161                    ) ) {
162                return false;
163            }
164    
165            return out;
166    
167        }
168    
169        @Override
170        public int hashCode() {
171    
172            return this.getSourceHashID().hashCode()+this.getTargetHashID().hashCode();
173    
174        }
175    
176        public Mapping getReverseMapping() {
177            if (reverseMapping==null) {
178                Mapping.addReverseMapping(this);
179            }
180            return reverseMapping;
181        }
182    
183        protected void setReverseMapping(Mapping reverseMapping) {
184            this.reverseMapping = reverseMapping;
185        }
186    
187        public static final MappingOnSourceComparator mappingOnSourceComparator
188                = new MappingOnSourceComparator();
189    
190        public static final MappingOnTargetComparator mappingOnTargetComparator
191                = new MappingOnTargetComparator();
192    
193    }
194    
195        /**
196         * Comparator Class for Mappings, based primarily on their source coordinates.
197         * This will sort a set of Mappings on the order of the source coordinates
198         * or failing that on the ids on the target object, then source object. Beware
199         * that this comparison doesn't match the equals logic.
200         * @author tpaterso
201         */
202         class MappingOnSourceComparator implements Comparator<Mapping>, Serializable {
203            public int compare(Mapping o1, Mapping o2) {
204                // this would only sort mappings with non identical source coordinates
205                // however we want to sort multiple things with the same source coordinates if they are different things!
206                //return o1.getSourceCoordinates().compareTo(o2.getSourceCoordinates());
207    
208                if (o1==null) {
209                    if (o2 == null) {
210                        return 0;
211                    } else {
212                        return -1;
213                    }
214                } else if (o2==null) {
215                    return +1;
216                }
217    
218                int out;
219    
220                if (o1.getSourceCoordinates()==null) {
221                    if (o2.getSourceCoordinates()== null) {
222                        out=0;
223                    } else {
224                        return -1;
225                    }
226                } else if (o2.getSourceCoordinates()== null) {
227                    return +1;
228                }  else {
229                    out = o1.getSourceCoordinates().compareTo(o2.getSourceCoordinates());
230                }
231    
232                if (out != 0) {
233                    return out;
234                } else {
235    
236                    //if both pairs of hashIDs are same this could break the contract of equals
237                    //for the set - however it could happen cos the mappings are mutable
238                    if (o1.getTargetHashID().equals(o2.getTargetHashID()) &&
239                            o1.getSourceHashID().equals(o2.getSourceHashID())  ) {
240                        //it looks like the mappings really are for the same things!
241                        //so return this
242                        return 0;
243                    } else {
244    
245                        //not the same
246                        out = -1;
247    
248                        try {
249                            //try to order them by the Target's ID number
250                            out = o1.getTargetHashID().compareTo(o2.getTargetHashID());
251                            // but if they have the same ID ignore this
252                            if (out == 0) {
253                                out = o1.getSourceHashID().compareTo(o2.getSourceHashID());
254                            }
255                            // but if these also have the same ID ignore this and return a random ordering
256                            if (out == 0) {
257                                out = -1;
258                            }
259                        } catch (Exception e) {
260                        }
261                        return out;
262                    }
263                }
264            }
265        }
266    
267        /**
268         * Comparator Class for Mappings, based primarily on their target coordinates.
269         * This will sort a set of Mappings on the order of the target coordinates
270         * or failing that on the ids on the source object, then target object. Beware
271         * that this comparison doesn't match the equals logic.
272         * @author tpaterso
273         */
274         class MappingOnTargetComparator implements Comparator<Mapping>, Serializable {
275            public int compare(Mapping o1, Mapping o2) {
276                // this would only sort mappings with non identical source coordinates
277                // however we want to sort multiple things with the same source coordinates if they are different things!
278                //return o1.getSourceCoordinates().compareTo(o2.getSourceCoordinates());
279    
280                if (o1==null) {
281                    if (o2 == null) {
282                        return 0;
283                    } else {
284                        return -1;
285                    }
286                } else if (o2==null) {
287                    return +1;
288                }
289    
290                int out;
291    
292                if (o1.getTargetCoordinates()==null) {
293                    if (o2.getTargetCoordinates()== null) {
294                        out=0;
295                    } else {
296                        return -1;
297                    }
298                } else if (o2.getTargetCoordinates()== null) {
299                    return +1;
300                }  else {
301                    out = o1.getTargetCoordinates().compareTo(o2.getTargetCoordinates());
302                }
303    
304                if (out != 0) {
305                    return out;
306                } else {
307    
308                    //if both pairs of hashIDs are same this could break the contract of equals
309                    //for the set - however it could happen cos the mappings are mutable
310                    if (o1.getTargetHashID().equals(o2.getTargetHashID()) &&
311                            o1.getSourceHashID().equals(o2.getSourceHashID())  ) {
312                        //it looks like the mappings really are for the same things!
313                        //so return this
314                        return 0;
315                    } else {
316    
317                        //not the same
318                        out = -1;
319    
320                        try {
321                            //try to order them by the Target's ID number
322                            out = o1.getSourceHashID().compareTo(o2.getSourceHashID());
323                            // but if they have the same ID ignore this
324                            if (out == 0) {
325                                out = o1.getTargetHashID().compareTo(o2.getTargetHashID());
326                            }
327                            // but if these also have the same ID ignore this and return a random ordering
328                            if (out == 0) {
329                                out = -1;
330                            }
331                        } catch (Exception e) {
332                        }
333                        return out;
334                    }
335                }
336            }
337        }
338    
339