/* $Revision$ $Author$ $Date$ * * Copyright (C) 2006-2007 Sam Adams <sea36@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.inchi; import net.sf.jniinchi.*; import org.openscience.cdk.CDKConstants; import org.openscience.cdk.exception.CDKException; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; import org.openscience.cdk.interfaces.IAtomParity; import org.openscience.cdk.interfaces.IBond; import org.openscience.cdk.interfaces.IChemObjectBuilder; import java.util.HashMap; import java.util.List; import java.util.Map; /** * <p>This class generates a CDK IAtomContainer from an InChI string. It places * calls to a JNI wrapper for the InChI C++ library. * * <p>The generated IAtomContainer will have all 2D and 3D coordinates set to 0.0, * but may have atom parities set. Double bond and allene stereochemistry are * not currently recorded. * * <h3>Example usage</h3> * * <code>// Generate factory - throws CDKException if native code does not load</code><br> * <code>InChIGeneratorFactory factory = new InChIGeneratorFactory();</code><br> * <code>// Get InChIToStructure</code><br> * <code>InChIToStructure intostruct = factory.getInChIToStructure(inchi);</code><br> * <code></code><br> * <code>INCHI_RET ret = intostruct.getReturnStatus();</code><br> * <code>if (ret == INCHI_RET.WARNING) {</code><br> * <code> // Structure generated, but with warning message</code><br> * <code> System.out.println("InChI warning: " + intostruct.getMessage());</code><br> * <code>} else if (ret != INCHI_RET.OKAY) {</code><br> * <code> // Structure generation failed</code><br> * <code> throw new CDKException("Structure generation failed failed: " + ret.toString()</code><br> * <code> + " [" + intostruct.getMessage() + "]");</code><br> * <code>}</code><br> * <code></code><br> * <code>IAtomContainer container = intostruct.getAtomContainer();</code><br> * <p><tt><b> * * @author Sam Adams * * @cdk.module inchi * @cdk.githash */ public class InChIToStructure { protected JniInchiInputInchi input; protected JniInchiOutputStructure output; protected IAtomContainer molecule; /** * Constructor. Generates CDK AtomContainer from InChI. * @param inchi * @throws CDKException */ protected InChIToStructure(String inchi, IChemObjectBuilder builder) throws CDKException { try { input = new JniInchiInputInchi(inchi, ""); } catch (JniInchiException jie) { throw new CDKException("Failed to convert InChI to molecule: " + jie.getMessage(), jie); } generateAtomContainerFromInchi(builder); } /** * Constructor. Generates CMLMolecule from InChI. * @param inchi * @param options * @throws CDKException */ protected InChIToStructure(String inchi, IChemObjectBuilder builder, String options) throws CDKException { try { input = new JniInchiInputInchi(inchi, options); } catch (JniInchiException jie) { throw new CDKException("Failed to convert InChI to molecule: " + jie.getMessage(), jie); } generateAtomContainerFromInchi(builder); } /** * Constructor. Generates CMLMolecule from InChI. * @param inchi * @param options * @throws CDKException */ protected InChIToStructure(String inchi, IChemObjectBuilder builder, List options) throws CDKException { try { input = new JniInchiInputInchi(inchi, options); } catch (JniInchiException jie) { throw new CDKException("Failed to convert InChI to molecule: " + jie.getMessage()); } generateAtomContainerFromInchi(builder); } /** * Gets structure from InChI, and converts InChI library data structure * into an IAtomContainer. * * @throws CDKException */ protected void generateAtomContainerFromInchi(IChemObjectBuilder builder) throws CDKException { try { output = JniInchiWrapper.getStructureFromInchi(input); } catch (JniInchiException jie) { throw new CDKException("Failed to convert InChI to molecule: " + jie.getMessage(), jie); } //molecule = new AtomContainer(); molecule = builder.newAtomContainer(); Map<JniInchiAtom, IAtom> inchiCdkAtomMap = new HashMap<JniInchiAtom, IAtom>(); for (int i = 0; i < output.getNumAtoms(); i ++) { JniInchiAtom iAt = output.getAtom(i); IAtom cAt = builder.newAtom(); inchiCdkAtomMap.put(iAt, cAt); cAt.setID("a" + i); cAt.setSymbol(iAt.getElementType()); // Ignore coordinates - all zero int charge = iAt.getCharge(); if (charge != 0) { cAt.setFormalCharge(charge); } // hydrogenCount contains number of implict hydrogens, not // total number // Ref: Posting to cdk-devel list by Egon Willighagen 2005-09-17 int numH = iAt.getImplicitH(); if (numH != 0) { cAt.setHydrogenCount(numH); } molecule.addAtom(cAt); } for (int i = 0; i < output.getNumBonds(); i ++) { JniInchiBond iBo = output.getBond(i); IBond cBo = builder.newBond(); IAtom atO = inchiCdkAtomMap.get(iBo.getOriginAtom()); IAtom atT = inchiCdkAtomMap.get(iBo.getTargetAtom()); IAtom[] atoms = new IAtom[2]; atoms[0] = atO; atoms[1] = atT; cBo.setAtoms(atoms); INCHI_BOND_TYPE type = iBo.getBondType(); if (type == INCHI_BOND_TYPE.SINGLE) { cBo.setOrder(CDKConstants.BONDORDER_SINGLE); } else if (type == INCHI_BOND_TYPE.DOUBLE) { cBo.setOrder(CDKConstants.BONDORDER_DOUBLE); } else if (type == INCHI_BOND_TYPE.TRIPLE) { cBo.setOrder(CDKConstants.BONDORDER_TRIPLE); } else if (type == INCHI_BOND_TYPE.ALTERN) { cBo.setFlag(CDKConstants.ISAROMATIC, true); } else { throw new CDKException("Unknown bond type: " + type); } INCHI_BOND_STEREO stereo = iBo.getBondStereo(); // No stereo definition if (stereo == INCHI_BOND_STEREO.NONE) { cBo.setStereo(IBond.Stereo.NONE); } // Bond ending (fat end of wedge) below the plane else if (stereo == INCHI_BOND_STEREO.SINGLE_1DOWN) { cBo.setStereo(IBond.Stereo.DOWN); } // Bond ending (fat end of wedge) above the plane else if (stereo == INCHI_BOND_STEREO.SINGLE_1UP) { cBo.setStereo(IBond.Stereo.UP); } // Bond starting (pointy end of wedge) below the plane else if (stereo == INCHI_BOND_STEREO.SINGLE_2DOWN) { cBo.setStereo(IBond.Stereo.DOWN_INVERTED); } // Bond starting (pointy end of wedge) above the plane else if (stereo == INCHI_BOND_STEREO.SINGLE_2UP) { cBo.setStereo(IBond.Stereo.UP_INVERTED); } // Bond with undefined stereochemistry else if (stereo == INCHI_BOND_STEREO.SINGLE_1EITHER || stereo == INCHI_BOND_STEREO.DOUBLE_EITHER) { cBo.setStereo((IBond.Stereo)CDKConstants.UNSET); } molecule.addBond(cBo); } for (int i = 0; i < output.getNumStereo0D(); i ++) { JniInchiStereo0D stereo0d = output.getStereo0D(i); if (stereo0d.getStereoType() == INCHI_STEREOTYPE.TETRAHEDRAL) { JniInchiAtom central = stereo0d.getCentralAtom(); JniInchiAtom[] neighbours = stereo0d.getNeighbors(); IAtom atC = (IAtom) inchiCdkAtomMap.get(central); IAtom at0 = (IAtom) inchiCdkAtomMap.get(neighbours[0]); IAtom at1 = (IAtom) inchiCdkAtomMap.get(neighbours[1]); IAtom at2 = (IAtom) inchiCdkAtomMap.get(neighbours[2]); IAtom at3 = (IAtom) inchiCdkAtomMap.get(neighbours[3]); int sign; if (stereo0d.getParity() == INCHI_PARITY.ODD) { sign = -1; } else if (stereo0d.getParity() == INCHI_PARITY.EVEN) { sign = +1; } else { // CDK Only supports parities of + or - continue; } IAtomParity parity = builder.newAtomParity(atC, at0, at1, at2, at3, sign); molecule.addAtomParity(parity); } else { // TODO - other types of atom parity - double bond, etc } } } /** * Returns generated molecule. * @return An AtomContainer object */ public IAtomContainer getAtomContainer() { return(molecule); } /** * Gets return status from InChI process. OKAY and WARNING indicate * InChI has been generated, in all other cases InChI generation * has failed. */ public INCHI_RET getReturnStatus() { return(output.getReturnStatus()); } /** * Gets generated (error/warning) messages. */ public String getMessage() { return(output.getMessage()); } /** * Gets generated log. */ public String getLog() { return(output.getLog()); } /** * <p>Returns warning flags, see INCHIDIFF in inchicmp.h. * * <p>[x][y]: * <br>x=0 => Reconnected if present in InChI otherwise Disconnected/Normal * <br>x=1 => Disconnected layer if Reconnected layer is present * <br>y=1 => Main layer or Mobile-H * <br>y=0 => Fixed-H layer */ public long[][] getWarningFlags() { return(output.getWarningFlags()); } }