Merge branch 'develop' of https://source.jalview.org/git/jalview.git into develop
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 25 Mar 2015 13:46:16 +0000 (13:46 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 25 Mar 2015 13:46:16 +0000 (13:46 +0000)
30 files changed:
.classpath
lib/jersey-client-1.19.jar [new file with mode: 0644]
lib/jersey-core-1.19.jar [new file with mode: 0644]
lib/jersey-json-1.19.jar [new file with mode: 0644]
lib/jsr311-api-1.1.1.jar [new file with mode: 0644]
resources/images/dna.png [new file with mode: 0644]
resources/images/error.png [new file with mode: 0644]
resources/images/good.png [new file with mode: 0644]
resources/images/loading.gif [new file with mode: 0644]
resources/images/protein.png [new file with mode: 0644]
resources/images/sugar.png [new file with mode: 0644]
resources/lang/Messages.properties
src/jalview/gui/PDBSearchPanel.java [new file with mode: 0644]
src/jalview/gui/PopupMenu.java
src/jalview/gui/SequenceFetcher.java
src/jalview/gui/StructureChooser.java [new file with mode: 0644]
src/jalview/jbgui/GPDBSearchPanel.java [new file with mode: 0644]
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GStructureChooser.java [new file with mode: 0644]
src/jalview/jbgui/PDBDocFieldPreferences.java [new file with mode: 0644]
src/jalview/util/Format.java
src/jalview/util/MessageManager.java
src/jalview/ws/dbsources/PDBRestClient.java [new file with mode: 0644]
src/jalview/ws/uimodel/PDBRestRequest.java [new file with mode: 0644]
src/jalview/ws/uimodel/PDBRestResponse.java [new file with mode: 0644]
test/jalview/gui/PDBSearchPanelTest.java [new file with mode: 0644]
test/jalview/gui/StructureChooserTest.java [new file with mode: 0644]
test/jalview/io/pdb_request_json_error.txt [new file with mode: 0644]
test/jalview/io/pdb_response_json.txt [new file with mode: 0644]
test/jalview/ws/dbsources/PDBRestClientTest.java [new file with mode: 0644]

index c3e70c0..c19e381 100644 (file)
        <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/Plugin.jar"/>
+       <classpathentry kind="lib" path="lib/jersey-client-1.19.jar"/>
+       <classpathentry kind="lib" path="lib/jersey-core-1.19.jar"/>
+       <classpathentry kind="lib" path="lib/jersey-json-1.19.jar"/>
+       <classpathentry kind="lib" path="lib/jsr311-api-1.1.1.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin.jar"/>
        <classpathentry kind="output" path="classes"/>
 </classpath>
diff --git a/lib/jersey-client-1.19.jar b/lib/jersey-client-1.19.jar
new file mode 100644 (file)
index 0000000..c9e0f56
Binary files /dev/null and b/lib/jersey-client-1.19.jar differ
diff --git a/lib/jersey-core-1.19.jar b/lib/jersey-core-1.19.jar
new file mode 100644 (file)
index 0000000..92feb63
Binary files /dev/null and b/lib/jersey-core-1.19.jar differ
diff --git a/lib/jersey-json-1.19.jar b/lib/jersey-json-1.19.jar
new file mode 100644 (file)
index 0000000..b609411
Binary files /dev/null and b/lib/jersey-json-1.19.jar differ
diff --git a/lib/jsr311-api-1.1.1.jar b/lib/jsr311-api-1.1.1.jar
new file mode 100644 (file)
index 0000000..ec8bc81
Binary files /dev/null and b/lib/jsr311-api-1.1.1.jar differ
diff --git a/resources/images/dna.png b/resources/images/dna.png
new file mode 100644 (file)
index 0000000..f9854fe
Binary files /dev/null and b/resources/images/dna.png differ
diff --git a/resources/images/error.png b/resources/images/error.png
new file mode 100644 (file)
index 0000000..6d68a8c
Binary files /dev/null and b/resources/images/error.png differ
diff --git a/resources/images/good.png b/resources/images/good.png
new file mode 100644 (file)
index 0000000..ebbacc9
Binary files /dev/null and b/resources/images/good.png differ
diff --git a/resources/images/loading.gif b/resources/images/loading.gif
new file mode 100644 (file)
index 0000000..23ed238
Binary files /dev/null and b/resources/images/loading.gif differ
diff --git a/resources/images/protein.png b/resources/images/protein.png
new file mode 100644 (file)
index 0000000..3789793
Binary files /dev/null and b/resources/images/protein.png differ
diff --git a/resources/images/sugar.png b/resources/images/sugar.png
new file mode 100644 (file)
index 0000000..5d62ce5
Binary files /dev/null and b/resources/images/sugar.png differ
index e6b69d5..0d3d491 100644 (file)
@@ -783,7 +783,6 @@ label.use_sequence_id_1 = Use $SEQUENCE_ID$ or $SEQUENCE_ID=/<regex>/=$
 label.use_sequence_id_2 = \nto embed sequence id in URL
 label.ws_parameters_for = Parameters for {0}
 label.switch_server = Switch server
-label.open_jabaws_web_page = Opens the JABAWS server's homepage in web browser
 label.choose_jabaws_server = Choose a server for running this service
 label.services_at = Services at {0}
 label.rest_client_submit = {0} using {1}
@@ -1207,5 +1206,22 @@ label.search_filter = Search Filter
 label.display_name = Display Label
 label.description = Description
 label.include_description= Include Description
+action.back = Back
+label.hide_insertions = Hide Insertions
+label.mark_as_representative = Mark as representative
+label.open_jabaws_web_page = Open JABAWS web page
+label.opens_the_jabaws_server_homepage = Opens the JABAWS server's homepage in web browser
+label.pdb_sequence_getcher = PDB Sequence Fetcher
+label.result = result
+label.results = results
+label.structure_chooser = Structure Chooser
+label.select = Select : 
+label.invert = Invert 
+label.select_pdb_file = Select PDB File
+info.select_filter_option = Select Filter Option/Manual Entry
+info.associate_wit_sequence = Associate with Sequence
+label.search_result = Search Result
+label.found_structures_summary = Found Structures Summary
+label.configure_displayed_columns = Configure Displayed Columns
 label.start_jalview = Start Jalview
 label.biojs_html_export = BioJS
diff --git a/src/jalview/gui/PDBSearchPanel.java b/src/jalview/gui/PDBSearchPanel.java
new file mode 100644 (file)
index 0000000..2d46714
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.gui;
+
+import jalview.jbgui.GPDBSearchPanel;
+import jalview.jbgui.PDBDocFieldPreferences;
+import jalview.util.MessageManager;
+import jalview.ws.dbsources.PDBRestClient;
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.ws.uimodel.PDBRestRequest;
+import jalview.ws.uimodel.PDBRestResponse;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.swing.table.DefaultTableModel;
+
+@SuppressWarnings("serial")
+public class PDBSearchPanel extends GPDBSearchPanel
+{
+  private SequenceFetcher seqFetcher;
+
+  private IProgressIndicator progressIdicator;
+
+  private Collection<PDBDocField> wantedFields;
+
+  public PDBSearchPanel(SequenceFetcher seqFetcher)
+  {
+    this.seqFetcher = seqFetcher;
+    this.progressIdicator = (seqFetcher == null) ? null : seqFetcher
+            .getProgressIndicator();
+  }
+
+
+  /**
+   * Action performed when an input is detected on txt_search field.
+   */
+  @Override
+  public void txt_search_ActionPerformed()
+  {
+    boolean allowEmptySequence = false;
+    mainFrame.setTitle(MessageManager
+            .getString("label.pdb_sequence_getcher"));
+    tbl_summary.setModel(new DefaultTableModel());
+    if (txt_search.getText().trim().length() > 0)
+    {
+      long startTime = System.currentTimeMillis();
+
+      String searchTarget = ((PDBDocField) cmb_searchTarget
+              .getSelectedItem()).getCode();
+
+      wantedFields = PDBDocFieldPreferences
+              .getSearchSummaryFields();
+
+      PDBRestRequest request = new PDBRestRequest();
+      request.setAllowEmptySeq(allowEmptySequence);
+      request.setResponseSize(100);
+      request.setFieldToSearchBy(searchTarget + ":");
+      request.setSearchTerm(txt_search.getText());
+      request.setWantedFields(wantedFields);
+
+      PDBRestClient pdbRestCleint = new PDBRestClient();
+      PDBRestResponse resultList = pdbRestCleint.executeRequest(request);
+      if (resultList.getSearchSummary() != null)
+      {
+        tbl_summary.setModel(PDBRestResponse.getTableModel(request,
+                resultList.getSearchSummary()));
+      }
+
+      long endTime = System.currentTimeMillis();
+      int resultSetCount = resultList.getNumberOfItemsFound();
+      String result = (resultSetCount > 1) ? MessageManager
+              .getString("label.results") : MessageManager
+              .getString("label.result");
+      mainFrame.setTitle(frameTitle + " - " + resultSetCount + " " + result
+              + " (" + (endTime - startTime) + " milli secs)");
+    }
+  }
+
+  @Override
+  public void btn_ok_ActionPerformed()
+  {
+    loadSelectedPDBSequencesToAlignment();
+  }
+
+  @Override
+  public void btn_back_ActionPerformed()
+  {
+    mainFrame.dispose();
+    new SequenceFetcher(progressIdicator);
+  }
+
+  @Override
+  public void btn_cancel_ActionPerformed()
+  {
+    mainFrame.dispose();
+  }
+
+  /**
+   * Add the discovered/selected sequences to a target alignment window
+   */
+  public void loadSelectedPDBSequencesToAlignment()
+  {
+    mainFrame.dispose();
+    StringBuilder selectedIds = new StringBuilder();
+    int pdbIdCol = PDBRestClient.getPDBIdColumIndex(wantedFields, false);
+    int[] selectedRows = tbl_summary.getSelectedRows();
+    for (int summaryRow : selectedRows)
+    {
+      String pdbIdStr = tbl_summary.getValueAt(summaryRow, pdbIdCol)
+              .toString();
+      selectedIds.append(";").append(pdbIdStr);
+    }
+
+    String ids = selectedIds.deleteCharAt(0).toString();
+    seqFetcher.textArea.setText(ids);
+    Thread worker = new Thread(seqFetcher);
+    worker.start();
+  }
+
+  /**
+   * Populates search target combo-box options
+   */
+  public void populateCmbSearchTargetOptions()
+  {
+    List<PDBDocField> searchableTargets = new ArrayList<PDBDocField>();
+    searchableTargets.add(PDBDocField.PDB_ID);
+    searchableTargets.add(PDBDocField.PFAM_ACCESSION);
+    searchableTargets.add(PDBDocField.MOLECULE_TYPE);
+    searchableTargets.add(PDBDocField.MOLECULE_NAME);
+    searchableTargets.add(PDBDocField.UNIPROT_ACCESSION);
+    searchableTargets.add(PDBDocField.GENE_NAME);
+    searchableTargets.add(PDBDocField.GENUS);
+    searchableTargets.add(PDBDocField.ALL);
+
+    Collections.sort(searchableTargets, new Comparator<PDBDocField>()
+    {
+      @Override
+      public int compare(PDBDocField o1, PDBDocField o2)
+      {
+        return o1.getName().compareTo(o2.getName());
+      }
+    });
+
+    for (PDBDocField searchTarget : searchableTargets)
+    {
+      cmb_searchTarget.addItem(searchTarget);
+    }
+  }
+
+}
index 174772c..1263b71 100644 (file)
@@ -124,9 +124,6 @@ public class PopupMenu extends JPopupMenu
 
   protected JRadioButtonMenuItem RNAInteractionColour = new JRadioButtonMenuItem();
 
-  // protected JRadioButtonMenuItem covariationColour = new
-  // JRadioButtonMenuItem();
-
   JRadioButtonMenuItem noColourmenuItem = new JRadioButtonMenuItem();
 
   protected JCheckBoxMenuItem conservationMenuItem = new JCheckBoxMenuItem();
@@ -181,12 +178,6 @@ public class PopupMenu extends JPopupMenu
 
   JMenuItem pdbFromFile = new JMenuItem();
 
-  // JBPNote: Commented these out - Should add these services via the web
-  // services menu system.
-  // JMenuItem ContraFold = new JMenuItem();
-
-  // JMenuItem RNAFold = new JMenuItem();
-
   JMenuItem enterPDB = new JMenuItem();
 
   JMenuItem discoverPDB = new JMenuItem();
@@ -213,15 +204,12 @@ public class PopupMenu extends JPopupMenu
 
   JMenu jMenu1 = new JMenu();
 
-  JMenu structureMenu = new JMenu();
+  JMenuItem structureMenu = new JMenuItem();
 
   JMenu viewStructureMenu = new JMenu();
 
-  // JMenu colStructureMenu = new JMenu();
   JMenuItem editSequence = new JMenuItem();
 
-  // JMenuItem annotationMenuItem = new JMenuItem();
-
   JMenu groupLinksMenu;
 
   JMenuItem hideInsertions = new JMenuItem();
@@ -274,7 +262,6 @@ public class PopupMenu extends JPopupMenu
     colours.add(BLOSUM62Colour);
     colours.add(purinePyrimidineColour);
     colours.add(RNAInteractionColour);
-    // colours.add(covariationColour);
 
     for (int i = 0; i < jalview.io.FormatAdapter.WRITEABLE_FORMATS.length; i++)
     {
@@ -379,7 +366,6 @@ public class PopupMenu extends JPopupMenu
         {
           structureMenu.remove(viewStructureMenu);
         }
-        // structureMenu.remove(colStructureMenu);
       }
       if (ap.av.getAlignment().isNucleotide() == true)
       {
@@ -400,19 +386,8 @@ public class PopupMenu extends JPopupMenu
               @Override
               public void actionPerformed(ActionEvent e)
               {
-                // // System.out.println("1:"+structureLine);
-                // System.out.println("1:sname" + seq.getName());
-                // System.out.println("2:seq" + seq);
-                //
-                // // System.out.println("3:"+seq.getSequenceAsString());
-                // System.out.println("3:strucseq" + rnastruc);
-                // // System.out.println("4:struc"+seq.getRNA());
-                // System.out.println("5:name" + seq.getName());
-                // System.out.println("6:ap" + ap);
                 new AppVarna(structureLine, seq, seq.getSequenceAsString(),
                         rnastruc, seq.getName(), ap);
-                // new AppVarna(seq.getName(),seq,rnastruc,seq.getRNA(),
-                // seq.getName(), ap);
                 System.out.println("end");
               }
             });
