Merge branch 'releases/Release_2_10_Branch' into releases/Release_2_10_0_Branch
authorJim Procter <jprocter@issues.jalview.org>
Thu, 20 Oct 2016 11:21:33 +0000 (12:21 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Thu, 20 Oct 2016 11:21:33 +0000 (12:21 +0100)
41 files changed:
examples/testdata/localstruct.pdb [new file with mode: 0644]
help/html/features/structurechooser.html
help/html/features/viewingpdbs.html
help/html/releases.html
resources/embl_mapping.xml
resources/lang/Messages.properties
resources/lang/Messages_es.properties
resources/uniprot_mapping.xml
src/MCview/PDBChain.java
src/MCview/PDBViewer.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AppletJmol.java
src/jalview/datamodel/PDBEntry.java
src/jalview/datamodel/xdb/embl/EmblFile.java
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AppJmol.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/Desktop.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/StructureViewer.java
src/jalview/io/StructureFile.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/structures/models/AAStructureBindingModel.java
src/jalview/util/DBRefUtils.java
src/jalview/ws/dbsources/EmblXmlSource.java
src/jalview/ws/dbsources/Uniprot.java
src/jalview/ws/sifts/SiftsClient.java
test/jalview/datamodel/PDBEntryTest.java
test/jalview/ext/jmol/JmolParserTest.java
test/jalview/gui/StructureChooserTest.java
test/jalview/gui/StructureViewerTest.java [new file with mode: 0644]
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/ws/dbsources/UniprotTest.java
utils/BufferedLineReader.java [new file with mode: 0644]
utils/HelpLinksChecker.java
utils/MessageBundleChecker.java

diff --git a/examples/testdata/localstruct.pdb b/examples/testdata/localstruct.pdb
new file mode 100644 (file)
index 0000000..bdf13db
--- /dev/null
@@ -0,0 +1,51 @@
+ATOM   7013  N   ALA A 650     -32.039 -14.559  -3.977  1.00 97.16           N  
+ATOM   7014  CA  ALA A 650     -33.253 -15.372  -3.971  1.00101.29           C  
+ATOM   7015  C   ALA A 650     -32.928 -16.865  -4.014  1.00102.48           C  
+ATOM   7016  O   ALA A 650     -33.833 -17.704  -4.075  1.00102.31           O  
+ATOM   7017  CB  ALA A 650     -34.168 -14.987  -5.138  1.00 94.98           C  
+ATOM   7018  N   MET A 651     -31.636 -17.186  -3.978  1.00 98.86           N  
+ATOM   7019  CA  MET A 651     -31.164 -18.568  -4.040  1.00 95.81           C  
+ATOM   7020  C   MET A 651     -29.680 -18.618  -3.678  1.00 95.69           C  
+ATOM   7021  O   MET A 651     -28.847 -18.030  -4.367  1.00 93.27           O  
+ATOM   7022  CB  MET A 651     -31.414 -19.157  -5.435  1.00 94.70           C  
+ATOM   7023  CG  MET A 651     -31.097 -18.189  -6.581  1.00 93.74           C  
+ATOM   7024  SD  MET A 651     -31.780 -18.651  -8.198  1.00 87.88           S  
+ATOM   7025  CE  MET A 651     -30.854 -20.142  -8.594  1.00 81.80           C  
+ATOM   7026  N   LYS A 652     -29.355 -19.313  -2.589  1.00 95.36           N  
+ATOM   7027  CA  LYS A 652     -27.982 -19.355  -2.075  1.00 88.97           C  
+ATOM   7028  C   LYS A 652     -27.021 -20.133  -2.984  1.00 89.03           C  
+ATOM   7029  O   LYS A 652     -27.393 -21.143  -3.592  1.00 90.36           O  
+ATOM   7030  CB  LYS A 652     -27.953 -19.930  -0.656  1.00 85.99           C  
+ATOM   7031  N   ARG A 653     -25.784 -19.655  -3.070  1.00 83.57           N  
+ATOM   7032  CA  ARG A 653     -24.765 -20.305  -3.888  1.00 82.09           C  
+ATOM   7033  C   ARG A 653     -23.704 -20.963  -3.017  1.00 79.82           C  
+ATOM   7034  O   ARG A 653     -23.327 -20.431  -1.977  1.00 79.58           O  
+ATOM   7035  CB  ARG A 653     -24.115 -19.291  -4.831  1.00 84.10           C  
+ATOM   7036  CG  ARG A 653     -23.554 -18.068  -4.129  1.00 82.91           C  
+ATOM   7037  CD  ARG A 653     -23.098 -17.010  -5.124  1.00 84.28           C  
+ATOM   7038  NE  ARG A 653     -23.064 -15.675  -4.527  1.00 83.48           N  
+ATOM   7039  CZ  ARG A 653     -21.959 -15.047  -4.134  1.00 82.34           C  
+ATOM   7040  NH1 ARG A 653     -20.770 -15.621  -4.277  1.00 80.52           N  
+ATOM   7041  NH2 ARG A 653     -22.045 -13.836  -3.602  1.00 84.76           N  
+ATOM   7042  N   ARG A 654     -23.219 -22.122  -3.446  1.00 83.16           N  
+ATOM   7043  CA  ARG A 654     -22.263 -22.882  -2.647  1.00 84.47           C  
+ATOM   7044  C   ARG A 654     -20.845 -22.831  -3.207  1.00 82.72           C  
+ATOM   7045  O   ARG A 654     -20.611 -22.306  -4.294  1.00 84.47           O  
+ATOM   7046  CB  ARG A 654     -22.720 -24.337  -2.500  1.00 86.90           C  
+ATOM   7047  CG  ARG A 654     -22.700 -25.156  -3.785  1.00 90.62           C  
+ATOM   7048  CD  ARG A 654     -21.792 -26.372  -3.625  1.00 94.56           C  
+ATOM   7049  NE  ARG A 654     -22.296 -27.559  -4.313  1.00100.64           N  
+ATOM   7050  CZ  ARG A 654     -21.755 -28.770  -4.203  1.00101.77           C  
+ATOM   7051  NH1 ARG A 654     -20.690 -28.949  -3.433  1.00 99.43           N  
+ATOM   7052  NH2 ARG A 654     -22.277 -29.803  -4.858  1.00103.21           N  
+ATOM   7053  N   ARG A 655     -19.901 -23.377  -2.452  1.00 80.06           N  
+ATOM   7054  CA  ARG A 655     -18.522 -23.461  -2.899  1.00 81.57           C  
+ATOM   7055  C   ARG A 655     -18.393 -24.576  -3.925  1.00 85.28           C  
+ATOM   7056  O   ARG A 655     -19.120 -25.567  -3.865  1.00 87.19           O  
+ATOM   7057  CB  ARG A 655     -17.617 -23.772  -1.718  1.00 83.19           C  
+ATOM   7058  CG  ARG A 655     -17.720 -25.217  -1.265  1.00 86.49           C  
+ATOM   7059  CD  ARG A 655     -17.099 -25.402   0.093  1.00 85.01           C  
+ATOM   7060  NE  ARG A 655     -15.880 -24.617   0.223  1.00 85.24           N  
+ATOM   7061  CZ  ARG A 655     -15.123 -24.608   1.310  1.00 87.53           C  
+ATOM   7062  NH1 ARG A 655     -15.465 -25.351   2.354  1.00 87.44           N  
+ATOM   7063  NH2 ARG A 655     -14.028 -23.864   1.350  1.00 89.64           N  
index daa1ac3..fc71826 100644 (file)
@@ -31,7 +31,7 @@
   <p>
     The Structure Chooser interface allows you to interactively select
     which PDB structures to view for the currently selected set of
-    sequences. It's opened by selecting the <strong>"3D
+    sequences. It is opened by selecting the <strong>"3D
       Structure Data.."</strong> option from the Sequence ID panel's <a
       href="../menus/popupMenu.html">pop-up menu</a>. The dialog
     provides:
     <li>Association of structure data from a local file (in mmCIF
       or PDB format)</li>
   </ul>
-
+  <p>
+    <strong>Selecting and Viewing Structures</strong>
+  </p>
+  <p>
+    Once one or more structures have been selected, pressing the <strong>View</strong>
+    button will import them into <a
+      href="viewingpdbs.html#afterviewbutton">a new or existing
+      structure view</a>.
+  </p>
   <p>
     <strong>Automated discovery of structure data</strong>
   </p>
-  <p>After selecting "3D Structure Data ..", Jalview queries the PDB
-    via the PDBe SOLR Rest API provided by EMBL-EBI to discover PDB ids
+  <p>
+    After selecting "3D Structure Data ..", Jalview queries the PDB via
+    the PDBe SOLR Rest API provided by EMBL-EBI to discover PDB IDs
     associated with the sequence. It does this based on the sequence's
-    ID string, and any other associated database IDs.</p>
+    ID string, and any other associated database IDs. <br />
+    <br />
   <p>
-    <strong>Exploration of meta-data for available structures</strong>
+    <strong><a name="cachedstructview">Viewing existing
+        structures for your sequences</a></strong>
+  </p>
+  <p>
+    If you have already loaded 3D structure data for the selected
+    sequences, the structure chooser will first open with the <strong>Cached
+      Structures View</strong>. This view shows associations between each
+    sequence, and chains for 3D structure files already in memory. If
+    you want to download additional structures, select one of the other
+    options from the drop down menu.
   </p>
-  <p>Information on each structure available is displayed in columns
-    in the dialog box. By default, only the title, resolution and PDB
-    identifier are shown, but many more are provided by the PDBe. To
-    configure which ones are displayed, select the 'Configure Displayed
-    Columns' tab and tick the columns which you want to see.</p>
   <p>
     <strong>Selection of the best structure for each sequence</strong>
   </p>
   <p>Jalview can automatically select the best structures according
-    to meta-data provided by the PDB. By default, the 'Best Quality'
-    structure for each sequence will be selected, but clicking on the
-    drop down menu allows other criteria to be chosen, including
-    Resolution (only defined for X-Ray structures), Highest Protein
-    Chain etc. When 'Invert' is selected, structures are selected in
-    reverse order for the current criteria (e.g. worst quality rather
-    than best).</p>
+    to meta-data provided by the PDB. For alignments with no existing
+    structure data, the 'Best Quality' structure for each sequence will
+    by default be selected, but clicking on the drop down menu allows
+    other criteria to be chosen, including Resolution (only defined for
+    X-Ray structures), Highest Protein Chain etc. When 'Invert' is
+    selected, structures are selected in reverse order for the current
+    criteria (e.g. worst quality rather than best).</p>
   <p>
 
     <img src="schooser_main.png" style="width: 464px; height: 369px;">
        <p><img src="schooser_cached.png"> -->
     <br>The screenshot above shows the Structure Chooser interface
     along with the meta-data of auto-discovered structures for the
-    sample alignment. Note however that if no structures were
-    auto-discovered, a different interface for manual association will
-    be invoked as seen in the screenshot below.
+    sample alignment. If no structures were
+    auto-discovered, options for manually associating PDB records will be shown (see below).
+  <p>
+    <strong>Exploration of meta-data for available structures</strong>
+  </p>
+  <p>Information on each structure available is displayed in columns
+    in the dialog box. By default, only the title, resolution and PDB
+    identifier are shown, but many more are provided by the PDBe. To
+    configure which ones are displayed, select the 'Configure Displayed
+    Columns' tab and tick the columns which you want to see.</p>
   <p>
     <img src="schooser_enter-id.png"
       style="width: 464px; height: 369px;">
-  <p>
+      <br/>
     <strong>Manual selection/association of PDB files with
       Sequences</strong>
   </p>
       for your sequence. The PDB Rest API, provided by EMBL-EBI, is used
       to validate and fetch structure data.<br></li>
   </ul>
-  <p>
-    <strong>Viewing existing structures for your sequences</strong>
-  </p>
-  <p>
-    If you have previously associated structure data on the alignment,
-    selecting <strong>Cached PDB Entries</strong> from the drop down
-    menu allows you to select these structures for display.
-  </p>
 
   <p>
     <em>The Structure Chooser interface was introduced in Jalview
index 21caca1..f60da1a 100755 (executable)
       'highest resolution', simply choose another to pick structures in
       a different way.<br />
       <ul>
-        <li><strong>Viewing Cached Structures</strong><br />If you
-          have previously downloaded structures for your sequences, they
-          can be viewed by selecting the <strong>Cached PDB
-            Entries</strong> option from the drop down menu at the top of the
-          dialog box.</li>
+        <li><strong>Viewing Cached Structures</strong><br />If
+          previously downloaded structures are available for your
+          sequences, the structure chooser will automatically offer them
+          via the <strong>Cached PDB Entries</strong> view. If you wish
+          to download new structures, select one of the PDBe selection
+          criteria from the drop-down menu.</li>
       </ul></li>
     <li><strong>To view selected structures, click the <strong>"View"</strong>
         button.
         <li><a href="siftsmapping.html">SIFTS</a> records will also
           be downloaded for mapping UniProt protein sequence data to PDB
           coordinates.</li>
+        <li>A new structure viewer will open, or you will be
+          prompted to add structures to existing viewers (see <a
+          href="#afterviewbutton">below</a> for details).
+        </li>
       </ul></li>
   </ol>
-
+  <p>
+  <strong>Structure Viewers in the Jalview Desktop</strong><br/>
   The
   <a href="jmol.html">Jmol viewer</a> has been included since Jalview
   2.3. Jalview 2.8.2 included support for
     the <a href="xsspannotation.html">Annotation from Structure</a> page
     for more information.
   </p>
-
   <p>
-    If a <strong>single</strong> PDB structure is selected, one of the
-    following will happen:
+    <strong><a name="afterviewbutton">After pressing the
+        'View' button in the Structure Chooser</a></strong><br /> The behaviour of
+    the 'View' button depends on the number of structures selected, and
+    whether structure views already exist for the selected structures or
+    aligned sequences.
+  </p>
+  <p>
+    If multiple structures are selected, then Jalview will always create
+    a new structure view. The selected structures will be imported into
+    this view, and superposed with the matched positions from the
+    aligned sequences.<br /> If a <strong>single</strong> PDB structure
+    is selected, one of the following will happen:
   </p>
 
   <ul>
 
     <li>If another structure is already shown for the current
       alignment, then you will be asked if you want to add and <a
-      href="jmol.html#align">align this structure</a> to the structure
-      in the existing view. (<em>new feature in Jalview 2.6</em>).
-    </li>
+      href="jmol.html#align"></a> to
+    the structure in the existing view. (<em>new feature in Jalview
+      2.6</em>).
+  </li>
 
     <li>If the structure is already shown, then you will be
       prompted to associate the sequence with an existing view of the
index c547ffb..11c7f58 100755 (executable)
     <tr>
       <td width="60" nowrap>
         <div align="center">
+          <strong><a name="Jalview.2.10.0b1">2.10.0b1</a><br />
+            <em>25/10/2016</em></strong>
+        </div>
+      </td>
+      <td><em>Application</em>
+        <ul>
+          <li>3D Structure chooser opens with 'Cached structures' view if structures already loaded</li>
+        </ul></td>
+      <td>
+        <div align="left">
+          <em>Application</em>
+          <ul>
+            <li>Cannot import or associated local PDB files without a PDB ID HEADER line</li>
+            <li>RMSD is not output in Jmol console when superposition is performed</li> 
+            <li>Drag and drop of URL from Browser fails for Linux and OSX versions earlier than El Capitan</li>
+            <li>ENA client ignores invalid content from ENA server</li>
+            <li>Exceptions are not raised when ENA client attempts to fetch non-existent IDs via Fetch DB Refs UI option</li>
+            <li>Exceptions are not raised when a new view is created on the alignment</li>
+          </ul>
+          <em>New Known Issues</em>
+          <ul><li>Drag and drop from URL links in browsers do not work on Windows</li></ul>
+          <em>Build and deployment</em>
+          <ul><li>URL link checker now copes with multi-line anchor tags</li></ul>
+        </div>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" nowrap>
+        <div align="center">
           <strong><a name="Jalview.2.10.0">2.10.0</a><br /> <em>06/10/2016</em></strong>
         </div>
       </td>
index 01b921a..3b80821 100644 (file)
@@ -28,6 +28,9 @@
        -->
        <class name="jalview.datamodel.xdb.embl.EmblFile">
                <map-to xml="ROOT"/>
+               <field name="text" type="string">
+                       <bind-xml node="text"/>
+               </field>
                <field name="entries" type="jalview.datamodel.xdb.embl.EmblEntry" collection="vector">
                        <bind-xml name="entry"/>
                </field>
index 378fb64..7a1f064 100644 (file)
@@ -1128,7 +1128,7 @@ status.das_feature_fetching_complete = DAS Feature Fetching Complete
 status.fetching_db_refs = Fetching db refs
 status.loading_cached_pdb_entries = Loading Cached PDB Entries
 status.searching_for_pdb_structures = Searching for PDB Structures
-status.opening_file = opening file
+status.opening_file_for = opening file for
 status.colouring_chimera = Colouring Chimera
 label.font_doesnt_have_letters_defined = Font doesn't have letters defined\nso cannot be used\nwith alignment data
 label.font_too_small = Font size is too small
index 7bea515..cf36638 100644 (file)
@@ -1127,7 +1127,7 @@ action.yes=S
 label.export_settings=Exportar Ajustes
 label.linked_view_title=Vista vinculada de cDNA y proteína
 label.couldnt_read_data=No se pudo leer los datos
-status.opening_file=abriendo fichero
+status.opening_file_for=abriendo fichero para
 label.except_selected_sequences=Todo excepto secuencias seleccionadas
 label.structure_chooser_no_of_structures=Selector de Estructuras - {0} Encontró ({1})
 label.search_filter=filtro de búsqueda
index a8634af..4a981ad 100755 (executable)
@@ -80,7 +80,7 @@
         <class name="jalview.datamodel.PDBEntry">
                <field name="type"><bind-xml node="attribute"/></field>
                <field name="id"><bind-xml node="attribute"/></field>
-               <field name="property" collection="hashtable">
+               <field name="props" collection="hashtable">
                        <bind-xml name="property">
                           <class name="org.exolab.castor.mapping.MapItem">
                              <field name="key">
index b74f101..7774dac 100755 (executable)
@@ -199,7 +199,8 @@ public class PDBChain
     }
     for (int i = 0; i < features.length; i++)
     {
-      if (features[i].getFeatureGroup().equals(pdbid))
+      if (features[i].getFeatureGroup() != null
+              && features[i].getFeatureGroup().equals(pdbid))
       {
         SequenceFeature tx = new SequenceFeature(features[i]);
         tx.setBegin(1 + residues.elementAt(tx.getBegin() - offset).atoms
index 8f014a4..eaa33df 100755 (executable)
@@ -128,18 +128,17 @@ public class PDBViewer extends JInternalFrame implements Runnable
       worker.start();
     }
 
-    if (pdbentry.getProperty() != null)
+    String method = (String) pdbentry.getProperty("method");
+    if (method != null)
     {
-      if (pdbentry.getProperty().get("method") != null)
-      {
-        title.append(" Method: ");
-        title.append(pdbentry.getProperty().get("method"));
-      }
-      if (pdbentry.getProperty().get("chains") != null)
-      {
-        title.append(" Chain:");
-        title.append(pdbentry.getProperty().get("chains"));
-      }
+      title.append(" Method: ");
+      title.append(method);
+    }
+    String ch = (String) pdbentry.getProperty("chains");
+    if (ch != null)
+    {
+      title.append(" Chain:");
+      title.append(ch);
     }
     Desktop.addInternalFrame(this, title.toString(), 400, 400);
   }
index 80984c1..122afa8 100644 (file)
@@ -102,7 +102,6 @@ import java.net.URLEncoder;
 import java.util.Arrays;
 import java.util.Deque;
 import java.util.HashMap;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
@@ -4024,12 +4023,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       }
       if (needtoadd)
       {
-        // make a note of the access mode and add
-        if (pdbentry.getProperty() == null)
-        {
-          pdbentry.setProperty(new Hashtable());
-        }
-        pdbentry.getProperty().put("protocol", protocol);
+        pdbentry.setProperty("protocol", protocol);
         toaddpdb.addPDBId(pdbentry);
         alignPanel.getStructureSelectionManager()
                 .registerPDBEntry(pdbentry);
@@ -4080,7 +4074,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     if (protocol == null || protocol.trim().length() == 0
             || protocol.equals("null"))
     {
-      protocol = (String) pdb.getProperty().get("protocol");
+      protocol = (String) pdb.getProperty("protocol");
       if (protocol == null)
       {
         System.err.println("Couldn't work out protocol to open structure: "
index 264ac14..b925284 100644 (file)
@@ -60,12 +60,9 @@ import java.awt.event.KeyListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.util.ArrayList;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Vector;
 
-import org.jmol.util.Logger;
-
 public class AppletJmol extends EmbmenuFrame implements
 // StructureListener,
         KeyListener, ActionListener, ItemListener
@@ -271,7 +268,6 @@ public class AppletJmol extends EmbmenuFrame implements
       jmb.allocateViewer(renderPanel, true, ap.av.applet.getName()
               + "_jmol_", ap.av.applet.getDocumentBase(),
               ap.av.applet.getCodeBase(), "-applet", scriptWindow, null);
-      Logger.setLogLevel(Logger.LEVEL_WARN);
     } catch (Exception e)
     {
       System.err
@@ -293,11 +289,8 @@ public class AppletJmol extends EmbmenuFrame implements
         closeViewer();
       }
     });
-    if (pdbentry.getProperty() == null)
-    {
-      pdbentry.setProperty(new Hashtable());
-      pdbentry.getProperty().put("protocol", protocol);
-    }
+    pdbentry.setProperty("protocol", protocol);
+
     if (pdbentry.getFile() != null)
     {
       // import structure data from pdbentry.getFile based on given protocol
index 1403595..6a6ccd0 100755 (executable)
@@ -22,10 +22,20 @@ package jalview.datamodel;
 
 import jalview.util.CaseInsensitiveString;
 
+import java.util.Collections;
+import java.util.Enumeration;
 import java.util.Hashtable;
 
 public class PDBEntry
 {
+
+  /**
+   * constant for storing chain code in properties table
+   */
+  private static final String CHAIN_ID = "chain_code";
+
+  private Hashtable<String, Object> properties;
+
   private static final int PDB_ID_LENGTH = 4;
 
   private String file;
@@ -67,12 +77,6 @@ public class PDBEntry
     }
   }
 
-  /**
-   * constant for storing chain code in properties table
-   */
-  private static final String CHAIN_ID = "chain_code";
-
-  Hashtable properties;
 
   /**
    * Answers true if obj is a PDBEntry with the same id and chain code (both
@@ -137,13 +141,13 @@ public class PDBEntry
   /**
    * @param pdbId
    * @param chain
-   * @param type
+   * @param entryType
    * @param filePath
    */
-  void init(String pdbId, String chain, PDBEntry.Type type, String filePath)
+  void init(String pdbId, String chain, PDBEntry.Type entryType, String filePath)
   {
     this.id = pdbId;
-    this.type = type == null ? null : type.toString();
+    this.type = entryType == null ? null : entryType.toString();
     this.file = filePath;
     setChainCode(chain);
   }
@@ -160,7 +164,7 @@ public class PDBEntry
     id = entry.id;
     if (entry.properties != null)
     {
-      properties = (Hashtable) entry.properties.clone();
+      properties = (Hashtable<String, Object>) entry.properties.clone();
     }
   }
 
