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}