/*
* 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.template;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eurocarbdb.resourcesdb.Config;
import org.eurocarbdb.resourcesdb.GlycanNamescheme;
import org.eurocarbdb.resourcesdb.ResourcesDbException;
import org.eurocarbdb.resourcesdb.atom.Atom;
import org.eurocarbdb.resourcesdb.atom.AtomConnection;
import org.eurocarbdb.resourcesdb.atom.Composition;
import org.eurocarbdb.resourcesdb.glycoconjugate_derived.GlycoconjugateException;
import org.eurocarbdb.resourcesdb.glycoconjugate_derived.LinkageType;
import org.eurocarbdb.resourcesdb.monosaccharide.SubstituentAlias;
import org.eurocarbdb.resourcesdb.monosaccharide.SubstituentSubpartTreeNode;
import org.eurocarbdb.resourcesdb.util.Utils;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
/**
* Class to store and manage Substituent Templates
* @author Thomas Lütteke
*
*/
public class SubstituentTemplateContainer {
private Config config = null;
//*****************************************************************************
//*** Constructors: ***********************************************************
//*****************************************************************************
public SubstituentTemplateContainer() {
this.setConfig(new Config());
}
public SubstituentTemplateContainer(Config conf) {
this.setConfig(conf);
}
//*****************************************************************************
//*** Getters/Setters: ********************************************************
//*****************************************************************************
private void setConfig(Config theConf) {
this.config = theConf;
}
public Config getConfig() {
return this.config;
}
//*****************************************************************************
//*** Template Lists / Maps: **************************************************
//*****************************************************************************
private ArrayList<SubstituentTemplate> substituentTemplateList;
private HashMap<GlycanNamescheme, ArrayList<String>> includedNameListsMap;
public ArrayList<SubstituentTemplate> getTemplateList() throws ResourcesDbException {
if(this.substituentTemplateList == null) {
this.substituentTemplateList = SubstituentTemplateContainer.readTemplateList(this.getConfig());
}
return this.substituentTemplateList;
}
public ArrayList<String> getResidueIncludedNameList(GlycanNamescheme scheme) throws ResourcesDbException {
if(this.includedNameListsMap == null) {
this.includedNameListsMap = new HashMap<GlycanNamescheme, ArrayList<String>>();
}
ArrayList<String> nameList = this.includedNameListsMap.get(scheme);
if(nameList == null) {
ArrayList<SubstituentTemplate> templates = this.getTemplateList();
nameList = new ArrayList<String>();
for(SubstituentTemplate template : templates) {
for(SubstituentAlias alias : template.getAliasList(scheme, null)) {
if(alias.getResidueIncludedName() != null && alias.getResidueIncludedName().length() > 0) {
if(!nameList.contains(alias.getResidueIncludedName())) {
nameList.add(alias.getResidueIncludedName());
}
}
}
}
this.includedNameListsMap.put(scheme, nameList);
}
return nameList;
}
public SubstituentTemplate forResidueIncludedName(GlycanNamescheme scheme, String name) throws ResourcesDbException {
for(SubstituentTemplate substTmpl : this.getTemplateList()) {
for(SubstituentAlias alias : substTmpl.getAliasList()) {
if(alias.getNamescheme().equals(scheme)) {
if(name.equalsIgnoreCase(alias.getResidueIncludedName())) {
if(alias.getSeparateDisplayName() == null || alias.getSeparateDisplayName().length() == 0) {
return substTmpl;
}
}
}
}
}
return null;
}
public SubstituentTemplate forSeparateDisplayName(GlycanNamescheme scheme, String name) throws ResourcesDbException {
for(SubstituentTemplate substTmpl : this.getTemplateList()) {
for(SubstituentAlias alias : substTmpl.getAliasList()) {
if(alias.getNamescheme().equals(scheme)) {
if(name.equalsIgnoreCase(alias.getSeparateDisplayName())) {
if(alias.getResidueIncludedName() == null || alias.getResidueIncludedName().length() == 0) {
return substTmpl;
}
}
}
}
}
return null;
}
public SubstituentTemplate forName(GlycanNamescheme scheme, String name) throws ResourcesDbException {
if(name != null) {
for(SubstituentTemplate substTmpl : this.getTemplateList()) {
for(SubstituentAlias alias : substTmpl.getAliasList()) {
if(alias.getNamescheme().equals(scheme)) {
if(name.equalsIgnoreCase(alias.getResidueIncludedName())) {
if(alias.getSeparateDisplayName() == null) {
return substTmpl;
}
}
if(name.equalsIgnoreCase(alias.getSeparateDisplayName())) {
if(alias.getResidueIncludedName() == null) {
return substTmpl;
}
}
}
}
}
}
return null;
}
//*****************************************************************************
//*** Methods for filling Template Lists / Maps: ******************************
//*****************************************************************************
private static ArrayList<SubstituentTemplate> readTemplateList(Config conf) throws ResourcesDbException {
return SubstituentTemplateContainer.getTemplateListFromXml(conf.getSubstituentTemplatesXmlUrl());
}
private static ArrayList<SubstituentTemplate> getTemplateListFromXml(URL xmlUrl) throws ResourcesDbException {
SAXBuilder parser = new SAXBuilder();
ArrayList<SubstituentTemplate> templateList = new ArrayList<SubstituentTemplate>();
try {
Document doc = parser.build(xmlUrl);
org.jdom.Element root = doc.getRootElement();
List<?> xmlTemplateList = root.getChildren();
Iterator<?> templatesIter = xmlTemplateList.iterator();
while(templatesIter.hasNext()) {
org.jdom.Element xmlTemplate = (org.jdom.Element) templatesIter.next();
SubstituentTemplate template = getTemplateFromXmlTree(xmlTemplate);
if(template != null) {
templateList.add(template);
}
}
} catch (JDOMException je) {
throw new ResourcesDbException("Exception in reading TrivialnameTemplate XML file.", je);
} catch (IOException ie) {
throw new ResourcesDbException("Exception in reading TrivialnameTemplate XML file.", ie);
}
return templateList;
}
private static SubstituentTemplate getTemplateFromXmlTree(org.jdom.Element xmlTemplate) throws ResourcesDbException {
if(xmlTemplate.getName().equalsIgnoreCase("template")) {
SubstituentTemplate substTemplate = new SubstituentTemplate();
List<?> propList = xmlTemplate.getChildren();
Iterator<?> propIter = propList.iterator();
while(propIter.hasNext()) {
org.jdom.Element property = (org.jdom.Element) propIter.next();
String propertyName = property.getName().toLowerCase();
String propertyValue = property.getValue();
if(propertyValue == null) {
propertyValue = "";
}
if(propertyName.equals("name")) {
substTemplate.setName(propertyValue);
//TODO: add explicit alias names for allowed linkage types to substituents xml file and remove the subsequent loop:
for(LinkageType link : LinkageType.values()) {
//*** in GlycoCT notation all substituents are separate residues: ***
//substTemplate.setSeparateDisplay(GlycanNamescheme.GLYCOCT, link, propertyValue);
if(link.equals(LinkageType.NONMONOSACCHARID)) {
continue;
}
SubstituentAlias msdbAlias = new SubstituentAlias(GlycanNamescheme.MONOSACCHARIDEDB, link, propertyValue, null, true);
substTemplate.addAlias(msdbAlias);
SubstituentAlias glycoctAlias = new SubstituentAlias(GlycanNamescheme.GLYCOCT, link, null, propertyValue, true);
substTemplate.addAlias(glycoctAlias);
//substTemplate.setPrimaryAlias(GlycanNamescheme.GLYCOCT, link, alias);
}
} else if(propertyName.equals("valence") && !propertyValue.equals("")) {
if(property.getChildren() != null && property.getChildren().size() > 0) {
HashMap<String, Integer> minMaxMap = SubstituentTemplateContainer.parseMinMaxTags(property);
if(minMaxMap.get("MIN") != null) {
substTemplate.setMinValence(minMaxMap.get("MIN").intValue());
}
if(minMaxMap.get("MAX") != null) {
substTemplate.setMaxValence(minMaxMap.get("MAX").intValue());
}
} else {
substTemplate.setValence(Integer.parseInt(propertyValue));
}
} else if(propertyName.equals("default_linking_position1") && !propertyValue.equals("")) {
substTemplate.setDefaultLinkingPosition1(Integer.parseInt(propertyValue));
} else if(propertyName.equals("default_linking_position2") && !propertyValue.equals("")) {
substTemplate.setDefaultLinkingPosition2(Integer.parseInt(propertyValue));
} else if(propertyName.equals("default_linkage_type1") && !propertyValue.equals("")) {
/*try {
substTemplate.setDefaultLinkagetype1(LinkageType.forName(propertyValue));
} catch(GlycoconjugateException ge) {
throw new ResourcesDbException("Error in reading substituent template from xml: unknown LinkageType " + propertyValue, ge);
}*/
} else if(propertyName.equals("default_linkage_type2") && !propertyValue.equals("")) {
/*try {
substTemplate.setDefaultLinkagetype2(LinkageType.forName(propertyValue));
} catch(GlycoconjugateException ge) {
throw new ResourcesDbException("Error in reading substituent template from xml: unknown LinkageType " + propertyValue, ge);
}*/
} else if(propertyName.equals("bondorder1")) {
//substTemplate.setBondOrder1(Integer.parseInt(propertyValue));
} else if(propertyName.equals("bondorder2") && !propertyValue.equals("")) {
//substTemplate.setBondOrder2(Integer.parseInt(propertyValue));
} else if(propertyName.equals("can_replace_ring_oxygen")) {
substTemplate.setCanReplaceRingOxygen(Utils.parseTrueFalseString(propertyValue, false));
} else if(propertyName.equals("is_linkable")) {
substTemplate.setLinkable(Utils.parseTrueFalseString(propertyValue, false));
} else if(propertyName.equals("haworth_name")) {
substTemplate.setHaworthName(propertyValue);
} else if(propertyName.equals("mirrored_haworth_name")) {
substTemplate.setMirroredHaworthName(propertyValue);
} else if(propertyName.equals("olinked_equivalent") && !propertyValue.equals("")) {
substTemplate.setOLinkedEquivalent(propertyValue);
} else if(propertyName.equals("formula")) {
substTemplate.setFormula(propertyValue);
if(substTemplate.getFormula() != null) {
substTemplate.setComposition(new Composition(substTemplate.getFormula()));
}
} else if(propertyName.equals("atoms")) {
List<?> atomList = property.getChildren();
Iterator<?> atomIter = atomList.iterator();
while(atomIter.hasNext()) {
org.jdom.Element atomtag = (org.jdom.Element) atomIter.next();
Atom a = Atom.parseXmlAtomTag(atomtag);
substTemplate.addAtom(a);
}
} else if(propertyName.equals("atom_connections")) {
List<?> connectionList = property.getChildren();
Iterator<?> connectionIter = connectionList.iterator();
while(connectionIter.hasNext()) {
org.jdom.Element atomtag = (org.jdom.Element) connectionIter.next();
AtomConnection.parseXmlAtomConnectionTag(atomtag, substTemplate);
}
} else if(propertyName.equals("valid_linking_positions")) {
List<?> vlpList = property.getChildren();
Iterator<?> vlpIter = vlpList.iterator();
while(vlpIter.hasNext()) {
org.jdom.Element vlpTag = (org.jdom.Element) vlpIter.next();
int id = Integer.parseInt(vlpTag.getAttributeValue("id"));
int atomId = Integer.parseInt(vlpTag.getAttributeValue("atom_id"));
Atom linkAtom = substTemplate.getAtomById(atomId);
if(linkAtom == null) {
throw new ResourcesDbException("Cannot get atom id " + atomId + " for substituent template " + substTemplate.getName());
}
Atom replAtom = null;
String replAtomStr = vlpTag.getAttributeValue("replaced_atom_id");
if(replAtomStr != null && replAtomStr.length() > 0) {
replAtom = substTemplate.getAtomById(Integer.parseInt(replAtomStr));
if(replAtom == null) {
throw new ResourcesDbException("Cannot get atom id " + replAtomStr + " for substituent template " + substTemplate.getName());
}
}
Double bo = null;
String boStr = vlpTag.getAttributeValue("bond_order");
if(boStr != null && boStr.length() > 0) {
bo = Double.parseDouble(boStr);
}
LinkageType defaultLinktype = null;
String linktypeStr = vlpTag.getAttributeValue("default_linkage_type");
if(linktypeStr != null && linktypeStr.length() > 0) {
try {
defaultLinktype = LinkageType.forName(linktypeStr);
} catch(GlycoconjugateException ge) {
//*** linktypeStr cannot be matched to a LinkageType ***
throw new ResourcesDbException("Error in reading substituent template from xml: unknown LinkageType " + linktypeStr, ge);
}
}
substTemplate.addValidLinkingPosition(id, linkAtom, replAtom, bo, defaultLinktype);
}
} else if(propertyName.equals("fuzzy")) {
substTemplate.setFuzzy(Utils.parseTrueFalseString(propertyValue, false));
} else if(propertyName.equals("synonyms")) {
List<?> synonymList = property.getChildren();
Iterator<?> synonymIter = synonymList.iterator();
while(synonymIter.hasNext()) {
org.jdom.Element synonym = (org.jdom.Element) synonymIter.next();
SubstituentAlias alias = parseXmlAliasTag(synonym);
if(alias != null) {
substTemplate.addAlias(alias);
}
}
} else if(propertyName.equals("substituent_part")) {
substTemplate.setSubparts(parseSubstituentPartsTag(property));
}
}
return(substTemplate);
} else {
return(null);
}
}
private static SubstituentAlias parseXmlAliasTag(org.jdom.Element synonymElem) throws ResourcesDbException {
SubstituentAlias alias = null;
if(synonymElem.getName().equalsIgnoreCase("primary_alias") || synonymElem.getName().equalsIgnoreCase("secondary_alias")) {
alias = new SubstituentAlias();
GlycanNamescheme scheme = GlycanNamescheme.forName(synonymElem.getAttribute("namescheme").getValue());
if(scheme == null) {
throw new ResourcesDbException("Unknown or empty namescheme string: '" + synonymElem.getAttribute("namescheme").getValue() + "'");
}
alias.setNamescheme(scheme);
String linkTypeName = synonymElem.getAttributeValue("linkage_type");
if(linkTypeName != null) {
try {
alias.setLinktype1(LinkageType.forName(linkTypeName));
} catch(GlycoconjugateException ge) {
throw new ResourcesDbException("Error in reading substituent alias from xml: unknown LinkageType " + linkTypeName, ge);
}
}
String linkTypeName2 = synonymElem.getAttributeValue("linkage_type2");
if(linkTypeName2 != null) {
try {
alias.setLinktype2(LinkageType.forName(linkTypeName2));
} catch(GlycoconjugateException ge) {
throw new ResourcesDbException("Error in reading substituent alias from xml: unknown LinkageType " + linkTypeName2, ge);
}
}
alias.setResidueIncludedName(synonymElem.getAttributeValue("residue_included"));
alias.setSeparateDisplayName(synonymElem.getAttributeValue("separate_display"));
if(synonymElem.getName().equalsIgnoreCase("primary_alias")) {
alias.setIsPrimary(true);
} else {
alias.setIsPrimary(false);
}
}
return(alias);
}
private static SubstituentSubpartTreeNode parseSubstituentPartsTag(org.jdom.Element partsTag) throws ResourcesDbException {
SubstituentSubpartTreeNode substPartNode = null;
substPartNode = new SubstituentSubpartTreeNode();
substPartNode.setUserObject(partsTag.getAttributeValue("name"));
substPartNode.setName(partsTag.getAttributeValue("name"));
List<?> childTagsList = partsTag.getChildren();
if(childTagsList != null) {
Iterator<?> childtagIter = childTagsList.iterator();
while(childtagIter.hasNext()) {
org.jdom.Element childTag = (org.jdom.Element) childtagIter.next();
if(childTag.getName().equalsIgnoreCase("substituent_part")) {
substPartNode.add(parseSubstituentPartsTag(childTag));
} else {
throw new ResourcesDbException("Illegal child tag of substituent_part: " + childTag.getName());
}
}
}
return(substPartNode);
}
private static HashMap<String, Integer> parseMinMaxTags(org.jdom.Element parentTag) {
HashMap<String, Integer> retMap = new HashMap<String, Integer>();
List<?> childList = parentTag.getChildren();
if(childList != null) {
for(int i = 0; i < parentTag.getChildren().size(); i++) {
org.jdom.Element childTag = (org.jdom.Element) parentTag.getChildren().get(i);
if(childTag.getName().toLowerCase().equals("min")) {
try {
retMap.put("MIN", Integer.parseInt(childTag.getValue()));
} catch(NumberFormatException nfe) {
}
continue;
}
if(childTag.getName().toLowerCase().equals("max")) {
try {
retMap.put("MAX", Integer.parseInt(childTag.getValue()));
} catch(NumberFormatException nfe) {
}
continue;
}
}
}
return retMap;
}
}