/* $Revision$ $Author$ $Date$
*
* Copyright (C) 2003-2007 Egon Willighagen <egonw@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.
* All we ask is that proper credit is given for our work, which includes
* - but is not limited to - adding the above copyright notice to the beginning
* of your source code files, and to any copyright notice that you may distribute
* with programs based on this work.
*
* 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.io;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.annotations.TestClass;
import org.openscience.cdk.annotations.TestMethod;
import org.openscience.cdk.config.AtomTypeFactory;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomType;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IChemFile;
import org.openscience.cdk.interfaces.IChemModel;
import org.openscience.cdk.interfaces.IChemObject;
import org.openscience.cdk.interfaces.IChemSequence;
import org.openscience.cdk.interfaces.IMolecule;
import org.openscience.cdk.interfaces.IMoleculeSet;
import org.openscience.cdk.io.formats.IResourceFormat;
import org.openscience.cdk.io.formats.Mol2Format;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;
import org.openscience.cdk.tools.manipulator.AtomTypeManipulator;
import org.openscience.cdk.tools.periodictable.PeriodicTable;
import javax.vecmath.Point3d;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.StringTokenizer;
/**
* Reads a molecule from an Mol2 file, such as written by Sybyl.
* See the specs <a href="http://www.tripos.com/data/support/mol2.pdf">here</a>.
*
* @cdk.module io
* @cdk.githash
*
* @author Egon Willighagen
* @cdk.created 2003-08-21
*
* @cdk.keyword file format, Mol2
*/
@TestClass("org.openscience.cdk.io.Mol2ReaderTest")
public class Mol2Reader extends DefaultChemObjectReader {
BufferedReader input = null;
private static ILoggingTool logger =
LoggingToolFactory.createLoggingTool(Mol2Reader.class);
/**
* Contructs a new MDLReader that can read Molecule from a given Reader.
*
* @param in The Reader to read from
*/
public Mol2Reader(Reader in) {
input = new BufferedReader(in);
}
public Mol2Reader(InputStream input) {
this(new InputStreamReader(input));
}
public Mol2Reader() {
this(new StringReader(""));
}
@TestMethod("testGetFormat")
public IResourceFormat getFormat() {
return Mol2Format.getInstance();
}
@TestMethod("testSetReader_Reader")
public void setReader(Reader input) throws CDKException {
if (input instanceof BufferedReader) {
this.input = (BufferedReader)input;
} else {
this.input = new BufferedReader(input);
}
}
@TestMethod("testSetReader_InputStream")
public void setReader(InputStream input) throws CDKException {
setReader(new InputStreamReader(input));
}
@TestMethod("testAccepts")
public boolean accepts(Class classObject) {
Class[] interfaces = classObject.getInterfaces();
for (int i=0; i<interfaces.length; i++) {
if (IChemModel.class.equals(interfaces[i])) return true;
if (IChemFile.class.equals(interfaces[i])) return true;
if (IMolecule.class.equals(interfaces[i])) return true;
}
Class superClass = classObject.getSuperclass();
if (superClass != null) return this.accepts(superClass);
return false;
}
/**
* Takes an object which subclasses IChemObject, e.g.Molecule, and will read
* this from from the Reader. If the specific implementation
* does not support a specific IChemObject it will throw an Exception.
*
* @param object The object that subclasses IChemObject
* @return The IChemObject read
* @exception CDKException
*/
public IChemObject read(IChemObject object) throws CDKException {
if (object instanceof IChemFile) {
IChemFile file = (IChemFile)object;
IChemSequence sequence = file.getBuilder().newChemSequence();
IChemModel model = file.getBuilder().newChemModel();
IMoleculeSet moleculeSet = file.getBuilder().newMoleculeSet();
moleculeSet.addMolecule(readMolecule(
model.getBuilder().newMolecule()
));
model.setMoleculeSet(moleculeSet);
sequence.addChemModel(model);
file.addChemSequence(sequence);
return file;
} else if (object instanceof IChemModel) {
IChemModel model = (IChemModel)object;
IMoleculeSet moleculeSet = model.getBuilder().newMoleculeSet();
moleculeSet.addMolecule(readMolecule(
model.getBuilder().newMolecule()
));
model.setMoleculeSet(moleculeSet);
return model;
} else if (object instanceof IMolecule) {
IMolecule molecule = (IMolecule)object;
return readMolecule(molecule);
} else {
throw new CDKException("Only supported is ChemModel, and not " +
object.getClass().getName() + "."
);
}
}
@TestMethod("testAccepts")
public boolean accepts(IChemObject object) {
if (object instanceof IChemFile) {
return true;
} else if (object instanceof IChemModel) {
return true;
} else if (object instanceof IMolecule) {
return true;
}
return false;
}
/**
* Read a Reaction from a file in MDL RXN format
*
* @return The Reaction that was read from the MDL file.
*/
private IMolecule readMolecule(IMolecule molecule) throws CDKException {
AtomTypeFactory atFactory = null;
try {
atFactory = AtomTypeFactory.getInstance(
"org/openscience/cdk/config/data/mol2_atomtypes.xml", molecule.getBuilder()
);
} catch (Exception exception) {
String error = "Could not instantiate an AtomTypeFactory";
logger.error(error);
logger.debug(exception);
throw new CDKException(error, exception);
}
try {
String line = input.readLine();
int atomCount = 0;
int bondCount = 0;
while (line != null) {
if (line.startsWith("@<TRIPOS>MOLECULE")) {
logger.info("Reading molecule block");
// second line has atom/bond counts?
input.readLine(); // disregard the name line
String counts = input.readLine();
StringTokenizer tokenizer = new StringTokenizer(counts);
try {
atomCount = Integer.parseInt(tokenizer.nextToken());
} catch (NumberFormatException nfExc) {
String error = "Error while reading atom count from MOLECULE block";
logger.error(error);
logger.debug(nfExc);
throw new CDKException(error, nfExc);
}
if (tokenizer.hasMoreTokens()) {
try {
bondCount = Integer.parseInt(tokenizer.nextToken());
} catch (NumberFormatException nfExc) {
String error = "Error while reading atom and bond counts";
logger.error(error);
logger.debug(nfExc);
throw new CDKException(error, nfExc);
}
} else {
bondCount = 0;
}
logger.info("Reading #atoms: ", atomCount);
logger.info("Reading #bonds: ", bondCount);
logger.warn("Not reading molecule qualifiers");
} else if (line.startsWith("@<TRIPOS>ATOM")) {
logger.info("Reading atom block");
for (int i=0; i<atomCount; i++) {
line = input.readLine().trim();
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken(); // disregard the id token
String nameStr = tokenizer.nextToken();
String xStr = tokenizer.nextToken();
String yStr = tokenizer.nextToken();
String zStr = tokenizer.nextToken();
String atomTypeStr = tokenizer.nextToken();
// fix OpenBabel atom type codes to SYBYL specification
if ("S.o2".equals(atomTypeStr)) atomTypeStr = "S.O2";
if ("S.o".equals(atomTypeStr)) atomTypeStr = "S.O";
IAtom atom = molecule.getBuilder().newAtom("X");
IAtomType atomType;
try {
atomType = atFactory.getAtomType(atomTypeStr);
} catch (Exception exception) {
// ok, *not* an mol2 atom type
atomType = null;
}
// Maybe it is just an element
if (atomType == null && isElementSymbol(atomTypeStr)) {
atom.setSymbol(atomTypeStr);
} else {
if (atomType == null) {
atomType = atFactory.getAtomType("X");
logger.error("Could not find specified atom type: ", atomTypeStr);
}
AtomTypeManipulator.configure(atom, atomType);
}
atom.setID(nameStr);
atom.setAtomTypeName(atomTypeStr);
try {
double x = Double.parseDouble(xStr);
double y = Double.parseDouble(yStr);
double z = Double.parseDouble(zStr);
atom.setPoint3d(new Point3d(x, y, z));
} catch (NumberFormatException nfExc) {
String error = "Error while reading atom coordinates";
logger.error(error);
logger.debug(nfExc);
throw new CDKException(error, nfExc);
}
molecule.addAtom(atom);
}
} else if (line.startsWith("@<TRIPOS>BOND")) {
logger.info("Reading bond block");
for (int i=0; i<bondCount; i++) {
line = input.readLine();
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken(); // disregard the id token
String atom1Str = tokenizer.nextToken();
String atom2Str = tokenizer.nextToken();
String orderStr = tokenizer.nextToken();
try {
int atom1 = Integer.parseInt(atom1Str);
int atom2 = Integer.parseInt(atom2Str);
if ("nc".equals(orderStr)) {
// do not connect the atoms
} else {
IBond bond = molecule.getBuilder().newBond(
molecule.getAtom(atom1-1),
molecule.getAtom(atom2-1)
);
if ("1".equals(orderStr)) {
bond.setOrder(CDKConstants.BONDORDER_SINGLE);
} else if ("2".equals(orderStr)) {
bond.setOrder(CDKConstants.BONDORDER_DOUBLE);
} else if ("3".equals(orderStr)) {
bond.setOrder(CDKConstants.BONDORDER_TRIPLE);
} else if ("am".equals(orderStr) || "ar".equals(orderStr)) {
bond.setOrder(CDKConstants.BONDORDER_SINGLE);
bond.setFlag(CDKConstants.ISAROMATIC, true);
bond.getAtom(0).setFlag(CDKConstants.ISAROMATIC, true);
bond.getAtom(1).setFlag(CDKConstants.ISAROMATIC, true);
} else if ("du".equals(orderStr)) {
bond.setOrder(CDKConstants.BONDORDER_SINGLE);
} else if ("un".equals(orderStr)) {
bond.setOrder(CDKConstants.BONDORDER_SINGLE);
}
molecule.addBond(bond);
}
} catch (NumberFormatException nfExc) {
String error = "Error while reading bond information";
logger.error(error);
logger.debug(nfExc);
throw new CDKException(error, nfExc);
}
}
}
line = input.readLine();
}
} catch (IOException exception) {
String error = "Error while reading general structure";
logger.error(error);
logger.debug(exception);
throw new CDKException(error, exception);
}
return molecule;
}
private boolean isElementSymbol(String atomTypeStr) {
for (int i = 1; i < PeriodicTable.getElementCount(); i++) {
if (PeriodicTable.getSymbol(i).equals(atomTypeStr)) return true;
}
return false;
}
@TestMethod("testClose")
public void close() throws IOException {
input.close();
}
}