1 /* Copyright (C) 2007-2015  Egon Willighagen <egonw@users.sf.net>
2  *                    2011  Nimish Gopal <nimishg@ebi.ac.uk>
3  *                    2011  Syed Asad Rahman <asad@ebi.ac.uk>
4  *                    2011  Gilleain Torrance <gilleain.torrance@gmail.com>
5  *
6  * Contact: cdk-devel@lists.sourceforge.net
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation, version 2.1.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 package org.openscience.cdk.atomtype;
22 
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Hashtable;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30 
31 import org.openscience.cdk.CDKConstants;
32 import org.openscience.cdk.config.AtomTypeFactory;
33 import org.openscience.cdk.exception.CDKException;
34 import org.openscience.cdk.interfaces.IAtom;
35 import org.openscience.cdk.interfaces.IAtomContainer;
36 import org.openscience.cdk.interfaces.IAtomType;
37 import org.openscience.cdk.interfaces.IAtomType.Hybridization;
38 import org.openscience.cdk.interfaces.IBond;
39 import org.openscience.cdk.interfaces.IBond.Order;
40 import org.openscience.cdk.interfaces.IChemObjectBuilder;
41 import org.openscience.cdk.interfaces.IPseudoAtom;
42 import org.openscience.cdk.interfaces.ISingleElectron;
43 import org.openscience.cdk.ringsearch.RingSearch;
44 import org.openscience.cdk.tools.manipulator.BondManipulator;
45 
46 /**
47  * Atom Type matcher that perceives atom types as defined in the CDK atom type list
48  * <code>org/openscience/cdk/dict/data/cdk-atom-types.owl</code>.
49  * If there is not an atom type defined for the tested atom, then NULL
50  * is returned.
51  *
52  * @author         egonw
53  * @cdk.created    2007-07-20
54  * @cdk.module     core
55  * @cdk.githash
56  */
57 public class CDKAtomTypeMatcher implements IAtomTypeMatcher {
58 
59     public final static int                                                  REQUIRE_NOTHING            = 1;
60     public final static int                                                  REQUIRE_EXPLICIT_HYDROGENS = 2;
61 
62     private AtomTypeFactory                                                  factory;
63     private int                                                              mode;
64 
65     private final static Object                                              LOCK                       = new Object();
66 
67     private static Map<Integer, Map<IChemObjectBuilder, CDKAtomTypeMatcher>> factories                  = new ConcurrentHashMap<>(5);
68 
CDKAtomTypeMatcher(IChemObjectBuilder builder, int mode)69     private CDKAtomTypeMatcher(IChemObjectBuilder builder, int mode) {
70         factory = AtomTypeFactory.getInstance("org/openscience/cdk/dict/data/cdk-atom-types.owl", builder);
71         this.mode = mode;
72     }
73 
getInstance(IChemObjectBuilder builder)74     public static CDKAtomTypeMatcher getInstance(IChemObjectBuilder builder) {
75         return getInstance(builder, REQUIRE_NOTHING);
76     }
77 
getInstance(IChemObjectBuilder builder, int mode)78     public static CDKAtomTypeMatcher getInstance(IChemObjectBuilder builder, int mode) {
79         synchronized (LOCK) {
80             if (!factories.containsKey(mode))
81                 factories.put(mode, new Hashtable<IChemObjectBuilder, CDKAtomTypeMatcher>(1));
82             if (!factories.get(mode).containsKey(builder))
83                 factories.get(mode).put(builder, new CDKAtomTypeMatcher(builder, mode));
84             return factories.get(mode).get(builder);
85         }
86     }
87 
88     /** {@inheritDoc} */
89     @Override
findMatchingAtomTypes(IAtomContainer atomContainer)90     public IAtomType[] findMatchingAtomTypes(IAtomContainer atomContainer) throws CDKException {
91         return findMatchingAtomTypes(atomContainer, null);
92     }
93 
findMatchingAtomTypes(IAtomContainer atomContainer, RingSearch searcher)94     private IAtomType[] findMatchingAtomTypes(IAtomContainer atomContainer, RingSearch searcher) throws CDKException {
95     	// cache the ring information
96     	if (searcher == null) searcher = new RingSearch(atomContainer);
97     	// cache atom bonds
98     	Map<IAtom, List<IBond>> connectedBonds = new HashMap<IAtom,List<IBond>>(atomContainer.getAtomCount());
99     	for (IBond bond : atomContainer.bonds()) {
100     		for (IAtom atom : bond.atoms()) {
101     			List<IBond> atomBonds = connectedBonds.get(atom);
102     			if (atomBonds == null) {
103     				atomBonds = new ArrayList<>(4);
104     				connectedBonds.put(atom, atomBonds);
105     			}
106     			atomBonds.add(bond);
107     		}
108     	}
109 
110         IAtomType[] types = new IAtomType[atomContainer.getAtomCount()];
111         int typeCounter = 0;
112         for (IAtom atom : atomContainer.atoms()) {
113             types[typeCounter] = findMatchingAtomType(atomContainer, atom, searcher, connectedBonds.get(atom));
114             typeCounter++;
115         }
116         return types;
117     }
118 
119     /** {@inheritDoc} */
120     @Override
findMatchingAtomType(IAtomContainer atomContainer, IAtom atom)121     public IAtomType findMatchingAtomType(IAtomContainer atomContainer, IAtom atom) throws CDKException {
122     	return findMatchingAtomType(atomContainer, atom, null, null);
123     }
124 
findMatchingAtomType(IAtomContainer atomContainer, IAtom atom, RingSearch searcher, List<IBond> connectedBonds)125     private IAtomType findMatchingAtomType(IAtomContainer atomContainer, IAtom atom, RingSearch searcher, List<IBond> connectedBonds) throws CDKException {
126         IAtomType type = null;
127         if (atom instanceof IPseudoAtom) {
128             return factory.getAtomType("X");
129         }
130         if ("C".equals(atom.getSymbol())) {
131             type = perceiveCarbons(atomContainer, atom, searcher, connectedBonds);
132         } else if ("H".equals(atom.getSymbol())) {
133             type = perceiveHydrogens(atomContainer, atom, connectedBonds);
134         } else if ("O".equals(atom.getSymbol())) {
135             type = perceiveOxygens(atomContainer, atom, searcher, connectedBonds);
136         } else if ("N".equals(atom.getSymbol())) {
137             type = perceiveNitrogens(atomContainer, atom, searcher, connectedBonds);
138         } else if ("S".equals(atom.getSymbol())) {
139             type = perceiveSulphurs(atomContainer, atom, searcher, connectedBonds);
140         } else if ("P".equals(atom.getSymbol())) {
141             type = perceivePhosphors(atomContainer, atom, connectedBonds);
142         } else if ("Si".equals(atom.getSymbol())) {
143             type = perceiveSilicon(atomContainer, atom);
144         } else if ("Li".equals(atom.getSymbol())) {
145             type = perceiveLithium(atomContainer, atom);
146         } else if ("B".equals(atom.getSymbol())) {
147             type = perceiveBorons(atomContainer, atom);
148         } else if ("Be".equals(atom.getSymbol())) {
149             type = perceiveBeryllium(atomContainer, atom);
150         } else if ("Cr".equals(atom.getSymbol())) {
151             type = perceiveChromium(atomContainer, atom);
152         } else if ("Se".equals(atom.getSymbol())) {
153             type = perceiveSelenium(atomContainer, atom, connectedBonds);
154         } else if ("Mo".equals(atom.getSymbol())) {
155             type = perceiveMolybdenum(atomContainer, atom);
156         } else if ("Rb".equals(atom.getSymbol())) {
157             type = perceiveRubidium(atomContainer, atom);
158         } else if ("Te".equals(atom.getSymbol())) {
159             type = perceiveTellurium(atomContainer, atom);
160         } else if ("Cu".equals(atom.getSymbol())) {
161             type = perceiveCopper(atomContainer, atom);
162         } else if ("Ba".equals(atom.getSymbol())) {
163             type = perceiveBarium(atomContainer, atom);
164         } else if ("Ga".equals(atom.getSymbol())) {
165             type = perceiveGallium(atomContainer, atom);
166         } else if ("Ru".equals(atom.getSymbol())) {
167             type = perceiveRuthenium(atomContainer, atom);
168         } else if ("Zn".equals(atom.getSymbol())) {
169             type = perceiveZinc(atomContainer, atom);
170         } else if ("Al".equals(atom.getSymbol())) {
171             type = perceiveAluminium(atomContainer, atom);
172         } else if ("Ni".equals(atom.getSymbol())) {
173             type = perceiveNickel(atomContainer, atom);
174         } else if ("Gd".equals(atom.getSymbol())) {
175             type = perceiveGadolinum(atomContainer, atom);
176         } else if ("Ge".equals(atom.getSymbol())) {
177             type = perceiveGermanium(atomContainer, atom);
178         } else if ("Co".equals(atom.getSymbol())) {
179             type = perceiveCobalt(atomContainer, atom);
180         } else if ("Br".equals(atom.getSymbol())) {
181             type = perceiveBromine(atomContainer, atom);
182         } else if ("V".equals(atom.getSymbol())) {
183             type = perceiveVanadium(atomContainer, atom);
184         } else if ("Ti".equals(atom.getSymbol())) {
185             type = perceiveTitanium(atomContainer, atom);
186         } else if ("Sr".equals(atom.getSymbol())) {
187             type = perceiveStrontium(atomContainer, atom);
188         } else if ("Pb".equals(atom.getSymbol())) {
189             type = perceiveLead(atomContainer, atom);
190         } else if ("Tl".equals(atom.getSymbol())) {
191             type = perceiveThallium(atomContainer, atom);
192         } else if ("Sb".equals(atom.getSymbol())) {
193             type = perceiveAntimony(atomContainer, atom);
194         } else if ("Pt".equals(atom.getSymbol())) {
195             type = perceivePlatinum(atomContainer, atom);
196         } else if ("Hg".equals(atom.getSymbol())) {
197             type = perceiveMercury(atomContainer, atom);
198         } else if ("Fe".equals(atom.getSymbol())) {
199             type = perceiveIron(atomContainer, atom);
200         } else if ("Ra".equals(atom.getSymbol())) {
201             type = perceiveRadium(atomContainer, atom);
202         } else if ("Au".equals(atom.getSymbol())) {
203             type = perceiveGold(atomContainer, atom);
204         } else if ("Ag".equals(atom.getSymbol())) {
205             type = perceiveSilver(atomContainer, atom);
206         } else if ("Cl".equals(atom.getSymbol())) {
207             type = perceiveChlorine(atomContainer, atom, connectedBonds);
208         } else if ("In".equals(atom.getSymbol())) {
209             type = perceiveIndium(atomContainer, atom);
210         } else if ("Pu".equals(atom.getSymbol())) {
211             type = perceivePlutonium(atomContainer, atom);
212         } else if ("Th".equals(atom.getSymbol())) {
213             type = perceiveThorium(atomContainer, atom);
214         } else if ("K".equals(atom.getSymbol())) {
215             type = perceivePotassium(atomContainer, atom);
216         } else if ("Mn".equals(atom.getSymbol())) {
217             type = perceiveManganese(atomContainer, atom);
218         } else if ("Mg".equals(atom.getSymbol())) {
219             type = perceiveMagnesium(atomContainer, atom);
220         } else if ("Na".equals(atom.getSymbol())) {
221             type = perceiveSodium(atomContainer, atom);
222         } else if ("As".equals(atom.getSymbol())) {
223             type = perceiveArsenic(atomContainer, atom);
224         } else if ("Cd".equals(atom.getSymbol())) {
225             type = perceiveCadmium(atomContainer, atom);
226         } else if ("Ca".equals(atom.getSymbol())) {
227             type = perceiveCalcium(atomContainer, atom);
228         } else {
229             if (type == null) type = perceiveHalogens(atomContainer, atom, connectedBonds);
230             if (type == null) type = perceiveCommonSalts(atomContainer, atom);
231             if (type == null) type = perceiveOrganometallicCenters(atomContainer, atom);
232             if (type == null) type = perceiveNobelGases(atomContainer, atom);
233         }
234 
235         // if no atom type can be assigned we set the atom type to 'X', this flags
236         // to other methods that atom typing was performed but did not yield a match
237         if (type == null) {
238             type = getAtomType("X");
239         }
240 
241         return type;
242     }
243 
perceiveGallium(IAtomContainer atomContainer, IAtom atom)244     private IAtomType perceiveGallium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
245         IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
246         if (!isCharged(atom) && maxBondOrder == IBond.Order.SINGLE && atomContainer.getConnectedBondsCount(atom) <= 3) {
247             IAtomType type = getAtomType("Ga");
248             if (isAcceptable(atom, atomContainer, type)) return type;
249         } else if (atom.getFormalCharge() == 3) {
250             IAtomType type = getAtomType("Ga.3plus");
251             if (isAcceptable(atom, atomContainer, type)) return type;
252         }
253         return null;
254     }
255 
perceiveGermanium(IAtomContainer atomContainer, IAtom atom)256     private IAtomType perceiveGermanium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
257         IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
258         if (!isCharged(atom) && maxBondOrder == IBond.Order.SINGLE && atomContainer.getConnectedBondsCount(atom) <= 4) {
259             IAtomType type = getAtomType("Ge");
260             if (isAcceptable(atom, atomContainer, type)) return type;
261         }
262         if (atom.getFormalCharge() == 0 && atomContainer.getConnectedBondsCount(atom) == 3) {
263             IAtomType type = getAtomType("Ge.3");
264             if (isAcceptable(atom, atomContainer, type)) return type;
265         }
266         return null;
267     }
268 
perceiveSelenium(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds)269     private IAtomType perceiveSelenium(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds) throws CDKException {
270         if ("Se".equals(atom.getSymbol())) {
271         	if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
272         	int doublebondcount = countAttachedDoubleBonds(connectedBonds, atom);
273             if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) {
274                 if (atomContainer.getConnectedBondsCount(atom) == 0) {
275                     if (atom.getImplicitHydrogenCount() != null && atom.getImplicitHydrogenCount() == 0) {
276                         IAtomType type = getAtomType("Se.2");
277                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
278                     } else {
279                         IAtomType type = getAtomType("Se.3");
280                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
281                     }
282                 } else if (atomContainer.getConnectedBondsCount(atom) == 1) {
283 
284                     if (doublebondcount == 1) {
285                         IAtomType type = getAtomType("Se.1");
286                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
287                     } else if (doublebondcount == 0) {
288                         IAtomType type = getAtomType("Se.3");
289                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
290                     }
291                 } else if (atomContainer.getConnectedBondsCount(atom) == 2) {
292                     if (doublebondcount == 0) {
293                         IAtomType type = getAtomType("Se.3");
294                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
295                     } else if (doublebondcount == 2) {
296                         IAtomType type = getAtomType("Se.sp2.2");
297                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
298                     }
299                 } else if (atomContainer.getConnectedBondsCount(atom) == 3) {
300                     IAtomType type = getAtomType("Se.sp3.3");
301                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
302                 } else if (atomContainer.getConnectedBondsCount(atom) == 4) {
303                     if (doublebondcount == 2) {
304                         IAtomType type = getAtomType("Se.sp3.4");
305                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
306                     } else if (doublebondcount == 0) {
307                         IAtomType type = getAtomType("Se.sp3d1.4");
308                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
309                     }
310                 } else if (atomContainer.getConnectedBondsCount(atom) == 5) {
311                     IAtomType type = getAtomType("Se.5");
312                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
313                 }
314             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 4)
315                     && atomContainer.getConnectedBondsCount(atom) == 0) {
316                 IAtomType type = getAtomType("Se.4plus");
317                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
318             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 1)
319                     && atomContainer.getConnectedBondsCount(atom) == 3) {
320                 IAtomType type = getAtomType("Se.plus.3");
321                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
322             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -2)
323                     && atomContainer.getConnectedBondsCount(atom) == 0) {
324                 IAtomType type = getAtomType("Se.2minus");
325                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
326             }
327         }
328         return null;
329     }
330 
perceiveTellurium(IAtomContainer atomContainer, IAtom atom)331     private IAtomType perceiveTellurium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
332         IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
333         if (!isCharged(atom) && maxBondOrder == IBond.Order.SINGLE && atomContainer.getConnectedBondsCount(atom) <= 2) {
334             IAtomType type = getAtomType("Te.3");
335             if (isAcceptable(atom, atomContainer, type)) return type;
336         } else if (atom.getFormalCharge() == 4) {
337             if (atomContainer.getConnectedBondsCount(atom) == 0) {
338                 IAtomType type = getAtomType("Te.4plus");
339                 if (isAcceptable(atom, atomContainer, type)) return type;
340             }
341         }
342         return null;
343     }
344 
perceiveBorons(IAtomContainer atomContainer, IAtom atom)345     private IAtomType perceiveBorons(IAtomContainer atomContainer, IAtom atom) throws CDKException {
346         IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
347         if (atom.getFormalCharge() == -1 && maxBondOrder == IBond.Order.SINGLE
348                 && atomContainer.getConnectedBondsCount(atom) <= 4) {
349             IAtomType type = getAtomType("B.minus");
350             if (isAcceptable(atom, atomContainer, type)) return type;
351         } else if (atom.getFormalCharge() == +3 && atomContainer.getConnectedBondsCount(atom) == 4) {
352             IAtomType type = getAtomType("B.3plus");
353             if (isAcceptable(atom, atomContainer, type)) return type;
354         } else if (atomContainer.getConnectedBondsCount(atom) <= 3) {
355             IAtomType type = getAtomType("B");
356             if (isAcceptable(atom, atomContainer, type)) return type;
357         }
358         return null;
359     }
360 
perceiveBeryllium(IAtomContainer atomContainer, IAtom atom)361     private IAtomType perceiveBeryllium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
362         if (atom.getFormalCharge() == -2 && atomContainer.getMaximumBondOrder(atom) == IBond.Order.SINGLE
363                 && atomContainer.getConnectedBondsCount(atom) <= 4) {
364             IAtomType type = getAtomType("Be.2minus");
365             if (isAcceptable(atom, atomContainer, type)) return type;
366         } else if (atom.getFormalCharge() == 0 && atomContainer.getConnectedBondsCount(atom) == 0) {
367             IAtomType type = getAtomType("Be.neutral");
368             if (isAcceptable(atom, atomContainer, type)) return type;
369         }
370         return null;
371     }
372 
perceiveCarbonRadicals(IAtomContainer atomContainer, IAtom atom)373     private IAtomType perceiveCarbonRadicals(IAtomContainer atomContainer, IAtom atom) throws CDKException {
374         if (atomContainer.getConnectedBondsCount(atom) == 0) {
375             IAtomType type = getAtomType("C.radical.planar");
376             if (isAcceptable(atom, atomContainer, type)) return type;
377         } else if (atomContainer.getConnectedBondsCount(atom) <= 3) {
378             IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
379             if (maxBondOrder == IBond.Order.SINGLE) {
380                 IAtomType type = getAtomType("C.radical.planar");
381                 if (isAcceptable(atom, atomContainer, type)) return type;
382             } else if (maxBondOrder == IBond.Order.DOUBLE) {
383                 IAtomType type = getAtomType("C.radical.sp2");
384                 if (isAcceptable(atom, atomContainer, type)) return type;
385             } else if (maxBondOrder == IBond.Order.TRIPLE) {
386                 IAtomType type = getAtomType("C.radical.sp1");
387                 if (isAcceptable(atom, atomContainer, type)) return type;
388             }
389         }
390         return null;
391     }
392 
perceiveCarbons(IAtomContainer atomContainer, IAtom atom, RingSearch searcher, List<IBond> connectedBonds)393     private IAtomType perceiveCarbons(IAtomContainer atomContainer, IAtom atom,
394     		                          RingSearch searcher, List<IBond> connectedBonds) throws CDKException {
395     	if (hasOneSingleElectron(atomContainer, atom)) {
396             return perceiveCarbonRadicals(atomContainer, atom);
397         }
398     	if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
399         // if hybridization is given, use that
400         if (hasHybridization(atom) && !isCharged(atom)) {
401             if (atom.getHybridization() == Hybridization.SP2) {
402                 IAtomType type = getAtomType("C.sp2");
403                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
404             } else if (atom.getHybridization() == Hybridization.SP3) {
405                 IAtomType type = getAtomType("C.sp3");
406                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
407             } else if (atom.getHybridization() == Hybridization.SP1) {
408                 IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
409                 if (maxBondOrder == Order.TRIPLE) {
410                     IAtomType type = getAtomType("C.sp");
411                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
412                 } else {
413                     IAtomType type = getAtomType("C.allene");
414                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
415                 }
416             }
417         } else if (isCharged(atom)) {
418             if (atom.getFormalCharge() == 1) {
419                 if (connectedBonds.isEmpty()) {
420                     IAtomType type = getAtomType("C.plus.sp2");
421                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
422                 } else {
423                 	IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
424                     if (maxBondOrder == Order.TRIPLE) {
425                         IAtomType type = getAtomType("C.plus.sp1");
426                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
427                     } else if (maxBondOrder == Order.DOUBLE) {
428                         IAtomType type = getAtomType("C.plus.sp2");
429                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
430                     } else if (maxBondOrder == Order.SINGLE) {
431                         IAtomType type = getAtomType("C.plus.planar");
432                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
433                     }
434                 }
435             } else if (atom.getFormalCharge() == -1) {
436                 IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
437                 if (maxBondOrder == Order.SINGLE && connectedBonds.size() <= 3) {
438                     if (bothNeighborsAreSp2(atom, atomContainer, connectedBonds) && isRingAtom(atom, atomContainer, searcher)) {
439                         IAtomType type = getAtomType("C.minus.planar");
440                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
441                     }
442                     IAtomType type = getAtomType("C.minus.sp3");
443                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
444                 } else if (maxBondOrder == Order.DOUBLE
445                         && connectedBonds.size() <= 3) {
446                     IAtomType type = getAtomType("C.minus.sp2");
447                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
448                 } else if (maxBondOrder == Order.TRIPLE
449                         && connectedBonds.size() <= 1) {
450                     IAtomType type = getAtomType("C.minus.sp1");
451                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
452                 }
453             }
454             return null;
455         } else if (atom.getFlag(CDKConstants.ISAROMATIC)) {
456             IAtomType type = getAtomType("C.sp2");
457             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
458         } else if (hasOneOrMoreSingleOrDoubleBonds(connectedBonds)) {
459             IAtomType type = getAtomType("C.sp2");
460             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
461         } else if (connectedBonds.size() > 4) {
462             // FIXME: I don't perceive carbons with more than 4 connections yet
463             return null;
464         } else { // OK, use bond order info
465             Order maxBondOrder = getMaximumBondOrder(connectedBonds);
466             if (maxBondOrder == Order.QUADRUPLE) {
467                 // WTF??
468                 return null;
469             } else if (maxBondOrder == Order.TRIPLE) {
470                 IAtomType type = getAtomType("C.sp");
471                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
472             } else if (maxBondOrder == Order.DOUBLE) {
473                 // OK, one or two double bonds?
474                 int doubleBondCount = countAttachedDoubleBonds(connectedBonds, atom);
475                 if (doubleBondCount == 2) {
476                     IAtomType type = getAtomType("C.allene");
477                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
478                 } else if (doubleBondCount == 1) {
479                     IAtomType type = getAtomType("C.sp2");
480                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
481                 }
482             } else {
483                 if (hasAromaticBond(connectedBonds)) {
484                     IAtomType type = getAtomType("C.sp2");
485                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
486                 }
487                 IAtomType type = getAtomType("C.sp3");
488                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
489             }
490         }
491         return null;
492     }
493 
getMaximumBondOrder(List<IBond> connectedBonds)494     private Order getMaximumBondOrder(List<IBond> connectedBonds) {
495     	IBond.Order max = IBond.Order.SINGLE;
496     	for (IBond bond : connectedBonds) {
497             if (bond.getOrder().numeric() > max.numeric())
498             	max = bond.getOrder();
499         }
500         return max;
501 	}
502 
hasOneOrMoreSingleOrDoubleBonds(List<IBond> bonds)503 	private boolean hasOneOrMoreSingleOrDoubleBonds(List<IBond> bonds) {
504         for (IBond bond : bonds) {
505             if (bond.getFlag(CDKConstants.SINGLE_OR_DOUBLE)) return true;
506         }
507         return false;
508     }
509 
hasOneSingleElectron(IAtomContainer atomContainer, IAtom atom)510     private boolean hasOneSingleElectron(IAtomContainer atomContainer, IAtom atom) {
511     	if (atomContainer.getSingleElectronCount() == 0) return false;
512         Iterator<ISingleElectron> singleElectrons = atomContainer.singleElectrons().iterator();
513         while (singleElectrons.hasNext()) {
514             if (singleElectrons.next().contains(atom)) return true;
515         }
516         return false;
517     }
518 
countSingleElectrons(IAtomContainer atomContainer, IAtom atom)519     private int countSingleElectrons(IAtomContainer atomContainer, IAtom atom) {
520     	// if there are no single electrons at all, then certainly not for any atom
521     	if (atomContainer.getSingleElectronCount() == 0) return 0;
522         Iterator<ISingleElectron> singleElectrons = atomContainer.singleElectrons().iterator();
523         int count = 0;
524         while (singleElectrons.hasNext()) {
525             if (singleElectrons.next().contains(atom)) count++;
526         }
527         return count;
528     }
529 
perceiveOxygenRadicals(IAtomContainer atomContainer, IAtom atom)530     private IAtomType perceiveOxygenRadicals(IAtomContainer atomContainer, IAtom atom) throws CDKException {
531         if (atom.getFormalCharge() == 0) {
532             if (atomContainer.getConnectedBondsCount(atom) <= 1) {
533                 IAtomType type = getAtomType("O.sp3.radical");
534                 if (isAcceptable(atom, atomContainer, type)) return type;
535             }
536         } else if (atom.getFormalCharge() == +1) {
537             if (atomContainer.getConnectedBondsCount(atom) == 0) {
538                 IAtomType type = getAtomType("O.plus.radical");
539                 if (isAcceptable(atom, atomContainer, type)) return type;
540             } else if (atomContainer.getConnectedBondsCount(atom) <= 2) {
541                 IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
542                 if (maxBondOrder == IBond.Order.SINGLE) {
543                     IAtomType type = getAtomType("O.plus.radical");
544                     if (isAcceptable(atom, atomContainer, type)) return type;
545                 } else if (maxBondOrder == IBond.Order.DOUBLE) {
546                     IAtomType type = getAtomType("O.plus.sp2.radical");
547                     if (isAcceptable(atom, atomContainer, type)) return type;
548                 }
549             }
550         }
551         return null;
552     }
553 
isCharged(IAtom atom)554     private boolean isCharged(IAtom atom) {
555         return (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() != 0);
556     }
557 
hasHybridization(IAtom atom)558     private boolean hasHybridization(IAtom atom) {
559         return atom.getHybridization() != CDKConstants.UNSET;
560     }
561 
perceiveOxygens(IAtomContainer atomContainer, IAtom atom, RingSearch searcher, List<IBond> connectedBonds)562     private IAtomType perceiveOxygens(IAtomContainer atomContainer, IAtom atom,
563     		                          RingSearch searcher, List<IBond> connectedBonds) throws CDKException {
564         if (hasOneSingleElectron(atomContainer, atom)) {
565             return perceiveOxygenRadicals(atomContainer, atom);
566         }
567 
568         // if hybridization is given, use that
569         if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
570         if (hasHybridization(atom) && !isCharged(atom)) {
571             if (atom.getHybridization() == Hybridization.SP2) {
572                 int connectedAtomsCount = connectedBonds.size();
573                 if (connectedAtomsCount == 1) {
574                     if (isCarboxylate(atomContainer, atom, connectedBonds)) {
575                         IAtomType type = getAtomType("O.sp2.co2");
576                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
577                     } else {
578                         IAtomType type = getAtomType("O.sp2");
579                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
580                     }
581                 } else if (connectedAtomsCount == 2) {
582                     IAtomType type = getAtomType("O.planar3");
583                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
584                 }
585             } else if (atom.getHybridization() == Hybridization.SP3) {
586                 IAtomType type = getAtomType("O.sp3");
587                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
588             } else if (atom.getHybridization() == Hybridization.PLANAR3) {
589                 IAtomType type = getAtomType("O.planar3");
590                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
591             }
592         } else if (isCharged(atom)) {
593             if (atom.getFormalCharge() == -1 && connectedBonds.size() <= 1) {
594                 if (isCarboxylate(atomContainer, atom, connectedBonds)) {
595                     IAtomType type = getAtomType("O.minus.co2");
596                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
597                 } else {
598                     IAtomType type = getAtomType("O.minus");
599                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
600                 }
601             } else if (atom.getFormalCharge() == -2 && connectedBonds.size() == 0) {
602                 IAtomType type = getAtomType("O.minus2");
603                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
604             } else if (atom.getFormalCharge() == +1) {
605                 if (connectedBonds.size() == 0) {
606                     IAtomType type = getAtomType("O.plus");
607                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
608                 }
609                 IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
610                 if (maxBondOrder == Order.DOUBLE) {
611                     IAtomType type = getAtomType("O.plus.sp2");
612                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
613                 } else if (maxBondOrder == Order.TRIPLE) {
614                     IAtomType type = getAtomType("O.plus.sp1");
615                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
616                 } else {
617                     IAtomType type = getAtomType("O.plus");
618                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
619                 }
620             }
621             return null;
622         } else if (connectedBonds.size() > 2) {
623             // FIXME: I don't perceive carbons with more than 4 connections yet
624             return null;
625         } else if (connectedBonds.size() == 0) {
626             IAtomType type = getAtomType("O.sp3");
627             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
628         } else { // OK, use bond order info
629             IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
630             if (maxBondOrder == Order.DOUBLE) {
631                 if (isCarboxylate(atomContainer, atom, connectedBonds)) {
632                     IAtomType type = getAtomType("O.sp2.co2");
633                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
634                 } else {
635                     IAtomType type = getAtomType("O.sp2");
636                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
637                 }
638             } else if (maxBondOrder == Order.SINGLE) {
639                 int explicitHydrogens = countExplicitHydrogens(atom, connectedBonds);
640                 int connectedHeavyAtoms = connectedBonds.size() - explicitHydrogens;
641                 if (connectedHeavyAtoms == 2) {
642                     // a O.sp3 which is expected to take part in an aromatic system
643                     if (bothNeighborsAreSp2(atom, atomContainer, connectedBonds) && isRingAtom(atom, atomContainer, searcher)) {
644                         IAtomType type = getAtomType("O.planar3");
645                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
646                     }
647                     IAtomType type = getAtomType("O.sp3");
648                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
649                 } else {
650                     IAtomType type = getAtomType("O.sp3");
651                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
652                 }
653             }
654         }
655         return null;
656     }
657 
isCarboxylate(IAtomContainer container, IAtom atom, List<IBond> connectedBonds)658     private boolean isCarboxylate(IAtomContainer container, IAtom atom, List<IBond> connectedBonds) {
659         // assumes that the oxygen only has one neighbor (C=O, or C-[O-])
660         if (connectedBonds.size() != 1) return false;
661         IAtom carbon = connectedBonds.get(0).getOther(atom);
662         if (!"C".equals(carbon.getSymbol())) return false;
663 
664         List<IBond> carbonBonds = container.getConnectedBondsList(carbon);
665         if (carbonBonds.size() < 2) return false;
666         int oxygenCount = 0;
667         int singleBondedNegativeOxygenCount = 0;
668         int doubleBondedOxygenCount = 0;
669         for (IBond cBond : carbonBonds) {
670             IAtom neighbor = cBond.getOther(carbon);
671             if ("O".equals(neighbor.getSymbol())) {
672                 oxygenCount++;
673                 IBond.Order order = cBond.getOrder();
674                 Integer charge = neighbor.getFormalCharge();
675                 if (order == IBond.Order.SINGLE && charge != null && charge == -1) {
676                     singleBondedNegativeOxygenCount++;
677                 } else if (order == IBond.Order.DOUBLE) {
678                     doubleBondedOxygenCount++;
679                 }
680             }
681         }
682         return (oxygenCount == 2) && (singleBondedNegativeOxygenCount == 1) && (doubleBondedOxygenCount == 1);
683     }
684 
atLeastTwoNeighborsAreSp2(IAtom atom, IAtomContainer atomContainer, List<IBond> connectedBonds)685     private boolean atLeastTwoNeighborsAreSp2(IAtom atom, IAtomContainer atomContainer, List<IBond> connectedBonds) {
686     	int count = 0;
687     	for (IBond bond : connectedBonds) {
688     		if (bond.getOrder() == Order.DOUBLE || bond.isAromatic()) {
689     			count++;
690     		} else {
691     			IAtom nextAtom = bond.getOther(atom);
692     			if (nextAtom.getHybridization() != CDKConstants.UNSET &&
693     					nextAtom.getHybridization() == Hybridization.SP2) {
694     				// OK, it's SP2
695     				count++;
696     			} else {
697     				List<IBond> nextConnectBonds = atomContainer.getConnectedBondsList(nextAtom);
698     				if (countAttachedDoubleBonds(nextConnectBonds, nextAtom) > 0) {
699     					// OK, it's SP2
700     					count++;
701     				}
702     			}
703     		}
704     		if (count >= 2) return true;
705     	}
706     	return false;
707     }
708 
bothNeighborsAreSp2(IAtom atom, IAtomContainer atomContainer, List<IBond> connectedBonds)709     private boolean bothNeighborsAreSp2(IAtom atom, IAtomContainer atomContainer, List<IBond> connectedBonds) {
710         return atLeastTwoNeighborsAreSp2(atom, atomContainer, connectedBonds);
711     }
712 
perceiveNitrogenRadicals(IAtomContainer atomContainer, IAtom atom)713     private IAtomType perceiveNitrogenRadicals(IAtomContainer atomContainer, IAtom atom) throws CDKException {
714         if (atomContainer.getConnectedBondsCount(atom) >= 1 && atomContainer.getConnectedBondsCount(atom) <= 2) {
715             IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
716             if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1) {
717                 if (maxBondOrder == IBond.Order.DOUBLE) {
718                     IAtomType type = getAtomType("N.plus.sp2.radical");
719                     if (isAcceptable(atom, atomContainer, type)) return type;
720                 } else if (maxBondOrder == IBond.Order.SINGLE) {
721                     IAtomType type = getAtomType("N.plus.sp3.radical");
722                     if (isAcceptable(atom, atomContainer, type)) return type;
723                 }
724             } else if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
725                 if (maxBondOrder == IBond.Order.SINGLE) {
726                     IAtomType type = getAtomType("N.sp3.radical");
727                     if (isAcceptable(atom, atomContainer, type)) return type;
728                 } else if (maxBondOrder == IBond.Order.DOUBLE) {
729                     IAtomType type = getAtomType("N.sp2.radical");
730                     if (isAcceptable(atom, atomContainer, type)) return type;
731                 }
732             }
733         } else {
734             IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
735             if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1
736                     && maxBondOrder == IBond.Order.SINGLE) {
737                 IAtomType type = getAtomType("N.plus.sp3.radical");
738                 if (isAcceptable(atom, atomContainer, type)) return type;
739             }
740         }
741         return null;
742     }
743 
perceiveMolybdenum(IAtomContainer atomContainer, IAtom atom)744     private IAtomType perceiveMolybdenum(IAtomContainer atomContainer, IAtom atom) throws CDKException {
745         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) {
746             int neighbors = atomContainer.getConnectedBondsCount(atom);
747             if (neighbors == 4) {
748                 IAtomType type = getAtomType("Mo.4");
749                 if (isAcceptable(atom, atomContainer, type)) {
750                     return type;
751                 }
752             }
753             IAtomType type1 = getAtomType("Mo.metallic");
754             if (isAcceptable(atom, atomContainer, type1)) {
755                 return type1;
756             }
757         }
758         return null;
759     }
760 
perceiveNitrogens(IAtomContainer atomContainer, IAtom atom, RingSearch searcher, List<IBond> connectedBonds)761     private IAtomType perceiveNitrogens(IAtomContainer atomContainer, IAtom atom,
762     		                            RingSearch searcher, List<IBond> connectedBonds) throws CDKException {
763         // if hybridization is given, use that
764         if (hasOneSingleElectron(atomContainer, atom)) {
765             return perceiveNitrogenRadicals(atomContainer, atom);
766         }
767 
768         if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
769         if (hasHybridization(atom) && !isCharged(atom)) {
770             if (atom.getHybridization() == Hybridization.SP1) {
771                 int neighborCount = connectedBonds.size();
772                 if (neighborCount > 1) {
773                     IAtomType type = getAtomType("N.sp1.2");
774                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
775                 } else {
776                     IAtomType type = getAtomType("N.sp1");
777                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
778                 }
779             } else if (atom.getHybridization() == Hybridization.SP2) {
780                 if (isAmide(atom, atomContainer, connectedBonds)) {
781                     IAtomType type = getAtomType("N.amide");
782                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
783                 } else if (isThioAmide(atom, atomContainer, connectedBonds)) {
784                     IAtomType type = getAtomType("N.thioamide");
785                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
786                 }
787                 // but an sp2 hyb N might N.sp2 or N.planar3 (pyrrole), so check for the latter
788                 int neighborCount = connectedBonds.size();
789                 if (neighborCount == 4 && IBond.Order.DOUBLE == getMaximumBondOrder(connectedBonds)) {
790                     IAtomType type = getAtomType("N.oxide");
791                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
792                 } else if (neighborCount > 1 && bothNeighborsAreSp2(atom, atomContainer, connectedBonds)) {
793                     if (isRingAtom(atom, atomContainer, searcher)) {
794                         if (neighborCount == 3) {
795                             IBond.Order maxOrder = getMaximumBondOrder(connectedBonds);
796                             if (maxOrder == IBond.Order.DOUBLE) {
797                                 IAtomType type = getAtomType("N.sp2.3");
798                                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
799                             } else if (maxOrder == IBond.Order.SINGLE) {
800                                 IAtomType type = getAtomType("N.planar3");
801                                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
802                             }
803                         } else if (neighborCount == 2) {
804                             IBond.Order maxOrder = getMaximumBondOrder(connectedBonds);
805                             if (maxOrder == IBond.Order.SINGLE) {
806                                 if (atom.getImplicitHydrogenCount() != CDKConstants.UNSET
807                                         && atom.getImplicitHydrogenCount() == 1) {
808                                     IAtomType type = getAtomType("N.planar3");
809                                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
810                                 } else {
811                                     IAtomType type = getAtomType("N.sp2");
812                                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
813                                 }
814                             } else if (maxOrder == IBond.Order.DOUBLE) {
815                                 IAtomType type = getAtomType("N.sp2");
816                                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
817                             }
818                         }
819                     }
820                 }
821                 IAtomType type = getAtomType("N.sp2");
822                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
823             } else if (atom.getHybridization() == Hybridization.SP3) {
824                 IAtomType type = getAtomType("N.sp3");
825                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
826             } else if (atom.getHybridization() == Hybridization.PLANAR3) {
827                 IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
828                 if (connectedBonds.size() == 3 && maxBondOrder == Order.DOUBLE
829                         && countAttachedDoubleBonds(connectedBonds, atom, "O") == 2) {
830                     IAtomType type = getAtomType("N.nitro");
831                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
832                 }
833                 IAtomType type = getAtomType("N.planar3");
834                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
835             }
836         } else if (isCharged(atom)) {
837             if (atom.getFormalCharge() == 1) {
838                 IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
839                 if (maxBondOrder == Order.SINGLE || connectedBonds.size() == 0) {
840                     if (atom.getHybridization() == IAtomType.Hybridization.SP2) {
841                         IAtomType type = getAtomType("N.plus.sp2");
842                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
843                     }
844                     IAtomType type = getAtomType("N.plus");
845                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
846                 } else if (maxBondOrder == Order.DOUBLE) {
847                     int doubleBonds = countAttachedDoubleBonds(connectedBonds, atom);
848                     if (doubleBonds == 1) {
849                         IAtomType type = getAtomType("N.plus.sp2");
850                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
851                     } else if (doubleBonds == 2) {
852                         IAtomType type = getAtomType("N.plus.sp1");
853                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
854                     }
855                 } else if (maxBondOrder == Order.TRIPLE) {
856                     if (connectedBonds.size() == 2) {
857                         IAtomType type = getAtomType("N.plus.sp1");
858                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
859                     }
860                 }
861             } else if (atom.getFormalCharge() == -1) {
862                 IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
863                 if (maxBondOrder == Order.SINGLE) {
864                     if (connectedBonds.size() >= 2 && bothNeighborsAreSp2(atom, atomContainer, connectedBonds)
865                             && isRingAtom(atom, atomContainer, searcher)) {
866                         IAtomType type = getAtomType("N.minus.planar3");
867                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
868                     } else if (connectedBonds.size() <= 2) {
869                         IAtomType type = getAtomType("N.minus.sp3");
870                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
871                     }
872                 } else if (maxBondOrder == Order.DOUBLE) {
873                     if (connectedBonds.size() <= 1) {
874                         IAtomType type = getAtomType("N.minus.sp2");
875                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
876                     }
877                 }
878             }
879         } else if (connectedBonds.size() > 3) {
880             if (connectedBonds.size() == 4 && countAttachedDoubleBonds(connectedBonds, atom) == 1) {
881                 IAtomType type = getAtomType("N.oxide");
882                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
883             }
884             return null;
885         } else if (connectedBonds.size() == 0) {
886             IAtomType type = getAtomType("N.sp3");
887             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
888         } else if (hasOneOrMoreSingleOrDoubleBonds(connectedBonds)) {
889             int connectedAtoms = connectedBonds.size()
890                     + (atom.getImplicitHydrogenCount() == CDKConstants.UNSET ? 0 : atom.getImplicitHydrogenCount());
891             if (connectedAtoms == 3) {
892                 IAtomType type = getAtomType("N.planar3");
893                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
894             }
895             IAtomType type = getAtomType("N.sp2");
896             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
897         } else { // OK, use bond order info
898             IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
899             if (maxBondOrder == Order.SINGLE) {
900                 if (isAmide(atom, atomContainer, connectedBonds)) {
901                     IAtomType type = getAtomType("N.amide");
902                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
903                 } else if (isThioAmide(atom, atomContainer, connectedBonds)) {
904                     IAtomType type = getAtomType("N.thioamide");
905                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
906                 }
907 
908                 List<IBond> heavy = heavyBonds(connectedBonds);
909 
910                 int expHCount = heavy.size() - connectedBonds.size();
911 
912                 if (heavy.size() == 2) {
913 
914                     if (heavy.get(0).getFlag(CDKConstants.ISAROMATIC) && heavy.get(1).getFlag(CDKConstants.ISAROMATIC)) {
915 
916                         int hCount = atom.getImplicitHydrogenCount() != null ? atom.getImplicitHydrogenCount()
917                                 + expHCount : expHCount;
918                         if (hCount == 0) {
919                             if (maxBondOrder == Order.SINGLE
920                                     && isSingleHeteroAtom(atom, atomContainer)) {
921                                 IAtomType type = getAtomType("N.planar3");
922                                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
923                             } else {
924                                 IAtomType type = getAtomType("N.sp2");
925                                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
926                             }
927                         } else if (hCount == 1) {
928                             IAtomType type = getAtomType("N.planar3");
929                             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
930                         }
931                     } else if (bothNeighborsAreSp2(atom, atomContainer, connectedBonds) && isRingAtom(atom, atomContainer, searcher)) {
932                         // a N.sp3 which is expected to take part in an aromatic system
933                         IAtomType type = getAtomType("N.planar3");
934                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
935                     } else {
936                         IAtomType type = getAtomType("N.sp3");
937                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
938                     }
939                 } else if (heavy.size() == 3) {
940                     if (bothNeighborsAreSp2(atom, atomContainer, connectedBonds) && isRingAtom(atom, atomContainer, searcher)) {
941                         IAtomType type = getAtomType("N.planar3");
942                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
943                     }
944                     IAtomType type = getAtomType("N.sp3");
945                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
946                 } else if (heavy.size() == 1) {
947                     IAtomType type = getAtomType("N.sp3");
948                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
949                 } else if (heavy.size() == 0) {
950                     IAtomType type = getAtomType("N.sp3");
951                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
952                 }
953             } else if (maxBondOrder == Order.DOUBLE) {
954                 if (connectedBonds.size() == 3
955                         && countAttachedDoubleBonds(connectedBonds, atom, "O") == 2) {
956                     IAtomType type = getAtomType("N.nitro");
957                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
958                 } else if (connectedBonds.size() == 3
959                         && countAttachedDoubleBonds(connectedBonds, atom) > 0) {
960                     IAtomType type = getAtomType("N.sp2.3");
961                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
962                 }
963                 IAtomType type = getAtomType("N.sp2");
964                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
965             } else if (maxBondOrder == Order.TRIPLE) {
966                 int neighborCount = connectedBonds.size();
967                 if (neighborCount > 1) {
968                     IAtomType type = getAtomType("N.sp1.2");
969                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
970                 } else {
971                     IAtomType type = getAtomType("N.sp1");
972                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
973                 }
974             }
975         }
976         return null;
977     }
978 
979     /**
980      * Determines whether the bonds (up to two spheres away) are only to non
981      * hetroatoms. Currently used in N.planar3 perception of (e.g. pyrrole).
982      *
983      * @param atom an atom to test
984      * @param container container of the atom
985      *
986      * @return whether the atom's only bonds are to heteroatoms
987      * @see #perceiveNitrogens(IAtomContainer, IAtom, RingSearch, List)
988      */
isSingleHeteroAtom(IAtom atom, IAtomContainer container)989     private boolean isSingleHeteroAtom(IAtom atom, IAtomContainer container) {
990 
991         List<IAtom> connected = container.getConnectedAtomsList(atom);
992 
993         for (IAtom atom1 : connected) {
994 
995             boolean aromatic = container.getBond(atom, atom1).isAromatic();
996 
997             // ignoring non-aromatic bonds
998             if (!aromatic) continue;
999 
1000             // found a hetroatom - we're not a single hetroatom
1001             if (!"C".equals(atom1.getSymbol())) return false;
1002 
1003             // check the second sphere
1004             for (IAtom atom2 : container.getConnectedAtomsList(atom1)) {
1005 
1006                 if (!atom2.equals(atom) && container.getBond(atom1, atom2).isAromatic()
1007                         && !"C".equals(atom2.getSymbol())) {
1008                     return false;
1009                 }
1010 
1011             }
1012 
1013         }
1014 
1015         return true;
1016 
1017     }
1018 
isRingAtom(IAtom atom, IAtomContainer atomContainer, RingSearch searcher)1019     private boolean isRingAtom(IAtom atom, IAtomContainer atomContainer, RingSearch searcher) {
1020         if (searcher == null) searcher = new RingSearch(atomContainer);
1021         return searcher.cyclic(atom);
1022     }
1023 
isAmide(IAtom atom, IAtomContainer atomContainer, List<IBond> connectedBonds)1024     private boolean isAmide(IAtom atom, IAtomContainer atomContainer, List<IBond> connectedBonds) {
1025     	if (connectedBonds.size() < 1) return false;
1026         for (IBond bond : connectedBonds) {
1027         	IAtom neighbor = bond.getOther(atom);
1028             if (neighbor.getSymbol().equals("C")) {
1029                 if (countAttachedDoubleBonds(atomContainer.getConnectedBondsList(neighbor), neighbor, "O") == 1) return true;
1030             }
1031         }
1032         return false;
1033     }
1034 
isThioAmide(IAtom atom, IAtomContainer atomContainer, List<IBond> connectedBonds)1035     private boolean isThioAmide(IAtom atom, IAtomContainer atomContainer, List<IBond> connectedBonds) {
1036     	if (connectedBonds.size() < 1) return false;
1037         for (IBond bond : connectedBonds) {
1038         	IAtom neighbor = bond.getOther(atom);
1039             if (neighbor.getSymbol().equals("C")) {
1040                 if (countAttachedDoubleBonds(atomContainer.getConnectedBondsList(neighbor), neighbor, "S") == 1) return true;
1041             }
1042         }
1043         return false;
1044     }
1045 
countExplicitHydrogens(IAtom atom, List<IBond> connectedBonds)1046     private int countExplicitHydrogens(IAtom atom, List<IBond> connectedBonds) {
1047         int count = 0;
1048         for (IBond bond : connectedBonds) {
1049         	IAtom aAtom = bond.getOther(atom);
1050             if (aAtom.getSymbol().equals("H")) {
1051                 count++;
1052             }
1053         }
1054         return count;
1055     }
1056 
1057     /**
1058      * Filter a bond list keeping only bonds between heavy atoms.
1059      *
1060      * @param bonds a list of bond
1061      * @return the bond list only with heavy bonds
1062      */
heavyBonds(final List<IBond> bonds)1063     private List<IBond> heavyBonds(final List<IBond> bonds) {
1064         final List<IBond> heavy = new ArrayList<IBond>(bonds.size());
1065         for (final IBond bond : bonds) {
1066             if (!(bond.getBegin().getSymbol().equals("H") && bond.getEnd().getSymbol().equals("H"))) {
1067                 heavy.add(bond);
1068             }
1069         }
1070         return heavy;
1071     }
1072 
perceiveIron(IAtomContainer atomContainer, IAtom atom)1073     private IAtomType perceiveIron(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1074         if ("Fe".equals(atom.getSymbol())) {
1075             if (hasOneSingleElectron(atomContainer, atom)) {
1076                 // no idea how to deal with this yet
1077                 return null;
1078             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == 0)) {
1079                 int neighbors = atomContainer.getConnectedBondsCount(atom);
1080                 if (neighbors == 0) {
1081                 	IAtomType type = getAtomType("Fe.metallic");
1082                 	if (isAcceptable(atom, atomContainer, type)) {
1083                 		return type;
1084                 	}
1085                 } else if (neighbors == 2) {
1086                     IAtomType type5 = getAtomType("Fe.2");
1087                     if (isAcceptable(atom, atomContainer, type5)) {
1088                         return type5;
1089                     }
1090                 } else if (neighbors == 3) {
1091                     IAtomType type6 = getAtomType("Fe.3");
1092                     if (isAcceptable(atom, atomContainer, type6)) {
1093                         return type6;
1094                     }
1095                 } else if (neighbors == 4) {
1096                     IAtomType type7 = getAtomType("Fe.4");
1097                     if (isAcceptable(atom, atomContainer, type7)) {
1098                         return type7;
1099                     }
1100                 } else if (neighbors == 5) {
1101                     IAtomType type8 = getAtomType("Fe.5");
1102                     if (isAcceptable(atom, atomContainer, type8)) {
1103                         return type8;
1104                     }
1105                 } else if (neighbors == 6) {
1106                     IAtomType type9 = getAtomType("Fe.6");
1107                     if (isAcceptable(atom, atomContainer, type9)) {
1108                         return type9;
1109                     }
1110                 }
1111             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == 2)) {
1112                 int neighbors = atomContainer.getConnectedBondsCount(atom);
1113                 if (neighbors <= 1) {
1114                     IAtomType type = getAtomType("Fe.2plus");
1115                     if (isAcceptable(atom, atomContainer, type)) {
1116                         return type;
1117                     }
1118                 }
1119             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == 1)) {
1120                 int neighbors = atomContainer.getConnectedBondsCount(atom);
1121 
1122                 if (neighbors == 2) {
1123                     IAtomType type0 = getAtomType("Fe.plus");
1124                     if (isAcceptable(atom, atomContainer, type0)) {
1125                         return type0;
1126                     }
1127                 }
1128             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == 3)) {
1129                 IAtomType type1 = getAtomType("Fe.3plus");
1130                 if (isAcceptable(atom, atomContainer, type1)) {
1131                     return type1;
1132                 }
1133             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == -2)) {
1134                 IAtomType type2 = getAtomType("Fe.2minus");
1135                 if (isAcceptable(atom, atomContainer, type2)) {
1136                     return type2;
1137                 }
1138             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == -3)) {
1139                 IAtomType type3 = getAtomType("Fe.3minus");
1140                 if (isAcceptable(atom, atomContainer, type3)) {
1141                     return type3;
1142                 }
1143             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == -4)) {
1144                 IAtomType type4 = getAtomType("Fe.4minus");
1145                 if (isAcceptable(atom, atomContainer, type4)) {
1146                     return type4;
1147                 }
1148             }
1149         }
1150         return null;
1151     }
1152 
perceiveMercury(IAtomContainer atomContainer, IAtom atom)1153     private IAtomType perceiveMercury(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1154         if ("Hg".equals(atom.getSymbol())) {
1155             if (hasOneSingleElectron(atomContainer, atom)) {
1156                 // no idea how to deal with this yet
1157                 return null;
1158             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == -1)) {
1159                 IAtomType type = getAtomType("Hg.minus");
1160                 if (isAcceptable(atom, atomContainer, type)) {
1161                     return type;
1162                 }
1163             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == 2)) {
1164                 IAtomType type = getAtomType("Hg.2plus");
1165                 if (isAcceptable(atom, atomContainer, type)) {
1166                     return type;
1167                 }
1168             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == +1)) {
1169                 int neighbors = atomContainer.getConnectedBondsCount(atom);
1170                 if (neighbors <= 1) {
1171                     IAtomType type = getAtomType("Hg.plus");
1172                     if (isAcceptable(atom, atomContainer, type)) {
1173                         return type;
1174                     }
1175                 }
1176             } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == 0)) {
1177                 int neighbors = atomContainer.getConnectedBondsCount(atom);
1178                 if (neighbors == 2) {
1179                     IAtomType type = getAtomType("Hg.2");
1180                     if (isAcceptable(atom, atomContainer, type)) {
1181                         return type;
1182                     }
1183                 } else if (neighbors == 1) {
1184                     IAtomType type = getAtomType("Hg.1");
1185                     if (isAcceptable(atom, atomContainer, type)) {
1186                         return type;
1187                     }
1188                 } else if (neighbors == 0) {
1189                     IAtomType type = getAtomType("Hg.metallic");
1190                     if (isAcceptable(atom, atomContainer, type)) {
1191                         return type;
1192                     }
1193                 }
1194             }
1195         }
1196         return null;
1197     }
1198 
perceiveSulphurs(IAtomContainer atomContainer, IAtom atom, RingSearch searcher, List<IBond> connectedBonds)1199     private IAtomType perceiveSulphurs(IAtomContainer atomContainer, IAtom atom,
1200     		                           RingSearch searcher, List<IBond> connectedBonds) throws CDKException {
1201         if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
1202         IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
1203         int neighborcount = connectedBonds.size();
1204         if (hasOneSingleElectron(atomContainer, atom)) {
1205             // no idea how to deal with this yet
1206             return null;
1207         } else if (atom.getHybridization() != CDKConstants.UNSET && atom.getHybridization() == Hybridization.SP2
1208                 && atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1) {
1209             if (neighborcount == 3) {
1210                 IAtomType type = getAtomType("S.inyl.charged");
1211                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1212             } else {
1213                 IAtomType type = getAtomType("S.plus");
1214                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1215             }
1216         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() != 0) {
1217 
1218             if (atom.getFormalCharge() == -1 && neighborcount == 1) {
1219                 IAtomType type = getAtomType("S.minus");
1220                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1221             } else if (atom.getFormalCharge() == +1 && neighborcount == 2) {
1222                 IAtomType type = getAtomType("S.plus");
1223                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1224             } else if (atom.getFormalCharge() == +1 && neighborcount == 3) {
1225                 IAtomType type = getAtomType("S.inyl.charged");
1226                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1227             } else if (atom.getFormalCharge() == +2 && neighborcount == 4) {
1228                 IAtomType type = getAtomType("S.onyl.charged");
1229                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1230             } else if (atom.getFormalCharge() == -2 && neighborcount == 0) {
1231                 IAtomType type = getAtomType("S.2minus");
1232                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1233             }
1234         } else if (neighborcount == 0) {
1235             if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) {
1236                 IAtomType type = getAtomType("S.3");
1237                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1238             }
1239         } else if (neighborcount == 1) {
1240             if (connectedBonds.get(0).getOrder() == Order.DOUBLE) {
1241                 IAtomType type = getAtomType("S.2");
1242                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1243             } else if (connectedBonds.get(0).getOrder() == Order.SINGLE) {
1244                 IAtomType type = getAtomType("S.3");
1245                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1246             }
1247         } else if (neighborcount == 2) {
1248             if (bothNeighborsAreSp2(atom, atomContainer, connectedBonds) && isRingAtom(atom, atomContainer, searcher)) {
1249                 if (countAttachedDoubleBonds(connectedBonds, atom) == 2) {
1250                     IAtomType type = getAtomType("S.inyl.2");
1251                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1252                 } else {
1253                     IAtomType type = getAtomType("S.planar3");
1254                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1255                 }
1256             } else if (countAttachedDoubleBonds(connectedBonds, atom, "O") == 2) {
1257                 IAtomType type = getAtomType("S.oxide");
1258                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1259             } else if (countAttachedDoubleBonds(connectedBonds, atom) == 2) {
1260                 IAtomType type = getAtomType("S.inyl.2");
1261                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1262             } else if (countAttachedDoubleBonds(connectedBonds, atom) <= 1) {
1263                 IAtomType type = getAtomType("S.3");
1264                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1265             } else if (countAttachedDoubleBonds(connectedBonds, atom) == 0
1266                     && countAttachedSingleBonds(connectedBonds, atom) == 2) {
1267                 IAtomType type = getAtomType("S.octahedral");
1268                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1269             }
1270         } else if (neighborcount == 3) {
1271             int doubleBondedAtoms = countAttachedDoubleBonds(connectedBonds, atom);
1272             if (doubleBondedAtoms == 1) {
1273                 IAtomType type = getAtomType("S.inyl");
1274                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1275             } else if (doubleBondedAtoms == 3) {
1276                 IAtomType type = getAtomType("S.trioxide");
1277                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1278             } else if (doubleBondedAtoms == 0) {
1279                 IAtomType type = getAtomType("S.anyl");
1280                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1281             }
1282         } else if (neighborcount == 4) {
1283             // count the number of double bonded oxygens
1284             int doubleBondedOxygens = countAttachedDoubleBonds(connectedBonds, atom, "O");
1285             int doubleBondedNitrogens = countAttachedDoubleBonds(connectedBonds, atom, "N");
1286             int doubleBondedSulphurs = countAttachedDoubleBonds(connectedBonds, atom, "S");
1287             int countAttachedDoubleBonds = countAttachedDoubleBonds(connectedBonds, atom);
1288 
1289             if (doubleBondedOxygens + doubleBondedNitrogens == 2) {
1290                 IAtomType type = getAtomType("S.onyl");
1291                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1292             } else if (doubleBondedSulphurs == 1 && doubleBondedOxygens == 1) {
1293                 IAtomType type = getAtomType("S.thionyl");
1294                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1295             } else if (maxBondOrder == Order.SINGLE) {
1296                 IAtomType type = getAtomType("S.anyl");
1297                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1298             } else if (doubleBondedOxygens == 1 && countAttachedDoubleBonds == 1) {
1299                 IAtomType type = getAtomType("S.sp3d1");
1300                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1301             } else if (countAttachedDoubleBonds == 2 && maxBondOrder == Order.DOUBLE) {
1302                 IAtomType type = getAtomType("S.sp3.4");
1303                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1304             }
1305 
1306         } else if (neighborcount == 5) {
1307 
1308             if (maxBondOrder == Order.DOUBLE) {
1309 
1310                 IAtomType type = getAtomType("S.sp3d1");
1311                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1312             } else if (maxBondOrder == Order.SINGLE) {
1313                 IAtomType type = getAtomType("S.octahedral");
1314                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1315             }
1316         } else if (neighborcount == 6) {
1317             if (maxBondOrder == Order.SINGLE) {
1318                 IAtomType type = getAtomType("S.octahedral");
1319                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1320             }
1321         }
1322         return null;
1323     }
1324 
perceivePhosphors(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds)1325     private IAtomType perceivePhosphors(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds) throws CDKException {
1326         if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
1327         int neighborcount = connectedBonds.size();
1328         IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
1329         if (countSingleElectrons(atomContainer, atom) == 3) {
1330             IAtomType type = getAtomType("P.se.3");
1331             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1332         } else if (hasOneSingleElectron(atomContainer, atom)) {
1333             // no idea how to deal with this yet
1334             return null;
1335         } else if (neighborcount == 0) {
1336             if (atom.getFormalCharge() == null || atom.getFormalCharge().intValue() == 0) {
1337                 IAtomType type = getAtomType("P.ine");
1338                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1339             }
1340         } else if (neighborcount == 1) {
1341             if (atom.getFormalCharge() == null || atom.getFormalCharge().intValue() == 0) {
1342                 IAtomType type = getAtomType("P.ide");
1343                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1344             }
1345         } else if (neighborcount == 3) {
1346             int doubleBonds = countAttachedDoubleBonds(connectedBonds, atom);
1347             if (atom.getFormalCharge() != null && atom.getFormalCharge().intValue() == 1) {
1348                 IAtomType type = getAtomType("P.anium");
1349                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1350             } else if (doubleBonds == 1) {
1351                 IAtomType type = getAtomType("P.ate");
1352                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1353             } else {
1354                 IAtomType type = getAtomType("P.ine");
1355                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1356             }
1357         } else if (neighborcount == 2) {
1358             if (maxBondOrder == Order.DOUBLE) {
1359                 if (atom.getFormalCharge() != null && atom.getFormalCharge().intValue() == 1) {
1360                     IAtomType type = getAtomType("P.sp1.plus");
1361                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1362                 } else {
1363                     IAtomType type = getAtomType("P.irane");
1364                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1365                 }
1366             } else if (maxBondOrder == Order.SINGLE) {
1367                 IAtomType type = getAtomType("P.ine");
1368                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1369             }
1370         } else if (neighborcount == 4) {
1371             // count the number of double bonded oxygens
1372             int doubleBonds = countAttachedDoubleBonds(connectedBonds, atom);
1373             if (atom.getFormalCharge() == 1 && doubleBonds == 0) {
1374                 IAtomType type = getAtomType("P.ate.charged");
1375                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1376             } else if (doubleBonds == 1) {
1377                 IAtomType type = getAtomType("P.ate");
1378                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1379             }
1380         } else if (neighborcount == 5) {
1381             if (atom.getFormalCharge() == null || atom.getFormalCharge().intValue() == 0) {
1382                 IAtomType type = getAtomType("P.ane");
1383                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1384             }
1385         }
1386         return null;
1387     }
1388 
perceiveHydrogens(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds)1389     private IAtomType perceiveHydrogens(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds) throws CDKException {
1390     	if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
1391         int neighborcount = connectedBonds.size();
1392         if (hasOneSingleElectron(atomContainer, atom)) {
1393             if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) && neighborcount == 0) {
1394                 IAtomType type = getAtomType("H.radical");
1395                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1396             }
1397             return null;
1398         } else if (neighborcount == 2) {
1399             // FIXME: bridging hydrogen as in B2H6
1400             return null;
1401         } else if (neighborcount == 1) {
1402             if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
1403                 IAtomType type = getAtomType("H");
1404                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1405             }
1406         } else if (neighborcount == 0) {
1407             if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
1408                 IAtomType type = getAtomType("H");
1409                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1410             } else if (atom.getFormalCharge() == 1) {
1411                 IAtomType type = getAtomType("H.plus");
1412                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1413             } else if (atom.getFormalCharge() == -1) {
1414                 IAtomType type = getAtomType("H.minus");
1415                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1416             }
1417         }
1418         return null;
1419     }
1420 
perceiveLithium(IAtomContainer atomContainer, IAtom atom)1421     private IAtomType perceiveLithium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1422         int neighborcount = atomContainer.getConnectedBondsCount(atom);
1423         if (neighborcount == 1) {
1424             if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
1425                 IAtomType type = getAtomType("Li");
1426                 if (isAcceptable(atom, atomContainer, type)) return type;
1427             }
1428         } else if (neighborcount == 0) {
1429             if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
1430                 IAtomType type = getAtomType("Li.neutral");
1431                 if (isAcceptable(atom, atomContainer, type)) return type;
1432             }
1433             if (atom.getFormalCharge() == +1) {
1434                 IAtomType type = getAtomType("Li.plus");
1435                 if (isAcceptable(atom, atomContainer, type)) return type;
1436             }
1437         }
1438         return null;
1439     }
1440 
perceiveHalogens(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds)1441     private IAtomType perceiveHalogens(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds) throws CDKException {
1442     	if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
1443         if ("F".equals(atom.getSymbol())) {
1444             if (hasOneSingleElectron(atomContainer, atom)) {
1445                 if (connectedBonds.size() == 0) {
1446                     if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1) {
1447                         IAtomType type = getAtomType("F.plus.radical");
1448                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1449                     } else if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
1450                         IAtomType type = getAtomType("F.radical");
1451                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1452                     }
1453                 } else if (connectedBonds.size() <= 1) {
1454                     IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
1455                     if (maxBondOrder == IBond.Order.SINGLE) {
1456                         IAtomType type = getAtomType("F.plus.radical");
1457                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1458                     }
1459                 }
1460                 return null;
1461             } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() != 0) {
1462                 if (atom.getFormalCharge() == -1) {
1463                     IAtomType type = getAtomType("F.minus");
1464                     if (isAcceptable(atom, atomContainer, type)) return type;
1465                 } else if (atom.getFormalCharge() == 1) {
1466                     IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
1467                     if (maxBondOrder == IBond.Order.DOUBLE) {
1468                         IAtomType type = getAtomType("F.plus.sp2");
1469                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1470                     } else if (maxBondOrder == IBond.Order.SINGLE) {
1471                         IAtomType type = getAtomType("F.plus.sp3");
1472                         if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1473                     }
1474                 }
1475             } else if (connectedBonds.size() == 1 || connectedBonds.size() == 0) {
1476                 IAtomType type = getAtomType("F");
1477                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1478             }
1479         } else if ("I".equals(atom.getSymbol())) {
1480             return perceiveIodine(atomContainer, atom, connectedBonds);
1481         }
1482 
1483         return null;
1484     }
1485 
perceiveArsenic(IAtomContainer atomContainer, IAtom atom)1486     private IAtomType perceiveArsenic(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1487         if (hasOneSingleElectron(atomContainer, atom)) {
1488             // no idea how to deal with this yet
1489             return null;
1490         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1 && atomContainer
1491                 .getConnectedBondsCount(atom) <= 4)) {
1492             IAtomType type = getAtomType("As.plus");
1493             if (isAcceptable(atom, atomContainer, type)) {
1494                 return type;
1495             }
1496         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)) {
1497             int neighbors = atomContainer.getConnectedBondsCount(atom);
1498             if (neighbors == 4) {
1499                 IAtomType type = getAtomType("As.5");
1500                 if (isAcceptable(atom, atomContainer, type)) {
1501                     return type;
1502                 }
1503             }
1504             if (neighbors == 2) {
1505                 IAtomType type = getAtomType("As.2");
1506                 if (isAcceptable(atom, atomContainer, type)) {
1507                     return type;
1508                 }
1509             }
1510             IAtomType type = getAtomType("As");
1511             if (isAcceptable(atom, atomContainer, type)) {
1512                 return type;
1513             }
1514         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +3)) {
1515             IAtomType type = getAtomType("As.3plus");
1516             if (isAcceptable(atom, atomContainer, type)) {
1517                 return type;
1518             }
1519         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -1)) {
1520             IAtomType type = getAtomType("As.minus");
1521             if (isAcceptable(atom, atomContainer, type)) {
1522                 return type;
1523             }
1524         }
1525         return null;
1526     }
1527 
perceiveThorium(IAtomContainer atomContainer, IAtom atom)1528     private IAtomType perceiveThorium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1529         if ("Th".equals(atom.getSymbol())) {
1530             if (atom.getFormalCharge() == 0 && atomContainer.getConnectedBondsCount(atom) == 0) {
1531                 IAtomType type = getAtomType("Th");
1532                 if (isAcceptable(atom, atomContainer, type)) {
1533                     return type;
1534                 }
1535             }
1536         }
1537         return null;
1538     }
1539 
perceiveRubidium(IAtomContainer atomContainer, IAtom atom)1540     private IAtomType perceiveRubidium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1541         if (hasOneSingleElectron(atomContainer, atom)) {
1542             return null;
1543         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1) {
1544             IAtomType type = getAtomType("Rb.plus");
1545             if (isAcceptable(atom, atomContainer, type)) {
1546                 return type;
1547             }
1548         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) {
1549             IAtomType type = getAtomType("Rb.neutral");
1550             if (isAcceptable(atom, atomContainer, type)) {
1551                 return type;
1552             }
1553         }
1554         return null;
1555     }
1556 
perceiveCommonSalts(IAtomContainer atomContainer, IAtom atom)1557     private IAtomType perceiveCommonSalts(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1558         if ("Mg".equals(atom.getSymbol())) {
1559             if (hasOneSingleElectron(atomContainer, atom)) {
1560                 // no idea how to deal with this yet
1561                 return null;
1562             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +2)) {
1563                 IAtomType type = getAtomType("Mg.2plus");
1564                 if (isAcceptable(atom, atomContainer, type)) return type;
1565             }
1566         } else if ("Co".equals(atom.getSymbol())) {
1567             if (hasOneSingleElectron(atomContainer, atom)) {
1568                 // no idea how to deal with this yet
1569                 return null;
1570             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +2)) {
1571                 IAtomType type = getAtomType("Co.2plus");
1572                 if (isAcceptable(atom, atomContainer, type)) return type;
1573             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +3)) {
1574                 IAtomType type = getAtomType("Co.3plus");
1575                 if (isAcceptable(atom, atomContainer, type)) return type;
1576             } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
1577                 IAtomType type = getAtomType("Co.metallic");
1578                 if (isAcceptable(atom, atomContainer, type)) return type;
1579             }
1580         } else if ("W".equals(atom.getSymbol())) {
1581             if (hasOneSingleElectron(atomContainer, atom)) {
1582                 // no idea how to deal with this yet
1583                 return null;
1584             } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
1585                 IAtomType type = getAtomType("W.metallic");
1586                 if (isAcceptable(atom, atomContainer, type)) return type;
1587             }
1588         }
1589         return null;
1590     }
1591 
perceiveCopper(IAtomContainer atomContainer, IAtom atom)1592     private IAtomType perceiveCopper(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1593         if (hasOneSingleElectron(atomContainer, atom)) {
1594             // no idea how to deal with this yet
1595             return null;
1596         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +2)) {
1597             IAtomType type = getAtomType("Cu.2plus");
1598             if (isAcceptable(atom, atomContainer, type)) {
1599                 return type;
1600             }
1601         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) {
1602             int neighbors = atomContainer.getConnectedBondsCount(atom);
1603             if (neighbors == 1) {
1604                 IAtomType type = getAtomType("Cu.1");
1605                 if (isAcceptable(atom, atomContainer, type)) {
1606                     return type;
1607                 }
1608             } else {
1609                 IAtomType type01 = getAtomType("Cu.metallic");
1610                 if (isAcceptable(atom, atomContainer, type01)) {
1611                     return type01;
1612                 }
1613             }
1614         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1) {
1615             IAtomType type02 = getAtomType("Cu.plus");
1616             if (isAcceptable(atom, atomContainer, type02)) {
1617                 return type02;
1618             }
1619         }
1620         return null;
1621     }
1622 
perceiveBarium(IAtomContainer atomContainer, IAtom atom)1623     private IAtomType perceiveBarium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1624         if (hasOneSingleElectron(atomContainer, atom)) {
1625             return null;
1626         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 2)) {
1627             IAtomType type = getAtomType("Ba.2plus");
1628             if (isAcceptable(atom, atomContainer, type)) {
1629                 return type;
1630             }
1631         }
1632         return null;
1633     }
1634 
perceiveAluminium(IAtomContainer atomContainer, IAtom atom)1635     private IAtomType perceiveAluminium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1636         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 3) {
1637             int connectedBondsCount = atomContainer.getConnectedBondsCount(atom);
1638             if (connectedBondsCount == 0) {
1639                 IAtomType type = getAtomType("Al.3plus");
1640                 if (isAcceptable(atom, atomContainer, type)) {
1641                     return type;
1642                 }
1643             }
1644         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0
1645                 && atomContainer.getConnectedBondsCount(atom) == 3) {
1646             IAtomType type = getAtomType("Al");
1647             if (isAcceptable(atom, atomContainer, type)) {
1648                 return type;
1649             }
1650         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -3
1651                 && atomContainer.getConnectedBondsCount(atom) == 6) {
1652             IAtomType type = getAtomType("Al.3minus");
1653             if (isAcceptable(atom, atomContainer, type)) {
1654                 return type;
1655             }
1656         }
1657         return null;
1658     }
1659 
perceiveZinc(IAtomContainer atomContainer, IAtom atom)1660     private IAtomType perceiveZinc(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1661         if (hasOneSingleElectron(atomContainer, atom)) {
1662             // no idea how to deal with this yet
1663             return null;
1664         } else if (atomContainer.getConnectedBondsCount(atom) == 0
1665                 && (atom.getFormalCharge() != null && atom.getFormalCharge() == 0)) {
1666             IAtomType type = getAtomType("Zn.metallic");
1667             if (isAcceptable(atom, atomContainer, type)) return type;
1668         } else if (atomContainer.getConnectedBondsCount(atom) == 0
1669                 && (atom.getFormalCharge() != null && atom.getFormalCharge() == 2)) {
1670             IAtomType type = getAtomType("Zn.2plus");
1671             if (isAcceptable(atom, atomContainer, type)) return type;
1672         } else if (atomContainer.getConnectedBondsCount(atom) == 1
1673                 && (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)) {
1674             IAtomType type = getAtomType("Zn.1");
1675             if (isAcceptable(atom, atomContainer, type)) return type;
1676         } else if (atomContainer.getConnectedBondsCount(atom) == 2
1677                 && (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)) {
1678             IAtomType type = getAtomType("Zn");
1679             if (isAcceptable(atom, atomContainer, type)) return type;
1680         }
1681         return null;
1682     }
1683 
perceiveChromium(IAtomContainer atomContainer, IAtom atom)1684     private IAtomType perceiveChromium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1685         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0
1686                 && atomContainer.getConnectedBondsCount(atom) == 6) {
1687             IAtomType type = getAtomType("Cr");
1688             if (isAcceptable(atom, atomContainer, type)) {
1689                 return type;
1690             }
1691         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0
1692                 && atomContainer.getConnectedBondsCount(atom) == 4) {
1693             IAtomType type = getAtomType("Cr.4");
1694             if (isAcceptable(atom, atomContainer, type)) {
1695                 return type;
1696             }
1697         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 6
1698                 && atomContainer.getConnectedBondsCount(atom) == 0) {
1699             IAtomType type = getAtomType("Cr.6plus");
1700             if (isAcceptable(atom, atomContainer, type)) {
1701                 return type;
1702             }
1703         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0
1704                 && atomContainer.getConnectedBondsCount(atom) == 0) {
1705             IAtomType type = getAtomType("Cr.neutral");
1706             if (isAcceptable(atom, atomContainer, type)) {
1707                 return type;
1708             }
1709         } else if ("Cr".equals(atom.getSymbol())) {
1710             if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 3
1711                     && atomContainer.getConnectedBondsCount(atom) == 0) {
1712                 IAtomType type = getAtomType("Cr.3plus");
1713                 if (isAcceptable(atom, atomContainer, type)) {
1714                     return type;
1715                 }
1716             }
1717         }
1718         return null;
1719     }
1720 
perceiveOrganometallicCenters(IAtomContainer atomContainer, IAtom atom)1721     private IAtomType perceiveOrganometallicCenters(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1722         if ("Po".equals(atom.getSymbol())) {
1723             if (hasOneSingleElectron(atomContainer, atom)) {
1724                 // no idea how to deal with this yet
1725                 return null;
1726             } else if (atomContainer.getConnectedBondsCount(atom) == 2) {
1727                 IAtomType type = getAtomType("Po");
1728                 if (isAcceptable(atom, atomContainer, type)) return type;
1729             }
1730         } else if ("Sn".equals(atom.getSymbol())) {
1731             if (hasOneSingleElectron(atomContainer, atom)) {
1732                 // no idea how to deal with this yet
1733                 return null;
1734             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0 && atomContainer
1735                     .getConnectedBondsCount(atom) <= 4)) {
1736                 IAtomType type = getAtomType("Sn.sp3");
1737                 if (isAcceptable(atom, atomContainer, type)) return type;
1738             }
1739         } else if ("Sc".equals(atom.getSymbol())) {
1740             if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -3
1741                     && atomContainer.getConnectedBondsCount(atom) == 6) {
1742                 IAtomType type = getAtomType("Sc.3minus");
1743                 if (isAcceptable(atom, atomContainer, type)) return type;
1744             }
1745         }
1746         return null;
1747     }
1748 
perceiveNickel(IAtomContainer atomContainer, IAtom atom)1749     private IAtomType perceiveNickel(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1750         if (hasOneSingleElectron(atomContainer, atom)) {
1751             // no idea how to deal with this yet
1752             return null;
1753         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +2)) {
1754             IAtomType type = getAtomType("Ni.2plus");
1755             if (isAcceptable(atom, atomContainer, type)) {
1756                 return type;
1757             }
1758         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)
1759                 && atomContainer.getConnectedBondsCount(atom) == 2) {
1760             IAtomType type = getAtomType("Ni");
1761             if (isAcceptable(atom, atomContainer, type)) {
1762                 return type;
1763             }
1764         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)
1765                 && atomContainer.getConnectedBondsCount(atom) == 0) {
1766             IAtomType type = getAtomType("Ni.metallic");
1767             if (isAcceptable(atom, atomContainer, type)) {
1768                 return type;
1769             }
1770         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 1)
1771                 && atomContainer.getConnectedBondsCount(atom) == 1) {
1772             IAtomType type = getAtomType("Ni.plus");
1773             if (isAcceptable(atom, atomContainer, type)) {
1774                 return type;
1775             }
1776         }
1777         return null;
1778     }
1779 
perceiveNobelGases(IAtomContainer atomContainer, IAtom atom)1780     private IAtomType perceiveNobelGases(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1781         if ("He".equals(atom.getSymbol())) {
1782             if (hasOneSingleElectron(atomContainer, atom)) {
1783                 // no idea how to deal with this yet
1784                 return null;
1785             } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
1786                 IAtomType type = getAtomType("He");
1787                 if (isAcceptable(atom, atomContainer, type)) return type;
1788             }
1789         } else if ("Ne".equals(atom.getSymbol())) {
1790             if (hasOneSingleElectron(atomContainer, atom)) {
1791                 // no idea how to deal with this yet
1792                 return null;
1793             } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
1794                 IAtomType type = getAtomType("Ne");
1795                 if (isAcceptable(atom, atomContainer, type)) return type;
1796             }
1797         } else if ("Ar".equals(atom.getSymbol())) {
1798             if (hasOneSingleElectron(atomContainer, atom)) {
1799                 // no idea how to deal with this yet
1800                 return null;
1801             } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
1802                 IAtomType type = getAtomType("Ar");
1803                 if (isAcceptable(atom, atomContainer, type)) return type;
1804             }
1805         } else if ("Kr".equals(atom.getSymbol())) {
1806             if (hasOneSingleElectron(atomContainer, atom)) {
1807                 // no idea how to deal with this yet
1808                 return null;
1809             } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
1810                 IAtomType type = getAtomType("Kr");
1811                 if (isAcceptable(atom, atomContainer, type)) return type;
1812             }
1813         } else if ("Xe".equals(atom.getSymbol())) {
1814             if (hasOneSingleElectron(atomContainer, atom)) {
1815                 // no idea how to deal with this yet
1816                 return null;
1817             } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
1818                 if (atomContainer.getConnectedBondsCount(atom) == 0) {
1819                     IAtomType type = getAtomType("Xe");
1820                     if (isAcceptable(atom, atomContainer, type)) return type;
1821                 } else {
1822                     IAtomType type = getAtomType("Xe.3");
1823                     if (isAcceptable(atom, atomContainer, type)) return type;
1824                 }
1825             }
1826         } else if ("Rn".equals(atom.getSymbol())) {
1827             if (hasOneSingleElectron(atomContainer, atom)) {
1828                 // no idea how to deal with this yet
1829                 return null;
1830             } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
1831                 IAtomType type = getAtomType("Rn");
1832                 if (isAcceptable(atom, atomContainer, type)) return type;
1833             }
1834         }
1835         return null;
1836     }
1837 
perceiveSilicon(IAtomContainer atomContainer, IAtom atom)1838     private IAtomType perceiveSilicon(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1839         if (hasOneSingleElectron(atomContainer, atom)) {
1840             // no idea how to deal with this yet
1841             return null;
1842         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) {
1843             if (atomContainer.getConnectedBondsCount(atom) == 2) {
1844                 IAtomType type = getAtomType("Si.2");
1845                 if (isAcceptable(atom, atomContainer, type)) return type;
1846             } else if (atomContainer.getConnectedBondsCount(atom) == 3) {
1847                 IAtomType type = getAtomType("Si.3");
1848                 if (isAcceptable(atom, atomContainer, type)) return type;
1849             } else if (atomContainer.getConnectedBondsCount(atom) == 4) {
1850                 IAtomType type = getAtomType("Si.sp3");
1851                 if (isAcceptable(atom, atomContainer, type)) return type;
1852             }
1853         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -2) {
1854             IAtomType type = getAtomType("Si.2minus.6");
1855             if (isAcceptable(atom, atomContainer, type)) return type;
1856         }
1857         return null;
1858     }
1859 
perceiveManganese(IAtomContainer atomContainer, IAtom atom)1860     private IAtomType perceiveManganese(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1861         if (hasOneSingleElectron(atomContainer, atom)) {
1862             // no idea how to deal with this yet
1863             return null;
1864         } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == 0)) {
1865             int neighbors = atomContainer.getConnectedBondsCount(atom);
1866             if (neighbors == 2) {
1867                 IAtomType type02 = getAtomType("Mn.2");
1868                 if (isAcceptable(atom, atomContainer, type02)) return type02;
1869             } else if (neighbors == 0) {
1870                 IAtomType type03 = getAtomType("Mn.metallic");
1871                 if (isAcceptable(atom, atomContainer, type03)) return type03;
1872             }
1873         } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == +2)) {
1874             IAtomType type = getAtomType("Mn.2plus");
1875             if (isAcceptable(atom, atomContainer, type)) return type;
1876         } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == +3)) {
1877             IAtomType type = getAtomType("Mn.3plus");
1878             if (isAcceptable(atom, atomContainer, type)) return type;
1879         }
1880         return null;
1881     }
1882 
perceiveSodium(IAtomContainer atomContainer, IAtom atom)1883     private IAtomType perceiveSodium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1884         if (hasOneSingleElectron(atomContainer, atom)) {
1885             // no idea how to deal with this yet
1886             return null;
1887         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 1)) {
1888             IAtomType type = getAtomType("Na.plus");
1889             if (isAcceptable(atom, atomContainer, type)) return type;
1890         } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)
1891                 && atomContainer.getConnectedBondsCount(atom) == 1) {
1892             IAtomType type = getAtomType("Na");
1893             if (isAcceptable(atom, atomContainer, type)) return type;
1894         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)
1895                 && atomContainer.getConnectedBondsCount(atom) == 0) {
1896             IAtomType type = getAtomType("Na.neutral");
1897             if (isAcceptable(atom, atomContainer, type)) return type;
1898         }
1899         return null;
1900     }
1901 
perceiveIodine(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds )1902     private IAtomType perceiveIodine(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds ) throws CDKException {
1903     	if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
1904         if (hasOneSingleElectron(atomContainer, atom)) {
1905             if (connectedBonds.size() == 0) {
1906                 if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1) {
1907                     IAtomType type = getAtomType("I.plus.radical");
1908                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1909                 } else if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
1910                     IAtomType type = getAtomType("I.radical");
1911                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1912                 }
1913             } else if (connectedBonds.size() <= 1) {
1914                 IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
1915                 if (maxBondOrder == IBond.Order.SINGLE) {
1916                     IAtomType type = getAtomType("I.plus.radical");
1917                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1918                 }
1919             }
1920             return null;
1921         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() != 0) {
1922             if (atom.getFormalCharge() == -1) {
1923                 if (connectedBonds.size() == 0) {
1924                     IAtomType type = getAtomType("I.minus");
1925                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1926                 } else {
1927                     IAtomType type = getAtomType("I.minus.5");
1928                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1929                 }
1930             } else if (atom.getFormalCharge() == 1) {
1931                 IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
1932                 if (maxBondOrder == IBond.Order.DOUBLE) {
1933                     IAtomType type = getAtomType("I.plus.sp2");
1934                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1935                 } else if (maxBondOrder == IBond.Order.SINGLE) {
1936                     IAtomType type = getAtomType("I.plus.sp3");
1937                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1938                 }
1939             }
1940         } else if (connectedBonds.size() == 3) {
1941             int doubleBondCount = countAttachedDoubleBonds(connectedBonds, atom);
1942             if (doubleBondCount == 2) {
1943                 IAtomType type = getAtomType("I.5");
1944                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1945             } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) {
1946                 IAtomType type = getAtomType("I.sp3d2.3");
1947                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1948             }
1949         } else if (connectedBonds.size() == 2) {
1950             IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
1951             if (maxBondOrder == IBond.Order.DOUBLE) {
1952                 IAtomType type = getAtomType("I.3");
1953                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1954             }
1955         } else if (connectedBonds.size() <= 1) {
1956             IAtomType type = getAtomType("I");
1957             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
1958         }
1959         return null;
1960     }
1961 
perceiveRuthenium(IAtomContainer atomContainer, IAtom atom)1962     private IAtomType perceiveRuthenium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1963         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) {
1964             IAtomType type = getAtomType("Ru.6");
1965             if (isAcceptable(atom, atomContainer, type)) return type;
1966         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -2) {
1967             IAtomType type = getAtomType("Ru.2minus.6");
1968             if (isAcceptable(atom, atomContainer, type)) return type;
1969         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -3) {
1970             IAtomType type = getAtomType("Ru.3minus.6");
1971             if (isAcceptable(atom, atomContainer, type)) return type;
1972         }
1973         return null;
1974     }
1975 
perceivePotassium(IAtomContainer atomContainer, IAtom atom)1976     private IAtomType perceivePotassium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1977         if (hasOneSingleElectron(atomContainer, atom)) {
1978             // no idea how to deal with this yet
1979             return null;
1980         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1)) {
1981             IAtomType type = getAtomType("K.plus");
1982             if (isAcceptable(atom, atomContainer, type)) return type;
1983         } else if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
1984             int neighbors = atomContainer.getConnectedBondsCount(atom);
1985             if (neighbors == 1) {
1986                 IAtomType type = getAtomType("K.neutral");
1987                 if (isAcceptable(atom, atomContainer, type)) return type;
1988             }
1989             IAtomType type = getAtomType("K.metallic");
1990             if (isAcceptable(atom, atomContainer, type)) return type;
1991         }
1992         return null;
1993     }
1994 
perceivePlutonium(IAtomContainer atomContainer, IAtom atom)1995     private IAtomType perceivePlutonium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
1996         if (atom.getFormalCharge() == 0 && atomContainer.getConnectedBondsCount(atom) == 0) {
1997             IAtomType type = getAtomType("Pu");
1998             if (isAcceptable(atom, atomContainer, type)) return type;
1999         }
2000         return null;
2001     }
2002 
perceiveCadmium(IAtomContainer atomContainer, IAtom atom)2003     private IAtomType perceiveCadmium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2004         if (hasOneSingleElectron(atomContainer, atom)) {
2005             // no idea how to deal with this yet
2006             return null;
2007         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +2)) {
2008             IAtomType type = getAtomType("Cd.2plus");
2009             if (isAcceptable(atom, atomContainer, type)) return type;
2010         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)) {
2011             if (atomContainer.getConnectedBondsCount(atom) == 0) {
2012                 IAtomType type = getAtomType("Cd.metallic");
2013                 if (isAcceptable(atom, atomContainer, type)) return type;
2014             } else if (atomContainer.getConnectedBondsCount(atom) == 2) {
2015                 IAtomType type = getAtomType("Cd.2");
2016                 if (isAcceptable(atom, atomContainer, type)) return type;
2017             }
2018         }
2019         return null;
2020     }
2021 
perceiveIndium(IAtomContainer atomContainer, IAtom atom)2022     private IAtomType perceiveIndium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2023         if (atom.getFormalCharge() == 0 && atomContainer.getConnectedBondsCount(atom) == 3) {
2024             IAtomType type = getAtomType("In.3");
2025             if (isAcceptable(atom, atomContainer, type)) return type;
2026         } else if (atom.getFormalCharge() == 3 && atomContainer.getConnectedBondsCount(atom) == 0) {
2027             IAtomType type = getAtomType("In.3plus");
2028             if (isAcceptable(atom, atomContainer, type)) return type;
2029         } else if (atom.getFormalCharge() == 0 && atomContainer.getConnectedBondsCount(atom) == 1) {
2030             IAtomType type = getAtomType("In.1");
2031             if (isAcceptable(atom, atomContainer, type)) return type;
2032         } else {
2033             IAtomType type = getAtomType("In");
2034             if (isAcceptable(atom, atomContainer, type)) return type;
2035         }
2036         return null;
2037     }
2038 
perceiveChlorine(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds)2039     private IAtomType perceiveChlorine(IAtomContainer atomContainer, IAtom atom, List<IBond> connectedBonds) throws CDKException {
2040     	if (connectedBonds == null) connectedBonds = atomContainer.getConnectedBondsList(atom);
2041         if (hasOneSingleElectron(atomContainer, atom)) {
2042             if (connectedBonds.size() > 1) {
2043                 if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1) {
2044                     IAtomType type = getAtomType("Cl.plus.radical");
2045                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2046                 }
2047             } else if (connectedBonds.size() == 1) {
2048                 IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
2049                 if (maxBondOrder == IBond.Order.SINGLE) {
2050                     IAtomType type = getAtomType("Cl.plus.radical");
2051                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2052                 }
2053             } else if (connectedBonds.size() == 0
2054                     && (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
2055                 IAtomType type = getAtomType("Cl.radical");
2056                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2057             }
2058         } else if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
2059             int neighborcount = connectedBonds.size();
2060             IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
2061 
2062             if (maxBondOrder == IBond.Order.DOUBLE) {
2063                 if (neighborcount == 2) {
2064                     IAtomType type = getAtomType("Cl.2");
2065                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2066                 } else if (neighborcount == 3) {
2067                     IAtomType type = getAtomType("Cl.chlorate");
2068                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2069                 } else if (neighborcount == 4) {
2070                     IAtomType type = getAtomType("Cl.perchlorate");
2071                     if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2072                 }
2073             } else if (neighborcount <= 1) {
2074                 IAtomType type = getAtomType("Cl");
2075                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2076             }
2077         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -1)) {
2078             IAtomType type = getAtomType("Cl.minus");
2079             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2080         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 1) {
2081             IBond.Order maxBondOrder = getMaximumBondOrder(connectedBonds);
2082             if (maxBondOrder == IBond.Order.DOUBLE) {
2083                 IAtomType type = getAtomType("Cl.plus.sp2");
2084                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2085             } else if (maxBondOrder == IBond.Order.SINGLE) {
2086                 IAtomType type = getAtomType("Cl.plus.sp3");
2087                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2088             }
2089         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +3)
2090                 && connectedBonds.size() == 4) {
2091             IAtomType type = getAtomType("Cl.perchlorate.charged");
2092             if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2093         } else {
2094             int doubleBonds = countAttachedDoubleBonds(connectedBonds, atom);
2095             if (connectedBonds.size() == 3 && doubleBonds == 2) {
2096                 IAtomType type = getAtomType("Cl.chlorate");
2097                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2098             } else if (connectedBonds.size() == 4 && doubleBonds == 3) {
2099                 IAtomType type = getAtomType("Cl.perchlorate");
2100                 if (isAcceptable(atom, atomContainer, type, connectedBonds)) return type;
2101             }
2102         }
2103         return null;
2104     }
2105 
perceiveSilver(IAtomContainer atomContainer, IAtom atom)2106     private IAtomType perceiveSilver(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2107         if (hasOneSingleElectron(atomContainer, atom)) {
2108             return null;
2109         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)) {
2110             int neighbors = atomContainer.getConnectedBondsCount(atom);
2111             if (neighbors == 1) {
2112                 IAtomType type = getAtomType("Ag.1");
2113                 if (isAcceptable(atom, atomContainer, type)) return type;
2114             }
2115             IAtomType type = getAtomType("Ag.neutral");
2116             if (isAcceptable(atom, atomContainer, type)) return type;
2117         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 1)) {
2118             IAtomType type = getAtomType("Ag.plus");
2119             if (isAcceptable(atom, atomContainer, type)) return type;
2120         }
2121         return null;
2122     }
2123 
perceiveGold(IAtomContainer atomContainer, IAtom atom)2124     private IAtomType perceiveGold(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2125         if (hasOneSingleElectron(atomContainer, atom)) {
2126             return null;
2127         }
2128         int neighbors = atomContainer.getConnectedBondsCount(atom);
2129         if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0) && neighbors == 1) {
2130             IAtomType type = getAtomType("Au.1");
2131             if (isAcceptable(atom, atomContainer, type)) return type;
2132         }
2133         return null;
2134     }
2135 
perceiveRadium(IAtomContainer atomContainer, IAtom atom)2136     private IAtomType perceiveRadium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2137         if (hasOneSingleElectron(atomContainer, atom)) {
2138             return null;
2139         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)) {
2140             IAtomType type = getAtomType("Ra.neutral");
2141             if (isAcceptable(atom, atomContainer, type)) return type;
2142         }
2143         return null;
2144     }
2145 
perceiveCalcium(IAtomContainer atomContainer, IAtom atom)2146     private IAtomType perceiveCalcium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2147         if ("Ca".equals(atom.getSymbol())) {
2148             if (hasOneSingleElectron(atomContainer, atom)) {
2149                 // no idea how to deal with this yet
2150                 return null;
2151             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 2 && atomContainer
2152                     .getConnectedBondsCount(atom) == 0)) {
2153                 IAtomType type = getAtomType("Ca.2plus");
2154                 if (isAcceptable(atom, atomContainer, type)) {
2155                     return type;
2156                 }
2157             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0 && atomContainer
2158                     .getConnectedBondsCount(atom) == 2)) {
2159                 IAtomType type = getAtomType("Ca.2");
2160                 if (isAcceptable(atom, atomContainer, type)) {
2161                     return type;
2162                 }
2163             } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0 && atomContainer
2164                     .getConnectedBondsCount(atom) == 1)) {
2165                 IAtomType type = getAtomType("Ca.1");
2166                 if (isAcceptable(atom, atomContainer, type)) {
2167                     return type;
2168                 }
2169             }
2170         }
2171         return null;
2172     }
2173 
perceivePlatinum(IAtomContainer atomContainer, IAtom atom)2174     private IAtomType perceivePlatinum(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2175         if (hasOneSingleElectron(atomContainer, atom)) {
2176             // no idea how to deal with this yet
2177             return null;
2178         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +2)) {
2179             int neighbors = atomContainer.getConnectedBondsCount(atom);
2180             if (neighbors == 4) {
2181                 IAtomType type = getAtomType("Pt.2plus.4");
2182                 if (isAcceptable(atom, atomContainer, type)) return type;
2183             } else {
2184                 IAtomType type = getAtomType("Pt.2plus");
2185                 if (isAcceptable(atom, atomContainer, type)) return type;
2186             }
2187         } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
2188             int neighbors = atomContainer.getConnectedBondsCount(atom);
2189             if (neighbors == 2) {
2190                 IAtomType type = getAtomType("Pt.2");
2191                 if (isAcceptable(atom, atomContainer, type)) return type;
2192             } else if (neighbors == 4) {
2193                 IAtomType type = getAtomType("Pt.4");
2194                 if (isAcceptable(atom, atomContainer, type)) return type;
2195             } else if (neighbors == 6) {
2196                 IAtomType type = getAtomType("Pt.6");
2197                 if (isAcceptable(atom, atomContainer, type)) return type;
2198             }
2199         }
2200         return null;
2201     }
2202 
perceiveAntimony(IAtomContainer atomContainer, IAtom atom)2203     private IAtomType perceiveAntimony(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2204         if (hasOneSingleElectron(atomContainer, atom)) {
2205             return null;
2206         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0 && atomContainer
2207                 .getConnectedBondsCount(atom) == 3)) {
2208             IAtomType type = getAtomType("Sb.3");
2209             if (isAcceptable(atom, atomContainer, type)) return type;
2210         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0 && atomContainer
2211                 .getConnectedBondsCount(atom) == 4)) {
2212             IAtomType type = getAtomType("Sb.4");
2213             if (isAcceptable(atom, atomContainer, type)) return type;
2214         }
2215         return null;
2216     }
2217 
perceiveGadolinum(IAtomContainer atomContainer, IAtom atom)2218     private IAtomType perceiveGadolinum(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2219         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +3
2220                 && atomContainer.getConnectedBondsCount(atom) == 0) {
2221             IAtomType type = getAtomType("Gd.3plus");
2222             if (isAcceptable(atom, atomContainer, type)) {
2223                 return type;
2224             }
2225         }
2226         return null;
2227     }
2228 
perceiveMagnesium(IAtomContainer atomContainer, IAtom atom)2229     private IAtomType perceiveMagnesium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2230         if (hasOneSingleElectron(atomContainer, atom)) {
2231             // no idea how to deal with this yet
2232             return null;
2233         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)) {
2234             int neighbors = atomContainer.getConnectedBondsCount(atom);
2235             if (neighbors == 4) {
2236                 IAtomType type = getAtomType("Mg.neutral");
2237                 if (isAcceptable(atom, atomContainer, type)) return type;
2238             } else if (neighbors == 2) {
2239                 IAtomType type = getAtomType("Mg.neutral.2");
2240                 if (isAcceptable(atom, atomContainer, type)) return type;
2241             } else if (neighbors == 1) {
2242                 IAtomType type = getAtomType("Mg.neutral.1");
2243                 if (isAcceptable(atom, atomContainer, type)) return type;
2244             } else {
2245                 IAtomType type = getAtomType("Mg.neutral");
2246                 if (isAcceptable(atom, atomContainer, type)) return type;
2247             }
2248         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +2)) {
2249             IAtomType type = getAtomType("Mg.2plus");
2250             if (isAcceptable(atom, atomContainer, type)) return type;
2251         }
2252         return null;
2253     }
2254 
perceiveThallium(IAtomContainer atomContainer, IAtom atom)2255     private IAtomType perceiveThallium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2256         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1
2257                 && atomContainer.getConnectedBondsCount(atom) == 0) {
2258             IAtomType type = getAtomType("Tl.plus");
2259             if (isAcceptable(atom, atomContainer, type)) return type;
2260         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0
2261                 && atomContainer.getConnectedBondsCount(atom) == 0) {
2262             IAtomType type = getAtomType("Tl");
2263             if (isAcceptable(atom, atomContainer, type)) return type;
2264         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0
2265                 && atomContainer.getConnectedBondsCount(atom) == 1) {
2266             IAtomType type = getAtomType("Tl.1");
2267             if (isAcceptable(atom, atomContainer, type)) return type;
2268         }
2269         return null;
2270     }
2271 
perceiveLead(IAtomContainer atomContainer, IAtom atom)2272     private IAtomType perceiveLead(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2273         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0
2274                 && atomContainer.getConnectedBondsCount(atom) == 0) {
2275             IAtomType type = getAtomType("Pb.neutral");
2276             if (isAcceptable(atom, atomContainer, type)) return type;
2277         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 2
2278                 && atomContainer.getConnectedBondsCount(atom) == 0) {
2279             IAtomType type = getAtomType("Pb.2plus");
2280             if (isAcceptable(atom, atomContainer, type)) return type;
2281         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0
2282                 && atomContainer.getConnectedBondsCount(atom) == 1) {
2283             IAtomType type = getAtomType("Pb.1");
2284             if (isAcceptable(atom, atomContainer, type)) return type;
2285         }
2286         return null;
2287     }
2288 
perceiveStrontium(IAtomContainer atomContainer, IAtom atom)2289     private IAtomType perceiveStrontium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2290         if (hasOneSingleElectron(atomContainer, atom)) {
2291             return null;
2292         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 2)) {
2293             IAtomType type = getAtomType("Sr.2plus");
2294             if (isAcceptable(atom, atomContainer, type)) return type;
2295         }
2296         return null;
2297     }
2298 
perceiveTitanium(IAtomContainer atomContainer, IAtom atom)2299     private IAtomType perceiveTitanium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2300         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -3
2301                 && atomContainer.getConnectedBondsCount(atom) == 6) {
2302             IAtomType type = getAtomType("Ti.3minus");
2303             if (isAcceptable(atom, atomContainer, type)) return type;
2304         } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)
2305                 && atomContainer.getConnectedBondsCount(atom) == 4) {
2306             IAtomType type = getAtomType("Ti.sp3");
2307             if (isAcceptable(atom, atomContainer, type)) return type;
2308         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == 0)
2309                 && atomContainer.getConnectedBondsCount(atom) == 2) {
2310             IAtomType type = getAtomType("Ti.2");
2311             if (isAcceptable(atom, atomContainer, type)) return type;
2312         }
2313         return null;
2314     }
2315 
perceiveVanadium(IAtomContainer atomContainer, IAtom atom)2316     private IAtomType perceiveVanadium(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2317         if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -3
2318                 && atomContainer.getConnectedBondsCount(atom) == 6) {
2319             IAtomType type = getAtomType("V.3minus");
2320             if (isAcceptable(atom, atomContainer, type)) return type;
2321         } else if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == -3
2322                 && atomContainer.getConnectedBondsCount(atom) == 4) {
2323             IAtomType type = getAtomType("V.3minus.4");
2324             if (isAcceptable(atom, atomContainer, type)) return type;
2325         }
2326         return null;
2327     }
2328 
perceiveBromine(IAtomContainer atomContainer, IAtom atom)2329     private IAtomType perceiveBromine(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2330         if (hasOneSingleElectron(atomContainer, atom)) {
2331             if (atomContainer.getConnectedBondsCount(atom) == 0) {
2332                 if (atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +1) {
2333                     IAtomType type = getAtomType("Br.plus.radical");
2334                     if (isAcceptable(atom, atomContainer, type)) return type;
2335                 } else if (atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0) {
2336                     IAtomType type = getAtomType("Br.radical");
2337                     if (isAcceptable(atom, atomContainer, type)) return type;
2338                 }
2339             } else if (atomContainer.getConnectedBondsCount(atom) <= 1) {
2340                 IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
2341                 if (maxBondOrder == IBond.Order.SINGLE) {
2342                     IAtomType type = getAtomType("Br.plus.radical");
2343                     if (isAcceptable(atom, atomContainer, type)) return type;
2344                 }
2345             }
2346             return null;
2347         } else if (atom.getFormalCharge() != null && atom.getFormalCharge() == -1) {
2348             IAtomType type = getAtomType("Br.minus");
2349             if (isAcceptable(atom, atomContainer, type)) return type;
2350         } else if (atom.getFormalCharge() != null && atom.getFormalCharge() == 1) {
2351             IBond.Order maxBondOrder = atomContainer.getMaximumBondOrder(atom);
2352             if (maxBondOrder == IBond.Order.DOUBLE) {
2353                 IAtomType type = getAtomType("Br.plus.sp2");
2354                 if (isAcceptable(atom, atomContainer, type)) return type;
2355             } else if (maxBondOrder == IBond.Order.SINGLE) {
2356                 IAtomType type = getAtomType("Br.plus.sp3");
2357                 if (isAcceptable(atom, atomContainer, type)) return type;
2358             }
2359         } else if (atomContainer.getConnectedBondsCount(atom) == 1 || atomContainer.getConnectedBondsCount(atom) == 0) {
2360             IAtomType type = getAtomType("Br");
2361             if (isAcceptable(atom, atomContainer, type)) return type;
2362         } else if (atomContainer.getConnectedBondsCount(atom) == 3) {
2363             IAtomType type = getAtomType("Br.3");
2364             if (isAcceptable(atom, atomContainer, type)) return type;
2365         }
2366         return null;
2367     }
2368 
countAttachedDoubleBonds(List<IBond> connectedAtoms, IAtom atom, String symbol)2369     private int countAttachedDoubleBonds(List<IBond> connectedAtoms, IAtom atom, String symbol) {
2370         return countAttachedBonds(connectedAtoms, atom, IBond.Order.DOUBLE, symbol);
2371     }
2372 
perceiveCobalt(IAtomContainer atomContainer, IAtom atom)2373     private IAtomType perceiveCobalt(IAtomContainer atomContainer, IAtom atom) throws CDKException {
2374         if (hasOneSingleElectron(atomContainer, atom)) {
2375             // no idea how to deal with this yet
2376             return null;
2377         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +2)) {
2378             IAtomType type = getAtomType("Co.2plus");
2379             if (isAcceptable(atom, atomContainer, type)) return type;
2380         } else if ((atom.getFormalCharge() != CDKConstants.UNSET && atom.getFormalCharge() == +3)) {
2381             IAtomType type = getAtomType("Co.3plus");
2382             if (isAcceptable(atom, atomContainer, type)) return type;
2383         } else if ((atom.getFormalCharge() == CDKConstants.UNSET || atom.getFormalCharge() == 0)) {
2384             int neighbors = atomContainer.getConnectedBondsCount(atom);
2385             if (neighbors == 2) {
2386                 IAtomType type = getAtomType("Co.2");
2387                 if (isAcceptable(atom, atomContainer, type)) return type;
2388             } else if (neighbors == 4) {
2389                 IAtomType type = getAtomType("Co.4");
2390                 if (isAcceptable(atom, atomContainer, type)) return type;
2391             } else if (neighbors == 6) {
2392                 IAtomType type = getAtomType("Co.6");
2393                 if (isAcceptable(atom, atomContainer, type)) return type;
2394             } else if (neighbors == 1) {
2395                 IAtomType type = getAtomType("Co.1");
2396                 if (isAcceptable(atom, atomContainer, type)) return type;
2397             } else {
2398                 IAtomType type = getAtomType("Co.metallic");
2399                 if (isAcceptable(atom, atomContainer, type)) return type;
2400             }
2401         } else if ((atom.getFormalCharge() != null && atom.getFormalCharge() == +1)) {
2402             int neighbors = atomContainer.getConnectedBondsCount(atom);
2403             if (neighbors == 2) {
2404                 IAtomType type = getAtomType("Co.plus.2");
2405                 if (isAcceptable(atom, atomContainer, type)) return type;
2406             } else if (neighbors == 4) {
2407                 IAtomType type = getAtomType("Co.plus.4");
2408                 if (isAcceptable(atom, atomContainer, type)) return type;
2409             } else if (neighbors == 1) {
2410                 IAtomType type = getAtomType("Co.plus.1");
2411                 if (isAcceptable(atom, atomContainer, type)) return type;
2412             } else if (neighbors == 6) {
2413                 IAtomType type = getAtomType("Co.plus.6");
2414                 if (isAcceptable(atom, atomContainer, type)) return type;
2415             } else if (neighbors == 5) {
2416                 IAtomType type = getAtomType("Co.plus.5");
2417                 if (isAcceptable(atom, atomContainer, type)) return type;
2418             } else {
2419                 IAtomType type = getAtomType("Co.plus");
2420                 if (isAcceptable(atom, atomContainer, type)) return type;
2421             }
2422         }
2423         return null;
2424     }
2425 
countAttachedDoubleBonds(List<IBond> connectedBonds, IAtom atom)2426     private int countAttachedDoubleBonds(List<IBond> connectedBonds, IAtom atom) {
2427         return countAttachedBonds(connectedBonds, atom, IBond.Order.DOUBLE, null);
2428     }
2429 
countAttachedSingleBonds(List<IBond> connectedBonds, IAtom atom)2430     private int countAttachedSingleBonds(List<IBond> connectedBonds, IAtom atom) {
2431         return countAttachedBonds(connectedBonds, atom, IBond.Order.SINGLE, null);
2432     }
2433 
hasAromaticBond(List<IBond> connectedBonds)2434     private boolean hasAromaticBond(List<IBond> connectedBonds) {
2435         for (IBond bond : connectedBonds) {
2436             if (bond.isAromatic()) return true;
2437         }
2438         return false;
2439     }
2440 
2441     /**
2442      * Count the number of doubly bonded atoms.
2443      *
2444      * @param connectedBonds bonds connected to the atom
2445      * @param atom the atom being looked at
2446      * @param order the desired bond order of the attached bonds
2447      * @param symbol If not null, then it only counts the double bonded atoms which
2448      *               match the given symbol.
2449      * @return the number of doubly bonded atoms
2450      */
countAttachedBonds(List<IBond> connectedBonds, IAtom atom, IBond.Order order, String symbol)2451     private int countAttachedBonds(List<IBond> connectedBonds, IAtom atom, IBond.Order order, String symbol) {
2452         // count the number of double bonded oxygens
2453         int neighborcount = connectedBonds.size();
2454         int doubleBondedAtoms = 0;
2455         for (int i = neighborcount - 1; i >= 0; i--) {
2456             IBond bond = connectedBonds.get(i);
2457             if (bond.getOrder() == order) {
2458                 if (bond.getAtomCount() == 2) {
2459                     if (symbol != null) {
2460                         // if other atom is of the given element (by its symbol)
2461                         if (bond.getOther(atom).getSymbol().equals(symbol)) {
2462                             doubleBondedAtoms++;
2463                         }
2464                     } else {
2465                         doubleBondedAtoms++;
2466                     }
2467                 }
2468             }
2469         }
2470         return doubleBondedAtoms;
2471     }
2472 
getAtomType(String identifier)2473     private IAtomType getAtomType(String identifier) throws CDKException {
2474         IAtomType type = factory.getAtomType(identifier);
2475         return type;
2476     }
2477 
isAcceptable(IAtom atom, IAtomContainer container, IAtomType type)2478     private boolean isAcceptable(IAtom atom, IAtomContainer container, IAtomType type) {
2479     	return isAcceptable(atom, container, type, null);
2480     }
2481 
isAcceptable(IAtom atom, IAtomContainer container, IAtomType type, List<IBond> connectedBonds)2482     private boolean isAcceptable(IAtom atom, IAtomContainer container, IAtomType type, List<IBond> connectedBonds) {
2483     	if (connectedBonds == null) connectedBonds = container.getConnectedBondsList(atom);
2484         if (mode == REQUIRE_EXPLICIT_HYDROGENS) {
2485             // make sure no implicit hydrogens were assumed
2486             int actualContainerCount = connectedBonds.size();
2487             int requiredContainerCount = type.getFormalNeighbourCount();
2488             if (actualContainerCount != requiredContainerCount) return false;
2489         } else if (atom.getImplicitHydrogenCount() != CDKConstants.UNSET) {
2490             // confirm correct neighbour count
2491             int connectedAtoms = connectedBonds.size();
2492             int hCount = atom.getImplicitHydrogenCount();
2493             int actualNeighbourCount = connectedAtoms + hCount;
2494             int requiredNeighbourCount = type.getFormalNeighbourCount();
2495             if (actualNeighbourCount > requiredNeighbourCount) return false;
2496         }
2497 
2498         // confirm correct bond orders
2499         IBond.Order typeOrder = type.getMaxBondOrder();
2500         if (typeOrder != null) {
2501             for (IBond bond : connectedBonds) {
2502                 IBond.Order order = bond.getOrder();
2503                 if (order != CDKConstants.UNSET && order != IBond.Order.UNSET) {
2504                     if (BondManipulator.isHigherOrder(order, typeOrder)) return false;
2505                 } else if (bond.getFlag(CDKConstants.SINGLE_OR_DOUBLE)) {
2506                     if (typeOrder != IBond.Order.SINGLE && typeOrder != IBond.Order.DOUBLE) return false;
2507                 } else {
2508                     return false;
2509                 }
2510             }
2511         }
2512 
2513         // confirm correct valency
2514         if (type.getValency() != CDKConstants.UNSET) {
2515             double valence = container.getBondOrderSum(atom);
2516             if (atom.getImplicitHydrogenCount() != null &&
2517             	atom.getImplicitHydrogenCount() != 0)
2518                 valence += atom.getImplicitHydrogenCount();
2519             if (valence > type.getValency())
2520                 return false;
2521         }
2522 
2523         // confirm correct formal charge
2524         if (atom.getFormalCharge() != CDKConstants.UNSET && !atom.getFormalCharge().equals(type.getFormalCharge()))
2525             return false;
2526 
2527         // confirm single electron count
2528         if (type.getProperty(CDKConstants.SINGLE_ELECTRON_COUNT) != null) {
2529             int count = countSingleElectrons(container, atom);
2530             if (count != type.getProperty(CDKConstants.SINGLE_ELECTRON_COUNT, Integer.class).intValue())
2531                 return false;
2532         }
2533 
2534         return true;
2535     }
2536 
2537 }
2538