JAL-4061 use alignment panel’s feature renderer to limit search to just the visible...
[jalview.git] / src / jalview / analysis / Finder.java
index f8cfcbf..21fa2f1 100644 (file)
  */
 package jalview.analysis;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 
+import com.stevesoft.pat.Regex;
+
 import jalview.api.AlignViewportI;
+import jalview.api.FeatureRenderer;
 import jalview.api.FinderI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.SequenceFeaturesI;
 import jalview.util.Comparison;
 import jalview.util.MapList;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
-import com.stevesoft.pat.Regex;
-
 /**
  * Implements the search algorithm for the Find dialog
  */
@@ -61,6 +63,11 @@ public class Finder implements FinderI
   private AlignViewportI viewport;
 
   /*
+   * feature renderer model - if available
+   */
+  FeatureRenderer frm = null;
+
+  /*
    * sequence index in alignment to search from
    */
   private int sequenceIndex;
@@ -72,6 +79,16 @@ public class Finder implements FinderI
   private int residueIndex;
 
   /*
+   * last feature matched when incrementally searching sequence features
+   */
+  private SequenceFeature lastFeature;
+
+  /*
+   * last sequenceIndex used when lastFeature was discovered
+   */
+  private int lastFeatureSequenceIndex;
+
+  /*
    * the true sequence position of the start of the 
    * last sequence searched (when 'ignore hidden regions' does not apply)
    */
@@ -99,30 +116,36 @@ public class Finder implements FinderI
 
   @Override
   public void findAll(String theSearchString, boolean matchCase,
-          boolean searchDescription, boolean ignoreHidden)
+          boolean searchDescription, boolean searchFeatureDesc,
+          boolean ignoreHidden)
   {
     /*
      * search from the start
      */
+    lastFeature = null;
+    lastFeatureSequenceIndex = 0;
     sequenceIndex = 0;
     residueIndex = -1;
 
-    doFind(theSearchString, matchCase, searchDescription, true,
-            ignoreHidden);
+    doFind(theSearchString, matchCase, searchDescription, searchFeatureDesc,
+            true, ignoreHidden);
 
     /*
      * reset to start for next search
      */
     sequenceIndex = 0;
     residueIndex = -1;
+    lastFeature = null;
+    lastFeatureSequenceIndex = 0;
   }
 
   @Override
   public void findNext(String theSearchString, boolean matchCase,
-          boolean searchDescription, boolean ignoreHidden)
+          boolean searchDescription, boolean searchFeatureDesc,
+          boolean ignoreHidden)
   {
-    doFind(theSearchString, matchCase, searchDescription, false,
-            ignoreHidden);
+    doFind(theSearchString, matchCase, searchDescription, searchFeatureDesc,
+            false, ignoreHidden);
 
     if (searchResults.isEmpty() && idMatches.isEmpty())
     {
@@ -131,6 +154,8 @@ public class Finder implements FinderI
        */
       sequenceIndex = 0;
       residueIndex = -1;
+      lastFeature = null;
+      lastFeatureSequenceIndex = 0;
     }
   }
 
@@ -144,7 +169,8 @@ public class Finder implements FinderI
    * @param ignoreHidden
    */
   protected void doFind(String theSearchString, boolean matchCase,
-          boolean searchDescription, boolean findAll, boolean ignoreHidden)
+          boolean searchDescription, boolean searchFeatureDesc,
+          boolean findAll, boolean ignoreHidden)
   {
     searchResults = new SearchResults();
     idMatches = new ArrayList<>();
@@ -169,7 +195,7 @@ public class Finder implements FinderI
     while ((!found || findAll) && sequenceIndex < end)
     {
       found = findNextMatch(searchString, searchPattern, searchDescription,
-              ignoreHidden);
+              searchFeatureDesc, ignoreHidden);
     }
   }
 
@@ -350,7 +376,8 @@ public class Finder implements FinderI
    * @return
    */
   protected boolean findNextMatch(String searchString, Regex searchPattern,
-          boolean matchDescription, boolean ignoreHidden)
+          boolean matchDescription, boolean matchFeatureDesc,
+          boolean ignoreHidden)
   {
     if (residueIndex < 0)
     {
@@ -380,6 +407,15 @@ public class Finder implements FinderI
       }
       else
       {
+        if (matchFeatureDesc)
+        {
+          matched = searchSequenceFeatures(residueIndex, searchPattern);
+          if (matched)
+          {
+            return true;
+          }
+          lastFeature = null;
+        }
         residueIndex = Integer.MAX_VALUE;
       }
     }
@@ -543,6 +579,69 @@ public class Finder implements FinderI
   }
 
   /**
+   * Searches for a match with the sequence features, and if found, adds the
+   * sequence to the list of match ids, (but not as a duplicate). Answers true
+   * if a match was added, else false.
+   * 
+   * @param seq
+   * @param searchPattern
+   * @return
+   */
+  protected boolean searchSequenceFeatures(int from, Regex searchPattern)
+  {
+    if (lastFeatureSequenceIndex != sequenceIndex)
+    {
+      lastFeatureSequenceIndex = sequenceIndex;
+      lastFeature = null;
+    }
+    SequenceI seq = viewport.getAlignment().getSequenceAt(sequenceIndex);
+    SequenceFeaturesI sf = seq.getFeatures();
+
+    // TODO - stash feature list and search incrementally
+    List<SequenceFeature> allFeatures = null;
+    if (frm != null)
+    {
+      allFeatures = frm.findFeaturesAtResidue(seq, seq.getStart(),
+              seq.getEnd());
+    }
+    else
+    {
+      allFeatures = sf.getAllFeatures(null);
+    }
+    // so we can check we are advancing when debugging
+    long fpos = 0;
+
+    for (SequenceFeature feature : allFeatures)
+    {
+      fpos++;
+      if (lastFeature != null)
+      {
+        // iterate till we find last feature matched
+        if (lastFeature != feature)
+        {
+          continue;
+        }
+        else
+        {
+          lastFeature = null;
+          continue;
+        }
+      }
+
+      if (searchPattern.search(feature.type) || (feature.description != null
+              && searchPattern.search(feature.description)))
+      {
+        searchResults.addResult(seq, feature.getBegin(), feature.getEnd());
+        lastFeature = feature;
+        return true;
+      }
+    }
+    residueIndex = Integer.MAX_VALUE;
+    lastFeature = null;
+    return false;
+  }
+
+  /**
    * Searches for a match with the sequence description, and if found, adds the
    * sequence to the list of match ids (but not as a duplicate). Answers true if
    * a match was added, else false.
@@ -634,4 +733,10 @@ public class Finder implements FinderI
   {
     return searchResults;
   }
+
+  @Override
+  public void setFeatureRenderer(FeatureRenderer featureRenderer)
+  {
+    frm = featureRenderer;
+  }
 }