/* $Revision$ $Author$ $Date$ * * Copyright (C) 2001-2007 Christoph Steinbeck <steinbeck@users.sf.net> * * Contact: cdk-devel@lists.sourceforge.net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.openscience.cdk.config; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; import org.openscience.cdk.annotations.TestClass; import org.openscience.cdk.annotations.TestMethod; import org.openscience.cdk.exception.CDKException; import org.openscience.cdk.exception.NoSuchAtomTypeException; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomType; import org.openscience.cdk.interfaces.IChemObjectBuilder; import org.openscience.cdk.interfaces.IPseudoAtom; import org.openscience.cdk.tools.ILoggingTool; import org.openscience.cdk.tools.LoggingToolFactory; /** * General class for defining AtomTypes. This class itself does not define the * items types; for this classes implementing the AtomTypeConfiguration * interface are used. * * <p>To see which AtomTypeConfigurator's CDK provides, one should check the * AtomTypeConfigurator API. * * <p>The AtomTypeFactory is a singleton class, which means that there exists * only one instance of the class. Well, almost. For each atom type table, * there is one AtomTypeFactory instance. An instance of this class is * obtained with: * <pre> * AtomTypeFactory factory = AtomTypeFactory.getInstance(someChemObjectBuilder); * </pre> * For each atom type list a separate AtomTypeFactory is instantiated. * * <p>To get all the atom types of an element from a specific list, this * code can be used: * <pre> * AtomTypeFactory factory = AtomTypeFactory.getInstance( * "org/openscience/cdk/config/data/jmol_atomtypes.txt", someChemObjectBuilder * ); * AtomType[] types = factory.getAtomTypes("C"); * </pre> * * @cdk.module core * @cdk.githash * * @author steinbeck * @cdk.created 2001-08-29 * @cdk.keyword atom, type * @see IAtomTypeConfigurator */ @TestClass("org.openscience.cdk.config.AtomTypeFactoryTest") public class AtomTypeFactory { /** * Used as an ID to describe the atom type. */ public final static String ATOMTYPE_ID_STRUCTGEN = "structgen"; /** * Used as an ID to describe the atom type. */ public final static String ATOMTYPE_ID_MODELING = "modeling"; // these are not available /** * Used as an ID to describe the atom type. */ public final static String ATOMTYPE_ID_JMOL = "jmol"; private final static String TXT_EXTENSION = "txt"; private final static String XML_EXTENSION = "xml"; private final static String OWL_EXTENSION = "owl"; private static ILoggingTool logger = LoggingToolFactory.createLoggingTool(AtomTypeFactory.class); private static Map<String, AtomTypeFactory> tables = null; private List<IAtomType> atomTypes = null; /** * Private constructor for the AtomTypeFactory singleton. * * */ private AtomTypeFactory(String configFile, IChemObjectBuilder builder) { atomTypes = new ArrayList<IAtomType>(100); readConfiguration(configFile, builder); } /** * Private constructor for the AtomTypeFactory singleton. * */ private AtomTypeFactory(InputStream ins, String format, IChemObjectBuilder builder) { atomTypes = new ArrayList<IAtomType>(100); readConfiguration(ins, format, builder); } /** * Method to create a default AtomTypeFactory, using the given InputStream. * An AtomType of this kind is not cached. * * @see #getInstance(String, IChemObjectBuilder) * @param ins InputStream containing the data * @param format String representing the possible formats ('xml' and 'txt') * @param builder IChemObjectBuilder used to make IChemObject instances * @return The AtomTypeFactory for the given data file */ @TestMethod("testGetInstance_InputStream_String_IChemObjectBuilder") public static AtomTypeFactory getInstance(InputStream ins, String format, IChemObjectBuilder builder) { return new AtomTypeFactory(ins, format, builder); } /** * Method to create a default AtomTypeFactory, using the structgen atom type list. * * @see #getInstance(String, IChemObjectBuilder) * @param builder IChemObjectBuilder used to make IChemObject instances * @return The AtomTypeFactory for the given data file */ @TestMethod("testGetInstance_IChemObjectBuilder") public static AtomTypeFactory getInstance(IChemObjectBuilder builder) { return getInstance("org/openscience/cdk/config/data/structgen_atomtypes.xml", builder); } /** * Method to create a specialized AtomTypeFactory. Available lists in CDK are: * <ul> * <li>org/openscience/cdk/config/data/jmol_atomtypes.txt * <li>org/openscience/cdk/config/data/mol2_atomtypes.xml * <li>org/openscience/cdk/config/data/structgen_atomtypes.xml * <li>org/openscience/cdk/config/data/mm2_atomtypes.xml * <li>org/openscience/cdk/config/data/mmff94_atomtypes.xml * <li>org/openscience/cdk/dict/data/cdk-atom-types.owl * <li>org/openscience/cdk/dict/data/sybyl-atom-types.owl * </ul> * * @param configFile String the name of the data file * @param builder IChemObjectBuilder used to make IChemObject instances * @return The AtomTypeFactory for the given data file */ @TestMethod("testGetInstance_String_IChemObjectBuilder") public static AtomTypeFactory getInstance(String configFile, IChemObjectBuilder builder) { if (tables == null) { tables = new Hashtable<String, AtomTypeFactory>(); } if (!(tables.containsKey(configFile))) { tables.put(configFile, new AtomTypeFactory(configFile, builder)); } return tables.get(configFile); } /** * Read the config from a text file. * * @param fileName name of the config file * @param builder IChemObjectBuilder used to make IChemObject instances */ private void readConfiguration(String fileName, IChemObjectBuilder builder) { logger.info("Reading config file from ", fileName); InputStream ins; { //try to see if this is a resource ins = this.getClass().getClassLoader().getResourceAsStream(fileName); if(ins==null){ // try to see if this configFile is an actual file File file = new File(fileName); if (file.exists()) { logger.debug("configFile is a File"); // what's next? try { ins = new FileInputStream(file); } catch (Exception exception) { logger.error(exception.getMessage()); logger.debug(exception); } } else { logger.error("no stream and no file"); } } } String format = XML_EXTENSION; if (fileName.endsWith(TXT_EXTENSION)) { format = TXT_EXTENSION; } else if (fileName.endsWith(XML_EXTENSION)) { format = XML_EXTENSION; } else if (fileName.endsWith(OWL_EXTENSION)) { format = OWL_EXTENSION; } readConfiguration(ins, format, builder); } private IAtomTypeConfigurator constructConfigurator(String format) { try { if (format.equals(TXT_EXTENSION)) { return (IAtomTypeConfigurator) this.getClass().getClassLoader(). loadClass("org.openscience.cdk.config.TXTBasedAtomTypeConfigurator"). newInstance(); } else if (format.equals(XML_EXTENSION)) { return (IAtomTypeConfigurator) this.getClass().getClassLoader(). loadClass("org.openscience.cdk.config.CDKBasedAtomTypeConfigurator"). newInstance(); } else if (format.equals(OWL_EXTENSION)) { return (IAtomTypeConfigurator) this.getClass().getClassLoader(). loadClass("org.openscience.cdk.config.OWLBasedAtomTypeConfigurator"). newInstance(); } } catch (Exception exc) { logger.error("Could not get instance of AtomTypeConfigurator for format ", format); logger.debug(exc); } return null; } private void readConfiguration(InputStream ins, String format, IChemObjectBuilder builder) { IAtomTypeConfigurator atc = constructConfigurator(format); if (atc != null) { atc.setInputStream(ins); try { atomTypes = atc.readAtomTypes(builder); } catch (Exception exc) { logger.error("Could not read AtomType's from file due to: ", exc.getMessage()); logger.debug(exc); } } else { logger.debug("AtomTypeConfigurator was null!"); atomTypes = new ArrayList<IAtomType>(); } } /** * Returns the number of atom types in this list. * * @return The number of atom types */ @TestMethod("testGetSize") public int getSize() { return atomTypes.size(); } /** * Get an AtomType with the given ID. * * @param identifier an ID for a particular atom type (like C$) * @return The AtomType for this id * @exception NoSuchAtomTypeException Thrown if the atom type does not exist. */ @TestMethod("testGetAtomType_String,testGetAtomTypeFromJmol,testGetAtomTypeFromMM2,testGetAtomTypeFromPDB") public IAtomType getAtomType(String identifier) throws NoSuchAtomTypeException { for (IAtomType atomType : atomTypes) { if (atomType.getAtomTypeName().equals(identifier)) { return atomType; } } throw new NoSuchAtomTypeException("The AtomType " + identifier + " could not be found"); } /** * Get an array of all atomTypes known to the AtomTypeFactory for the given * element symbol and atomtype class. * * @param symbol An element symbol to search for * @return An array of atomtypes that matches the given element symbol * and atomtype class */ @TestMethod("testGetAtomTypes_String") public IAtomType[] getAtomTypes(String symbol) { logger.debug("Request for atomtype for symbol ", symbol); List<IAtomType> atomList = new ArrayList<IAtomType>(); for (IAtomType atomType : atomTypes) { // logger.debug(" does symbol match for: ", atomType); if (atomType.getSymbol().equals(symbol)) { // logger.debug("Atom type found for symbol: ", atomType); IAtomType clone; try { clone = (IAtomType) atomType.clone(); atomList.add(clone); } catch (CloneNotSupportedException e) { logger.error("Could not clone IAtomType: ", e.getMessage()); logger.debug(e); } } } IAtomType[] atomTypes = (IAtomType[])atomList.toArray(new IAtomType[atomList.size()]); if (atomTypes.length > 0) logger.debug("Atomtype for symbol ", symbol, " has this number of types: " + atomTypes.length); else logger.debug("No atomtype for symbol ", symbol); return atomTypes; } /** * Gets the allAtomTypes attribute of the AtomTypeFactory object. * * @return The allAtomTypes value */ @TestMethod("testGetAllAtomTypes") public IAtomType[] getAllAtomTypes() { logger.debug("Returning list of size: ", getSize()); List<IAtomType> atomtypeList = new ArrayList<IAtomType>(); for (IAtomType atomType : atomTypes) { IAtomType clone; try { clone = (IAtomType) atomType.clone(); atomtypeList.add(clone); } catch (CloneNotSupportedException e) { logger.error("Could not clone IAtomType: ", e.getMessage()); logger.debug(e); } } return (IAtomType[])atomtypeList.toArray(new IAtomType[atomtypeList.size()]); } /** * Configures an atom. Finds the correct element type by looking at the Atom's * atom type name, and if that fails, picks the first atom type matching * the Atom's element symbol.. * * @param atom The atom to be configured * @return The configured atom * @throws CDKException when it could not recognize and configure the * IAtom */ @TestMethod("testConfigure_IAtom") public IAtom configure(IAtom atom) throws CDKException { if (atom instanceof IPseudoAtom) { // do not try to configure PseudoAtom's return atom; } try { IAtomType atomType; String atomTypeName = atom.getAtomTypeName(); if (atomTypeName == null || atomTypeName.length() == 0) { logger.debug("Using atom symbol because atom type name is empty..."); IAtomType[] types = getAtomTypes(atom.getSymbol()); if (types.length > 0) { logger.warn("Taking first atom type, but other may exist"); atomType = types[0]; } else { String message = "Could not configure atom with unknown ID: " + atom.toString() + " + (id=" + atom.getAtomTypeName() + ")"; logger.warn(message); throw new CDKException(message); } } else { atomType = getAtomType(atom.getAtomTypeName()); } logger.debug("Configuring with atomtype: ", atomType); atom.setSymbol(atomType.getSymbol()); atom.setMaxBondOrder(atomType.getMaxBondOrder()); atom.setBondOrderSum(atomType.getBondOrderSum()); atom.setCovalentRadius(atomType.getCovalentRadius()); atom.setHybridization(atomType.getHybridization()); Object color = atomType.getProperty("org.openscience.cdk.renderer.color"); if (color != null) { atom.setProperty("org.openscience.cdk.renderer.color", color); } atom.setAtomicNumber(atomType.getAtomicNumber()); atom.setExactMass(atomType.getExactMass()); } catch (Exception exception) { logger.warn("Could not configure atom with unknown ID: ", atom, " + (id=", atom.getAtomTypeName(), ")"); logger.debug(exception); throw new CDKException(exception.toString(), exception); } logger.debug("Configured: ", atom); return atom; } }