JAL-3985 formatting
[jalview.git] / src / jalview / gui / structurechooser / ThreeDBStructureChooserQuerySource.java
index ff414dd..8b4258f 100644 (file)
@@ -1,9 +1,28 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ 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.structurechooser;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -13,6 +32,7 @@ import java.util.Set;
 
 import javax.swing.JTable;
 
+import jalview.bin.Console;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
@@ -24,9 +44,9 @@ import jalview.fts.core.FTSDataColumnPreferences;
 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
 import jalview.fts.core.FTSRestRequest;
 import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.threedbeacons.TDB_FTSData;
 import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
 import jalview.jbgui.FilterOption;
-import jalview.util.MessageManager;
 
 /**
  * logic for querying the 3DBeacons API for structures of sequences
@@ -45,8 +65,6 @@ public class ThreeDBStructureChooserQuerySource
 
   private static final String FILTER_SOURCE_PREFIX = "only_";
 
-  private static int MAX_QLENGTH = 7820;
-
   protected FTSRestRequest lastTdbRequest;
 
   protected FTSRestClientI tdbRestClient;
@@ -74,39 +92,44 @@ public class ThreeDBStructureChooserQuerySource
    * @return the built query string
    */
 
+  @Override
   public String buildQuery(SequenceI seq)
   {
-    boolean isPDBRefsFound = false;
-    boolean isUniProtRefsFound = false;
-    StringBuilder queryBuilder = new StringBuilder();
-    Set<String> seqRefs = new LinkedHashSet<>();
-
-    /*
-     * note PDBs as DBRefEntry so they are not duplicated in query
-     */
-    Set<String> pdbids = new HashSet<>();
-
     List<DBRefEntry> refs = seq.getDBRefs();
+    int ib = checkUniprotRefs(refs);
+    if (ib > -1)
+    {
+      return getDBRefId(refs.get(ib));
+    }
+    return null;
+  }
+
+  /**
+   * Searches DBRefEntry for uniprot refs
+   * 
+   * @param seq
+   * @return -2 if no uniprot refs, -1 if no canonical ref., otherwise index of
+   *         Uniprot canonical DBRefEntry
+   */
+  public static int checkUniprotRefs(List<DBRefEntry> refs)
+  {
+    boolean hasUniprot = false;
     if (refs != null && refs.size() != 0)
     {
       for (int ib = 0, nb = refs.size(); ib < nb; ib++)
       {
         DBRefEntry dbRef = refs.get(ib);
-        if (isValidSeqName(getDBRefId(dbRef))
-                && queryBuilder.length() < MAX_QLENGTH)
+        if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
         {
-          if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT)
-                  && dbRef.isCanonical())
+          hasUniprot = true;
+          if (dbRef.isCanonical())
           {
-            // TODO: pick best Uniprot accession
-            isUniProtRefsFound = true;
-            return getDBRefId(dbRef);
-
+            return ib;
           }
         }
       }
     }
