1 /*
2  * Copyright 2006-2011 Sam Adams <sea36 at users.sourceforge.net>
3  *
4  * This file is part of JNI-InChI.
5  *
6  * JNI-InChI is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * JNI-InChI is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with JNI-InChI.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 package org.jmol.inchi;
20 
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.StringReader;
24 import java.util.Hashtable;
25 import java.util.Map;
26 
27 import org.jmol.adapter.smarter.AtomSetCollection;
28 import org.jmol.api.JmolAdapter;
29 import org.jmol.api.JmolAdapterAtomIterator;
30 import org.jmol.api.JmolAdapterBondIterator;
31 import org.jmol.api.JmolInChI;
32 import org.jmol.modelset.Atom;
33 import org.jmol.modelset.Bond;
34 import org.jmol.util.Edge;
35 import org.jmol.util.Elements;
36 import org.jmol.viewer.Viewer;
37 
38 import javajs.util.BS;
39 import javajs.util.P3;
40 import net.sf.jniinchi.INCHI_BOND_TYPE;
41 import net.sf.jniinchi.JniInchiAtom;
42 import net.sf.jniinchi.JniInchiBond;
43 import net.sf.jniinchi.JniInchiInput;
44 import net.sf.jniinchi.JniInchiInputInchi;
45 import net.sf.jniinchi.JniInchiStructure;
46 import net.sf.jniinchi.JniInchiWrapper;
47 
48 public class InChIJNI implements JmolInChI {
49 
InChIJNI()50   public InChIJNI() {
51     // for dynamic loading
52   }
53 
54   @Override
getInchi(Viewer vwr, BS atoms, String molData, String options)55   public String getInchi(Viewer vwr, BS atoms, String molData, String options) {
56     try {
57       if (atoms == null ? molData == null : atoms.cardinality() == 0)
58         return "";
59       if (options == null)
60         options = "";
61       if (options.startsWith("structure/")) {
62         String inchi = options.substring(10);
63         JniInchiInputInchi in = new JniInchiInputInchi(inchi);
64         return getStructure(JniInchiWrapper.getStructureFromInchi(in));
65       }
66       String inchi = null;
67       boolean haveKey = false;
68       if (molData != null && molData.startsWith("InChI=")) {
69         inchi = molData;
70         haveKey = true;
71       } else {
72         options = options.toLowerCase();
73         haveKey = (options.indexOf("key") >= 0);
74         if (haveKey) {
75           options = options.replace("inchikey", "");
76           options = options.replace("key", "");
77         }
78         JniInchiInput in = new JniInchiInput(options);
79         if (atoms == null) {
80           in.setStructure(newJniInchiStructure(vwr, molData));
81         } else {
82           in.setStructure(newJniInchiStructure(vwr, atoms));
83         }
84         inchi = JniInchiWrapper.getInchi(in).getInchi();
85       }
86       return (haveKey ? JniInchiWrapper.getInchiKey(inchi).getKey() : inchi);
87     } catch (Exception e) {
88       if (e.getMessage().indexOf("ption") >= 0)
89         System.out.println(e.getMessage() + ": " + options.toLowerCase()
90             + "\n See https://www.inchi-trust.org/download/104/inchi-faq.pdf for valid options");
91       else
92         e.printStackTrace();
93       return "";
94     }
95   }
96 
getStructure(JniInchiStructure mol)97   private String getStructure(JniInchiStructure mol) {
98     return toString(mol);
99   }
100 
101   /**
102    * Jmol addition to create a JniInchiStructure from Jmol atoms. Currently only
103    * supports single, double, aromatic_single and aromatic_double.
104    *
105    * @param vwr
106    * @param bsAtoms
107    * @return a structure for JniInput
108    */
newJniInchiStructure(Viewer vwr, BS bsAtoms)109   private static JniInchiStructure newJniInchiStructure(Viewer vwr, BS bsAtoms) {
110     JniInchiStructure mol = new JniInchiStructure();
111     JniInchiAtom[] atoms = new JniInchiAtom[bsAtoms.cardinality()];
112     int[] map = new int[bsAtoms.length()];
113     BS bsBonds = vwr.ms.getBondsForSelectedAtoms(bsAtoms, false);
114     for (int pt = 0, i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms
115         .nextSetBit(i + 1)) {
116       Atom a = vwr.ms.at[i];
117       String sym = a.getElementSymbol();
118       int iso = a.getIsotopeNumber();
119       if (a.getElementNumber() == 1) {
120         sym = "H"; // in case this is D
121       }
122       mol.addAtom(
123           atoms[pt] = new JniInchiAtom(a.x, a.y, a.z, sym));
124       atoms[pt].setCharge(a.getFormalCharge());
125       if (iso > 0)
126         atoms[pt].setIsotopicMass(iso);
127       map[i] = pt++;
128     }
129     Bond[] bonds = vwr.ms.bo;
130     for (int i = bsBonds.nextSetBit(0); i >= 0; i = bsBonds.nextSetBit(i + 1)) {
131       Bond bond = bonds[i];
132       INCHI_BOND_TYPE order = getOrder(bond.order);
133       if (order != null)
134         mol.addBond(new JniInchiBond(atoms[map[bond.getAtomIndex1()]],
135             atoms[map[bond.getAtomIndex2()]], order));
136     }
137     return mol;
138   }
139 
140   /**
141    * Jmol addition to create a JniInchiStructure from MOL data. Currently only
142    * supports single, double, aromatic_single and aromatic_double.
143    *
144    * @param vwr
145    * @param molData
146    * @return a structure for JniInput
147    */
newJniInchiStructure(Viewer vwr, String molData)148   private static JniInchiStructure newJniInchiStructure(Viewer vwr,
149                                                         String molData) {
150     JniInchiStructure mol = new JniInchiStructure();
151     BufferedReader r = new BufferedReader(new StringReader(molData));
152     try {
153       Map<String, Object> htParams = new Hashtable<String, Object>();
154       JmolAdapter adapter = vwr.getModelAdapter();
155       Object atomSetReader = adapter.getAtomSetCollectionReader("String", null,
156           r, htParams);
157       if (atomSetReader instanceof String) {
158         System.err.println("InChIJNI could not read molData");
159         return null;
160       }
161       AtomSetCollection asc = (AtomSetCollection) adapter
162           .getAtomSetCollection(atomSetReader);
163       JmolAdapterAtomIterator ai = adapter.getAtomIterator(asc);
164       JmolAdapterBondIterator bi = adapter.getBondIterator(asc);
165       JniInchiAtom[] atoms = new JniInchiAtom[asc.getAtomSetAtomCount(0)];
166       int n = 0;
167       while (ai.hasNext() && n < atoms.length) {
168         P3 p = ai.getXYZ();
169         JniInchiAtom a = new JniInchiAtom(p.x, p.y, p.z,
170             Elements.elementSymbolFromNumber(ai.getElementNumber()));
171         a.setCharge(ai.getFormalCharge());
172         mol.addAtom(a);
173         atoms[n++] = a;
174       }
175       while (bi.hasNext()) {
176         INCHI_BOND_TYPE order = getOrder(bi.getEncodedOrder());
177         if (order != null)
178           mol.addBond(new JniInchiBond(
179               atoms[((Integer) bi.getAtomUniqueID1()).intValue()],
180               atoms[((Integer) bi.getAtomUniqueID2()).intValue()], order));
181       }
182     } finally {
183       try {
184         r.close();
185       } catch (IOException e) {
186       }
187     }
188     return mol;
189   }
190 
getOrder(int order)191   private static INCHI_BOND_TYPE getOrder(int order) {
192     switch (order) {
193     case Edge.BOND_COVALENT_SINGLE:
194     case Edge.BOND_AROMATIC_SINGLE:
195       return INCHI_BOND_TYPE.SINGLE;
196     case Edge.BOND_AROMATIC_DOUBLE:
197     case Edge.BOND_COVALENT_DOUBLE:
198       return INCHI_BOND_TYPE.DOUBLE;
199     case Edge.BOND_COVALENT_TRIPLE:
200       return INCHI_BOND_TYPE.TRIPLE;
201     default:
202       return null;
203     }
204   }
205 
toString(JniInchiStructure mol)206   private static String toString(JniInchiStructure mol) {
207     int na = mol.getNumAtoms();
208     int nb = mol.getNumBonds();
209     String s = "";
210     for (int i = 0; i < na; i++) {
211       s += mol.getAtom(i).getDebugString() + "\n";
212     }
213     for (int i = 0; i < nb; i++) {
214       s += mol.getBond(i).getDebugString() + "\n";
215     }
216     return s;
217   }
218 
219 }
220