Merge branch 'develop' into menard
[jalview.git] / src / jalview / io / FeaturesFile.java
index 64bb539..abf89d5 100755 (executable)
@@ -1,25 +1,35 @@
-/*\r
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)\r
- * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle\r
- * \r
- * This file is part of Jalview.\r
- * \r
- * Jalview is free software: you can redistribute it and/or\r
- * modify it under the terms of the GNU General Public License \r
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\r
- * \r
- * Jalview is distributed in the hope that it will be useful, but \r
- * WITHOUT ANY WARRANTY; without even the implied warranty \r
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
- * PURPOSE.  See the GNU General Public License for more details.\r
- * \r
- * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
- */\r
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
+ * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ */
 package jalview.io;\r
 \r
 import java.io.*;\r
 import java.util.*;\r
 \r
+import javax.xml.parsers.ParserConfigurationException;\r
+\r
+import org.xml.sax.SAXException;\r
+\r
+import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;\r
+import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed;\r
+import fr.orsay.lri.varna.exceptions.ExceptionPermissionDenied;\r
+import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;\r
+\r
+import jalview.analysis.SequenceIdMatcher;\r
 import jalview.datamodel.*;\r
 import jalview.schemes.*;\r
 import jalview.util.Format;\r
@@ -58,40 +68,79 @@ public class FeaturesFile extends AlignFile
    * \r
    * @throws IOException\r
    *           DOCUMENT ME!\r
+ * @throws SAXException \r
+ * @throws ParserConfigurationException \r
+ * @throws ExceptionFileFormatOrSyntax \r
+ * @throws ExceptionLoadingFailed \r
+ * @throws ExceptionPermissionDenied \r
+ * @throws InterruptedException \r
+ * @throws ExceptionUnmatchedClosingParentheses \r
    */\r
-  public FeaturesFile(String inFile, String type) throws IOException\r
+  public FeaturesFile(String inFile, String type) throws IOException, ExceptionFileFormatOrSyntax, ParserConfigurationException, SAXException, ExceptionPermissionDenied, ExceptionLoadingFailed, InterruptedException, ExceptionUnmatchedClosingParentheses\r
   {\r
     super(inFile, type);\r
   }\r
 \r
-  public FeaturesFile(FileParse source) throws IOException\r
+  public FeaturesFile(FileParse source) throws IOException, ExceptionFileFormatOrSyntax, ParserConfigurationException, SAXException, ExceptionPermissionDenied, ExceptionLoadingFailed, InterruptedException, ExceptionUnmatchedClosingParentheses\r
   {\r
     super(source);\r
   }\r
 \r
   /**\r
-   * The Application can render HTML, but the applet will remove HTML tags and\r
-   * replace links with %LINK% Both need to read links in HTML however\r
-   * \r
-   * @throws IOException\r
-   *           DOCUMENT ME!\r
+   * Parse GFF or sequence features file using case-independent matching, discarding URLs\r
+   * @param align - alignment/dataset containing sequences that are to be annotated\r
+   * @param colours - hashtable to store feature colour definitions\r
+   * @param removeHTML - process html strings into plain text\r
+   * @return true if features were added\r
    */\r
   public boolean parse(AlignmentI align, Hashtable colours,\r
           boolean removeHTML)\r
   {\r
-    return parse(align, colours, null, removeHTML);\r
+    return parse(align, colours, null, removeHTML, false);\r
   }\r
 \r
   /**\r
-   * The Application can render HTML, but the applet will remove HTML tags and\r
-   * replace links with %LINK% Both need to read links in HTML however\r
-   * \r
-   * @throws IOException\r
-   *           DOCUMENT ME!\r
+   * Parse GFF or sequence features file optionally using case-independent matching, discarding URLs\r
+   * @param align - alignment/dataset containing sequences that are to be annotated\r
+   * @param colours - hashtable to store feature colour definitions\r
+   * @param removeHTML - process html strings into plain text\r
+   * @param relaxedIdmatching - when true, ID matches to compound sequence IDs are allowed\r
+   * @return true if features were added\r
    */\r
