1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.jniinchi;
20
21 import net.sf.jnati.NativeCodeException;
22 import net.sf.jnati.deploy.NativeLibraryLoader;
23 import org.apache.log4j.Logger;
24
25 import java.util.List;
26 import java.util.StringTokenizer;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.TimeoutException;
29 import java.util.concurrent.locks.Lock;
30 import java.util.concurrent.locks.ReentrantLock;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class JniInchiWrapper {
50
51 private static final Logger LOG = Logger.getLogger(JniInchiWrapper.class);
52
53 private static final String ID = "jniinchi";
54 private static final String VERSION = "1.03_1";
55
56
57
58
59 private static final int MAX_LOCK_TIMEOUT = 15;
60
61
62
63
64 private static final boolean IS_WINDOWS = System.getProperty("os.name", "").toLowerCase().startsWith("windows");
65
66
67
68
69 static final String flagChar = IS_WINDOWS ? "/" : "-";
70
71
72
73
74 private static boolean libraryLoaded = false;
75
76 private static JniInchiWrapper inchiWrapper;
77
78 private static final Lock lock = new ReentrantLock(true);
79
80
81
82
83
84 public static synchronized void loadLibrary() throws LoadNativeLibraryException {
85 if (!libraryLoaded) {
86 try {
87 NativeLibraryLoader.loadLibrary(ID, VERSION);
88
89
90
91 checkNativeCodeVersion();
92
93
94 libraryLoaded = true;
95 } catch (NativeCodeException ex) {
96 System.err.println();
97 System.err.println("Error loading JNI InChI native code.");
98 System.err.println("You may need to compile the native code for your platform.");
99 System.err.println("See http://jni-inchi.sourceforge.net for instructions.");
100 System.err.println();
101 throw new LoadNativeLibraryException(ex);
102 }
103 }
104 }
105
106
107
108
109
110 private static void checkNativeCodeVersion() throws NativeCodeException {
111
112 LOG.trace("Checking native code version");
113
114
115 String nativeVersion;
116 try {
117 nativeVersion = JniInchiWrapper.LibInchiGetVersion();
118 } catch (UnsatisfiedLinkError e) {
119 LOG.error("Unable to get native code version", e);
120 throw new NativeCodeException("Unable get native code version", e);
121 }
122
123
124 if (!VERSION.equals(nativeVersion)) {
125 LOG.error("Native code version mismatch; expected " + VERSION + ", found " + nativeVersion);
126 throw new NativeCodeException("JNI InChI native code version mismatch: expected "
127 + VERSION + ", found " + nativeVersion);
128 }
129
130 LOG.trace("Expected native code version found: " + nativeVersion);
131 }
132
133
134 private static synchronized JniInchiWrapper getWrapper() throws LoadNativeLibraryException {
135 if (inchiWrapper == null) {
136 loadLibrary();
137 init();
138 inchiWrapper = new JniInchiWrapper();
139 }
140 return inchiWrapper;
141 }
142
143
144
145
146 private JniInchiWrapper() throws LoadNativeLibraryException {
147
148 }
149
150
151
152
153
154
155
156 protected static String checkOptions(List<INCHI_OPTION> ops) throws JniInchiException {
157 if (ops == null) {
158 throw new IllegalArgumentException("Null options");
159 }
160 StringBuffer sbOptions = new StringBuffer();
161
162 for (int i = 0; i < ops.size(); i++) {
163 Object op = ops.get(i);
164 if (op instanceof INCHI_OPTION) {
165 sbOptions.append(flagChar + ((INCHI_OPTION) op).name() + " ");
166 } else {
167 throw new JniInchiException("Unrecognised InChI option");
168 }
169 }
170
171 return sbOptions.toString();
172 }
173
174
175
176
177
178
179
180
181 protected static String checkOptions(final String ops) throws JniInchiException {
182 if (ops == null) {
183 throw new IllegalArgumentException("Null options");
184 }
185 StringBuilder sbOptions = new StringBuilder();
186
187 StringTokenizer tok = new StringTokenizer(ops);
188 while (tok.hasMoreTokens()) {
189 String op = tok.nextToken();
190
191 if (op.startsWith("-") || op.startsWith("/")) {
192 op = op.substring(1);
193 }
194
195 INCHI_OPTION option = INCHI_OPTION.valueOfIgnoreCase(op);
196 if (option != null) {
197 sbOptions.append(flagChar + option.name());
198 if (tok.hasMoreTokens()) {
199 sbOptions.append(" ");
200 }
201 } else {
202 throw new JniInchiException("Unrecognised InChI option");
203 }
204 }
205
206 return sbOptions.toString();
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241 @SuppressWarnings("unchecked")
242 public static JniInchiOutput getInchi(JniInchiInput input) throws JniInchiException {
243 if (input == null) {
244 throw new IllegalArgumentException("Null input");
245 }
246 JniInchiWrapper wrapper = getWrapper();
247 wrapper.getLock();
248 try {
249 return wrapper.GetINCHI(input);
250 } finally {
251 lock.unlock();
252 }
253 }
254
255
256
257
258
259
260
261
262
263
264 @SuppressWarnings("unchecked")
265 public static JniInchiOutput getStdInchi(JniInchiInput input) throws JniInchiException {
266 if (input == null) {
267 throw new IllegalArgumentException("Null input");
268 }
269 JniInchiWrapper wrapper = getWrapper();
270 wrapper.getLock();
271 try {
272 return wrapper.GetStdINCHI(input);
273 } finally {
274 lock.unlock();
275 }
276 }
277
278
279
280
281
282
283
284
285
286
287
288 public static JniInchiOutput getInchiFromInchi(JniInchiInputInchi input) throws JniInchiException {
289 if (input == null) {
290 throw new IllegalArgumentException("Null input");
291 }
292 JniInchiWrapper wrapper = getWrapper();
293 wrapper.getLock();
294 try {
295 return wrapper.GetINCHIfromINCHI(input.getInchi(), input.getOptions());
296 } finally {
297 lock.unlock();
298 }
299 }
300
301
302
303
304
305
306
307 public static JniInchiOutputStructure getStructureFromInchi(JniInchiInputInchi input) throws JniInchiException {
308 if (input == null) {
309 throw new IllegalArgumentException("Null input");
310 }
311 JniInchiWrapper wrapper = getWrapper();
312 wrapper.getLock();
313 try {
314 return wrapper.GetStructFromINCHI(input.getInchi(), input.getOptions());
315 } finally {
316 lock.unlock();
317 }
318 }
319
320
321
322
323
324
325
326
327 public static JniInchiOutputKey getInchiKey(final String inchi) throws JniInchiException {
328 if (inchi == null) {
329 throw new IllegalArgumentException("Null InChI");
330 }
331 JniInchiWrapper wrapper = getWrapper();
332 wrapper.getLock();
333 try {
334 return wrapper.GetINCHIKeyFromINCHI(inchi);
335 } finally {
336 lock.unlock();
337 }
338 }
339
340
341
342
343
344
345
346
347 public static INCHI_KEY_STATUS checkInchiKey(final String key) throws JniInchiException {
348 if (key == null) {
349 throw new IllegalArgumentException("Null InChI key");
350 }
351 JniInchiWrapper wrapper = getWrapper();
352 wrapper.getLock();
353 try {
354 int ret = wrapper.CheckINCHIKey(key);
355 INCHI_KEY_STATUS retStatus = INCHI_KEY_STATUS.getValue(ret);
356 if (retStatus == null) {
357 throw new JniInchiException("Unknown return status: " + ret);
358 }
359 return retStatus;
360 } finally {
361 lock.unlock();
362 }
363 }
364
365
366
367
368
369
370
371
372
373
374
375 public static INCHI_STATUS checkInchi(final String inchi, final boolean strict) throws JniInchiException {
376 if (inchi == null) {
377 throw new IllegalArgumentException("Null InChI");
378 }
379 JniInchiWrapper wrapper = getWrapper();
380 wrapper.getLock();
381 try {
382 int ret = wrapper.CheckINCHI(inchi, strict);
383 INCHI_STATUS retStatus = INCHI_STATUS.getValue(ret);
384 if (retStatus == null) {
385 throw new JniInchiException("Unknown return status: " + ret);
386 }
387 return retStatus;
388 } finally {
389 lock.unlock();
390 }
391 }
392
393 public static JniInchiInputData getInputFromAuxInfo(String auxInfo) throws JniInchiException {
394 if (auxInfo == null) {
395 throw new IllegalArgumentException("Null AuxInfo");
396 }
397 JniInchiWrapper wrapper = getWrapper();
398 wrapper.getLock();
399 try {
400 return wrapper.GetINCHIInputFromAuxInfo(auxInfo, false, false);
401 } finally {
402 lock.unlock();
403 }
404 }
405
406
407 private static synchronized void getLock() throws JniInchiException {
408 try {
409 if (!lock.tryLock(MAX_LOCK_TIMEOUT, TimeUnit.SECONDS)) {
410 throw new TimeoutException("Unable to get lock");
411 }
412 } catch (TimeoutException ex) {
413 throw new JniInchiException(ex);
414 } catch (InterruptedException ex) {
415 throw new JniInchiException(ex);
416 }
417 }
418
419
420
421 protected native static String LibInchiGetVersion();
422
423 private native static void init();
424
425
426 private native JniInchiOutput GetINCHI(JniInchiInput input);
427
428 private native JniInchiOutput GetStdINCHI(JniInchiInput input);
429
430 private native JniInchiOutput GetINCHIfromINCHI(String inchi, String options);
431
432 private native JniInchiOutputStructure GetStructFromINCHI(String inchi, String options);
433
434 private native JniInchiOutputKey GetINCHIKeyFromINCHI(String inchi);
435
436 private native JniInchiOutputKey GetStdINCHIKeyFromStdINCHI(String inchi);
437
438 private native int CheckINCHIKey(String key);
439
440 private native int CheckINCHI(String inchi, boolean strict);
441
442 private native JniInchiInputData GetINCHIInputFromAuxInfo(String auxInfo, boolean bDoNotAddH, boolean bDiffUnkUndfStereo);
443
444 }