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 * Created on Jun 5, 2010 021 * Author: Jianjiong Gao 022 * 023 */ 024 025package org.biojava.nbio.protmod.structure; 026 027import org.biojava.nbio.protmod.ModificationCategory; 028import org.biojava.nbio.protmod.ProteinModification; 029import org.biojava.nbio.protmod.ProteinModificationImpl; 030import org.biojava.nbio.protmod.io.ModifiedCompoundXMLConverter; 031 032import java.io.Serializable; 033import java.util.*; 034 035 036/** 037 * 038 * @author Jianjiong Gao 039 * @since 3.0 040 */ 041public class ModifiedCompoundImpl 042implements ModifiedCompound, Serializable, Comparable<ModifiedCompound> { 043 044 /** 045 * 046 */ 047 private static final long serialVersionUID = 1656563037849815427L; 048 049 ProteinModification originalModification; 050 051 ProteinModification modification; 052 053 Set<StructureGroup> groups; 054 055 Map<Set<StructureGroup>, Set<StructureAtomLinkage>> atomLinkages; 056 057 public static final String newline = System.getProperty("line.separator"); 058 059 public ModifiedCompoundImpl(){ 060 061 } 062 063 /** 064 * Create a ModifiedCompoundImpl that has only one involved component. 065 * Use this constructor for a modified residue. 066 * @param modification {@link ProteinModification}. 067 * @param modifiedResidue modified group. 068 * @return a {@link ModifiedCompound}. 069 * @throws IllegalArgumentException if either argument is null. 070 */ 071 public ModifiedCompoundImpl ( 072 ProteinModification modification, 073 StructureGroup modifiedResidue) { 074 if (modification==null || modifiedResidue==null) { 075 throw new IllegalArgumentException("Null argument(s)"); 076 } 077 078 groups = new HashSet<StructureGroup>(1); 079 groups.add(modifiedResidue); 080 081 // is it possible that components be added by addLinkage later? 082 atomLinkages = null; 083 084 setModification(modification); 085 } 086 087 /** 088 * 089 * @param modification ProteinModification. 090 * @param linkages a collection of atom linkages. 091 * @see ProteinModification 092 * @see StructureAtomLinkage 093 */ 094 public ModifiedCompoundImpl( ProteinModification modification, 095 Collection<StructureAtomLinkage> linkages) { 096 if (modification==null) { 097 throw new IllegalArgumentException("modification cannot be null"); 098 } 099 100 if (linkages==null||linkages.isEmpty()) { 101 throw new IllegalArgumentException("at least one linkage."); 102 } 103 104 this.groups = new HashSet<StructureGroup>(); 105 106 addAtomLinkages(linkages); 107 108 setModification(modification); 109 110 } 111 112 @Override 113 public void setModification(ProteinModification protmod){ 114 originalModification = protmod; 115 116 resetModification(); 117 } 118 119 @Override 120 public ProteinModification getModification() { 121 return modification; 122 } 123 124 private void resetModification() { 125 if (originalModification == null) 126 modification = originalModification; 127 else if (originalModification.getCategory()!=ModificationCategory.UNDEFINED) 128 modification = originalModification; 129 else { 130 int nRes = 0; 131 Set<String> ligands = new HashSet<String>(); 132 for (StructureGroup group : groups) { 133 if (group.isAminoAcid()) { 134 nRes ++; 135 } else { 136 ligands.add(group.getPDBName().trim()); 137 } 138 } 139 140 ModificationCategory cat; 141 switch (nRes) { 142 case 0: 143 modification = originalModification; return; 144 case 1: 145 cat = ModificationCategory.ATTACHMENT; break; 146 case 2: 147 cat = ModificationCategory.CROSS_LINK_2; break; 148 case 3: 149 cat = ModificationCategory.CROSS_LINK_3; break; 150 case 4: 151 cat = ModificationCategory.CROSS_LINK_4; break; 152 case 5: 153 cat = ModificationCategory.CROSS_LINK_5; break; 154 case 6: 155 cat = ModificationCategory.CROSS_LINK_6; break; 156 case 7: 157 cat = ModificationCategory.CROSS_LINK_7; break; 158 default: 159 cat = ModificationCategory.CROSS_LINK_8_OR_LARGE; break; 160 } 161 162 modification = new ProteinModificationImpl.Builder(originalModification) 163 .setCategory(cat).addKeywords(ligands).build(); 164 } 165 } 166 167 /** 168 * 169 * @return the original modification ID. 170 * @deprecated use getModification().getId() 171 */ 172 @Deprecated 173 public String getOriginalModificationId() { 174 if (originalModification==null) 175 return null; 176 177 return originalModification.getId(); 178 } 179 180 @Override 181 public Set<StructureGroup> getGroups() { 182 if ( groups == null) 183 return null; 184 185 return Collections.unmodifiableSet(groups); 186 } 187 188 @Override 189 public Set<StructureGroup> getGroups(boolean isAminoAcid) { 190 Set<StructureGroup> result = new HashSet<StructureGroup>(); 191 for (StructureGroup group : groups) { 192 if (group.isAminoAcid() == isAminoAcid) { 193 result.add(group); 194 } 195 } 196 return result; 197 } 198 199 @Override 200 public void setGroups(Set<StructureGroup> groups){ 201 this.groups = groups; 202 resetModification(); 203 } 204 205 @Override 206 public Set<StructureAtomLinkage> getAtomLinkages() { 207 if (atomLinkages==null) { 208 return Collections.emptySet(); 209 } else { 210 Set<StructureAtomLinkage> result = new HashSet<StructureAtomLinkage>(); 211 for (Set<StructureAtomLinkage> linkages : atomLinkages.values()) { 212 result.addAll(linkages); 213 } 214 215 return result; 216 } 217 } 218 219 220 @Override 221 public void setAtomLinkages(Set<StructureAtomLinkage> linkages) { 222 for (StructureAtomLinkage sali : linkages){ 223 addAtomLinkage(sali); 224 } 225 resetModification(); 226 } 227 228 @Override 229 public boolean addAtomLinkage(StructureAtomLinkage linkage) { 230 if (linkage==null) { 231 throw new IllegalArgumentException("Null linkage"); 232 } 233 234 Set<StructureGroup> gs = new HashSet<StructureGroup>(2); 235 gs.add(linkage.getAtom1().getGroup()); 236 gs.add(linkage.getAtom2().getGroup()); 237 238 if (atomLinkages==null) { 239 atomLinkages = new HashMap<Set<StructureGroup>, Set<StructureAtomLinkage>>(); 240 } 241 242 Set<StructureAtomLinkage> linkages = atomLinkages.get(gs); 243 if (linkages == null) { 244 linkages = new HashSet<StructureAtomLinkage>(); 245 atomLinkages.put(gs, linkages); 246 groups.addAll(gs); // it's possible of new groups 247 }; 248 249 return linkages.add(linkage); 250 } 251 252 @Override 253 public void addAtomLinkages(Collection<StructureAtomLinkage> linkages) { 254 if (linkages==null) { 255 throw new IllegalArgumentException("Null linkages"); 256 } 257 258 for (StructureAtomLinkage link : linkages) { 259 addAtomLinkage(link); 260 } 261 262 resetModification(); 263 } 264 265 @Override 266 public boolean crossChains() { 267 if (groups==null || groups.isEmpty()) 268 return false; 269 270 Iterator<StructureGroup> it = groups.iterator(); 271 String chain = it.next().getChainId(); 272 while (it.hasNext()) { 273 if (!it.next().getChainId().equals(chain)) 274 return true; 275 } 276 277 return false; 278 } 279 280 281 @Override 282 public String toString(){ 283 StringBuilder sb = new StringBuilder(); 284 if ( originalModification == null) 285 return "ModifiedCompoundImpl -- not initialized"; 286 287 //sb.append("Modification_"); 288 sb.append(originalModification.getId()); 289 ModificationCategory cat ; 290 if (originalModification.getCategory()==ModificationCategory.UNDEFINED) { 291 cat = getModification().getCategory(); 292 } else 293 cat = originalModification.getCategory(); 294 sb.append("_"); 295 sb.append(cat.toString()); 296 return sb.toString(); 297 } 298 299 300 @Override 301 public String getDescription() { 302 303 StringBuilder sb = new StringBuilder(); 304 //sb.append("Category: "); 305 306 if ( getModification() == null) { 307 sb.append(" !!! not initialized !!!"); 308 return sb.toString(); 309 } 310 311 sb.append(originalModification.toString()); 312// sb.append(getModification().getCategory()); 313// if (!modification.getKeywords().isEmpty()) { 314// sb.append("; "); 315// sb.append(modification.getKeywords()); 316// } 317// sb.append("; Modification ID: "); 318// sb.append(modification.getId()); 319// 320// if (modification.getResidId()!=null) { 321// sb.append("; RESID: "); 322// sb.append(modification.getResidId()); 323// sb.append(" ["); 324// sb.append(modification.getResidName()); 325// sb.append(']'); 326// } 327// 328// sb.append(" | "); 329// 330// if (atomLinkages==null) { 331// for (StructureGroup group : groups) { 332// sb.append(group); 333// sb.append(" | "); 334// } 335// } else { 336// for (Set<StructureAtomLinkage> linkages : atomLinkages.values()) { 337// for (StructureAtomLinkage linkage : linkages) { 338// sb.append(linkage); 339// sb.append(" | "); 340// } 341// } 342// } 343 344 return sb.toString(); 345 } 346 347 @Override 348 public void setDescription(String desc){ 349 // do nothing.... 350 351 } 352 353 @Override 354 public int compareTo(ModifiedCompound compound){ 355 try { 356 // quite complex objects so the easiest is to just wrap it as XML 357 // and compare the two strings... 358 String xml = ModifiedCompoundXMLConverter.toXML(this); 359 String xml2 = ModifiedCompoundXMLConverter.toXML(compound); 360 return xml.compareTo(xml2); 361 } catch (Exception e){ 362 363 } 364 return this.toString().compareTo(compound.toString()); 365 } 366 367 /** 368 * @return true if same modification and same components; false, otherwise. 369 */ 370 @Override 371 public boolean equals(Object obj) { 372 if (!(obj instanceof ModifiedCompound)) { 373 return false; 374 } 375 376 ModifiedCompound mci = (ModifiedCompound)obj; 377 if (mci.getModification() != originalModification) { 378 return false; 379 } 380 381 if (!groups.equals(mci.getGroups())) { 382 return false; 383 } 384 385 return true; 386 387 // Do not need to consider linkage, since they can be determined by 388 // modification and groups. 389 } 390 391 @Override 392 public int hashCode() { 393 int result = 17; 394 result = result * 32 + originalModification.hashCode(); 395 396 int sum = 0; 397 for (StructureGroup group : groups) { 398 sum += group.hashCode(); 399 } 400 401 result = result * 32 + sum; 402 403 return result; 404 } 405}