1 /*  Copyright (C) 2004-2008  The Chemistry Development Kit (CDK) project
2  *
3  *  Contact: cdk-devel@lists.sourceforge.net
4  *
5  *  This program is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 package org.openscience.cdk.isomorphism.matchers;
20 
21 import org.openscience.cdk.AtomRef;
22 import org.openscience.cdk.BondRef;
23 import org.openscience.cdk.interfaces.IAtom;
24 import org.openscience.cdk.interfaces.IAtomContainer;
25 import org.openscience.cdk.interfaces.IBond;
26 import org.openscience.cdk.interfaces.IChemObject;
27 import org.openscience.cdk.interfaces.IStereoElement;
28 
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.Map;
33 import java.util.Set;
34 
35 /**
36  * Utilities for creating queries from 'real' molecules. Note that most of this
37  * functionality has now been replaced by the
38  * {@link QueryAtomContainer#create(IAtomContainer, Expr.Type...)} method and
39  * the documentation simply indicates what settings are used.
40  */
41 public class QueryAtomContainerCreator {
42 
43     /**
44      * Creates a QueryAtomContainer with the following settings:
45      *
46      * <pre>
47      * QueryAtomContainer.create(container,
48      *                           Expr.Type.ALIPHATIC_ELEMENT,
49      *                           Expr.Type.AROMATIC_ELEMENT,
50      *                           Expr.Type.IS_AROMATIC,
51      *                           Expr.Type.ALIPHATIC_ORDER,
52      *                           Expr.Type.STEREOCHEMISTRY);
53      * </pre>
54      *
55      * @param container The AtomContainer that stands as model
56      * @return The new QueryAtomContainer created from container.
57      */
createBasicQueryContainer(IAtomContainer container)58     public static QueryAtomContainer createBasicQueryContainer(IAtomContainer container) {
59         return QueryAtomContainer.create(container,
60                                          Expr.Type.ALIPHATIC_ELEMENT,
61                                          Expr.Type.AROMATIC_ELEMENT,
62                                          Expr.Type.IS_AROMATIC,
63                                          Expr.Type.ALIPHATIC_ORDER,
64                                          Expr.Type.STEREOCHEMISTRY);
65     }
66 
67     /**
68      * Creates a QueryAtomContainer with the following settings:
69      *
70      * <pre>
71      * QueryAtomContainer.create(container,
72      *                           Expr.Type.ELEMENT,
73      *                           Expr.Type.ORDER);
74      * </pre>
75      *
76      * @param container The AtomContainer that stands as model
77      * @return The new QueryAtomContainer created from container.
78      */
createSymbolAndBondOrderQueryContainer(IAtomContainer container)79     public static QueryAtomContainer createSymbolAndBondOrderQueryContainer(IAtomContainer container) {
80         return QueryAtomContainer.create(container,
81                                          Expr.Type.ELEMENT,
82                                          Expr.Type.ORDER);
83     }
84 
85     /**
86      * Creates a QueryAtomContainer with the following settings:
87      *
88      * <pre>
89      * QueryAtomContainer.create(container,
90      *                           Expr.Type.ELEMENT,
91      *                           Expr.Type.FORMAL_CHARGE,
92      *                           Expr.Type.IS_AROMATIC,
93      *                           Expr.Type.ORDER);
94      * </pre>
95      *
96      * @param container The AtomContainer that stands as model
97      * @return The new QueryAtomContainer created from container.
98      */
createSymbolAndChargeQueryContainer(IAtomContainer container)99     public static QueryAtomContainer createSymbolAndChargeQueryContainer(IAtomContainer container) {
100         return QueryAtomContainer.create(container,
101                                          Expr.Type.ELEMENT,
102                                          Expr.Type.FORMAL_CHARGE,
103                                          Expr.Type.IS_AROMATIC,
104                                          Expr.Type.ORDER);
105     }
106 
createSymbolChargeIDQueryContainer(IAtomContainer container)107     public static QueryAtomContainer createSymbolChargeIDQueryContainer(IAtomContainer container) {
108         QueryAtomContainer queryContainer = new QueryAtomContainer(container.getBuilder());
109         for (int i = 0; i < container.getAtomCount(); i++) {
110             queryContainer.addAtom(new SymbolChargeIDQueryAtom(container.getAtom(i)));
111         }
112         Iterator<IBond> bonds = container.bonds().iterator();
113         while (bonds.hasNext()) {
114             IBond bond   = bonds.next();
115             int   index1 = container.indexOf(bond.getBegin());
116             int   index2 = container.indexOf(bond.getEnd());
117             if (bond.isAromatic()) {
118                 QueryBond qbond = new QueryBond(queryContainer.getAtom(index1),
119                                                 queryContainer.getAtom(index2),
120                                                 Expr.Type.IS_AROMATIC);
121                 queryContainer.addBond(qbond);
122             } else {
123                 QueryBond qbond = new QueryBond(queryContainer.getAtom(index1),
124                                                 queryContainer.getAtom(index2),
125                                                 Expr.Type.ORDER,
126                                                 bond.getOrder().numeric());
127                 qbond.setOrder(bond.getOrder()); // backwards compatibility
128                 queryContainer.addBond(qbond);
129             }
130         }
131         return queryContainer;
132     }
133 
134     /**
135      * Creates a QueryAtomContainer with the following settings:
136      *
137      * <pre>
138      * // aromaticity = true
139      * QueryAtomContainer.create(container,
140      *                           Expr.Type.IS_AROMATIC,
141      *                           Expr.Type.ALIPHATIC_ORDER);
142      * // aromaticity = false
143      * QueryAtomContainer.create(container,
144      *                           Expr.Type.ORDER);
145      * </pre>
146      *
147      * @param container   The AtomContainer that stands as model
148      * @param aromaticity option flag
149      * @return The new QueryAtomContainer created from container.
150      */
createAnyAtomContainer(IAtomContainer container, boolean aromaticity)151     public static QueryAtomContainer createAnyAtomContainer(IAtomContainer container, boolean aromaticity) {
152         if (aromaticity)
153             return QueryAtomContainer.create(container,
154                                              Expr.Type.IS_AROMATIC,
155                                              Expr.Type.ALIPHATIC_ORDER);
156         else
157             return QueryAtomContainer.create(container,
158                                              Expr.Type.ORDER);
159     }
160 
161     /**
162      * Creates a QueryAtomContainer with the following settings:
163      *
164      * <pre>
165      * // aromaticity = true
166      * QueryAtomContainer.create(container,
167      *                           Expr.Type.IS_AROMATIC);
168      * // aromaticity = false
169      * QueryAtomContainer.create(container);
170      * </pre>
171      *
172      * @param container   The AtomContainer that stands as model
173      * @param aromaticity option flag
174      * @return The new QueryAtomContainer created from container.
175      */
createAnyAtomAnyBondContainer(IAtomContainer container, boolean aromaticity)176     public static QueryAtomContainer createAnyAtomAnyBondContainer(IAtomContainer container, boolean aromaticity) {
177         if (aromaticity)
178             return QueryAtomContainer.create(container, Expr.Type.IS_AROMATIC);
179         else
180             return QueryAtomContainer.create(container);
181     }
182 
183     /**
184      * Creates a QueryAtomContainer with the following settings:
185      *
186      * <pre>
187      * QueryAtomContainer.create(container,
188      *                           Expr.Type.ELEMENT,
189      *                           Expr.Type.IS_AROMATIC,
190      *                           Expr.Type.ALIPHATIC_ORDER);
191      * </pre>
192      *
193      * @param container The AtomContainer that stands as model
194      * @return The new QueryAtomContainer created from container.
195      */
createAnyAtomForPseudoAtomQueryContainer(IAtomContainer container)196     public static QueryAtomContainer createAnyAtomForPseudoAtomQueryContainer(IAtomContainer container) {
197         return QueryAtomContainer.create(container,
198                                          Expr.Type.ELEMENT,
199                                          Expr.Type.IS_AROMATIC,
200                                          Expr.Type.ALIPHATIC_ORDER);
201     }
202 
isSimpleHydrogen(Expr expr)203     static boolean isSimpleHydrogen(Expr expr) {
204         switch (expr.type()) {
205             case ELEMENT:
206             case ALIPHATIC_ELEMENT:
207                 return expr.value() == 1;
208             default:
209                 return false;
210         }
211     }
212 
suppressQueryHydrogens(IAtomContainer mol)213     public static IAtomContainer suppressQueryHydrogens(IAtomContainer mol) {
214 
215         // pre-checks
216         for (IAtom atom : mol.atoms()) {
217             if (!(AtomRef.deref(atom) instanceof QueryAtom))
218                 throw new IllegalArgumentException("Non-query atoms found!");
219         }
220         for (IBond bond : mol.bonds()) {
221             if (!(BondRef.deref(bond) instanceof QueryBond))
222                 throw new IllegalArgumentException("Non-query bonds found!");
223         }
224 
225         Map<IChemObject,IChemObject> plainHydrogens = new HashMap<>();
226         for (IAtom atom : mol.atoms()) {
227             int hcnt = 0;
228             for (IAtom nbor : mol.getConnectedAtomsList(atom)) {
229                 QueryAtom qnbor = (QueryAtom) AtomRef.deref(nbor);
230                 if (mol.getConnectedBondsCount(nbor) == 1 &&
231                     isSimpleHydrogen(qnbor.getExpression())) {
232                     hcnt++;
233                     plainHydrogens.put(nbor, atom);
234                 }
235             }
236             if (hcnt > 0) {
237                 QueryAtom qatom = (QueryAtom) AtomRef.deref(atom);
238                 Expr e = qatom.getExpression();
239                 Expr hexpr = new Expr();
240                 for (int i = 0; i < hcnt; i++)
241                     hexpr.and(new Expr(Expr.Type.TOTAL_H_COUNT, i).negate());
242                 e.and(hexpr);
243             }
244         }
245 
246         // nothing to do
247         if (plainHydrogens.isEmpty())
248             return mol;
249 
250         IAtomContainer res = new QueryAtomContainer(mol.getBuilder());
251         for (IAtom atom : mol.atoms()) {
252             if (!plainHydrogens.containsKey(atom))
253                 res.addAtom(atom);
254         }
255         for (IBond bond : mol.bonds()) {
256             if (!plainHydrogens.containsKey(bond.getBegin()) &&
257                 !plainHydrogens.containsKey(bond.getEnd()))
258                 res.addBond(bond);
259         }
260         for (IStereoElement se : mol.stereoElements()) {
261             res.addStereoElement(se.map(plainHydrogens));
262         }
263 
264         return res;
265     }
266 }
267