-  public boolean parse(AlignmentI align, Hashtable colours,\r
-          Hashtable featureLink, boolean removeHTML)\r
+  public boolean parse(AlignmentI align, \r
+          Map colours, boolean removeHTML, boolean relaxedIdMatching)\r
+  {\r
+    return parse(align, colours, null, removeHTML, relaxedIdMatching);\r
+  }\r
+\r
+  /**\r
+   * Parse GFF or sequence features file optionally using case-independent matching\r
+   * @param align - alignment/dataset containing sequences that are to be annotated\r
+   * @param colours - hashtable to store feature colour definitions\r
+   * @param featureLink - hashtable to store associated URLs \r
+   * @param removeHTML - process html strings into plain text\r
+   * @return true if features were added\r
+   */\r
+  public boolean parse(AlignmentI align, Map colours,\r
+          Map featureLink, boolean removeHTML)\r
   {\r
+    return parse(align, colours, featureLink, removeHTML, false);\r
+  }\r
+\r
+  /**\r
+  /**\r
+   * Parse GFF or sequence features file \r
+   * @param align - alignment/dataset containing sequences that are to be annotated\r
+   * @param colours - hashtable to store feature colour definitions\r
+   * @param featureLink - hashtable to store associated URLs \r
+   * @param removeHTML - process html strings into plain text\r
+   * @param relaxedIdmatching - when true, ID matches to compound sequence IDs are allowed\r
+   * @return true if features were added\r
+   */\r
+  public boolean parse(AlignmentI align,\r
+          Map colours, Map featureLink, boolean removeHTML, boolean relaxedIdmatching)\r
+  {\r
+\r
     String line = null;\r
     try\r
     {\r
@@ -103,7 +152,7 @@ public class FeaturesFile extends AlignFile
       StringTokenizer st;\r
       SequenceFeature sf;\r
       String featureGroup = null, groupLink = null;\r
-      Hashtable typeLink = new Hashtable();\r
+      Map typeLink = new Hashtable();\r
       /**\r
        * when true, assume GFF style features rather than Jalview style.\r
        */\r
@@ -369,7 +418,7 @@ public class FeaturesFile extends AlignFile
             // Still possible this is an old Jalview file,\r
             // which does not have type colours at the beginning\r
             seqId = token = st.nextToken();\r
-            seq = align.findName(seqId, true);\r
+            seq = findName(align, seqId, relaxedIdmatching);\r
             if (seq != null)\r
             {\r
               desc = st.nextToken();\r
@@ -478,7 +527,7 @@ public class FeaturesFile extends AlignFile
 \r
           if (!token.equals("ID_NOT_SPECIFIED"))\r
           {\r
-            seq = align.findName(seqId = token, true);\r
+            seq = findName(align, seqId = token, relaxedIdmatching);\r
             st.nextToken();\r
           }\r
           else\r
@@ -548,88 +597,64 @@ public class FeaturesFile extends AlignFile
           GFFFile = false;\r
         }\r
       }\r
+      resetMatcher();\r
     } catch (Exception ex)\r
     {\r
       System.out.println(line);\r
       System.out.println("Error parsing feature file: " + ex + "\n" + line);\r
       ex.printStackTrace(System.err);\r
+      resetMatcher();\r
       return false;\r
     }\r
 \r
     return true;\r
   }\r
 \r
-  public void parseDescriptionHTML(SequenceFeature sf, boolean removeHTML)\r
+  private AlignmentI lastmatchedAl = null;\r
+\r
+  private SequenceIdMatcher matcher = null;\r
+\r
+  /**\r
+   * clear any temporary handles used to speed up ID matching\r
+   */\r
+  private void resetMatcher()\r
   {\r
-    if (sf.getDescription() == null)\r
+    lastmatchedAl = null;\r
+    matcher = null;\r
+  }\r
+\r
+  private SequenceI findName(AlignmentI align, String seqId,\r
+          boolean relaxedIdMatching)\r
+  {\r
+    SequenceI match = null;\r
+    if (relaxedIdMatching)\r
     {\r
-      return;\r
+      if (lastmatchedAl != align)\r
+      {\r
+        matcher = new SequenceIdMatcher(\r
+                (lastmatchedAl = align).getSequencesArray());\r
+      }\r
+      match = matcher.findIdMatch(seqId);\r
     }\r
-\r
-    if (removeHTML\r
-            && sf.getDescription().toUpperCase().indexOf("<HTML>") == -1)\r
+    else\r
     {\r
-      removeHTML = false;\r
+      match = align.findName(seqId, true);\r
     }\r
+    return match;\r
+  }\r
 \r
