/* $Revision$ $Author$ $Date$
*
* Copyright (C) 2003-2005 Christoph Steinbeck
* 2003-2008 Egon Willighagen
* Stefan Kuhn
* Rajarshi Guha
*
* 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.layout;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.vecmath.Point2d;
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.IAtomContainerSet;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IChemFile;
import org.openscience.cdk.interfaces.IChemObjectBuilder;
import org.openscience.cdk.interfaces.IMolecule;
import org.openscience.cdk.io.CMLReader;
import org.openscience.cdk.isomorphism.UniversalIsomorphismTester;
import org.openscience.cdk.isomorphism.mcss.RMap;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;
import org.openscience.cdk.tools.manipulator.ChemFileManipulator;
/**
* Helper class for Structure Diagram Generation. Handles templates. This is
* our layout solution for ring systems which are notoriously difficult to
* layout, like cubane, adamantane, porphyrin, etc.
*
* @author steinbeck
* @cdk.created 2003-09-04
* @cdk.keyword layout
* @cdk.keyword 2D-coordinates
* @cdk.keyword structure diagram generation
* @cdk.require java1.4+
* @cdk.module sdg
* @cdk.githash
*/
public class TemplateHandler
{
private static ILoggingTool logger =
LoggingToolFactory.createLoggingTool(TemplateHandler.class);
private List<IAtomContainer> templates = null;
/**
* Creates a new TemplateHandler.
*/
public TemplateHandler(IChemObjectBuilder builder)
{
templates = new ArrayList<IAtomContainer>();
loadTemplates(builder);
}
/**
* Loads all existing templates into memory. To add templates to be used in
* SDG, place a drawing with the new template in org/openscience/cdk/layout/templates and add the
* template filename to org/openscience/cdk/layout/templates/template.list
*/
public void loadTemplates(IChemObjectBuilder builder)
{
String line = null;
try
{
InputStream ins = this.getClass().getClassLoader().getResourceAsStream("org/openscience/cdk/layout/templates/templates.list");
BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
while (reader.ready()) {
line = reader.readLine();
line = "org/openscience/cdk/layout/templates/" + line;
logger.debug("Attempting to read template ", line);
CMLReader structureReader = new CMLReader(
this.getClass().getClassLoader().getResourceAsStream(line)
);
IChemFile file = (IChemFile) structureReader.read(builder.newChemFile());
List<IAtomContainer> files = ChemFileManipulator.getAllAtomContainers(file);
for (int i = 0; i < files.size(); i++)
templates.add(files.get(i));
logger.debug("Successfully read template ", line);
}
} catch (Exception exc) {
logger.debug("Could not read templates");
System.out.println("Reason: " + exc.getMessage());
exc.printStackTrace();
logger.debug(exc);
}
}
/**
* Adds a Molecule to the list of templates use by this TemplateHandler.
*
* @param molecule The molecule to be added to the TemplateHandler
*/
public void addMolecule(IAtomContainer molecule) {
templates.add(molecule);
}
public IAtomContainer removeMolecule(IAtomContainer molecule) throws CDKException {
IAtomContainer ac1 = molecule.getBuilder().newAtomContainer(molecule);
IAtomContainer ac2 = null;
IAtomContainer mol2 = null;
for (int f = 0; f < templates.size(); f++)
{
mol2 = templates.get(f);
ac2 = molecule.getBuilder().newAtomContainer(mol2);
if (UniversalIsomorphismTester.isIsomorph(ac1, ac2)) {
templates.remove(f);
return mol2;
}
}
return null;
}
/**
* Checks if one of the loaded templates is isomorph to the given
* Molecule. If so, it assigns the coordinates from the template to the
* respective atoms in the Molecule, and marks the atoms as ISPLACED.
*
* @param molecule The molecule to be check for potential templates
* @return True if there was a possible mapping
*/
public boolean mapTemplateExact(IAtomContainer molecule) throws CDKException {
logger.debug("Trying to map a molecule...");
boolean mapped = false;
IMolecule template = null;
RMap map = null;
IAtom atom1 = null;
IAtom atom2 = null;
for (int f = 0; f < templates.size(); f++)
{
template = (IMolecule) templates.get(f);
if (UniversalIsomorphismTester.isIsomorph(molecule, template))
{
List<RMap> list = UniversalIsomorphismTester.getIsomorphAtomsMap(
molecule.getBuilder().newAtomContainer(molecule),
molecule.getBuilder().newAtomContainer(template)
);
logger.debug("Found a subgraph mapping of size " + list.size() + ", template: " + template.getID());
for (int i = 0; i < list.size(); i++)
{
map = list.get(i);
atom1 = molecule.getAtom(map.getId1());
atom2 = template.getAtom(map.getId2());
atom1.setPoint2d(new Point2d(atom2.getPoint2d()));
atom1.setFlag(CDKConstants.ISPLACED, true);
}
mapped = true;
} else {
logger.debug("Structure does not match template: ", template.getID());
}
}
return mapped;
}
/**
* Checks if one of the loaded templates is a substructure in the given
* Molecule. If so, it assigns the coordinates from the template to the
* respective atoms in the Molecule, and marks the atoms as ISPLACED.
*
* @param molecule The molecule to be check for potential templates
* @return True if there was a possible mapping
*/
public boolean mapTemplates(IAtomContainer molecule) throws CDKException {
logger.debug("Trying to map a molecule...");
boolean mapped = false;
IAtomContainer template = null;
RMap map = null;
IAtom atom1 = null;
IAtom atom2 = null;
for (int f = 0; f < templates.size(); f++)
{
template = templates.get(f);
if (UniversalIsomorphismTester.isSubgraph(molecule, template))
{
List listOfLists = UniversalIsomorphismTester.getSubgraphAtomsMaps(
molecule.getBuilder().newAtomContainer(molecule),
molecule.getBuilder().newAtomContainer(template)
);
logger.debug("Found " + listOfLists.size() + " subgraphs matching template: " + template.getID());
for (Iterator listOfListsIterator = listOfLists.iterator(); listOfListsIterator.hasNext(); ) {
List list = (List) listOfListsIterator.next();
logger.debug("Found a subgraph mapping of size " + list.size() + ", template: " + template.getID());
for (int i = 0; i < list.size(); i++)
{
map = (RMap) list.get(i);
atom1 = molecule.getAtom(map.getId1());
atom2 = template.getAtom(map.getId2());
atom1.setPoint2d(new Point2d(atom2.getPoint2d()));
atom1.setFlag(CDKConstants.ISPLACED, true);
}
mapped = true;
}
} else {
logger.debug("Structure does not match template: ", template.getID());
}
}
return mapped;
}
/**
* Gets the templateCount attribute of the TemplateHandler object
*
*@return The templateCount value
*/
public int getTemplateCount()
{
return templates.size();
}
/**
* Gets the templateAt attribute of the TemplateHandler object
*
*@param position Description of the Parameter
*@return The templateAt value
*/
public IAtomContainer getTemplateAt(int position)
{
return templates.get(position);
}
/**
* Checks if one of the loaded templates is a substructure in the given
* Molecule and returns all matched substructures in a IAtomContainerSet.
* This method does not assign any coordinates.
*
* @param molecule The molecule to be check for potential templates
* @return an IAtomContainerSet of all matched substructures of
* the molecule
* @throws CDKException if an error occurs
*/
public IAtomContainerSet getMappedSubstructures(IAtomContainer molecule) throws CDKException {
logger.debug("Trying get mapped substructures...");
IAtomContainerSet matchedSubstructures = molecule.getBuilder().newAtomContainerSet();
for (int f = 0; f < templates.size(); f++)
{
IAtomContainer template = templates.get(f);
if (UniversalIsomorphismTester.isSubgraph(molecule, template))
{
List listOfLists = UniversalIsomorphismTester.getSubgraphAtomsMaps(
molecule.getBuilder().newAtomContainer(molecule),
molecule.getBuilder().newAtomContainer(template)
);
logger.debug("Found " + listOfLists.size() + " subgraphs matching template: " + template.getID());
for (Iterator listOfListsIterator = listOfLists.iterator(); listOfListsIterator.hasNext(); ) {
List list = (List) listOfListsIterator.next();
logger.debug("Found a subgraph mapping of size " + list.size() + ", template: " + template.getID());
IAtomContainer matchedSubstructure = molecule.getBuilder().newAtomContainer();
for (Iterator listIterator = list.iterator(); listIterator.hasNext(); )
{
RMap map = (RMap) listIterator.next();
IAtom atom = molecule.getAtom(map.getId1());
matchedSubstructure.addAtom(atom);
}
for (Iterator<IAtom> atomIterator = matchedSubstructure.atoms().iterator(); atomIterator.hasNext(); ) {
IAtom atom = atomIterator.next();
for (Iterator<IBond> connectedBondsIterator = molecule.getConnectedBondsList(atom).iterator(); connectedBondsIterator.hasNext(); ) {
IBond bond = connectedBondsIterator.next();
boolean addBond = true;
for (Iterator<IAtom> bondIterator = bond.atoms().iterator(); bondIterator.hasNext(); ) {
IAtom connectedAtom = bondIterator.next();
if (!matchedSubstructure.contains(connectedAtom) || matchedSubstructure.contains(bond))
addBond = false;
}
if (addBond)
matchedSubstructure.addBond(bond);
}
}
matchedSubstructures.addAtomContainer(matchedSubstructure);
}
} else {
logger.debug("Structure does not match template: ", template.getID());
}
}
/*
* Uniquify matchedSubstructures
*/
for (int i = 0; i < matchedSubstructures.getAtomContainerCount(); i++) {
for (int j = i + 1; j < matchedSubstructures.getAtomContainerCount(); j++) {
if (haveSameAtoms(matchedSubstructures.getAtomContainer(i), matchedSubstructures.getAtomContainer(j)))
matchedSubstructures.removeAtomContainer(j--);
}
}
logger.debug("Found " + matchedSubstructures.getAtomContainerCount() + " unique matched subgraphs");
return matchedSubstructures;
}
/**
* Returns true if both IAtomContainers have the same number of atoms and all atoms
* are equal, false otherwise.
* @param atomContainer1 an IAtomContainer
* @param atomContainer2 another IAtomContainer
* @return true if both IAtomContainers have the same number of atoms and all atoms
* are equal, false otherwise.
*/
private boolean haveSameAtoms(IAtomContainer atomContainer1, IAtomContainer atomContainer2) {
if (atomContainer1.getAtomCount() != atomContainer2.getAtomCount())
return false;
for (Iterator<IAtom> iterator = atomContainer1.atoms().iterator(); iterator.hasNext(); )
if (!atomContainer2.contains(iterator.next()))
return false;
return true;
}
}