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