@@ -193,9 +197,9 @@ public class PDBEntry
     init(pdbId, chainCode, null, null);
   }
 
-  public void setFile(String file)
+  public void setFile(String f)
   {
-    this.file = file;
+    this.file = f;
   }
 
   public String getFile()
@@ -228,14 +232,33 @@ public class PDBEntry
     return id;
   }
 
-  public void setProperty(Hashtable property)
+  public void setProperty(String key, Object value)
   {
-    this.properties = property;
+    if (this.properties == null)
+    {
+      this.properties = new Hashtable<String, Object>();
+    }
+    properties.put(key, value);
   }
 
-  public Hashtable getProperty()
+  public Object getProperty(String key)
   {
-    return properties;
+    return properties == null ? null : properties.get(key);
+  }
+
+  /**
+   * Returns an enumeration of the keys of this object's properties (or an empty
+   * enumeration if it has no properties)
+   * 
+   * @return
+   */
+  public Enumeration<String> getProperties()
+  {
+    if (properties == null)
+    {
+      return Collections.emptyEnumeration();
+    }
+    return properties.keys();
   }
 
   /**
@@ -248,24 +271,37 @@ public class PDBEntry
             : properties.get(CHAIN_ID).toString();
   }
 
+  /**
+   * Sets a non-case-sensitive property for the given chain code. Two PDBEntry
+   * objects which differ only in the case of their chain code are considered
+   * equal. This avoids duplication of objects in lists of PDB ids.
+   * 
+   * @param chainCode
+   */
   public void setChainCode(String chainCode)
   {
-    if (properties == null)
+    if (chainCode == null)
     {
-      if (chainCode == null)
-      {
-        // nothing to do.
-        return;
-      }
-      properties = new Hashtable();
+      deleteProperty(CHAIN_ID);
     }
-    if (chainCode == null)
+    else
+    {
+      setProperty(CHAIN_ID, new CaseInsensitiveString(chainCode));
+    }
+  }
+
+  /**
+   * Deletes the property with the given key, and returns the deleted value (or
+   * null)
+   */
+  Object deleteProperty(String key)
+  {
+    Object result = null;
+    if (properties != null)
     {
-      properties.remove(CHAIN_ID);
-      return;
+      result = properties.remove(key);
     }
-    // update property for non-null chainCode
-    properties.put(CHAIN_ID, new CaseInsensitiveString(chainCode));
+    return result;
   }
 
   @Override