@@ -420,7 +395,6 @@ public class PopupMenu extends JPopupMenu
           }
         }
 
-        // SequenceFeatures[] test = seq.getSequenceFeatures();
 
         if (seq.getAnnotation() != null)
         {
@@ -640,50 +614,6 @@ public class PopupMenu extends JPopupMenu
                 new PDBEntry[pdbe.size()]), pr = reppdb.values().toArray(
                 new PDBEntry[reppdb.size()]);
         final JMenuItem gpdbview, rpdbview;
-        if (pdbe.size() == 1)
-        {
-          structureMenu.add(gpdbview = new JMenuItem(MessageManager
-                  .formatMessage("label.view_structure_for", new Object[]
-                  { sqass.getDisplayId(false) })));
-        }
-        else
-        {
-          structureMenu.add(gpdbview = new JMenuItem(MessageManager
-                  .formatMessage("label.view_all_structures", new Object[]
-                  { new Integer(pdbe.size()).toString() })));
-        }
-        gpdbview.setToolTipText(MessageManager
-                .getString("label.open_new_jmol_view_with_all_structures_associated_current_selection_superimpose_using_alignment"));
-        gpdbview.addActionListener(new ActionListener()
-        {
-
-          @Override
-          public void actionPerformed(ActionEvent e)
-          {
-            new StructureViewer(ap.getStructureSelectionManager())
-                    .viewStructures(ap, pe, ap.av.collateForPDB(pe));
-          }
-        });
-        if (reppdb.size() > 1 && reppdb.size() < pdbe.size())
-        {
-          structureMenu.add(rpdbview = new JMenuItem(MessageManager
-                  .formatMessage(
-                          "label.view_all_representative_structures",
-                          new Object[]
-                          { new Integer(reppdb.size()).toString() })));
-          rpdbview.setToolTipText(MessageManager
-                  .getString("label.open_new_jmol_view_with_all_representative_structures_associated_current_selection_superimpose_using_alignment"));
-          rpdbview.addActionListener(new ActionListener()
-          {
-
-            @Override
-            public void actionPerformed(ActionEvent e)
-            {
-              new StructureViewer(ap.getStructureSelectionManager())
-                      .viewStructures(ap, pr, ap.av.collateForPDB(pr));
-            }
-          });
-        }
       }
     }
     else
@@ -1090,8 +1020,6 @@ public class PopupMenu extends JPopupMenu
       if (urlset != null)
       {
         int type = urlLink.getGroupURLType() & 3;
-        // System.out.println(urlLink.getGroupURLType()
-        // +" "+((String[])urlset[3])[0]);
         // first two bits ofurlLink type bitfield are sequenceids and sequences
         // TODO: FUTURE: ensure the groupURL menu structure can be generalised
         addshowLink(linkMenus[type], label
@@ -1402,32 +1330,7 @@ public class PopupMenu extends JPopupMenu
         pdbFromFile_actionPerformed();
       }
     });
-    // RNAFold.setText("From RNA Fold with predict2D");
-    // RNAFold.addActionListener(new ActionListener()
-    // {
-    // public void actionPerformed(ActionEvent e)
-    // {
-    // try {
-    // RNAFold_actionPerformed();
-    // } catch (Exception e1) {
-    // // TODO Auto-generated catch block
-    // e1.printStackTrace();
-    // }
-    // }
-    // });
-    // ContraFold.setText("From Contra Fold with predict2D");
-    // ContraFold.addActionListener(new ActionListener()
-    // {
-    // public void actionPerformed(ActionEvent e)
-    // {
-    // try {
-    // ContraFold_actionPerformed();
-    // } catch (Exception e1) {
-    // // TODO Auto-generated catch block
-    // e1.printStackTrace();
-    // }
-    // }
-    // });
+
     enterPDB.setText(MessageManager.getString("label.enter_pdb_id"));
     enterPDB.addActionListener(new ActionListener()
     {
@@ -1476,7 +1379,22 @@ public class PopupMenu extends JPopupMenu
       }
     });
     jMenu1.setText(MessageManager.getString("label.group"));
-    structureMenu.setText(MessageManager.getString("label.structure"));
+    structureMenu.setText(MessageManager.getString("label.view_structure"));
+    structureMenu.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent actionEvent)
+      {
+        SequenceI[] selectedSeqs = new SequenceI[]
+        { sequence };
+        if (ap.av.getSelectionGroup() != null)
+        {
+          selectedSeqs = ap.av.getSequenceSelection();
+        }
+        new StructureChooser(selectedSeqs, sequence, ap);
+      }
+    });
+
     viewStructureMenu.setText(MessageManager
             .getString("label.view_structure"));
     // colStructureMenu.setText("Colour By Structure");
@@ -1564,10 +1482,8 @@ public class PopupMenu extends JPopupMenu
     if (ap.getAlignment().isNucleotide())
     {
       // JBPNote - commented since the colourscheme isn't functional
-      // colourMenu.add(RNAInteractionColour);
       colourMenu.add(purinePyrimidineColour);
     }
-    // colourMenu.add(covariationColour);
     colourMenu.add(userDefinedColour);
 
     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
@@ -1593,7 +1509,6 @@ public class PopupMenu extends JPopupMenu
     colourMenu.addSeparator();
     colourMenu.add(abovePIDColour);
     colourMenu.add(conservationMenuItem);
-    // colourMenu.add(annotationMenuItem);
     editMenu.add(copy);
     editMenu.add(cut);
     editMenu.add(editSequence);
@@ -1614,9 +1529,6 @@ public class PopupMenu extends JPopupMenu
     jMenu1.add(showColourText);
     jMenu1.add(outline);
     jMenu1.add(displayNonconserved);
