Merge branch 'spike/JAL-2040_JAL-2137_phyre2' into features/JAL-2040_JAL-2137_phyre2 features/JAL-2040_JAL-2137_phyre2
authorJim Procter <jprocter@issues.jalview.org>
Thu, 19 Jan 2017 17:08:41 +0000 (17:08 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Thu, 19 Jan 2017 17:08:41 +0000 (17:08 +0000)
1  2 
src/jalview/ext/jmol/JmolParser.java
src/jalview/io/AnnotationFile.java
src/jalview/structure/StructureSelectionManager.java

@@@ -22,18 -22,16 +22,18 @@@ package jalview.ext.jmol
  
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.Annotation;
 -import jalview.datamodel.DBRefSource;
 +import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
  import jalview.io.FileParse;
  import jalview.io.StructureFile;
  import jalview.schemes.ResidueProperties;
 -import jalview.structure.StructureViewSettings;
 +import jalview.structure.StructureImportSettings;
 +import jalview.util.Format;
  import jalview.util.MessageManager;
  
  import java.io.IOException;
  import java.util.ArrayList;
 +import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Vector;
@@@ -61,12 -59,15 +61,12 @@@ public class JmolParser extends Structu
  {
    Viewer viewer = null;
  
 -  public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr,
 -          boolean externalSecStr, String inFile, String type)
 -          throws IOException
 +  public JmolParser(String inFile, String type) throws IOException
    {
      super(inFile, type);
    }
  
 -  public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr,
 -          boolean externalSecStr, FileParse fp) throws IOException
 +  public JmolParser(FileParse fp) throws IOException
    {
      super(fp);
    }
    @Override
    public void parse() throws IOException
    {
 -    String dataName = getDataName();
 -    if (dataName.endsWith(".cif"))
 -    {
 -      setDbRefType(DBRefSource.MMCIF);
 -    }
 -    else
 -    {
 -      setDbRefType(DBRefSource.PDB);
 -    }
      setChains(new Vector<PDBChain>());
      Viewer jmolModel = getJmolData();
      jmolModel.openReader(getDataName(), getDataName(), getReader());
       */
      if (jmolModel.ms.mc > 0)
      {
 +      // ideally we do this
 +      // try
 +      // {
 +      // setStructureFileType(jmolModel.evalString("show _fileType"));
 +      // } catch (Exception q)
 +      // {
 +      // }
 +      // ;
 +      // instead, we distinguish .cif from non-.cif by filename
 +      setStructureFileType(getDataName().toLowerCase().endsWith(".cif") ? PDBEntry.Type.MMCIF
 +              .toString() : "PDB");
 +
        transformJmolModelToJalview(jmolModel.ms);
      }
    }
      {
        try
        {
 +        /*
 +         * params -o (output to sysout) -n (nodisplay) -x (exit when finished)
 +         * see http://wiki.jmol.org/index.php/Jmol_Application
 +         */
          viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
                  null, "-x -o -n", this);
          // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
        List<SequenceI> prot = new ArrayList<SequenceI>();
        PDBChain tmpchain;
        String pdbId = (String) ms.getInfo(0, "title");
 -      setId(pdbId);
 +
 +      if (pdbId == null)
 +      {
 +        setId(safeName(getDataName()));
 +        setPDBIdAvailable(false);
 +      }
 +      else
 +      {
 +        setId(pdbId);
 +        setPDBIdAvailable(true);
 +      }
        List<Atom> significantAtoms = convertSignificantAtoms(ms);
        for (Atom tmpatom : significantAtoms)
        {
            tmpchain.atoms.addElement(tmpatom);
          } catch (Exception e)
          {
 -          tmpchain = new PDBChain(pdbId, tmpatom.chain);
 +          tmpchain = new PDBChain(getId(), tmpatom.chain);
            getChains().add(tmpchain);
            tmpchain.atoms.addElement(tmpatom);
          }
        makeResidueList();
        makeCaBondList();
  
+       if (getId() == null)
+       {
+         // always use resource name, not the hardwired file
+         // Does the value of ID get used ? Behaviour needs to be
+         // documented and tested
+         setId(getDataName());
+       }
++
        for (PDBChain chain : getChains())
        {
          SequenceI chainseq = postProcessChain(chain);
            prot.add(chainseq);
          }
  
 -        if (StructureViewSettings.isPredictSecondaryStructure())
 +        if (StructureImportSettings.isProcessSecondaryStructure())
          {
            createAnnotation(chainseq, chain, ms.at);
          }
    private List<Atom> convertSignificantAtoms(ModelSet ms)
    {
      List<Atom> significantAtoms = new ArrayList<Atom>();
 +    HashMap<String, org.jmol.modelset.Atom> chainTerMap = new HashMap<String, org.jmol.modelset.Atom>();
 +    org.jmol.modelset.Atom prevAtom = null;
      for (org.jmol.modelset.Atom atom : ms.at)
      {
        if (atom.getAtomName().equalsIgnoreCase("CA")
                || atom.getAtomName().equalsIgnoreCase("P"))
        {
 +        if (!atomValidated(atom, prevAtom, chainTerMap))
 +        {
 +          continue;
 +        }
          Atom curAtom = new Atom(atom.x, atom.y, atom.z);
          curAtom.atomIndex = atom.getIndex();
          curAtom.chain = atom.getChainIDStr();
 -        curAtom.insCode = atom.group.getInsertionCode();
 +        curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' '
 +                : atom.group.getInsertionCode();
          curAtom.name = atom.getAtomName();
          curAtom.number = atom.getAtomNumber();
          curAtom.resName = atom.getGroup3(true);
          curAtom.resNumber = atom.getResno();
          curAtom.occupancy = ms.occupancies != null ? ms.occupancies[atom
                  .getIndex()] : Float.valueOf(atom.getOccupancy100());
 -        curAtom.resNumIns = "" + curAtom.resNumber + curAtom.insCode;
 +        String fmt = new Format("%4i").form(curAtom.resNumber);
 +        curAtom.resNumIns = (fmt + curAtom.insCode);
          curAtom.tfactor = atom.getBfactor100() / 100f;
          curAtom.type = 0;
 -        significantAtoms.add(curAtom);
 +        // significantAtoms.add(curAtom);
 +        // ignore atoms from subsequent models
 +        if (!significantAtoms.contains(curAtom))
 +        {
 +          significantAtoms.add(curAtom);
 +        }
 +        prevAtom = atom;
        }
      }
      return significantAtoms;
    }
  
 +  private boolean atomValidated(org.jmol.modelset.Atom curAtom,
 +          org.jmol.modelset.Atom prevAtom,
 +          HashMap<String, org.jmol.modelset.Atom> chainTerMap)
 +  {
 +    // System.out.println("Atom: " + curAtom.getAtomNumber()
 +    // + "   Last atom index " + curAtom.group.lastAtomIndex);
 +    if (chainTerMap == null || prevAtom == null)
 +    {
 +      return true;
 +    }
 +    String curAtomChId = curAtom.getChainIDStr();
 +    String prevAtomChId = prevAtom.getChainIDStr();
 +    // new chain encoutered
 +    if (!prevAtomChId.equals(curAtomChId))
 +    {
 +      // On chain switch add previous chain termination to xTerMap if not exists
 +      if (!chainTerMap.containsKey(prevAtomChId))
 +      {
 +        chainTerMap.put(prevAtomChId, prevAtom);
 +      }
 +      // if current atom belongs to an already terminated chain and the resNum
 +      // diff < 5 then mark as valid and update termination Atom
 +      if (chainTerMap.containsKey(curAtomChId))
 +      {
 +        if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
 +        {
 +          return false;
 +        }
 +        if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5)
 +        {
 +          chainTerMap.put(curAtomChId, curAtom);
 +          return true;
 +        }
 +        return false;
 +      }
 +    }
 +    // atom with previously terminated chain encountered
 +    else if (chainTerMap.containsKey(curAtomChId))
 +    {
 +      if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
 +      {
 +        return false;
 +      }
 +      if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5)
 +      {
 +        chainTerMap.put(curAtomChId, curAtom);
 +        return true;
 +      }
 +      return false;
 +    }
 +    // HETATM with resNum jump > 2
 +    return !(curAtom.isHetero() && ((curAtom.getResno() - prevAtom
 +            .getResno()) > 2));
 +  }
 +
    private void createAnnotation(SequenceI sequence, PDBChain chain,
            org.jmol.modelset.Atom[] jmolAtoms)
    {
        {
          try
          {
 -        asecstr[p] = new Annotation(String.valueOf(secstr[p]), null,
 -                secstrcode[p], Float.NaN);
 -        ssFound = true;
 +          asecstr[p] = new Annotation(String.valueOf(secstr[p]), null,
 +                  secstrcode[p], Float.NaN);
 +          ssFound = true;
          } catch (Exception e)
          {
            // e.printStackTrace();
@@@ -28,14 -28,20 +28,19 @@@ import jalview.datamodel.Annotation
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.GraphLine;
  import jalview.datamodel.HiddenSequences;
+ import jalview.datamodel.PDBEntry;
+ import jalview.datamodel.PDBEntry.Type;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
 -import jalview.schemes.ResidueProperties;
  import jalview.schemes.UserColourScheme;
+ import jalview.structure.StructureSelectionManager;
  
  import java.io.BufferedReader;
+ import java.io.File;
  import java.io.FileReader;
+ import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.StringReader;
  import java.net.URL;
@@@ -686,21 -692,41 +691,41 @@@ public class AnnotationFil
    public boolean readAnnotationFile(AlignmentI al, ColumnSelection colSel,
            String file, String protocol)
    {
+     baseUri = "";
      BufferedReader in = null;
      try
      {
        if (protocol.equals(AppletFormatAdapter.FILE))
        {
          in = new BufferedReader(new FileReader(file));
+         baseUri = new File(file).getParent();
+         if (baseUri == null)
+         {
+           baseUri = "";
+         }
+         else
+         {
+           baseUri += "/";
+         }
        }
        else if (protocol.equals(AppletFormatAdapter.URL))
        {
          URL url = new URL(file);
          in = new BufferedReader(new InputStreamReader(url.openStream()));
+         String bs = url.toExternalForm();
+         baseUri = bs.substring(0, bs.indexOf(url.getHost())
+                 + url.getHost().length());
+         baseUri += url.toURI().getPath();
+         if (baseUri.lastIndexOf("/") > -1)
+         {
+           baseUri = baseUri.substring(0, baseUri.lastIndexOf("/")) + "/";
+         }
        }
        else if (protocol.equals(AppletFormatAdapter.PASTE))
        {
          in = new BufferedReader(new StringReader(file));
+         // TODO - support mimencoded PDBs for a paste.. ?
+         baseUri = "";
        }
        else if (protocol.equals(AppletFormatAdapter.CLASSLOADER))
        {
          if (is != null)
          {
            in = new BufferedReader(new java.io.InputStreamReader(is));
+           // TODO: this probably doesn't work for classloader - needs a test
+           baseUri = new File("/" + file).getParent() + "/";
          }
        }
        if (in != null)
  
    String lastread = "";
  
-   private static String GRAPHLINE = "GRAPHLINE", COMBINE = "COMBINE";
+   /**
+    * used for resolving absolute references to resources relative to
+    * annotationFile location
+    */
+   String baseUri = "";
+   private static String GRAPHLINE = "GRAPHLINE", COMBINE = "COMBINE",
+           STRUCTMODEL = "STRUCTMODEL";
  
    public boolean parseAnnotationFrom(AlignmentI al, ColumnSelection colSel,
            BufferedReader in) throws Exception
            modified = true;
            continue;
          }
+         else if (token.equalsIgnoreCase(STRUCTMODEL))
+         {
+           boolean failedtoadd = true;
+           // expect
+           // STRUCTMODEL <QUERYID> <TemplateID> <URL to model> <URL to
+           // alignment>
+           if (st.hasMoreTokens()) {
+             refSeq = al.findName(refSeqId = st.nextToken());
+             if (refSeq == null)
+             {
+               System.err.println("Couldn't locate " + refSeqId
+                       + " in the alignment for STRUCTMODEL");
+               refSeqId = null;
+             }
+             else
+             {
+               String tempId = st.nextToken();
+               String urlToModel = st.nextToken();
+               String urlToPairwise = st.hasMoreTokens() ? st.nextToken()
+                       : "";
+               if (add_structmodel(al, refSeq, tempId, urlToModel,
+                       urlToPairwise))
+               {
+                 failedtoadd = false;
+               }
+             }
+           }
+           if (failedtoadd)
+           {
+             System.err
+                     .println("Need <QueryId> <TemplateId> <URL to Model> [<URL to pairwise alignment>] as tab separated fields after "
+                             + STRUCTMODEL);
+           } else {
+             modified = true;
+           }
+           continue;
+         }
          // Parse out the annotation row
          graphStyle = AlignmentAnnotation.getGraphValueFromString(token);
          label = st.nextToken();
      return modified;
    }
  
+   /**
+    * resolve a structural model and generate and add an alignment sequence for
+    * it
+    * 
+    * @param refSeq2
+    * @param tempId
+    * @param urlToModel
+    * @param urlToPairwise
+    * @return true if model and sequence was added
+    */
+   private boolean add_structmodel(AlignmentI al, SequenceI refSeq2, String tempId,
+           String urlToModel, String urlToPairwise)
+   {
+     String warningMessage = null, modelPath = null, modelProt = null, aliPath = null, aliProt = null;
+     boolean added = false;
+     try {
+       // locate tempId. if it exists, will need to merge, otherwise:
+       SequenceI templateSeq = al.findName(tempId);
+       // 1. load urlToModel
+       modelPath = resolveAbsolute(urlToModel);
+       modelProt = AppletFormatAdapter.checkProtocol(modelPath);
+       // need to transfer to local temp file ?
+       PDBEntry modelpe = new PDBEntry(tempId, null, Type.FILE, modelPath);
+       PDBEntry templpe = new PDBEntry(tempId, null, Type.FILE, modelPath);
+       refSeq2.addPDBId(modelpe);
+       aliPath = resolveAbsolute(urlToPairwise);
+       aliProt = AppletFormatAdapter.checkProtocol(aliPath);
+       // 2. load urlToPairwise
+       AlignmentI pwa = new AppletFormatAdapter().readFile(aliPath, aliProt,
+               "FASTA");
+       SequenceI qPw = null, tPw = null;
+       if (pwa != null)
+       {
+         // resolve query/template sequences in provided alignment
+         qPw = pwa.findName(refSeqId);
+         tPw = pwa.findName(tempId);
+       }
+       if (false)
+       // (qPw != null && tPw != null)
+       {
+         // not yet complete
+         // refalQ vvva--addrvvvtttddd
+         // refalT ---aaaa---sss---ddd
+         // profalQ ---v-v-v-a.-.-a---dd--r--vvvtt--td--dd
+         // profalT ---.-.-.-aa-a-a---..--.--sss..--.d--dd
+         // Pragmatic solution here:
+         // Map templpe onto refalT only where refalT and refalQ are both
+         // non-gaps
+         // columns for start..end in refSeq2
+         int[] gapMap = refSeq2.gapMap();
+         // insert gaps in tPw
+         int curi = 0, width = refSeq2.getLength();
+         // TBC
+       }
+       else
+       {
+         // assume 1:1 - so synthesise sequences to use to construct mapping
+         StructureFile pdbf = StructureSelectionManager
+                 .getStructureSelectionManager().setMapping(false,
+                         new SequenceI[] { refSeq2.getDatasetSequence() },
+                         null, modelPath, modelProt);
+         refSeq2.getDatasetSequence().addPDBId(modelpe);
+         if (templateSeq == null && tPw != null)
+         {
+           tPw.createDatasetSequence();
+           tPw.getDatasetSequence().addPDBId(templpe); // needs to set mapping based on model yet...
+           al.addSequence(tPw);
+           added = true;
+         }
+       }
+     // 3. pad/insert gaps in urlToPairwise according to gaps already present in
+     // refSeq2
+     // 4. add padded tempId sequence to alignment
+     // 4. associate urlToModel with refSeq2 based on position map provided by
+     // urlToPairwise
+     // 5. associate urlToModel with tempId based on position map provided by
+     // urlToPairwise
+     // start a thread to load urlToModel and process/annotate sequences.
+     } catch (IOException x)
+     {
+       warningMessage = x.toString();
+     } finally {
+       if (warningMessage !=null)
+       {
+         System.err.println("Warnings whilst processing STRUCTMODEL: "+warningMessage);
+       }
+       return added;
+     }
+   }
+   private String resolveAbsolute(String relURI)
+   {
+     if (relURI.indexOf(":/") > -1 || relURI.startsWith("/")
+             || "".equals(baseUri) || relURI.startsWith(baseUri))
+     {
+       return relURI;
+     }
+     return baseUri + relURI;
+   }
    private void parseHideCols(ColumnSelection colSel, String nextToken)
    {
      StringTokenizer inval = new StringTokenizer(nextToken, ",");
          else if (key.equalsIgnoreCase("consThreshold"))
          {
            sg.cs.setConservationInc(Integer.parseInt(value));
 -          Conservation c = new Conservation("Group",
 -                  ResidueProperties.propHash, 3, sg.getSequences(null),
 -                  sg.getStartRes(), sg.getEndRes() + 1);
 +          Conservation c = new Conservation("Group", 3,
 +                  sg.getSequences(null), sg.getStartRes(),
 +                  sg.getEndRes() + 1);
  
            c.calculate();
            c.verdict(false, 25); // TODO: refer to conservation percent threshold
@@@ -32,7 -32,6 +32,7 @@@ import jalview.datamodel.Annotation
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SearchResults;
  import jalview.datamodel.SequenceI;
 +import jalview.ext.jmol.JmolParser;
  import jalview.gui.IProgressIndicator;
  import jalview.io.AppletFormatAdapter;
  import jalview.io.StructureFile;
@@@ -327,6 -326,7 +327,6 @@@ public class StructureSelectionManage
      return setMapping(true, sequence, targetChains, pdbFile, protocol);
    }
  
 -
    /**
     * create sequence structure mappings between each sequence and the given
     * pdbFile (retrieved via the given protocol).
     */
    synchronized public StructureFile setMapping(boolean forStructureView,
            SequenceI[] sequenceArray, String[] targetChainIds,
 -          String pdbFile,
 -          String protocol)
 +          String pdbFile, String protocol)
    {
      /*
       * There will be better ways of doing this in the future, for now we'll use
      boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
      try
      {
 -
 -      if (pdbFile != null && isCIFFile(pdbFile))
 -      {
 -        pdb = new jalview.ext.jmol.JmolParser(addTempFacAnnot, parseSecStr,
 -                secStructServices, pdbFile, protocol);
 -      }
 -      else
 -      {
 -        pdb = new PDBfile(addTempFacAnnot, parseSecStr, secStructServices,
 -                pdbFile, protocol);
 -      }
 +      pdb = new JmolParser(pdbFile, protocol);
  
        if (pdb.getId() != null && pdb.getId().trim().length() > 0
                && AppletFormatAdapter.FILE.equals(protocol))
        {
          registerPDBFile(pdb.getId().trim(), pdbFile);
        }
 +      // if PDBId is unavailable then skip SIFTS mapping execution path
 +      isMapUsingSIFTs = pdb.isPPDBIdAvailable();
 +
      } catch (Exception ex)
      {
        ex.printStackTrace();
      {
        boolean infChain = true;
        final SequenceI seq = sequenceArray[s];
 +      SequenceI ds = seq;
 +      while (ds.getDatasetSequence() != null)
 +      {
 +        ds = ds.getDatasetSequence();
 +      }
 +
        if (targetChainIds != null && targetChainIds[s] != null)
        {
          infChain = false;
        }
  
        ArrayList<StructureMapping> seqToStrucMapping = new ArrayList<StructureMapping>();
 -      if (isMapUsingSIFTs)
 +      if (isMapUsingSIFTs && seq.isProtein())
        {
          setProgressBar(null);
          setProgressBar(MessageManager
                      pdb, maxChain, sqmpping, maxAlignseq);
              seqToStrucMapping.add(siftsMapping);
              maxChain.makeExactMapping(maxAlignseq, seq);
 -            maxChain.transferRESNUMFeatures(seq, null);
 +            maxChain.transferRESNUMFeatures(seq, null);// FIXME: is this
 +                                                       // "IEA:SIFTS" ?
              maxChain.transferResidueAnnotation(siftsMapping, sqmpping);
 +            ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 +
            } catch (SiftsException e)
            {
              // fall back to NW alignment
              StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                      targetChainId, maxChain, pdb, maxAlignseq);
              seqToStrucMapping.add(nwMapping);
 +            maxChain.makeExactMapping(maxAlignseq, seq);
 +            maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
 +                                                        // "IEA:Jalview" ?
 +            maxChain.transferResidueAnnotation(nwMapping, sqmpping);
 +            ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
            }
          }
          else
              try
              {
                StructureMapping siftsMapping = getStructureMapping(seq,
 -                      pdbFile,
 -                      chain.id, pdb, chain, sqmpping, maxAlignseq);
 +                      pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq);
                foundSiftsMappings.add(siftsMapping);
              } catch (SiftsException e)
              {
            {
              seqToStrucMapping.addAll(foundSiftsMappings);
              maxChain.makeExactMapping(maxAlignseq, seq);
 -            maxChain.transferRESNUMFeatures(seq, null);
 +            maxChain.transferRESNUMFeatures(seq, null);// FIXME: is this
 +                                                       // "IEA:SIFTS" ?
              maxChain.transferResidueAnnotation(foundSiftsMappings.get(0),
                      sqmpping);
 +            ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
            }
            else
            {
              StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                      maxChainId, maxChain, pdb, maxAlignseq);
              seqToStrucMapping.add(nwMapping);
 +            maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
 +                                                        // "IEA:Jalview" ?
 +            maxChain.transferResidueAnnotation(nwMapping, sqmpping);
 +            ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
            }
          }
        }
          setProgressBar(null);
          setProgressBar(MessageManager
                  .getString("status.obtaining_mapping_with_nw_alignment"));
 -        seqToStrucMapping.add(getNWMappings(seq, pdbFile, maxChainId,
 -                maxChain, pdb, maxAlignseq));
 +        StructureMapping nwMapping = getNWMappings(seq, pdbFile,
 +                maxChainId, maxChain, pdb, maxAlignseq);
 +        seqToStrucMapping.add(nwMapping);
 +        ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 +
        }
  
        if (forStructureView)
      return "cif".equalsIgnoreCase(fileExt);
    }
  
 +  /**
 +   * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
 +   * uniprot or PDB
 +   * 
 +   * @param seq
 +   * @param pdbFile
 +   * @param targetChainId
 +   * @param pdb
 +   * @param maxChain
 +   * @param sqmpping
 +   * @param maxAlignseq
 +   * @return
 +   * @throws SiftsException
 +   */
    private StructureMapping getStructureMapping(SequenceI seq,
            String pdbFile, String targetChainId, StructureFile pdb,
            PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
            AlignSeq maxAlignseq) throws SiftsException
    {
 -      StructureMapping curChainMapping = siftsClient
 -              .getSiftsStructureMapping(seq, pdbFile, targetChainId);
 -      try
 -      {
 +    StructureMapping curChainMapping = siftsClient
 +            .getSiftsStructureMapping(seq, pdbFile, targetChainId);
 +    try
 +    {
        PDBChain chain = pdb.findChain(targetChainId);
        if (chain != null)
        {
          chain.transferResidueAnnotation(curChainMapping, sqmpping);
        }
 -      } catch (Exception e)
 -      {
 -        e.printStackTrace();
 -      }
 -      return curChainMapping;
 +    } catch (Exception e)
 +    {
 +      e.printStackTrace();
 +    }
 +    return curChainMapping;
    }
  
 -  private StructureMapping getNWMappings(SequenceI seq,
 -          String pdbFile,
 +  private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
            String maxChainId, PDBChain maxChain, StructureFile pdb,
            AlignSeq maxAlignseq)
    {
      return seqmappings;
    }
  
+   public static StructureSelectionManager getStructureSelectionManager()
+   {
+     return instances.values().iterator().next();
+   }
  }