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}