2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.datamodel;
23 import java.util.Collections;
24 import java.util.Enumeration;
25 import java.util.Hashtable;
27 import jalview.util.CaseInsensitiveString;
33 * constant for storing chain code in properties table
35 private static final String CHAIN_ID = "chain_code";
37 private Hashtable<String, Object> properties;
39 private static final int PDB_ID_LENGTH = 4;
42 * property set when id is a 'manufactured' identifier from the structure
45 private static final String FAKED_ID = "faked_pdbid";
48 * property set when the id is authoritative, and should be used in preference
49 * to any identifiers in the structure data
51 private static final String AUTHORITATIVE_ID = "authoritative_pdbid";
61 // TODO is FILE needed; if not is this enum needed, or can we
62 // use FileFormatI for PDB, MMCIF?
63 PDB("pdb", "pdb"), MMCIF("mmcif", "cif"), BCIF("bcif", "bcif"),
67 * file extension for cached structure file; must be one that
68 * is recognised by Chimera 'open' command
69 * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/filetypes.html
74 * format specifier used in dbfetch request
75 * @see http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/dbfetch.databases#pdb
79 private Type(String fmt, String ex)
85 public String getFormat()
90 public String getExtension()
96 * case insensitive matching for Type enum
101 public static Type getType(String value)
103 for (Type t : Type.values())
105 if (t.toString().equalsIgnoreCase(value))
114 * case insensitive equivalence for strings resolving to PDBEntry type
119 public boolean matches(String t)
121 return (this.toString().equalsIgnoreCase(t));
126 * Answers true if obj is a PDBEntry with the same id and chain code (both
127 * ignoring case), file, type and properties
130 public boolean equals(Object obj)
132 if (obj == null || !(obj instanceof PDBEntry))
140 PDBEntry o = (PDBEntry) obj;
143 * note that chain code is stored as a property wrapped by a
144 * CaseInsensitiveString, so we are in effect doing a
145 * case-insensitive comparison of chain codes
147 boolean idMatches = id == o.id
148 || (id != null && id.equalsIgnoreCase(o.id));
149 boolean fileMatches = file == o.file
150 || (file != null && file.equals(o.file));
151 boolean typeMatches = type == o.type
152 || (type != null && type.equals(o.type));
153 if (idMatches && fileMatches && typeMatches)
155 return properties == o.properties
156 || (properties != null && properties.equals(o.properties));
162 * Default constructor
169 * Entry point when file is not known and fileType may be string
171 * @param chain may be null
172 * @param fileType "pdb", "mmcif", or "bcif"; null defaults to mmcif
174 public PDBEntry(String pdbId, String chain, String fileType) {
175 this.id = pdbId.toLowerCase();
176 setChainCode(chain); // I note that PDB Chains ARE case-sensitive now
177 if (fileType == null)
179 switch (fileType.toLowerCase()) {
181 this.type = Type.PDB.toString();
184 this.type = Type.MMCIF.toString();
188 System.out.println("format " + fileType + " has not been implemented; using mmCIF");
189 this.type = Type.MMCIF.toString();
194 public PDBEntry(String pdbId, String chain, PDBEntry.Type type,
197 init(pdbId, chain, type, filePath);
206 void init(String pdbId, String chain, PDBEntry.Type entryType,
210 this.type = entryType == null ? null : entryType.toString();
211 this.file = filePath;
220 public PDBEntry(PDBEntry entry)
225 if (entry.properties != null)
227 properties = (Hashtable<String, Object>) entry.properties.clone();
232 * Make a PDBEntry from a DBRefEntry. The accession code is used for the PDB
233 * id, but if it is 5 characters in length, the last character is removed and
234 * set as the chain code instead.
238 public PDBEntry(DBRefEntry dbr)
240 if (!DBRefSource.PDB.equals(dbr.getSource()))
242 throw new IllegalArgumentException(
243 "Invalid source: " + dbr.getSource());
246 String pdbId = dbr.getAccessionId();
247 String chainCode = null;
248 if (pdbId.length() == PDB_ID_LENGTH + 1)
250 char chain = pdbId.charAt(PDB_ID_LENGTH);
251 if (('a' <= chain && chain <= 'z') || ('A' <= chain && chain <= 'Z'))
253 pdbId = pdbId.substring(0, PDB_ID_LENGTH);
254 chainCode = String.valueOf(chain);
257 init(pdbId, chainCode, null, null);
260 public void setFile(String f)
265 public String getFile()
270 public void setType(String t)
275 public void setType(PDBEntry.Type type)
277 this.type = type == null ? null : type.toString();
280 public String getType()
285 public void setId(String id)
290 public String getId()
298 * @param key "protocol"
301 public void setProperty(String key, Object value)
303 if (this.properties == null)
305 this.properties = new Hashtable<String, Object>();
307 properties.put(key, value);
310 public Object getProperty(String key)
312 return properties == null ? null : properties.get(key);
316 * Returns an enumeration of the keys of this object's properties (or an empty
317 * enumeration if it has no properties)
321 public Enumeration<String> getProperties()
323 if (properties == null)
325 return Collections.emptyEnumeration();
327 return properties.keys();
332 * @return null or a string for associated chain IDs
334 public String getChainCode()
336 return (properties == null || properties.get(CHAIN_ID) == null) ? null
337 : properties.get(CHAIN_ID).toString();
341 * Sets a non-case-sensitive property for the given chain code. Two PDBEntry
342 * objects which differ only in the case of their chain code are considered
343 * equal. This avoids duplication of objects in lists of PDB ids.
347 public void setChainCode(String chainCode)
349 if (chainCode == null)
351 deleteProperty(CHAIN_ID);
355 setProperty(CHAIN_ID, new CaseInsensitiveString(chainCode));
360 * Deletes the property with the given key, and returns the deleted value (or
363 Object deleteProperty(String key)
365 Object result = null;
366 if (properties != null)
368 result = properties.remove(key);
374 public String toString()
380 * Getter provided for Castor binding only. Application code should call
381 * getProperty() or getProperties() instead.
384 * @see #getProperty(String)
385 * @see #getProperties()
386 * @see jalview.ws.dbsources.Uniprot#getUniprotEntries
390 public Hashtable<String, Object> getProps()
396 * Setter provided for Castor binding only. Application code should call
397 * setProperty() instead.
403 public void setProps(Hashtable<String, Object> props)
409 * Answers true if this object is either equivalent to, or can be 'improved'
410 * by, the given entry.
412 * If newEntry has the same id (ignoring case), and doesn't have a conflicting
413 * file spec or chain code, then update this entry from its file and/or chain
417 * @return true if modifications were made
419 public boolean updateFrom(PDBEntry newEntry)
421 if (this.equals(newEntry))
426 String newId = newEntry.getId();
427 if (newId == null || getId() == null)
429 return false; // shouldn't happen
432 boolean idMatches = getId().equalsIgnoreCase(newId);
435 * Don't update if associated with different structure files
437 String newFile = newEntry.getFile();
438 if (newFile != null && getFile() != null)
440 if (!newFile.equals(getFile()))
449 // this shouldn't happen, but could do if the id from the
450 // file is not the same as the id from the authority that provided
452 if (!newEntry.fakedPDBId() && !isAuthoritative())
455 } // otherwise we can update
461 // one has data, one doesn't ..
465 } // otherwise maybe can update
469 * Don't update if associated with different chains (ignoring case)
471 String newChain = newEntry.getChainCode();
472 if (newChain != null && newChain.length() > 0 && getChainCode() != null
473 && getChainCode().length() > 0
474 && !getChainCode().equalsIgnoreCase(newChain))
480 * set file path if not already set
482 String newType = newEntry.getType();
483 if (getFile() == null && newFile != null)
490 * set file type if new entry has it and we don't
491 * (for the case where file was not updated)
493 if (getType() == null && newType != null)
499 * set chain if not already set (we excluded differing
500 * chains earlier) (ignoring case change only)
502 if (newChain != null && newChain.length() > 0
503 && !newChain.equalsIgnoreCase(getChainCode()))
505 setChainCode(newChain);
509 * copy any new or modified properties
511 Enumeration<String> newProps = newEntry.getProperties();
512 while (newProps.hasMoreElements())
515 * copy properties unless value matches; this defends against changing
516 * the case of chain_code which is wrapped in a CaseInsensitiveString
518 String key = newProps.nextElement();
519 Object value = newEntry.getProperty(key);
520 if (FAKED_ID.equals(key) || AUTHORITATIVE_ID.equals(key))
522 // we never update the fake ID property
525 if (!value.equals(getProperty(key)))
527 setProperty(key, value);
533 public void setAuthoritative(boolean isAuthoritative)
535 setProperty(AUTHORITATIVE_ID, Boolean.valueOf(isAuthoritative));
540 * @return true if the identifier should be preferred over any identifiers
541 * embedded in the structure data
543 public boolean isAuthoritative()
545 if (_hasProperty(AUTHORITATIVE_ID))
547 return ((Boolean) getProperty(AUTHORITATIVE_ID));
553 * set when Jalview has manufactured the ID using a local filename
557 public boolean fakedPDBId()
559 if (_hasProperty(FAKED_ID))
566 public void setFakedPDBId(boolean faked)
570 setProperty(FAKED_ID, Boolean.TRUE);
574 if (properties != null)
576 properties.remove(FAKED_ID);
581 private boolean _hasProperty(final String key)
583 return (properties != null && properties.containsKey(key));
586 private static final String RETRIEVE_FROM = "RETRIEVE_FROM";
588 private static final String PROVIDER = "PROVIDER";
590 private static final String MODELPAGE = "PROVIDERPAGE";
593 * Permanent URI for retrieving the original structure data
597 public void setRetrievalUrl(String urlStr)
599 setProperty(RETRIEVE_FROM, urlStr);
602 public boolean hasRetrievalUrl()
604 return _hasProperty(RETRIEVE_FROM);
608 * get the Permanent URI for retrieving the original structure data
610 public String getRetrievalUrl()
612 return (String) getProperty(RETRIEVE_FROM);
616 * Data provider name - from 3D Beacons
620 public void setProvider(String provider)
622 setProperty(PROVIDER, provider);
626 * Get Data provider name - from 3D Beacons
629 public String getProvider()
631 return (String) getProperty(PROVIDER);
635 * Permanent URI for retrieving the original structure data
639 public void setProviderPage(String urlStr)
641 setProperty(MODELPAGE, urlStr);
645 * get the Permanent URI for retrieving the original structure data
647 public String getProviderPage()
649 return (String) getProperty(MODELPAGE);
652 public boolean hasProviderPage()
654 return _hasProperty(MODELPAGE);
657 public boolean hasProvider()
659 return _hasProperty(PROVIDER);