/*
* EuroCarbDB, a framework for carbohydrate bioinformatics
*
* Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
* A copy of this license accompanies this distribution in the file LICENSE.txt.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* Last commit: $Rev: 1210 $ by $Author: glycoslave $ on $Date:: 2009-06-12 #$
*/
/**
*
*/
package org.eurocarbdb.MolecularFramework.util.analytical.mass;
import java.util.ArrayList;
import java.util.Iterator;
import org.eurocarbdb.MolecularFramework.sugar.GlycoEdge;
import org.eurocarbdb.MolecularFramework.sugar.GlycoNode;
import org.eurocarbdb.MolecularFramework.sugar.GlycoconjugateException;
import org.eurocarbdb.MolecularFramework.sugar.Linkage;
import org.eurocarbdb.MolecularFramework.sugar.LinkageType;
import org.eurocarbdb.MolecularFramework.sugar.Modification;
import org.eurocarbdb.MolecularFramework.sugar.ModificationType;
import org.eurocarbdb.MolecularFramework.sugar.Monosaccharide;
import org.eurocarbdb.MolecularFramework.sugar.NonMonosaccharide;
import org.eurocarbdb.MolecularFramework.sugar.Substituent;
import org.eurocarbdb.MolecularFramework.sugar.SubstituentType;
import org.eurocarbdb.MolecularFramework.sugar.Sugar;
import org.eurocarbdb.MolecularFramework.sugar.SugarUnitAlternative;
import org.eurocarbdb.MolecularFramework.sugar.SugarUnitCyclic;
import org.eurocarbdb.MolecularFramework.sugar.SugarUnitRepeat;
import org.eurocarbdb.MolecularFramework.sugar.UnderdeterminedSubTree;
import org.eurocarbdb.MolecularFramework.sugar.UnvalidatedGlycoNode;
import org.eurocarbdb.MolecularFramework.util.traverser.GlycoTraverser;
import org.eurocarbdb.MolecularFramework.util.traverser.GlycoTraverserTreeSingle;
import org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor;
import org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitorException;
import org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitorNodeType;
/**
* NOTE: THIS VISITOR HANDLES ONLY FULL CONNECTED MONOSACCHARIDES
*
* @author rene
*
*/
public class GlycoVisitorMass implements GlycoVisitor
{
protected boolean m_bMonoisotopic = true;
protected double m_dMass = 0;
protected MassComponents m_objMasses = new MassComponents();
public double getMass()
{
return this.m_dMass;
}
public void setMonoisotopic(boolean a_bMonoisotpic)
{
this.m_bMonoisotopic = a_bMonoisotpic;
}
public boolean getMonoisotopic()
{
return this.m_bMonoisotopic;
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#clear()
*/
public void clear()
{
this.m_dMass = 0;
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#getTraverser(org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor)
*/
public GlycoTraverser getTraverser(GlycoVisitor a_objVisitor) throws GlycoVisitorException
{
return new GlycoTraverserTreeSingle(a_objVisitor);
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#visit(org.eurocarbdb.MolecularFramework.sugar.Monosaccharide)
*/
public void visit(Monosaccharide a_objMonosaccharide) throws GlycoVisitorException
{
// mass of the basetype
double t_dMass = this.m_objMasses.getSuperclassMass(a_objMonosaccharide.getSuperclass(), this.m_bMonoisotopic);
// mass of the modifications
for (Iterator<Modification> t_iterModi = a_objMonosaccharide.getModification().iterator(); t_iterModi.hasNext();)
{
Modification t_objModi = t_iterModi.next();
if ( t_objModi.getModificationType() != ModificationType.KETO || t_objModi.getPositionOne() != 1 )
{
t_dMass += this.m_objMasses.getModificationMass(t_objModi.getModificationType(), this.m_bMonoisotopic,t_objModi.getPositionOne());
}
}
// mass of the linkages around
if ( a_objMonosaccharide.getParentEdge() != null )
{
for (Iterator<Linkage> t_iterLinkages = a_objMonosaccharide.getParentEdge().getGlycosidicLinkages().iterator(); t_iterLinkages.hasNext();)
{
LinkageType t_objLinkageType = t_iterLinkages.next().getChildLinkageType();
t_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkageType,this.m_bMonoisotopic);
}
}
for (Iterator<GlycoEdge> t_iterEdges = a_objMonosaccharide.getChildEdges().iterator(); t_iterEdges.hasNext();)
{
for (Iterator<Linkage> t_iterLinkages = t_iterEdges.next().getGlycosidicLinkages().iterator(); t_iterLinkages.hasNext();)
{
LinkageType t_objLinkageType = t_iterLinkages.next().getParentLinkageType();
t_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkageType,this.m_bMonoisotopic);
}
}
this.m_dMass += t_dMass;
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#visit(org.eurocarbdb.MolecularFramework.sugar.Substituent)
*/
public void visit(Substituent a_objSubstituent) throws GlycoVisitorException
{
double t_dMass = this.m_objMasses.getSubstitutionsMass(a_objSubstituent.getSubstituentType(), this.m_bMonoisotopic);
int t_iLinkageCount = 0;
if ( a_objSubstituent.getParentEdge() != null )
{
for (Iterator<Linkage> t_iterLinkage = a_objSubstituent.getParentEdge().getGlycosidicLinkages().iterator(); t_iterLinkage.hasNext();)
{
t_iterLinkage.next();
t_iLinkageCount++;
}
}
for (Iterator<GlycoEdge> t_iterEdge = a_objSubstituent.getChildEdges().iterator(); t_iterEdge.hasNext();)
{
for (Iterator<Linkage> t_iterLinkage = t_iterEdge.next().getGlycosidicLinkages().iterator(); t_iterLinkage.hasNext();)
{
t_iterLinkage.next();
t_iLinkageCount++;
}
}
SubstituentType t_objSubstType = a_objSubstituent.getSubstituentType();
if ( t_iLinkageCount < t_objSubstType.getMinValence() )
{
throw new GlycoMassException("Error with minimum linkage count of substituent " + a_objSubstituent.getSubstituentType().getName() + "." );
}
if ( t_iLinkageCount > t_objSubstType.getMinValence() )
{
t_dMass += this.handleMultipleLinkedSubstituents(0, t_iLinkageCount, a_objSubstituent );
}
this.m_dMass += t_dMass;
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#visit(org.eurocarbdb.MolecularFramework.sugar.UnvalidatedGlycoNode)
*/
public void visit(UnvalidatedGlycoNode unvalidated) throws GlycoVisitorException
{
throw new GlycoMassException("Mass calculation of Unvalidated residues (UnvalidatedGlycoNode) is not supported ." );
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#visit(org.eurocarbdb.MolecularFramework.sugar.GlycoEdge)
*/
public void visit(GlycoEdge linkage) throws GlycoVisitorException
{
// nothing to do
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#visit(org.eurocarbdb.MolecularFramework.sugar.SugarUnitAlternative)
*/
public void visit(SugarUnitAlternative alternative) throws GlycoVisitorException
{
throw new GlycoMassException("Mass calculation of alternative SugarUnits is not supported ." );
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#visit(org.eurocarbdb.MolecularFramework.sugar.NonMonosaccharide)
*/
public void visit(NonMonosaccharide residue) throws GlycoVisitorException
{
throw new GlycoMassException("Mass calculation of NonMonosaccharide " + residue.getName() + " is not supported ." );
}
/**
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#visit(org.eurocarbdb.MolecularFramework.sugar.SugarUnitRepeat)
*/
public void visit(SugarUnitRepeat a_objRepeate) throws GlycoVisitorException
{
if ( a_objRepeate.getMinRepeatCount() != a_objRepeate.getMaxRepeatCount() || a_objRepeate.getMinRepeatCount() == SugarUnitRepeat.UNKNOWN )
{
throw new GlycoMassException("Mass calculation of repeat units with not exactly defined repeat count is not possible.." );
}
GlycoVisitorMass t_objMass = new GlycoVisitorMass();
GlycoTraverser t_trav = this.getTraverser(t_objMass);
t_trav.traverseGraph(a_objRepeate);
this.m_dMass += t_objMass.getMass() * a_objRepeate.getMinRepeatCount();
// internal repeat linkage
double t_dLinkIn = this.specialLinkage(a_objRepeate.getRepeatLinkage(),true,a_objRepeate);
double t_dLinkOut = this.specialLinkage(a_objRepeate.getRepeatLinkage(),false,a_objRepeate);
this.m_dMass += (t_dLinkIn + t_dLinkOut) * (a_objRepeate.getMinRepeatCount() - 1);
// in and out linkages
if ( a_objRepeate.getParentEdge() != null )
{
if ( a_objRepeate.getParentEdge().getGlycosidicLinkages().size() != a_objRepeate.getRepeatLinkage().getGlycosidicLinkages().size() )
{
throw new GlycoMassException("Repeat in linkage and repeat linkage weight does not match." );
}
this.m_dMass += t_dLinkIn;
}
for (Iterator<GlycoEdge> t_iterEdges = a_objRepeate.getChildEdges().iterator(); t_iterEdges.hasNext();)
{
if ( t_iterEdges.next().getGlycosidicLinkages().size() != a_objRepeate.getRepeatLinkage().getGlycosidicLinkages().size() )
{
throw new GlycoMassException("Repeat out linkage and repeat linkage weight does not match." );
}
this.m_dMass += t_dLinkOut;
}
// underdetermined units
for (Iterator<UnderdeterminedSubTree> t_iterUnder = a_objRepeate.getUndeterminedSubTrees().iterator(); t_iterUnder.hasNext();)
{
UnderdeterminedSubTree t_objUTree = t_iterUnder.next();
if ( t_objUTree.getProbabilityLower() < 100 )
{
throw new GlycoMassException("Mass calculation for stoichometric distribution is not possible.");
}
else
{
t_objMass = new GlycoVisitorMass();
t_trav = this.getTraverser(t_objMass);
t_trav.traverseGraph(t_objUTree);
this.m_dMass += t_objMass.getMass();
// incoming linkage
for (Iterator<Linkage> t_iterLinkage = t_objUTree.getConnection().getGlycosidicLinkages().iterator(); t_iterLinkage.hasNext();)
{
Linkage t_objLinkage= t_iterLinkage.next();
if ( t_objLinkage.getParentLinkageType() == LinkageType.NONMONOSACCHARID )
{
if ( this.isHomogenSubst( t_objUTree.getParents() ) )
{
this.m_dMass += this.checkAndCalculateSubstituent(t_objUTree.getParents(),t_objUTree.getConnection());
}
else
{
throw new GlycoMassException("Mass calculation of (heterogen) composition repeat unit is not possible." );
}
}
else
{
this.m_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkage.getParentLinkageType(),this.m_bMonoisotopic);
}
if ( t_objLinkage.getChildLinkageType() == LinkageType.NONMONOSACCHARID )
{
try
{
if ( this.isHomogenSubst( t_objUTree.getRootNodes() ) )
{
this.m_dMass += this.checkAndCalculateSubstituent(t_objUTree.getRootNodes(),t_objUTree.getConnection());
}
else
{
throw new GlycoMassException("Mass calculation of (heterogen) composition repeat unit is not possible." );
}
}
catch (GlycoconjugateException e)
{
throw new GlycoVisitorException(e.getMessage(),e);
}
}
else
{
this.m_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkage.getChildLinkageType(),this.m_bMonoisotopic);
}
}
}
}
}
/**
* @param repeatLinkage
* @throws GlycoVisitorException
*/
private double specialLinkage(GlycoEdge a_objRepeatEdge,boolean a_bIn,SugarUnitRepeat a_objRepeat) throws GlycoVisitorException
{
double t_dMass = 0;
if ( a_bIn )
{
for (Iterator<Linkage> t_iterLinkage = a_objRepeatEdge.getGlycosidicLinkages().iterator(); t_iterLinkage.hasNext();)
{
Linkage t_objLinkage = t_iterLinkage.next();
if ( t_objLinkage.getChildLinkageType() == LinkageType.NONMONOSACCHARID )
{
GlycoVisitorRepeatLinkType t_visStart = new GlycoVisitorRepeatLinkType();
a_objRepeat.accept(t_visStart);
if ( t_visStart.getEdge() == null )
{
throw new GlycoMassException("Mass calculation of repeat is not possible." );
}
else
{
if ( a_objRepeatEdge.getGlycosidicLinkages().size() != t_visStart.getEdge().getGlycosidicLinkages().size() )
{
throw new GlycoMassException("Repeat linkage and inner repeat linkage weight does not match." );
}
else
{
if ( this.isHomogenSubst( t_visStart.getStartNodes() ) )
{
t_dMass += this.checkAndCalculateSubstituent(t_visStart.getStartNodes(),t_visStart.getEdge());
}
else
{
throw new GlycoMassException("Mass calculation of (heterogen) composition repeat unit is not possible." );
}
}
}
}
else
{
t_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkage.getChildLinkageType(),this.m_bMonoisotopic);
}
}
}
else
{
for (Iterator<Linkage> t_iterLinkage = a_objRepeatEdge.getGlycosidicLinkages().iterator(); t_iterLinkage.hasNext();)
{
Linkage t_objLinkage = t_iterLinkage.next();
if ( t_objLinkage.getParentLinkageType() == LinkageType.NONMONOSACCHARID )
{
GlycoVisitorRepeatLinkType t_visStart = new GlycoVisitorRepeatLinkType();
t_visStart.setRepeatIn(false);
a_objRepeat.accept(t_visStart);
if ( t_visStart.getEdge() == null )
{
throw new GlycoMassException("Mass calculation of repeat is not possible." );
}
else
{
if ( a_objRepeatEdge.getGlycosidicLinkages().size() != t_visStart.getEdge().getGlycosidicLinkages().size() )
{
throw new GlycoMassException("Repeat linkage and inner repeat linkage weight does not match." );
}
else
{
if ( this.isHomogenSubst( t_visStart.getStartNodes() ) )
{
t_dMass += this.checkAndCalculateSubstituent(t_visStart.getStartNodes(),t_visStart.getEdge());
}
else
{
throw new GlycoMassException("Mass calculation of (heterogen) composition repeat unit is not possible." );
}
}
}
}
else
{
t_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkage.getParentLinkageType(),this.m_bMonoisotopic);
}
}
}
return t_dMass;
}
/*
* @see org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitor#start(org.eurocarbdb.MolecularFramework.sugar.Sugar)
*/
public void start(Sugar a_objSugar) throws GlycoVisitorException
{
this.clear();
GlycoTraverser t_objTraverser = this.getTraverser(this);
t_objTraverser.traverseGraph(a_objSugar);
// underdetermined units
for (Iterator<UnderdeterminedSubTree> t_iterUnder = a_objSugar.getUndeterminedSubTrees().iterator(); t_iterUnder.hasNext();)
{
UnderdeterminedSubTree t_objUTree = t_iterUnder.next();
if ( t_objUTree.getProbabilityLower() < 100 )
{
throw new GlycoMassException("Mass calculation for stoichometric distribution is not possible.");
}
else
{
GlycoVisitorMass t_objMass = new GlycoVisitorMass();
GlycoTraverser t_trav = this.getTraverser(t_objMass);
t_trav.traverseGraph(t_objUTree);
this.m_dMass += t_objMass.getMass();
// incoming linkage
for (Iterator<Linkage> t_iterLinkage = t_objUTree.getConnection().getGlycosidicLinkages().iterator(); t_iterLinkage.hasNext();)
{
Linkage t_objLinkage= t_iterLinkage.next();
if ( t_objLinkage.getParentLinkageType() == LinkageType.NONMONOSACCHARID )
{
if ( this.isHomogenSubst( t_objUTree.getParents() ) )
{
this.m_dMass += this.checkAndCalculateSubstituent(t_objUTree.getParents(),t_objUTree.getConnection());
}
else
{
throw new GlycoMassException("Mass calculation of (heterogen) composition repeat unit is not possible." );
}
}
else
{
this.m_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkage.getParentLinkageType(),this.m_bMonoisotopic);
}
if ( t_objLinkage.getChildLinkageType() == LinkageType.NONMONOSACCHARID )
{
try
{
if ( this.isHomogenSubst( t_objUTree.getRootNodes() ) )
{
this.m_dMass += this.checkAndCalculateSubstituent(t_objUTree.getRootNodes(),t_objUTree.getConnection());
}
else
{
throw new GlycoMassException("Mass calculation of (heterogen) composition repeat unit is not possible." );
}
}
catch (GlycoconjugateException e)
{
throw new GlycoVisitorException(e.getMessage(),e);
}
}
else
{
this.m_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkage.getChildLinkageType(),this.m_bMonoisotopic);
}
}
}
}
}
public void visit(SugarUnitCyclic a_objCyclic) throws GlycoVisitorException
{
if ( a_objCyclic.getParentEdge() != null )
{
for (Iterator<Linkage> t_iterLinkages = a_objCyclic.getParentEdge().getGlycosidicLinkages().iterator(); t_iterLinkages.hasNext();)
{
// for each linkage
LinkageType t_objLinkageType = t_iterLinkages.next().getChildLinkageType();
if ( t_objLinkageType == LinkageType.NONMONOSACCHARID )
{
GlycoVisitorRepeatLinkType t_visStart = new GlycoVisitorRepeatLinkType();
a_objCyclic.getCyclicStart().accept(t_visStart);
if ( t_visStart.getEdge() == null )
{
throw new GlycoMassException("Mass calculation of cyclic start is not possible." );
}
else
{
if ( a_objCyclic.getParentEdge().getGlycosidicLinkages().size() != t_visStart.getEdge().getGlycosidicLinkages().size() )
{
throw new GlycoMassException("Cyclic linkage and repeat linkage weight does not match." );
}
else
{
if ( this.isHomogenSubst( t_visStart.getStartNodes() ) )
{
this.m_dMass += this.checkAndCalculateSubstituent(t_visStart.getStartNodes(),t_visStart.getEdge());
}
else
{
throw new GlycoMassException("Mass calculation of (heterogen) composition repeat unit is not possible." );
}
}
}
}
else
{
this.m_dMass += this.m_objMasses.getLinkageTypeMass(t_objLinkageType,this.m_bMonoisotopic);
}
}
}
else
{
throw new GlycoMassException("Mass calculation of unconnected cylcic unit is not possible." );
}
}
/**
* @param startNodes
* @param edge
* @return
* @throws GlycoVisitorException
*/
private double checkAndCalculateSubstituent( ArrayList<GlycoNode> a_aStartNodes, GlycoEdge a_objEdge) throws GlycoVisitorException
{
double t_dMass = -1;
double t_dMassTemp = -1;
int t_iLinkageCount = 0;
GlycoVisitorNodeType t_visType = new GlycoVisitorNodeType();
for (Iterator<GlycoNode> t_iterNodes = a_aStartNodes.iterator(); t_iterNodes.hasNext();)
{
GlycoNode t_objNode = t_iterNodes.next();
if ( t_objNode.getParentEdge() != null )
{
t_iLinkageCount += t_objNode.getParentEdge().getGlycosidicLinkages().size();
}
for (Iterator<GlycoEdge> t_iterLinkage = t_objNode.getChildEdges().iterator(); t_iterLinkage.hasNext();)
{
t_iLinkageCount += t_iterLinkage.next().getGlycosidicLinkages().size();
}
t_dMassTemp = this.handleMultipleLinkedSubstituents(t_iLinkageCount, a_objEdge.getGlycosidicLinkages().size(),t_visType.getSubstituent(t_objNode));
if ( t_dMass == -1 )
{
t_dMass = t_dMassTemp;
}
else
{
if ( t_dMass != t_dMassTemp )
{
throw new GlycoMassException("Mass calculation of heterogen repeat substituents is not possible." );
}
}
}
return t_dMass;
}
private boolean isHomogenSubst(ArrayList<GlycoNode> startNodes) throws GlycoVisitorException
{
boolean t_bOther = false;
GlycoVisitorNodeType t_visType = new GlycoVisitorNodeType();
for (Iterator<GlycoNode> t_iter = startNodes.iterator(); t_iter.hasNext();)
{
if ( !t_visType.isSubstituent(t_iter.next()) )
{
t_bOther = true;
}
}
return !t_bOther;
}
private double handleMultipleLinkedSubstituents(int t_iLinkageCalculated, int t_iLinkageCountNew , Substituent a_objSubstituent) throws GlycoMassException
{
SubstituentType t_objSubstType = a_objSubstituent.getSubstituentType();
if ( t_objSubstType == SubstituentType.PHOSPHATE )
{
double t_dIncMass = 0;
if ( this.m_bMonoisotopic )
{
t_dIncMass = 17.0027396541;
}
else
{
t_dIncMass = 17.00734568218410;
}
if ( (t_iLinkageCountNew + t_iLinkageCalculated) > t_objSubstType.getMaxValence() )
{
throw new GlycoMassException("Error with max. linkage count) of substituent phosphate." );
}
return (1-(t_iLinkageCalculated+t_iLinkageCountNew)) * t_dIncMass;
}
throw new GlycoMassException("Error with linkage count of substituent " + a_objSubstituent.getSubstituentType().getName() + "." );
}
}