001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Created on 01-21-2010
021 */
022
023package org.biojava.nbio.core.sequence.features;
024
025import org.biojava.nbio.core.sequence.location.template.AbstractLocation;
026import org.biojava.nbio.core.sequence.template.AbstractSequence;
027import org.biojava.nbio.core.sequence.template.Compound;
028
029import java.util.ArrayList;
030import java.util.Comparator;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034
035/**
036 * A feature is currently any descriptive item that can be associated with a sequence position(s)
037 * A feature has a type and a source which is currently a string to allow flexibility for the user
038 * Ideally well defined features should have a class to describe attributes of that feature
039 * @author Scooter Willis <willishf at gmail dot com>
040 */
041public abstract class AbstractFeature<S extends AbstractSequence<C>, C extends Compound>
042                implements FeatureInterface<S, C> {
043        List<FeatureInterface<S, C>> childrenFeatures = new ArrayList<FeatureInterface<S, C>>();
044        FeatureInterface<S, C> parentFeature;
045        AbstractLocation sequenceLocation;
046        String type = "";
047        String source = "";
048        private String description = "";
049        private String shortDescription = "";
050        private Object userObject = null;
051        private Map<String, List<Qualifier>> Qualifiers = new HashMap<String, List<Qualifier>>();
052
053        /**
054         * A feature has a type and a source
055         * @param type
056         * @param source
057         */
058        public AbstractFeature(String type,String source){
059                this.type = type;
060                this.source = source;
061        }
062
063        /**
064         * A feature could be a single sequence position like a mutation or a post translational modification of an amino acid.
065         * It could also be the docking interface of N number of amino acids on the surface. The location wold then be a collection
066         * of sequence positions instead of a single sequence position or the begin and end of a sequence segment.
067         * @return
068         */
069
070        @Override
071        public AbstractLocation getLocations() {
072                return sequenceLocation;
073        }
074
075        /**
076         *  A feature could be a single sequence position like a mutation or a post translational modification of an amino acid.
077         * It could also be the docking interface of N number of amino acids on the surface. The location wold then be a collection
078         * of sequence positions instead of a single sequence position or the begin and end of a sequence segment.
079         * @param loc
080         */
081        @Override
082        public void setLocation(AbstractLocation loc) {
083                sequenceLocation = loc;
084        }
085
086        /**
087         * The feature type
088         * @return
089         */
090        @Override
091        public String getType() {
092                return type;
093        }
094
095        /**
096         * Set the feature type
097         * @param type
098         */
099        @Override
100        public void setType(String type) {
101                this.type = type;
102        }
103
104        /**
105         * The feature source
106         * @return
107         */
108
109        @Override
110        public String getSource() {
111                return source;
112        }
113
114        /**
115         * Set the feature source
116         * @param source
117         */
118        @Override
119        public void setSource(String source) {
120                this.source = source;
121        }
122
123        /**
124         * A feature can be the child or contained by a parent feature. An example is a Helix feature could contain
125         * children features. A PFAM domain could contain secondary structures.
126         * @param feature
127         */
128        @Override
129        public void setParentFeature(FeatureInterface<S, C> feature) {
130                parentFeature = feature;
131        }
132
133        /**
134         * Get the parent Feature
135         * @return
136         */
137        @Override
138        public FeatureInterface<S, C> getParentFeature() {
139           return parentFeature;
140        }
141
142        /**
143         * Get the children features
144         * @return
145         */
146        @Override
147        public List<FeatureInterface<S, C>> getChildrenFeatures() {
148                return childrenFeatures;
149        }
150
151        /**
152         * Set the children features
153         * @param features
154         */
155        @Override
156        public void setChildrenFeatures(List<FeatureInterface<S, C>> features) {
157                childrenFeatures = features;
158
159        }
160
161        /**
162         * @return the description
163         */
164        @Override
165        public String getDescription() {
166                return description;
167        }
168
169        /**
170         * @param description the description to set
171         */
172        @Override
173        public void setDescription(String description) {
174                this.description = description;
175        }
176
177        /**
178         * @return the shortDescription
179         */
180        @Override
181        public String getShortDescription() {
182                return shortDescription;
183        }
184
185        /**
186         * @param shortDescription the shortDescription to set
187         */
188        @Override
189        public void setShortDescription(String shortDescription) {
190                this.shortDescription = shortDescription;
191        }
192
193        /**
194         * Sort features by start position and then longest length. When features are added
195         * having them sorted by start position and then longest length helps on the layout
196         * of overlapping features so they are delivered in a proper order.
197         */
198
199        public static final Comparator<FeatureInterface<?, ?>> LOCATION_LENGTH = new Comparator<FeatureInterface<?, ?>>() {
200
201                @Override
202                public int compare(FeatureInterface<?, ?> e1, FeatureInterface<?, ?> e2) {
203                        double v1 = e1.getLocations().getStart().getPosition();
204                        double v2 = e2.getLocations().getStart().getPosition();
205                        if (v1 < v2) {
206                                return -1;
207                        } else if (v1 > v2) {
208                                return 1;
209                        } else {
210                                double end1 = e1.getLocations().getEnd().getPosition();
211                                double end2 = e2.getLocations().getEnd().getPosition();
212                                if(end1 > end2)
213                                        return -1;
214                                else if(end1 < end2)
215                                        return 1;
216                                else
217                                return 0;
218                        }
219
220                }
221        };
222
223         /**
224         * Sort features by length. //TODO need to handle cases where features have multiple locations, strand etc
225         *
226         */
227
228        static public final Comparator<FeatureInterface<?, ?>> LENGTH = new Comparator<FeatureInterface<?, ?>>() {
229
230                @Override
231                public int compare(FeatureInterface<?, ?> e1, FeatureInterface<?, ?> e2) {
232                        double v1 = Math.abs(e1.getLocations().getEnd().getPosition()- e1.getLocations().getStart().getPosition());
233                        double v2 = Math.abs(e2.getLocations().getEnd().getPosition() -  e2.getLocations().getStart().getPosition());
234                        if (v1 < v2) {
235                                return -1;
236                        } else if (v1 > v2) {
237                                return 1;
238                        } else {
239                                return 0;
240                        }
241
242                }
243        };
244
245        /**
246         * Sort features by type
247         */
248        public static final Comparator<FeatureInterface<?, ?>> TYPE = new Comparator<FeatureInterface<?, ?>>() {
249
250                @Override
251                public int compare(FeatureInterface<?, ?> o1, FeatureInterface<?, ?> o2) {
252                        return o1.getType().compareTo(o2.getType());
253                }
254        };
255
256        /**
257         * @return the userObject
258         */
259        @Override
260        public Object getUserObject() {
261                return userObject;
262        }
263
264        /**
265         * Allow the user to associate an object with the feature. This way if a feature which is displayed in a GUI
266         * is clicked on the application can then get a user defined object associated with the feature.
267         * @param userObject the userObject to set
268         */
269        @Override
270        public void setUserObject(Object userObject) {
271                this.userObject = userObject;
272        }
273
274        @Override
275        public Map<String, List<Qualifier>> getQualifiers() {
276                // TODO Auto-generated method stub
277                return Qualifiers;
278        }
279
280        @Override
281        public void setQualifiers(Map<String, List<Qualifier>> qualifiers) {
282                // TODO Auto-generated method stub
283                Qualifiers = qualifiers;
284
285        }
286
287        @Override
288        public void addQualifier(String key, Qualifier qualifier) {
289                // Check for key. Update list of values
290                if (Qualifiers.containsKey(key)){
291                        List<Qualifier> vals = Qualifiers.get(key);
292                        vals.add(qualifier);
293                        Qualifiers.put(key, vals);
294                } else {
295                        List<Qualifier> vals = new ArrayList<Qualifier>();
296                        vals.add(qualifier);
297                        Qualifiers.put(key, vals);
298                }
299
300        }
301
302}