~ubuntu-branches/ubuntu/maverick/cdk/maverick

« back to all changes in this revision

Viewing changes to src/org/openscience/cdk/tools/HOSECodeGenerator.java

  • Committer: Bazaar Package Importer
  • Author(s): Paul Cager
  • Date: 2008-04-09 21:17:53 UTC
  • Revision ID: james.westby@ubuntu.com-20080409211753-46lmjw5z8mx5pd8d
Tags: upstream-1.0.2
ImportĀ upstreamĀ versionĀ 1.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Revision: 8067 $ $Author: egonw $ $Date: 2007-03-09 14:59:01 +0100 (Fri, 09 Mar 2007) $
 
2
 *
 
3
 * Copyright (C) 1997-2007  The Chemistry Development Kit (CDK) project
 
4
 *
 
5
 * Contact: cdk-devel@lists.sourceforge.net
 
6
 *
 
7
 * This program is free software; you can redistribute it and/or
 
8
 * modify it under the terms of the GNU Lesser General Public License
 
9
 * as published by the Free Software Foundation; either version 2.1
 
10
 * of the License, or (at your option) any later version.
 
11
 * All I ask is that proper credit is given for my work, which includes
 
12
 * - but is not limited to - adding the above copyright notice to the beginning
 
13
 * of your source code files, and to any copyright notice that you may distribute
 
14
 * with programs based on this work.
 
15
 *
 
16
 * This program is distributed in the hope that it will be useful,
 
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
19
 * GNU Lesser General Public License for more details.
 
20
 *
 
21
 * You should have received a copy of the GNU Lesser General Public License
 
22
 * along with this program; if not, write to the Free Software
 
23
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
24
 */
 
25
package org.openscience.cdk.tools;
 
26
 
 
27
import java.util.Collections;
 
28
import java.util.Comparator;
 
29
import java.util.List;
 
30
import java.util.ArrayList;
 
31
 
 
32
import org.openscience.cdk.CDKConstants;
 
33
import org.openscience.cdk.ChemObject;
 
34
import org.openscience.cdk.Molecule;
 
35
import org.openscience.cdk.config.IsotopeFactory;
 
36
import org.openscience.cdk.exception.CDKException;
 
37
import org.openscience.cdk.graph.invariant.CanonicalLabeler;
 
38
import org.openscience.cdk.interfaces.IAtom;
 
39
import org.openscience.cdk.interfaces.IAtomContainer;
 
40
import org.openscience.cdk.interfaces.IIsotope;
 
41
import org.openscience.cdk.interfaces.IRing;
 
42
import org.openscience.cdk.interfaces.IRingSet;
 
43
import org.openscience.cdk.ringsearch.SSSRFinder;
 
44
 
 
45
/**
 
46
 * Generates HOSE codes {@cdk.cite BRE78}.
 
47
 * IMPORTANT: Your molecule must contain implicit or explicit hydrogens
 
48
 * for this method to work properly
 
49
 *
 
50
 * @author     steinbeck
 
51
 * @cdk.keyword    HOSE code, spherical atom search
 
52
 * @cdk.created    2002-05-10
 
53
 */
 
54
public class HOSECodeGenerator implements java.io.Serializable
 