@@ -275,6 +311,35 @@ public class PDBEntry
   }
 
   /**
+   * Getter provided for Castor binding only. Application code should call
+   * getProperty() or getProperties() instead.
+   * 
+   * @deprecated
+   * @see #getProperty(String)
+   * @see #getProperties()
+   * @see jalview.ws.dbsources.Uniprot#getUniprotEntries
+   * @return
+   */
+  @Deprecated
+  public Hashtable<String, Object> getProps()
+  {
+    return properties;
+  }
+
+  /**
+   * Setter provided for Castor binding only. Application code should call
+   * setProperty() instead.
+   * 
+   * @deprecated
+   * @return
+   */
+  @Deprecated
+  public void setProps(Hashtable<String, Object> props)
+  {
+    properties = props;
+  }
+
+  /**
    * Answers true if this object is either equivalent to, or can be 'improved'
    * by, the given entry.
    * <p>
@@ -285,7 +350,7 @@ public class PDBEntry
    * @param newEntry
    * @return true if modifications were made
    */
-  protected boolean updateFrom(PDBEntry newEntry)
+  public boolean updateFrom(PDBEntry newEntry)
   {
     if (this.equals(newEntry))
     {
@@ -299,7 +364,7 @@ public class PDBEntry
     }
 
     /*
-     * id (less any chain code) has to match (ignoring case)
+     * id has to match (ignoring case)
      */
     if (!getId().equalsIgnoreCase(newId))
     {
@@ -356,26 +421,20 @@ public class PDBEntry
     }
 
     /*
-     * copy any new properties; notice this may include chain_code,
-     * but we excluded differing chain codes earlier
+     * copy any new or modified properties
      */
-    if (newEntry.getProperty() != null)
+    Enumeration<String> newProps = newEntry.getProperties();
+    while (newProps.hasMoreElements())
     {
-      if (properties == null)
+      /*
+       * copy properties unless value matches; this defends against changing
+       * the case of chain_code which is wrapped in a CaseInsensitiveString
+       */
+      String key = newProps.nextElement();
+      Object value = newEntry.getProperty(key);
+      if (!value.equals(getProperty(key)))
       {
-        properties = new Hashtable();
-      }
-      for (Object p : newEntry.getProperty().keySet())
-      {
-        /*
-         * copy properties unless value matches; this defends against changing
-         * the case of chain_code which is wrapped in a CaseInsensitiveString
-         */
-        Object value = newEntry.getProperty().get(p);
-        if (!value.equals(properties.get(p)))
-        {
-          properties.put(p, newEntry.getProperty().get(p));
-        }
+        setProperty(key, value);
       }
     }
     return true;
index 69870b6..1dd854a 100644 (file)
@@ -46,6 +46,8 @@ public class EmblFile
 
   Vector<EmblError> errors;
 
+  String text;
+
   /**
    * @return the entries
    */
@@ -152,6 +154,10 @@ public class EmblFile
    */
   static void canonicaliseDbRefs(EmblFile record)
   {
+    if (record.getEntries() == null)
+    {
+      return;
+    }
     for (EmblEntry entry : record.getEntries())
     {
       if (entry.getDbRefs() != null)
@@ -183,4 +189,14 @@ public class EmblFile
       }
     }
   }
+
+  public String getText()
+  {
+    return text;
+  }
+
+  public void setText(String text)
+  {
+    this.text = text;
+  }
 }
index b7b1985..5903f69 100644 (file)
@@ -50,6 +50,10 @@ import com.stevesoft.pat.Regex;
  */
 abstract class EnsemblRestClient extends EnsemblSequenceFetcher
 {
+  private static final int DEFAULT_READ_TIMEOUT = 5 * 60 * 1000; // 5 minutes
+
+  private static final int CONNECT_TIMEOUT_MS = 10 * 1000; // 10 seconds
+
   /*
    * update these constants when Jalview has been checked / updated for
    * changes to Ensembl REST API
@@ -186,7 +190,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
    */
   private boolean checkEnsembl()
   {
-    HttpURLConnection conn = null;
+    BufferedReader br = null;
     try
     {
       // note this format works for both ensembl and ensemblgenomes
@@ -196,8 +200,9 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
 
       /*
        * expect {"ping":1} if ok
+       * if ping takes more than 2 seconds to respond, treat as if unavailable
        */
-      BufferedReader br = getHttpResponse(ping, null);
+      br = getHttpResponse(ping, null, 2 * 1000);
       JSONParser jp = new JSONParser();
       JSONObject val = (JSONObject) jp.parse(br);
       String pingString = val.get("ping").toString();
@@ -208,9 +213,15 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
               + t.getMessage());
     } finally
     {
-      if (conn != null)
+      if (br != null)
       {
-        conn.disconnect();
+        try
+        {
+          br.close();
+        } catch (IOException e)
+        {
+          // ignore
+        }
       }
     }
     return false;
@@ -239,17 +250,34 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
   }
 
   /**
+   * Gets a reader to the HTTP response, using the default read timeout of 5
+   * minutes
+   * 
+   * @param url
+   * @param ids
+   * @return
+   * @throws IOException
+   */
+  protected BufferedReader getHttpResponse(URL url, List<String> ids)
+          throws IOException
+  {
+    return getHttpResponse(url, ids, DEFAULT_READ_TIMEOUT);
+  }
+
+  /**
    * Writes the HTTP request and gets the response as a reader.
    * 
    * @param url
    * @param ids
    *          written as Json POST body if more than one
+   * @param readTimeout
+   *          in milliseconds
    * @return
    * @throws IOException
    *           if response code was not 200, or other I/O error
    */
