001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure; 022 023import java.io.Serializable; 024import java.util.regex.Pattern; 025 026/** 027 * A wrapper class for the PDB identifier. 028 * 029 * It handles conversion between current (short) <code>[1-9][0-9A-Z]{3}</code> and 030 * upcoming (extended) <code>PDB_\d{4}[1-9][09-A-Z]</code> PDB ID format.<br> 031 * Instances of this class are <em>immutable</em>.<br> 032 * Creation of PdBId instance follows strict PDB ID convention. 033 * There is only one exception to this rule which is <b>XXXX</b>. XXXX objects 034 * are not considered equal (unless they are the one and the same object). 035 * @author Amr ALHOSSARY 036 * @since 6.0.0 037 * 038 */ 039public class PdbId implements Comparable<PdbId>, Serializable{ 040 041 private static final long serialVersionUID = -7740865530486255113L; 042 private static final String PREFIX_PDB_ = "PDB_"; 043 private static final String STRING_0000 = "0000"; 044 private static final String PDB_0000 = PREFIX_PDB_ + STRING_0000; 045 046 /** 047 * Controls how the PDB ID output/conversion should go, if possible. 048 * The default is to try to produce short PDB ID. If failed, produce extended PDB ID. 049 */ 050 private static final boolean defaultShorteningBehaviour = true; 051 052 053 /** 054 * A regular expression that matches a PDB ID in the short format. 055 */ 056 public static final Pattern PATTERN_SHORT_PDBID = Pattern.compile("[1-9]\\p{Alnum}{3}"); 057 /** 058 * A regular expression that matches a PDB ID in the extended format. 059 */ 060 public static final Pattern PATTERN_EXTENDED_PDBID = Pattern.compile("(pdb|PDB)_\\p{Alnum}{8}"); 061 /** 062/ * A regular expression that matches an extended PDB ID that is compatible with the short format. 063 */ 064 public static final Pattern PATTERN_SHORTABLE_EXTENDED_PDBID = Pattern.compile("(pdb|PDB)_0000[1-9]\\p{Alnum}{3}"); 065 066 /** 067 * Keeps the ID in <b>UPPER CASE</b>, in a <em>reduced</em> form (without the <code>PDB_</code> prefix). 068 */ 069 private String idCode; 070 071 /** 072 * @param id A <i>valid</i> PDB ID in either <i>short (case insensitive)</i> or <i>extended</i> format. 073 * @throws IllegalArgumentException If <code>id</code> is not a valid identifier. 074 * @throws NullPointerException If <code>id</code> is <code>null</code>. 075 */ 076 public PdbId(String id){ 077 if (id == null) { 078 throw new IllegalArgumentException("ID can not be null"); 079 } 080 this.idCode = toInternalFormat(id); 081 } 082 083 /** 084 * Check whether <code>id</code> represents a valid PDB ID in the <em>short</em> format. 085 * @param id Prospect ID 086 * @return <code>true</code> if <code>id</code> is a valid short PDB ID, <code>false</code> otherwise. 087 * @throws NullPointerException if <code>id</code> is <code>null</code>. 088 * @see #isValidExtendedPdbId(String) 089 */ 090 public static boolean isValidShortPdbId(String id) { 091 return PATTERN_SHORT_PDBID.matcher(id).matches(); 092 } 093 094 /** 095 * Check whether <code>id</code> represents a valid PDB ID in the <em>extended</em> format. 096 * @param id Prospect ID 097 * @return <code>true</code> if <code>id</code> is a valid extended PDB ID, <code>false</code> otherwise. 098 * @throws NullPointerException if <code>id</code> is <code>null</code>. 099 * @see #isValidShortPdbId(String) 100 */ 101 public static boolean isValidExtendedPdbId(String id) { 102 return PATTERN_EXTENDED_PDBID.matcher(id).matches(); 103 } 104 105 /** 106 * Checks whether an Extended PDB ID is shortable, <i>assuming it is a valid extended PDB ID</i>. 107 * @see #isValidExtendedPdbId(String) 108 * @param extendedId the supposedly valid extended PDB ID. 109 * @return <code>true</code> if <code>extendedId</code> can be shortened 110 * (ie. it matches the regular expression "(pdb|PDB)_0000[1-9][a-zA-Z0-9]{3}"), <code>false</code> otherwise. 111 */ 112 public static boolean isShortCompatible(String extendedId) { 113 return PATTERN_SHORTABLE_EXTENDED_PDBID.matcher(extendedId).matches(); 114 } 115 116 @Override 117 public int hashCode() { 118 return idCode.hashCode(); 119 } 120 121 @Override 122 public boolean equals(Object obj) { 123 if (this == obj) 124 return true; 125 if (obj == null) 126 return false; 127 if (getClass() != obj.getClass()) 128 return false; 129 // We are sure they are both objects of the same class and their respective IDs are in the same (UPPER) case. 130 return this.idCode.equals(((PdbId)obj).idCode); 131 } 132 133 @Override 134 protected Object clone() throws CloneNotSupportedException { 135 return new PdbId(this.getId()); 136 } 137 138 @Override 139 public String toString() { 140 return getId(); 141 } 142 143 /** 144 * Get a <code>String</code> representation of this PdbId instance.<br> 145 * By default this function will try to get the PdbId in the short (4 letters) format. 146 * If not possible, it will return the long format. 147 * N.B. This default behavior may change later; 148 * @return the PdbId code, preferably in short format. 149 */ 150 public String getId() { 151 return getId(defaultShorteningBehaviour); 152 } 153 154 /** 155 * Get a <code>String</code> representation of this PdbId instance, using the <i>passed in</i> behavior.<br> 156 * @param prefereShort when it is <code>true</code>, the class will try to produce the short ID whenever possible. 157 * @return The PdbId in short format if possible and <code>prefereShort</code> is <code>true</code>, the extended PDB ID form otherwise. 158 */ 159 public String getId(boolean prefereShort) { 160 if (prefereShort && isInternalShortCompatible(idCode)) 161 return internalToShortNoCheck(idCode); 162 return PREFIX_PDB_ + idCode; 163 } 164 165 /** 166 * Get the PDB Id in the short format. Throws an exception if the conversion is not possible.<br> 167 * Use this method only if you know that this PDB ID is shortable. 168 * @return the PDB ID in the short format. 169 * @throws StructureException if the conversion was not possible. 170 */ 171 public String getShortId() throws StructureException{ 172 if(isInternalShortCompatible(idCode)) { 173 return internalToShortNoCheck(idCode); 174 } else { 175 throw new StructureException("ID (" + getId() + ") is not short format compatible"); 176 } 177 } 178 179 /** 180 * Converts <code>shortId</code> to the PDB ID extended format. 181 * If <code>shortId</code> is a valid short PDB ID, it would be converted to an extended ID, 182 * if <code>shortId</code> is a valid extended PDB ID, it would be returned in UPPER CASE, 183 * a {@link StructureException} is thrown otherwise. 184 * @param shortId the PDB ID to convert to extended format 185 * @return the ID in the extended UPPER CASE format. 186 * @throws StructureException if the conversion was not possible. 187 */ 188 public static String toExtendedId(String shortId) throws StructureException{ 189 if (isValidShortPdbId(shortId)) { 190 return PDB_0000 + shortId.toUpperCase(); 191 }else if (isValidExtendedPdbId(shortId)) { 192 return shortId.toUpperCase(); 193 } else { 194 throw new StructureException("Unknown format ["+shortId+"]"); 195 } 196 } 197 198 /** 199 * Converts <code>extendedId</code> to the PDB ID short format. 200 * If <code>extendedId</code> is a valid extended PDB ID, it would be converted to a short ID, 201 * if <code>extendedId</code> is a valid short PDB ID, it would be returned in UPPER CASE, 202 * a {@link StructureException} is thrown otherwise. 203 * @param extendedId the PDB ID to convert to short format 204 * @return the ID in the short UPPER CASE format. 205 * @throws StructureException if the conversion was not possible. 206 */ 207 public static String toShortId(String extendedId) throws StructureException{ 208 if (isShortCompatible(extendedId)) { 209 return extendedId.substring(8).toUpperCase(); 210 } else if (isValidShortPdbId(extendedId)) { 211 return extendedId.toUpperCase(); 212 } else { 213 throw new StructureException("Conversion not possible of ID ["+extendedId+"]"); 214 } 215 } 216 217 private static boolean isInternalShortCompatible(String intId) { 218 return intId.substring(0, 4).equals(STRING_0000); 219 } 220 221 private static String toInternalFormat(String id) throws IllegalArgumentException { 222 if (isValidShortPdbId(id)) { 223 return STRING_0000 + id.toUpperCase(); 224 }else if (isValidExtendedPdbId(id)) { 225 return id.substring(4).toUpperCase(); 226 } else { 227 throw new IllegalArgumentException("Unknown format [" + id + "]"); 228 } 229 } 230 231 private static String internalToShortNoCheck(String extendedId) { 232 return extendedId.substring(4).toUpperCase(); 233 } 234 235 @Override 236 public int compareTo(PdbId o) { 237 //We know that both idCode fields are 8 UPPER CASE characters strings. 238 return this.idCode.compareTo(o.idCode); 239 } 240 241}