X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fdatamodel%2FSequence.java;h=98b0fb3a4846dae0f1b1ba01fced2039b319b70a;hb=62d2ee96fc3876b3773e786596bc5bf3ed2388df;hp=8c53482f4871085ddae6017fc852096b120de5a9;hpb=b5aea33b1a9dcb1831ef681342be55c8929cf080;p=jalview.git diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 8c53482..98b0fb3 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -89,7 +89,7 @@ public class Sequence extends ASequence implements SequenceI */ int index = -1; - private SequenceFeatures sequenceFeatureStore; + private SequenceFeaturesI sequenceFeatureStore; /* * A cursor holding the approximate current view position to the sequence, @@ -155,8 +155,8 @@ public class Sequence extends ASequence implements SequenceI { if (name == null) { - System.err - .println("POSSIBLE IMPLEMENTATION ERROR: null sequence name passed to constructor."); + System.err.println( + "POSSIBLE IMPLEMENTATION ERROR: null sequence name passed to constructor."); name = ""; } // Does sequence have the /start-end signature? @@ -260,9 +260,8 @@ public class Sequence extends ASequence implements SequenceI protected void initSeqFrom(SequenceI seq, AlignmentAnnotation[] alAnnotation) { - char[] oseq = seq.getSequence(); - initSeqAndName(seq.getName(), Arrays.copyOf(oseq, oseq.length), - seq.getStart(), seq.getEnd()); + char[] oseq = seq.getSequence(); // returns a copy of the array + initSeqAndName(seq.getName(), oseq, seq.getStart(), seq.getEnd()); description = seq.getDescription(); if (seq != datasetSequence) @@ -563,7 +562,9 @@ public class Sequence extends ASequence implements SequenceI @Override public char[] getSequence() { - return sequence; + // return sequence; + return sequence == null ? null : Arrays.copyOf(sequence, + sequence.length); } /* @@ -644,10 +645,10 @@ public class Sequence extends ASequence implements SequenceI } /** - * DOCUMENT ME! + * Sets the sequence description, and also parses out any special formats of + * interest * * @param desc - * DOCUMENT ME! */ @Override public void setDescription(String desc) @@ -655,10 +656,67 @@ public class Sequence extends ASequence implements SequenceI this.description = desc; } + @Override + public void setGeneLoci(String speciesId, String assemblyId, + String chromosomeId, MapList map) + { + addDBRef(new DBRefEntry(speciesId, assemblyId, DBRefEntry.CHROMOSOME + + ":" + chromosomeId, new Mapping(map))); + } + /** - * DOCUMENT ME! + * Returns the gene loci mapping for the sequence (may be null) * - * @return DOCUMENT ME! + * @return + */ + @Override + public GeneLociI getGeneLoci() + { + DBRefEntry[] refs = getDBRefs(); + if (refs != null) + { + for (final DBRefEntry ref : refs) + { + if (ref.isChromosome()) + { + return new GeneLociI() + { + @Override + public String getSpeciesId() + { + return ref.getSource(); + } + + @Override + public String getAssemblyId() + { + return ref.getVersion(); + } + + @Override + public String getChromosomeId() + { + // strip of "chromosome:" prefix to chrId + return ref.getAccessionId().substring( + DBRefEntry.CHROMOSOME.length() + 1); + } + + @Override + public MapList getMap() + { + return ref.getMap().getMap(); + } + }; + } + } + } + return null; + } + + /** + * Answers the description + * + * @return */ @Override public String getDescription() @@ -682,11 +740,20 @@ public class Sequence extends ASequence implements SequenceI int j = start; int i = 0; - // Rely on end being at least as long as the length of the sequence. + int startColumn = 0; + + /* + * traverse sequence from the start counting gaps; make a note of + * the column of the first residue to save in the cursor + */ while ((i < sequence.length) && (j <= end) && (j <= pos)) { if (!Comparison.isGap(sequence[i])) { + if (j == start) + { + startColumn = i; + } j++; } i++; @@ -697,7 +764,7 @@ public class Sequence extends ASequence implements SequenceI return end + 1; } - updateCursor(pos, i); + updateCursor(pos, i, startColumn); return i; } @@ -708,10 +775,22 @@ public class Sequence extends ASequence implements SequenceI * (start..) * @param column * (1..) + * @param startColumn + * column position of the first sequence residue */ - protected void updateCursor(int residuePos, int column) + protected void updateCursor(int residuePos, int column, int startColumn) { - cursor = new SequenceCursor(this, residuePos, column, this.changeCount); + /* + * preserve end residue column provided cursor was valid + */ + int endColumn = isValidCursor(cursor) ? cursor.lastColumnPosition : 0; + if (residuePos == this.end) + { + endColumn = column; + } + + cursor = new SequenceCursor(this, residuePos, column, startColumn, + endColumn, this.changeCount); } /** @@ -760,7 +839,7 @@ public class Sequence extends ASequence implements SequenceI } col++; // convert back to base 1 - updateCursor(pos, col); + updateCursor(pos, col, curs.firstColumnPosition); return col; } @@ -778,13 +857,19 @@ public class Sequence extends ASequence implements SequenceI { return findPosition(column + 1, cursor); } - + // TODO recode this more naturally i.e. count residues only // as they are found, not 'in anticipation' + /* + * traverse the sequence counting gaps; note the column position + * of the first residue, to save in the cursor + */ + int firstResidueColumn = 0; int lastPosFound = 0; int lastPosFoundColumn = 0; int seqlen = sequence.length; + if (seqlen > 0 && !Comparison.isGap(sequence[0])) { lastPosFound = start; @@ -800,6 +885,10 @@ public class Sequence extends ASequence implements SequenceI { lastPosFound = pos; lastPosFoundColumn = j; + if (pos == this.start) + { + firstResidueColumn = j; + } pos++; } j++; @@ -808,6 +897,10 @@ public class Sequence extends ASequence implements SequenceI { lastPosFound = pos; lastPosFoundColumn = j; + if (pos == this.start) + { + firstResidueColumn = j; + } } /* @@ -816,7 +909,8 @@ public class Sequence extends ASequence implements SequenceI */ if (lastPosFound != 0) { - updateCursor(lastPosFound, lastPosFoundColumn + 1); + updateCursor(lastPosFound, lastPosFoundColumn + 1, + firstResidueColumn + 1); } return pos; @@ -840,7 +934,7 @@ public class Sequence extends ASequence implements SequenceI /* * sanity check against range */ - if (curs.columnPosition < 0 || curs.columnPosition >= sequence.length) + if (curs.columnPosition < 0 || curs.columnPosition > sequence.length) { return false; } @@ -876,9 +970,31 @@ public class Sequence extends ASequence implements SequenceI return curs.residuePosition; // easy case :-) } + if (curs.lastColumnPosition > 0 && curs.lastColumnPosition < col) + { + /* + * sequence lies entirely to the left of col + * - return last residue + 1 + */ + return end + 1; + } + + if (curs.firstColumnPosition > 0 && curs.firstColumnPosition > col) + { + /* + * sequence lies entirely to the right of col + * - return first residue + */ + return start; + } + + // todo could choose closest to col out of column, + // firstColumnPosition, lastColumnPosition as a start point + /* * move left or right to find pos from cursor position */ + int firstResidueColumn = curs.firstColumnPosition; int column = curs.columnPosition - 1; // to base 0 int newPos = curs.residuePosition; int delta = curs.columnPosition > col ? -1 : 1; @@ -899,12 +1015,17 @@ public class Sequence extends ASequence implements SequenceI newPos += delta; lastFoundPosition = newPos; lastFoundPositionColumn = column + 1; + if (lastFoundPosition == this.start) + { + firstResidueColumn = column + 1; + } } } if (cursor == null || lastFoundPosition != cursor.residuePosition) { - updateCursor(lastFoundPosition, lastFoundPositionColumn); + updateCursor(lastFoundPosition, lastFoundPositionColumn, + firstResidueColumn); } /* @@ -920,6 +1041,53 @@ public class Sequence extends ASequence implements SequenceI } /** + * {@inheritDoc} + */ + @Override + public Range findPositions(int fromColumn, int toColumn) + { + if (toColumn < fromColumn || fromColumn < 1) + { + return null; + } + + /* + * find the first non-gapped position, if any + */ + int firstPosition = 0; + int col = fromColumn - 1; + int length = sequence.length; + while (col < length && col < toColumn) + { + if (!Comparison.isGap(sequence[col])) + { + firstPosition = findPosition(col++); + break; + } + col++; + } + + if (firstPosition == 0) + { + return null; + } + + /* + * find the last non-gapped position + */ + int lastPosition = firstPosition; + while (col < length && col < toColumn) + { + if (!Comparison.isGap(sequence[col++])) + { + lastPosition++; + } + } + + return new Range(firstPosition, lastPosition); + } + + /** * Returns an int array where indices correspond to each residue in the * sequence and the element value gives its position in the alignment * @@ -1253,8 +1421,9 @@ public class Sequence extends ASequence implements SequenceI @Override public AlignmentAnnotation[] getAnnotation() { - return annotation == null ? null : annotation - .toArray(new AlignmentAnnotation[annotation.size()]); + return annotation == null ? null + : annotation + .toArray(new AlignmentAnnotation[annotation.size()]); } @Override @@ -1335,7 +1504,7 @@ public class Sequence extends ASequence implements SequenceI private boolean _isNa; - private long _seqhash = 0; + private int _seqhash = 0; /** * Answers false if the sequence is more than 85% nucleotide (ACGTU), else @@ -1366,8 +1535,9 @@ public class Sequence extends ASequence implements SequenceI { if (datasetSequence == null) { - Sequence dsseq = new Sequence(getName(), AlignSeq.extractGaps( - jalview.util.Comparison.GapChars, getSequenceAsString()), + Sequence dsseq = new Sequence(getName(), + AlignSeq.extractGaps(jalview.util.Comparison.GapChars, + getSequenceAsString()), getStart(), getEnd()); datasetSequence = dsseq; @@ -1509,7 +1679,7 @@ public class Sequence extends ASequence implements SequenceI List sfs = entry.getSequenceFeatures(); for (SequenceFeature feature : sfs) { - SequenceFeature sf[] = (mp != null) ? mp.locateFeature(feature) + SequenceFeature sf[] = (mp != null) ? mp.locateFeature(feature) : new SequenceFeature[] { new SequenceFeature(feature) }; if (sf != null) { @@ -1666,8 +1836,8 @@ public class Sequence extends ASequence implements SequenceI } } // whilst it looks like it is a primary ref, we also sanity check type - if (DBRefUtils.getCanonicalName(DBRefSource.PDB).equals( - DBRefUtils.getCanonicalName(ref.getSource()))) + if (DBRefUtils.getCanonicalName(DBRefSource.PDB) + .equals(DBRefUtils.getCanonicalName(ref.getSource()))) { // PDB dbrefs imply there should be a PDBEntry associated // TODO: tighten PDB dbrefs @@ -1703,47 +1873,45 @@ public class Sequence extends ASequence implements SequenceI String... types) { int startPos = findPosition(fromColumn - 1); // convert base 1 to base 0 - int endPos = findPosition(toColumn - 1); + int endPos = fromColumn == toColumn ? startPos + : findPosition(toColumn - 1); - List result = new ArrayList<>(); - if (datasetSequence != null) - { - result = datasetSequence.getFeatures().findFeatures(startPos, endPos, - types); - } - else - { - result = sequenceFeatureStore.findFeatures(startPos, endPos, types); - } + List result = getFeatures().findFeatures(startPos, + endPos, types); /* - * if the start or end column is gapped, startPos or endPos may be to the - * left or right, and we may have included adjacent or enclosing features; + * if end column is gapped, endPos may be to the right, + * and we may have included adjacent or enclosing features; * remove any that are not enclosing, non-contact features */ - if (endPos > this.end || Comparison.isGap(sequence[fromColumn - 1]) - || Comparison.isGap(sequence[toColumn - 1])) + if (endPos > this.end || Comparison.isGap(sequence[toColumn - 1])) { ListIterator it = result.listIterator(); while (it.hasNext()) { SequenceFeature sf = it.next(); - int featureStartColumn = findIndex(sf.getBegin()); - int featureEndColumn = findIndex(sf.getEnd()); - boolean noOverlap = featureStartColumn > toColumn - || featureEndColumn < fromColumn; - - /* - * reject an 'enclosing' feature if it is actually a contact feature - */ - if (sf.isContactFeature() && featureStartColumn < fromColumn - && featureEndColumn > toColumn) + int sfBegin = sf.getBegin(); + int sfEnd = sf.getEnd(); + int featureStartColumn = findIndex(sfBegin); + if (featureStartColumn > toColumn) { - noOverlap = true; + it.remove(); } - if (noOverlap) + else if (featureStartColumn < fromColumn) { - it.remove(); + int featureEndColumn = sfEnd == sfBegin ? featureStartColumn + : findIndex(sfEnd); + if (featureEndColumn < fromColumn) + { + it.remove(); + } + else if (featureEndColumn > toColumn && sf.isContactFeature()) + { + /* + * remove an enclosing feature if it is a contact feature + */ + it.remove(); + } } } } @@ -1760,4 +1928,34 @@ public class Sequence extends ASequence implements SequenceI { changeCount++; } + + /** + * {@inheritDoc} + */ + @Override + public int replace(char c1, char c2) + { + if (c1 == c2) + { + return 0; + } + int count = 0; + synchronized (sequence) + { + for (int c = 0; c < sequence.length; c++) + { + if (sequence[c] == c1) + { + sequence[c] = c2; + count++; + } + } + } + if (count > 0) + { + sequenceChanged(); + } + + return count; + } }