-  protected BufferedReader getHttpResponse(URL url, List<String> ids)
-          throws IOException
+  protected BufferedReader getHttpResponse(URL url, List<String> ids,
+          int readTimeout) throws IOException
   {
     // long now = System.currentTimeMillis();
     HttpURLConnection connection = (HttpURLConnection) url.openConnection();
@@ -269,6 +297,9 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
     connection.setDoInput(true);
     connection.setDoOutput(multipleIds);
 
+    connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
+    connection.setReadTimeout(readTimeout);
+
     if (multipleIds)
     {
       writePostBody(connection, ids);
index 2f3464b..fbac400 100644 (file)
@@ -43,6 +43,7 @@ import java.awt.event.ComponentListener;
 import java.io.File;
 import java.net.URL;
 import java.security.AccessControlException;
+import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -93,11 +94,6 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   boolean loadedInline;
 
-  /**
-   * current set of model filenames loaded in the Jmol instance
-   */
-  String[] modelFileNames = null;
-
   StringBuffer resetLastRes = new StringBuffer();
 
   public Viewer viewer;
@@ -258,8 +254,12 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       } catch (InterruptedException i)
       {
       }
-      ;
     }
+
+    /*
+     * get the distinct structure files modelled
+     * (a file with multiple chains may map to multiple sequences)
+     */
     String[] files = getPdbFile();
     if (!waitForFileLoad(files))
     {
@@ -307,6 +307,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
        * 'matched' array will hold 'true' for visible alignment columns where
        * all sequences have a residue with a mapping to the PDB structure
        */
+      // TODO could use a BitSet for matched
       boolean matched[] = new boolean[alignment.getWidth()];
       for (int m = 0; m < matched.length; m++)
       {
@@ -352,6 +353,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
        * generate select statements to select regions to superimpose structures
        */
       {
+        // TODO extract method to construct selection statements
         for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
         {
           String chainCd = ":" + structures[pdbfnum].chain;
@@ -419,6 +421,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
         }
       }
       StringBuilder command = new StringBuilder(256);
+      // command.append("set spinFps 10;\n");
+
       for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
       {
         if (pdbfnum == refStructure || selcom[pdbfnum] == null
@@ -449,6 +453,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       }
       if (selectioncom.length() > 0)
       {
+        // TODO is performing selectioncom redundant here? is done later on
         // System.out.println("Select regions:\n" + selectioncom.toString());
         evalStateCommand("select *; cartoons off; backbone; select ("
                 + selectioncom.toString() + "); cartoons; ");
@@ -647,15 +652,15 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     }
     if (modelFileNames == null)
     {
-      String mset[] = new String[viewer.ms.mc];
-      _modelFileNameMap = new int[mset.length];
+      List<String> mset = new ArrayList<String>();
+      _modelFileNameMap = new int[viewer.ms.mc];
       String m = viewer.ms.getModelFileName(0);
       if (m != null)
       {
-        mset[0] = m;
+        String filePath = m;
         try
         {
-          mset[0] = new File(m).getAbsolutePath();
+          filePath = new File(m).getAbsolutePath();
         } catch (AccessControlException x)
         {
           // usually not allowed to do this in applet
@@ -663,39 +668,43 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
                   .println("jmolBinding: Using local file string from Jmol: "
                           + m);
         }
-        if (mset[0].indexOf("/file:") != -1)
+        if (filePath.indexOf("/file:") != -1)
         {
           // applet path with docroot - discard as format won't match pdbfile
-          mset[0] = m;
+          filePath = m;
         }
+        mset.add(filePath);
         _modelFileNameMap[0] = 0; // filename index for first model is always 0.
       }
       int j = 1;
-      for (int i = 1; i < mset.length; i++)
+      for (int i = 1; i < viewer.ms.mc; i++)
       {
         m = viewer.ms.getModelFileName(i);
-        mset[j] = m;
+        String filePath = m;
         if (m != null)
         {
           try
           {
-            mset[j] = new File(m).getAbsolutePath();
+            filePath = new File(m).getAbsolutePath();
           } catch (AccessControlException x)
           {
             // usually not allowed to do this in applet, so keep raw handle
             // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
           }
         }
-        _modelFileNameMap[j] = i; // record the model index for the filename
-        // skip any additional models in the same file (NMR structures)
-        if ((mset[j] == null ? mset[j] != mset[j - 1]
-                : (mset[j - 1] == null || !mset[j].equals(mset[j - 1]))))
+
+        /*
+         * add this model unless it is read from a structure file we have
+         * already seen (example: 2MJW is an NMR structure with 10 models)
+         */
+        if (!mset.contains(filePath))
         {
+          mset.add(filePath);
+          _modelFileNameMap[j] = i; // record the model index for the filename
           j++;
         }
       }
-      modelFileNames = new String[j];
-      System.arraycopy(mset, 0, modelFileNames, 0, j);
+      modelFileNames = mset.toArray(new String[mset.size()]);
     }
     return modelFileNames;
   }
index 1800ef0..b2ba256 100644 (file)
@@ -124,12 +124,11 @@ public class JmolParser extends StructureFile implements JmolStatusListener
       try
       {
         /*
-         * params -o (output to sysout) -i (no info logging, less verbose)
-         * -n (nodisplay) -x (exit when finished)
+         * params -o (output to sysout) -n (nodisplay) -x (exit when finished)
          * see http://wiki.jmol.org/index.php/Jmol_Application
          */
         viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
-                null, "-x -o -n -i", this);
+                null, "-x -o -n", this);
         // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
         viewer.setBooleanProperty("defaultStructureDSSP", true);
       } catch (ClassCastException x)
@@ -151,7 +150,17 @@ public class JmolParser extends StructureFile implements JmolStatusListener
       List<SequenceI> prot = new ArrayList<SequenceI>();
       PDBChain tmpchain;
       String pdbId = (String) ms.getInfo(0, "title");
-      setId(pdbId);
+
+      if (pdbId == null)
+      {
+        setId(safeName(getDataName()));
+        setPDBIdAvailable(false);
+      }
+      else
+      {
+        setId(pdbId);
+        setPDBIdAvailable(true);
+      }
       List<Atom> significantAtoms = convertSignificantAtoms(ms);
       for (Atom tmpatom : significantAtoms)
       {
@@ -166,7 +175,7 @@ public class JmolParser extends StructureFile implements JmolStatusListener
           tmpchain.atoms.addElement(tmpatom);
         } catch (Exception e)
         {
-          tmpchain = new PDBChain(pdbId, tmpatom.chain);
+          tmpchain = new PDBChain(getId(), tmpatom.chain);
           getChains().add(tmpchain);
           tmpchain.atoms.addElement(tmpatom);
         }
@@ -177,10 +186,6 @@ public class JmolParser extends StructureFile implements JmolStatusListener
       makeResidueList();
       makeCaBondList();
 
-      if (getId() == null)
-      {
-        setId(safeName(getDataName()));
-      }
       for (PDBChain chain : getChains())
       {
         SequenceI chainseq = postProcessChain(chain);
index 944ef52..7ba9186 100644 (file)
@@ -101,11 +101,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   private String lastCommand;
 
-  /*
-   * current set of model filenames loaded
-   */
-  String[] modelFileNames = null;
-
   String lastHighlightCommand;
 
   /*
index 672f7ac..63620e5 100644 (file)
@@ -108,6 +108,7 @@ import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.DataFlavor;
 import java.awt.datatransfer.StringSelection;
 import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DnDConstants;
 import java.awt.dnd.DropTargetDragEvent;
 import java.awt.dnd.DropTargetDropEvent;
 import java.awt.dnd.DropTargetEvent;
@@ -4814,6 +4815,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void drop(DropTargetDropEvent evt)
   {
+    // JAL-1552 - acceptDrop required before getTransferable call for
+    // Java's Transferable for native dnd
+    evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
     java.util.List<String> files = new ArrayList<String>(), protocols = new ArrayList<String>();
 
index 24604ed..1c0dfe6 100644 (file)
@@ -79,6 +79,9 @@ import javax.swing.event.MenuListener;
 
 public class AppJmol extends StructureViewerBase
 {
+  // ms to wait for Jmol to load files
+  private static final int JMOL_LOAD_TIMEOUT = 20000;
+
   private static final String SPACE = " ";
 
   private static final String BACKSLASH = "\"";
@@ -309,12 +312,10 @@ public class AppJmol extends StructureViewerBase
     jmb.setColourBySequence(true);
     setSize(400, 400); // probably should be a configurable/dynamic default here
     initMenus();
-    worker = null;
-    {
-      addingStructures = false;
-      worker = new Thread(this);
-      worker.start();
-    }
+    addingStructures = false;
+    worker = new Thread(this);
+    worker.start();
+
     this.addInternalFrameListener(new InternalFrameAdapter()
     {
       @Override
@@ -381,10 +382,7 @@ public class AppJmol extends StructureViewerBase
       scriptWindow.setVisible(false);
     }
 
-    /*
-     * -i for no info logging (less verbose)
-     */
-    jmb.allocateViewer(renderPanel, true, "", null, null, "-i",
+    jmb.allocateViewer(renderPanel, true, "", null, null, "",
             scriptWindow, null);
     // jmb.newJmolPopup("Jmol");
     if (command == null)
@@ -554,7 +552,7 @@ public class AppJmol extends StructureViewerBase
     }
 
     // need to wait around until script has finished
-    int waitMax = 5000; // give up after 5 seconds
+    int waitMax = JMOL_LOAD_TIMEOUT;
     int waitFor = 35;
     int waitTotal = 0;
     while (addingStructures ? lastnotify >= jmb.getLoadNotifiesHandled()
@@ -571,7 +569,13 @@ public class AppJmol extends StructureViewerBase
       }
       if (waitTotal > waitMax)
       {
-        System.err.println("Timed out waiting for Jmol to load files");
+        System.err
+                .println("Timed out waiting for Jmol to load files after "
+                        + waitTotal + "ms");
+//        System.err.println("finished: " + jmb.isFinishedInit()
+//                + "; loaded: " + Arrays.toString(jmb.getPdbFile())
+//                + "; files: " + files.toString());
+        jmb.getPdbFile();
         break;
       }
     }
index 592f56c..c30a418 100644 (file)
@@ -660,7 +660,8 @@ public class ChimeraViewFrame extends StructureViewerBase
           {
             int pos = filePDBpos.get(num).intValue();
             long startTime = startProgressBar("Chimera "
-                    + MessageManager.getString("status.opening_file"));
+                    + MessageManager.getString("status.opening_file_for")
+                    + " " + pe.getId());
             jmb.openFile(pe);
             jmb.addSequence(pos, jmb.getSequence()[pos]);
             File fl = new File(pe.getFile());
