JAL-1551 spotlessApply
[jalview.git] / src / jalview / analysis / AlignmentUtils.java
index d04d810..1158c53 100644 (file)
@@ -29,6 +29,7 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
@@ -36,7 +37,7 @@ import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
-import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.commands.RemoveGapColCommand;
 import jalview.datamodel.AlignedCodon;
 import jalview.datamodel.AlignedCodonFrame;
@@ -44,6 +45,7 @@ import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.GeneLociI;
 import jalview.datamodel.IncompleteCodonException;
@@ -182,10 +184,10 @@ public class AlignmentUtils
       // TODO use Character.toLowerCase to avoid creating String objects?
       char[] upstream = new String(ds
               .getSequence(s.getStart() - 1 - ustream_ds, s.getStart() - 1))
-                      .toLowerCase().toCharArray();
+                      .toLowerCase(Locale.ROOT).toCharArray();
       char[] downstream = new String(
-              ds.getSequence(s_end - 1, s_end + dstream_ds)).toLowerCase()
-                      .toCharArray();
+              ds.getSequence(s_end - 1, s_end + dstream_ds))
+                      .toLowerCase(Locale.ROOT).toCharArray();
       char[] coreseq = s.getSequence();
       char[] nseq = new char[offset + upstream.length + downstream.length
               + coreseq.length];
