001package com.hfg.bio.seq;
002
003import com.hfg.bio.AminoAcid;
004import com.hfg.chem.Element;
005import com.hfg.bio.HfgBioXML;
006import com.hfg.chem.Molecule;
007import com.hfg.util.CompareUtil;
008import com.hfg.xml.XMLNode;
009import com.hfg.xml.XMLTag;
010
011import java.util.Map;
012
013//------------------------------------------------------------------------------
014/**
015 * Biological protein sequence crosslink.
016 *
017 * @author J. Alex Taylor, hairyfatguy.com
018 */
019//------------------------------------------------------------------------------
020// com.hfg XML/HTML Coding Library
021//
022// This library is free software; you can redistribute it and/or
023// modify it under the terms of the GNU Lesser General Public
024// License as published by the Free Software Foundation; either
025// version 2.1 of the License, or (at your option) any later version.
026//
027// This library is distributed in the hope that it will be useful,
028// but WITHOUT ANY WARRANTY; without even the implied warranty of
029// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
030// Lesser General Public License for more details.
031//
032// You should have received a copy of the GNU Lesser General Public
033// License along with this library; if not, write to the Free Software
034// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
035//
036// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
037// jataylor@hairyfatguy.com
038//------------------------------------------------------------------------------
039
040
041public class ProteinXLink extends Molecule implements Cloneable
042{
043
044   private ProteinXLinkType mType;
045   private Protein          mDonorChain;
046   private String           mDonorChainId;
047   private Integer          mDonorPosition;
048   private Protein          mAcceptorChain;
049   private String           mAcceptorChainId;
050   private Integer          mAcceptorPosition;
051
052   private char             mDonorResidue;
053   private char             mAcceptorResidue;
054
055   private Protein          mParentProtein;
056
057   //##########################################################################
058   // CONSTRUCTORS
059   //##########################################################################
060
061   //--------------------------------------------------------------------------
062   public ProteinXLink(ProteinXLinkType inType, Protein inDonorChain, int inDonorPosition, Protein inAcceptorChain, int inAcceptorPosition)
063   {
064      mType             = inType;
065      mDonorChain       = inDonorChain;
066      mDonorChainId     = inDonorChain.getID();
067      mDonorPosition    = inDonorPosition;
068      mAcceptorChain    = inAcceptorChain;
069      mAcceptorChainId  = inAcceptorChain.getID();
070      mAcceptorPosition = inAcceptorPosition;
071
072      mDonorResidue     = inDonorChain.residueAt(inDonorPosition);
073      mAcceptorResidue  = inAcceptorChain.residueAt(inAcceptorPosition);
074
075      validate();
076   }
077
078   //--------------------------------------------------------------------------
079   public ProteinXLink(XMLNode inXML)
080   {
081      if (! inXML.getTagName().equals(HfgBioXML.XLINK_TAG))
082      {
083         throw new RuntimeException("Cannot construct an " + this.getClass().getSimpleName() + " from a " + inXML.getTagName() + " tag!");
084      }
085
086      mType = new ProteinXLinkType(inXML.getRequiredSubtagByName(HfgBioXML.XLINK_TYPE_TAG));
087
088      XMLNode donorTag = inXML.getRequiredSubtagByName(HfgBioXML.DONOR_TAG);
089      mDonorChainId = donorTag.getAttributeValue(HfgBioXML.CHAIN_ID_ATT);
090      mDonorPosition = Integer.parseInt(donorTag.getAttributeValue(HfgBioXML.RESIDUE_ATT));
091
092      XMLNode acceptorTag = inXML.getRequiredSubtagByName(HfgBioXML.ACCEPTOR_TAG);
093      mAcceptorChainId = acceptorTag.getAttributeValue(HfgBioXML.CHAIN_ID_ATT);
094      mAcceptorPosition = Integer.parseInt(acceptorTag.getAttributeValue(HfgBioXML.RESIDUE_ATT));
095   }
096
097   //##########################################################################
098   // PUBLIC METHODS
099   //##########################################################################
100
101   //---------------------------------------------------------------------------
102   @Override
103   public ProteinXLink clone()
104   {
105      ProteinXLink copy = (ProteinXLink) super.clone();
106
107      return copy;
108   }
109
110   //--------------------------------------------------------------------------
111   public ProteinXLinkChainLinkage getChainLinkage()
112   {
113      return (0 == CompareUtil.compare(getDonorChainId(), getAcceptorChainId()) ? ProteinXLinkChainLinkage.INTRACHAIN : ProteinXLinkChainLinkage.INTERCHAIN);
114   }
115
116   //--------------------------------------------------------------------------
117   public ProteinXLinkType getType()
118   {
119      return mType;
120   }
121
122
123   //--------------------------------------------------------------------------
124   public String getDonorChainId()
125   {
126      return mDonorChainId;
127   }
128
129   //--------------------------------------------------------------------------
130   protected ProteinXLink setDonorChainId(String inValue)
131   {
132      mDonorChainId = inValue;
133      return this;
134   }
135
136
137   //--------------------------------------------------------------------------
138   public int getDonorPosition()
139   {
140      return mDonorPosition;
141   }
142
143
144   //--------------------------------------------------------------------------
145   public String getAcceptorChainId()
146   {
147      return mAcceptorChainId;
148   }
149
150   //--------------------------------------------------------------------------
151   protected ProteinXLink setAcceptorChainId(String inValue)
152   {
153      mAcceptorChainId = inValue;
154      return this;
155   }
156
157
158   //--------------------------------------------------------------------------
159   public int getAcceptorPosition()
160   {
161      return mAcceptorPosition;
162   }
163
164   //--------------------------------------------------------------------------
165   public String toString()
166   {
167      return mType.name() + ": " + (mDonorChainId != null ? mDonorChainId : "") + " " + mDonorResidue + mDonorPosition + "  -->  "
168                              + (mAcceptorChainId != null ? mAcceptorChainId : "") + " " + mAcceptorResidue + mAcceptorPosition;
169   }
170
171   //--------------------------------------------------------------------------
172   public XMLNode toXMLNode()
173   {
174      XMLNode node = new XMLTag(HfgBioXML.XLINK_TAG);
175
176      node.addSubtag(mType.toXMLNode());
177
178      XMLNode donorTag = new XMLTag(HfgBioXML.DONOR_TAG);
179      node.addSubtag(donorTag);
180      donorTag.setAttribute(HfgBioXML.CHAIN_ID_ATT, mDonorChainId);
181      donorTag.setAttribute(HfgBioXML.RESIDUE_ATT, mDonorPosition);
182
183      XMLNode acceptorTag = new XMLTag(HfgBioXML.ACCEPTOR_TAG);
184      node.addSubtag(acceptorTag);
185      acceptorTag.setAttribute(HfgBioXML.CHAIN_ID_ATT, mAcceptorChainId);
186      acceptorTag.setAttribute(HfgBioXML.RESIDUE_ATT, mAcceptorPosition);
187
188
189      return node;
190   }
191
192   //--------------------------------------------------------------------------
193   /**
194    Returns an unmodifiable copy of the elemental composition Map. The keys are
195    Element objects and the values are Floats. Why Floats instead of Integers you
196    ask? Because some amino acid codes such as B and Z are ambiguous averages.
197    @return an elemental composition map
198    */
199   public Map<Element, Float> getElementalComposition()
200   {
201      return mType.getElementalComposition();
202   }
203
204   //--------------------------------------------------------------------------
205   public Double getMonoisotopicMass()
206   {
207      return mType.getMonoisotopicMass();
208   }
209
210   //--------------------------------------------------------------------------
211   public Double getAverageMass()
212   {
213      return mType.getAverageMass();
214   }
215
216   //--------------------------------------------------------------------------
217   public Double getOrganicAverageMass()
218   {
219      return mType.getOrganicAverageMass();
220   }
221
222   //--------------------------------------------------------------------------
223   /**
224    Sets the protein container to which this x-link belongs.
225    @param inParentProtein the protein to which this x-link belongs
226    */
227   protected void setParentProtein(Protein inParentProtein)
228   {
229      if (inParentProtein != null)
230      {
231         // Validate that the donor chain is present in the parent protein.
232         if (mDonorChain != null)
233         {
234            if (inParentProtein != mDonorChain
235                && null == inParentProtein.getChain(mDonorChainId))
236            {
237               throw new RuntimeException("Donor chain '" + mDonorChainId + "' is not present in this Protein!");
238            }
239         }
240         else
241         {
242            mDonorChain = inParentProtein.getChain(mDonorChainId);
243            if (null == mDonorChain)
244            {
245               throw new RuntimeException("Donor chain '" + mDonorChainId + "' is not present in this Protein!");
246            }
247         }
248
249         // Validate that the acceptor chain is present in the parent protein.
250         if (mAcceptorChain != null)
251         {
252            if (inParentProtein != mAcceptorChain
253                && null == inParentProtein.getChain(mAcceptorChainId))
254            {
255               throw new RuntimeException("Acceptor chain '" + mAcceptorChainId + "' is not present in this Protein!");
256            }
257         }
258         else
259         {
260            mAcceptorChain = inParentProtein.getChain(mAcceptorChainId);
261            if (null == mAcceptorChain)
262            {
263               throw new RuntimeException("Acceptor chain '" + mAcceptorChainId + "' is not present in this Protein!");
264            }
265         }
266
267         // Be sure the donor residue is not already occupied
268         // TODO
269
270         // Be sure the acceptor residue is not already occupied
271         // TODO
272      }
273
274      mParentProtein = inParentProtein;
275   }
276
277   //--------------------------------------------------------------------------
278   protected void validate()
279   {
280      AminoAcid donor    = mDonorChain.aminoAcidAt(mDonorPosition);
281      AminoAcid acceptor = mAcceptorChain.aminoAcidAt(mAcceptorPosition);
282      if (! mType.isAcceptableSite(donor, acceptor))
283      {
284         throw new RuntimeException("Not a valid X-Link!\n" + this);
285      }
286   }
287
288   //--------------------------------------------------------------------------
289   @Override
290   public boolean equals(Object inObj)
291   {
292      return inObj instanceof ProteinXLink ? compareTo(inObj) == 0 : false;
293   }
294
295   //--------------------------------------------------------------------------
296   @Override
297   public int hashCode()
298   {
299      int hashCode = getType().hashCode();
300      if (mDonorChainId != null) hashCode += mDonorChainId.hashCode();
301      hashCode += mDonorPosition.hashCode();
302      if (mAcceptorChainId != null) hashCode += mAcceptorChainId.hashCode();
303      hashCode += mAcceptorPosition.hashCode();
304
305      return  hashCode;
306   }
307
308   //--------------------------------------------------------------------------
309   public int compareTo(Object inObj)
310   {
311      int result = -1;
312
313      if (inObj != null)
314      {
315         if (inObj instanceof ProteinXLink)
316         {
317            ProteinXLink xlink2 = (ProteinXLink) inObj;
318
319            result = getDonorChainId().compareTo(xlink2.getDonorChainId());
320            if (0 == result)
321            {
322               result = mDonorPosition.compareTo(xlink2.mDonorPosition);
323
324               if (0 == result)
325               {
326                  result = getAcceptorChainId().compareTo(xlink2.getAcceptorChainId());
327                  if (0 == result)
328                  {
329                     result = mAcceptorPosition.compareTo(xlink2.mAcceptorPosition);
330                  }
331               }
332            }
333         }
334         else
335         {
336            result = CompareUtil.compare(hashCode(), inObj.hashCode());
337         }
338      }
339
340      return result;
341   }
342
343}