index f99af34..3f457ea 100644 (file)
@@ -941,6 +941,9 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   public void drop(DropTargetDropEvent evt)
   {
     boolean success = true;
+    // JAL-1552 - acceptDrop required before getTransferable call for
+    // Java's Transferable for native dnd
+    evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
     java.util.List<String> files = new ArrayList<String>();
     java.util.List<String> protocols = new ArrayList<String>();
@@ -3180,7 +3183,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     {
       // Works on Windows and MacOSX
       Cache.log.debug("Drop handled as javaFileListFlavor");
-      evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
       for (Object file : (List) t
               .getTransferData(DataFlavor.javaFileListFlavor))
       {
@@ -3197,7 +3199,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       {
         Cache.log.debug("Drop handled as uriListFlavor");
         // This is used by Unix drag system
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
         data = (String) t.getTransferData(uriListFlavor);
       }
       if (data == null)
@@ -3252,7 +3253,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements
           {
             Cache.log.debug("Supported transfer dataflavor: "
                     + fl.toString());
-            evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
             Object df = t.getTransferData(fl);
             if (df != null)
             {
index 9a8e5f6..1c90889 100644 (file)
@@ -992,17 +992,16 @@ public class Jalview2XML
             }
           }
 
-          if (entry.getProperty() != null && !entry.getProperty().isEmpty())
+          Enumeration<String> props = entry.getProperties();
+          if (props.hasMoreElements())
           {
             PdbentryItem item = new PdbentryItem();
-            Hashtable properties = entry.getProperty();
-            Enumeration en2 = properties.keys();
-            while (en2.hasMoreElements())
+            while (props.hasMoreElements())
             {
               Property prop = new Property();
-              String key = en2.nextElement().toString();
+              String key = props.nextElement();
               prop.setName(key);
-              prop.setValue(properties.get(key).toString());
+              prop.setValue(entry.getProperty(key).toString());
               item.addProperty(prop);
             }
             pdb.addPdbentryItem(item);
@@ -3018,7 +3017,8 @@ public class Jalview2XML
                 entry.setType(PDBEntry.Type.FILE);
               }
             }
-            if (ids[p].getFile() != null)
+            // jprovider is null when executing 'New View'
+            if (ids[p].getFile() != null && jprovider != null)
             {
               if (!pdbloaded.containsKey(ids[p].getFile()))
               {
@@ -3032,12 +3032,11 @@ public class Jalview2XML
             }
             if (ids[p].getPdbentryItem() != null)
             {
-              entry.setProperty(new Hashtable());
               for (PdbentryItem item : ids[p].getPdbentryItem())
               {
                 for (Property pr : item.getProperty())
                 {
-                  entry.getProperty().put(pr.getName(), pr.getValue());
+                  entry.setProperty(pr.getName(), pr.getValue());
                 }
               }
             }
index 57debe3..3350f6c 100644 (file)
@@ -81,6 +81,8 @@ public class StructureChooser extends GStructureChooser implements
 
   private boolean isValidPBDEntry;
 
+  private boolean cachedPDBExists;
+
   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
           AlignmentPanel ap)
   {
@@ -102,7 +104,7 @@ public class StructureChooser extends GStructureChooser implements
     }
 
     // ensure a filter option is in force for search
-    populateFilterComboBox(true);
+    populateFilterComboBox(true, cachedPDBExists);
     Thread discoverPDBStructuresThread = new Thread(new Runnable()
     {
       @Override
@@ -118,7 +120,7 @@ public class StructureChooser extends GStructureChooser implements
                 startTime);
         fetchStructuresMetaData();
         // revise filter options if no results were found
-        populateFilterComboBox(isStructuresDiscovered());
+        populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
         updateProgressIndicator(null, startTime);
         mainFrame.setVisible(true);
         updateCurrentView();
@@ -237,7 +239,7 @@ public class StructureChooser extends GStructureChooser implements
         }
       }
     }
-
+    cachedPDBExists = !entries.isEmpty();
     PDBEntryTableModel tableModelx = new PDBEntryTableModel(entries);
     tbl_local_pdb.setModel(tableModelx);
   }
@@ -524,7 +526,8 @@ public class StructureChooser extends GStructureChooser implements
    * Populates the filter combo-box options dynamically depending on discovered
    * structures
    */
-  protected void populateFilterComboBox(boolean haveData)
+  protected void populateFilterComboBox(boolean haveData,
+          boolean cachedPDBExists)
   {
     /*
      * temporarily suspend the change listener behaviour
@@ -549,8 +552,14 @@ public class StructureChooser extends GStructureChooser implements
             VIEWS_ENTER_ID));
     cmb_filterOption.addItem(new FilterOption("From File", "-",
             VIEWS_FROM_FILE));
-    cmb_filterOption.addItem(new FilterOption("Cached PDB Entries", "-",
-            VIEWS_LOCAL_PDB));
+    FilterOption cachedOption = new FilterOption("Cached PDB Entries", "-",
+            VIEWS_LOCAL_PDB);
+    cmb_filterOption.addItem(cachedOption);
+
+    if (/*!haveData &&*/cachedPDBExists)
+    {
+      cmb_filterOption.setSelectedItem(cachedOption);
+    }
 
     cmb_filterOption.addItemListener(this);
   }
index 21b2984..189d490 100644 (file)
@@ -136,14 +136,16 @@ public class StructureViewer
   protected JalviewStructureDisplayI viewStructures(ViewerType viewerType,
           PDBEntry[] pdbs, SequenceI[][] seqsForPdbs, AlignmentPanel ap)
   {
+    PDBEntry[] pdbsForFile = getUniquePdbFiles(pdbs);
     JalviewStructureDisplayI sview = null;
     if (viewerType.equals(ViewerType.JMOL))
     {
-      sview = new AppJmol(ap, pdbs, ap.av.collateForPDB(pdbs));
+      sview = new AppJmol(ap, pdbsForFile, ap.av.collateForPDB(pdbsForFile));
     }
     else if (viewerType.equals(ViewerType.CHIMERA))
     {
-      sview = new ChimeraViewFrame(pdbs, ap.av.collateForPDB(pdbs), ap);
+      sview = new ChimeraViewFrame(pdbsForFile,
+              ap.av.collateForPDB(pdbsForFile), ap);
     }
     else
     {
@@ -153,6 +155,36 @@ public class StructureViewer
     return sview;
   }
 
+  /**
+   * Convert the array of PDBEntry into an array with no filename repeated
+   * 
+   * @param pdbs
+   * @return
+   */
+  static PDBEntry[] getUniquePdbFiles(PDBEntry[] pdbs)
+  {
+    if (pdbs == null)
+    {
+      return null;
+    }
+    List<PDBEntry> uniques = new ArrayList<PDBEntry>();
+    List<String> filesSeen = new ArrayList<String>();
+    for (PDBEntry entry : pdbs)
+    {
+      String file = entry.getFile();
+      if (file == null)
+      {
+        uniques.add(entry);
+      }
+      else if (!filesSeen.contains(file))
+      {
+        uniques.add(entry);
+        filesSeen.add(file);
+      }
+    }
+    return uniques.toArray(new PDBEntry[uniques.size()]);
+  }
+
   protected JalviewStructureDisplayI viewStructures(ViewerType viewerType,
           PDBEntry pdb, SequenceI[] seqsForPdb, AlignmentPanel ap)
   {
index 61a9b48..26c202c 100644 (file)
@@ -35,7 +35,6 @@ import jalview.structure.StructureImportSettings;
 import java.awt.Color;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Vector;
 
@@ -68,6 +67,8 @@ public abstract class StructureFile extends AlignFile
 
   private Vector<PDBChain> chains;
 
+  private boolean pdbIdAvailable;
+
   public StructureFile(String inFile, String type) throws IOException
   {
     super(inFile, type);
@@ -112,7 +113,6 @@ public abstract class StructureFile extends AlignFile
   {
   }
 
-  @SuppressWarnings("rawtypes")
   protected SequenceI postProcessChain(PDBChain chain)
   {
     SequenceI pdbSequence = chain.sequence;
@@ -120,10 +120,9 @@ public abstract class StructureFile extends AlignFile
     PDBEntry entry = new PDBEntry();
     entry.setId(getId());
     entry.setType(getStructureFileType());
-    entry.setProperty(new Hashtable());
     if (chain.id != null)
     {
-      entry.setChainCode(String.valueOf(chain.id));
+      entry.setChainCode(chain.id);
     }
     if (inFile != null)
     {
@@ -470,4 +469,19 @@ public abstract class StructureFile extends AlignFile
   {
     return new PDBFeatureSettings();
   }
+
+  /**
+   * Answers true if the structure file has a PDBId
+   * 
+   * @return
+   */
+  public boolean isPPDBIdAvailable()
+  {
+    return pdbIdAvailable;
+  }
+
+  public void setPDBIdAvailable(boolean pdbIdAvailable)
+  {
+    this.pdbIdAvailable = pdbIdAvailable;
+  }
 }
index 612b168..7e691be 100644 (file)
@@ -390,6 +390,9 @@ public class StructureSelectionManager
       {
         registerPDBFile(pdb.getId().trim(), pdbFile);
       }
+      // if PDBId is unavailable then skip SIFTS mapping execution path
+      isMapUsingSIFTs = pdb.isPPDBIdAvailable();
+
     } catch (Exception ex)
     {
       ex.printStackTrace();
index dc42315..b00f1bc 100644 (file)
@@ -51,6 +51,10 @@ public abstract class AAStructureBindingModel extends
 
   private StructureSelectionManager ssm;
 
+  /*
+   * distinct PDB entries (pdb files) associated
+   * with sequences
+   */
   private PDBEntry[] pdbEntry;
 
   /*
@@ -75,6 +79,11 @@ public abstract class AAStructureBindingModel extends
   private boolean finishedInit = false;
 
   /**
+   * current set of model filenames loaded in the Jmol instance
+   */
+  protected String[] modelFileNames = null;
+
+  /**
    * Data bean class to simplify parameterisation in superposeStructures
    */
   protected class SuperposeData
@@ -239,24 +248,21 @@ public abstract class AAStructureBindingModel extends
     // TODO: give a more informative title when multiple structures are
     // displayed.
     StringBuilder title = new StringBuilder(64);
-    final PDBEntry pdbEntry = getPdbEntry(0);
+    final PDBEntry pdbe = getPdbEntry(0);
     title.append(viewerName + " view for " + getSequence()[0][0].getName()
-            + ":" + pdbEntry.getId());
+            + ":" + pdbe.getId());
 
     if (verbose)
     {
-      if (pdbEntry.getProperty() != null)
+      String method = (String) pdbe.getProperty("method");
+      if (method != null)
       {
-        if (pdbEntry.getProperty().get("method") != null)
-        {
-          title.append(" Method: ");
-          title.append(pdbEntry.getProperty().get("method"));
-        }
-        if (pdbEntry.getProperty().get("chains") != null)
-        {
-          title.append(" Chain:");
-          title.append(pdbEntry.getProperty().get("chains"));
-        }
+        title.append(" Method: ").append(method);
+      }
+      String chain = (String) pdbe.getProperty("chains");
+      if (chain != null)
+      {
+        title.append(" Chain:").append(chain);
       }
     }
     return title.toString();
@@ -521,6 +527,10 @@ public abstract class AAStructureBindingModel extends
   {
     int refStructure = -1;
     String[] files = getPdbFile();
+    if (files == null)
+    {
+      return -1;
+    }
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
       StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]);
@@ -565,7 +575,11 @@ public abstract class AAStructureBindingModel extends
             }
             structures[pdbfnum].pdbId = mapping.getPdbId();
             structures[pdbfnum].isRna = theSequence.getRNA() != null;
-            // move on to next pdb file
+
+            /*
+             * move on to next pdb file (ignore sequences for other chains
+             * for the same structure)
+             */
             s = seqCountForPdbFile;
             break;
           }