@@ -463,7 +465,8 @@ public class AlignmentUtils
     if (cdnaLength != mappedLength && cdnaLength > 2)
     {
       String lastCodon = String.valueOf(cdnaSeqChars,
-              cdnaLength - CODON_LENGTH, CODON_LENGTH).toUpperCase();
+              cdnaLength - CODON_LENGTH, CODON_LENGTH)
+              .toUpperCase(Locale.ROOT);
       for (String stop : ResidueProperties.STOP_CODONS)
       {
         if (lastCodon.equals(stop))
@@ -480,7 +483,8 @@ public class AlignmentUtils
      */
     int startOffset = 0;
     if (cdnaLength != mappedLength && cdnaLength > 2
-            && String.valueOf(cdnaSeqChars, 0, CODON_LENGTH).toUpperCase()
+            && String.valueOf(cdnaSeqChars, 0, CODON_LENGTH)
+                    .toUpperCase(Locale.ROOT)
                     .equals(ResidueProperties.START))
     {
       startOffset += CODON_LENGTH;
@@ -961,12 +965,11 @@ public class AlignmentUtils
             .findMappingsForSequence(cdsSeq, mappings);
     for (AlignedCodonFrame mapping : dnaMappings)
     {
-      List<SequenceToSequenceMapping> foundMap=new ArrayList<>();
-      SequenceI peptide = mapping.findAlignedSequence(cdsSeq, protein,foundMap);
+      SequenceI peptide = mapping.findAlignedSequence(cdsSeq, protein);
       if (peptide != null)
       {
         final int peptideLength = peptide.getLength();
-        Mapping map = foundMap.get(0).getMapping();
+        Mapping map = mapping.getMappingBetween(cdsSeq, peptide);
         if (map != null)
         {
           MapList mapList = map.getMap();
@@ -975,14 +978,14 @@ public class AlignmentUtils
             mapList = mapList.getInverse();
           }
           final int cdsLength = cdsDss.getLength();
-          int mappedFromLength = MappingUtils.getLength(mapList
-                  .getFromRanges());
+          int mappedFromLength = MappingUtils
+                  .getLength(mapList.getFromRanges());
           int mappedToLength = MappingUtils
                   .getLength(mapList.getToRanges());
-          boolean addStopCodon = peptide.getDatasetSequence().getEnd()==peptide.getEnd() && ((cdsLength == mappedFromLength
+          boolean addStopCodon = (cdsLength == mappedFromLength
                   * CODON_LENGTH + CODON_LENGTH)
                   || (peptide.getDatasetSequence()
-                          .getLength() == mappedFromLength - 1));
+                          .getLength() == mappedFromLength - 1);
           if (cdsLength != mappedToLength && !addStopCodon)
           {
             System.err.println(String.format(
@@ -1468,7 +1471,19 @@ public class AlignmentUtils
          */
         final Iterable<AlignmentAnnotation> matchedAlignmentAnnotations = al
                 .findAnnotations(seq, dsann.getCalcId(), dsann.label);
-        if (!matchedAlignmentAnnotations.iterator().hasNext())
+        boolean found = false;
+        if (matchedAlignmentAnnotations != null)
+        {
+          for (AlignmentAnnotation matched : matchedAlignmentAnnotations)
+          {
+            if (dsann.description.equals(matched.description))
+            {
+              found = true;
+              break;
+            }
+          }
+        }
+        if (!found)
         {
           result.add(dsann);
           if (labelForCalcId != null)
@@ -1489,7 +1504,8 @@ public class AlignmentUtils
 
   /**
    * Adds annotations to the top of the alignment annotations, in the same order
-   * as their related sequences.
+   * as their related sequences. If you already have an annotation and want to
+   * add it to a sequence in an alignment use {@code addReferenceAnnotationTo}
    * 
    * @param annotations
    *          the annotations to add
@@ -1506,31 +1522,60 @@ public class AlignmentUtils
     {
       for (AlignmentAnnotation ann : annotations.get(seq))
       {
-        AlignmentAnnotation copyAnn = new AlignmentAnnotation(ann);
-        int startRes = 0;
-        int endRes = ann.annotations.length;
-        if (selectionGroup != null)
-        {
-          startRes = selectionGroup.getStartRes();
-          endRes = selectionGroup.getEndRes();
-        }
-        copyAnn.restrict(startRes, endRes);
+        addReferenceAnnotationTo(alignment, seq, ann, selectionGroup);
+      }
+    }
+  }
 
-        /*
-         * Add to the sequence (sets copyAnn.datasetSequence), unless the
-         * original annotation is already on the sequence.
-         */
-        if (!seq.hasAnnotation(ann))
-        {
-          seq.addAlignmentAnnotation(copyAnn);
-        }
-        // adjust for gaps
-        copyAnn.adjustForAlignment();
-        // add to the alignment and set visible
-        alignment.addAnnotation(copyAnn);
-        copyAnn.visible = true;
+  /**
+   * Make a copy of a reference annotation {@code ann} and add it to an
+   * alignment sequence {@code seq} in {@code alignment}, optionally limited to
+   * the extent of {@code selectionGroup}
+   * 
+   * @param alignment
+   * @param seq
+   * @param ann
+   * @param selectionGroup
+   *          - may be null
+   * @return annotation added to {@code seq and {@code alignment}
+   */
+  public static AlignmentAnnotation addReferenceAnnotationTo(
+          final AlignmentI alignment, final SequenceI seq,
+          final AlignmentAnnotation ann, final SequenceGroup selectionGroup)
+  {
+    AlignmentAnnotation copyAnn = new AlignmentAnnotation(ann);
+    int startRes = 0;
+    int endRes = ann.annotations.length;
+    if (selectionGroup != null)
+    {
+      startRes = -1 + Math.min(seq.getEnd(), Math.max(seq.getStart(),
+              seq.findPosition(selectionGroup.getStartRes())));
+      endRes = -1 + Math.min(seq.getEnd(),
+              seq.findPosition(selectionGroup.getEndRes()));
+
+    }
+    copyAnn.restrict(startRes, endRes + 0);
+
+    /*
+     * Add to the sequence (sets copyAnn.datasetSequence), unless the
+     * original annotation is already on the sequence.
+     */
+    if (!seq.hasAnnotation(ann))
+    {
+      ContactMatrixI cm = seq.getDatasetSequence().getContactMatrixFor(ann);
+      if (cm != null)
+      {
+        seq.addContactListFor(copyAnn, cm);
       }
+      seq.addAlignmentAnnotation(copyAnn);
     }
+    // adjust for gaps
+    copyAnn.adjustForAlignment();
+    // add to the alignment and set visible
+    alignment.addAnnotation(copyAnn);
+    copyAnn.visible = true;
+
+    return copyAnn;
   }
 
   /**
@@ -1570,6 +1615,21 @@ public class AlignmentUtils
     }
   }
 
+  public static AlignmentAnnotation getFirstSequenceAnnotationOfType(
+          AlignmentI al, int graphType)
+  {
+    AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
+    if (anns != null)
+    {
+      for (AlignmentAnnotation aa : anns)
+      {
+        if (aa.sequenceRef != null && aa.graph == graphType)
+          return aa;
+      }
+    }
+    return null;
+  }
+
   /**
    * Returns true if either sequence has a cross-reference to the other
    * 
@@ -1599,11 +1659,12 @@ public class AlignmentUtils
       return false;
     }
     String name = seq2.getName();
-    final DBRefEntry[] xrefs = seq1.getDBRefs();
+    final List<DBRefEntry> xrefs = seq1.getDBRefs();
     if (xrefs != null)
     {
-      for (DBRefEntry xref : xrefs)
+      for (int ix = 0, nx = xrefs.size(); ix < nx; ix++)
       {
+        DBRefEntry xref = xrefs.get(ix);
         String xrefName = xref.getSource() + "|" + xref.getAccessionId();
         // case-insensitive test, consistent with DBRefEntry.equalRef()
         if (xrefName.equalsIgnoreCase(name))
@@ -1649,8 +1710,8 @@ public class AlignmentUtils
       productSeqs = new HashSet<>();
       for (SequenceI seq : products)
       {
-        productSeqs.add(seq.getDatasetSequence() == null ? seq : seq
-                .getDatasetSequence());
+        productSeqs.add(seq.getDatasetSequence() == null ? seq
+                : seq.getDatasetSequence());
       }
     }
 
@@ -1754,7 +1815,7 @@ public class AlignmentUtils
             dataset.addSequence(cdsSeqDss);
             AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame();
             cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct,
-                  cdsToProteinMap);
+                    cdsToProteinMap);
 
             /*
              * guard against duplicating the mapping if repeating this action
@@ -1804,22 +1865,24 @@ public class AlignmentUtils
           // need to
           // synthesize an xref.
 
-          for (DBRefEntry primRef : dnaDss.getPrimaryDBRefs())
+          List<DBRefEntry> primrefs = dnaDss.getPrimaryDBRefs();
+          for (int ip = 0, np = primrefs.size(); ip < np; ip++)
           {
+            DBRefEntry primRef = primrefs.get(ip);
             /*
              * create a cross-reference from CDS to the source sequence's
              * primary reference and vice versa
              */
             String source = primRef.getSource();
             String version = primRef.getVersion();
-            DBRefEntry cdsCrossRef = new DBRefEntry(source, source + ":"
-                    + version, primRef.getAccessionId());
-            cdsCrossRef.setMap(new Mapping(dnaDss, new MapList(cdsToDnaMap)));
+            DBRefEntry cdsCrossRef = new DBRefEntry(source,
+                    source + ":" + version, primRef.getAccessionId());
+            cdsCrossRef
+                    .setMap(new Mapping(dnaDss, new MapList(cdsToDnaMap)));
             cdsSeqDss.addDBRef(cdsCrossRef);
 
-            dnaSeq.addDBRef(new DBRefEntry(source, version, cdsSeq
-                    .getName(), new Mapping(cdsSeqDss, dnaToCdsMap)));
-
+            dnaSeq.addDBRef(new DBRefEntry(source, version,
+                    cdsSeq.getName(), new Mapping(cdsSeqDss, dnaToCdsMap)));
             // problem here is that the cross-reference is synthesized -
             // cdsSeq.getName() may be like 'CDS|dnaaccession' or
             // 'CDS|emblcdsacc'
@@ -1828,11 +1891,10 @@ public class AlignmentUtils
             DBRefEntry proteinToCdsRef = new DBRefEntry(source, version,
                     cdsSeq.getName());
             //
-            proteinToCdsRef.setMap(new Mapping(cdsSeqDss, cdsToProteinMap
-                    .getInverse()));
+            proteinToCdsRef.setMap(
+                    new Mapping(cdsSeqDss, cdsToProteinMap.getInverse()));
             proteinProduct.addDBRef(proteinToCdsRef);
           }
-
           /*
            * transfer any features on dna that overlap the CDS
            */
@@ -1842,8 +1904,8 @@ public class AlignmentUtils
       }
     }
 
-    AlignmentI cds = new Alignment(cdsSeqs.toArray(new SequenceI[cdsSeqs
-            .size()]));
+    AlignmentI cds = new Alignment(
+            cdsSeqs.toArray(new SequenceI[cdsSeqs.size()]));
     cds.setDataset(dataset);
 
     return cds;
@@ -2054,8 +2116,9 @@ public class AlignmentUtils
           }
           else
           {
-            Cache.log.error(
-                    "JAL-2154 regression: warning - found (and ignored) a duplicate CDS sequence:" + mtch.toString());
+            Console.error(
+                    "JAL-2154 regression: warning - found (and ignored) a duplicate CDS sequence:"
+                            + mtch.toString());
           }
         }
       }
@@ -2083,13 +2146,15 @@ public class AlignmentUtils
     List<DBRefEntry> direct = new ArrayList<>();
     HashSet<String> directSources = new HashSet<>();
 
-    if (contig.getDBRefs() != null)
+    List<DBRefEntry> refs = contig.getDBRefs();
+    if (refs != null)
     {
-      for (DBRefEntry dbr : contig.getDBRefs())
+      for (int ib = 0, nb = refs.size(); ib < nb; ib++)
       {
-        if (dbr.hasMap() && dbr.getMap().getMap().isTripletMap())
+        DBRefEntry dbr = refs.get(ib);
+        MapList map;
+        if (dbr.hasMap() && (map = dbr.getMap().getMap()).isTripletMap())
         {
-          MapList map = dbr.getMap().getMap();
           // check if map is the CDS mapping
           if (mapping.getMap().equals(map))
           {
@@ -2099,21 +2164,22 @@ public class AlignmentUtils
         }
       }
     }
-    DBRefEntry[] onSource = DBRefUtils.selectRefs(
+    List<DBRefEntry> onSource = DBRefUtils.selectRefs(
             proteinProduct.getDBRefs(),
             directSources.toArray(new String[0]));
     List<DBRefEntry> propagated = new ArrayList<>();
 
     // and generate appropriate mappings
-    for (DBRefEntry cdsref : direct)
+    for (int ic = 0, nc = direct.size(); ic < nc; ic++)
     {
+      DBRefEntry cdsref = direct.get(ic);
+      Mapping m = cdsref.getMap();
       // clone maplist and mapping
       MapList cdsposmap = new MapList(
               Arrays.asList(new int[][]
               { new int[] { cdsSeq.getStart(), cdsSeq.getEnd() } }),
-              cdsref.getMap().getMap().getToRanges(), 3, 1);
-      Mapping cdsmap = new Mapping(cdsref.getMap().getTo(),
-              cdsref.getMap().getMap());
+              m.getMap().getToRanges(), 3, 1);
+      Mapping cdsmap = new Mapping(m.getTo(), m.getMap());
 
       // create dbref
       DBRefEntry newref = new DBRefEntry(cdsref.getSource(),
@@ -2176,9 +2242,9 @@ public class AlignmentUtils
     /*
      * get features, optionally restricted by an ontology term
      */
-    List<SequenceFeature> sfs = select == null ? fromSeq.getFeatures()
-            .getPositionalFeatures() : fromSeq.getFeatures()
-            .getFeaturesByOntology(select);
+    List<SequenceFeature> sfs = select == null
+            ? fromSeq.getFeatures().getPositionalFeatures()
+            : fromSeq.getFeatures().getFeaturesByOntology(select);
 
     int count = 0;
     for (SequenceFeature sf : sfs)
@@ -2323,8 +2389,8 @@ public class AlignmentUtils
   {
     List<int[]> result = new ArrayList<>();
 
-    List<SequenceFeature> sfs = dnaSeq.getFeatures().getFeaturesByOntology(
-            SequenceOntologyI.CDS);
+    List<SequenceFeature> sfs = dnaSeq.getFeatures()
+            .getFeaturesByOntology(SequenceOntologyI.CDS);
     if (sfs.isEmpty())
     {
       return result;
@@ -2336,10 +2402,14 @@ public class AlignmentUtils
       int phase = 0;
       try
       {
-        phase = Integer.parseInt(sf.getPhase());
+        String s = sf.getPhase();
+        if (s != null)
+        {
+          phase = Integer.parseInt(s);
+        }
       } catch (NumberFormatException e)
       {
-        // ignore
+        // leave as zero
       }
       /*
        * phase > 0 on first codon means 5' incomplete - skip to the start
@@ -2392,19 +2462,25 @@ public class AlignmentUtils
     SequenceIdMatcher matcher = new SequenceIdMatcher(seqs);
     if (xrefs != null)
     {
-      for (SequenceI xref : xrefs)
+      // BH 2019.01.25 recoded to remove iterators
+
+      for (int ix = 0, nx = xrefs.length; ix < nx; ix++)
       {
-        DBRefEntry[] dbrefs = xref.getDBRefs();
+        SequenceI xref = xrefs[ix];
+        List<DBRefEntry> dbrefs = xref.getDBRefs();
         if (dbrefs != null)
         {
-          for (DBRefEntry dbref : dbrefs)
+          for (int ir = 0, nir = dbrefs.size(); ir < nir; ir++)
           {
-            if (dbref.getMap() == null || dbref.getMap().getTo() == null
-                    || dbref.getMap().getTo().isProtein() != isProtein)
+            DBRefEntry dbref = dbrefs.get(ir);
+            Mapping map = dbref.getMap();
+            SequenceI mto;
+            if (map == null || (mto = map.getTo()) == null
+                    || mto.isProtein() != isProtein)
             {
               continue;
             }
-            SequenceI mappedTo = dbref.getMap().getTo();
+            SequenceI mappedTo = mto;
             SequenceI match = matcher.findIdMatch(mappedTo);
             if (match == null)
             {
@@ -2505,10 +2581,10 @@ public class AlignmentUtils
    * true; else returns false
    * 
    * @param unaligned
-   *                    - sequences to be aligned based on aligned
+   *          - sequences to be aligned based on aligned
    * @param aligned
-   *                    - 'guide' alignment containing sequences derived from same
-   *                    dataset as unaligned
+   *          - 'guide' alignment containing sequences derived from same dataset
+   *          as unaligned
    * @return
    */
   static boolean alignAsSameSequences(AlignmentI unaligned,
@@ -2544,8 +2620,7 @@ public class AlignmentUtils
       {
         return false;
       }
-      SequenceI alignedSeq = alignedDatasets.get(ds)
-              .get(0);
+      SequenceI alignedSeq = alignedDatasets.get(ds).get(0);
       int startCol = alignedSeq.findIndex(seq.getStart()); // 1..
       leftmost = Math.min(leftmost, startCol);
     }