-    structureMenu.add(pdbMenu);
-    structureMenu.add(viewStructureMenu);
-    // structureMenu.add(colStructureMenu);
     noColourmenuItem.setText(MessageManager.getString("label.none"));
     noColourmenuItem.addActionListener(new java.awt.event.ActionListener()
     {
@@ -2548,18 +2460,7 @@ public class PopupMenu extends JPopupMenu
 
   }
 
-  // JBNote: commented out - these won't be instantiated here...!
-  // public void RNAFold_actionPerformed() throws Exception
-  // {
-  // Predict2D P2D = new Predict2D();
-  // P2D.getStructure2DFromRNAFold("toto");
-  // }
-  //
-  // public void ContraFold_actionPerformed() throws Exception
-  // {
-  // Predict2D P2D = new Predict2D();
-  // P2D.getStructure2DFromContraFold("toto");
-  // }
+
   public void enterPDB_actionPerformed()
   {
     String id = JOptionPane.showInternalInputDialog(Desktop.desktop,
index 06f4b0f..6a0c712 100755 (executable)
@@ -59,7 +59,42 @@ import com.stevesoft.pat.Regex;
 
 public class SequenceFetcher extends JPanel implements Runnable
 {
-  // ASequenceFetcher sfetch;
+  JLabel dbeg = new JLabel();
+
+  JDatabaseTree database;
+
+  JButton databaseButt;
+
+  JLabel jLabel1 = new JLabel();
+
+  JCheckBox replacePunctuation = new JCheckBox();
+
+  JButton ok = new JButton();
+
+  JButton clear = new JButton();
+
+  JButton example = new JButton();
+
+  JButton close = new JButton();
+
+  JPanel jPanel1 = new JPanel();
+
+  JTextArea textArea = new JTextArea();
+
+  JScrollPane jScrollPane1 = new JScrollPane();
+
+  JPanel jPanel2 = new JPanel();
+
+  JPanel jPanel3 = new JPanel();
+
+  JPanel jPanel4 = new JPanel();
+
+  BorderLayout borderLayout1 = new BorderLayout();
+
+  BorderLayout borderLayout2 = new BorderLayout();
+
+  BorderLayout borderLayout3 = new BorderLayout();
+
   JInternalFrame frame;
 
   IProgressIndicator guiWindow;
@@ -80,6 +115,7 @@ public class SequenceFetcher extends JPanel implements Runnable
 
   private static Thread initingThread = null;
 
+  int debounceTrap = 0;
   /**
    * Blocking method that initialises and returns the shared instance of the
    * SequenceFetcher client
@@ -154,9 +190,10 @@ public class SequenceFetcher extends JPanel implements Runnable
     return sfetch;
   }
 
+  private IProgressIndicator progressIndicator;
   public SequenceFetcher(IProgressIndicator guiIndic)
   {
-    final IProgressIndicator guiWindow = guiIndic;
+    this.progressIndicator = guiIndic;
     final SequenceFetcher us = this;
     // launch initialiser thread
     Thread sf = new Thread(new Runnable()
@@ -165,9 +202,9 @@ public class SequenceFetcher extends JPanel implements Runnable
       @Override
       public void run()
       {
-        if (getSequenceFetcherSingleton(guiWindow) != null)
+        if (getSequenceFetcherSingleton(progressIndicator) != null)
         {
-          us.initGui(guiWindow);
+          us.initGui(progressIndicator);
         }
         else
         {
@@ -318,37 +355,21 @@ public class SequenceFetcher extends JPanel implements Runnable
     databaseButt.setFont(JvSwingUtils.getLabelFont());
     database.addActionListener(new ActionListener()
     {
-
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        try
-        {
-          databaseButt.setText(database.getSelectedItem()
-                  + (database.getSelectedSources().size() > 1 ? " (and "
-                          + database.getSelectedSources().size()
-                          + " others)" : ""));
-          String eq = database.getExampleQueries();
-          dbeg.setText(MessageManager.formatMessage(
-                  "label.example_query_param", new String[]
-                  { eq }));
-          boolean enablePunct = !(eq != null && eq.indexOf(",") > -1);
-          for (DbSourceProxy dbs : database.getSelectedSources())
-          {
-            if (dbs instanceof jalview.ws.dbsources.das.datamodel.DasSequenceSource)
-            {
-              enablePunct = false;
-              break;
-            }
-          }
-          replacePunctuation.setEnabled(enablePunct);
+        debounceTrap++;
+        String currentSelection = database.getSelectedItem();
 
-        } catch (Exception ex)
+        if (!currentSelection.equalsIgnoreCase("pdb"))
+        {
+            otherSourceAction();
+        }
+        if (currentSelection.equalsIgnoreCase("pdb") && ((debounceTrap % 2) == 0))
         {
-          dbeg.setText("");
-          replacePunctuation.setEnabled(true);
+          pdbSourceAction();
         }
-        jPanel2.repaint();
+
       }
     });
     dbeg.setText("");
@@ -367,6 +388,44 @@ public class SequenceFetcher extends JPanel implements Runnable
 
   }
 
+  private void pdbSourceAction()
+  {
+    databaseButt.setText(database.getSelectedItem());
+    new PDBSearchPanel(this);
+    frame.dispose();
+  }
+
+  private void otherSourceAction()
+  {
+    try
+    {
+      databaseButt.setText(database.getSelectedItem()
+              + (database.getSelectedSources().size() > 1 ? " (and "
+                      + database.getSelectedSources().size() + " others)"
+                      : ""));
+      String eq = database.getExampleQueries();
+      dbeg.setText(MessageManager.formatMessage(
+              "label.example_query_param", new String[]
+              { eq }));
+      boolean enablePunct = !(eq != null && eq.indexOf(",") > -1);
+      for (DbSourceProxy dbs : database.getSelectedSources())
+      {
+        if (dbs instanceof jalview.ws.dbsources.das.datamodel.DasSequenceSource)
+        {
+          enablePunct = false;
+          break;
+        }
+      }
+      replacePunctuation.setEnabled(enablePunct);
+
+    } catch (Exception ex)
+    {
+      dbeg.setText("");
+      replacePunctuation.setEnabled(true);
+    }
+    jPanel2.repaint();
+  }
+
   protected void example_actionPerformed()
   {
     DbSourceProxy db = null;
@@ -385,41 +444,7 @@ public class SequenceFetcher extends JPanel implements Runnable
     jPanel3.repaint();
   }
 
-  JLabel dbeg = new JLabel();
-
-  JDatabaseTree database;
-
-  JButton databaseButt;
 
-  JLabel jLabel1 = new JLabel();
-
-  JCheckBox replacePunctuation = new JCheckBox();
-
-  JButton ok = new JButton();
-
-  JButton clear = new JButton();
-
-  JButton example = new JButton();
-
-  JButton close = new JButton();
-
-  JPanel jPanel1 = new JPanel();
-
-  JTextArea textArea = new JTextArea();
-
-  JScrollPane jScrollPane1 = new JScrollPane();
-
-  JPanel jPanel2 = new JPanel();
-
-  JPanel jPanel3 = new JPanel();
-
-  JPanel jPanel4 = new JPanel();
-
-  BorderLayout borderLayout1 = new BorderLayout();
-
-  BorderLayout borderLayout2 = new BorderLayout();
-
-  BorderLayout borderLayout3 = new BorderLayout();
 
   public void close_actionPerformed(ActionEvent e)
   {
@@ -861,4 +886,14 @@ public class SequenceFetcher extends JPanel implements Runnable
       }
     });
   }
+
+  public IProgressIndicator getProgressIndicator()
+  {
+    return progressIndicator;
+  }
+
+  public void setProgressIndicator(IProgressIndicator progressIndicator)
+  {
+    this.progressIndicator = progressIndicator;
+  }
 }
diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java
new file mode 100644 (file)
index 0000000..3a54cc1
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.gui;
+
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.jbgui.GStructureChooser;
+import jalview.jbgui.PDBDocFieldPreferences;
+import jalview.util.MessageManager;
+import jalview.ws.dbsources.PDBRestClient;
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.ws.uimodel.PDBRestRequest;
+import jalview.ws.uimodel.PDBRestResponse;
+import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
+
+import java.awt.event.ItemEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+
+/**
+ * Provides the behaviors for the Structure chooser Panel
+ * 
+ * @author tcnofoegbu
+ *
+ */
+@SuppressWarnings("serial")
+public class StructureChooser extends GStructureChooser
+{
+  private boolean structuresDiscovered = false;
+
+  private SequenceI selectedSequence;
+
+  private SequenceI[] selectedSequences;
+
+  private IProgressIndicator progressIndicator;
+
+  private Collection<PDBResponseSummary> discoveredStructuresSet;
+
+  private PDBRestRequest lastPdbRequest;
+
+  private PDBRestClient pdbRestCleint;
+
+  private String selectedPdbFileName;
+
+  private boolean isValidPBDEntry;
+
+  public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
+          AlignmentPanel ap)
+  {
+    this.ap = ap;
+    this.selectedSequence = selectedSeq;
+    this.selectedSequences = selectedSeqs;
+    this.progressIndicator = (ap == null) ? null : ap.alignFrame;
+    init();
+  }
+
+  /**
+   * Initializes parameters used by the Structure Chooser Panel
+   */
+  public void init()
+  {
+    Thread discoverPDBStructuresThread = new Thread(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        long startTime = System.currentTimeMillis();
+        String msg = MessageManager.getString("status.fetching_db_refs");
+        updateProgressIndicator(msg, startTime);
+        fetchStructuresMetaData();
+        populateFilterComboBox();
+        updateProgressIndicator(null, startTime);
+        mainFrame.setVisible(true);
+        updateCurrentView();
+      }
+    });
+    discoverPDBStructuresThread.start();
+  }
+
+  /**
+   * Updates the progress indicator with the specified message
+   * 
+   * @param message
+   *          displayed message for the operation
+   * @param id
+   *          unique handle for this indicator
+   */
+  public void updateProgressIndicator(String message, long id)
+  {
+    if (progressIndicator != null)
+    {
+      progressIndicator.setProgressBar(message, id);
+    }
+  }
+
+  /**
+   * Retrieve meta-data for all the structure(s) for a given sequence(s) in a
+   * selection group
+   */
+  public void fetchStructuresMetaData()
+  {
+    long startTime = System.currentTimeMillis();
+    Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
+            .getStructureSummaryFields();
+
+    discoveredStructuresSet = new LinkedHashSet<PDBResponseSummary>();
+    for (SequenceI seq : selectedSequences)
+    {
+      PDBRestRequest pdbRequest = new PDBRestRequest();
+      pdbRequest.setAllowEmptySeq(false);
+      pdbRequest.setResponseSize(500);
+      pdbRequest.setFieldToSearchBy("(text:");
+      pdbRequest.setWantedFields(wantedFields);
+      pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+      pdbRequest.setAssociatedSequence(seq.getName());
+      pdbRestCleint = new PDBRestClient();
+      PDBRestResponse resultList = pdbRestCleint.executeRequest(pdbRequest);
+      lastPdbRequest = pdbRequest;
+      if (resultList.getSearchSummary() != null
+              && !resultList.getSearchSummary().isEmpty())
+      {
+        discoveredStructuresSet.addAll(resultList.getSearchSummary());
+        updateSequenceDbRef(seq, resultList.getSearchSummary());
+      }
+    }
+
+    int noOfStructuresFound = 0;
+    if (discoveredStructuresSet != null
+            && !discoveredStructuresSet.isEmpty())
+    {
+      tbl_summary.setModel(PDBRestResponse.getTableModel(lastPdbRequest,
+              discoveredStructuresSet));
+      structuresDiscovered = true;
+      noOfStructuresFound = discoveredStructuresSet.size();
+    }
+    String totalTime = (System.currentTimeMillis() - startTime)
+            + " milli secs";
+    mainFrame.setTitle("Structure Chooser - " + noOfStructuresFound
+            + " Found (" + totalTime + ")");
+  }
+
+  /**
+   * Update the DBRef entry for a given sequence with values retrieved from
+   * PDBResponseSummary
+   * 
+   * @param seq
+   *          the Sequence to update its DBRef entry
+   * @param responseSummaries
+   *          a collection of PDBResponseSummary
+   */
+  public void updateSequenceDbRef(SequenceI seq,
+          Collection<PDBResponseSummary> responseSummaries)
+  {
+    for (PDBResponseSummary response : responseSummaries)
+    {
+      PDBEntry newEntry = new PDBEntry();
+      newEntry.setId(response.getPdbId());
+      newEntry.setType("PDB");
+      seq.getDatasetSequence().addPDBId(newEntry);
+    }
+  }
+
+  /**
+   * Builds a query string for a given sequences using its DBRef entries
+   * 
+   * @param seq
+   *          the sequences to build a query for
+   * @return the built query string
+   */
+  @SuppressWarnings("unchecked")
+  public static String buildQuery(SequenceI seq)
+  {
+    String query = seq.getName();
+    StringBuilder queryBuilder = new StringBuilder();
+    int count = 0;
+
+    if (seq.getPDBId() != null)
+    {
+      for (PDBEntry entry : (Vector<PDBEntry>) seq.getPDBId())
+      {
+        queryBuilder.append("text:").append(entry.getId()).append(" OR ");
+      }
+    }
+
+    if (seq.getDBRef() != null && seq.getDBRef().length != 0)
+    {
+      for (DBRefEntry dbRef : seq.getDBRef())
+      {
+        queryBuilder.append("text:")
+                .append(dbRef.getAccessionId().replaceAll("GO:", ""))
+                .append(" OR ");
+        ++count;
+        if (count > 10)
+        {
+          break;
+        }
+      }
+      int endIndex = queryBuilder.lastIndexOf(" OR ");
+      query = queryBuilder.toString().substring(5, endIndex);
+    }
+    return query;
+  }
+
+  /**
+   * Filters a given list of discovered structures based on supplied argument
+   * 
+   * @param fieldToFilterBy
+   *          the field to filter by
+   */
+  public void filterResultSet(final String fieldToFilterBy)
+  {
+    Thread filterThread = new Thread(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        long startTime = System.currentTimeMillis();
+        try
+        {
+          lbl_loading.setVisible(true);
+
+          Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
+                  .getStructureSummaryFields();
+          Collection<PDBResponseSummary> filteredResponse = new HashSet<PDBResponseSummary>();
+          for (SequenceI seq : selectedSequences)
+          {
+            PDBRestRequest pdbRequest = new PDBRestRequest();
+            pdbRequest.setAllowEmptySeq(false);
+            pdbRequest.setResponseSize(1);
+            pdbRequest.setFieldToSearchBy("(text:");
+            pdbRequest.setFieldToSortBy(fieldToFilterBy,
+                    !chk_invertFilter.isSelected());
+            pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+            pdbRequest.setWantedFields(wantedFields);
+            pdbRequest.setAssociatedSequence(seq.getName());
+            pdbRestCleint = new PDBRestClient();
+            PDBRestResponse resultList = pdbRestCleint
+                    .executeRequest(pdbRequest);
+            lastPdbRequest = pdbRequest;
+            if (resultList.getSearchSummary() != null
+                    && !resultList.getSearchSummary().isEmpty())
+            {
+              filteredResponse.addAll(resultList.getSearchSummary());
+            }
+          }
+
+          if (!filteredResponse.isEmpty())
+          {
+            final int filterResponseCount = filteredResponse.size();
+            Collection<PDBResponseSummary> reorderedStructuresSet = new LinkedHashSet<PDBResponseSummary>();
+            reorderedStructuresSet.addAll(filteredResponse);
+            reorderedStructuresSet.addAll(discoveredStructuresSet);
+            tbl_summary.setModel(PDBRestResponse.getTableModel(
+                    lastPdbRequest, reorderedStructuresSet));
+
+            // Update table selection model here
+            tbl_summary.addRowSelectionInterval(0, filterResponseCount - 1);
+
+          }
+
+          lbl_loading.setVisible(false);
+          String totalTime = (System.currentTimeMillis() - startTime)
+                  + " milli secs";
+          mainFrame.setTitle("Structure Chooser - Filter time ("
+                  + totalTime + ")");
+
+          validateSelections();
+        } catch (Exception e)
+        {
+          e.printStackTrace();
+        }
+      }
+    });
+    filterThread.start();
+  }
+
+
+  /**
+   * Handles action event for btn_pdbFromFile
+   */
+  public void pdbFromFile_actionPerformed()
+  {
+    jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
+    chooser.setFileView(new jalview.io.JalviewFileView());
+    chooser.setDialogTitle(MessageManager.formatMessage(
+            "label.select_pdb_file_for", new String[]
+            { selectedSequence.getDisplayId(false) }));
+    chooser.setToolTipText(MessageManager.formatMessage(
+            "label.load_pdb_file_associate_with_sequence", new String[]
+            { selectedSequence.getDisplayId(false) }));
+
+    int value = chooser.showOpenDialog(null);
+    if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
+    {
+      selectedPdbFileName = chooser.getSelectedFile().getPath();
+      jalview.bin.Cache.setProperty("LAST_DIRECTORY", selectedPdbFileName);
+      validateSelections();
+    }
+  }
+
+  /**
+   * Populates the filter combo-box options dynamically depending on discovered
+   * structures
+   */
+  protected void populateFilterComboBox()
+  {
+    if (isStructuresDiscovered())
+    {
+      cmb_filterOption.addItem(new FilterOption("Best Quality",
+              PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Best UniProt Coverage",
+              PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Highest Resolution",
+              PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Highest Protein Chain",
+              PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Highest Bound Molecules",
+              PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Highest Polymer Residues",
+              PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
+    }
+    cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
+            VIEWS_ENTER_ID));
+    cmb_filterOption.addItem(new FilterOption("From File", "-",
+            VIEWS_FROM_FILE));
+  }
+
+  /**
+   * Updates the displayed view based on the selected filter option
+   */
+  protected void updateCurrentView()
+  {
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+    layout_switchableViews.show(pnl_switchableViews,
+            selectedFilterOpt.getView());
+    String filterTitle = mainFrame.getTitle();
+    mainFrame.setTitle(frameTitle);
+    chk_invertFilter.setVisible(false);
+    if (selectedFilterOpt.getView() == VIEWS_FILTER)
+    {
+      mainFrame.setTitle(filterTitle);
+      chk_invertFilter.setVisible(true);
+      filterResultSet(selectedFilterOpt.getValue());
+    }
+    else
+    {
+      idInputAssSeqPanel.loadCmbAssSeq();
+      fileChooserAssSeqPanel.loadCmbAssSeq();
+    }
+    validateSelections();
+  }
+
+  /**
+   * Validates user selection and activates the view button if all parameters
+   * are correct
+   */
+  public void validateSelections()
+  {
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+    btn_view.setEnabled(false);
+    String currentView = selectedFilterOpt.getView();
+    if (currentView == VIEWS_FILTER)
+    {
+      if (tbl_summary.getSelectedRows().length > 0)
+      {
+        btn_view.setEnabled(true);
+      }
+    }
+    else if (currentView == VIEWS_ENTER_ID)
+    {
+      validateAssociationEnterPdb();
+    }
+    else if (currentView == VIEWS_FROM_FILE)
+    {
+      validateAssociationFromFile();
+    }
+  }
+
+  /**
+   * Validates inputs from the Manual PDB entry panel
+   */
+  public void validateAssociationEnterPdb()
+  {
+    AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) idInputAssSeqPanel
+            .getCmb_assSeq().getSelectedItem();
+    lbl_pdbManualFetchStatus.setIcon(errorImage);
+    if (selectedSequences.length == 1
+            || !assSeqOpt.getName().equalsIgnoreCase(
+                    "-Select Associated Seq-"))
+    {
+      txt_search.setEnabled(true);
+      if (isValidPBDEntry)
+      {
+        btn_view.setEnabled(true);
+        lbl_pdbManualFetchStatus.setIcon(goodImage);
+      }
+    }
+    else
+    {
+      txt_search.setEnabled(false);
+      lbl_pdbManualFetchStatus.setIcon(errorImage);
+    }
+  }
+
+  /**
+   * Validates inputs for the manual PDB file selection options
+   */
+  public void validateAssociationFromFile()
+  {
+    AssociateSeqOptions assSeqOpt = (AssociateSeqOptions) fileChooserAssSeqPanel
+            .getCmb_assSeq().getSelectedItem();
+    lbl_fromFileStatus.setIcon(errorImage);
+    if (selectedSequences.length == 1
+            || (assSeqOpt != null
+            && !assSeqOpt.getName().equalsIgnoreCase(
+                    "-Select Associated Seq-")))
+    {
+      btn_pdbFromFile.setEnabled(true);
+      if (selectedPdbFileName != null && selectedPdbFileName.length() > 0)
+      {
+        btn_view.setEnabled(true);
+        lbl_fromFileStatus.setIcon(goodImage);
+      }
+    }
+    else
+    {
+      btn_pdbFromFile.setEnabled(false);
+      lbl_fromFileStatus.setIcon(errorImage);
+    }
+  }
+
+  @Override
+  public void cmbAssSeqStateChanged()
+  {
+    validateSelections();
+  }
+
+  /**
+   * Handles the state change event for the 'filter' combo-box and 'invert'
+   * check-box
+   */
+  @Override
+  protected void stateChanged(ItemEvent e)
+  {
+    if (e.getSource() instanceof JCheckBox)
+    {
+      updateCurrentView();
+    }
+    else
+    {
+      if (e.getStateChange() == ItemEvent.SELECTED)
+      {
+        updateCurrentView();
+      }
+    }
+
+  }
+
+  /**
+   * Handles action event for btn_ok
+   */
+  @Override
+  public void ok_ActionPerformed()
+  {
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+    String currentView = selectedFilterOpt.getView();
+    if (currentView == VIEWS_FILTER)
+    {
+      int pdbIdCol = PDBRestClient.getPDBIdColumIndex(
+              lastPdbRequest.getWantedFields(), true);
+      int[] selectedRows = tbl_summary.getSelectedRows();
+      PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
+      int count = 0;
+      for (int summaryRow : selectedRows)
+      {
+        String pdbIdStr = tbl_summary.getValueAt(summaryRow, pdbIdCol)
+                .toString();
+        PDBEntry pdbEntry = new PDBEntry();
+        pdbEntry.setId(pdbIdStr);
+        pdbEntry.setType("PDB");
+        pdbEntriesToView[count++] = pdbEntry;
+      }
+      new StructureViewer(ap.getStructureSelectionManager())
+              .viewStructures(ap, pdbEntriesToView,
+                      ap.av.collateForPDB(pdbEntriesToView));
+    }
+    else if (currentView == VIEWS_ENTER_ID)
+    {
+      selectedSequence = ((AssociateSeqOptions) idInputAssSeqPanel
+              .getCmb_assSeq().getSelectedItem()).getSequence();
+      PDBEntry pdbEntry = new PDBEntry();
+      pdbEntry.setId(txt_search.getText());
+      pdbEntry.setType("PDB");
+      selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
+      PDBEntry[] pdbEntriesToView = new PDBEntry[]
+      { pdbEntry };
+      new StructureViewer(ap.getStructureSelectionManager())
+              .viewStructures(ap, pdbEntriesToView,
+                      ap.av.collateForPDB(pdbEntriesToView));
+    }
+    else if (currentView == VIEWS_FROM_FILE)
+    {
+      selectedSequence = ((AssociateSeqOptions) fileChooserAssSeqPanel
+              .getCmb_assSeq().getSelectedItem()).getSequence();
+      new AssociatePdbFileWithSeq().associatePdbWithSeq(
+              selectedPdbFileName, jalview.io.AppletFormatAdapter.FILE,
+              selectedSequence, true, Desktop.instance);
+    }
+    mainFrame.dispose();
+  }
+
+  /**
+   * Populates the combo-box used in associating manually fetched structures to
+   * a unique sequence when more than one sequence selection is made.
+   */
+  public void populateCmbAssociateSeqOptions(
+          JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq)
+  {
+    cmb_assSeq.removeAllItems();
+    cmb_assSeq.addItem(new AssociateSeqOptions("-Select Associated Seq-",
+            null));
+    // cmb_assSeq.addItem(new AssociateSeqOptions("Auto Detect", null));
+    lbl_associateSeq.setVisible(false);
+    if (selectedSequences.length > 1)
+    {
+      for (SequenceI seq : selectedSequences)
+      {
+        cmb_assSeq.addItem(new AssociateSeqOptions(seq));
+      }
+    }
+    else
+    {
+      String seqName = selectedSequence.getDisplayId(false);
+      seqName = seqName.length() <= 40 ? seqName : seqName.substring(0, 39);
+      lbl_associateSeq.setText(seqName);
+      lbl_associateSeq.setVisible(true);
+      cmb_assSeq.setVisible(false);
+    }
+  }
+
+  public boolean isStructuresDiscovered()
+  {
+    return structuresDiscovered;
+  }
+
+  public void setStructuresDiscovered(boolean structuresDiscovered)
+  {
+    this.structuresDiscovered = structuresDiscovered;
+  }
+
+  public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
+  {
+    return discoveredStructuresSet;
+  }
+
+  @Override
+  protected void txt_search_ActionPerformed()
+  {
+    isValidPBDEntry = false;
+    if (txt_search.getText().length() > 0)
+    {
+      List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+      wantedFields.add(PDBDocField.PDB_ID);
+      PDBRestRequest pdbRequest = new PDBRestRequest();
+      pdbRequest.setAllowEmptySeq(false);
+      pdbRequest.setResponseSize(1);
+      pdbRequest.setFieldToSearchBy("(pdb_id:");
+      pdbRequest.setWantedFields(wantedFields);
+      pdbRequest.setSearchTerm(txt_search.getText() + ")");
+      pdbRequest.setAssociatedSequence(selectedSequence.getName());
+      pdbRestCleint = new PDBRestClient();
+      PDBRestResponse resultList = pdbRestCleint.executeRequest(pdbRequest);
+      if (resultList.getSearchSummary() != null
+              && resultList.getSearchSummary().size() > 0)
+      {
+        isValidPBDEntry = true;
+      }
+    }
+    validateSelections();
+  }
+
+  @Override
+  public void tabRefresh()
+  {
+    if (selectedSequences != null)
+    {
+      Thread refreshThread = new Thread(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          fetchStructuresMetaData();
+          filterResultSet(((FilterOption) cmb_filterOption
+                  .getSelectedItem()).getValue());
+        }
+      });
+      refreshThread.start();
+    }
+  }
+
+}
diff --git a/src/jalview/jbgui/GPDBSearchPanel.java b/src/jalview/jbgui/GPDBSearchPanel.java
new file mode 100644 (file)
index 0000000..29a0014
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.jbgui;
+
+import jalview.gui.Desktop;
+import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
+import jalview.util.MessageManager;
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+/**
+ * GUI layout for PDB Fetch Panel
+ * 
+ * @author tcnofoegbu
+ *
+ */
+@SuppressWarnings("serial")
+public abstract class GPDBSearchPanel extends JPanel
+{
+  protected String frameTitle = MessageManager
+          .getString("label.pdb_sequence_getcher");
+
+  protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
+
+  protected JComboBox<PDBDocField> cmb_searchTarget = new JComboBox<PDBDocField>();
+
+  protected JButton btn_ok = new JButton();
+
+  protected JButton btn_back = new JButton();
+  
+  protected JButton btn_cancel = new JButton();
+  
+  protected JTextField txt_search = new JTextField(20);
+  
+  protected JTable tbl_summary = new JTable();
+
+  protected JScrollPane scrl_searchResult = new JScrollPane(
+tbl_summary);
+
+  private JTabbedPane tabbedPane = new JTabbedPane();
+
+  private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
+          PreferenceSource.SEARCH_SUMMARY);
+
+  private JPanel pnl_actions = new JPanel();
+
+  private JPanel pnl_results = new JPanel();
+
+  private JPanel pnl_inputs = new JPanel();
+
+  private BorderLayout mainLayout = new BorderLayout();
+
+  public GPDBSearchPanel()
+  {
+    try
+    {
+      jbInit();
+      mainFrame.invalidate();
+      mainFrame.pack();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Initializes the GUI default properties
+   * 
+   * @throws Exception
+   */
+  private void jbInit() throws Exception
+  {
+    tbl_summary.setAutoCreateRowSorter(true);
+    btn_back.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_back.setText(MessageManager.getString("action.back"));
+    btn_back.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_back_ActionPerformed();
+      }
+    });
+    btn_ok.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_ok.setText(MessageManager.getString("action.ok"));
+    btn_ok.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_ok_ActionPerformed();
+      }
+    });
+    btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_cancel.setText(MessageManager.getString("action.cancel"));
+    btn_cancel.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_cancel_ActionPerformed();
+      }
+    });
+
+    scrl_searchResult.setPreferredSize(new Dimension(500, 300));
+    scrl_searchResult
+            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+
+    cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12));
+    cmb_searchTarget.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+    });
+
+    populateCmbSearchTargetOptions();
+
+    txt_search.setFont(new java.awt.Font("Verdana", 0, 12));
+    txt_search.getDocument().addDocumentListener(new DocumentListener()
+    {
+      @Override
+      public void insertUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+
+      @Override
+      public void changedUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+    });
+
+    final String searchTabTitle = MessageManager
+            .getString("label.search_result");
+    ChangeListener changeListener = new ChangeListener()
+    {
+      public void stateChanged(ChangeEvent changeEvent)
+      {
+        JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
+                .getSource();
+        int index = sourceTabbedPane.getSelectedIndex();
+        if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
+        {
+          txt_search_ActionPerformed();
+        }
+      }
+    };
+    tabbedPane.addChangeListener(changeListener);
+    tabbedPane.setPreferredSize(new Dimension(500, 300));
+    tabbedPane.add(searchTabTitle, scrl_searchResult);
+    tabbedPane.add(
+            MessageManager.getString("label.configure_displayed_columns"),
+            pdbDocFieldPrefs);
+
+    pnl_actions.add(btn_back);
+    pnl_actions.add(btn_ok);
+    pnl_actions.add(btn_cancel);
+
+    pnl_results.add(tabbedPane);
+    pnl_inputs.add(cmb_searchTarget);
+    pnl_inputs.add(txt_search);
+
+    this.setLayout(mainLayout);
+    this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
+    this.add(pnl_results, java.awt.BorderLayout.CENTER);
+    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
+    mainFrame.setVisible(true);
+    mainFrame.setContentPane(this);
+    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+    Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
+  }
+
+  public JComboBox<PDBDocField> getCmbSearchTarget()
+  {
+    return cmb_searchTarget;
+  }
+
+  public JTextField getTxtSearch()
+  {
+    return txt_search;
+  }
+
+  public JInternalFrame getMainFrame()
+  {
+    return mainFrame;
+  }
+
+  public abstract void txt_search_ActionPerformed();
+
+  public abstract void btn_ok_ActionPerformed();
+
+  public abstract void btn_back_ActionPerformed();
+
+  public abstract void btn_cancel_ActionPerformed();
+
+  public abstract void populateCmbSearchTargetOptions();
+
+}
index bec4cdb..1d35477 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.jbgui;
 
 import jalview.gui.JvSwingUtils;
 import jalview.gui.StructureViewer.ViewerType;
