+ /**
+ * Answers true if the sequence being retrieved may occupy discontiguous
+ * regions on the genomic sequence.
+ */
+ protected boolean isSpliceable()
+ {
+ return true;
+ }
+
+ /**
+ * Returns true if the sequence feature marks positions of the genomic
+ * sequence feature which are within the sequence being retrieved. For
+ * example, an 'exon' feature whose parent is the target transcript marks the
+ * cdna positions of the transcript.
+ *
+ * @param sf
+ * @param accId
+ * @return
+ */
+ protected abstract boolean identifiesSequence(SequenceFeature sf,
+ String accId);
+
+ /**
+ * Transfers the sequence feature to the target sequence, locating its start
+ * and end range based on the mapping. Features which do not overlap the
+ * target sequence are ignored.
+ *
+ * @param sf
+ * @param targetSequence
+ * @param mapping
+ * mapping from the sequence feature's coordinates to the target
+ * sequence
+ */
+ protected void transferFeature(SequenceFeature sf,
+ SequenceI targetSequence, MapList mapping)
+ {
+ int start = sf.getBegin();
+ int end = sf.getEnd();
+ int[] mappedRange = mapping.locateInTo(start, end);
+
+ if (mappedRange != null)
+ {
+ SequenceFeature copy = new SequenceFeature(sf);
+ copy.setBegin(Math.min(mappedRange[0], mappedRange[1]));
+ copy.setEnd(Math.max(mappedRange[0], mappedRange[1]));
+ targetSequence.addSequenceFeature(copy);
+
+ /*
+ * for sequence_variant, make an additional feature with consequence
+ */
+ // if (SequenceOntologyFactory.getInstance().isA(sf.getType(),
+ // SequenceOntologyI.SEQUENCE_VARIANT))
+ // {
+ // String consequence = (String) sf.getValue(CONSEQUENCE_TYPE);
+ // if (consequence != null)
+ // {
+ // SequenceFeature sf2 = new SequenceFeature("consequence",
+ // consequence, copy.getBegin(), copy.getEnd(), 0f,
+ // null);
+ // targetSequence.addSequenceFeature(sf2);
+ // }
+ // }
+ }
+ }
+
+ /**
+ * Transfers features from sourceSequence to targetSequence
+ *
+ * @param accessionId
+ * @param sourceSequence
+ * @param targetSequence
+ * @return true if any features were transferred, else false
+ */
+ protected boolean transferFeatures(String accessionId,
+ SequenceI sourceSequence, SequenceI targetSequence)
+ {
+ if (sourceSequence == null || targetSequence == null)
+ {
+ return false;
+ }
+
+ // long start = System.currentTimeMillis();
+ SequenceFeature[] sfs = sourceSequence.getSequenceFeatures();
+ MapList mapping = getGenomicRangesFromFeatures(sourceSequence, accessionId,
+ targetSequence.getStart());
+ if (mapping == null)
+ {
+ return false;
+ }
+
+ boolean result = transferFeatures(sfs, targetSequence, mapping,
+ accessionId);
+ // System.out.println("transferFeatures (" + (sfs.length) + " --> "
+ // + targetSequence.getSequenceFeatures().length + ") to "
+ // + targetSequence.getName()
+ // + " took " + (System.currentTimeMillis() - start) + "ms");
+ return result;
+ }
+
+ /**
+ * Transfer features to the target sequence. The start/end positions are
+ * converted using the mapping. Features which do not overlap are ignored.
+ * Features whose parent is not the specified identifier are also ignored.
+ *
+ * @param features
+ * @param targetSequence
+ * @param mapping
+ * @param parentId
+ * @return
+ */
+ protected boolean transferFeatures(SequenceFeature[] features,
+ SequenceI targetSequence, MapList mapping, String parentId)
+ {
+ final boolean forwardStrand = mapping.isFromForwardStrand();
+
+ /*
+ * sort features by start position (descending if reverse strand)
+ * before transferring (in forwards order) to the target sequence
+ */
+ Arrays.sort(features, new Comparator<SequenceFeature>()
+ {
+ @Override
+ public int compare(SequenceFeature o1, SequenceFeature o2)
+ {
+ int c = Integer.compare(o1.getBegin(), o2.getBegin());
+ return forwardStrand ? c : -c;
+ }
+ });
+
+ boolean transferred = false;
+ for (SequenceFeature sf : features)
+ {
+ if (retainFeature(sf, parentId))
+ {
+ transferFeature(sf, targetSequence, mapping);
+ transferred = true;