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 @Override 168 public Set<StructureGroup> getGroups() { 169 if ( groups == null) 170 return null; 171 172 return Collections.unmodifiableSet(groups); 173 } 174 175 @Override 176 public Set<StructureGroup> getGroups(boolean isAminoAcid) { 177 Set<StructureGroup> result = new HashSet<StructureGroup>(); 178 for (StructureGroup group : groups) { 179 if (group.isAminoAcid() == isAminoAcid) { 180 result.add(group); 181 } 182 } 183 return result; 184 } 185 186 @Override 187 public void setGroups(Set<StructureGroup> groups){ 188 this.groups = groups; 189 resetModification(); 190 } 191 192 @Override 193 public Set<StructureAtomLinkage> getAtomLinkages() { 194 if (atomLinkages==null) { 195 return Collections.emptySet(); 196 } else { 197 Set<StructureAtomLinkage> result = new HashSet<StructureAtomLinkage>(); 198 for (Set<StructureAtomLinkage> linkages : atomLinkages.values()) { 199 result.addAll(linkages); 200 } 201 202 return result; 203 } 204 } 205 206 207 @Override 208 public void setAtomLinkages(Set<StructureAtomLinkage> linkages) { 209 for (StructureAtomLinkage sali : linkages){ 210 addAtomLinkage(sali); 211 } 212 resetModification(); 213 } 214 215 @Override 216 public boolean addAtomLinkage(StructureAtomLinkage linkage) { 217 if (linkage==null) { 218 throw new IllegalArgumentException("Null linkage"); 219 } 220 221 Set<StructureGroup> gs = new HashSet<StructureGroup>(2); 222 gs.add(linkage.getAtom1().getGroup()); 223 gs.add(linkage.getAtom2().getGroup()); 224 225 if (atomLinkages==null) { 226 atomLinkages = new HashMap<Set<StructureGroup>, Set<StructureAtomLinkage>>(); 227 } 228 229 Set<StructureAtomLinkage> linkages = atomLinkages.get(gs); 230 if (linkages == null) { 231 linkages = new HashSet<StructureAtomLinkage>(); 232 atomLinkages.put(gs, linkages); 233 groups.addAll(gs); // it's possible of new groups 234 }; 235 236 return linkages.add(linkage); 237 } 238 239 @Override 240 public void addAtomLinkages(Collection<StructureAtomLinkage> linkages) { 241 if (linkages==null) { 242 throw new IllegalArgumentException("Null linkages"); 243 } 244 245 for (StructureAtomLinkage link : linkages) { 246 addAtomLinkage(link); 247 } 248 249 resetModification(); 250 } 251 252 @Override 253 public boolean crossChains() { 254 if (groups==null || groups.isEmpty()) 255 return false; 256 257 Iterator<StructureGroup> it = groups.iterator(); 258 String chain = it.next().getChainId(); 259 while (it.hasNext()) { 260 if (!it.next().getChainId().equals(chain)) 261 return true; 262 } 263 264 return false; 265 } 266 267 268 @Override 269 public String toString(){ 270 StringBuilder sb = new StringBuilder(); 271 if ( originalModification == null) 272 return "ModifiedCompoundImpl -- not initialized"; 273 274 //sb.append("Modification_"); 275 sb.append(originalModification.getId()); 276 ModificationCategory cat ; 277 if (originalModification.getCategory()==ModificationCategory.UNDEFINED) { 278 cat = getModification().getCategory(); 279 } else 280 cat = originalModification.getCategory(); 281 sb.append("_"); 282 sb.append(cat.toString()); 283 return sb.toString(); 284 } 285 286 287 @Override 288 public String getDescription() { 289 290 StringBuilder sb = new StringBuilder(); 291 //sb.append("Category: "); 292 293 if ( getModification() == null) { 294 sb.append(" !!! not initialized !!!"); 295 return sb.toString(); 296 } 297 298 sb.append(originalModification.toString()); 299// sb.append(getModification().getCategory()); 300// if (!modification.getKeywords().isEmpty()) { 301// sb.append("; "); 302// sb.append(modification.getKeywords()); 303// } 304// sb.append("; Modification ID: "); 305// sb.append(modification.getId()); 306// 307// if (modification.getResidId()!=null) { 308// sb.append("; RESID: "); 309// sb.append(modification.getResidId()); 310// sb.append(" ["); 311// sb.append(modification.getResidName()); 312// sb.append(']'); 313// } 314// 315// sb.append(" | "); 316// 317// if (atomLinkages==null) { 318// for (StructureGroup group : groups) { 319// sb.append(group); 320// sb.append(" | "); 321// } 322// } else { 323// for (Set<StructureAtomLinkage> linkages : atomLinkages.values()) { 324// for (StructureAtomLinkage linkage : linkages) { 325// sb.append(linkage); 326// sb.append(" | "); 327// } 328// } 329// } 330 331 return sb.toString(); 332 } 333 334 @Override 335 public void setDescription(String desc){ 336 // do nothing.... 337 338 } 339 340 @Override 341 public int compareTo(ModifiedCompound compound){ 342 try { 343 // quite complex objects so the easiest is to just wrap it as XML 344 // and compare the two strings... 345 String xml = ModifiedCompoundXMLConverter.toXML(this); 346 String xml2 = ModifiedCompoundXMLConverter.toXML(compound); 347 return xml.compareTo(xml2); 348 } catch (Exception e){ 349 350 } 351 return this.toString().compareTo(compound.toString()); 352 } 353 354 /** 355 * @return true if same modification and same components; false, otherwise. 356 */ 357 @Override 358 public boolean equals(Object obj) { 359 if (!(obj instanceof ModifiedCompound)) { 360 return false; 361 } 362 363 ModifiedCompound mci = (ModifiedCompound)obj; 364 if (mci.getModification() != originalModification) { 365 return false; 366 } 367 368 if (!groups.equals(mci.getGroups())) { 369 return false; 370 } 371 372 return true; 373 374 // Do not need to consider linkage, since they can be determined by 375 // modification and groups. 376 } 377 378 @Override 379 public int hashCode() { 380 int result = 17; 381 result = result * 32 + originalModification.hashCode(); 382 383 int sum = 0; 384 for (StructureGroup group : groups) { 385 sum += group.hashCode(); 386 } 387 388 result = result * 32 + sum; 389 390 return result; 391 } 392}