2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.datamodel;
23 import jalview.datamodel.features.FeatureLocationI;
25 import java.util.HashMap;
27 import java.util.Map.Entry;
28 import java.util.Vector;
36 public class SequenceFeature implements FeatureLocationI
39 * score value if none is set; preferably Float.Nan, but see
40 * JAL-2060 and JAL-2554 for a couple of blockers to that
42 private static final float NO_SCORE = 0f;
44 private static final String STATUS = "status";
46 private static final String STRAND = "STRAND";
48 // private key for Phase designed not to conflict with real GFF data
49 private static final String PHASE = "!Phase";
51 // private key for ENA location designed not to conflict with real GFF data
52 private static final String LOCATION = "!Location";
55 * ATTRIBUTES is reserved for the GFF 'column 9' data, formatted as
56 * name1=value1;name2=value2,value3;...etc
58 private static final String ATTRIBUTES = "ATTRIBUTES";
61 * type, begin, end, featureGroup, score and contactFeature are final
62 * to ensure that the integrity of SequenceFeatures data store
63 * can't be broken by direct update of these fields
65 public final String type;
67 public final int begin;
71 public final String featureGroup;
73 public final float score;
75 private final boolean contactFeature;
77 public String description;
80 * a map of key-value pairs; may be populated from GFF 'column 9' data,
81 * other data sources (e.g. GenBank file), or programmatically
83 public Map<String, Object> otherDetails;
85 public Vector<String> links;
88 * Constructs a duplicate feature. Note: Uses makes a shallow copy of the
89 * otherDetails map, so the new and original SequenceFeature may reference the
90 * same objects in the map.
94 public SequenceFeature(SequenceFeature cpy)
96 this(cpy, cpy.getBegin(), cpy.getEnd(), cpy.getFeatureGroup(), cpy
109 public SequenceFeature(String theType, String theDesc, int theBegin,
110 int theEnd, String group)
112 this(theType, theDesc, theBegin, theEnd, NO_SCORE, group);
116 * Constructor including a score value
125 public SequenceFeature(String theType, String theDesc, int theBegin,
126 int theEnd, float theScore, String group)
129 this.description = theDesc;
130 this.begin = theBegin;
132 this.featureGroup = group;
133 this.score = theScore;
136 * for now, only "Disulfide/disulphide bond" is treated as a contact feature
138 this.contactFeature = "disulfide bond".equalsIgnoreCase(type)
139 || "disulphide bond".equalsIgnoreCase(type);
143 * A copy constructor that allows the value of final fields to be 'modified'
152 public SequenceFeature(SequenceFeature sf, String newType, int newBegin,
153 int newEnd, String newGroup, float newScore)
155 this(newType, sf.getDescription(), newBegin, newEnd, newScore,
158 if (sf.otherDetails != null)
160 otherDetails = new HashMap<String, Object>();
161 for (Entry<String, Object> entry : sf.otherDetails.entrySet())
163 otherDetails.put(entry.getKey(), entry.getValue());
166 if (sf.links != null && sf.links.size() > 0)
168 links = new Vector<String>();
169 for (int i = 0, iSize = sf.links.size(); i < iSize; i++)
171 links.addElement(sf.links.elementAt(i));
177 * A copy constructor that allows the value of final fields to be 'modified'
185 public SequenceFeature(SequenceFeature sf, int newBegin, int newEnd,
186 String newGroup, float newScore)
188 this(sf, sf.getType(), newBegin, newEnd, newGroup, newScore);
192 * Two features are considered equal if they have the same type, group,
193 * description, start, end, phase, strand, and (if present) 'Name', ID' and
194 * 'Parent' attributes.
196 * Note we need to check Parent to distinguish the same exon occurring in
197 * different transcripts (in Ensembl GFF). This allows assembly of transcript
198 * sequences from their component exon regions.
201 public boolean equals(Object o)
203 return equals(o, false);
207 * Overloaded method allows the equality test to optionally ignore the
208 * 'Parent' attribute of a feature. This supports avoiding adding many
209 * superficially duplicate 'exon' or CDS features to genomic or protein
213 * @param ignoreParent
216 public boolean equals(Object o, boolean ignoreParent)
218 if (o == null || !(o instanceof SequenceFeature))
223 SequenceFeature sf = (SequenceFeature) o;
224 boolean sameScore = Float.isNaN(score) ? Float.isNaN(sf.score)
226 if (begin != sf.begin || end != sf.end || !sameScore)
231 if (getStrand() != sf.getStrand())
236 if (!(type + description + featureGroup + getPhase()).equals(
237 sf.type + sf.description + sf.featureGroup + sf.getPhase()))
241 if (!equalAttribute(getValue("ID"), sf.getValue("ID")))
245 if (!equalAttribute(getValue("Name"), sf.getValue("Name")))
251 if (!equalAttribute(getValue("Parent"), sf.getValue("Parent")))
260 * Returns true if both values are null, are both non-null and equal
266 protected static boolean equalAttribute(Object att1, Object att2)
268 if (att1 == null && att2 == null)
274 return att1.equals(att2);
276 return att2.equals(att1);
282 * @return DOCUMENT ME!
285 public int getBegin()
293 * @return DOCUMENT ME!
304 * @return DOCUMENT ME!
306 public String getType()
314 * @return DOCUMENT ME!
316 public String getDescription()
321 public void setDescription(String desc)
326 public String getFeatureGroup()
331 public void addLink(String labelLink)
335 links = new Vector<String>();
338 if (!links.contains(labelLink))
340 links.insertElementAt(labelLink, 0);
344 public float getScore()
350 * Used for getting values which are not in the basic set. eg STRAND, PHASE
356 public Object getValue(String key)
358 if (otherDetails == null)
364 return otherDetails.get(key);
369 * Returns a property value for the given key if known, else the specified
373 * @param defaultValue
376 public Object getValue(String key, Object defaultValue)
378 Object value = getValue(key);
379 return value == null ? defaultValue : value;
383 * Used for setting values which are not in the basic set. eg STRAND, FRAME
391 public void setValue(String key, Object value)
395 if (otherDetails == null)
397 otherDetails = new HashMap<String, Object>();
400 otherDetails.put(key, value);
405 * The following methods are added to maintain the castor Uniprot mapping file
408 public void setStatus(String status)
410 setValue(STATUS, status);
413 public String getStatus()
415 return (String) getValue(STATUS);
418 public void setAttributes(String attr)
420 setValue(ATTRIBUTES, attr);
423 public String getAttributes()
425 return (String) getValue(ATTRIBUTES);
429 * Return 1 for forward strand ('+' in GFF), -1 for reverse strand ('-' in
430 * GFF), and 0 for unknown or not (validly) specified
434 public int getStrand()
437 if (otherDetails != null)
439 Object str = otherDetails.get(STRAND);
444 else if ("+".equals(str))
453 * Set the value of strand
456 * should be "+" for forward, or "-" for reverse
458 public void setStrand(String strand)
460 setValue(STRAND, strand);
463 public void setPhase(String phase)
465 setValue(PHASE, phase);
468 public String getPhase()
470 return (String) getValue(PHASE);
474 * Sets the 'raw' ENA format location specifier e.g. join(12..45,89..121)
478 public void setEnaLocation(String loc)
480 setValue(LOCATION, loc);
484 * Gets the 'raw' ENA format location specifier e.g. join(12..45,89..121)
488 public String getEnaLocation()
490 return (String) getValue(LOCATION);
494 * Readable representation, for debug only, not guaranteed not to change
498 public String toString()
500 return String.format("%d %d %s %s", getBegin(), getEnd(), getType(),
505 * Overridden to ensure that whenever two objects are equal, they have the
509 public int hashCode()
511 String s = getType() + getDescription() + getFeatureGroup()
512 + getValue("ID") + getValue("Name") + getValue("Parent")
514 return s.hashCode() + getBegin() + getEnd() + (int) getScore()
519 * Answers true if the feature's start/end values represent two related
520 * positions, rather than ends of a range. Such features may be visualised or
521 * reported differently to features on a range.
524 public boolean isContactFeature()
526 return contactFeature;
530 * Answers true if the sequence has zero start and end position
534 public boolean isNonPositional()
536 return begin == 0 && end == 0;