-    StringBuffer sb = new StringBuffer();\r
-    StringTokenizer st = new StringTokenizer(sf.getDescription(), "<");\r
-    String token, link;\r
-    int startTag;\r
-    String tag = null;\r
-    while (st.hasMoreElements())\r
+  public void parseDescriptionHTML(SequenceFeature sf, boolean removeHTML)\r
+  {\r
+    if (sf.getDescription() == null)\r
     {\r
-      token = st.nextToken("&>");\r
-      if (token.equalsIgnoreCase("html") || token.startsWith("/"))\r
-      {\r
-        continue;\r
-      }\r
-\r
-      tag = null;\r
-      startTag = token.indexOf("<");\r
-\r
-      if (startTag > -1)\r
-      {\r
-        tag = token.substring(startTag + 1);\r
-        token = token.substring(0, startTag);\r
-      }\r
-\r
-      if (tag != null && tag.toUpperCase().startsWith("A HREF="))\r
-      {\r
-        if (token.length() > 0)\r
-        {\r
-          sb.append(token);\r
-        }\r
-        link = tag.substring(tag.indexOf("\"") + 1, tag.length() - 1);\r
-        String label = st.nextToken("<>");\r
-        sf.addLink(label + "|" + link);\r
-        sb.append(label + "%LINK%");\r
-      }\r
-      else if (tag != null && tag.equalsIgnoreCase("br"))\r
-      {\r
-        sb.append(newline);\r
-      }\r
-      else if (token.startsWith("lt;"))\r
-      {\r
-        sb.append("<" + token.substring(3));\r
-      }\r
-      else if (token.startsWith("gt;"))\r
-      {\r
-        sb.append(">" + token.substring(3));\r
-      }\r
-      else if (token.startsWith("amp;"))\r
-      {\r
-        sb.append("&" + token.substring(4));\r
-      }\r
-      else\r
-      {\r
-        sb.append(token);\r
-      }\r
+      return;\r
     }\r
+    jalview.util.ParseHtmlBodyAndLinks parsed = new jalview.util.ParseHtmlBodyAndLinks(sf.getDescription(), removeHTML, newline);\r
 \r
-    if (removeHTML)\r
+    sf.description = (removeHTML) ? parsed.getNonHtmlContent() : sf.description;\r
+    for (String link:parsed.getLinks())\r
     {\r
-      sf.description = sb.toString();\r
+      sf.addLink(link);\r
     }\r
 \r
   }\r
@@ -929,19 +954,18 @@ public class FeaturesFile extends AlignFile
           out.append(source);\r
           out.append("\t");\r
           out.append(next[j].type);\r
-          out.append( "\t");\r
-          out.append(  next[j].begin );\r
           out.append("\t");\r
-          out.append(\r
-                  next[j].end);\r
-          out.append( "\t");\r
-          out.append( next[j].score);\r
-          out.append( "\t");\r
+          out.append(next[j].begin);\r
+          out.append("\t");\r
+          out.append(next[j].end);\r
+          out.append("\t");\r
+          out.append(next[j].score);\r
+          out.append("\t");\r
 \r
           if (next[j].getValue("STRAND") != null)\r
           {\r
             out.append(next[j].getValue("STRAND"));\r
-            out.append( "\t");\r
+            out.append("\t");\r
           }\r
           else\r
           {\r
@@ -956,7 +980,8 @@ public class FeaturesFile extends AlignFile
           {\r
             out.append(".");\r
           }\r
-          // TODO: verify/check GFF - should there be a /t here before attribute output ?\r
+          // TODO: verify/check GFF - should there be a /t here before attribute\r
+          // output ?\r
 \r
           if (next[j].getValue("ATTRIBUTES") != null)\r
           {\r
@@ -989,4 +1014,5 @@ public class FeaturesFile extends AlignFile
   {\r
     return "USE printGFFFormat() or printJalviewFormat()";\r
   }\r
+\r
 }\r