/*
* 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.application.glycanbuilder;
import org.eurocarbdb.MolecularFramework.sugar.GlycoEdge;
import java.util.*;
/**
Objects of this class contain the information about the linkage
between two residues. A linkage can be formed by multipled chemical
bonds between different atoms of the two residues. The linkage
information can be partially or completely undefined.
@author Alessio Ceroni (a.ceroni@imperial.ac.uk)
*/
public class Linkage {
//----------------------------
// internal classes
/**
Compare two linkage objects.
*/
static public class LinkageComparator implements java.util.Comparator<Linkage> {
public int compare(Linkage o1, Linkage o2) {
return GWSParser.toStringLinkage(o1).compareTo(GWSParser.toStringLinkage(o2));
}
}
//----------------------------
// members
private Residue parent;
private Residue child;
private Vector<Bond> bonds; // the last bond is the glycosidic bond
//----------------------------
// construction
/**
Empty constructor.
*/
public Linkage() {
parent = null;
child = null;
setLinkagePositions('?');
}
/**
Create a new linkage between a parent and child residue.
*/
public Linkage(Residue _parent, Residue _child) {
parent = _parent;
child = _child;
setLinkagePositions('?');
}
/**
Create a new linkage between a parent and child residue at a
specified parent position.
*/
public Linkage(Residue _parent, Residue _child, char link_pos) {
parent = _parent;
child = _child;
setLinkagePositions(link_pos);
}
/**
Create a new linkage between a parent and child residue given a
set of parent positions.
*/
public Linkage(Residue _parent, Residue _child, char[] link_poss) {
parent = _parent;
child = _child;
setLinkagePositions(link_poss);
}
/**
Create a new linkage between a parent and child residue with
chemical bonds between two pair of atoms.
@param link_poss parent positions
@param second_p_poss parent positions of the second bond
@param second_c_pos child position of the second bond
*/
public Linkage(Residue _parent, Residue _child, char[] link_poss, char[] second_p_poss, char second_c_pos) {
parent = _parent;
child = _child;
setLinkagePositions(link_poss,second_p_poss,second_c_pos);
}
/**
Create a new linkage between a parent and child residue given a
list of chemical bonds between them.
*/
public Linkage(Residue _parent, Residue _child, Collection<Bond> _bonds) {
parent = _parent;
child = _child;
setLinkagePositions(_bonds);
}
//----------------------------
// Members access
/**
Return the parent residue.
*/
public Residue getParentResidue() {
return parent;
}
/**
Set the parent residue.
*/
public void setParentResidue(Residue _parent) {
parent = _parent;
}
/**
Return the child residue.
*/
public Residue getChildResidue() {
return child;
}
/**
Set the child residue.
*/
public void setChildResidue(Residue _child) {
child = _child;
}
/**
Return the list of chemical bonds between the two residues.
*/
public Vector<Bond> getBonds() {
return bonds;
}
/**
Return an ordered list of chemical bonds between the two residues.
*/
public Vector<Bond> getBondsSorted() {
Vector<Bond> ret = new Vector<Bond>(bonds);
Collections.sort(ret,new Bond.Comparator());
return ret;
}
/**
Set the list of chemical bonds between the two residues.
*/
public void setBonds(Vector<Bond> _bonds) {
if( _bonds!=null && _bonds.size()>0 ) {
bonds = _bonds;
setAnomericCarbon(child.getAnomericCarbon());
}
else
setLinkagePositions('?');
}
/**
Return the chemical bond between the anomeric carbon of the
child residue and the parent residue.
*/
public Bond glycosidicBond() {
return bonds.get(bonds.size()-1);
}
/**
Return the number of chemical bonds forming the linkage.
*/
public int getNoBonds() {
return bonds.size();
}
/**
Return <code>true</code> if the linkage is formed by a single
chemical bond.
*/
public boolean hasSingleBond() {
return bonds.size()==1;
}
/**
Return <code>true</code> if the linkage is formed by multiple
chemical bonds.
*/
public boolean hasMultipleBonds() {
return bonds.size()>1;
}
/**
Set the parent position for this linkage.
*/
public void setLinkagePositions(char link_pos) {
bonds = new Vector<Bond>(0,1);
char c_pos = (child==null) ?'?' :child.getAnomericCarbon();
bonds.add(new Bond(link_pos,c_pos));
}
/**
Set the parent positions for this linkage.
*/
public void setLinkagePositions(char[] link_poss) {
bonds = new Vector<Bond>(0,1);
char c_pos = (child==null) ?'?' :child.getAnomericCarbon();
bonds.add(new Bond(link_poss,c_pos));
}
/**
Set the positions for the chemical bonds forming this linkage.
@param link_poss parent positions
@param second_p_poss parent positions of the second bond
@param second_c_pos child position of the second bond
*/
public void setLinkagePositions(char[] link_poss, char[] second_p_poss, char second_c_pos) {
bonds = new Vector<Bond>(0,1);
// add second bond
bonds.add(new Bond(second_p_poss,second_c_pos));
// add glycosidic bond
char c_pos = (child==null) ?'?' :child.getAnomericCarbon();
bonds.add(new Bond(link_poss,c_pos));
}
/**
Set the bonds forming this linkage.
*/
public void setLinkagePositions(Collection<Bond> _bonds) {
bonds = new Vector<Bond>(0,1);
for( Bond toadd : _bonds )
bonds.add(toadd.clone());
if( bonds.size()==0 )
bonds.add(new Bond());
if( child!=null )
setAnomericCarbon(child.getAnomericCarbon());
}
/**
Set the anomeric carbon position for this linkage.
*/
public void setAnomericCarbon(char pos) {
glycosidicBond().setChildPosition(pos);
}
/**
Return the anomeric carbon position for this linkage.
*/
public char getAnomericCarbon() {
return glycosidicBond().getChildPosition();
}
/**
Return the list of all parent positions for all bonds forming
this linkage.
*/
public Collection<Character> getParentPositions() {
Vector<Character> ret = new Vector<Character>();
for( Bond b: bonds ) {
char[] p_poss = b.getParentPositions();
for( int i=0; i<p_poss.length; i++ )
ret.add(p_poss[i]);
}
return ret;
}
/**
Return the list of all child positions for all bonds forming
this linkage.
*/
public Collection<Character> getChildPositions() {
Vector<Character> ret = new Vector<Character>();
for( int i=0; i<bonds.size(); i++ )
ret.add(bonds.get(i).getChildPosition());
return ret;
}
/**
Return the list of all parent positions for all bonds forming
this linkage as a comma separated string.
*/
public String getParentPositionsString() {
StringBuilder sb = new StringBuilder();
for( Bond b : getBondsSorted() ) {
if( sb.length()>0 )
sb.append(',');
char[] p_poss = b.getParentPositions();
for( int i=0; i<p_poss.length; i++ ) {
if( i>0 )
sb.append('/');
sb.append(p_poss[i]);
}
}
return sb.toString();
}
/**
Return the list of all child positions for all bonds forming
this linkage as a comma separated string.
*/
public String getChildPositionsString() {
StringBuilder sb = new StringBuilder();
for( Bond b : getBondsSorted() ) {
if( sb.length()>0 )
sb.append(',');
sb.append(b.getChildPosition());
}
return sb.toString();
}
/**
Return <code>true</code> if the linkage is formed by a single
bond with a single parent position.
*/
public boolean hasSingleLinkagePosition() {
return ( bonds.size()==1 && bonds.get(0).getParentPositions().length==1 );
}
/**
Return the parent position of this linkage as a single
character.
@return undefined if there's more than one parent position
*/
public char getParentPositionsSingle() {
if( bonds.size()==1 && bonds.get(0).getParentPositions().length==1 )
return bonds.get(0).getParentPositions()[0];
return '?';
}
/**
Return the child position of this linkage as a single
character.
@return undefined if there's more than one child position
*/
public char getChildPositionsSingle() {
if( bonds.size()==1 )
return bonds.get(0).getChildPosition();
return '?';
}
/**
Return <code>true</code> if some bonds have uncertain parent
positions.
*/
public boolean hasUncertainParentPositions() {
for( Bond b : bonds ) {
if( b.getParentPositions().length>1 || b.getParentPositions()[0]=='?' )
return true;
}
return false;
}
/**
Return <code>true</code> if some bonds have uncertain child
positions.
*/
public boolean hasUncertainChildPositions() {
for( Bond b : bonds ) {
if( b.getChildPosition()=='?' )
return true;
}
return false;
}
/**
Return <code>true</code> if the two objects contains the same
information. Propagates to the subtrees starting at the
children residues.
*/
public boolean subtreeEquals(Linkage other) {
if( other==null )
return false;
if( this.bonds.size()!=other.bonds.size() )
return false;
for( int i=0; i<this.bonds.size(); i++ ) {
if( !this.bonds.get(i).equals(other.bonds.get(i)) )
return false;
if( !this.child.subtreeEquals(other.child) )
return false;
}
return true;
}
/**
Return <code>true</code> if the two objects are
similar. Undefined positions are treated as wildcards.
*/
public boolean fuzzyMatch(Linkage other) {
if( other==null )
return false;
if( this.bonds.size()!=other.bonds.size() )
return false;
// try all bond permutations
PermutationGenerator cg = new PermutationGenerator(other.bonds.size());
while( cg.hasMore() ) {
int[] indices = cg.getNext();
int i=0;
for( ; i<this.bonds.size(); i++ ) {
if( !this.bonds.get(i).fuzzyMatch(other.bonds.get(indices[i])) )
break;
}
if( i==this.bonds.size() )
return true; // all bonds matches
}
return false; // no permutation matches
}
//----------------------------
// serialization
/**
Create a string representation of this linkage object in IUPAC
notation.
*/
public String toIupac() {
StringBuilder sb = new StringBuilder();
if( hasSingleLinkagePosition() && child.getAnomericState()!='?' && !hasUncertainChildPositions() && !hasUncertainParentPositions() )
sb.append(child.getAnomericState());
if( !hasUncertainChildPositions() && !hasUncertainParentPositions() )
sb.append(getChildPositionsString());
if( hasSingleLinkagePosition() )
sb.append("-");
else
sb.append("=");
if( !hasUncertainParentPositions() )
sb.append(getParentPositionsString());
return sb.toString();
}
}