-    return null;
+    return hasUniprot ? -1 : -2;
   }
 
   /**
@@ -118,7 +141,6 @@ public class ThreeDBStructureChooserQuerySource
    */
   static boolean isValidSeqName(String seqName)
   {
-    // System.out.println("seqName : " + seqName);
     String ignoreList = "pdb,uniprot,swiss-prot";
     if (seqName.length() < 3)
     {
@@ -128,7 +150,7 @@ public class ThreeDBStructureChooserQuerySource
     {
       return false;
     }
-    seqName = seqName.toLowerCase();
+    seqName = seqName.toLowerCase(Locale.ROOT);
     for (String ignoredEntry : ignoreList.split(","))
     {
       if (seqName.contains(ignoredEntry))
@@ -160,22 +182,26 @@ public class ThreeDBStructureChooserQuerySource
    * @return
    * @throws Exception
    */
+  @Override
   public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
           Collection<FTSDataColumnI> wantedFields,
           FilterOption selectedFilterOpt, boolean b) throws Exception
   {
     FTSRestResponse resultList;
-    if (selectedFilterOpt!=null && tdBeaconsFilter(selectedFilterOpt.getValue()))
+    if (selectedFilterOpt != null
+            && tdBeaconsFilter(selectedFilterOpt.getValue()))
     {
       FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
       resultList = tdbRestClient.executeRequest(tdbRequest);
 
       lastTdbRequest = tdbRequest;
+      if (resultList != null)
+      { // Query the PDB and add additional metadata
+        List<FTSRestResponse> pdbResponse = fetchStructuresMetaDataFor(
+                getPDBQuerySource(), resultList);
 
-      // Query the PDB and add additional metadata
-      FTSRestResponse pdbResponse = fetchStructuresMetaDataFor(
-              getPDBQuerySource(), resultList);
-      FTSRestResponse joinedResp = joinResponses(resultList, pdbResponse);
+        resultList = joinResponses(resultList, pdbResponse);
+      }
       return resultList;
     }
     // use the PDBFTS directly
@@ -183,7 +209,7 @@ public class ThreeDBStructureChooserQuerySource
             wantedFields, selectedFilterOpt, b);
     lastTdbRequest = getPDBQuerySource().lastPdbRequest;
     lastPdbRequest = lastTdbRequest; // both queries the same - indicates we
-                                     // rank using PDBe
+    // rank using PDBe
     return resultList;
 
   }
@@ -235,22 +261,43 @@ public class ThreeDBStructureChooserQuerySource
   public void updateAvailableFilterOptions(String VIEWS_FILTER,
           List<FilterOption> xtantOptions, Collection<FTSData> tdbEntries)
   {
-    if (tdbEntries !=null && lastTdbRequest != null)
+    if (tdbEntries != null && lastTdbRequest != null)
     {
-      int prov_idx = lastTdbRequest.getFieldIndex("Provider");
-
-      for (FTSData row : tdbEntries)
+      boolean hasPDBe = false;
+      for (FTSData _row : tdbEntries)
       {
-        String provider = (String) row.getSummaryData()[prov_idx];
-        FilterOption providerOpt = new FilterOption("3DB Provider - " + provider,
-                FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER,
-                false, this);
+        // tdb returns custom object
+        TDB_FTSData row = (TDB_FTSData) _row;
+        String provider = row.getProvider();
+        FilterOption providerOpt = new FilterOption(
+                "3DB Provider - " + provider,
+                FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER, false, this);
         if (!xtantOptions.contains(providerOpt))
         {
-          xtantOptions.add(1,
-                  providerOpt);
-          tdBeaconsFilters.add(FILTER_SOURCE_PREFIX+provider);
-
+          xtantOptions.add(1, providerOpt);
+          tdBeaconsFilters.add(FILTER_SOURCE_PREFIX + provider);
+          if ("PDBe".equalsIgnoreCase(provider))
+          {
+            hasPDBe = true;
+          }
+        }
+      }
+      if (!hasPDBe)
+      {
+        // remove the PDBe options from the available filters
+        int op = 0;
+        while (op < xtantOptions.size())
+        {
+          FilterOption filter = xtantOptions.get(op);
+          if (filter
+                  .getQuerySource() instanceof PDBStructureChooserQuerySource)
+          {
+            xtantOptions.remove(op);
+          }
+          else
+          {
+            op++;
+          }
         }
       }
     }
@@ -280,8 +327,9 @@ public class ThreeDBStructureChooserQuerySource
   @Override
   public boolean needsRefetch(FilterOption selectedFilterOpt)
   {
-    return selectedFilterOpt==null || !tdBeaconsFilter(selectedFilterOpt.getValue())
-            && lastPdbRequest != lastTdbRequest;
+    return selectedFilterOpt == null
+            || !tdBeaconsFilter(selectedFilterOpt.getValue())
+                    && lastPdbRequest != lastTdbRequest;
   }
 
   /**
@@ -299,12 +347,13 @@ public class ThreeDBStructureChooserQuerySource
    * @return
    * @throws Exception
    */
+  @Override
   public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
           Collection<FTSData> collectedResults,
           Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
           boolean b) throws Exception
   {
-    if (fieldToFilterBy!=null && tdBeaconsFilter(fieldToFilterBy))
+    if (fieldToFilterBy != null && tdBeaconsFilter(fieldToFilterBy))
     {
       TDBResultAnalyser analyser = new TDBResultAnalyser(seq,
               collectedResults, lastTdbRequest, fieldToFilterBy,
@@ -336,8 +385,8 @@ public class ThreeDBStructureChooserQuerySource
     int idColumnIndex = restable.getColumn("Model id").getModelIndex();
     int urlColumnIndex = restable.getColumn("Url").getModelIndex();
     int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
-    int categoryColumnIndex = restable.getColumn("Model Category")
-            .getModelIndex();
+    int humanUrl = restable.getColumn("Page URL").getModelIndex();
+    int modelformat = restable.getColumn("Model Format").getModelIndex();
     final int up_start_idx = restable.getColumn("Uniprot Start")
             .getModelIndex();
     final int up_end_idx = restable.getColumn("Uniprot End")
@@ -372,6 +421,10 @@ public class ThreeDBStructureChooserQuerySource
       String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
       String typeColumn = restable.getValueAt(row, typeColumnIndex)
               .toString();
+      String modelPage = humanUrl < 1 ? null
+              : (String) restable.getValueAt(row, humanUrl);
+      String strucFormat = restable.getValueAt(row, modelformat).toString();
+
       SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
               refSeqColIndex);
       selectedSeqsToView.add(selectedSeq);
@@ -385,13 +438,22 @@ public class ThreeDBStructureChooserQuerySource
       {
         pdbEntry = new PDBEntry();
         pdbEntry.setId(pdbIdStr);
-        boolean hasCif = urlStr.toLowerCase(Locale.ENGLISH).endsWith("cif");
-        boolean probablyPdb = urlStr.toLowerCase(Locale.ENGLISH).contains("pdb");
-        pdbEntry.setType(hasCif ? PDBEntry.Type.MMCIF : probablyPdb ? PDBEntry.Type.PDB : PDBEntry.Type.FILE);
+        pdbEntry.setAuthoritative(true);
+        try
+        {
+          pdbEntry.setType(PDBEntry.Type.valueOf(strucFormat));
+        } catch (Exception q)
+        {
+          Console.warn("Unknown filetype for 3D Beacons Model from: "
+                  + strucFormat + " - " + pdbIdStr + " - " + modelPage);
+        }
+
         if (!"PDBe".equalsIgnoreCase(typeColumn))
         {
           pdbEntry.setRetrievalUrl(urlStr);
         }
+        pdbEntry.setProvider(typeColumn);
+        pdbEntry.setProviderPage(modelPage);
         selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
       }
       pdbEntriesToView[count++] = pdbEntry;
@@ -413,9 +475,10 @@ public class ThreeDBStructureChooserQuerySource
    * @return
    */
 
-  public String buildPDBFTSQueryFor(FTSRestResponse upResponse)
+  public List<String> buildPDBFTSQueryFor(FTSRestResponse upResponse)
   {
-    List<String> pdbIds = new ArrayList<String>();
+    List<String> ftsQueries = new ArrayList<String>();
+    Set<String> pdbIds = new HashSet<String>();
     int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
     int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
     for (FTSData row : upResponse.getSearchSummary())
@@ -427,7 +490,25 @@ public class ThreeDBStructureChooserQuerySource
         pdbIds.add(id);
       }
     }
-    return String.join(" OR ", pdbIds).toString();
+    StringBuilder sb = new StringBuilder();
+    for (String pdbId : pdbIds)
+    {
+      if (sb.length() > 2500)
+      {
+        ftsQueries.add(sb.toString());
+        sb.setLength(0);
+      }
+      if (sb.length() > 0)
+      {
+        sb.append(" OR ");
+      }
+      sb.append(pdbId);
+    }
+    if (sb.length() > 0)
+    {
+      ftsQueries.add(sb.toString());
+    }
+    return ftsQueries;
   }
 
   /**
@@ -437,58 +518,86 @@ public class ThreeDBStructureChooserQuerySource
    * @param upResponse
    * @return FTSRestResponse via PDBStructureChooserQuerySource
    */
-  public FTSRestResponse fetchStructuresMetaDataFor(
+  public List<FTSRestResponse> fetchStructuresMetaDataFor(
           PDBStructureChooserQuerySource pdbquery,
           FTSRestResponse upResponse) throws Exception
   {
-
-    String pdb_Query = buildPDBFTSQueryFor(upResponse);
-    if (pdb_Query.length()==0)
+    List<String> pdb_Queries = buildPDBFTSQueryFor(upResponse);
+    if (pdb_Queries.size() == 0)
     {
       return null;
     }
-    FTSRestResponse resultList;
-    FTSRestRequest pdbRequest = new FTSRestRequest();
-    pdbRequest.setAllowEmptySeq(false);
-    pdbRequest.setResponseSize(500);
-    pdbRequest.setFieldToSearchBy("(");
-    // pdbRequest.setFieldToSortBy("pdb_id");
-    pdbRequest.setWantedFields(
-            pdbquery.getDocFieldPrefs().getStructureSummaryFields());
-    pdbRequest.setSearchTerm(pdb_Query + ")");
-    
-    resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
-
-    lastPdbRequest = pdbRequest;
-    return resultList;
+    List<FTSRestResponse> results = new ArrayList<FTSRestResponse>();
+
+    for (String pdb_Query : pdb_Queries)
+    {
+      FTSRestResponse resultList;
+      FTSRestRequest pdbRequest = new FTSRestRequest();
+      pdbRequest.setAllowEmptySeq(false);
+      pdbRequest.setResponseSize(500);
+      pdbRequest.setFieldToSearchBy("(");
+      // pdbRequest.setFieldToSortBy("pdb_id");
+      pdbRequest.setWantedFields(
+              pdbquery.getDocFieldPrefs().getStructureSummaryFields());
+      pdbRequest.setSearchTerm(pdb_Query + ")");
+
+      // handle exceptions like server errors here - means the threedbeacons
+      // discovery isn't broken by issues to do with the PDBe SOLR api
+      try
+      {
+        resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
+        results.add(resultList);
+        lastPdbRequest = pdbRequest;
+      } catch (Exception ex)
+      {
+        Console.error("PDBFTSQuery failed", ex);
+      }
+
+    }
+
+    return results;
   }
 
   public FTSRestResponse joinResponses(FTSRestResponse upResponse,
-          FTSRestResponse pdbResponse)
+          List<FTSRestResponse> pdbResponses)
   {
+    boolean hasPdbResp = lastPdbRequest != null;
+
     int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
     // join on
     int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
-    int pdbIdx = lastPdbRequest.getFieldIndex("PDB Id");
-    int pdbTitle_idx = lastPdbRequest.getFieldIndex("Title");
+    int pdbIdx = hasPdbResp ? lastPdbRequest.getFieldIndex("PDB Id") : -1;
+    int pdbTitle_idx = hasPdbResp ? lastPdbRequest.getFieldIndex("Title")
+            : -1;
     int tdbTitle_idx = getLastFTSRequest().getFieldIndex("Title");
 
-    List<FTSData> joinedRows = new ArrayList<FTSData>();
     for (final FTSData row : upResponse.getSearchSummary())
     {
       String id = (String) row.getSummaryData()[idx_modelId];
       String provider = (String) row.getSummaryData()[idx_provider];
       if ("PDBe".equalsIgnoreCase(provider))
       {
-        for (final FTSData pdbrow : pdbResponse.getSearchSummary())
+        if (!hasPdbResp)
+        {
+          System.out.println(
+                  "Warning: seems like we couldn't get to the PDBe search interface.");
+        }
+        else
         {
-          String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
-          if (id.equalsIgnoreCase(pdbid))
+          for (final FTSRestResponse pdbResponse : pdbResponses)
           {
-            row.getSummaryData()[tdbTitle_idx] = pdbrow
-                    .getSummaryData()[pdbTitle_idx];
+            for (final FTSData pdbrow : pdbResponse.getSearchSummary())
+            {
+              String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
+              if (id.equalsIgnoreCase(pdbid))
+              {
+                row.getSummaryData()[tdbTitle_idx] = pdbrow
+                        .getSummaryData()[pdbTitle_idx];
+              }
+            }
           }
         }
+
       }
       else
       {
@@ -498,4 +607,22 @@ public class ThreeDBStructureChooserQuerySource
     return upResponse;
   }
 
+  public TDB_FTSData getFTSDataFor(JTable restable, int selectedRow,
+          Collection<FTSData> discoveredStructuresSet)
+  {
+    int idColumnIndex = restable.getColumn("Model id").getModelIndex();
+
+    String modelId = (String) restable.getValueAt(selectedRow,
+            idColumnIndex);
+    for (FTSData row : discoveredStructuresSet)
+    {
+      if (row instanceof TDB_FTSData
+              && ((TDB_FTSData) row).getModelId().equals(modelId))
+      {
+        return ((TDB_FTSData) row);
+      }
+    }
+    return null;
+  }
+
 }
\ No newline at end of file