55
{
 
56
 
 
57
        private LoggingTool logger;
 
58
        
 
59
    private static final long serialVersionUID = -4353471818831864513L;
 
60
    
 
61
    /**
 
62
         *  Container for the nodes in a sphere.
 
63
         */
 
64
        protected List sphereNodes = null;
 
65
    protected List sphereNodesWithAtoms = null;
 
66
    
 
67
        /**
 
68
         *  Container for the node in the next sphere Assembled in a recursive method
 
69
         *  and then passed to the next recursion to become "sphereNodes"
 
70
         */
 
71
        protected List nextSphereNodes = null;
 
72
        /**
 
73
         *  Counter for the sphere in which we currently work
 
74
         */
 
75
        protected int sphere = 0;
 
76
        /**
 
77
         *  How many spheres are we supposed inspect.
 
78
         */
 
79
        protected int maxSphere = 0;
 
80
 
 
81
        /**
 
82
         *  Here we store the spheres that we assemble, in order to parse them into a
 
83
         *  code later.
 
84
         */
 
85
        protected List[] spheres = null;
 
86
        protected List[] spheresWithAtoms = null;
 
87
 
 
88
        /**
 
89
         *  The HOSECode string that we assemble
 
90
         */
 
91
        protected StringBuffer HOSECode = null;
 
92
 
 
93
        /**
 
94
         *  The molecular structure on which we work
 
95
         */
 
96
        protected IAtomContainer atomContainer;
 
97
 
 
98
        /**
 
99
         *  Delimiters used to separate spheres in the output string. Bremser uses the
 
100
         *  sequence"(//)" for the first four spheres.
 
101
         */
 
102
        protected String[] sphereDelimiters =
 
103
                        {
 
104
                        "(", "/", "/", ")", "/", "/", "/", "/", "/", "/", "/", "/"
 
105
                        };
 
106
        /**
 
107
         *  The bond symbols used for bond orders "single", "double", "triple" and
 
108
         *  "aromatic"
 
109
         */
 
110
        protected String bondSymbols[] =
 
111
                        {
 
112
                        "", "", "=", "%", "*"
 
113
                        };
 
114
                        
 
115
        protected String centerCode = null;                     
 
116
                        
 
117
        public TreeNode rootNode = null;
 
118
                        
 
119
                        
 
120
        boolean debug = false;
 
121
        
 
122
        private IAtomContainer acold=null;
 
123
        private IRingSet soar=null;
 
124
 
 
125
        /**
 
126
         *  The rank order for the given element symbols
 
127
         */
 
128
 
 
129
        static final String[] rankedSymbols =
 
130
                        {
 
131
                        "C", "O", "N", "S", "P", "Si", "B", "F", "Cl", "Br", ";", "I", "#", "&", ","
 
132
                        };
 
133
 
 
134
        /**
 
135
         *  The ranking values to be used for the symbols above
 
136
         */
 
137
        static final int[] symbolRankings =
 
138
                        {
 
139
                        9000, 8900, 8800, 8700,
 
140
                        8600, 8500, 8400, 8300, 8200, 8100, 8000, 7900, 1200, 1100, 1000
 
141
                        };
 
142
 
 
143
        /**
 
144
         *  The bond rankings to be used for the four bond order possibilities
 
145
         */
 
146
 
 
147
        static final int[] bondRankings =
 
148
                        {
 
149
                        0, 0, 200000, 300000, 100000
 
150
                        };
 
151
 
 
152
 
 
153
        /**
 
154
         *  Constructor for the HOSECodeGenerator
 
155
         */
 
156
        public HOSECodeGenerator()
 
157
        {
 
158
                logger = new LoggingTool(this);
 
159
                
 
160
                sphereNodes = new ArrayList();
 
161
                sphereNodesWithAtoms = new ArrayList();
 
162
                nextSphereNodes = new ArrayList();
 
163
                HOSECode = new StringBuffer();
 
164
        }
 
165
  
 
166
  
 
167
        /**
 
168
         *  This method is intended to be used to get the atoms around an atom in spheres. It is not used in this class, but is provided for other classes to use.
 
169
         *  It also creates the HOSE code in HOSECode as a side-effect
 
170
         *  
 
171
         *@param  ac  The AtomContainer with the molecular skeleton in which the root atom resides.
 
172
         *@param  root The root atom for which to produce the spheres.
 
173
         *@param  noOfSpheres  The number of spheres to look at.
 
174
         *@paramm ringsize  Shall the center code have the ring size in it? Only use if you want to have the hose code later, else say false.
 
175
         *@return An array of Vectors. The vector at i-1 contains the atoms at sphere i as TreeNodes.
 
176
         **/
 
177
        public List[] getSpheres(Molecule ac, IAtom root, int noOfSpheres, boolean ringsize) throws org.openscience.cdk.exception.CDKException
 
178
        {
 
179
                centerCode = "";
 
180
                this.atomContainer = ac;
 
181
                maxSphere = noOfSpheres;
 
182
                spheres = new List[noOfSpheres + 1];
 
183
                spheresWithAtoms = new List[noOfSpheres + 1];
 
184
                for (int i = 0; i < ac.getAtomCount(); i++)
 
185
                {
 
186
                        ac.getAtom(i).setFlag(CDKConstants.VISITED, false);
 
187
                }
 
188
                root.setFlag(CDKConstants.VISITED, true);
 
189
                rootNode = new TreeNode(root.getSymbol(), null, root, (double)0, atomContainer.getConnectedBondsCount(root), 0);
 
190
                /*
 
191
                 *  All we need to observe is how the ranking of substituents
 
192
                 *  in the subsequent spheres of the root nodes influences the
 
193
                 *  ranking of the first sphere, sinces the order of a node in a sphere
 
194
                 *  depends on the order the preceding node in its branch
 
195
                 */
 
196
                HOSECode = new StringBuffer();
 
197
                createCenterCode(root,ac,ringsize);
 
198
                breadthFirstSearch(root, false);
 
199
                createCode();
 
200
                fillUpSphereDelimiters();
 
201
                logger.debug("HOSECodeGenerator -> HOSECode: " + HOSECode.toString());
 
202
                return spheresWithAtoms;
 
203
        }
 
204
 
 
205
 
 
206
        /**
 
207
         *  Produces a HOSE code for Atom 'root' in the AtomContainer 'ac'. The HOSE
 
208
         *  code is produced for the number of spheres given by noOfSpheres
 
209
         *  IMPORTANT: if you want aromaticity to be included in the code, you need
 
210
         *  to run the AtomContainer ac to the HueckelAromaticityDetector prior to 
 
211
         *  using getHOSECode(). This method only gives proper results if the molecule is
 
212
         *  fully saturated (if not, the order of the HOSE code might depend on atoms in higher spheres).
 
213
         *  This method is known to fail for protons sometimes.
 
214
         *  IMPORTANT: Your molecule must contain implicit or explicit hydrogens
 
215
         * for this method to work properly
 
216
         *
 
217
         *@param  ac  The AtomContainer with the molecular skeleton in which the root atom resides
 
218
         *@param  root The root atom for which to produce the HOSE code
 
219
         *@param  noOfSpheres  The number of spheres to look at
 
220
         *@return The HOSECode value
 
221
         *@exception  org.openscience.cdk.exception.CDKException  Thrown if something is wrong
 
222
         */
 
223
        public String getHOSECode(IAtomContainer ac, IAtom root, int noOfSpheres) throws org.openscience.cdk.exception.CDKException
 
224
        {
 
225
                return getHOSECode(ac,root,noOfSpheres, false);
 
226
        }
 
227
        
 
228
        
 
229
        /**
 
230
         *  Produces a HOSE code for Atom 'root' in the AtomContainer 'ac'. The HOSE
 
231
         *  code is produced for the number of spheres given by noOfSpheres
 
232
         *  IMPORTANT: if you want aromaticity to be included in the code, you need
 
233
         *  to run the AtomContainer ac to the HueckelAromaticityDetector prior to 
 
234
         *  using getHOSECode(). This method only gives proper results if the molecule is
 
235
   *  fully saturated (if not, the order of the HOSE code might depend on atoms in higher spheres).
 
236
   *  This method is known to fail for protons sometimes.
 
237
         *
 
238
         *@param  ac  The AtomContainer with the molecular skeleton in which the root atom resides
 
239
         *@param  root The root atom for which to produce the HOSE code
 
240
         *@param  noOfSpheres  The number of spheres to look at
 
241
         *@param  ringsize  The size of the ring(s) it is in is included in center atom code
 
242
         *@return The HOSECode value
 
243
         *@exception  org.openscience.cdk.exception.CDKException  Thrown if something is wrong
 
244
         */
 
245
        public String getHOSECode(IAtomContainer ac, IAtom root, int noOfSpheres, boolean ringsize) throws org.openscience.cdk.exception.CDKException
 
246
        {
 
247
    CanonicalLabeler canLabler = new CanonicalLabeler();
 
248
    canLabler.canonLabel(ac);
 
249
                centerCode = "";
 
250
                this.atomContainer = ac;
 
251
                maxSphere = noOfSpheres;
 
252
                spheres = new List[noOfSpheres + 1];
 
253
                for (int i = 0; i < ac.getAtomCount(); i++)
 
254
                {
 
255
                        ac.getAtom(i).setFlag(CDKConstants.VISITED, false);
 
256
                }
 
257
                root.setFlag(CDKConstants.VISITED, true);
 
258
                rootNode = new TreeNode(root.getSymbol(), null, root, (double)0, atomContainer.getConnectedBondsCount(root), 0);
 
259
                /*
 
260
                 *  All we need to observe is how the ranking of substituents
 
261
                 *  in the subsequent spheres of the root nodes influences the
 
262
                 *  ranking of the first sphere, sinces the order of a node in a sphere
 
263
                 *  depends on the order the preceding node in its branch
 
264
                 */
 
265
                HOSECode = new StringBuffer();
 
266
                createCenterCode(root, ac, ringsize);
 
267
                breadthFirstSearch(root,true);
 
268
                createCode();
 
269
                fillUpSphereDelimiters();
 
270
                logger.debug("HOSECodeGenerator -> HOSECode: ", HOSECode);
 
271
                return HOSECode.toString();
 
272
        }
 
273
 
 
274
        private void createCenterCode(IAtom root, IAtomContainer ac, boolean ringsize)
 
275
        {
 
276
                int partnerCount = 0;
 
277
                partnerCount = atomContainer.getConnectedBondsCount(root) + root.getHydrogenCount(); 
 
278
                centerCode = root.getSymbol() + "-" + partnerCount + createChargeCode(root)+(ringsize ? getRingcode(root, ac) : "" )+";";
 
279
        }
 
280
        
 
281
        
 
282
        private String getRingcode(IAtom root, IAtomContainer ac){
 
283
                if(ac!=acold){
 
284
                        soar=new SSSRFinder(ac).findSSSR();
 
285
                }
 
286
                boolean[] bool=new boolean[1000];
 
287
                StringBuffer sb=new StringBuffer();
 
288
                for(int i=0;i<soar.getRings(root).getAtomContainerCount();i++){
 
289
                        if(((IRing)soar.getRings(root).getAtomContainer(i)).getAtomCount()<bool.length)
 
290
                                bool[((IRing)soar.getRings(root).getAtomContainer(i)).getAtomCount()]=true;
 
291
                }
 
292
                for(int i=0;i<bool.length;i++){
 
293
                        if(bool[i])
 
294
                                sb.append(i+"");
 
295
                }
 
296
                if(sb.toString().equals(""))
 
297
                        return "";
 
298
                else
 
299
                        return "-"+sb.toString();
 
300
        }
 
301
  
 
302
  private String createChargeCode(IAtom atom){
 
303
    StringBuffer tempCode=new StringBuffer();
 
304
    if (atom != null && atom.getFormalCharge()!=0){
 
305
      if(Math.abs(atom.getFormalCharge())==1){
 
306
        if(atom.getFormalCharge()<0)
 
307
          tempCode.append("-");
 
308
        else
 
309
          tempCode.append("+");
 
310
      }else{
 
311
        tempCode.append("'");
 
312
        if(atom.getFormalCharge()>0)
 
313
          tempCode.append("+");
 
314
        tempCode.append(atom.getFormalCharge()+"'");
 
315
      }
 
316
    }
 
317
    return(tempCode+"");
 
318
  }
 
319
 
 
320
        /**
 
321
         *  Prepares for a breadth first search within the AtomContainer. The actual
 
322
         *  recursion is done in nextSphere()
 
323
         *
 
324
         *@param  root  The atom at which we start the search
 
325
         *@exception  org.openscience.cdk.exception.CDKException  If something goes wrong.
 
326
         */
 
327
        private void breadthFirstSearch(IAtom root,boolean addTreeNode) throws org.openscience.cdk.exception.CDKException
 
328
        {
 
329
                sphere = 0;
 
330
                TreeNode tempNode = null;
 
331
                java.util.List conAtoms = atomContainer.getConnectedAtomsList(root);
 
332
                IAtom atom;
 
333
                org.openscience.cdk.interfaces.IBond bond = null;
 
334
                sphereNodes.clear();
 
335
                sphereNodesWithAtoms.clear();
 
336
                for (int i = 0; i < conAtoms.size(); i++){
 
337
                        try{
 
338
                                atom = (IAtom)conAtoms.get(i);
 
339
                                if(atom.getSymbol().equals("H"))
 
340
                                        continue;
 
341
                                bond = atomContainer.getBond(root, atom);
 
342
                                /*
 
343
                                 *  In the first sphere the atoms are labled with
 
344
                                 *  their own atom atom as source
 
345
                                 */
 
346
                                if (bond.getFlag(CDKConstants.ISAROMATIC))
 
347
                                {
 
348
                                        tempNode = new TreeNode(atom.getSymbol(), new TreeNode(root.getSymbol(), null, root, (double) 0, 0, (long) 0), atom, 4, atomContainer.getConnectedBondsCount(atom), 0);
 
349
                                } else
 
350
                                {
 
351
                                        tempNode = new TreeNode(atom.getSymbol(), new TreeNode(root.getSymbol(), null, root, (double) 0, 0, (long) 0), atom, bond.getOrder(), atomContainer.getConnectedBondsCount(atom), 0);
 
352
                                }
 
353
                                
 
354
                        sphereNodes.add(tempNode);
 
355
                        if(!addTreeNode)
 
356
                                sphereNodesWithAtoms.add(atom);
 
357
                        
 
358
//                      rootNode.childs.addElement(tempNode);
 
359
                                atom.setFlag(CDKConstants.VISITED, true);
 
360
                        } catch (Exception exc)
 
361
                        {
 
362
                                throw new CDKException("Error in HOSECodeGenerator->breadthFirstSearch.", exc);
 
363
                        }
 
364
                }
 
365
                Collections.sort(sphereNodes,new TreeNodeComparator());
 
366
                nextSphere(sphereNodes);
 
367
        }
 
368
 
 
369
        /**
 
370
         *  The actual recursion method for our breadth first search Each node in
 
371
         *  sphereNodes is inspected for its decendants which are then stored in
 
372
         *  nextSphereNodes, which again is passed to the next recursion level of
 
373
         *  nextSphere()
 
374
         *
 
375
         *@param  sphereNodes The sphereNodes to be inspected
 
376
         *@exception  org.openscience.cdk.exception.CDKException  If something goes wrong
 
377
         */
 
378
        private void nextSphere(List sphereNodes) throws org.openscience.cdk.exception.CDKException
 
379
        {
 
380
                spheres[sphere] = sphereNodes;
 
381
                if(spheresWithAtoms!=null)
 
382
                        spheresWithAtoms[sphere] = sphereNodesWithAtoms;
 
383
                /*
 
384
                 *  From here we start assembling the next sphere
 
385
                 */
 
386
        IAtom node = null;
 
387
        IAtom toNode = null;
 
388
        java.util.List conAtoms = null;
 
389
                TreeNode treeNode = null;
 
390
                nextSphereNodes = new ArrayList();
 
391
                org.openscience.cdk.interfaces.IBond bond = null;
 
392
                for (int i = 0; i < sphereNodes.size(); i++)
 
393
                {
 
394
                        treeNode = (TreeNode) sphereNodes.get(i);
 
395
                        if (!("&;#:,".indexOf(treeNode.symbol) >= 0))
 
396
                        {
 
397
                                node = treeNode.atom;
 
398
                                if(node.getSymbol().equals("H"))
 
399
                                        continue;
 
400
                                
 
401
                                conAtoms = atomContainer.getConnectedAtomsList(node);
 
402
                                if (conAtoms.size() == 1){
 
403
                                        nextSphereNodes.add(new TreeNode(",", treeNode, null, 0, 0, treeNode.score));
 
404
                                }else{
 
405
                                        for (int j = 0; j < conAtoms.size(); j++)
 
406
                                        {
 
407
                                                toNode = (IAtom)conAtoms.get(j);
 
408
                                                if (toNode != treeNode.source.atom)
 
409
                                                {
 
410
                                                        bond = atomContainer.getBond(node, toNode);
 
411
                                                        if (bond.getFlag(CDKConstants.ISAROMATIC))
 
412
                                                        {
 
413
                                                                nextSphereNodes.add(new TreeNode(toNode.getSymbol(), treeNode, toNode, 4, atomContainer.getConnectedBondsCount(toNode), treeNode.score));
 
414
                                                        } else
 
415
                                                        {
 
416
                                                                nextSphereNodes.add(new TreeNode(toNode.getSymbol(), treeNode, toNode, bond.getOrder(), atomContainer.getConnectedBondsCount(toNode), treeNode.score));
 
417
                                                        }
 
418
                                                }
 
419
                                        }
 
420
                                }
 
421
                        }
 
422
                }
 
423
                Collections.sort(nextSphereNodes,new TreeNodeComparator());
 
424
                if (sphere < maxSphere)
 
425
                {
 
426
                        sphere++;
 
427
                        nextSphere(nextSphereNodes);
 
428
                }
 
429
        }
 
430
 
 
431
        public String makeBremserCompliant(String code)
 
432
        {
 
433
                int sepIndex = code.indexOf(";");
 
434
                if (sepIndex >= 0)
 
435
                {
 
436
                        code = code.substring(sepIndex + 1, code.length());     
 
437
                }
 
438
                return code;
 
439
        }
 
440
        
 
441
        /**
 
442
         *  After recursivly having established the spheres and assigning each node an
 
443
         *  appropriate score, we now generate the complete HOSE code.
 
444
         *
 
445
         *@exception  org.openscience.cdk.exception.CDKException  Thrown if something goes wrong
 
446
         */
 
447
        private void createCode() throws org.openscience.cdk.exception.CDKException
 
448
        {
 
449
                List sphereNodes = null;
 
450
                TreeNode tn = null;
 
451
                for (int f = 0; f < atomContainer.getAtomCount(); f++)
 
452
                {
 
453
                        atomContainer.getAtom(f).setFlag(CDKConstants.VISITED, false);
 
454
                }
 
455
 
 
456
                for (int f = 0; f < maxSphere; f++)
 
457
                {
 
458
                        sphereNodes = spheres[maxSphere - f];
 
459
                        for (int g = 0; g < sphereNodes.size(); g++)
 
460
                        {
 
461
                                tn = (TreeNode) sphereNodes.get(g);
 
462
                                if (tn.source != null)
 
463
                                {
 
464
                                        tn.source.ranking += tn.degree;
 
465
                                }
 
466
                                
 
467
                        }
 
468
                }
 
469
 
 
470
                for (int f = 0; f < maxSphere; f++)
 
471
                {
 
472
                        sphereNodes = spheres[f];
 
473
                        calculateNodeScores(sphereNodes);
 
474
                        sortNodesByScore(sphereNodes);
 
475
                }
 
476
 
 
477
                for (int f = 0; f < maxSphere; f++)
 
478
                {
 
479
                        sphereNodes = spheres[f];
 
480
                        for (int g = 0; g < sphereNodes.size() ; g++)
 
481
                        {
 
482
                                tn = (TreeNode) sphereNodes.get(g);
 
483
                                tn.score += tn.ranking;
 
484
                        }
 
485
                        sortNodesByScore(sphereNodes);
 
486
                }
 
487
                for (int f = 0; f < maxSphere; f++)
 
488
                {
 
489
                        sphereNodes = spheres[f];
 
490
                        for (int g = 0; g < sphereNodes.size() ; g++)
 
491
                        {
 
492
                                tn = (TreeNode) sphereNodes.get(g);
 
493
        String localscore=tn.score+"";
 
494
        while(localscore.length()<6){
 
495
          localscore="0"+localscore;
 
496
        }
 
497
        tn.stringscore=tn.source.stringscore+""+localscore;
 
498
                        }
 
499
                        sortNodesByScore(sphereNodes);
 
500
                }
 
501
                HOSECode.append(centerCode);
 
502
                for (int f = 0; f < maxSphere; f++)
 
503
                {
 
504
                        sphere = f + 1;
 
505
                        sphereNodes = spheres[f];
 
506
                        String s = getSphereCode(sphereNodes);
 
507
                        HOSECode.append(s);
 
508
                }
 
509
        }
 
510
 
 
511
        /**
 
512
         *  Generates the string code for a given sphere
 
513
         *
 
514
         *@param  sphereNodes A vector of TreeNodes for which a string code is to be generated
 
515
         *@return The SphereCode value
 
516
         *@exception  org.openscience.cdk.exception.CDKException  Thrown if something goes wrong
 
517
         */
 
518
        private String getSphereCode(List sphereNodes) throws org.openscience.cdk.exception.CDKException
 
519
        {
 
520
                if (sphereNodes == null || sphereNodes.size() < 1)
 
521
                {
 
522
                        return sphereDelimiters[sphere - 1];
 
523
                }
 
524
                TreeNode treeNode = null;
 
525
                StringBuffer code = new StringBuffer();
 
526
                /*
 
527
                 *  append the tree node code to the HOSECode in
 
528
                 *  their now determined order, using commas to
 
529
                 *  separate nodes from different branches
 
530
                 */
 
531
                IAtom branch = ((TreeNode) (((TreeNode) sphereNodes.get(0)).source)).atom;
 
532
                StringBuffer tempCode = null;
 
533
                for (int i = 0; i < sphereNodes.size(); i++)
 
534
                {
 
535
                        treeNode = (TreeNode) sphereNodes.get(i);
 
536
                        tempCode = new StringBuffer();
 
537
                        if (!treeNode.source.stopper && treeNode.source.atom != branch)
 
538
                        {
 
539
                                branch = treeNode.source.atom;
 
540
                                code.append(",");
 
541
                        }
 
542
                        
 
543
                        if (!treeNode.source.stopper && treeNode.source.atom == branch)
 
544
                        {
 
545
                                if (treeNode.bondType <= 4)
 
546
                                {
 
547
                                        tempCode.append(bondSymbols[(int) treeNode.bondType]);
 
548
                                } else
 
549
                                {
 
550
                                        throw new CDKException("Unknown bond type");
 
551
                                }
 
552
                                if (treeNode.atom != null && !treeNode.atom.getFlag(CDKConstants.VISITED))
 
553
                                {
 
554
                                        tempCode.append(getElementSymbol(treeNode.symbol));
 
555
                                }
 
556
                                else if (treeNode.atom != null && treeNode.atom.getFlag(CDKConstants.VISITED))
 
557
                                {
 
558
                                        tempCode.append("&");
 
559
                                        treeNode.stopper = true;
 
560
                                }
 
561
        code.append(tempCode+createChargeCode(treeNode.atom));
 
562
                                treeNode.hSymbol = tempCode.toString();
 
563
                        }
 
564
                        if (treeNode.atom != null) treeNode.atom.setFlag(CDKConstants.VISITED, true);
 
565
                        if (treeNode.source.stopper) treeNode.stopper = true;
 
566
                }
 
567
                code.append(sphereDelimiters[sphere - 1]);
 
568
                return code.toString();
 
569
        }
 
570
 
 
571
 
 
572
        /**
 
573
         *  Gets the element rank for a given element symbol as given in Bremser's
 
574
         *  publication
 
575
         *
 
576
         *@param  symbol  The element symbol for which the rank is to be determined
 
577
         *@return         The element rank
 
578
         */
 
579
        private double getElementRank(String symbol)
 
580
        {
 
581
                for (int f = 0; f < rankedSymbols.length; f++)
 
582
                {
 
583
                        if (rankedSymbols[f].equals(symbol))
 
584
                        {
 
585
                                return symbolRankings[f];
 
586
                        }
 
587
                }
 
588
        try {
 
589
            IIsotope isotope = IsotopeFactory.getInstance(new ChemObject().getBuilder()).getMajorIsotope(symbol);
 
590
            return ((double) 800000 - isotope.getMassNumber());
 
591
        } catch (Exception exception) {
 
592
            System.err.println("Could not find major isotope for this element!!! : " + symbol);
 
593
            System.err.println("Because of this error: " + exception.getMessage());
 
594
        }
 
595
        return (double)800000;
 
596
        }
 
597
 
 
598
        /**
 
599
         *  Returns the Bremser-compatible symbols for a given element. Silicon, for
 
600
         *  example, is actually "Q". :-)
 
601
         *
 
602
         *@param  sym  The element symbol to be converted
 
603
         *@return      The converted symbol
 
604
         */
 
605
        private String getElementSymbol(String sym)
 
606
        {
 
607
                if (sym.equals("Si"))
 
608
                {
 
609
                        return "Q";
 
610
                }
 
611
                if (sym.equals("Cl"))
 
612
                {
 
613
                        return "X";
 
614
                }
 
615
                if (sym.equals("Br"))
 
616
                {
 
617
                        return "Y";
 
618
                }
 
619
                if (sym.equals(","))
 
620
                {
 
621
                        return "";
 
622
                }
 
623
                return sym;
 
624
        }
 
625
 
 
626
 
 
627
        /**
 
628
         *  Determines the ranking score for each node, allowing for a sorting of nodes
 
629
         *  within one sphere.
 
630
         *
 
631
         *@param  sphereNodes The nodes for which the score is to be calculated.
 
632
         *@exception  org.openscience.cdk.exception.CDKException  Thrown if something goes wrong.
 
633
         */
 
634
        private void calculateNodeScores(List sphereNodes) throws org.openscience.cdk.exception.CDKException
 
635
        {
 
636
                TreeNode treeNode = null;
 
637
                for (int i = 0; i < sphereNodes.size(); i++)
 
638
                {
 
639
                        treeNode = (TreeNode) sphereNodes.get(i);
 
640
                        treeNode.score += getElementRank(treeNode.symbol);
 
641
                        if (treeNode.bondType <= 4)
 
642
                        {
 
643
                                treeNode.score += bondRankings[(int) treeNode.bondType];
 
644
                        } else
 
645
                        {
 
646
                                throw new CDKException("Unknown bond type encountered in HOSECodeGenerator");
 
647
                        }
 
648
                }
 
649
        }
 
650
 
 
651
        /**
 
652
         *  Sorts the nodes (atoms) in the sphereNode vector according to their score
 
653
         *  This is used for the essential ranking of nodes in HOSE code sphere
 
654
         *
 
655
         *@param  sphereNodes  A vector with sphere nodes to be sorted.
 
656
         */
 
657
        private void sortNodesByScore(List sphereNodes)
 
658
        {
 
659
                Object obj;
 
660
                boolean changed;
 
661
                if (sphereNodes.size() == 0) return;
 
662
                /*
 
663
                 *  Now we sort by score
 
664
                 */
 
665
                do
 
666
                {
 
667
                        changed = false;
 
668
                        for (int i = 0; i < sphereNodes.size() - 1; i++)
 
669
                        {
 
670
                                if (((TreeNode) sphereNodes.get(i + 1)).stringscore.compareTo(((TreeNode) sphereNodes.get(i)).stringscore)>0)
 
671
                                {
 
672
                                        obj = sphereNodes.get(i + 1);
 
673
                                        sphereNodes.remove(i + 1);
 
674
                                        sphereNodes.add(i, obj);
 
675
                                        changed = true;
 
676
                                }
 
677
                        }
 
678
                } while (changed);
 
679
                /* Having sorted a sphere, we lable the nodes with their sort order */
 
680
                TreeNode temp = null;
 
681
                for (int i = 0; i < sphereNodes.size(); i++)
 
682
                {
 
683
                        temp = ((TreeNode) sphereNodes.get(i));
 
684
                        temp.sortOrder = sphereNodes.size() - i;
 
685
                }
 
686
        }
 
687
 
 
688
        /**
 
689
         *  If we use less than four sphere, this fills up the code with the missing
 
690
         *  delimiters such that we are compatible with Bremser's HOSE code table.
 
691
         */
 
692
        private void fillUpSphereDelimiters()
 
693
        {
 
694
                logger.debug("Sphere: " + sphere);
 
695
                for (int f = sphere; f < 4; f++)
 
696
                {
 
697
                        HOSECode.append(sphereDelimiters[f]);
 
698
                }
 
699
        }
 
700
 
 
701
        
 
702
        class TreeNodeComparator implements Comparator {
 
703
    /**
 
704
     *The compare method, compares by canonical label of atoms
 
705
     *
 
706
     * @param  obj1  The first TreeNode
 
707
     * @param  obj2  The second TreeNode
 
708
     * @return       -1,0,1
 
709
     */
 
710
    public int compare(Object obj1, Object obj2) {
 
711
        if(obj1==null || obj2==null || ((TreeNode) obj1).getAtom()==null || ((TreeNode) obj2).getAtom()==null)
 
712
                return 0;
 
713
        Long label1 = (Long)((TreeNode) obj1).getAtom().getProperty("CanonicalLable");
 
714
        Long label2 = (Long)((TreeNode) obj2).getAtom().getProperty("CanonicalLable");
 
715
        if(label1==null || label2==null)
 
716
                return 0;
 
717
        if (label1.intValue() < label2.intValue()) {
 
718
                return (-1);
 
719
        }
 
720
        if (label1.intValue() > label2.intValue()) {
 
721
                return (1);
 
722
        }
 
723
        return (0);
 
724
    }
 
725
  }
 
726
    
 
727
  /**
 
728
         *  Helper class for storing the properties of a node in our breadth first
 
729
         *  search
 
730
         *
 
731
         * @author     steinbeck
 
732
         * @cdk.created    2002-11-16
 
733
         */
 
734
        class TreeNode
 
735
        {
 
736
                String symbol;
 
737
                TreeNode source;
 
738
                IAtom atom;
 
739
                double bondType;
 
740
                int degree;
 
741
                long score;
 
742
                int ranking;
 
743
                int sortOrder = 1;
 
744
                List childs = null;
 
745
                String hSymbol = null;
 
746
                boolean stopper = false;
 
747
    String stringscore="";
 
748
 
 
749
                /**
 
750
                 *  Constructor for the TreeNode object
 
751
                 *
 
752
                 *@param  symbol    The Element symbol of the node
 
753
                 *@param  source    The preceding node for this node
 
754
                 *@param  atom      The cdk atom object belonging to this node
 
755
                 *@param  bondType  The bond type by which this node was connect to its
 
756
                 *      predecessor
 
757
                 *@param  score     The score used to rank this node within its sphere.
 
758
                 *@param  degree    Description of the Parameter
 
759
                 */
 
760
                TreeNode(String symbol, TreeNode source, IAtom atom, double bondType, int degree, long score)
 
761
                {
 
762
                        this.symbol = symbol;
 
763
                        this.source = source;
 
764
                        this.atom = atom;
 
765
                        this.degree = degree;
 
766
                        this.score = score;
 
767
                        this.bondType = bondType;
 
768
                        ranking = 0;
 
769
                        sortOrder = 1;
 
770
                        childs = new ArrayList();
 
771
                }
 
772
    
 
773
    public IAtom getAtom(){
 
774
      return atom;
 
775
    }
 
776
 
 
777
 
 
778
                /**
 
779
                 *  A TreeNode is equal to another TreeNode if it
 
780
                 *  stands for the same atom object
 
781
                 *
 
782
                 *@param  o  The object tht we compare this TreeNode to
 
783
                 *@return    True, if the this TreeNode's atom object equals the one of the other TreeNode
 
784
                 */
 
785
                public boolean equals(Object o)
 
786
                {
 
787
                        try
 
788
                        {
 
789
                                if (this.atom == ((TreeNode) o).atom)
 
790
                                {
 
791
                                        return true;
 
792
                                }
 
793
                        }
 
794
                        catch(Exception exc)
 
795
                        {
 
796
                                /* we do nothing here because anything 
 
797
                                that could seriously happen here is the we 
 
798
                                got something which is not a TreeNode and then got 
 
799
                                a class cast exception. Thus we can just wait until the
 
800
                                end of the method returns a "false" */
 
801
                        }
 
802
                        return false;
 
803
                }
 
804
                
 
805
                public String toString()
 
806
                {
 
807
                        String s = "";
 
808
                        try
 
809
                        {
 
810
                                s += (atomContainer.getAtomNumber(atom) + 1);
 
811
                                s += " " + hSymbol;
 
812
                                s += "; s=" + score; 
 
813
                                s += "; r=" + ranking;
 
814
                                s += "; d = " + degree;
 
815
                        }
 
816
                        catch(Exception exc)
 
817
                        {
 
818
                                return exc.toString();  
 
819
                        }
 
820
                        return s;
 
821
                }
 
822
        }
 
823
  
 
824
  
 
825
        public List getNodesInSphere(int sphereNumber){
 
826
                sphereNodes = spheres[sphereNumber-1];
 
827
                List atoms=new ArrayList();
 
828
                for (int g = 0; g < sphereNodes.size() ; g++) {
 
829
                        atoms.add(((TreeNode) sphereNodes.get(g)).atom);
 
830
                }
 
831
                return(atoms);
 
832
        }
 
833
}
 
834
 
 
835