+ /*
+ * found the reverse dbref; update its mapping if null
+ */
+ if (toRef.getMap().getMap() == null)
+ {
+ MapList inverse = dbref.getMap().getMap().getInverse();
+ toRef.getMap().setMap(inverse);
+ mappings.addMap(mapTo, mapFrom, inverse);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns null or the first sequence in the dataset which is identical to
+ * xref.mapTo, and has a) a primary dbref matching xref, or if none found, the
+ * first one with an ID source|xrefacc
+ *
+ * @param xref
+ * with map and mapped-to sequence
+ * @return
+ */
+ SequenceI findInDataset(DBRefEntry xref)
+ {
+ if (xref == null || !xref.hasMap() || xref.getMap().getTo() == null)
+ {
+ return null;
+ }
+ SequenceI mapsTo = xref.getMap().getTo();
+ String name = xref.getAccessionId();
+ String name2 = xref.getSource() + "|" + name;
+ SequenceI dss = mapsTo.getDatasetSequence() == null ? mapsTo
+ : mapsTo.getDatasetSequence();
+ // first check ds if ds is directly referenced
+ if (dataset.findIndex(dss) > -1)
+ {
+ return dss;
+ }
+ DBRefEntry template = new DBRefEntry(xref.getSource(), null,
+ xref.getAccessionId());
+ /**
+ * remember the first ID match - in case we don't find a match to template
+ */
+ SequenceI firstIdMatch = null;
+ for (SequenceI seq : dataset.getSequences())
+ {
+ // first check primary refs.
+ List<DBRefEntry> match = DBRefUtils.searchRefs(
+ seq.getPrimaryDBRefs().toArray(new DBRefEntry[0]), template);
+ if (match != null && match.size() == 1 && sameSequence(seq, dss))
+ {
+ return seq;
+ }
+ /*
+ * clumsy alternative to using SequenceIdMatcher which currently
+ * returns sequences with a dbref to the matched accession id
+ * which we don't want
+ */
+ if (firstIdMatch == null && (name.equals(seq.getName())
+ || seq.getName().startsWith(name2)))
+ {
+ if (sameSequence(seq, dss))
+ {
+ firstIdMatch = seq;
+ }
+ }
+ }
+ return firstIdMatch;
+ }
+
+ /**
+ * Answers true if seq1 and seq2 contain exactly the same characters (ignoring
+ * case), else false. This method compares the lengths, then each character in
+ * turn, in order to 'fail fast'. For case-sensitive comparison, it would be
+ * possible to use Arrays.equals(seq1.getSequence(), seq2.getSequence()).
+ *
+ * @param seq1
+ * @param seq2
+ * @return
+ */
+ // TODO move to Sequence / SequenceI
+ static boolean sameSequence(SequenceI seq1, SequenceI seq2)
+ {
+ if (seq1 == seq2)
+ {
+ return true;
+ }
+ if (seq1 == null || seq2 == null)
+ {
+ return false;
+ }
+ char[] c1 = seq1.getSequence();
+ char[] c2 = seq2.getSequence();
+ if (c1.length != c2.length)
+ {
+ return false;
+ }
+ for (int i = 0; i < c1.length; i++)
+ {
+ int diff = c1[i] - c2[i];
+ /*
+ * same char or differ in case only ('a'-'A' == 32)
+ */
+ if (diff != 0 && diff != 32 && diff != -32)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Updates any empty mappings in the cross-references with one to a compatible
+ * retrieved sequence if found, and adds any new mappings to the
+ * AlignedCodonFrame
+ *
+ * @param mapFrom
+ * @param xrefs
+ * @param retrieved
+ * @param acf
+ */
+ void updateDbrefMappings(SequenceI mapFrom, DBRefEntry[] xrefs,
+ SequenceI[] retrieved, AlignedCodonFrame acf, boolean fromDna)
+ {
+ SequenceIdMatcher idMatcher = new SequenceIdMatcher(retrieved);
+ for (DBRefEntry xref : xrefs)
+ {
+ if (!xref.hasMap())
+ {
+ String targetSeqName = xref.getSource() + "|"
+ + xref.getAccessionId();
+ SequenceI[] matches = idMatcher.findAllIdMatches(targetSeqName);
+ if (matches == null)
+ {
+ return;
+ }
+ for (SequenceI seq : matches)
+ {
+ constructMapping(mapFrom, seq, xref, acf, fromDna);
+ }