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