+import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -149,6 +150,7 @@ public class GPreferences extends JPanel
 
   protected JTextField chimeraPath = new JTextField();
 
+
   /*
    * Colours tab components
    */
@@ -745,8 +747,8 @@ public class GPreferences extends JPanel
             .getString("label.structure_options")));
     structureTab.setLayout(null);
     final int width = 400;
-    final int height = 23;
-    final int lineSpacing = 30;
+    final int height = 22;
+    final int lineSpacing = 25;
     int ypos = 30;
 
     structFromPdb.setFont(verdana11);
@@ -840,9 +842,23 @@ public class GPreferences extends JPanel
     });
     structureTab.add(chimeraPath);
 
+    ypos += lineSpacing;
+    // scrl_pdbDocFieldConfig.setPreferredSize(new Dimension(450, 100));
+    // scrl_pdbDocFieldConfig
+    // .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+    // scrl_pdbDocFieldConfig.setBounds();
+    PDBDocFieldPreferences docFieldPref = new PDBDocFieldPreferences(
+            PreferenceSource.PREFERENCES);
+    docFieldPref.setBounds(new Rectangle(10, ypos + 5, 450, 120));
+    structureTab.add(docFieldPref);
+
+
+
+
     return structureTab;
   }
 
+
   /**
    * Action on choosing a structure viewer from combobox options.
    * 
diff --git a/src/jalview/jbgui/GStructureChooser.java b/src/jalview/jbgui/GStructureChooser.java
new file mode 100644 (file)
index 0000000..b79a7f7
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.jbgui;
+
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.Desktop;
+import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
+import jalview.util.MessageManager;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+@SuppressWarnings("serial")
+/**
+ * GUI layout for structure chooser 
+ * @author tcnofoegbu
+ *
+ */
+public abstract class GStructureChooser extends JPanel implements
+        ItemListener
+{
+  protected String frameTitle = MessageManager
+          .getString("label.structure_chooser");
+
+  protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
+
+  protected JComboBox<FilterOption> cmb_filterOption = new JComboBox<FilterOption>();
+
+  protected AlignmentPanel ap;
+
+  protected JLabel lbl_result = new JLabel(
+          MessageManager.getString("label.select"));
+
+  protected JButton btn_view = new JButton();
+
+  protected JButton btn_cancel = new JButton();
+
+  protected JButton btn_pdbFromFile = new JButton();
+
+  protected JTextField txt_search = new JTextField(14);
+
+  private JPanel pnl_actions = new JPanel();
+
+  private JPanel pnl_filter = new JPanel();
+
+  private JPanel pnl_idInput = new JPanel(new FlowLayout());
+
+  private JPanel pnl_fileChooser = new JPanel(new FlowLayout());
+
+  private JPanel pnl_idInputBL = new JPanel(new BorderLayout());
+
+  private JPanel pnl_fileChooserBL = new JPanel(new BorderLayout());
+
+  protected JPanel pnl_switchableViews = new JPanel(new CardLayout());
+
+  protected CardLayout layout_switchableViews = (CardLayout) (pnl_switchableViews
+          .getLayout());
+
+  private BorderLayout mainLayout = new BorderLayout();
+
+  protected JCheckBox chk_rememberSettings = new JCheckBox(
+          MessageManager.getString("label.dont_ask_me_again"));
+
+  protected JCheckBox chk_invertFilter = new JCheckBox(
+          MessageManager.getString("label.invert"));
+
+  protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
+          "/images/loading.gif"));
+
+  protected ImageIcon goodImage = new ImageIcon(getClass().getResource(
+          "/images/good.png"));
+
+  protected ImageIcon errorImage = new ImageIcon(getClass().getResource(
+          "/images/error.png"));
+
+  protected JLabel lbl_loading = new JLabel(loadingImage);
+
+  protected JLabel lbl_pdbManualFetchStatus = new JLabel(errorImage);
+
+  protected JLabel lbl_fromFileStatus = new JLabel(errorImage);
+
+
+  protected AssciateSeqPanel idInputAssSeqPanel = new AssciateSeqPanel();
+
+  protected AssciateSeqPanel fileChooserAssSeqPanel = new AssciateSeqPanel();
+
+  protected static final String VIEWS_FILTER = "VIEWS_FILTER";
+
+  protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
+
+  protected static final String VIEWS_ENTER_ID = "VIEWS_ENTER_ID";
+
+  protected JTable tbl_summary = new JTable();
+
+  protected JScrollPane scrl_foundStructures = new JScrollPane(
+          tbl_summary);
+
+  private JTabbedPane tabbedPane = new JTabbedPane();
+
+  private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
+          PreferenceSource.STRUCTURE_CHOOSER);
+
+  public GStructureChooser()
+  {
+    try
+    {
+      jbInit();
+      mainFrame.setVisible(false);
+      mainFrame.invalidate();
+      mainFrame.pack();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Initializes the GUI default properties
+   * 
+   * @throws Exception
+   */
+  private void jbInit() throws Exception
+  {
+    tbl_summary.setAutoCreateRowSorter(true);
+    btn_view.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_view.setText(MessageManager.getString("action.view"));
+    btn_view.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        ok_ActionPerformed();
+      }
+    });
+    btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_cancel.setText(MessageManager.getString("action.cancel"));
+    btn_cancel.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        mainFrame.dispose();
+      }
+    });
+
+    btn_pdbFromFile.setFont(new java.awt.Font("Verdana", 0, 12));
+    String btn_title = MessageManager.getString("label.select_pdb_file");
+    btn_pdbFromFile.setText(btn_title + "              ");
+    btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        pdbFromFile_actionPerformed();
+      }
+    });
+
+    scrl_foundStructures.setPreferredSize(new Dimension(500, 300));
+    scrl_foundStructures
+            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+
+    cmb_filterOption.setFont(new java.awt.Font("Verdana", 0, 12));
+    chk_invertFilter.setFont(new java.awt.Font("Verdana", 0, 12));
+    chk_rememberSettings.setFont(new java.awt.Font("Verdana", 0, 12));
+
+
+    txt_search.setToolTipText(MessageManager
+            .getString("label.enter_pdb_id"));
+    cmb_filterOption.setToolTipText(MessageManager
+            .getString("info.select_filter_option"));
+    txt_search.getDocument().addDocumentListener(new DocumentListener()
+    {
+      @Override
+      public void insertUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+
+      @Override
+      public void removeUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+
+      @Override
+      public void changedUpdate(DocumentEvent e)
+      {
+        txt_search_ActionPerformed();
+      }
+    });
+
+    cmb_filterOption.addItemListener(this);
+    chk_invertFilter.addItemListener(this);
+
+    pnl_actions.add(chk_rememberSettings);
+    pnl_actions.add(btn_view);
+    pnl_actions.add(btn_cancel);
+
+    // pnl_filter.add(lbl_result);
+    pnl_filter.add(cmb_filterOption);
+    pnl_filter.add(lbl_loading);
+    pnl_filter.add(chk_invertFilter);
+    lbl_loading.setVisible(false);
+
+    pnl_idInput.add(txt_search);
+    pnl_idInput.add(lbl_pdbManualFetchStatus);
+
+    pnl_fileChooser.add(btn_pdbFromFile);
+    pnl_fileChooser.add(lbl_fromFileStatus);
+
+    pnl_fileChooserBL.add(fileChooserAssSeqPanel, BorderLayout.NORTH);
+    pnl_fileChooserBL.add(pnl_fileChooser, BorderLayout.CENTER);
+
+    pnl_idInputBL.add(idInputAssSeqPanel, BorderLayout.NORTH);
+    pnl_idInputBL.add(pnl_idInput, BorderLayout.CENTER);
+    
+    final String foundStructureSummary = MessageManager
+            .getString("label.found_structures_summary");
+
+    ChangeListener changeListener = new ChangeListener()
+    {
+      public void stateChanged(ChangeEvent changeEvent)
+      {
+        JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
+                .getSource();
+        int index = sourceTabbedPane.getSelectedIndex();
+        if (sourceTabbedPane.getTitleAt(index)
+                .equals(foundStructureSummary))
+        {
+          tabRefresh();
+        }
+      }
+    };
+    tabbedPane.addChangeListener(changeListener);
+    tabbedPane.setPreferredSize(new Dimension(500, 300));
+    tabbedPane.add(foundStructureSummary, scrl_foundStructures);
+    tabbedPane.add(
+            MessageManager.getString("label.configure_displayed_columns"),
+            pdbDocFieldPrefs);
+    
+
+    pnl_switchableViews.add(pnl_fileChooserBL, VIEWS_FROM_FILE);
+    pnl_switchableViews.add(pnl_idInputBL, VIEWS_ENTER_ID);
+    pnl_switchableViews.add(tabbedPane, VIEWS_FILTER);
+    
+    this.setLayout(mainLayout);
+    this.add(pnl_filter, java.awt.BorderLayout.NORTH);
+    this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
+    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
+
+    mainFrame.setVisible(true);
+    mainFrame.setContentPane(this);
+    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+    Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
+  }
+
+  
+  @Override
+  /**
+   * Event listener for the 'filter' combo-box and 'invert' check-box
+   */
+  public void itemStateChanged(ItemEvent e)
+  {
+    stateChanged(e);
+  }
+
+  /**
+   * This inner class provides the data model for the structure filter combo-box
+   * 
+   * @author tcnofoegbu
+   *
+   */
+  public class FilterOption
+  {
+    private String name;
+
+    private String value;
+
+    private String view;
+
+    public FilterOption(String name, String value, String view)
+    {
+      this.name = name;
+      this.value = value;
+      this.view = view;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+
+    public void setName(String name)
+    {
+      this.name = name;
+    }
+
+    public String getValue()
+    {
+      return value;
+    }
+
+    public void setValue(String value)
+    {
+      this.value = value;
+    }
+
+    public String getView()
+    {
+      return view;
+    }
+
+    public void setView(String view)
+    {
+      this.view = view;
+    }
+
+    public String toString()
+    {
+      return this.name;
+    }
+  }
+
+  /**
+   * This inner class provides the provides the data model for associate
+   * sequence combo-box - cmb_assSeq
+   * 
+   * @author tcnofoegbu
+   *
+   */
+  public class AssociateSeqOptions
+  {
+    private SequenceI sequence;
+    private String name;
+
+    public AssociateSeqOptions(SequenceI seq)
+    {
+      this.sequence = seq;
+      this.name = (seq.getName().length() >= 23) ? seq.getName().substring(
+              0, 23) : seq.getName();
+    }
+
+    public AssociateSeqOptions(String name, SequenceI seq)
+    {
+      this.name = name;
+      this.sequence = seq;
+    }
+
+    public String toString()
+    {
+      return name;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+
+    public void setName(String name)
+    {
+      this.name = name;
+    }
+
+    public SequenceI getSequence()
+    {
+      return sequence;
+    }
+
+    public void setSequence(SequenceI sequence)
+    {
+      this.sequence = sequence;
+    }
+
+  }
+
+  /**
+   * This inner class holds the Layout and configuration of the panel which
+   * handles association of manually fetched structures to a unique sequence
+   * when more than one sequence selection is made
+   * 
+   * @author tcnofoegbu
+   *
+   */
+  public class AssciateSeqPanel extends JPanel implements ItemListener
+  {
+    private JComboBox<AssociateSeqOptions> cmb_assSeq = new JComboBox<AssociateSeqOptions>();
+
+    private JLabel lbl_associateSeq = new JLabel();
+
+    public AssciateSeqPanel()
+    {
+      this.setLayout(new FlowLayout());
+      this.add(cmb_assSeq);
+      this.add(lbl_associateSeq);
+      cmb_assSeq.setToolTipText(MessageManager
+              .getString("info.associate_wit_sequence"));
+      cmb_assSeq.addItemListener(this);
+    }
+
+    public void loadCmbAssSeq()
+    {
+      populateCmbAssociateSeqOptions(cmb_assSeq, lbl_associateSeq);
+    }
+
+    public JComboBox<AssociateSeqOptions> getCmb_assSeq()
+    {
+      return cmb_assSeq;
+    }
+
+    public void setCmb_assSeq(JComboBox<AssociateSeqOptions> cmb_assSeq)
+    {
+      this.cmb_assSeq = cmb_assSeq;
+    }
+
+    @Override
+    public void itemStateChanged(ItemEvent e)
+    {
+      if (e.getStateChange() == ItemEvent.SELECTED)
+      {
+        cmbAssSeqStateChanged();
+      }
+    }
+  }
+
+  public JComboBox<FilterOption> getCmbFilterOption()
+  {
+    return cmb_filterOption;
+  }
+
+  protected abstract void stateChanged(ItemEvent e);
+
+  protected abstract void updateCurrentView();
+
+  protected abstract void populateFilterComboBox();
+
+  protected abstract void ok_ActionPerformed();
+
+  protected abstract void pdbFromFile_actionPerformed();
+
+  protected abstract void txt_search_ActionPerformed();
+
+  public abstract void populateCmbAssociateSeqOptions(
+          JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq);
+
+  public abstract void cmbAssSeqStateChanged();
+
+  public abstract void tabRefresh();
+}
diff --git a/src/jalview/jbgui/PDBDocFieldPreferences.java b/src/jalview/jbgui/PDBDocFieldPreferences.java
new file mode 100644 (file)
index 0000000..2021d0b
--- /dev/null
@@ -0,0 +1,228 @@
+package jalview.jbgui;
+
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+
+@SuppressWarnings("serial")
+public class PDBDocFieldPreferences extends JScrollPane
+{
+  protected JTable tbl_pdbDocFieldConfig = new JTable();
+
+  protected JScrollPane scrl_pdbDocFieldConfig = new JScrollPane(
+          tbl_pdbDocFieldConfig);
+
+  private HashMap<String, PDBDocField> map = new HashMap<String, PDBDocField>();
+
+  private static Collection<PDBDocField> searchSummaryFields = new HashSet<PDBDocField>();
+
+  private static Collection<PDBDocField> structureSummaryFields = new HashSet<PDBDocField>();
+
+  public enum PreferenceSource
+  {
+    SEARCH_SUMMARY, STRUCTURE_CHOOSER, PREFERENCES;
+  }
+
+  private PreferenceSource currentSource;
+
+  static
+  {
+    searchSummaryFields.add(PDBDocField.PDB_ID);
+    searchSummaryFields.add(PDBDocField.TITLE);
+
+    structureSummaryFields.add(PDBDocField.PDB_ID);
+    structureSummaryFields.add(PDBDocField.TITLE);
+  }
+
+  public PDBDocFieldPreferences(PreferenceSource source)
+  {
+    tbl_pdbDocFieldConfig.setAutoCreateRowSorter(true);
+    this.getViewport().add(tbl_pdbDocFieldConfig);
+    this.currentSource = source;
+
+    String[] columnNames = null;
+    switch (source)
+    {
+    case SEARCH_SUMMARY:
+      columnNames = new String[]
+      { "PDB Feild", "Show in search summary" };
+      break;
+    case STRUCTURE_CHOOSER:
+      columnNames = new String[]
+      { "PDB Feild", "Show in structure summary" };
+      break;
+    case PREFERENCES:
+      columnNames = new String[]
+      { "PDB Feild", "Show in search summary", "Show in structure summary" };
+      break;
+    default:
+      break;
+    }
+
+    Object[][] data = new Object[PDBDocField.values().length - 1][3];
+    int x = 0;
+    for (PDBDocField field : PDBDocField.values())
+    {
+      if (field.getName().equalsIgnoreCase("all"))
+      {
+        continue;
+      }
+
+      switch (source)
+      {
+      case SEARCH_SUMMARY:
+        data[x++] = new Object[]
+        { field.getName(), searchSummaryFields.contains(field) };
+        break;
+      case STRUCTURE_CHOOSER:
+        data[x++] = new Object[]
+        { field.getName(), structureSummaryFields.contains(field) };
+        break;
+      case PREFERENCES:
+        data[x++] = new Object[]
+        { field.getName(), searchSummaryFields.contains(field),
+            structureSummaryFields.contains(field) };
+        break;
+      default:
+        break;
+      }
+      map.put(field.getName(), field);
+    }
+
+    PDBFieldTableModel model = new PDBFieldTableModel(columnNames, data);
+    tbl_pdbDocFieldConfig.setModel(model);
+  }
+
+  public static Collection<PDBDocField> getSearchSummaryFields()
+  {
+    return searchSummaryFields;
+  }
+
+  public static void setSearchSummaryFields(
+          Collection<PDBDocField> searchSummaryFields)
+  {
+    PDBDocFieldPreferences.searchSummaryFields = searchSummaryFields;
+  }
+
+  public static Collection<PDBDocField> getStructureSummaryFields()
+  {
+    return structureSummaryFields;
+  }
+
+  public static void setStructureSummaryFields(
+          Collection<PDBDocField> structureSummaryFields)
+  {
+    PDBDocFieldPreferences.structureSummaryFields = structureSummaryFields;
+  }
+
+  class PDBFieldTableModel extends AbstractTableModel
+  {
+
+    public PDBFieldTableModel(String[] columnNames, Object[][] data)
+    {
+      this.data = data;
+      this.columnNames = columnNames;
+    }
+
+    private Object[][] data;
+
+    private String[] columnNames;
+
+    public int getColumnCount()
+    {
+      return columnNames.length;
+    }
+
+    public int getRowCount()
+    {
+      return data.length;
+    }
+
+    public String getColumnName(int col)
+    {
+      return columnNames[col];
+    }
+
+    public Object getValueAt(int row, int col)
+    {
+      return data[row][col];
+    }
+
+    /*
+     * JTable uses this method to determine the default renderer/ editor for
+     * each cell. If we didn't implement this method, then the last column would
+     * contain text ("true"/"false"), rather than a check box.
+     */
+    public Class getColumnClass(int c)
+    {
+      return getValueAt(0, c).getClass();
+    }
+
+    /*
+     * Don't need to implement this method unless your table's editable.
+     */
+    public boolean isCellEditable(int row, int col)
+    {
+      // Note that the data/cell address is constant,
+      // no matter where the cell appears onscreen.
+      return col == 1 || col == 2;
+
+    }
+
+    /*
+     * Don't need to implement this method unless your table's data can change.
+     */
+    public void setValueAt(Object value, int row, int col)
+    {
+      data[row][col] = value;
+      fireTableCellUpdated(row, col);
+
+      String name = getValueAt(row, 0).toString();
+      boolean selected = ((Boolean) value).booleanValue();
+
+      PDBDocField pdbField = map.get(name);
+
+      if (currentSource == PreferenceSource.SEARCH_SUMMARY)
+      {
+        updatePrefs(searchSummaryFields, pdbField, selected);
+      }
+      else if (currentSource == PreferenceSource.STRUCTURE_CHOOSER)
+      {
+        updatePrefs(structureSummaryFields, pdbField, selected);
+      }
+      else if (currentSource == PreferenceSource.PREFERENCES)
+      {
+        if (col == 1)
+        {
+          updatePrefs(searchSummaryFields, pdbField, selected);
+        }
+        else if (col == 2)
+        {
+          updatePrefs(structureSummaryFields, pdbField, selected);
+        }
+      }
+    }
+
+    private void updatePrefs(Collection<PDBDocField> prefConfig,
+            PDBDocField pdbField,
+            boolean selected)
+    {
+      if (prefConfig.contains(pdbField) && !selected)
+      {
+        prefConfig.remove(pdbField);
+      }
+
+      if (!prefConfig.contains(pdbField) && selected)
+      {
+        prefConfig.add(pdbField);
+      }
+    }
+
+  }
+}
index 369d766..d14e4ad 100755 (executable)
@@ -625,7 +625,7 @@ public class Format
   /**
    * Formats a character into a string (like sprintf in C)
    * 
-   * @param x
+   * @param debounceTrap
    *          the value to format
    * @return the formatted string
    */
@@ -644,7 +644,7 @@ public class Format
   /**
    * Formats a string into a larger string (like sprintf in C)
    * 
-   * @param x
+   * @param debounceTrap
    *          the value to format
    * @return the formatted string
    */
index a92b29a..4001cb2 100644 (file)
@@ -92,6 +92,11 @@ public class MessageManager
 
   public static String formatMessage(String key, Object... params)
   {
+    return MessageFormat.format(rb.getString(key), params);
+  }
+
+  public static String formatMessage(String key, String[] params)
+  {
     return MessageFormat.format(rb.getString(key), (Object[]) params);
   }
 
diff --git a/src/jalview/ws/dbsources/PDBRestClient.java b/src/jalview/ws/dbsources/PDBRestClient.java
new file mode 100644 (file)
index 0000000..1ab6125
--- /dev/null
@@ -0,0 +1,338 @@
+package jalview.ws.dbsources;
+
+import jalview.ws.uimodel.PDBRestRequest;
+import jalview.ws.uimodel.PDBRestResponse;
+import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.json.JSONConfiguration;
+
+/**
+ * A rest client for querying the Search endpoing of the PDB REST API
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class PDBRestClient
+{
+  private static String PDB_SEARCH_ENDPOINT = "http://wwwdev.ebi.ac.uk/pdbe/search/pdb/select?";
+
+  private static int DEFAULT_RESPONSE_SIZE = 200;
+
+  /**
+   * Takes a PDBRestRequest object and returns a response upon execution
+   * 
+   * @param pdbRestRequest
+   *          the PDBRestRequest instance to be processed
+   * @return the pdbResponse object for the given request
+   */
+  public PDBRestResponse executeRequest(PDBRestRequest pdbRestRequest)
+  {
+    ClientConfig clientConfig = new DefaultClientConfig();
+    clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING,
+            Boolean.TRUE);
+    Client client = Client.create(clientConfig);
+
+    String wantedFields = getPDBDocFieldsAsCommaDelimitedString(pdbRestRequest
+            .getWantedFields());
+    int responseSize = (pdbRestRequest.getResponseSize() == 0) ? DEFAULT_RESPONSE_SIZE
+            : pdbRestRequest.getResponseSize();
+    String sortParam = (pdbRestRequest.getFieldToSortBy() == null || pdbRestRequest
+            .getFieldToSortBy().trim().isEmpty()) ? "" : (pdbRestRequest
+            .getFieldToSortBy() + (pdbRestRequest.isAscending() ? " asc"
+            : " desc"));
+
+    // Build request parameters for the REST Request
+    WebResource webResource = client.resource(PDB_SEARCH_ENDPOINT)
+            .queryParam("wt", "json").queryParam("fl", wantedFields)
+            .queryParam("rows", String.valueOf(responseSize))
+            .queryParam("q", pdbRestRequest.getQuery())
+            .queryParam("sort", sortParam);
+
+    // Execute the REST request
+    ClientResponse clientResponse = webResource.accept(
+            MediaType.APPLICATION_JSON).get(ClientResponse.class);
+
+    // Get the JSON string from the response object
+    String responseString = clientResponse.getEntity(String.class);
+
+    // Check the response status and report exception if one occurs
+    if (clientResponse.getStatus() != 200)
+    {
+      String errorMessage = "";
+      if (clientResponse.getStatus() == 400)
+      {
+        errorMessage = parseJsonExceptionString(responseString);
+        throw new RuntimeException(errorMessage);
+      }
+      else
+      {
+        errorMessage = "Failed : HTTP error code : "
+                + clientResponse.getStatus();
+        throw new RuntimeException(errorMessage);
+      }
+    }
+
+    // Make redundant objects eligible for garbage collection to conserve
+    // memory
+    clientResponse = null;
+    client = null;
+
+    // Process the response and return the result to the caller.
+    return parsePDBJsonResponse(responseString, pdbRestRequest);
+  }
+
+  /**
+   * Process error response from PDB server if/when one occurs.
+   * 
+   * @param jsonResponse
+   *          the JSON string containing error message from the server
+   * @return the processed error message from the JSON string
+   */
+  public static String parseJsonExceptionString(String jsonErrorResponse)
+  {
+    String errorMessage = "RunTime error";
+    try
+    {
+      JSONParser jsonParser = new JSONParser();
+      JSONObject jsonObj = (JSONObject) jsonParser.parse(jsonErrorResponse);
+      JSONObject errorResponse = (JSONObject) jsonObj.get("error");
+      errorMessage = errorResponse.get("msg").toString();
+
+      JSONObject responseHeader = (JSONObject) jsonObj
+              .get("responseHeader");
+      errorMessage += responseHeader.get("params").toString();
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return errorMessage;
+  }
+
+  /**
+   * Parses the JSON response string from PDB REST API. The response is dynamic
+   * hence, only fields specifically requested for in the 'wantedFields'
+   * parameter is fetched/processed
+   * 
+   * @param pdbJsonResponseString
+   *          the JSON string to be parsed
+   * @param pdbRestRequest
+   *          the request object which contains parameters used to process the
+   *          JSON string
+   * @return
+   */
+  @SuppressWarnings("unchecked")
+  public static PDBRestResponse parsePDBJsonResponse(
+          String pdbJsonResponseString, PDBRestRequest pdbRestRequest)
+  {
+    PDBRestResponse searchResult = new PDBRestResponse();
+    List<PDBResponseSummary> result = null;
+    try
+    {
+      JSONParser jsonParser = new JSONParser();
+      JSONObject jsonObj = (JSONObject) jsonParser
+              .parse(pdbJsonResponseString);
+
+      JSONObject pdbResponse = (JSONObject) jsonObj.get("response");
+      String queryTime = ((JSONObject) jsonObj.get("responseHeader")).get(
+              "QTime").toString();
+      int numFound = Integer
+              .valueOf(pdbResponse.get("numFound").toString());
+      if (numFound > 0)
+      {
+        result = new ArrayList<PDBResponseSummary>();
+        JSONArray docs = (JSONArray) pdbResponse.get("docs");
+        for (Iterator<JSONObject> docIter = docs.iterator(); docIter
+                .hasNext();)
+        {
+          JSONObject doc = docIter.next();
+          result.add(searchResult.new PDBResponseSummary(doc,
+                  pdbRestRequest));
+        }
+        searchResult.setNumberOfItemsFound(numFound);
+        searchResult.setResponseTime(queryTime);
+        searchResult.setSearchSummary(result);
+      }
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return searchResult;
+  }
+
+  /**
+   * Takes a collection of PDBDocField and converts its 'code' Field values into
+   * a comma delimited string.
+   * 
+   * @param pdbDocfields
+   *          the collection of PDBDocField to process
+   * @return the comma delimited string from the pdbDocFields collection
+   */
+  public static String getPDBDocFieldsAsCommaDelimitedString(
+          Collection<PDBDocField> pdbDocfields)
+  {
+    String result = "";
+    if (pdbDocfields != null && !pdbDocfields.isEmpty())
+    {
+      StringBuilder returnedFields = new StringBuilder();
+      for (PDBDocField field : pdbDocfields)
+      {
+        returnedFields.append(",").append(field.getCode());
+      }
+      returnedFields.deleteCharAt(0);
+      result = returnedFields.toString();
+    }
+    return result;
+  }
+
+  /**
+   * Determines the column index for 'PDB Id' Fields in the dynamic summary
+   * table. The PDB Id serves as a unique identifier for a given row in the
+   * summary table
+   * 
+   * @param wantedFeilds
+   *          the available table columns in no particular order
+   * @return the pdb id field column index
+   */
+  public static int getPDBIdColumIndex(
+          Collection<PDBDocField> wantedFeilds, boolean hasRefSeq)
+  {
+
+    // If a reference sequence is attached then start counting from 1 else
+    // start from zero
+    int pdbFeildIndexCounter = hasRefSeq ? 1 : 0;
+
+    for (PDBDocField feild : wantedFeilds)
+    {
+      if (feild.equals(PDBDocField.PDB_ID))
+      {
+        break; // Once PDB Id index is determined exit iteration
+      }
+      ++pdbFeildIndexCounter;
+    }
+    return pdbFeildIndexCounter;
+  }
+
+  /**
+   * This enum represents the fields available in the PDB JSON response
+   *
+   */
+  public enum PDBDocField
+  {
+    PDB_ID("PDB Id", "pdb_id"), TITLE("Title", "title"), MOLECULE_NAME(
+            "Molecule", "molecule_name"), MOLECULE_TYPE("Molecule Type",
+            "molecule_type"), MOLECULE_SEQUENCE("Sequence",
+            "molecule_sequence"), PFAM_ACCESSION("PFAM Accession",
+            "pfam_accession"), PFAM_NAME("PFAM Name", "pfam_name"), INTERPRO_NAME(
+            "InterPro Name", "interpro_name"), INTERPRO_ACCESSION(
+            "InterPro Accession", "interpro_accession"), UNIPROT_ID(
+            "UniProt Id", "uniprot_id"), UNIPROT_ACCESSION(
+            "UniProt Accession", "uniprot_accession"), UNIPROT_COVERAGE(
+            "UniProt Coverage", "uniprot_coverage"), UNIPROT_FEATURES(
+            "Uniprot Features", "uniprot_features"), R_FACTOR("R Factor",
+            "r_factor"), RESOLUTION("Resolution", "resolution"), DATA_QUALITY(
+            "Data Quality", "data_quality"), OVERALL_QUALITY(
+            "Overall Quality", "overall_quality"), POLYMER_COUNT(
+            "Number of Polymers", "number_of_polymers"), PROTEIN_CHAIN_COUNT(
+            "Number of Protein Chains", "number_of_protein_chains"), BOUND_MOLECULE_COUNT(
+            "Number of Bound Molecule", "number_of_bound_molecules"), POLYMER_RESIDUE_COUNT(
+            "Number of Polymer Residue", "number_of_polymer_residues"), GENUS(
+            "GENUS", "genus"), GENE_NAME("Gene Name", "gene_name"), EXPERIMENTAL_METHOD(
+            "Experimental Method", "experimental_method"), GO_ID("GO Id",
+            "go_id"), ASSEMBLY_ID("Assembly Id", "assembly_form"), ASSEMBLY_FORM(
+            "Assembly Form", "assembly_id"), ASSEMBLY_TYPE("Assembly Type",
+            "assembly_type"), SPACE_GROUP("Space Group", "spacegroup"), CATH_CODE(
+            "Cath Code", "cath_code"), TAX_ID("Tax Id", "tax_id"), TAX_QUERY(
+            "Tax Query", "tax_query"), INTERACTING_ENTRY_ID(
+            "Interacting Entry Id", "interacting_entry_id"), INTERACTING_ENTITY_ID(
+            "Interacting Entity Id", "interacting_entity_id"), INTERACTING_MOLECULES(
+            "Interacting Molecules", "interacting_molecules"), PUBMED_ID(
+            "Pubmed Id", "pubmed_id"), STATUS("Status", "status"), MODEL_QUALITY(
+            "Model Quality", "model_quality"), PIVOT_RESOLUTION(
+            "Pivot Resolution", "pivot_resolution"), DATA_REDUCTION_SOFTWARE(
+            "Data reduction software", "data_reduction_software"), MAX_OBSERVED_RES(
+            "Max observed residues", "max_observed_residues"), ORG_SCI_NAME(
+            "Organism scientific name", "organism_scientific_name"), SUPER_KINGDOM(
+            "Super kingdom", "superkingdom"), RANK("Rank", "rank"), CRYSTALLISATION_PH(
+            "Crystallisation Ph", "crystallisation_ph"), BIOLOGICAL_FUNCTION(
+            "Biological Function", "biological_function"), BIOLOGICAL_PROCESS(
+            "Biological Process", "biological_process"), BIOLOGICAL_CELL_COMPONENT(
+            "Biological Cell Component", "biological_cell_component"), COMPOUND_NAME(
+            "Compound Name", "compound_name"), COMPOUND_ID("Compound Id",
+            "compound_id"), COMPOUND_WEIGHT("Compound Weight",
+            "compound_weight"), COMPOUND_SYSTEMATIC_NAME(
+            "Compound Systematic Name", "compound_systematic_name"), INTERACTING_LIG(
+            "Interacting Ligands", "interacting_ligands"), JOURNAL(
+            "Journal", "journal"), ALL_AUTHORS("All Authors", "all_authors"), EXPERIMENTAL_DATA_AVAILABLE(
+            "Experiment Data Available", "experiment_data_available"), DIFFRACTION_PROTOCOL(
+            "Diffraction Protocol", "diffraction_protocol"), REFINEMENT_SOFTWARE(
+            "Refinement Software", "refinement_software"), STRUCTURE_DETERMINATION_METHOD(
+            "Structure Determination Method",
+            "structure_determination_method"), SYNCHROTON_SITE(
+            "Synchrotron Site", "synchrotron_site"), SAMPLE_PREP_METHOD(
+            "Sample Preparation Method", "sample_preparation_method"), ENTRY_AUTHORS(
+            "Entry Authors", "entry_authors"), CITATION_TITLE(
+            "Citation Title", "citation_title"), STRUCTURE_SOLUTION_SOFTWARE(
+            "Structure Solution Software", "structure_solution_software"), ENTRY_ENTITY(
+            "Entry Entity", "entry_entity"), R_FREE("R Free", "r_free"), NO_OF_POLYMER_ENTITIES(
+            "Number of Polymer Entities", "number_of_polymer_entities"), NO_OF_BOUND_ENTITIES(
+            "Number of Bound Entities", "number_of_bound_entities"), CRYSTALLISATION_RESERVOIR(
+            "Crystallisation Reservoir", "crystallisation_reservoir"), DATA_SCALING_SW(
+            "Data Scalling Software", "data_scaling_software"), DETECTOR(
+            "Detector", "detector"), DETECTOR_TYPE("Detector Type",
+            "detector_type"), MODIFIED_RESIDUE_FLAG(
+            "Modified Residue Flag", "modified_residue_flag"), NUMBER_OF_COPIES(
+            "Number of Copies", "number_of_copies"), STRUCT_ASYM_ID(
+            "Struc Asym Id", "struct_asym_id"), HOMOLOGUS_PDB_ENTITY_ID(
+            "Homologus PDB Entity Id", "homologus_pdb_entity_id"), MOLECULE_SYNONYM(
+            "Molecule Synonym", "molecule_synonym"), DEPOSITION_SITE(
+            "Deposition Site", "deposition_site"), SYNCHROTRON_BEAMLINE(
+            "Synchrotron Beamline", "synchrotron_beamline"), ENTITY_ID(
+            "Entity Id", "entity_id"), BEAM_SOURCE_NAME("Beam Source Name",
+            "beam_source_name"), PROCESSING_SITE("Processing Site",
+            "processing_site"), ENTITY_WEIGHT("Entity Weight",
+            "entity_weight"), VERSION("Version", "_version_"), ALL("ALL",
+            "text");
+
+    private String name;
+
+    private String code;
+
+    PDBDocField(String name, String code)
+    {
+      this.name = name;
+      this.code = code;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+
+    public String getCode()
+    {
+      return code;
+    }
+
+    public String toString()
+    {
+      return name;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/jalview/ws/uimodel/PDBRestRequest.java b/src/jalview/ws/uimodel/PDBRestRequest.java
new file mode 100644 (file)
index 0000000..58d7aeb
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.ws.uimodel;
+
+
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+
+import java.util.Collection;
+
+/**
+ * Represents the PDB request to be consumed by the PDBRestClient
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class PDBRestRequest
+{
+  private String fieldToSearchBy;
+
+  private String searchTerm;
+
+  private String fieldToSortBy;
+
+  private String associatedSequence;
+
+  private boolean allowEmptySequence;
+
+  private int responseSize;
+
+  private boolean isSortAscending;
+
+  private Collection<PDBDocField> wantedFields;// = new
+                                               // Collection<PDBDocField>();
+
+  public String getFieldToSearchBy()
+  {
+    return fieldToSearchBy;
+  }
+
+  public void setFieldToSearchBy(String fieldToSearchBy)
+  {
+    this.fieldToSearchBy = fieldToSearchBy;
+  }
+
+  public String getSearchTerm()
+  {
+    return searchTerm;
+  }
+
+  public void setSearchTerm(String searchTerm)
+  {
+    this.searchTerm = searchTerm;
+  }
+
+  public boolean isAllowEmptySeq()
+  {
+    return allowEmptySequence;
+  }
+
+  public void setAllowEmptySeq(boolean allowEmptySeq)
+  {
+    this.allowEmptySequence = allowEmptySeq;
+  }
+
+  public int getResponseSize()
+  {
+    return responseSize;
+  }
+
+  public void setResponseSize(int responseSize)
+  {
+    this.responseSize = responseSize;
+  }
+
+  public Collection<PDBDocField> getWantedFields()
+  {
+    return wantedFields;
+  }
+
+  public void setWantedFields(Collection<PDBDocField> wantedFields)
+  {
+    this.wantedFields = wantedFields;
+  }
+
+  public String getFieldToSortBy()
+  {
+    return fieldToSortBy;
+  }
+
+  public void setFieldToSortBy(String fieldToSortBy, boolean isSortAscending)
+  {
+    this.fieldToSortBy = fieldToSortBy;
+    this.isSortAscending = isSortAscending;
+  }
+
+  public boolean isAscending()
+  {
+    return isSortAscending;
+  }
+
+  public String getAssociatedSequence()
+  {
+    return associatedSequence;
+  }
+
+  public void setAssociatedSequence(String associatedSequence)
+  {
+    this.associatedSequence = associatedSequence;
+  }
+
+  public String getQuery()
+  {
+    return fieldToSearchBy + searchTerm
+            + (isAllowEmptySeq() ? "" : " AND molecule_sequence:['' TO *]");
+  }
+
+  public String toString()
+  {
+    return "Query : " + getQuery() + " sort field: " + fieldToSortBy
+            + " isAsc: " + isAscending() + " Associated Seq : "
+            + associatedSequence;
+  }
+}
diff --git a/src/jalview/ws/uimodel/PDBRestResponse.java b/src/jalview/ws/uimodel/PDBRestResponse.java
new file mode 100644 (file)
index 0000000..3b5b05c
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+ * Copyright (C) 2014 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.ws.uimodel;
+
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+
+import java.util.Collection;
+import java.util.Objects;
+
+import javax.swing.table.DefaultTableModel;
+
+import org.json.simple.JSONObject;
+
+/**
+ * Represents the response model produced by the PDBRestClient upon successful
+ * execution of a given request
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class PDBRestResponse
+{
+  private int numberOfItemsFound;
+
+  private String responseTime;
+
+  private Collection<PDBResponseSummary> searchSummary;
+
+  public int getNumberOfItemsFound()
+  {
+    return numberOfItemsFound;
+  }
+
+  public void setNumberOfItemsFound(int itemFound)
+  {
+    this.numberOfItemsFound = itemFound;
+  }
+
+  public String getResponseTime()
+  {
+    return responseTime;
+  }
+
+  public void setResponseTime(String responseTime)
+  {
+    this.responseTime = responseTime;
+  }
+
+  public Collection<PDBResponseSummary> getSearchSummary()
+  {
+    return searchSummary;
+  }
+
+  public void setSearchSummary(Collection<PDBResponseSummary> searchSummary)
+  {
+    this.searchSummary = searchSummary;
+  }
+
+
+  /**
+   * Convenience method to obtain a Table model for a given summary List based
+   * on the request parameters
+   * 
+   * @param request
+   *          the PDBRestRequest object which holds useful information for
+   *          creating a table model
+   * @param summariesList
+   *          the summary list which contains the data for populating the
+   *          table's rows
+   * @return the table model which was dynamically generated
+   */
+  public static DefaultTableModel getTableModel(PDBRestRequest request,
+          Collection<PDBResponseSummary> summariesList)
+  {
+    DefaultTableModel tableModel = new DefaultTableModel();
+
+    if (request.getAssociatedSequence() != null)
+    {
+      tableModel.addColumn("Sequence"); // Create sequence column header if
+                                        // exists in the request
+    }
+    for (PDBDocField field : request.getWantedFields())
+    {
+      tableModel.addColumn(field.getName()); // Create sequence column header if
+                                             // exists in the request
+    }
+
+    for (PDBResponseSummary res : summariesList)
+    {
+      tableModel.addRow(res.getSummaryData()); // Populate table rows with
+                                               // summary list
+    }
+
+    return tableModel;
+  }
+
+  /**
+   * Model for a unique response summary
+   * 
+   */
+  public class PDBResponseSummary
+  {
+    private String pdbId;
+
+    private String[] summaryRowData;
+
+    private String associatedSequence;
+
+    public PDBResponseSummary(JSONObject pdbJsonDoc, PDBRestRequest request)
+    {
+      Collection<PDBDocField> diplayFields = request.getWantedFields();
+      String associatedSeq = request.getAssociatedSequence();
+      int colCounter = 0;
+      summaryRowData = new String[(associatedSeq != null) ? diplayFields
+              .size() + 1 : diplayFields.size()];
+      if (associatedSeq != null)
+      {
+        this.associatedSequence = (associatedSeq.length() > 18) ? associatedSeq
+                .substring(0, 18) : associatedSeq;
+        summaryRowData[0] = associatedSequence;
+        colCounter = 1;
+      }
+
+      for (PDBDocField field : diplayFields)
+      {
+        String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
+                : pdbJsonDoc
+                .get(field.getCode()).toString();
+        if (field.equals(PDBDocField.PDB_ID))
+        {
+          this.pdbId = fieldData;
+          summaryRowData[colCounter++] = this.pdbId;
+        }
+        else
+        {
+          summaryRowData[colCounter++] = fieldData;
+        }
+      }
+    }
+
+    public String getPdbId()
+    {
+      return pdbId;
+    }
+
+    public void setPdbId(String pdbId)
+    {
+      this.pdbId = pdbId;
+    }
+
+    public String[] getSummaryData()
+    {
+      return summaryRowData;
+    }
+
+    public void setSummaryData(String[] summaryData)
+    {
+      this.summaryRowData = summaryData;
+    }
+
+    /**
+     * Returns a string representation of this object;
+     */
+    @Override
+    public String toString()
+    {
+      StringBuilder summaryFieldValues = new StringBuilder();
+      for (String summaryField : summaryRowData)
+      {
+        summaryFieldValues.append(summaryField).append("\t");
+      }
+      return summaryFieldValues.toString();
+    }
+
+    /**
+     * Returns hash code value for this object
+     */
+    @Override
+    public int hashCode()
+    {
+      return Objects.hash(this.pdbId, this.toString());
+    }
+
+    /**
+     * Indicates whether some object is equal to this one
+     */
+    @Override
+    public boolean equals(Object that)
+    {
+      if (!(that instanceof PDBResponseSummary))
+      {
+        return false;
+      }
+      PDBResponseSummary another = (PDBResponseSummary) that;
+      return this.toString().equals(another.toString());
+    }
+
+  }
+
+}
+
diff --git a/test/jalview/gui/PDBSearchPanelTest.java b/test/jalview/gui/PDBSearchPanelTest.java
new file mode 100644 (file)
index 0000000..17ba85a
--- /dev/null
@@ -0,0 +1,50 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertTrue;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JTextField;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PDBSearchPanelTest
+{
+
+  @Before
+  public void setUp() throws Exception
+  {
+  }
+
+  @After
+  public void tearDown() throws Exception
+  {
+  }
+
+  @Test
+  public void populateCmbSearchTargetOptionsTest()
+  {
+    PDBSearchPanel searchPanel = new PDBSearchPanel(null);
+    assertTrue(searchPanel.getCmbSearchTarget().getItemCount() > 0);
+    searchPanel.populateCmbSearchTargetOptions();
+  }
+
+  @Test
+  public void txt_search_ActionPerformedTest()
+  {
+    PDBSearchPanel searchPanel = new PDBSearchPanel(null);
+    JInternalFrame mainFrame = searchPanel.getMainFrame();
+    JTextField txt_search = searchPanel.getTxtSearch();
+
+    assertTrue(mainFrame.getTitle().length() == 20);
+    assertTrue(mainFrame.getTitle()
+            .equalsIgnoreCase("PDB Sequence Fetcher"));
+
+    txt_search.setText("ABC");
+
+    assertTrue(mainFrame.getTitle().length() > 20);
+    assertTrue(!mainFrame.getTitle().equalsIgnoreCase(
+            "PDB Sequence Fetcher"));
+  }
+}
diff --git a/test/jalview/gui/StructureChooserTest.java b/test/jalview/gui/StructureChooserTest.java
new file mode 100644 (file)
index 0000000..24e8bc9
--- /dev/null
@@ -0,0 +1,87 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import java.util.Vector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class StructureChooserTest
+{
+  Sequence seq;
+
+  @Before
+  public void setUp() throws Exception
+  {
+    seq = new Sequence("Test_Seq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1, 26);
+    seq.setDatasetSequence(seq);
+    for (int x = 1; x < 5; x++)
+    {
+      DBRefEntry dbRef = new DBRefEntry();
+      dbRef.setAccessionId("XYZ_" + x);
+      seq.addDBRef(dbRef);
+    }
+
+    PDBEntry dbRef = new PDBEntry();
+    dbRef.setId("1tim");
+
+    Vector<PDBEntry> pdbIds = new Vector<PDBEntry>();
+    pdbIds.add(dbRef);
+
+    seq.setPDBId(pdbIds);
+  }
+
+  @After
+  public void tearDown() throws Exception
+  {
+    seq = null;
+  }
+
+
+
+  @Test
+  public void buildQueryTest()
+  {
+    assertEquals(
+            "1tim OR text:XYZ_1 OR text:XYZ_2 OR text:XYZ_3 OR text:XYZ_4",
+            StructureChooser.buildQuery(seq));
+  }
+
+  @Test
+  public void populateFilterComboBoxTest()
+  {
+    SequenceI[] selectedSeqs = new SequenceI[]
+    { seq };
+    StructureChooser sc = new StructureChooser(selectedSeqs, seq,
+            null);
+    sc.populateFilterComboBox();
+    int optionsSize = sc.getCmbFilterOption().getItemCount();
+    assertEquals(2, optionsSize); // if structures are not discovered then don't
+                                  // populate filter options
+
+    sc.setStructuresDiscovered(true);
+    sc.populateFilterComboBox();
+    optionsSize = sc.getCmbFilterOption().getItemCount();
+    assertTrue(optionsSize > 2); // if structures are found, filter options
+                                 // should be populated
+  }
+
+  @Test
+  public void fetchStructuresInfoTest()
+  {
+    SequenceI[] selectedSeqs = new SequenceI[]
+    { seq };
+    StructureChooser sc = new StructureChooser(selectedSeqs, seq, null);
+    sc.fetchStructuresMetaData();
+    assertTrue(sc.getDiscoveredStructuresSet() != null);
+    assertTrue(sc.getDiscoveredStructuresSet().size() > 0);
+
+  }
+}
diff --git a/test/jalview/io/pdb_request_json_error.txt b/test/jalview/io/pdb_request_json_error.txt
new file mode 100644 (file)
index 0000000..de14bf1
--- /dev/null
@@ -0,0 +1,17 @@
+{
+    "responseHeader": {
+        "status": 400,
+        "QTime": 0,
+        "params": {
+            "sort": "",
+            "fl": "pdb_id",
+            "q": "text:abc OR text:go:abc AND molecule_sequence:['' TO *]",
+            "wt": "json",
+            "rows": "100"
+        }
+    },
+    "error": {
+        "msg": "org.apache.solr.search.SyntaxError: Cannot parse 'text:abc OR text:go:abc AND molecule_sequence:['' TO *]': Encountered \" \":\" \": \"\" at line 1, column 19.",
+        "code": 400
+    }
+}
\ No newline at end of file
diff --git a/test/jalview/io/pdb_response_json.txt b/test/jalview/io/pdb_response_json.txt
new file mode 100644 (file)
index 0000000..e3f3583
--- /dev/null
@@ -0,0 +1,176 @@
+{
+    "responseHeader": {
+        "status": 0,
+        "QTime": 0,
+        "params": {
+            "sort": "",
+            "fl": "molecule_type,pdb_id,genus,gene_name,title",
+            "q": "text:abc AND molecule_sequence:['' TO *]",
+            "wt": "json",
+            "rows": "100"
+        }
+    },
+    "response": {
+        "numFound": 931,
+        "start": 0,
+        "docs": [
+            {
+                "pdb_id": "3qf4",
+                "title": "Crystal structure of a heterodimeric ABC transporter in its inward-facing conformation",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "THEMA_03290",
+                    "Tmari_0285",
+                    "TM_0287"
+                ]
+            },
+            {
+                "pdb_id": "4wbs",
+                "title": "Crystal structure of an ABC transporter related protein from Burkholderia phymatum",
+                "genus": [
+                    "Burkholderia"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "Bphy_0327"
+                ]
+            },
+            {
+                "pdb_id": "1hn0",
+                "title": "CRYSTAL STRUCTURE OF CHONDROITIN ABC LYASE I FROM PROTEUS VULGARIS AT 1.9 ANGSTROMS RESOLUTION",
+                "genus": [
+                    "Proteus"
+                ],
+                "molecule_type": "Protein"
+            },
+            {
+                "pdb_id": "4a82",
+                "title": "Fitted model of staphylococcus aureus sav1866 model ABC transporter in the human cystic fibrosis transmembrane conductance regulator volume map EMD-1966.",
+                "genus": [
+                    "Homo"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "SAV1866"
+                ]
+            },
+            {
+                "pdb_id": "3nh6",
+                "title": "Nucleotide Binding Domain of human ABCB6 (apo structure)",
+                "genus": [
+                    "Homo"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "ABCB6",
+                    "UMAT",
+                    "MTABC3",
+                    "PRP"
+                ]
+            },
+            {
+                "pdb_id": "2nq2",
+                "title": "An inward-facing conformation of a putative metal-chelate type ABC transporter.",
+                "genus": [
+                    "Haemophilus"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "HI_1471"
+                ]
+            },
+            {
+                "pdb_id": "3s4u",
+                "title": "Crystal structure of open, unliganded E. coli PhnD H157A",
+                "genus": [
+                    "Escherichia"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "phnD",
+                    "UTI89_C4699"
+                ]
+            },
+            {
+                "pdb_id": "4q4a",
+                "title": "Improved model of AMP-PNP bound TM287/288",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "TM_0288"
+                ]
+            },
+            {
+                "pdb_id": "1oxs",
+                "title": "Crystal structure of GlcV, the ABC-ATPase of the glucose ABC transporter from Sulfolobus solfataricus",
+                "genus": [
+                    "Sulfolobus"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "SSO2850"
+                ]
+            },
+            {
+                "pdb_id": "4q4j",
+                "title": "Structure of crosslinked TM287/288_S498C_S520C mutant",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "TM_0288"
+                ]
+            },
+            {
+                "pdb_id": "1oxt",
+                "title": "Crystal structure of GlcV, the ABC-ATPase of the glucose ABC transporter from Sulfolobus solfataricus",
+                "genus": [
+                    "Sulfolobus"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "SSO2850"
+                ]
+            },
+            {
+                "pdb_id": "4q4h",
+                "title": "TM287/288 in its apo state",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "TM_0288"
+                ]
+            },
+            {
+                "pdb_id": "3qf4",
+                "title": "Crystal structure of a heterodimeric ABC transporter in its inward-facing conformation",
+                "genus": [
+                    "Thermotoga"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "TM_0288"
+                ]
+            },
+            {
+                "pdb_id": "2fgk",
+                "title": "Crystal structure of the ABC-cassette E631Q mutant of HlyB with bound ATP",
+                "genus": [
+                    "Escherichia"
+                ],
+                "molecule_type": "Protein",
+                "gene_name": [
+                    "hlyB"
+                ]
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/test/jalview/ws/dbsources/PDBRestClientTest.java b/test/jalview/ws/dbsources/PDBRestClientTest.java
new file mode 100644 (file)
index 0000000..ba07562
--- /dev/null
@@ -0,0 +1,181 @@
+package jalview.ws.dbsources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.ws.uimodel.PDBRestRequest;
+import jalview.ws.uimodel.PDBRestResponse;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PDBRestClientTest
+{
+
+  @Before
+  public void setUp() throws Exception
+  {
+  }
+
+  @After
+  public void tearDown() throws Exception
+  {
+  }
+
+  @Test
+  public void executeRequestTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+
+    PDBRestRequest request = new PDBRestRequest();
+    request.setAllowEmptySeq(false);
+    request.setResponseSize(100);
+    request.setFieldToSearchBy("text:");
+    request.setSearchTerm("abc");
+    request.setWantedFields(wantedFields);
+
+    PDBRestResponse response = new PDBRestClient().executeRequest(request);
+    assertTrue(response.getNumberOfItemsFound() > 99);
+    assertTrue(response.getSearchSummary() != null);
+    assertTrue(response.getSearchSummary().size() > 99);
+  }
+
+  @Test
+  public void getPDBDocFieldsAsCommaDelimitedStringTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+    assertEquals("molecule_type,pdb_id,genus,gene_name,title",
+            PDBRestClient
+                    .getPDBDocFieldsAsCommaDelimitedString(wantedFields));
+  }
+
+  @Test
+  public void parsePDBJsonExceptionStringTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+
+    PDBRestRequest request = new PDBRestRequest();
+    request.setAllowEmptySeq(false);
+    request.setResponseSize(100);
+    request.setFieldToSearchBy("text:");
+    request.setSearchTerm("abc");
+    request.setWantedFields(wantedFields);
+
+    String jsonErrorResponse = "";
+    try
+    {
+      jsonErrorResponse = readJsonStringFromFile("test/jalview/io/pdb_request_json_error.txt");
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+
+    String parsedErrorResponse = PDBRestClient
+            .parseJsonExceptionString(jsonErrorResponse);
+    String expectedErrorMsg = "org.apache.solr.search.SyntaxError: Cannot parse 'text:abc OR text:go:abc AND molecule_sequence:['' TO *]': Encountered \" \":\" \": \"\" at line 1, column 19.{\"q\":\"text:abc OR text:go:abc AND molecule_sequence:['' TO *]\",\"fl\":\"pdb_id\",\"sort\":\"\",\"rows\":\"100\",\"wt\":\"json\"}";
+
+    assertEquals(expectedErrorMsg, parsedErrorResponse);
+  }
+
+  @Test(expected = RuntimeException.class)
+  public void testForExpectedRuntimeException()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.PDB_ID);
+
+    PDBRestRequest request = new PDBRestRequest();
+    request.setFieldToSearchBy("text:");
+    request.setSearchTerm("abc OR text:go:abc");
+    request.setWantedFields(wantedFields);
+    new PDBRestClient().executeRequest(request);
+  }
+
+  @Test
+  public void parsePDBJsonResponseTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+
+    PDBRestRequest request = new PDBRestRequest();
+    request.setAllowEmptySeq(false);
+    request.setWantedFields(wantedFields);
+
+    String jsonString = "";
+    try
+    {
+      jsonString = readJsonStringFromFile("test/jalview/io/pdb_response_json.txt");
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+    PDBRestResponse response = PDBRestClient.parsePDBJsonResponse(
+            jsonString, request);
+    assertTrue(response.getSearchSummary() != null);
+    assertTrue(response.getNumberOfItemsFound() == 931);
+    assertTrue(response.getSearchSummary().size() == 14);
+  }
+
+  @Test
+  public void getPDBIdColumIndexTest()
+  {
+    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
+    wantedFields.add(PDBDocField.MOLECULE_TYPE);
+    wantedFields.add(PDBDocField.GENUS);
+    wantedFields.add(PDBDocField.GENE_NAME);
+    wantedFields.add(PDBDocField.TITLE);
+    wantedFields.add(PDBDocField.PDB_ID);
+    assertEquals(5, PDBRestClient.getPDBIdColumIndex(wantedFields, true));
+    assertEquals(4, PDBRestClient.getPDBIdColumIndex(wantedFields, false));
+  }
+
+  public String readJsonStringFromFile(String filePath) throws IOException
+  {
+    String fileContent;
+    BufferedReader br = new BufferedReader(new FileReader(filePath));
+    try
+    {
+      StringBuilder sb = new StringBuilder();
+      String line = br.readLine();
+
+      while (line != null)
+      {
+        sb.append(line);
+        sb.append(System.lineSeparator());
+        line = br.readLine();
+        }
+      fileContent = sb.toString();
+    } finally
+    {
+      br.close();
+    }
+    return fileContent;
+  }
+
+
+}