/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
- * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
+ * 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.
- *
+ * 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/>.
+ * 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.datamodel;
-import java.util.*;
+import jalview.util.CaseInsensitiveString;
+
+import java.util.Hashtable;
public class PDBEntry
{
- String file;
+ private static final int PDB_ID_LENGTH = 4;
+
+ private String file;
+
+ private String type;
+
+ private String id;
+
+ public enum Type
+ {
+ PDB, MMCIF, FILE;
+ /**
+ * case insensitive matching for Type enum
+ *
+ * @param value
+ * @return
+ */
+ public static Type getType(String value)
+ {
+ for (Type t : Type.values())
+ {
+ if (t.toString().equalsIgnoreCase(value))
+ {
+ return t;
+ }
+ }
+ return null;
+ }
- String type;
+ /**
+ * case insensitive equivalence for strings resolving to PDBEntry type
+ *
+ * @param t
+ * @return
+ */
+ public boolean matches(String t)
+ {
+ return (this.toString().equalsIgnoreCase(t));
+ }
+ }
- String id;
+ /**
+ * constant for storing chain code in properties table
+ */
+ private static final String CHAIN_ID = "chain_code";
Hashtable properties;
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#equals(java.lang.Object)
+ /**
+ * Answers true if obj is a PDBEntry with the same id and chain code (both
+ * ignoring case), file, type and properties
*/
+ @Override
public boolean equals(Object obj)
{
if (obj == null || !(obj instanceof PDBEntry))
return false;
}
if (obj == this)
+ {
return true;
+ }
PDBEntry o = (PDBEntry) obj;
- return (file == o.file || (file != null && o.file != null && o.file
- .equals(file)))
- && (type == o.type || (type != null && o.type != null && o.type
- .equals(type)))
- && (id == o.id || (id != null && o.id != null && o.id
- .equalsIgnoreCase(id)))
- && (properties == o.properties || (properties != null
- && o.properties != null && properties
- .equals(o.properties)));
+
+ /*
+ * note that chain code is stored as a property wrapped by a
+ * CaseInsensitiveString, so we are in effect doing a
+ * case-insensitive comparison of chain codes
+ */
+ boolean idMatches = id == o.id
+ || (id != null && id.equalsIgnoreCase(o.id));
+ boolean fileMatches = file == o.file
+ || (file != null && file.equals(o.file));
+ boolean typeMatches = type == o.type
+ || (type != null && type.equals(o.type));
+ if (idMatches && fileMatches && typeMatches)
+ {
+ return properties == o.properties
+ || (properties != null && properties.equals(o.properties));
+ }
+ return false;
}
+ /**
+ * Default constructor
+ */
public PDBEntry()
{
}
+ /**
+ * Constructor given file path and PDB id.
+ *
+ * @param filePath
+ */
+ // public PDBEntry(String filePath, String pdbId)
+ // {
+ // this.file = filePath;
+ // this.id = pdbId;
+ // }
+
+ public PDBEntry(String pdbId, String chain, PDBEntry.Type type,
+ String filePath)
+ {
+ init(pdbId, chain, type, filePath);
+ }
+
+ /**
+ * @param pdbId
+ * @param chain
+ * @param type
+ * @param filePath
+ */
+ void init(String pdbId, String chain, PDBEntry.Type type, String filePath)
+ {
+ this.id = pdbId;
+ this.type = type == null ? null : type.toString();
+ this.file = filePath;
+ setChainCode(chain);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param entry
+ */
public PDBEntry(PDBEntry entry)
{
file = entry.file;
}
}
+ /**
+ * Make a PDBEntry from a DBRefEntry. The accession code is used for the PDB
+ * id, but if it is 5 characters in length, the last character is removed and
+ * set as the chain code instead.
+ *
+ * @param dbr
+ */
+ public PDBEntry(DBRefEntry dbr)
+ {
+ if (!DBRefSource.PDB.equals(dbr.getSource()))
+ {
+ throw new IllegalArgumentException("Invalid source: "
+ + dbr.getSource());
+ }
+
+ String pdbId = dbr.getAccessionId();
+ String chainCode = null;
+ if (pdbId.length() == PDB_ID_LENGTH + 1)
+ {
+ char chain = pdbId.charAt(PDB_ID_LENGTH);
+ if (('a' <= chain && chain <= 'z') || ('A' <= chain && chain <= 'Z'))
+ {
+ pdbId = pdbId.substring(0, PDB_ID_LENGTH);
+ chainCode = String.valueOf(chain);
+ }
+ }
+ init(pdbId, chainCode, null, null);
+ }
+
public void setFile(String file)
{
this.file = file;
return file;
}
- public void setType(String type)
+ public void setType(String t)
{
- this.type = type;
+ this.type = t;
+ }
+
+ public void setType(PDBEntry.Type type)
+ {
+ this.type = type == null ? null : type.toString();
}
public String getType()
return properties;
}
+ /**
+ *
+ * @return null or a string for associated chain IDs
+ */
+ public String getChainCode()
+ {
+ return (properties == null || properties.get(CHAIN_ID) == null) ? null
+ : properties.get(CHAIN_ID).toString();
+ }
+
+ public void setChainCode(String chainCode)
+ {
+ if (properties == null)
+ {
+ if (chainCode == null)
+ {
+ // nothing to do.
+ return;
+ }
+ properties = new Hashtable();
+ }
+ if (chainCode == null)
+ {
+ properties.remove(CHAIN_ID);
+ return;
+ }
+ // update property for non-null chainCode
+ properties.put(CHAIN_ID, new CaseInsensitiveString(chainCode));
+ }
+
+ @Override
+ public String toString()
+ {
+ return id;
+ }
+
+ /**
+ * Answers true if this object is either equivalent to, or can be 'improved'
+ * by, the given entry.
+ * <p>
+ * If newEntry has the same id (ignoring case), and doesn't have a conflicting
+ * file spec or chain code, then update this entry from its file and/or chain
+ * code.
+ *
+ * @param newEntry
+ * @return true if modifications were made
+ */
+ protected boolean updateFrom(PDBEntry newEntry)
+ {
+ if (this.equals(newEntry))
+ {
+ return true;
+ }
+
+ String newId = newEntry.getId();
+ if (newId == null || getId() == null)
+ {
+ return false; // shouldn't happen
+ }
+
+ /*
+ * id (less any chain code) has to match (ignoring case)
+ */
+ if (!getId().equalsIgnoreCase(newId))
+ {
+ return false;
+ }
+
+ /*
+ * Don't update if associated with different structure files
+ */
+ String newFile = newEntry.getFile();
+ if (newFile != null && getFile() != null && !newFile.equals(getFile()))
+ {
+ return false;
+ }
+
+ /*
+ * Don't update if associated with different chains (ignoring case)
+ */
+ String newChain = newEntry.getChainCode();
+ if (newChain != null && newChain.length() > 0 && getChainCode() != null
+ && getChainCode().length() > 0
+ && !getChainCode().equalsIgnoreCase(newChain))
+ {
+ return false;
+ }
+
+ /*
+ * set file path if not already set
+ */
+ String newType = newEntry.getType();
+ if (getFile() == null && newFile != null)
+ {
+ setFile(newFile);
+ setType(newType);
+ }
+
+ /*
+ * set file type if new entry has it and we don't
+ * (for the case where file was not updated)
+ */
+ if (getType() == null && newType != null)
+ {
+ setType(newType);
+ }
+
+ /*
+ * set chain if not already set (we excluded differing
+ * chains earlier) (ignoring case change only)
+ */
+ if (newChain != null && newChain.length() > 0
+ && !newChain.equalsIgnoreCase(getChainCode()))
+ {
+ setChainCode(newChain);
+ }
+
+ /*
+ * copy any new properties; notice this may include chain_code,
+ * but we excluded differing chain codes earlier
+ */
+ if (newEntry.getProperty() != null)
+ {
+ if (properties == null)
+ {
+ properties = new Hashtable();
+ }
+ for (Object p : newEntry.getProperty().keySet())
+ {
+ /*
+ * copy properties unless value matches; this defends against changing
+ * the case of chain_code which is wrapped in a CaseInsensitiveString
+ */
+ Object value = newEntry.getProperty().get(p);
+ if (!value.equals(properties.get(p)))
+ {
+ properties.put(p, newEntry.getProperty().get(p));
+ }
+ }
+ }
+ return true;
+ }
}