JAL-3703 fix Gff3 shared InputStream with embedded FASTA data
[jalview.git] / src / jalview / io / AlignFile.java
index d221d37..b30fe33 100755 (executable)
  */
 package jalview.io;
 
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.util.MessageManager;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Enumeration;
@@ -34,6 +27,13 @@ import java.util.Hashtable;
 import java.util.List;
 import java.util.Vector;
 
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.util.MessageManager;
+
 /**
  * DOCUMENT ME!
  * 
@@ -41,6 +41,7 @@ import java.util.Vector;
  * @version $Revision$
  */
 public abstract class AlignFile extends FileParse
+        implements AlignmentFileReaderI, AlignmentFileWriterI
 {
   int noSeqs = 0;
 
@@ -65,15 +66,28 @@ public abstract class AlignFile extends FileParse
   /**
    * Properties to be added to generated alignment object
    */
-  protected Hashtable properties;
+  private Hashtable properties;
 
   long start;
 
   long end;
 
-  boolean jvSuffix = true;
+  /**
+   * true if parse() has been called
+   */
+  private boolean parseCalled = false;
+
+  private boolean parseImmediately = true;
 
-  private boolean parseCalled;
+  private boolean dataClosed = false;
+
+  /**
+   * @return if doParse() was called at construction time
+   */
+  protected boolean isParseImmediately()
+  {
+    return parseImmediately;
+  }
 
   /**
    * Creates a new AlignFile object.
@@ -86,38 +100,49 @@ public abstract class AlignFile extends FileParse
     initData();
   }
 
+  public AlignFile(SequenceI[] seqs)
+  {
+    this();
+    setSeqs(seqs);
+  }
+
   /**
    * Constructor which parses the data from a file of some specified type.
    * 
-   * @param inFile
-   *          Filename to read from.
-   * @param type
-   *          What type of file to read from (File, URL)
+   * @param dataObject
+   *          Filename, URL or Pasted String to read from.
+   * @param sourceType
+   *          What type of file to read from (File, URL, Pasted String)
    */
-  public AlignFile(String inFile, String type) throws IOException
+  public AlignFile(String dataObject, DataSourceType sourceType)
+          throws IOException
   {
-    this(true, inFile, type);
+    this(true, dataObject, sourceType);
   }
-  
+
   /**
-   * Constructor which (optionally delays) parsing of data from a file of some specified type.
+   * Constructor which (optionally delays) parsing of data from a file of some
+   * specified type.
    * 
    * @param parseImmediately
    *          if false, need to call 'doParse()' to begin parsing data
-   * @param inFile
-   *          Filename to read from.
-   * @param type
+   * @param dataObject
+   *          Filename, URL or Pasted String to read from.
+   * @param sourceType
    *          What type of file to read from (File, URL)
    * @throws IOException
    */
-  public AlignFile(boolean parseImmediately, String inFile, String type) throws IOException
+  public AlignFile(boolean parseImmediately, String dataObject,
+          DataSourceType sourceType) throws IOException
   {
-    super(inFile, type);
+    super(dataObject, sourceType);
     initData();
-    if (parseImmediately) {
+    if (parseImmediately)
+    {
       doParse();
     }
   }
+
   /**
    * Attempt to read from the position where some other parsing process left
    * off.
@@ -127,45 +152,66 @@ public abstract class AlignFile extends FileParse
    */
   public AlignFile(FileParse source) throws IOException
   {
-    this(true,source);
+    this(true, source);
   }
+
   /**
-   * Construct a new parser to read from the position where some other parsing process left
+   * Construct a new parser to read from the position where some other parsing
+   * process left
    * 
    * @param parseImmediately
    *          if false, need to call 'doParse()' to begin parsing data
    * @param source
    */
-  public AlignFile(boolean parseImmediately, FileParse source) throws IOException
+  public AlignFile(boolean parseImmediately, FileParse source)
+          throws IOException
+  {
+    this(parseImmediately, source, true);
+  }
+
+  public AlignFile(boolean parseImmediately, FileParse source,
+          boolean closeData) throws IOException
   {
     super(source);
     initData();
-    if (parseImmediately) {
-      doParse();
+
+    // stash flag in case parse needs to know if it has to autoconfigure or was
+    // configured after construction
+    this.parseImmediately = parseImmediately;
+
+    if (parseImmediately)
+    {
+      doParse(closeData);
     }
   }
+
   /**
    * called if parsing was delayed till after parser was constructed
+   * 
    * @throws IOException
    */
   public void doParse() throws IOException
   {
+    doParse(true);
+  }
+
+  public void doParse(boolean closeData) throws IOException
+  {
     if (parseCalled)
     {
       throw new IOException(
               "Implementation error: Parser called twice for same data.\n"
                       + "Need to call initData() again before parsing can be reattempted.");
     }
-    parseCalled=true;
+    parseCalled = true;
     parse();
-    // sets the index of each sequence in the alignment
-    for (int i = 0, c = seqs.size(); i < c; i++)
+    if (closeData && !dataClosed)
     {
-      seqs.get(i).setIndex(i);
+      dataIn.close();
+      dataClosed = true;
     }
   }
 
-
   /**
    * Return the seqs Vector
    */
@@ -182,6 +228,7 @@ public abstract class AlignFile extends FileParse
   /**
    * Return the Sequences in the seqs Vector as an array of Sequences
    */
+  @Override
   public SequenceI[] getSeqsAsArray()
   {
     SequenceI[] s = new SequenceI[seqs.size()];
@@ -200,6 +247,7 @@ public abstract class AlignFile extends FileParse
    * 
    * @param al
    */
+  @Override
   public void addAnnotations(AlignmentI al)
   {
     addProperties(al);
@@ -212,8 +260,7 @@ public abstract class AlignFile extends FileParse
        * Rna.GetBasePairsFromAlignmentAnnotation(annotations.elementAt(i));
        * Rna.HelixMap(pairArray);
        */
-      AlignmentAnnotation an = annotations
-              .elementAt(i);
+      AlignmentAnnotation an = annotations.elementAt(i);
       an.validateRangeAndDisplay();
       al.addAnnotation(an);
     }
@@ -265,7 +312,8 @@ public abstract class AlignFile extends FileParse
   {
     if (key == null)
     {
-      throw new Error(MessageManager.getString("error.implementation_error_cannot_have_null_alignment"));
+      throw new Error(MessageManager.getString(
+              "error.implementation_error_cannot_have_null_alignment"));
     }
     if (value == null)
     {
@@ -295,7 +343,7 @@ public abstract class AlignFile extends FileParse
     seqs = new Vector<SequenceI>();
     annotations = new Vector<AlignmentAnnotation>();
     seqGroups = new ArrayList<SequenceGroup>();
-    parseCalled=false;
+    parseCalled = false;
   }
 
   /**
@@ -304,7 +352,8 @@ public abstract class AlignFile extends FileParse
    * @param s
    *          DOCUMENT ME!
    */
-  protected void setSeqs(SequenceI[] s)
+  @Override
+  public void setSeqs(SequenceI[] s)
   {
     seqs = new Vector<SequenceI>();
 
@@ -320,16 +369,6 @@ public abstract class AlignFile extends FileParse
   public abstract void parse() throws IOException;
 
   /**
-   * Print out in alignment file format the Sequences in the seqs Vector.
-   */
-  public abstract String print();
-
-  public void addJVSuffix(boolean b)
-  {
-    jvSuffix = b;
-  }
-
-  /**
    * A general parser for ids.
    * 
    * @String id Id to be parsed
@@ -342,7 +381,15 @@ public abstract class AlignFile extends FileParse
     if (space > -1)
     {
       seq = new Sequence(id.substring(0, space), "");
-      seq.setDescription(id.substring(space + 1));
+      String desc = id.substring(space + 1);
+      seq.setDescription(desc);
+
+      /*
+       * it is tempting to parse Ensembl style gene description e.g.
+       * chromosome:GRCh38:7:140696688:140721955:1 and set the
+       * start position of the sequence, but this causes much confusion
+       * for reverse strand feature locations
+       */
     }
     else
     {
@@ -353,14 +400,21 @@ public abstract class AlignFile extends FileParse
   }
 
   /**
-   * Creates the output id. Adds prefix Uniprot format source|id And suffix
-   * Jalview /start-end
+   * Creates the output id. Adds prefix Uniprot format source|id and optionally
+   * suffix Jalview /start-end
+   * 
+   * @param jvsuffix
    * 
    * @String id Id to be parsed
    */
+  String printId(SequenceI seq, boolean jvsuffix)
+  {
+    return seq.getDisplayId(jvsuffix);
+  }
+
   String printId(SequenceI seq)
   {
-    return seq.getDisplayId(jvSuffix);
+    return printId(seq, true);
   }
 
   /**
@@ -374,8 +428,7 @@ public abstract class AlignFile extends FileParse
     {
       newickStrings = new Vector<String[]>();
     }
-    newickStrings.addElement(new String[]
-    { treeName, newickString });
+    newickStrings.addElement(new String[] { treeName, newickString });
   }
 
   protected int getTreeCount()
@@ -383,6 +436,7 @@ public abstract class AlignFile extends FileParse
     return newickStrings == null ? 0 : newickStrings.size();
   }
 
+  @Override
   public void addGroups(AlignmentI al)
   {
 
@@ -392,4 +446,8 @@ public abstract class AlignFile extends FileParse
     }
   }
 
+  protected void addSequence(SequenceI seq)
+  {
+    seqs.add(seq);
+  }
 }