index d43f5bc..e6aa472 100755 (executable)
@@ -29,7 +29,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -508,9 +507,7 @@ public class DBRefUtils
           PDBEntry pdbr = new PDBEntry();
           pdbr.setId(pdbid);
           pdbr.setType(PDBEntry.Type.PDB);
-          pdbr.setProperty(new Hashtable());
           pdbr.setChainCode(chaincode);
-          // pdbr.getProperty().put("CHAIN", chaincode);
           seq.addPDBId(pdbr);
         }
         else
index 2049766..b139574 100644 (file)
@@ -102,8 +102,13 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
       }
     }
 
+    /*
+     * invalid accession gets a reply with no <entry> elements, text content of
+     * EmbFile reads something like (e.g.) this ungrammatical phrase
+     * Entry: <acc> display type is either not supported or entry is not found.
+     */
     List<SequenceI> peptides = new ArrayList<SequenceI>();
-    if (efile != null)
+    if (efile != null && efile.getEntries() != null)
     {
       for (EmblEntry entry : efile.getEntries())
       {
index 4030d8c..b6f53cd 100644 (file)
@@ -226,8 +226,7 @@ public class Uniprot extends DbSourceProxyImpl
       if ("EMBL".equals(pdb.getType()))
       {
         // look for a CDS reference and add it, too.
-        String cdsId = (String) pdb.getProperty()
-                .get("protein sequence ID");
+        String cdsId = (String) pdb.getProperty("protein sequence ID");
         if (cdsId != null && cdsId.trim().length() > 0)
         {
           // remove version
@@ -246,8 +245,7 @@ public class Uniprot extends DbSourceProxyImpl
         * <property type="gene ID" value="ENSG00000158828"/>
         * </dbReference> 
          */
-        String cdsId = (String) pdb.getProperty()
-                .get("protein sequence ID");
+        String cdsId = (String) pdb.getProperty("protein sequence ID");
         if (cdsId != null && cdsId.trim().length() > 0)
         {
           dbr = new DBRefEntry(DBRefSource.ENSEMBL, DBRefSource.UNIPROT
index fb59071..acca50f 100644 (file)
@@ -390,8 +390,8 @@ public class SiftsClient implements SiftsClientI
           String pdbFile, String chain) throws SiftsException
   {
     structId = (chain == null) ? pdbId : pdbId + "|" + chain;
-    System.out.println("Getting mapping for: " + pdbId + "|" + chain
-            + " : seq- " + seq.getName());
+    System.out.println("Getting SIFTS mapping for " + structId + ": seq "
+            + seq.getName());
 
     final StringBuilder mappingDetails = new StringBuilder(128);
     PrintStream ps = new PrintStream(System.out)
@@ -470,12 +470,13 @@ public class SiftsClient implements SiftsClientI
     int pdbStart = UNASSIGNED;
     int pdbEnd = UNASSIGNED;
 
-    Integer[] keys = mapping.keySet().toArray(new Integer[0]);
-    Arrays.sort(keys);
-    if (keys.length < 1)
+    if (mapping.isEmpty())
     {
-      throw new SiftsException(">>> Empty SIFTS mapping generated!!");
+      throw new SiftsException("SIFTS mapping failed");
     }
+
+    Integer[] keys = mapping.keySet().toArray(new Integer[0]);
+    Arrays.sort(keys);
     seqStart = keys[0];
     seqEnd = keys[keys.length - 1];
 
index 979fee4..e9d5cb2 100644 (file)
@@ -32,8 +32,6 @@ import static org.testng.Assert.fail;
 
 import jalview.datamodel.PDBEntry.Type;
 
-import java.util.Hashtable;
-
 //import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -101,17 +99,17 @@ public class PDBEntryTest
     assertNotEquals(case9, case10);
 
     // add properties
-    case7.getProperty().put("hello", "world");
+    case7.setProperty("hello", "world");
     assertNotEquals(case7, case9);
-    case9.getProperty().put("hello", "world");
+    case9.setProperty("hello", "world");
     assertEquals(case7, case9);
-    case9.getProperty().put("hello", "WORLD");
+    case9.setProperty("hello", "WORLD");
     assertNotEquals(case7, case9);
 
     /*
      * change string wrapper property to string...
      */
-    case1.getProperty().put("chain_code", "a");
+    case1.setProperty("chain_code", "a");
     assertFalse(pdbEntry.equals(case1));
     assertFalse(case1.equals(pdbEntry));
   }
@@ -245,28 +243,23 @@ public class PDBEntryTest
      */
     pdb1 = new PDBEntry("3A6S", null, null, null);
     pdb2 = new PDBEntry("3A6S", null, null, null);
-    // ughh properties not null if chain code has been set...
-    // JAL-2196 addresses this
-    pdb1.properties = new Hashtable();
-    pdb2.properties = new Hashtable();
-    pdb1.properties.put("destination", "mars");
-    pdb1.properties.put("hello", "world");
-    pdb2.properties.put("hello", "moon");
-    pdb2.properties.put("goodbye", "world");
+    pdb1.setProperty("destination", "mars");
+    pdb1.setProperty("hello", "world");
+    pdb2.setProperty("hello", "moon");
+    pdb2.setProperty("goodbye", "world");
     assertTrue(pdb1.updateFrom(pdb2));
-    assertEquals(pdb1.properties.get("destination"), "mars");
-    assertEquals(pdb1.properties.get("hello"), "moon");
-    assertEquals(pdb1.properties.get("goodbye"), "world");
+    assertEquals(pdb1.getProperty("destination"), "mars");
+    assertEquals(pdb1.getProperty("hello"), "moon");
+    assertEquals(pdb1.getProperty("goodbye"), "world");
 
     /*
      * add properties only
      */
     pdb1 = new PDBEntry("3A6S", null, null, null);
     pdb2 = new PDBEntry("3A6S", null, null, null);
-    pdb2.properties = new Hashtable();
-    pdb2.properties.put("hello", "moon");
+    pdb2.setProperty("hello", "moon");
     assertTrue(pdb1.updateFrom(pdb2));
-    assertEquals(pdb1.properties.get("hello"), "moon");
+    assertEquals(pdb1.getProperty("hello"), "moon");
   }
 
   @Test(groups = { "Functional" })
index fb092f6..b2d3253 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.ext.jmol;
 
 import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.bin.Cache;
@@ -248,4 +249,30 @@ public class JmolParserTest
     assertEquals('H', structCode[4]);
     assertEquals('E', structCode[5]);
   }
+
+  @Test(groups = "Functional")
+  public void testLocalPDBId() throws Exception
+  {
+    JmolParser structureData;
+    /*
+     * reads a local structure
+     */
+    structureData = new JmolParser("examples/testdata/localstruct.pdb",
+            AppletFormatAdapter.FILE);
+    assertNotNull(structureData);
+    /*
+     * local structure files should yield a false ID based on the filename
+     */
+    assertNotNull(structureData.getId());
+    assertEquals(structureData.getId(), "localstruct.pdb");
+    assertNotNull(structureData.getSeqs());
+    /*
+     * the ID is also the group for features derived from structure data 
+     */
+    assertNotNull(structureData.getSeqs().get(0).getSequenceFeatures()[0].featureGroup);
+    assertEquals(
+            structureData.getSeqs().get(0).getSequenceFeatures()[0].featureGroup,
+            "localstruct.pdb");
+
+  }
 }
index 1e41a16..446d32d 100644 (file)
@@ -28,6 +28,7 @@ import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
+import jalview.jbgui.GStructureChooser.FilterOption;
 
 import java.util.Vector;
 
@@ -109,15 +110,21 @@ public class StructureChooserTest
   {
     SequenceI[] selectedSeqs = new SequenceI[] { seq };
     StructureChooser sc = new StructureChooser(selectedSeqs, seq, null);
-    sc.populateFilterComboBox(false);
+    sc.populateFilterComboBox(false, false);
     int optionsSize = sc.getCmbFilterOption().getItemCount();
     assertEquals(3, optionsSize); // if structures are not discovered then don't
                                   // populate filter options
 
-    sc.populateFilterComboBox(true);
+    sc.populateFilterComboBox(true, false);
     optionsSize = sc.getCmbFilterOption().getItemCount();
     assertTrue(optionsSize > 3); // if structures are found, filter options
                                  // should be populated
+
+    sc.populateFilterComboBox(true, true);
+    assertTrue(sc.getCmbFilterOption().getSelectedItem() != null);
+    FilterOption filterOpt = (FilterOption) sc.getCmbFilterOption()
+            .getSelectedItem();
+    assertEquals("Cached PDB Entries", filterOpt.getName());
   }
 
   @Test(groups = { "Functional" })
diff --git a/test/jalview/gui/StructureViewerTest.java b/test/jalview/gui/StructureViewerTest.java
new file mode 100644 (file)
index 0000000..f8e9133
--- /dev/null
@@ -0,0 +1,33 @@
+package jalview.gui;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.PDBEntry.Type;
+
+import org.testng.annotations.Test;
+
+public class StructureViewerTest
+{
+  @Test(groups = "Functional")
+  public void testGetUniquePdbFiles()
+  {
+    assertNull(StructureViewer.getUniquePdbFiles(null));
+
+    PDBEntry pdbe1 = new PDBEntry("1A70", "A", Type.PDB, "path1");
+    PDBEntry pdbe2 = new PDBEntry("3A6S", "A", Type.PDB, "path2");
+    PDBEntry pdbe3 = new PDBEntry("1A70", "B", Type.PDB, "path1");
+    PDBEntry pdbe4 = new PDBEntry("1GAQ", "A", Type.PDB, null);
+    PDBEntry pdbe5 = new PDBEntry("3A6S", "B", Type.PDB, "path2");
+    PDBEntry pdbe6 = new PDBEntry("1GAQ", "B", Type.PDB, null);
+
+    /*
+     * pdbe2 and pdbe5 get removed as having a duplicate file path
+     */
+    PDBEntry[] uniques = StructureViewer.getUniquePdbFiles(new PDBEntry[] {
+        pdbe1, pdbe2, pdbe3, pdbe4, pdbe5, pdbe6 });
+    assertEquals(uniques,
+ new PDBEntry[] { pdbe1, pdbe2, pdbe4, pdbe6 });
+  }
+}
index bb81992..0d00169 100644 (file)
@@ -49,26 +49,36 @@ import org.testng.annotations.Test;
  */
 public class AAStructureBindingModelTest
 {
+  /*
+   * Scenario: Jalview has 4 sequences, corresponding to 1YCS (chains A and B), 3A6S|B, 1OOT|A
+   */
   private static final String PDB_1 = "HEADER    COMPLEX (ANTI-ONCOGENE/ANKYRIN REPEATS) 30-SEP-96   1YCS              \n"
           + "ATOM      2  CA  VAL A  97      24.134   4.926  45.821  1.00 47.43           C  \n"
           + "ATOM      9  CA  PRO A  98      25.135   8.584  46.217  1.00 41.60           C  \n"
           + "ATOM     16  CA  SER A  99      28.243   9.596  44.271  1.00 39.63           C  \n"
           + "ATOM     22  CA  GLN A 100      31.488  10.133  46.156  1.00 35.60           C  \n"
-          + "ATOM     31  CA  LYS A 101      33.323  11.587  43.115  1.00 41.69           C  \n";
+          // artificial jump in residue numbering to prove it is correctly
+          // mapped:
+          + "ATOM     31  CA  LYS A 102      33.323  11.587  43.115  1.00 41.69           C  \n"
+          + "ATOM   1857  CA  GLU B 374       9.193 -16.005  95.870  1.00 54.22           C  \n"
+          + "ATOM   1866  CA  ILE B 375       7.101 -14.921  92.847  1.00 46.82           C  \n"
+          + "ATOM   1874  CA  VAL B 376      10.251 -13.625  91.155  1.00 47.80           C  \n"
+          + "ATOM   1881  CA  LYS B 377      11.767 -17.068  91.763  1.00 50.21           C  \n"
+          + "ATOM   1890  CA  PHE B 378       8.665 -18.948  90.632  1.00 44.85           C  \n";
 
   private static final String PDB_2 = "HEADER    HYDROLASE                               09-SEP-09   3A6S              \n"
-          + "ATOM      2  CA  MET A   1      15.366 -11.648  24.854  1.00 32.05           C  \n"
-          + "ATOM     10  CA  LYS A   2      16.846  -9.215  22.340  1.00 25.68           C  \n"
-          + "ATOM     19  CA  LYS A   3      15.412  -6.335  20.343  1.00 19.42           C  \n"
-          + "ATOM     28  CA  LEU A   4      15.629  -5.719  16.616  1.00 15.49           C  \n"
-          + "ATOM     36  CA  GLN A   5      14.412  -2.295  15.567  1.00 12.19           C  \n";
+          + "ATOM      2  CA  MET B   1      15.366 -11.648  24.854  1.00 32.05           C  \n"
+          + "ATOM     10  CA  LYS B   2      16.846  -9.215  22.340  1.00 25.68           C  \n"
+          + "ATOM     19  CA  LYS B   3      15.412  -6.335  20.343  1.00 19.42           C  \n"
+          + "ATOM     28  CA  LEU B   4      15.629  -5.719  16.616  1.00 15.49           C  \n"
+          + "ATOM     36  CA  GLN B   5      14.412  -2.295  15.567  1.00 12.19           C  \n";
 
   private static final String PDB_3 = "HEADER    STRUCTURAL GENOMICS                     04-MAR-03   1OOT              \n"
-          + "ATOM      2  CA  SER A   1      29.427   3.330  -6.578  1.00 32.50           C  \n"
-          + "ATOM      8  CA  PRO A   2      29.975   3.340  -2.797  1.00 17.62           C  \n"
-          + "ATOM     16  CA ALYS A   3      26.958   3.024  -0.410  0.50  8.78           C  \n"
-          + "ATOM     33  CA  ALA A   4      26.790   4.320   3.172  1.00 11.98           C  \n"
-          + "ATOM     39  CA AVAL A   5      24.424   3.853   6.106  0.50 13.83           C  \n";
+          + "ATOM      2  CA  SER A   7      29.427   3.330  -6.578  1.00 32.50           C  \n"
+          + "ATOM      8  CA  PRO A   8      29.975   3.340  -2.797  1.00 17.62           C  \n"
+          + "ATOM     16  CA ALYS A   9      26.958   3.024  -0.410  0.50  8.78           C  \n"
+          + "ATOM     33  CA  ALA A  10      26.790   4.320   3.172  1.00 11.98           C  \n"
+          + "ATOM     39  CA AVAL A  12      24.424   3.853   6.106  0.50 13.83           C  \n";
 
   AAStructureBindingModel testee;
 
@@ -80,24 +90,28 @@ public class AAStructureBindingModelTest
   @BeforeMethod(alwaysRun = true)
   public void setUp()
   {
-    SequenceI seq1 = new Sequence("1YCS", "-VPSQK");
+    SequenceI seq1a = new Sequence("1YCS|A", "-VPSQK");
+    SequenceI seq1b = new Sequence("1YCS|B", "EIVKF-");
     SequenceI seq2 = new Sequence("3A6S", "MK-KLQ");
     SequenceI seq3 = new Sequence("1OOT", "SPK-AV");
-    al = new Alignment(new SequenceI[] { seq1, seq2, seq3 });
+    al = new Alignment(new SequenceI[] { seq1a, seq1b, seq2, seq3 });
     al.setDataset(null);
 
+    /*
+     * give pdb files the name generated by Jalview for PASTE source
+     */
     PDBEntry[] pdbFiles = new PDBEntry[3];
-    pdbFiles[0] = new PDBEntry("1YCS", "A", Type.PDB, "1YCS.pdb");
-    pdbFiles[1] = new PDBEntry("3A6S", "B", Type.PDB, "3A6S.pdb");
-    pdbFiles[2] = new PDBEntry("1OOT", "A", Type.PDB, "1OOT.pdb");
+    pdbFiles[0] = new PDBEntry("1YCS", "A", Type.PDB, "INLINE1YCS");
+    pdbFiles[1] = new PDBEntry("3A6S", "B", Type.PDB, "INLINE3A6S");
+    pdbFiles[2] = new PDBEntry("1OOT", "A", Type.PDB, "INLINE1OOT");
     String[][] chains = new String[3][];
     SequenceI[][] seqs = new SequenceI[3][];
-    seqs[0] = new SequenceI[] { seq1 };
+    seqs[0] = new SequenceI[] { seq1a, seq1b };
     seqs[1] = new SequenceI[] { seq2 };
     seqs[2] = new SequenceI[] { seq3 };
     StructureSelectionManager ssm = new StructureSelectionManager();
 
-    ssm.setMapping(new SequenceI[] { seq1 }, null, PDB_1,
+    ssm.setMapping(new SequenceI[] { seq1a, seq1b }, null, PDB_1,
             AppletFormatAdapter.PASTE);
     ssm.setMapping(new SequenceI[] { seq2 }, null, PDB_2,
             AppletFormatAdapter.PASTE);
@@ -109,10 +123,6 @@ public class AAStructureBindingModelTest
       @Override
       public String[] getPdbFile()
       {
-        /*
-         * fudge 'filenames' to match those generated when PDBFile parses PASTE
-         * data
-         */
         return new String[] { "INLINE1YCS", "INLINE3A6S", "INLINE1OOT" };
       }
 
@@ -140,7 +150,10 @@ public class AAStructureBindingModelTest
   @Test(groups = { "Functional" })
   public void testFindSuperposableResidues()
   {
-    SuperposeData[] structs = new SuperposeData[al.getHeight()];
+    /*
+     * create a data bean to hold data per structure file
+     */
+    SuperposeData[] structs = new SuperposeData[testee.getPdbFile().length];
     for (int i = 0; i < structs.length; i++)
     {
       structs[i] = testee.new SuperposeData(al.getWidth());
@@ -162,10 +175,23 @@ public class AAStructureBindingModelTest
      */
     assertFalse(matched[0]); // gap in first sequence
     assertTrue(matched[1]);
-    assertFalse(matched[2]); // gap in second sequence
-    assertFalse(matched[3]); // gap in third sequence
+    assertFalse(matched[2]); // gap in third sequence
+    assertFalse(matched[3]); // gap in fourth sequence
     assertTrue(matched[4]);
-    assertTrue(matched[5]);
+    assertTrue(matched[5]); // gap in second sequence
+
+    assertEquals("1YCS", structs[0].pdbId);
+    assertEquals("3A6S", structs[1].pdbId);
+    assertEquals("1OOT", structs[2].pdbId);
+    assertEquals("A", structs[0].chain); // ? struct has chains A _and_ B
+    assertEquals("B", structs[1].chain);
+    assertEquals("A", structs[2].chain);
+    // the 0's for unsuperposable positions propagate down the columns:
+    assertEquals("[0, 97, 98, 99, 100, 102]",
+            Arrays.toString(structs[0].pdbResNo));
+    assertEquals("[0, 2, 0, 3, 4, 5]", Arrays.toString(structs[1].pdbResNo));
+    assertEquals("[0, 8, 0, 0, 10, 12]",
+            Arrays.toString(structs[2].pdbResNo));
   }
 
   @Test(groups = { "Functional" })
index 57980b8..2df8be6 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.ws.dbsources;
 
 import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertNull;
 
@@ -117,25 +118,21 @@ public class UniprotTest
     PDBEntry xref = xrefs.get(0);
     assertEquals("2FSQ", xref.getId());
     assertEquals("PDB", xref.getType());
-    assertEquals(2, xref.getProperty().size());
-    assertEquals("X-ray", xref.getProperty().get("method"));
-    assertEquals("1.40", xref.getProperty().get("resolution"));
+    assertEquals("X-ray", xref.getProperty("method"));
+    assertEquals("1.40", xref.getProperty("resolution"));
 
     xref = xrefs.get(1);
     assertEquals("2FSR", xref.getId());
     assertEquals("PDBsum", xref.getType());
-    assertNull(xref.getProperty());
+    assertFalse(xref.getProperties().hasMoreElements());
 
     xref = xrefs.get(2);
     assertEquals("AE007869", xref.getId());
     assertEquals("EMBL", xref.getType());
-    assertNotNull(xref.getProperty());
     assertEquals("AAK85932.1",
-            (String) xref.getProperty().get("protein sequence ID"));
+ xref.getProperty("protein sequence ID"));
     assertEquals("Genomic_DNA",
-            (String) xref.getProperty().get("molecule type"));
-    assertEquals(2, xref.getProperty().size());
-
+ xref.getProperty("molecule type"));
   }
 
   @Test(groups = { "Functional" })
diff --git a/utils/BufferedLineReader.java b/utils/BufferedLineReader.java
new file mode 100644 (file)
index 0000000..b813fb2
--- /dev/null
@@ -0,0 +1,182 @@
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.StringReader;
+
+/**
+ * A file reader that concatenates lines
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class BufferedLineReader
+{
+  interface LineCleaner
+  {
+    String cleanLine(String l);
+  }
+
+  /*
+   * a reader for the file being read
+   */
+  private BufferedReader br;
+  
+  /*
+   * optional handler to post-process each line as it is read
+   */
+  private LineCleaner cleaner;
+
+  /*
+   * current buffer of <bufferSize> post-processed input lines
+   */
+  private String[] buffer;
+
+  private boolean atEof;
+
+  /**
+   * Constructor
+   * 
+   * @param reader
+   * @param bufferSize
+   *          the number of lines to concatenate at a time while reading
+   * @param tidier
+   *          an optional callback handler to post-process each line after
+   *          reading
+   * @throws FileNotFoundException
+   */
+  public BufferedLineReader(BufferedReader reader, int bufferSize,
+          LineCleaner tidier)
+          throws IOException
+  {
+    br = reader;
+    buffer = new String[bufferSize];
+    cleaner = tidier;
+
+    /*
+     * load up the buffer with N-1 lines, ready for the first read
+     */
+    for (int i = 1; i < bufferSize; i++)
+    {
+      readLine();
+    }
+
+  }
+
+  /**
+   * Reads the next line from file, invokes the post-processor if one was
+   * provided, and returns the 'cleaned' line, or null at end of file.
+   * 
+   * @return
+   */
+  private String readLine() // throws IOException
+  {
+    if (atEof)
+    {
+      return null;
+    }
+
+    String line = null;
+    try
+    {
+      line = br.readLine();
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+    if (line == null)
+    {
+      atEof = true;
+      return null;
+    }
+    if (cleaner != null)
+    {
+      line = cleaner.cleanLine(line);
+    }
+
+    /*
+     * shuffle down the lines buffer and add the new line
+     * in the last position
+     */
+    for (int i = 1; i < buffer.length; i++)
+    {
+      buffer[i - 1] = buffer[i];
+    }
+    buffer[buffer.length - 1] = line;
+    return line;
+  }
+
+  /**
+   * Returns a number of concatenated lines from the file, or null at end of
+   * file.
+   * 
+   * @return
+   */
+  public String read()
+  {
+    if (readLine() == null)
+    {
+      return null;
+    }
+    StringBuilder result = new StringBuilder(100 * buffer.length);
+    for (String line : buffer)
+    {
+      if (line != null)
+      {
+        result.append(line);
+      }
+    }
+    return result.toString();
+  }
+
+  /**
+   * A main 'test' method!
+   * 
+   * @throws IOException
+   */
+  public static void main(String[] args) throws IOException
+  {
+    String data = "Now is the winter\n" + "Of our discontent\n"
+            + "Made glorious summer\n" + "By this sun of York\n";
+    BufferedReader br = new BufferedReader(new StringReader(data));
+    BufferedLineReader reader = new BufferedLineReader(br, 3,
+            new LineCleaner()
+            {
+              @Override
+              public String cleanLine(String l)
+              {
+                return l.toUpperCase();
+              }
+            });
+    String line = reader.read();
+    String expect = "NOW IS THE WINTEROF OUR DISCONTENTMADE GLORIOUS SUMMER";
+    if (!line.equals(expect))
+    {
+      System.err.println("Fail: expected '" + expect + "', found '" + line
+              + ";");
+    }
+    else
+    {
+      System.out.println("Line one ok!");
+    }
+    line = reader.read();
+    expect = "OF OUR DISCONTENTMADE GLORIOUS SUMMERBY THIS SUN OF YORK";
+    if (!line.equals(expect))
+    {
+      System.err.println("Fail: expected '" + expect + "', found '" + line
+              + "'");
+    }
+    else
+    {
+      System.out.println("Line two ok!!");
+    }
+    line = reader.read();
+    if (line != null)
+    {
+      System.err.println("Fail: expected null at eof, got '" + line + "'");
+    }
+    else
+    {
+      System.out.println("EOF ok!!!");
+    }
+  }
+}
index 7c57cc7..1279b31 100644 (file)
@@ -34,7 +34,7 @@ import java.util.Map;
  * @author gmcarstairs
  *
  */
-public class HelpLinksChecker
+public class HelpLinksChecker implements BufferedLineReader.LineCleaner
 {
   private static final String HELP_HS = "help.hs";
 
@@ -358,7 +358,8 @@ public class HelpLinksChecker
     try
     {
       BufferedReader br = new BufferedReader(new FileReader(hrefFile));
-      String data = br.readLine();
+      BufferedLineReader blr = new BufferedLineReader(br, 3, this);
+      String data = blr.read();
       while (data != null)
       {
         if (data.contains(nameAnchor) || data.contains(idAnchor))
@@ -366,7 +367,7 @@ public class HelpLinksChecker
           found = true;
           break;
         }
-        data = br.readLine();
+        data = blr.read();
       }
       br.close();
     } catch (IOException e)
@@ -544,4 +545,14 @@ public class HelpLinksChecker
     }
     return value;
   }
+
+  /**
+   * Trim whitespace from concatenated lines but preserve one space for valid
+   * parsing
+   */
+  @Override
+  public String cleanLine(String l)
+  {
+    return l.trim() + " ";
+  }
 }
