/* * 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 java.util.*; import java.util.regex.*; import java.awt.*; /** Objects of this class constitute the components of glycan molecules. Each residue object has a {linkplain ResidueType residue type} and hold the non-static information about a saccharide or substituente. A residue can have a parent and multiple children. @see ResidueDictionary @see ResidueType @see Glycan @author Alessio Ceroni (a.ceroni@imperial.ac.uk) */ public class Residue { private static int class_id; static { class_id=0; } /** Unique id of this residue object */ public final int id; // properties private ResidueType type; private char anomeric_state; private char anomeric_carbon; private char chirality; private char ring_size; private boolean alditol; // structure private Linkage parent_linkage; private Vector<Linkage> children_linkages; // cleavage private Residue cleaved_residue = null; // positioning private ResiduePlacement preferred_placement = null; private boolean was_sticky = false; // ---- /** Empty constructor. */ public Residue() { id = class_id++; // init type = new ResidueType(); // empty type anomeric_state = '?'; anomeric_carbon = '?'; chirality = '?'; ring_size = '?'; alditol = false; parent_linkage = null; children_linkages = new Vector<Linkage>(0,1); } /** Create a new residue of a specific type. */ public Residue(ResidueType _type) { id = class_id++; // init type = _type; anomeric_state = '?'; anomeric_carbon = type.getAnomericCarbon(); chirality = type.getChirality(); ring_size = type.getRingSize(); alditol = false; parent_linkage = null; children_linkages = new Vector<Linkage>(0,1); } /** Create a new residue of a specific type and set the additional information about the saccharide chemistry. */ public Residue(ResidueType _type, char _anomeric_state, char _anomeric_carbon, char _chirality, char _ring_size) { id = class_id++; // init type = _type; anomeric_state = _anomeric_state; anomeric_carbon = _anomeric_carbon; chirality = _chirality; ring_size = _ring_size; parent_linkage = null; children_linkages = new Vector<Linkage>(0,1); } // ------------- // properties access /** Return the type name. @see ResidueType#getName */ public String getTypeName() { return type.getName(); } /** Return the residue name. @see ResidueType#getResidueName */ public String getResidueName() { return type.getResidueName(); } /** Return the residue type. */ public ResidueType getType() { return type; } /** Set the residue type. */ public void setType(ResidueType _type) { type = _type; } /** Return the cleavage type. see ResidueType#getCleavageType() */ public String getCleavageType() { return type.getCleavageType(); } /** Return the anomeric state. */ public char getAnomericState() { return anomeric_state; } /** Set the anomeric state as [a - alpha, b - beta, ? - unspecified]. */ public void setAnomericState(char _anomeric_state) { anomeric_state = _anomeric_state; } /** Return <code>true</code> if the anomeric state is specified. */ public boolean hasAnomericState() { return anomeric_state!='?'; } /** Return the anomeric carbon position. */ public char getAnomericCarbon() { return anomeric_carbon; } /** Set the anomeric carbon position. */ public void setAnomericCarbon(char _anomeric_carbon) { anomeric_carbon = _anomeric_carbon; if( parent_linkage!=null ) parent_linkage.setAnomericCarbon(anomeric_carbon); } /** Return <code>true</code> if the anomeric carbon position is specified. */ public boolean hasAnomericCarbon() { return anomeric_carbon!='?'; } /** Return the chirality configuration. */ public char getChirality() { return chirality; } /** Set the chirality configuration as [D - dextro, L - levo, ? - unspecified]. */ public void setChirality(char _chirality) { chirality = _chirality; } /** Return <code>true</code> if the chirality configuration is specified. */ public boolean hasChirality() { return chirality!='?'; } /** Return the ring size. */ public char getRingSize() { return ring_size; } /** Set the ring size as [p - pyranose, f - furanose, o - open, ? - unspecified] */ public void setRingSize(char _ring_size) { ring_size = _ring_size; } /** Return <code>true</code> if the ring size is specified. */ public boolean hasRingSize() { return ring_size!='?'; } /** Set to <code>true</code> if this residue is a alditol. */ public void setAlditol(boolean a) { alditol = a; } /** Return <code>true</code> if this residue is a alditol. */ public boolean isAlditol() { return alditol; } /** Return the maxinum number of available linkage position. @see ResidueType#getMaxLinkages */ public int getMaxLinkages() { return type.getMaxLinkages(); } /** Return <code>true</code> if this residue is a saccharide. @see ResidueType#isSaccharide */ public boolean isSaccharide() { return type.isSaccharide(); } /** Return <code>true</code> if this residue is a substituent. @see ResidueType#isSubstituent */ public boolean isSubstituent() { return type.isSubstituent(); } /** Return <code>true</code> if this residue can be cleaved off the structure. @see ResidueType#isCleavable */ public boolean isCleavable() { return type.isCleavable(); } /** Return <code>true</code> if this residue is of a specialy type. @see ResidueType#isSpecial */ public boolean isSpecial() { return type.isSpecial(); } /** Return <code>true</code> if this residue is labile during fragmentation. @see ResidueType#isLabile */ public boolean isLabile() { return (type.isLabile() && !hasChildren()); } /** Return <code>true</code> if this residuecan have a parent. @see ResidueType#canHaveParent */ public boolean canHaveParent() { return type.canHaveParent(); } /** Return <code>true</code> if this residue can have children. @see ResidueType#canHaveChildren */ public boolean canHaveChildren() { return type.canHaveChildren(); } /** Return <code>true</code> if this residue can be used as a reducing end marker. @see ResidueType#canBeReducingEnd */ public boolean canBeReducingEnd() { return type.canBeReducingEnd(); } /** Return <code>true</code> if this residue represent a free reducing end. @see ResidueType#isFreeReducingEnd */ public boolean isFreeReducingEnd() { return type.isFreeReducingEnd(); } /** Return <code>true</code> if this residue represent reducing end marker. */ public boolean isReducingEnd() { return (parent_linkage==null && type.canBeReducingEnd()); } /** Return <code>true</code> if this residue represent the beginning or the end of a repeat block. @see ResidueType#isRepetition */ public boolean isRepetition() { return type.isRepetition(); } /** Return <code>true</code> if this residue represent the beginning of a repeat block. @see ResidueType#isStartRepetition */ public boolean isStartRepetition() { return type.isStartRepetition(); } /** Return <code>true</code> if this residue represent the end of a repeat block. @see ResidueType#isEndRepetition */ public boolean isEndRepetition() { return type.isEndRepetition(); } /** Return the lower bound of a repeat block range, applies only to end repetition residues. @return -1 if the type is not an end repetition. @see ResidueType#getMinRepetitions */ public int getMinRepetitions() { return type.getMinRepetitions(); } /** Set the lower bound of a repeat block range, applies only to end repetition types. @see ResidueType#setMinRepetitions */ public void setMinRepetitions(String min) { type.setMinRepetitions(min); } /** Return the upper bound of a repeat block range, applies only to end repetition residues. @return -1 if the type is not an end repetition. @see ResidueType#getMaxRepetitions */ public int getMaxRepetitions() { return type.getMaxRepetitions(); } /** Set the upper bound of a repeat block range, applies only to end repetition types. @see ResidueType#setMaxRepetitions */ public void setMaxRepetitions(String max) { type.setMaxRepetitions(max); } /** Return <code>true</code> if this residue represent a bracket node. @see ResidueType#isBracket */ public boolean isBracket() { return type.isBracket(); } /** Return <code>true</code> if this residue is contained in a terminal structure linked to a bracket residue. */ public boolean isAntenna() { if( parent_linkage==null ) return false; if( parent_linkage.getParentResidue().isBracket() ) return true; return parent_linkage.getParentResidue().isAntenna(); } /** Return <code>true</code> if this residue represent an attach point. @see ResidueType#isAttachPoint */ public boolean isAttachPoint() { return type.isAttachPoint(); } /** Return <code>true</code> if this residue represent a glycosidic cleavage marker. @see ResidueType#isGlycosidicCleavage */ public boolean isGlycosidicCleavage() { return type.isGlycosidicCleavage(); } /** Return <code>true</code> if this residue represent a cleavage marker. @see ResidueType#isCleavage */ public boolean isCleavage() { return type.isCleavage(); } /** Return <code>true</code> if this residue represent a labile cleavage marker. @see ResidueType#isLCleavage */ public boolean isLCleavage() { return type.isLCleavage(); } /** Return <code>true</code> if this residue represent a ring fragment type. @see ResidueType#isRingFragment */ public boolean isRingFragment() { return type.isRingFragment(); } /** Return the residue that was present at this position before cleavage. */ public Residue getCleavedResidue() { return cleaved_residue; } /** Set the residue that was present at this position before cleavage. */ public void setCleavedResidue(Residue _cleaved_residue) { cleaved_residue = _cleaved_residue; } /** Return <code>true</code> if any one of the connected residues (parent and children) is a glycosidic cleavage. */ public boolean hasGlycosidicCleavages() { for(Linkage l : children_linkages) { if( l.getChildResidue().isGlycosidicCleavage() ) return true; } Residue parent = getParent(); return (parent!=null && parent.isGlycosidicCleavage()); } /** Return <code>true</code> if any one of the connected residues (parent and children) is a ring fragment */ public boolean hasRingFragments() { for(Linkage l : children_linkages) { if( l.getChildResidue().isRingFragment() ) return true; } Residue parent = getParent(); return (parent!=null && parent.isRingFragment()); } /** Return <code>true</code> if any one of the children is a saccharide */ public boolean hasSaccharideChildren() { for(Linkage l : children_linkages) { if( l.getChildResidue().isSaccharide() ) return true; } return false; } /** Return <code>true</code> if the residue has a preferred placement position for displaying. @see GlycanRenderer @see BBoxManager */ public boolean hasPreferredPlacement() { return (preferred_placement!=null); } /** Clear the preferred placement position for displaying. @see GlycanRenderer @see BBoxManager */ public void resetPreferredPlacement() { preferred_placement = null; } /** Set the preferred placement position for displaying. @see GlycanRenderer @see BBoxManager */ public void setPreferredPlacement(ResiduePlacement new_place) { preferred_placement = new_place; } /** Return the preferred placement position for displaying. @see GlycanRenderer @see BBoxManager */ public ResiduePlacement getPreferredPlacement() { return preferred_placement; } protected void setWasSticky(boolean flag) { was_sticky = flag; } protected boolean getWasSticky() { return was_sticky; } // ------------- // structure access /** Set the {@link Linkage linkage} to the parent. */ public void setParentLinkage(Linkage _parent_linkage) { parent_linkage = _parent_linkage; } /** Return the {@link Linkage linkage} to the parent. */ public Linkage getParentLinkage() { return parent_linkage; } /** Return the parent residue. */ public Residue getParent() { if( parent_linkage!=null ) return parent_linkage.getParentResidue(); return null; } /** Return the children {@link Linkage linkages}. */ public Vector<Linkage> getChildrenLinkages() { return children_linkages; } /** Return an iterator over the children {@link Linkage linkages}. */ public Iterator<Linkage> iterator() { return children_linkages.iterator(); } /** Return the first children {@link Linkage linkage} or <code>null</code> if none are present. */ public Linkage firstLinkage() { return ( children_linkages.size()>0 ) ?children_linkages.get(0) :null; } /** Return the first children or <code>null</code> if none are present. */ public Residue firstChild() { return ( children_linkages.size()>0 ) ?children_linkages.get(0).getChildResidue() :null; } /** Return the last children or <code>null</code> if none are present. */ public Residue lastChild() { return ( children_linkages.size()>0 ) ?children_linkages.get(children_linkages.size()-1).getChildResidue() :null; } /** Return the first saccharide children or <code>null</code> if none are present. */ public Residue firstSaccharideChild() { for( Linkage l : children_linkages ) if( l.getChildResidue().isSaccharide() ) return l.getChildResidue(); return null; } /** Return the child with index <code>ind</code>. */ public Residue getChildAt(int ind) { return children_linkages.get(ind).getChildResidue(); } /** Return the child {@link Linkage linkage} with index <code>ind</code>. */ public Linkage getLinkageAt(int ind) { return children_linkages.get(ind); } /** Return the index of the <code>child</code> residue or -1 if it is not in the children collection. */ public int indexOf(Residue child) { if( child==null ) return -1; for(int i=0; i<children_linkages.size(); i++ ) if( getChildAt(i)==child ) return i; return -1; } /** Return <code>true</code> if the residue has a parent. */ public boolean hasParent() { return (parent_linkage!=null && parent_linkage.getParentResidue()!=null); } /** Return <code>true</code> if the residue has at least one child. */ public boolean hasChildren() { return !children_linkages.isEmpty(); } /** Return the number of children. */ public int getNoChildren() { return children_linkages.size(); } /** Return the number of saccharide children. */ public int getNoSaccharideChildren() { int count = 0; for( Linkage l : children_linkages ) { if( l.getChildResidue().isSaccharide() ) count++; } return count; } /** Return the number of connected residues, comprising the parent. */ public int getNoLinkages() { if( parent_linkage==null ) return children_linkages.size(); return (children_linkages.size()+1); } /** Return the total number of chemical bonds with all the connected residues, comprising the parent. */ public int getNoBonds() { int ret = 0; if( parent_linkage!=null ) ret += parent_linkage.getNoBonds(); for( Linkage l : children_linkages ) ret += l.getNoBonds(); return ret; } protected int countLeftBrothers(Residue sub_root, int depth) { Residue parent = getParent(); if( parent==null || this==sub_root ) return 0; int ret = 0; int ind = parent.indexOf(this); for( int i=0; i<ind; i++ ) ret += parent.getChildAt(i).countLeafs(depth); return ret + parent.countLeftBrothers(sub_root,depth+1); } protected int countRightBrothers(Residue sub_root, int depth) { Residue parent = getParent(); if( parent==null || this==sub_root ) return 0; int ret = 0; int ind = parent.indexOf(this); for( int i=ind+1; i<parent.getNoChildren(); i++ ) ret += parent.getChildAt(i).countLeafs(depth); ret += parent.countRightBrothers(sub_root,depth+1); return ret; } protected int countLeafs(int max_depth) { if( max_depth==0 || getNoChildren()==0 ) return 1; int ret=0; for( Iterator<Linkage> i=children_linkages.iterator(); i.hasNext(); ) ret += i.next().getChildResidue().countLeafs(max_depth-1); return ret; } protected boolean hasLinkedParent() { return( parent_linkage!=null && parent_linkage.getParentResidue()!=null && (parent_linkage.getParentResidue().getTypeName().equals("freeEnd") || parent_linkage.getParentResidue().getTypeName().equals("redEnd")) ); } /** Return <code>true</code> if all linkage position are valid and defined. */ public boolean checkLinkages() { if( isBracket() ) { // check that all children have linkage position set for( Linkage l : children_linkages ) { if( l.hasUncertainParentPositions() ) return false; } } else if( isSaccharide() ) { // get linkages Vector<Character> all_pos = new Vector<Character>(0,1); if( hasLinkedParent() ) { if( parent_linkage.hasUncertainChildPositions() ) return false; all_pos.addAll(parent_linkage.getChildPositions()); } for( Linkage l : children_linkages ) { if( l.hasUncertainParentPositions() ) return false; all_pos.addAll(l.getParentPositions()); } // check for conflicts HashSet<Character> set = new HashSet<Character>(); for( Character c : all_pos ) { char pos = c.charValue(); if( set.contains(pos) ) return false; set.add(pos); } // check linkage positions for( Character pos : all_pos ) if( !type.isValidPosition(pos.charValue()) ) return false; } return true; } /** Return <code>true</code> if the linkage position are valid and defined for all the residues in the subtree. */ public boolean checkLinkagesSubtree() { // check current if( !checkLinkages() ) return false; // check children for( Linkage l : children_linkages ) { if( !l.getChildResidue().checkLinkagesSubtree() ) return false; } return true; } /** Return <code>true</code> if all linkage position are defined. */ public boolean isFullySpecified() { if( isBracket() ) return false; if( hasLinkedParent() && parent_linkage.hasUncertainChildPositions() ) return false; for( Linkage l : children_linkages ) { if( l.hasUncertainParentPositions() ) return false; } return true; } /** Return <code>true</code> if the linkage position are defined for all the residues in the subtree. */ public boolean isFullySpecifiedSubtree() { // check current if( !isFullySpecified() ) return false; // check children for( Iterator<Linkage> i=children_linkages.iterator(); i.hasNext(); ) { if( !i.next().getChildResidue().isFullySpecified() ) return false; } return true; } //---------- // structure navigation private boolean fuzzyMatch(char c1, char c2) { return (c1==c2 || c1=='?' || c2=='?' ); } private boolean fuzzyMatch(ResidueType rt1, ResidueType rt2) { return rt1.getName().equals(rt2.getName()) || rt1.getCompositionClass().equals(rt2.getName()) || rt1.getName().equals(rt2.getCompositionClass()); } /** Return <code>true</code> if the two residues match, considering undefined stereochemistry configurations and residue super classes as wildcards. */ public boolean fuzzyMatch(Residue other) { if( other==null ) return false; if( !fuzzyMatch(this.getType(),other.getType()) ) return false; if( !fuzzyMatch(this.anomeric_state,other.anomeric_state) ) return false; if( !fuzzyMatch(this.anomeric_carbon,other.anomeric_carbon) ) return false; if( !fuzzyMatch(this.chirality,other.chirality) ) return false; //if( !fuzzyMatch(this.ring_size,other.ring_size) ) //return false; return true; } /** Return <code>true</code> if the subtree rooted at this residue contains one residue that matches <code>node</code> */ public boolean subtreeContains(Residue node) { if( this==node ) return true; for( Linkage l : children_linkages ) { if( l.getChildResidue().subtreeContains(node) ) return true; } return false; } /** Return <code>true</code> if the two residues match exactly. */ public boolean typeEquals(Residue other) { if( other==null ) return false; if( !this.getTypeName().equals(other.getTypeName()) ) return false; if( this.anomeric_state!=other.anomeric_state ) return false; if( this.anomeric_carbon!=other.anomeric_carbon ) return false; if( this.chirality!=other.chirality ) return false; if( this.ring_size!=other.ring_size ) return false; return true; } /** Return <code>true</code> if the subtree rooted at this residue contains one residue that matches <code>node</code> exactly. */ public boolean subtreeEquals(Residue other) { if( !this.typeEquals(other) ) return false; if( this.getNoChildren()!=other.getNoChildren() ) return false; for( int i=0; i<this.children_linkages.size(); i++ ) { if( !this.getLinkageAt(i).subtreeEquals(other.getLinkageAt(i)) ) return false; } return true; } /** Return the root of the glycan tree to which this residue belongs. */ public Residue getTreeRoot() { if( parent_linkage==null ) return this; return parent_linkage.getParentResidue().getTreeRoot(); } /** Return <code>true</code> if this residue is part of a repeat block. */ public boolean isInRepetition() { return (findStartRepetition()!=null); } /** Return the start of the repeat block to which this residue belong or <code>null</code> otherwise. */ public Residue findStartRepetition() { return findStartRepetition(false); } private Residue findStartRepetition(boolean stop_at_end) { if( this.isStartRepetition() ) return this; if( stop_at_end && this.isEndRepetition() ) return null; if( this.getParent()==null ) return null; return this.getParent().findStartRepetition(true); } /** Return the end of the repeat block to which this residue belong or <code>null</code> otherwise. */ public Residue findEndRepetition() { return findEndRepetition(false); } private Residue findEndRepetition(boolean stop_at_start) { if( this.isEndRepetition() ) return this; if( stop_at_start && this.isStartRepetition() ) return null; for( Linkage l : children_linkages ) { Residue ret = l.getChildResidue().findEndRepetition(true); if( ret!=null ) return ret; } return null; } //--------------- // structure modification /** Add a child residue. @return <code>true</code> if the operation was successful */ public boolean addChild(Residue child) { return addChild(child,Bond.single()); } /** Return <code>true</code> if <code>child</code> can be added to this residue. */ public boolean canAddChild(Residue child) { return canAddChild(child,Bond.single()); } /** Add a child residue at a specific linkage position. @return <code>true</code> if the operation was successful */ public boolean addChild(Residue child, char parent_link_pos) { return addChild(child,Bond.single(parent_link_pos)); } /** Return <code>true</code> if <code>child</code> can be added to this residue at a specific linkage position. */ public boolean canAddChild(Residue child, char parent_link_pos) { return canAddChild(child,Bond.single(parent_link_pos)); } /** Add a child residue with specific bonds. @return <code>true</code> if the operation was successful */ public boolean addChild(Residue child, Collection<Bond> bonds) { if( child==null ) return false; // remove attachment if( child.isAttachPoint() ) { if( !child.hasChildren() ) return false; Linkage link = child.children_linkages.get(0); return addChild(link.getChildResidue(),link.getBonds()); } // cannot add to a repetition if there's already another child if( this.isRepetition() && this.getNoChildren()!=0 ) return false; // cannot add a reducing end if( child.isReducingEnd() && !child.canHaveParent() ) return this.addChild(child.firstChild(),bonds); // add labile back to lcleavage if( isLCleavage() && cleaved_residue.getTypeName().equals(child.getTypeName()) ) { this.copyResidue(cleaved_residue); return true; } // check for available space //if( (getNoLinkages()+bonds.size())<getMaxLinkages() ) Linkage link = new Linkage(this,child,bonds); children_linkages.add(link); child.parent_linkage = link; return true; } /** Return <code>true</code> if <code>child</code> can be added to this residue with specific bonds. */ public boolean canAddChild(Residue child, Collection<Bond> bonds) { if( child==null ) return false; // remove attachment if( child.isAttachPoint() ) { if( !child.hasChildren() ) return false; Linkage link = child.children_linkages.get(0); return canAddChild(link.getChildResidue(),link.getBonds()); } // cannot add to a repetition if there's already another child if( this.isRepetition() && this.getNoChildren()!=0 ) return false; // cannot add a reducing end if( child.isReducingEnd() && !child.canHaveParent() ) return this.canAddChild(child.firstChild(),bonds); // add labile back to lcleavage if( isLCleavage() && cleaved_residue.getTypeName().equals(child.getTypeName()) ) return true; // check for available space //return ( (getNoLinkages()+bonds.size())<getMaxLinkages() ); return true; } /** Move the <code>child</code> residue before <code>other</code> in the children list. @return <code>false</code> if the residues are not children of this object */ public boolean moveChildBefore(Residue child, Residue other) { if( child==null || other==null || child.getParent()!=this || other.getParent()!=this ) return false; int child_ind = indexOf(child); children_linkages.remove(child_ind); int other_ind = indexOf(other); children_linkages.add(other_ind,child.getParentLinkage()); return true; } /** Move the <code>child</code> residue after <code>other</code> in the children list. @return <code>false</code> if the residues are not children of this object */ public boolean moveChildAfter(Residue child, Residue other) { if( child==null || other==null || child.getParent()!=this || other.getParent()!=this ) return false; int child_ind = indexOf(child); children_linkages.remove(child_ind); int other_ind = indexOf(other); children_linkages.add(other_ind+1,child.getParentLinkage()); return true; } /** Insert the <code>child</code> residue at the specified position in the children list. @return <code>true</code> if the operation was successful */ public boolean insertChildAt(Residue child, int ind) { return insertChildAt(child,Bond.single(),ind); } /** Insert the <code>child</code> residue at the specified position in the children list and with specific linkage position. @return <code>true</code> if the operation was successful */ public boolean insertChildAt(Residue child, char parent_link_pos, int ind) { return insertChildAt(child,Bond.single(parent_link_pos),ind); } /** Insert the <code>child</code> residue at the specified position in the children list and with specific bonds. @return <code>true</code> if the operation was successful */ public boolean insertChildAt(Residue child, Collection<Bond> bonds, int ind) { if( child==null ) return false; // cannot add a reducing end if( child.isReducingEnd() && !child.canHaveParent() ) return insertChildAt(child.firstChild(),ind); // check for available space //if( (getNoLinkages()+bonds.size())<getMaxLinkages() ) { Linkage link = new Linkage(this,child,bonds); children_linkages.add(ind,link); child.parent_linkage = link; return true; } /** Remove a child residue. @return <code>true</code> if the operation was successful */ public boolean removeChild(Residue toremove) { if( toremove==null) return false; // remove child from this node int ind = indexOf(toremove); if( ind!=-1 ) { // unlink child from this node children_linkages.remove(ind); toremove.parent_linkage = null; // connect grand children to this node if( this.isStartRepetition() && !toremove.isEndRepetition() ) { // start repetition can have only one children // find the one on the backbone Residue newchild = null; for( Linkage l : toremove.children_linkages ) { if( l.getChildResidue().findEndRepetition()!=null ) newchild = l.getChildResidue(); } // connect it to this residue this.children_linkages.add(newchild.getParentLinkage()); newchild.getParentLinkage().setParentResidue(this); // connect other children to the backbone for( Linkage l : toremove.children_linkages ) { if( l.getChildResidue()!=newchild ) { newchild.children_linkages.add(l); l.setParentResidue(newchild); } } } else { for( int l=0; l<toremove.getNoChildren(); l++ ){ Linkage grand_child_link = toremove.children_linkages.get(l); this.children_linkages.add(ind+l,grand_child_link); grand_child_link.setParentResidue(this); } } // remove other side of repetition if( toremove.isStartRepetition() ) removeChild(this.findEndRepetition()); else if( toremove.isEndRepetition() ) { Residue start = this.findStartRepetition(); if( start!=null ) start.getParent().removeChild(start); } if( this.isStartRepetition() ) { // remove empty repetition boolean removed = false; for( int l=0; l<this.getNoChildren(); l++ ) { if( this.getChildAt(l).isEndRepetition() ) { removed = true; removeChild(this.getChildAt(l)); } } } return true; } // navigate down the tree for( Linkage l : children_linkages ) { if( l.getChildResidue().removeChild(toremove) ) return true; } return false; } /** Insert a residue between this and its parent. @return <code>true</code> if the operation was successful. */ public boolean insertParent(Residue toinsert) { return insertParent(toinsert,Bond.single()); } /** Insert a residue with a specific linkage position between this and its parent. @return <code>true</code> if the operation was successful. */ public boolean insertParent(Residue toinsert, char parent_link_pos) { return insertParent(toinsert,Bond.single(parent_link_pos)); } /** Insert a residue with specific bonds between this and its parent. @return <code>true</code> if the operation was successful. */ public boolean insertParent(Residue toinsert, Collection<Bond> bonds) { if( parent_linkage==null ) return false; // check for compatibility if( !toinsert.canHaveParent() || !toinsert.canHaveChildren() || toinsert.getMaxLinkages()<2 ) return false; // add Residue grand_parent = parent_linkage.getParentResidue(); int ind = grand_parent.indexOf(this); // unlink this from grand parent grand_parent.children_linkages.remove(ind); // link this to new parent parent_linkage.setParentResidue(toinsert); toinsert.children_linkages.add(parent_linkage); // link new parent to grand parent grand_parent.insertChildAt(toinsert,bonds,ind); return true; } /** Swap positions of the two residues in the children list @return <code>false</code> if the residues are not children of this object */ public boolean swapChildren(Residue child1, Residue child2) { if( child1==null || child2==null ) return false; int ind1 = indexOf(child1); int ind2 = indexOf(child2); if( ind1==-1 || ind2==-1 ) return false; Linkage link1 = children_linkages.get(ind1); Linkage link2 = children_linkages.get(ind2); children_linkages.set(ind2,link1); children_linkages.set(ind1,link2); return true; } /** Create a new residue that is a copy of the current one. */ public Residue cloneResidue() { Residue ret = new Residue(this.type); ret.anomeric_state = this.anomeric_state; ret.anomeric_carbon = this.anomeric_carbon; ret.chirality = this.chirality; ret.ring_size = this.ring_size; ret.cleaved_residue = (this.cleaved_residue!=null) ?this.cleaved_residue.cloneResidue() :null; ret.preferred_placement = (this.preferred_placement!=null) ?this.preferred_placement.clone() :null; ret.was_sticky = this.was_sticky; return ret; } /** Copy the information about the current residue into the other residue. */ public void copyResidue(Residue other) { this.type = other.type; this.anomeric_state = other.anomeric_state; this.anomeric_carbon = other.anomeric_carbon; this.chirality = other.chirality; this.ring_size = other.ring_size; this.cleaved_residue = (other.cleaved_residue!=null) ?other.cleaved_residue.cloneResidue() :null; this.preferred_placement = (other.preferred_placement!=null) ?other.preferred_placement.clone() :null; this.was_sticky = other.was_sticky; } /** Create a copy of the subtree rooted at this residue. */ public Residue cloneSubtree() { return cloneSubtree(null,(Residue)null); } protected Residue cloneSubtree(Residue stop_el, ResidueType stop_type) { Residue stop = new Residue(stop_type); if( stop.isCleavage() && stop_el!=null ) stop.setCleavedResidue(stop_el.cloneResidue()); return cloneSubtree(stop_el,stop); } protected Residue cloneSubtree(Residue stop_el, Residue stop) { if( this==stop_el ) return stop; // clone this Residue clone = this.cloneResidue(); // clone children for( Linkage l : children_linkages ) clone.addChild(l.getChildResidue().cloneSubtree(stop_el,stop),l.getBonds()); return clone; } protected Residue cloneSubtreeAdd(Residue add_el, Residue toadd, char toadd_link) { return cloneSubtreeAdd(add_el,toadd,Bond.single(toadd_link)); } protected Residue cloneSubtreeAdd(Residue add_el, Residue toadd, Collection<Bond> toadd_bonds) { // clone this Residue clone = this.cloneResidue(); // clone children for( Linkage l : children_linkages ) clone.addChild(l.getChildResidue().cloneSubtreeAdd(add_el,toadd,toadd_bonds),l.getBonds()); // add child where necessary if( this==add_el && toadd!=null ) { Linkage link = new Linkage(clone,toadd,toadd_bonds); clone.children_linkages.add(link); toadd.parent_linkage = link; } return clone; } //----------------- // serialization }