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'
151 public SequenceFeature(SequenceFeature sf, int newBegin, int newEnd,
152 String newGroup, float newScore)
154 this(sf.getType(), sf.getDescription(), newBegin, newEnd, newScore,
157 if (sf.otherDetails != null)
159 otherDetails = new HashMap<String, Object>();
160 for (Entry<String, Object> entry : sf.otherDetails.entrySet())
162 otherDetails.put(entry.getKey(), entry.getValue());
165 if (sf.links != null && sf.links.size() > 0)
167 links = new Vector<String>();
168 for (int i = 0, iSize = sf.links.size(); i < iSize; i++)
170 links.addElement(sf.links.elementAt(i));
176 * Two features are considered equal if they have the same type, group,
177 * description, start, end, phase, strand, and (if present) 'Name', ID' and
178 * 'Parent' attributes.
180 * Note we need to check Parent to distinguish the same exon occurring in
181 * different transcripts (in Ensembl GFF). This allows assembly of transcript
182 * sequences from their component exon regions.
185 public boolean equals(Object o)
187 return equals(o, false);
191 * Overloaded method allows the equality test to optionally ignore the
192 * 'Parent' attribute of a feature. This supports avoiding adding many
193 * superficially duplicate 'exon' or CDS features to genomic or protein
197 * @param ignoreParent
200 public boolean equals(Object o, boolean ignoreParent)
202 if (o == null || !(o instanceof SequenceFeature))
207 SequenceFeature sf = (SequenceFeature) o;
208 boolean sameScore = Float.isNaN(score) ? Float.isNaN(sf.score)
210 if (begin != sf.begin || end != sf.end || !sameScore)
215 if (getStrand() != sf.getStrand())
220 if (!(type + description + featureGroup + getPhase()).equals(sf.type
221 + sf.description + sf.featureGroup + sf.getPhase()))
225 if (!equalAttribute(getValue("ID"), sf.getValue("ID")))
229 if (!equalAttribute(getValue("Name"), sf.getValue("Name")))
235 if (!equalAttribute(getValue("Parent"), sf.getValue("Parent")))
244 * Returns true if both values are null, are both non-null and equal
250 protected static boolean equalAttribute(Object att1, Object att2)
252 if (att1 == null && att2 == null)
258 return att1.equals(att2);
260 return att2.equals(att1);
266 * @return DOCUMENT ME!
269 public int getBegin()
277 * @return DOCUMENT ME!
288 * @return DOCUMENT ME!
290 public String getType()
298 * @return DOCUMENT ME!
300 public String getDescription()
305 public void setDescription(String desc)
310 public String getFeatureGroup()
315 public void addLink(String labelLink)
319 links = new Vector<String>();
322 if (!links.contains(labelLink))
324 links.insertElementAt(labelLink, 0);
328 public float getScore()
334 * Used for getting values which are not in the basic set. eg STRAND, PHASE
340 public Object getValue(String key)
342 if (otherDetails == null)
348 return otherDetails.get(key);
353 * Returns a property value for the given key if known, else the specified
357 * @param defaultValue
360 public Object getValue(String key, Object defaultValue)
362 Object value = getValue(key);
363 return value == null ? defaultValue : value;
367 * Used for setting values which are not in the basic set. eg STRAND, FRAME
375 public void setValue(String key, Object value)
379 if (otherDetails == null)
381 otherDetails = new HashMap<String, Object>();
384 otherDetails.put(key, value);
389 * The following methods are added to maintain the castor Uniprot mapping file
392 public void setStatus(String status)
394 setValue(STATUS, status);
397 public String getStatus()
399 return (String) getValue(STATUS);
402 public void setAttributes(String attr)
404 setValue(ATTRIBUTES, attr);
407 public String getAttributes()
409 return (String) getValue(ATTRIBUTES);
413 * Return 1 for forward strand ('+' in GFF), -1 for reverse strand ('-' in
414 * GFF), and 0 for unknown or not (validly) specified
418 public int getStrand()
421 if (otherDetails != null)
423 Object str = otherDetails.get(STRAND);
428 else if ("+".equals(str))
437 * Set the value of strand
440 * should be "+" for forward, or "-" for reverse
442 public void setStrand(String strand)
444 setValue(STRAND, strand);
447 public void setPhase(String phase)
449 setValue(PHASE, phase);
452 public String getPhase()
454 return (String) getValue(PHASE);
458 * Sets the 'raw' ENA format location specifier e.g. join(12..45,89..121)
462 public void setEnaLocation(String loc)
464 setValue(LOCATION, loc);
468 * Gets the 'raw' ENA format location specifier e.g. join(12..45,89..121)
472 public String getEnaLocation()
474 return (String) getValue(LOCATION);
478 * Readable representation, for debug only, not guaranteed not to change
482 public String toString()
484 return String.format("%d %d %s %s", getBegin(), getEnd(), getType(),
489 * Overridden to ensure that whenever two objects are equal, they have the
493 public int hashCode()
495 String s = getType() + getDescription() + getFeatureGroup()
496 + getValue("ID") + getValue("Name") + getValue("Parent")
498 return s.hashCode() + getBegin() + getEnd() + (int) getScore()
503 * Answers true if the feature's start/end values represent two related
504 * positions, rather than ends of a range. Such features may be visualised or
505 * reported differently to features on a range.
508 public boolean isContactFeature()
510 return contactFeature;
514 * Answers true if the sequence has zero start and end position
518 public boolean isNonPositional()
520 return begin == 0 && end == 0;