+package jalview.structures.models;
+
+import jalview.api.StructureSelectionManagerProvider;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.structure.StructureListener;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+
+import java.awt.event.ComponentEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A base class to hold common function for protein structure model binding.
+ * Initial version created by refactoring JMol and Chimera binding models, but
+ * other structure viewers could in principle be accommodated in future.
+ *
+ * @author gmcarstairs
+ *
+ */
+public abstract class AAStructureBindingModel extends
+ SequenceStructureBindingModel implements StructureListener,
+ StructureSelectionManagerProvider
+{
+
+ private StructureSelectionManager ssm;
+
+ private PDBEntry[] pdbEntry;
+
+ /*
+ * sequences mapped to each pdbentry
+ */
+ private SequenceI[][] sequence;
+
+ /*
+ * array of target chains for sequences - tied to pdbentry and sequence[]
+ */
+ private String[][] chains;
+
+ /*
+ * datasource protocol for access to PDBEntrylatest
+ */
+ String protocol = null;
+
+ protected boolean colourBySequence = true;
+
+ /**
+ * Constructor
+ *
+ * @param ssm
+ * @param seqs
+ */
+ public AAStructureBindingModel(StructureSelectionManager ssm,
+ SequenceI[][] seqs)
+ {
+ this.ssm = ssm;
+ this.sequence = seqs;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param ssm
+ * @param pdbentry
+ * @param sequenceIs
+ * @param chains
+ * @param protocol
+ */
+ public AAStructureBindingModel(StructureSelectionManager ssm,
+ PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
+ String protocol)
+ {
+ this.ssm = ssm;
+ this.sequence = sequenceIs;
+ this.chains = chains;
+ this.pdbEntry = pdbentry;
+ this.protocol = protocol;
+ if (chains == null)
+ {
+ this.chains = new String[pdbentry.length][];
+ }
+ }
+
+ public StructureSelectionManager getSsm()
+ {
+ return ssm;
+ }
+
+ /**
+ * Returns the i'th PDBEntry (or null)
+ *
+ * @param i
+ * @return
+ */
+ public PDBEntry getPdbEntry(int i)
+ {
+ return (pdbEntry != null && pdbEntry.length > i) ? pdbEntry[i] : null;
+ }
+
+ /**
+ * Returns the number of modelled PDB file entries.
+ *
+ * @return
+ */
+ public int getPdbCount()
+ {
+ return pdbEntry == null ? 0 : pdbEntry.length;
+ }
+
+ public SequenceI[][] getSequence()
+ {
+ return sequence;
+ }
+
+ public String[][] getChains()
+ {
+ return chains;
+ }
+
+ public String getProtocol()
+ {
+ return protocol;
+ }
+
+ // TODO may remove this if calling methods can be pulled up here
+ protected void setPdbentry(PDBEntry[] pdbentry)
+ {
+ this.pdbEntry = pdbentry;
+ }
+
+ protected void setSequence(SequenceI[][] sequence)
+ {
+ this.sequence = sequence;
+ }
+
+ protected void setChains(String[][] chains)
+ {
+ this.chains = chains;
+ }
+
+ /**
+ * Construct a title string for the viewer window based on the data Jalview
+ * knows about
+ * @param viewerName TODO
+ * @param verbose
+ *
+ * @return
+ */
+ public String getViewerTitle(String viewerName, boolean verbose)
+ {
+ if (getSequence() == null || getSequence().length < 1
+ || getPdbCount() < 1
+ || getSequence()[0].length < 1)
+ {
+ return ("Jalview " + viewerName + " Window");
+ }
+ // TODO: give a more informative title when multiple structures are
+ // displayed.
+ StringBuilder title = new StringBuilder(64);
+ final PDBEntry pdbEntry = getPdbEntry(0);
+ title.append(viewerName + " view for " + getSequence()[0][0].getName()
+ + ":"
+ + pdbEntry.getId());
+
+ if (verbose)
+ {
+ if (pdbEntry.getProperty() != null)
+ {
+ if (pdbEntry.getProperty().get("method") != null)
+ {
+ title.append(" Method: ");
+ title.append(pdbEntry.getProperty().get("method"));
+ }
+ if (pdbEntry.getProperty().get("chains") != null)
+ {
+ title.append(" Chain:");
+ title.append(pdbEntry.getProperty().get("chains"));
+ }
+ }
+ }
+ return title.toString();
+ }
+
+ /**
+ * Called by after closeViewer is called, to release any resources and
+ * references so they can be garbage collected. Override if needed.
+ */
+ protected void releaseUIResources()
+ {
+
+ }
+
+ public boolean isColourBySequence()
+ {
+ return colourBySequence;
+ }
+
+ public void setColourBySequence(boolean colourBySequence)
+ {
+ this.colourBySequence = colourBySequence;
+ }
+
+ protected void addSequenceAndChain(int pe, SequenceI[] seq,
+ String[] tchain)
+ {
+ if (pe < 0 || pe >= getPdbCount())
+ {
+ throw new Error(MessageManager.formatMessage(
+ "error.implementation_error_no_pdbentry_from_index",
+ new Object[]
+ { Integer.valueOf(pe).toString() }));
+ }
+ final String nullChain = "TheNullChain";
+ List<SequenceI> s = new ArrayList<SequenceI>();
+ List<String> c = new ArrayList<String>();
+ if (getChains() == null)
+ {
+ setChains(new String[getPdbCount()][]);
+ }
+ if (getSequence()[pe] != null)
+ {
+ for (int i = 0; i < getSequence()[pe].length; i++)
+ {
+ s.add(getSequence()[pe][i]);
+ if (getChains()[pe] != null)
+ {
+ if (i < getChains()[pe].length)
+ {
+ c.add(getChains()[pe][i]);
+ }
+ else
+ {
+ c.add(nullChain);
+ }
+ }
+ else
+ {
+ if (tchain != null && tchain.length > 0)
+ {
+ c.add(nullChain);
+ }
+ }
+ }
+ }
+ for (int i = 0; i < seq.length; i++)
+ {
+ if (!s.contains(seq[i]))
+ {
+ s.add(seq[i]);
+ if (tchain != null && i < tchain.length)
+ {
+ c.add(tchain[i] == null ? nullChain : tchain[i]);
+ }
+ }
+ }
+ SequenceI[] tmp = s.toArray(new SequenceI[s.size()]);
+ getSequence()[pe] = tmp;
+ if (c.size() > 0)
+ {
+ String[] tch = c.toArray(new String[c.size()]);
+ for (int i = 0; i < tch.length; i++)
+ {
+ if (tch[i] == nullChain)
+ {
+ tch[i] = null;
+ }
+ }
+ getChains()[pe] = tch;
+ }
+ else
+ {
+ getChains()[pe] = null;
+ }
+ }
+
+ /**
+ * add structures and any known sequence associations
+ *
+ * @returns the pdb entries added to the current set.
+ */
+ public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe, SequenceI[][] seq,
+ String[][] chns)
+ {
+ List<PDBEntry> v = new ArrayList<PDBEntry>();
+ List<int[]> rtn = new ArrayList<int[]>();
+ for (int i = 0; i < getPdbCount(); i++)
+ {
+ v.add(getPdbEntry(i));
+ }
+ for (int i = 0; i < pdbe.length; i++)
+ {
+ int r = v.indexOf(pdbe[i]);
+ if (r == -1 || r >= getPdbCount())
+ {
+ rtn.add(new int[]
+ { v.size(), i });
+ v.add(pdbe[i]);
+ }
+ else
+ {
+ // just make sure the sequence/chain entries are all up to date
+ addSequenceAndChain(r, seq[i], chns[i]);
+ }
+ }
+ pdbe = v.toArray(new PDBEntry[v.size()]);
+ setPdbentry(pdbe);
+ if (rtn.size() > 0)
+ {
+ // expand the tied sequence[] and string[] arrays
+ SequenceI[][] sqs = new SequenceI[getPdbCount()][];
+ String[][] sch = new String[getPdbCount()][];
+ System.arraycopy(getSequence(), 0, sqs, 0, getSequence().length);
+ System.arraycopy(getChains(), 0, sch, 0, this.getChains().length);
+ setSequence(sqs);
+ setChains(sch);
+ pdbe = new PDBEntry[rtn.size()];
+ for (int r = 0; r < pdbe.length; r++)
+ {
+ int[] stri = (rtn.get(r));
+ // record the pdb file as a new addition
+ pdbe[r] = getPdbEntry(stri[0]);
+ // and add the new sequence/chain entries
+ addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
+ }
+ }
+ else
+ {
+ pdbe = null;
+ }
+ return pdbe;
+ }
+
+ /**
+ * Add sequences to the pe'th pdbentry's sequence set.
+ *
+ * @param pe
+ * @param seq
+ */
+ public void addSequence(int pe, SequenceI[] seq)
+ {
+ addSequenceAndChain(pe, seq, null);
+ }
+
+}
\ No newline at end of file