index 15e16cf..4489a93 100644 (file)
@@ -40,7 +40,7 @@ import java.util.regex.Pattern;
  * @author gmcarstairs
  *
  */
-public class MessageBundleChecker
+public class MessageBundleChecker implements BufferedLineReader.LineCleaner
 {
   /*
    * regex ^"[^"]*"$
@@ -212,64 +212,37 @@ public class MessageBundleChecker
       return;
     }
 
-    String[] lines = new String[bufferSize];
     BufferedReader br = new BufferedReader(new FileReader(f));
-    for (int i = 0; i < bufferSize; i++)
-    {
-      String readLine = br.readLine();
-      lines[i] = stripCommentsAndTrim(readLine);
-    }
+    BufferedLineReader blr = new BufferedLineReader(br, bufferSize, this);
 
     int lineNo = 0;
-
-    while (lines[bufferSize - 1] != null)
+    String line = blr.read();
+    while (line != null)
     {
       lineNo++;
-      inspectSourceLines(path, lineNo, lines);
-
-      for (int i = 0; i < bufferSize - 1; i++)
-      {
-        lines[i] = lines[i + 1];
-      }
-      lines[bufferSize - 1] = stripCommentsAndTrim(br.readLine());
+      inspectSourceLines(path, lineNo, line);
+      line = blr.read();
     }
     br.close();
 
   }
 
-  /*
-   * removes anything after (and including) '//'
-   */
-  private String stripCommentsAndTrim(String line)
-  {
-    if (line != null)
-    {
-      int pos = line.indexOf("//");
-      if (pos != -1)
-      {
-        line = line.substring(0, pos);
-      }
-      line = line.replace("\t", " ").trim();
-    }
-    return line;
-  }
-
   /**
    * Look for calls to MessageManager methods, possibly split over two or more
-   * lines
+   * lines that have been concatenated while parsing the file
    * 
    * @param path
    * @param lineNo
-   * @param lines
+   * @param line
    */
-  private void inspectSourceLines(String path, int lineNo, String[] lines)
+  private void inspectSourceLines(String path, int lineNo, String line)
   {
-    String lineNos = String.format("%d-%d", lineNo, lineNo + lines.length
+    String lineNos = String
+            .format("%d-%d", lineNo, lineNo + bufferSize
             - 1);
-    String combined = combineLines(lines);
     for (String method : METHODS)
     {
-      int pos = combined.indexOf(method);
+      int pos = line.indexOf(method);
       if (pos == -1)
       {
         continue;
@@ -278,7 +251,7 @@ public class MessageBundleChecker
       /*
        * extract what follows the opening bracket of the method call
        */
-      String methodArgs = combined.substring(pos + method.length()).trim();
+      String methodArgs = line.substring(pos + method.length()).trim();
       if ("".equals(methodArgs))
       {
         /*
@@ -305,7 +278,7 @@ public class MessageBundleChecker
       if (METHOD3 == method)
       {
         System.out.println(String.format("Dynamic key at %s line %s %s",
-                path.substring(sourcePath.length()), lineNos, combined));
+                path.substring(sourcePath.length()), lineNos, line));
         continue;
       }
 
@@ -313,14 +286,14 @@ public class MessageBundleChecker
       if (messageKey == null)
       {
         System.out.println(String.format("Trouble parsing %s line %s %s",
-                path.substring(sourcePath.length()), lineNos, combined));
+                path.substring(sourcePath.length()), lineNos, line));
         continue;
       }
 
       if (!(STRING_PATTERN.matcher(messageKey).matches()))
       {
         System.out.println(String.format("Dynamic key at %s line %s %s",
-                path.substring(sourcePath.length()), lineNos, combined));
+                path.substring(sourcePath.length()), lineNos, line));
         continue;
       }
 
@@ -384,22 +357,6 @@ public class MessageBundleChecker
     return endPos == -1 ? null : key.substring(0, endPos);
   }
 
-  private String combineLines(String[] lines)
-  {
-    String combined = "";
-    if (lines != null)
-    {
-      for (String line : lines)
-      {
-        if (line != null)
-        {
-          combined += line;
-        }
-      }
-    }
-    return combined;
-  }
-
   /**
    * Loads properties from Message.properties
    * 
@@ -421,4 +378,22 @@ public class MessageBundleChecker
 
   }
 
+  /**
+   * Remove any trailing comments, change tabs to space, and trim
+   */
+  @Override
+  public String cleanLine(String l)
+  {
+    if (l != null)
+    {
+      int pos = l.indexOf("//");
+      if (pos != -1)
+      {
+        l = l.substring(0, pos);
+      }
+      l = l.replace("\t", " ").trim();
+    }
+    return l;
+  }
+
 }