/* * 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.resourcesdb.monosaccharide; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eurocarbdb.resourcesdb.*; import org.eurocarbdb.resourcesdb.atom.*; import org.eurocarbdb.resourcesdb.glycoconjugate_derived.LinkageType; import org.eurocarbdb.resourcesdb.io.GlycoCTExporter; import org.eurocarbdb.resourcesdb.io.GlycoCTImporter; import org.eurocarbdb.resourcesdb.template.TemplateContainer; import org.eurocarbdb.resourcesdb.util.Utils; /** * Basetype object, stores the properties of a monosaccharide basetype. * * @author Thomas Luetteke */ public class Basetype extends MolecularEntity { private Stereocode stereocode; private int size; private int ringStart; private int ringEnd; private int defaultCarbonylPosition; private Anomer anomer; private StereoConfiguration configuration; private List<CoreModification> coreModifications; private String superclass; private Boolean isSuperclassFlag; private int dbId; /** * Constant to mark unknown ring size (0) */ public static final int UNKNOWN_RING = 0; /** * Constant to mark open chain residues (-1) */ public static final int OPEN_CHAIN = -1; //***************************************************************************** //*** constructors: *********************************************************** //***************************************************************************** public Basetype() { this(null, null); } public Basetype(Config conf, TemplateContainer container) { this.setConfig(conf); this.setTemplateContainer(container); this.init(); } public Basetype(String glycoCTName) throws ResourcesDbException { this(glycoCTName, null, null); } public Basetype(String glycoCTName, Config conf, TemplateContainer container) throws ResourcesDbException { this.setConfig(conf); this.setTemplateContainer(container); this.init(); GlycoCTImporter importer = new GlycoCTImporter(GlycanNamescheme.GLYCOCT, this.getConfig(), this.getTemplateContainer()); Monosaccharide ms = new Monosaccharide(this); importer.parseMsString(glycoCTName, ms); } //***************************************************************************** //*** getters/setters: ******************************************************** //***************************************************************************** /** * @return the dbId */ public int getDbId() { return dbId; } /** * @param dbId the dbId to set */ public void setDbId(int dbId) { this.dbId = dbId; } /** * Get the ring start (carbonyl position) of the basetype. * @return the ring start position */ public int getRingStart() { return this.ringStart; } /** * Set the ring start (carbonyl position) of the basetype. * In case the ring oxygen position is defined, it is adjusted to the new carbonyl position, i.e. the ring type is conserved * @param position the ring start position to set */ public void setRingStart(int position) { int currentPosition = getRingStart(); this.ringStart =position; if(getRingEnd() > 0) { //*** ring oxygen was set before: adjust value to maintain correct ring type *** setRingEnd(getRingEnd() + (position - currentPosition)); } } /** * Set the ring start (carbonyl position) of the basetype. * In contrast to the <code>setRingStart()</code> method, the ring oxygen is not touched here. * @param position the ring start position to set */ public void setRingStartNoAdjustment(int position) { this.ringStart = position; } /** * Get the default carbonyl position (default ring start) of this basetype * @return the default carbonyl position */ public int getDefaultCarbonylPosition() { return defaultCarbonylPosition; } /** * Set the default carbonyl position (default ring start) for this basetype * @param position the position to set */ public void setDefaultCarbonylPosition(int position) { this.defaultCarbonylPosition = position; } /** * Get the size of the basetype * @return the size */ public int getSize() { return this.size; } /** * Set the size of the basetype * @param size the size to set */ public void setSize(int size) { this.size = size; } /** * Get the anomer of the basetype * @return the anomer */ public Anomer getAnomer() { return this.anomer; } /** * Set the anomer of the basetype * @param anomer the anomer to set */ public void setAnomer(Anomer anomer) { this.anomer = anomer; } /** * Set the anomer of the basetype by the anomer namestring * @param anomerStr the string representation of the anomer to set * @throws MonosaccharideException in case anomerStr cannot be translated into a valid anomer */ public void setAnomer(String anomerStr) throws MonosaccharideException { setAnomer(Anomer.forNameOrSymbol(anomerStr)); } /** * Get the anomer symbol of this basetype's anomer. * This direct access to the anomer symbol is needed for Hibernate mapping. * @return the anomer symbol, or null in case this.anomer is null */ public String getAnomerSymbol() { if(this.getAnomer() == null) { return null; } return(this.getAnomer().getSymbol()); } /** * This method is mainly needed for Hibernate mapping of the anomer to the database table, in which not the anomer itself but its symbol is stored. * If anomerStr is not null, the setAnomer(String) method is called. * @param anomerStr the symbol of the anomer to set, may be null (anomer is set to null then) * @throws MonosaccharideException */ public void setAnomerSymbol(String anomerStr) throws MonosaccharideException { if(anomerStr == null) { this.setAnomer((Anomer)null); } else { this.setAnomer(anomerStr); } } /** * Get the configuration of the basetype * @return the configuration */ public StereoConfiguration getConfiguration() { return this.configuration; } /** * Set the configuration of the basetype * @param configuration the configuration to set */ public void setConfiguration(StereoConfiguration configuration) { this.configuration = configuration; } /** * Set the configuration of the basetype by a configuration string * @param configStr the string representation of the configuration to set * @throws MonosaccharideException in case configStr does not encode a valid configuration */ public void setConfiguration(String configStr) throws MonosaccharideException { setConfiguration(StereoConfiguration.forNameOrSymbol(configStr)); } public String getConfigurationSymbol() { if(this.configuration == null) { return StereoConfiguration.Unknown.getSymbol(); } else { return this.configuration.getSymbol(); } } public void setConfigurationSymbol(String confSymbol) throws MonosaccharideException { if(confSymbol == null) { this.setConfiguration((StereoConfiguration) null); } else { this.setConfiguration(confSymbol); } } /** * Get the ring end of the basetype * @return the ring end position */ public int getRingEnd() { return this.ringEnd; } /** * Set the ring end position of the basetype * @param pos the ring end position to set */ public void setRingEnd(int pos) { this.ringEnd = pos; } public void setRingtype(Ringtype type) throws MonosaccharideException { if(type.equals(Ringtype.PYRANOSE)) { setRingEnd(getRingStart() + 4); } else if(type.equals(Ringtype.FURANOSE)) { setRingEnd(getRingStart() + 3); } else if(type.equals(Ringtype.OPEN)) { setRingStartNoAdjustment(Basetype.OPEN_CHAIN); setRingEnd(Basetype.OPEN_CHAIN); } else if(type.equals(Ringtype.UNKNOWN)) { setRingStartNoAdjustment(Basetype.UNKNOWN_RING); setRingEnd(Basetype.UNKNOWN_RING); } } public Ringtype getRingtype() { if(getRingEnd() == Basetype.OPEN_CHAIN) { return(Ringtype.OPEN); } if(getRingStart() <= 0 || getRingEnd() == Basetype.UNKNOWN_RING) { return(Ringtype.UNKNOWN); } if(getRingEnd() - getRingStart() == 4) { return(Ringtype.PYRANOSE); } if(getRingEnd() - getRingStart() == 3) { return(Ringtype.FURANOSE); } return(Ringtype.UNKNOWN); } /** * Get the CarbBank symbol for this basetype's ring type * @return the ring type symbol to be used in CarbBank style names */ public String getRingtypeSymbol() { return(getRingtype().getCarbbankSymbol()); } /** * Set the ring start and end in one step * @param start the ring start postition * @param end the ring end position */ public void setRingClosure(int start, int end) { this.ringStart = start; this.ringEnd = end; } /** * Check, if this basetype is an alditol. * @return true, if this basetype has an alditol core modification at position 1 */ public boolean isAlditol() { return this.hasCoreModification(CoreModificationTemplate.ALDITOL, 1); } /** * Set / unset an alditol modification for this basetype * @param alditol flag to indicate whether to set (true) or unset (false) the alditol modification * @throws MonosaccharideException in case the ring oxygen is > 0 (i.e. monosaccharide is ring form, which excludes alditol) */ public void setAlditol(boolean alditol) throws MonosaccharideException { if(alditol) { if(this.getRingEnd() > 0) { throw new MonosaccharideException("Monosaccharide cannot be an alditol if it is in ring form"); } this.setRingEnd(Basetype.OPEN_CHAIN); this.setRingStart(Basetype.OPEN_CHAIN); if(!this.hasCoreModification(CoreModificationTemplate.ALDITOL, 1)) { this.addCoreModification(new CoreModification(CoreModificationTemplate.ALDITOL, 1)); } } else { if(this.hasCoreModification(CoreModificationTemplate.ALDITOL, 1)) { this.deleteCoreModification(CoreModificationTemplate.ALDITOL, 1); } } } /** * Get the Boolean object that marks whether this basetype is a superclass or not. * @return the isSuperclass */ public Boolean getIsSuperclassFlag() { return this.isSuperclassFlag; } /** * Get the booleanValue of the isSuperclass object. * @return */ public boolean isSuperclass() { return this.isSuperclassFlag.booleanValue(); } /** * @param isSuperclass the isSuperclass to set */ public void setIsSuperclassFlag(Boolean isSuperclass) { this.isSuperclassFlag = isSuperclass; } public void setIsSuperclass(boolean flag) { this.isSuperclassFlag = new Boolean(flag); } public void checkIsSuperclass() { this.setIsSuperclass(Stereocode.getChiralOnlyStereoString(this.getStereoStr()).length() == 0); } /** * @return the superclass */ public String getSuperclass() { return this.superclass; } /** * @param superclass the superclass to set */ public void setSuperclass(String superclass) { this.superclass = superclass; } //***************************************************************************** //*** stereocode related methods: ********************************************* //***************************************************************************** /** * Get the stereocode of the monosaccharide * @return the stereocode */ public Stereocode getStereocode() { return this.stereocode; } /** * Set the stereocode of the monosaccharide * @param stereocode the stereocode to set */ public void setStereocode(Stereocode stereocode) { this.stereocode = stereocode; } /** * Get the stereocode string of the monosaccharide * @return the string value of the stereocode */ public String getStereoStr() { if(getStereocode() != null) { return(getStereocode().getStereoStr()); } return(""); } /** * Set the string value of the stereocode of the monosaccharide * @param stereoStr the stereo string to set */ public void setStereoStr(String stereoStr) { if(getStereocode() == null) { setStereocode(new Stereocode(stereoStr)); } else { getStereocode().setStereoStr(stereoStr); } } public String getStereoStrWithoutAnomeric() throws ResourcesDbException { String stereo = this.getStereoStr(); if(this.getRingStart() > 0 && this.getRingStart() < stereo.length()) { stereo = Stereocode.maskAnomerInStereoString(stereo, this); } return stereo; } /** * Set the stereochemistry resulting from the anomer in the stereocode */ public void setAnomerInStereocode() throws ResourcesDbException { String stereo = getStereoStr(); int anomerPosition = getRingStart(); if(anomerPosition >= 1) { String stereosymbolAnomer = getAnomer().getStereosymbolD(); StereoConfiguration refConf = getAnomericReferenceConfiguration(); if(refConf.equals(StereoConfiguration.Laevus)) { stereosymbolAnomer = Stereocode.changeDLinStereoString(stereosymbolAnomer); } else if(refConf.equals(StereoConfiguration.XLaevus)) { stereosymbolAnomer = Stereocode.changeDLinStereoString(stereosymbolAnomer); stereosymbolAnomer = Stereocode.absoluteToRelative(stereosymbolAnomer); } else if(refConf.equals(StereoConfiguration.XDexter)) { stereosymbolAnomer = Stereocode.absoluteToRelative(stereosymbolAnomer); } stereo = stereo.substring(0, anomerPosition - 1) + stereosymbolAnomer + stereo.substring(anomerPosition); } setStereoStr(stereo); } /** * Get the configuration that to which the anomeric center has to be compared to decide whether a monosaccharide is alpha or beta * @return the configuration of the anomeric reference atom * @throws MonosaccharideException */ public StereoConfiguration getAnomericReferenceConfiguration() throws ResourcesDbException { String stereo = getStereoStr(); int anomerPosition = getRingStart(); if(anomerPosition >= 1) { stereo = Stereocode.setPositionInStereoString(stereo, Stereocode.StereoN, anomerPosition); } stereo = Stereocode.getChiralOnlyStereoString(stereo); StereoConfiguration conf; if(stereo.length() > 4) { conf = StereoConfiguration.forStereosymbol(stereo.charAt(3)); } else if(stereo.length() == 0) { conf = StereoConfiguration.Unknown; } else { conf = StereoConfiguration.forStereosymbol(stereo.charAt(stereo.length() - 1)); } return(conf); } /** * Determine this basetype's anomer using the stereocode * @return the resulting anomer * @throws ResourcesDBException */ public Anomer getAnomerFromStereocode() throws ResourcesDbException { if(this.getRingtype().equals(Ringtype.OPEN)) { return Anomer.OPEN_CHAIN; } if(this.getRingStart() > 0 && this.isStereolossPositionWithIgnoreType(this.getRingStart(), CoreModificationTemplate.KETO)) { return Anomer.NONE; } StereoConfiguration anomRefConf = this.getAnomericReferenceConfiguration(); StereoConfiguration anomConf = Stereocode.getPositionFromStereoString(this.getStereoStr(), this.getRingStart()); if(anomRefConf.equals(StereoConfiguration.Dexter)) { if(anomConf.equals(StereoConfiguration.Dexter)) { return Anomer.ALPHA; } if(anomConf.equals(StereoConfiguration.Laevus)) { return Anomer.BETA; } } if(anomRefConf.equals(StereoConfiguration.Laevus)) { if(anomConf.equals(StereoConfiguration.Laevus)) { return Anomer.ALPHA; } if(anomConf.equals(StereoConfiguration.Dexter)) { return Anomer.BETA; } } if(anomRefConf.equals(StereoConfiguration.XDexter)) { if(anomConf.equals(StereoConfiguration.XDexter)) { return Anomer.ALPHA; } if(anomConf.equals(StereoConfiguration.XLaevus)) { return Anomer.BETA; } } if(anomRefConf.equals(StereoConfiguration.XLaevus)) { if(anomConf.equals(StereoConfiguration.XLaevus)) { return Anomer.ALPHA; } if(anomConf.equals(StereoConfiguration.XDexter)) { return Anomer.BETA; } } return Anomer.UNKNOWN; } public StereoConfiguration getStereoConfigurationByPosition(int pos) throws ResourcesDbException { if(this.getStereoStr().length() >= pos) { return StereoConfiguration.forStereosymbol(this.getStereoStr().charAt(pos - 1)); } return StereoConfiguration.Unknown; } //***************************************************************************** //*** modification related methods: ******************************************* //***************************************************************************** /** * Get this basetype's core modifications * @return a list of core modifications */ public List<CoreModification> getCoreModifications() { if(this.coreModifications == null) { this.coreModifications = new ArrayList<CoreModification>(); } return this.coreModifications; } /** * Set this basetype's core modifications * @param coreModList the list of core modifications to set */ public void setCoreModifications(List<CoreModification> coreModList) { this.coreModifications = coreModList; sortCoreModifications(); } /** * Check, if the position of a modification is valid * @param mod The modification to be checked * @return true, if modification is at valid position(s); false, if modification is already present * @throws MonosaccharideException (in case the modification is at an invalid position) */ public boolean checkModificationPosition(CoreModification mod) throws MonosaccharideException { ArrayList<Integer> positionsList = mod.getPosition1Clone(); positionsList.addAll(mod.getPosition2()); Collections.sort(positionsList); for(int i = 0; i < positionsList.size(); i++) { int position = positionsList.get(i).intValue(); if(position < 0 || position > getSize()) { throw new MonosaccharideException("Modification position out of range: " + mod.toString()); } if(position > 0) { if(position == getRingStart()) { /*if(mod.getTemplate().equals(CoreModificationTemplate.Ulo)) { return(false); }*/ //*** "acid" is allowed for open chain residues, other modifications are not allowed at carbonyl position *** //TODO: check rules for modifications (e.g. EN is allowed) if(!this.getRingtype().equals(Ringtype.OPEN) && !(mod.getTemplate().equals(CoreModificationTemplate.ACID) && this.getRingStart() == 1) && !(mod.getTemplate().equals(CoreModificationTemplate.KETO)) && !(mod.getTemplate().equals(CoreModificationTemplate.ENX)) && !(mod.getTemplate().equals(CoreModificationTemplate.EN)) && !(mod.getTemplate().equals(CoreModificationTemplate.DEOXY))) { throw new MonosaccharideException("Cannot modify monosaccharide at carbonyl position (" + getRingStart() + ")."); } } ArrayList<CoreModification> posMods = getCoreModificationsByPosition(position); //*** list of modifications, that are already present at the position *** for(CoreModification exstMod : posMods) { if(mod.equals(exstMod)) { //*** modification already present *** System.out.println("checkModificationPosition(): modification already present"); return false; } if(!mod.isSubstitutable() && !exstMod.isSubstitutable()) { throw new MonosaccharideException("Position already modified: " + exstMod.toString() + "\n Cannot add modification " + mod.toString()); } } } } return(true); } /** * Add a modification to this monosaccharide * @param mod The modification to be added * @return true, if modification was added; false, if modification is already present * @throws MonosaccharideException (in case the modification is at an invalid position) */ public boolean addCoreModification(CoreModification mod) throws MonosaccharideException { if(this.hasCoreModification(mod)) { return false; } int pos = mod.getIntValuePosition1(); /*if(pos > 0 && pos == this.getCarbonylPosition()) { if(mod.getTemplate().equals(CoreModificationTemplate.Acid)) { if(this.getRingOxygen() > 0) { throw new MonosaccharideException("Cannot add 'Acid' modification to carbonyl position in ring monosaccharide."); } this.setRingOxygen(-1); } }*/ this.coreModifications.add(mod); if(pos > 0 && mod.getTemplate().equals(CoreModificationTemplate.KETO)) { if(this.getRingStart() == 1) { if(pos != 1 && this.getCoreModification(CoreModificationTemplate.KETO.getName(), 1) == null) { setRingStart(pos); } } else if(this.getRingStart() < 1) { //TODO: check, if and how carbonyl position has to be adjusted } } sortCoreModifications(); return true; } /** * Add a modification to this monosaccharide * @param modTmpl The CoreModificationTemplate of the modification to be added * @param position The position of the modification to be added * @return true, if modification was added; false, if modification is already present * @throws MonosaccharideException (in case the modification is at an invalid position) */ public boolean addCoreModification(CoreModificationTemplate modTmpl, int position) throws MonosaccharideException { return this.addCoreModification(new CoreModification(modTmpl, position)); } public void deleteCoreModification(CoreModificationTemplate modTmpl, int position) throws MonosaccharideException { this.deleteCoreModification(modTmpl.getName(), position); } public void deleteCoreModification(String name, int position) throws MonosaccharideException { List<CoreModification> modList = getCoreModifications(); for(int i = 0; i < modList.size(); i++) { CoreModification mod = modList.get(i); if(mod.getName().equals(name) && mod.getPosition1().get(0).intValue() == position) { modList.remove(i); return; } } throw new MonosaccharideException("Cannot remove CoreModification " + position + name + ": modification not found"); } public void deleteCoreModification(CoreModification mod) throws MonosaccharideException { List<CoreModification> modList = getCoreModifications(); for(int i = 0; i < modList.size(); i++) { CoreModification presentMod = modList.get(i); if(presentMod.equals(mod)) { modList.remove(i); return; } } throw new MonosaccharideException("Cannot remove CoreModification: modification not found (" + mod.toString() + ")"); } public void initCoreModifications() { if(getCoreModifications() == null) { this.coreModifications = new ArrayList<CoreModification>(); } getCoreModifications().clear(); } /** * Get the number of core modifications * @return the core modification count */ public int countCoreModifications() { return(getCoreModifications().size()); } /** * Get the number of core modifications of a given type (identified by mod. name) * @param name the name of the core modification * @return the core modification count */ public int countCoreModifications(String name) { int count = 0; List<CoreModification> modifications = getCoreModifications(); for(int i = 0; i < modifications.size(); i++) { CoreModification mod = modifications.get(i); if(mod.getName().equals(name)) { count ++; } } return count; } /** * Get the number of core modifications of a given type (identified by mod. template) * @param tmpl the core modification template * @return the core modification count */ public int countCoreModifications(CoreModificationTemplate tmpl) { int count = 0; List<CoreModification> modifications = getCoreModifications(); for(int i = 0; i < modifications.size(); i++) { CoreModification mod = modifications.get(i); if(mod.getTemplate().equals(tmpl)) { count ++; } } return count; } public void setUronic() throws MonosaccharideException { CoreModification mod = new CoreModification(CoreModificationTemplate.ACID, getSize()); addCoreModification(mod); } public boolean isUronic() { return(!this.hasCoreModification(CoreModificationTemplate.ACID, 1) && this.hasCoreModification(CoreModificationTemplate.ACID, getSize())); } public void setAldonic() throws MonosaccharideException { CoreModification mod = new CoreModification(CoreModificationTemplate.ACID, 1); addCoreModification(mod); } public boolean isAldonic() { return(this.hasCoreModification(CoreModificationTemplate.ACID, 1) && !this.hasCoreModification(CoreModificationTemplate.ACID, getSize())); } public void setAldaric() throws MonosaccharideException { setAldonic(); setUronic(); } public boolean isAldaric() { return (this.hasCoreModification(CoreModificationTemplate.ACID, 1) && this.hasCoreModification(CoreModificationTemplate.ACID, getSize())); } /** * Check, if this basetype has a double bond at a given position * @param pos the position to check * @return true, if there is an EN or ENX core modification present at the given position */ public boolean hasDoubleBond(int pos) { return (this.hasCoreModification(CoreModificationTemplate.EN, pos) || this.hasCoreModification(CoreModificationTemplate.ENX, pos)); } /** * Get a list of all positions that have a modification which results in a loss of stereochemistry * @return list of achiral positions (empty list, if no hits are found) */ public ArrayList<Integer> getStereolossPositions() { List<CoreModification> modifications = getCoreModifications(); ArrayList<Integer> positions = new ArrayList<Integer>(); for(int i = 0; i < modifications.size(); i++) { CoreModification mod = modifications.get(i); if(mod.getTemplate().isStereoLoss()) { ArrayList<Integer> modPositions = mod.getPositions(); for(int p = 0; p < modPositions.size(); p++) { Integer pos = modPositions.get(p); if(!positions.contains(pos)) { positions.add(pos); } } } } Collections.sort(positions); return(positions); } /** * Check, if there is a loss of stereochemistry at a given position (e.g. due to deoxygenation, double bonds, etc.) * @param pos the position to check * @return true, if the position has a loss of stereochemistry, otherwise false */ public boolean isStereolossPosition(int pos) { ArrayList<CoreModification> modifications = getCoreModificationsByPosition(pos); for(CoreModification mod : modifications) { if(mod.getTemplate().isStereoLoss()) { return(true); } } return(false); } /** * Check, if there is a loss of stereochemistry at a given position (e.g. due to deoxygenation, double bonds, etc.) * that is not caused by the modification type specified in the "ignore" parameter. * @param pos the position to check * @param ignore the modification type to ignore * @return true, if the position has a loss of stereochemistry (not caused by the ignored modification type), otherwise false */ public boolean isStereolossPositionWithIgnoreType(int pos, CoreModificationTemplate ignore) { ArrayList<CoreModification> modifications = getCoreModificationsByPosition(pos); for(CoreModification mod : modifications) { if(mod.getTemplate().equals(ignore)) { continue; } if(mod.getTemplate().isStereoLoss()) { return(true); } } return(false); } /** * Get a list of all core modifications that are present at a given position * @param position the position for which the core modifications are listed * @return list of core modifications (empty list, if no hits are found) */ public ArrayList<CoreModification> getCoreModificationsByPosition(int position) { ArrayList<CoreModification> positionMods = new ArrayList<CoreModification>(); Integer positionInt = new Integer(position); for(CoreModification mod : getCoreModifications()) { if(mod.getPositions().contains(positionInt)) { positionMods.add(mod); } } return(positionMods); } /** * Get a list of all core modification types that are present at a given position * @param position the position for which the core modification types are listed * @return list of core modification templates (empty list, if no hits are found) */ public ArrayList<CoreModificationTemplate> getCoreModificationTemplatesByPosition(int position) { ArrayList<CoreModificationTemplate> templateList = new ArrayList<CoreModificationTemplate>(); for(CoreModification mod : this.getCoreModificationsByPosition(position)) { templateList.add(mod.getTemplate()); } return(templateList); } /** * Get a list of all core modifications of a given type (identified by type name) * @param name the name of the core modification * @return list of core modifications (empty list, if no hits are found) */ public ArrayList<CoreModification> getCoreModifications(String name) { ArrayList<CoreModification> modList = new ArrayList<CoreModification>(); for(int i = 0; i < getCoreModifications().size(); i++) { if(getCoreModifications().get(i).getName().equalsIgnoreCase(name)) { modList.add(getCoreModifications().get(i)); } } return(modList); } /** * Get a list of all core modifications of a given type (identified by type template) * @param tmpl the core modification template * @return list of core modifications (empty list, if no hits are found) */ public ArrayList<CoreModification> getCoreModifications(CoreModificationTemplate tmpl) { ArrayList<CoreModification> modList = new ArrayList<CoreModification>(); for(int i = 0; i < getCoreModifications().size(); i++) { if(getCoreModifications().get(i).getTemplate().equals(tmpl)) { modList.add(getCoreModifications().get(i)); } } return(modList); } /** * Get a core modification specified by name and position * Note: If more than one modification matches the given criteria (e.g. due to uncertain positions) the first match is returned. * @param name the name of the core modification * @param position the position of the core modification * @return the core modification or null if no modification matching the given criteria is present */ public CoreModification getCoreModification(String name, int position) { ArrayList<CoreModification> modList = getCoreModificationsByPosition(position); for(int i = 0; i < modList.size(); i++) { if(modList.get(i).getName().equals(name)) { return(modList.get(i)); } } return(null); } public CoreModification getCoreModification(CoreModificationTemplate tmpl, int position) { ArrayList<CoreModification> modList = getCoreModificationsByPosition(position); for(int i = 0; i < modList.size(); i++) { if(modList.get(i).getTemplate().equals(tmpl)) { return(modList.get(i)); } } return(null); } /** * Get a list of EN/ENX core modifications present in this basetype * @return */ public ArrayList<CoreModification> getEnModifications() { ArrayList<CoreModification> enList = new ArrayList<CoreModification>(); for(CoreModification mod : this.getCoreModifications()) { if(mod.getTemplate().equals(CoreModificationTemplate.EN)) { enList.add(mod); } else if(mod.getTemplate().equals(CoreModificationTemplate.ENX)) { enList.add(mod); } } return enList; } /** * Test, if a given core modification is present in the basetype. * @param mod: The core modification to be checked for. * @return true, if the core modification is present, otherwise false. */ public boolean hasCoreModification(CoreModification mod) { for(int i = 0; i < getCoreModifications().size(); i++) { if(getCoreModifications().get(i).equals(mod)) { return(true); } } return(false); } /** * Test, if a given core modification is present in the basetype. * @param tmpl: the core modification template * @param position: the position of the core modification * @return true, if the core modification is present, otherwise false. */ public boolean hasCoreModification(CoreModificationTemplate tmpl, int position) { try { CoreModification coreMod = new CoreModification(tmpl, position); return(hasCoreModification(coreMod)); } catch(MonosaccharideException me) { return(false); } } /** * Test, if a core modification of a certain type is present in the basetype (at any position) * @param tmpl the CoreModificationTemplate indicating the modification type * @return true, if such a modification is present, otherwise false */ public boolean hasCoreModification(CoreModificationTemplate tmpl) { for(CoreModification mod : this.getCoreModifications()) { if(mod.getName().equals(tmpl.getName())) { return true; } } return false; } /** * Check, whether the monosaccharide contains a core modification that is located at an uncertain position * @return */ public boolean hasUncertainCoremodificationPosition() { for(CoreModification mod : getCoreModifications()) { if(mod.hasUncertainLinkagePosition()) { return(true); } } return(false); } /** * Adjust the positions of core modifications resulting from a rotation of the monosaccharide by 180 degrees * Such a rotation might occurr with alditol residues or aldaric acids. */ public void mirrorCoreModificationPositions() { List<CoreModification> modList = getCoreModifications(); for(int m = 0; m < modList.size(); m++) { CoreModification mod = modList.get(m); if(mod.getTemplate().equals(CoreModificationTemplate.ALDITOL)) { continue; //*** alditol modification remains at position 1 when a basetype is mirrored *** } ArrayList<Integer> positions1 = mod.getPosition1(); for(int i = 0; i < positions1.size(); i++) { int pos = positions1.get(i); if(pos > 0) { pos = getSize() + 1 - pos; positions1.set(i, new Integer(pos)); } } //Collections.sort(positions1); ArrayList<Integer> positions2 = mod.getPosition2(); for(int i = 0; i < positions2.size(); i++) { int pos = positions2.get(i); if(pos > 0) { pos = getSize() + 1 - pos; positions2.set(i, new Integer(pos)); } } //Collections.sort(positions2); mod.setPosition1(positions1); mod.setPosition2(positions2); mod.sortPositions(); } //Collections.sort(modList, new Modification()); sortCoreModifications(); } public void sortCoreModifications() { List<CoreModification> modList = this.getCoreModifications(); for(int i = 0; i < modList.size(); i++) { for(int j = modList.size() - 1; j > 0; j--) { if(modList.get(j).makeCmpString().compareTo(modList.get(j - 1).makeCmpString()) < 0) { CoreModification tmpMod = modList.get(j - 1); modList.set(j - 1, modList.get(j)); modList.set(j, tmpMod); } } } } //***************************************************************************** //*** atom related methods: *************************************************** //***************************************************************************** /** * Generate the atoms of this basetype using the global config. * @throws MonosaccharideException */ public void buildAtoms() throws ResourcesDbException { this.buildAtoms(Config.getGlobalConfig()); } /** * Generate the atoms of this basetype * @param conf the config. to be used for building the atoms * @throws MonosaccharideException */ public void buildAtoms(Config conf) throws ResourcesDbException { this.setAtoms(new ArrayList<Atom>()); Atom anomericCarbon = null; Atom previousCarbon = null; for(int pos = 1; pos <= this.getSize(); pos++) { int hCount = 1; if(pos == 1 || pos == this.getSize()) { hCount ++; } boolean hasOH = true; boolean isAcid = false; boolean isUlo = false; double boToPreviousCarbon = 1.0; Atom currentCarbon = new Atom(Periodic.C); if(pos == this.getRingStart() && !this.isAlditol()) { hCount --; if(this.getRingEnd() > 0) { anomericCarbon = currentCarbon; } else { hasOH = false; isUlo = true; } } //*** check core modifications: *** for(CoreModification mod : this.getCoreModificationsByPosition(pos)) { if(mod.getIntValuePosition1() == 0) { throw new MonosaccharideException("Cannot build atoms for basetype with uncertain core modification position."); } if(mod.getTemplate().equals(CoreModificationTemplate.EN)) { hCount--; if(mod.getIntValuePosition2() == pos) { boToPreviousCarbon = 2; } continue; } if(mod.getTemplate().equals(CoreModificationTemplate.ENX)) { throw new MonosaccharideException("Cannot build atoms for basetype with Enx core modification."); } if(mod.getTemplate().equals(CoreModificationTemplate.YN)) { hCount -= 2; if(mod.getIntValuePosition2() == pos) { boToPreviousCarbon = 3; } continue; } if(mod.getTemplate().equals(CoreModificationTemplate.DEOXY)) { hCount++; hasOH = false; continue; } if(mod.getTemplate().equals(CoreModificationTemplate.SP2)) { hCount--; continue; } if(mod.getTemplate().equals(CoreModificationTemplate.SP)) { hCount -= 2; continue; } if(mod.getTemplate().equals(CoreModificationTemplate.ACID)) { isAcid = true; hasOH = false; hCount -= 2; continue; } if(mod.getTemplate().equals(CoreModificationTemplate.KETO) && !(this.getRingStart() == pos)) { hCount --; hasOH = false; isUlo = true; } } //*** build atoms for current position: *** if(isAcid) { if(conf.isCarboxylGroupsDeprotonated()) { currentCarbon.setTemplateAndInit(AtomTemplate.COOH_C_OX); } else { currentCarbon.setTemplateAndInit(AtomTemplate.COOH_C_OH); } } else { currentCarbon.setTemplateAndInit(AtomTemplate.BB_C); } currentCarbon.setName(currentCarbon.getTemplate().formatAtomName(pos)); if(pos == 1) { this.addAtom(currentCarbon); } else { this.addAtom(currentCarbon, previousCarbon, boToPreviousCarbon); } if(hasOH) { if(pos == this.getRingEnd()) { Atom ringO = new Atom(Periodic.O); ringO.setTemplateAndInit(AtomTemplate.BB_OR); ringO.setName(ringO.getTemplate().formatAtomName(pos)); this.addAtom(ringO, currentCarbon, 1); this.addBond(ringO, anomericCarbon, 1); } else { Atom ohO = new Atom(Periodic.O); ohO.setTemplateAndInit(AtomTemplate.BB_O); ohO.setName(ohO.getTemplate().formatAtomName(pos)); this.addAtom(ohO, currentCarbon, 1); Atom ohH = new Atom(Periodic.H); ohH.setTemplateAndInit(AtomTemplate.BB_HO); ohH.setName(ohH.getTemplate().formatAtomName(pos)); this.addAtom(ohH, ohO, 1); } } if(isUlo) { Atom oc = new Atom(Periodic.O); oc.setTemplateAndInit(AtomTemplate.BB_O); oc.setName(oc.getTemplate().formatAtomName(pos)); this.addAtom(oc, currentCarbon, 2); } if(isAcid) { if(conf.isCarboxylGroupsDeprotonated()) { Atom oa = new Atom(Periodic.O); oa.setTemplateAndInit(AtomTemplate.COOH_OX); oa.setName(oa.getTemplate().formatAtomName(pos, 0)); this.addAtom(oa, currentCarbon, 1.5); Atom ob = new Atom(Periodic.O); ob.setTemplateAndInit(AtomTemplate.COOH_OX); ob.setName(oa.getTemplate().formatAtomName(pos, 1)); this.addAtom(ob, currentCarbon, 1.5); } else { Atom o = new Atom(Periodic.O); o.setTemplateAndInit(AtomTemplate.COOH_O); o.setName(o.getTemplate().formatAtomName(pos)); this.addAtom(o, currentCarbon, 2); Atom ohO = new Atom(Periodic.O); ohO.setTemplateAndInit(AtomTemplate.COOH_OH); ohO.setName(ohO.getTemplate().formatAtomName(pos)); this.addAtom(ohO, currentCarbon, 1); Atom ohH = new Atom(Periodic.H); ohH.setTemplateAndInit(AtomTemplate.COOH_H); ohH.setName(ohH.getTemplate().formatAtomName(pos)); this.addAtom(ohH, ohO, 1); } } if(hCount == 1) { Atom hn = new Atom(Periodic.H); hn.setTemplateAndInit(AtomTemplate.BB_H); hn.setName(hn.getTemplate().formatAtomName(pos)); this.addAtom(hn, currentCarbon, 1); } else { //char indexChar = 'a'; for(int h = 0; h < hCount; h++) { Atom hn = new Atom(Periodic.H); hn.setTemplateAndInit(AtomTemplate.BB_HX); //hn.setName("H" + pos + indexChar++); hn.setName(hn.getTemplate().formatAtomName(pos, h)); this.addAtom(hn, currentCarbon, 1); } } previousCarbon = currentCarbon; } } public Atom getLinkingAtom(int position, LinkageType linktype) throws ResourcesDbException { Atom linkAtom = null; Atom removeAtom = null; if(linktype.equals(LinkageType.H_AT_OH)) { linkAtom = this.getAtomByName(AtomTemplate.BB_O.formatAtomName(position)); removeAtom = this.getAtomByName(AtomTemplate.BB_HO.formatAtomName(position)); } else if(linktype.equals(LinkageType.DEOXY)) { linkAtom = this.getAtomByName(AtomTemplate.BB_C.formatAtomName(position)); removeAtom = this.getAtomByName(AtomTemplate.BB_O.formatAtomName(position)); } else if(linktype.equals(LinkageType.H_LOSE)) { linkAtom = this.getAtomByName(AtomTemplate.BB_C.formatAtomName(position)); removeAtom = this.getAtomByName(AtomTemplate.BB_H.formatAtomName(position)); } if(linkAtom == null) { throw new MonosaccharideException("Cannot get linking atom for " + linktype.name() + " linkage at position " + position); } if(removeAtom == null) { throw new MonosaccharideException("Cannot get atom to be removed for " + linktype.name() + " linkage at position " + position); } return linkAtom; } //***************************************************************************** //*** composition related methods: ******************************************** //***************************************************************************** public void buildComposition() throws ResourcesDbException { this.buildComposition(Config.getGlobalConfig()); } public void buildComposition(Config conf) throws ResourcesDbException { Composition compo = new Composition(); //*** 1. set standard basetype composition: CnH2nOn *** compo.increaseCount(Periodic.C, this.getSize()); compo.increaseCount(Periodic.H, 2 * this.getSize()); compo.increaseCount(Periodic.O, this.getSize()); //*** 2. add changes caused by core modifications: *** for(CoreModification mod : this.getCoreModifications()) { if(mod.getTemplate().equals(CoreModificationTemplate.ENX)) { throw new MonosaccharideException("Cannot build composition for basetype with enx core modification"); } compo.addComposition(mod.getTemplate().getCompositionChanges()); if(mod.getTemplate().equals(CoreModificationTemplate.ACID)) { if(mod.getIntValuePosition1() == 0) { throw new MonosaccharideException("Cannot build composition for basetype with unknown acid position"); } else if(this.getRingStart() == 0 && mod.getIntValuePosition1() == 1) { throw new MonosaccharideException("Cannot build composition for basetype with unknown carbonyl position and acid modification at position 1"); } if(mod.getIntValuePosition1() != this.getRingStart()) { compo.decreaseCount(Periodic.H, 2); } if(conf.isCarboxylGroupsDeprotonated()) { compo.decreaseCount(Periodic.H, 1); } } if(mod.getTemplate().equals(CoreModificationTemplate.LACTONE) && conf.isCarboxylGroupsDeprotonated()) { compo.increaseCount(Periodic.H, 1); } //*** check for keto group at position > 1: if no keto group at position 1 and not alditol, add alditol composition change (C1 of a ketose has the same composition as an alditol C1) *** if(mod.getTemplate().equals(CoreModificationTemplate.KETO) && mod.getIntValuePosition1() > 1) { if(!this.hasCoreModification(CoreModificationTemplate.KETO, 1) && !this.hasCoreModification(CoreModificationTemplate.ALDITOL, 1)) { compo.addComposition(CoreModificationTemplate.ALDITOL.getCompositionChanges()); } } } this.setComposition(compo); } //***************************************************************************** //*** notation related methods: *********************************************** //***************************************************************************** public void buildName() throws ResourcesDbException { GlycoCTExporter exporter = new GlycoCTExporter(GlycanNamescheme.MONOSACCHARIDEDB, this.getConfig(), this.getTemplateContainer()); Monosaccharide ms = new Monosaccharide(this); this.setName(exporter.export(ms)); } //***************************************************************************** //*** methods related to BuilderGroups: *************************************** //***************************************************************************** /** * Set the stereo and modification properties of a Basetype using an extended Stereocode String. * @param extStereoStr the stereostring to parse * @param bt the basetype, the properties of which are to be set. Size and ring properties of bt have to be set before calling this method! * @throws ResourcesDbException in case an the extStereoStr parameter cannot be parsed or does not match the size or ring properties of the basetye */ public static void buildBasetypeByExtendedStereocode(String extStereoStr, Basetype bt) throws ResourcesDbException { Utils.setTemplateDataIfNotSet(Config.getGlobalConfig()); if(bt == null) { throw new MonosaccharideException("Basetype must not be null in buildBasetypeByExtendedStereocode()."); } if(extStereoStr == null) { throw new MonosaccharideException("StereoString must not be null in buildBasetypeByExtendedStereocode()."); } if(extStereoStr.length() != bt.getSize()) { throw new MonosaccharideException("StereoString length (" + extStereoStr.length() + ") doesn't match basetype size (" + bt.size + ")"); } //*** translate extended stereocode into BasetypeBuilderGroups: *** ArrayList<BasetypeBuilderGroup> groupList = new ArrayList<BasetypeBuilderGroup>(bt.getSize()); for(int pos = 1; pos <= bt.getSize(); pos ++) { char posSymbol = extStereoStr.charAt(pos - 1); BasetypeBuilderGroup buildergroup = BasetypeBuilderGroup.forExtStereoSymbol(posSymbol); if(buildergroup == null) { throw new MonosaccharideException("Unknown stereocode symbol: '" + posSymbol + "'"); } //*** check, if builder group is valid at current position: *** if(pos == 1 && bt.getRingStart() != 1) { if(!buildergroup.isHeadTail()) { throw new MonosaccharideException("Stereocode symbol '" + posSymbol + "' is not allowed at position " + pos + " (only valid for non-terminal exocyclic positions)"); } } else if(pos == bt.getSize() && bt.getRingEnd() < pos) { if(!buildergroup.isHeadTail()) { throw new MonosaccharideException("Stereocode symbol '" + posSymbol + "' is not allowed at position " + pos + " (only valid for non-terminal exocyclic positions)"); } } else if(buildergroup.isHeadTail() && pos < bt.getSize() && pos > 1) { throw new MonosaccharideException("Stereocode symbol '" + posSymbol + "' is not allowed at position " + pos + " (only valid for terminal positions)"); } groupList.add(buildergroup); } //*** build basetype from BasetypeBuilderGroups: *** String stereo = ""; for(int pos = 1; pos <= bt.getSize(); pos ++) { BasetypeBuilderGroup buildergroup = groupList.get(pos - 1); //*** check for explicit carbonyl group at ring start: *** if(pos == bt.getRingStart()) { if(buildergroup.equals(BasetypeBuilderGroup.KETO)) { throw new MonosaccharideException("KETO modification not allowed at ring start position. Select D or L configuration to indicate orientation of OH group at anomeric center."); } if(buildergroup.equals(BasetypeBuilderGroup.CHO)) { throw new MonosaccharideException("Carbonyl group not allowed at ring start position. Select D or L configuration to indicate orientation of OH group at anomeric center."); } } //*** check for alditol: *** if(pos == 1) { if(buildergroup.equals(BasetypeBuilderGroup.CH3) || buildergroup.equals(BasetypeBuilderGroup.H2COH)) { if(bt.getRingEnd() == Basetype.OPEN_CHAIN) { if(!BasetypeBuilderGroup.hasCoreModification(groupList, CoreModificationTemplate.KETO)) { bt.addCoreModification(CoreModificationTemplate.ALDITOL, 1); } } } } //*** build position: *** stereo += buildergroup.getStereoSymbol(); for(CoreModificationTemplate modTmpl : buildergroup.getCoreMods()) { if(modTmpl.equals(CoreModificationTemplate.EN)) { CoreModification existEn = bt.getCoreModification(CoreModificationTemplate.EN, pos -1); if(existEn != null && existEn.getIntValuePosition1() == pos - 1) { continue; //*** current position is second position of an en modification, mod already added at previous position *** } if(pos < bt.getSize()) { char nextPosSymbol = extStereoStr.charAt(pos); BasetypeBuilderGroup nextPosGroup = BasetypeBuilderGroup.forExtStereoSymbol(nextPosSymbol); if(nextPosGroup.getCoreMods() != null && nextPosGroup.getCoreMods().contains(CoreModificationTemplate.EN)) { CoreModification mod = new CoreModification(modTmpl, pos, pos + 1); bt.addCoreModification(mod); continue; } } throw new MonosaccharideException("Position " + pos + " is single EN modification position (must be followed or preceeded by another EN modification position)"); } else if(modTmpl.equals(CoreModificationTemplate.YN)) { CoreModification existYn = bt.getCoreModification(CoreModificationTemplate.YN, pos -1); if(existYn != null && existYn.getIntValuePosition1() == pos - 1) { continue; //*** current position is second position of an yn modification, mod already added at previous position *** } if(pos < bt.getSize()) { char nextPosSymbol = extStereoStr.charAt(pos); BasetypeBuilderGroup nextPosGroup = BasetypeBuilderGroup.forExtStereoSymbol(nextPosSymbol); if(nextPosGroup.getCoreMods() != null && nextPosGroup.getCoreMods().contains(CoreModificationTemplate.YN)) { CoreModification mod = new CoreModification(modTmpl, pos, pos + 1); bt.addCoreModification(mod); continue; } } throw new MonosaccharideException("Position " + pos + " is single EN modification position (must be followed or preceeded by another EN modification position)"); } else { if(modTmpl.equals(CoreModificationTemplate.KETO) && pos > bt.getRingStart() && bt.getRingStart() > 0) { if(!bt.hasCoreModification(CoreModificationTemplate.KETO, bt.getRingStart())) { bt.addCoreModification(CoreModificationTemplate.KETO, bt.getRingStart()); } } bt.addCoreModification(modTmpl, pos); } } } bt.setStereoStr(stereo); bt.setConfiguration(Stereocode.getConfigurationFromStereoString(stereo)); bt.setAnomer(bt.getAnomerFromStereocode()); if(bt.getRingStart() > 1) { if(!bt.hasCoreModification(CoreModificationTemplate.KETO, bt.getRingStart())) { bt.addCoreModification(new CoreModification(CoreModificationTemplate.KETO, bt.getRingStart())); } } else { if(bt.hasCoreModification(CoreModificationTemplate.KETO, 1)) { if(bt.countCoreModifications(CoreModificationTemplate.KETO) == 1) { bt.deleteCoreModification(CoreModificationTemplate.KETO, 1); } } } bt.buildName(); } /** * Set the stereo and modification properties of this Basetype using an extended Stereocode String. * @param extStereoStr the stereostring to parse * @param size the Basetype size * @param ringStart the carbonyl position * @param ringEnd the position of the ring oxygen * @throws ResourcesDbException */ public void buildByExtendedStereocode(String extStereoStr, int size, int ringStart, int ringEnd) throws ResourcesDbException { this.init(); this.setSize(size); this.setRingStart(ringStart); this.setRingEnd(ringEnd); Basetype.buildBasetypeByExtendedStereocode(extStereoStr, this); } public ArrayList<BasetypeBuilderGroup> toBuilderGroups() throws ResourcesDbException { ArrayList<BasetypeBuilderGroup> outList = new ArrayList<BasetypeBuilderGroup>(); for(int i = 1; i <= this.getSize(); i++) { StereoConfiguration posConf = this.getStereoConfigurationByPosition(i); if(posConf.equals(StereoConfiguration.Dexter) || posConf.equals(StereoConfiguration.XDexter)) { outList.add(BasetypeBuilderGroup.HCOH_D); continue; } if(posConf.equals(StereoConfiguration.Laevus) || posConf.equals(StereoConfiguration.XLaevus)) { outList.add(BasetypeBuilderGroup.HCOH_L); continue; } Boolean headTailFlag = false; if((i == 1 && i != this.getRingStart()) || i == this.getSize()) { headTailFlag = true; } ArrayList<CoreModificationTemplate> modList = this.getCoreModificationTemplatesByPosition(i); modList.remove(CoreModificationTemplate.ALDITOL); BasetypeBuilderGroup bbgroup = BasetypeBuilderGroup.forCoreModifications(modList, headTailFlag); if(bbgroup == null) { bbgroup = BasetypeBuilderGroup.UNKNOWN; } outList.add(bbgroup); } return outList; } //***************************************************************************** //*** other methods: ********************************************************** //***************************************************************************** /** * Initialize the properties of this Basetype */ public void init() { this.setStereocode(new Stereocode()); this.setSize(0); this.setRingStart(Basetype.UNKNOWN_RING); this.setRingEnd(Basetype.UNKNOWN_RING); this.setDefaultCarbonylPosition(Basetype.UNKNOWN_RING); this.coreModifications = new ArrayList<CoreModification>(); this.setAnomer((Anomer) null); this.setConfiguration((StereoConfiguration) null); this.setName(null); this.setComposition(null); this.setMonoMass(null); this.setAvgMass(null); this.setSuperclass(null); this.setIsSuperclassFlag(null); this.setInchi(null); this.setSmiles(null); this.setDbId(0); } public String toString() { String outStr = "Basetype: "; outStr += "[Name: " + getName() + "; "; String anomerStr = "null"; if(getAnomer() != null) { anomerStr = getAnomer().getSymbol(); } String stereocodeStr = "null"; if(getStereocode() != null) { stereocodeStr = getStereocode().getStereoStr(); } String configStr = "null"; if(getConfiguration() != null) { configStr = getConfiguration().getSymbol(); } String modStr = ""; if(getCoreModifications() != null) { for(int i = 0; i < getCoreModifications().size(); i++) { modStr += getCoreModifications().get(i).toString(); } } outStr += "Anomer: " + anomerStr + "; "; outStr += "Stereocode: " + stereocodeStr + "; "; outStr += "Configuration: " + configStr + "; "; outStr += "Ring atoms: " + getRingStart() + "/" + getRingEnd() + "; "; outStr += "Modifications: [" + modStr + "]"; outStr += "]"; return(outStr); } }