Source code: joelib/io/types/ChemicalMarkupLanguage.java
1 ///////////////////////////////////////////////////////////////////////////////
2 // Filename: $RCSfile: ChemicalMarkupLanguage.java,v $
3 // Purpose: Reader/Writer for CML files.
4 // Language: Java
5 // Compiler: JDK 1.4
6 // Authors: Joerg K. Wegner
7 // Version: $Revision: 1.25 $
8 // $Date: 2003/08/22 15:56:17 $
9 // $Author: wegner $
10 // Original Author: steinbeck@ice.mpg.de, gezelter@maul.chem.nd.edu, egonw@sci.kun.nl
11 // Original Version: Chemical Development Kit, http://sourceforge.net/projects/cdk
12 //
13 // Copyright (c) Dept. Computer Architecture, University of Tuebingen, Germany
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 ///////////////////////////////////////////////////////////////////////////////
24 package joelib.io.types;
25
26 import joelib.io.MoleculeFileType;
27 import joelib.io.PropertyWriter;
28
29 import joelib.io.types.cml.CDOInterface;
30 import joelib.io.types.cml.CMLErrorHandler;
31 import joelib.io.types.cml.CMLHandler;
32 import joelib.io.types.cml.CMLMoleculeWriter;
33 import joelib.io.types.cml.CMLResolver;
34 import joelib.io.types.cml.CMLWriterProperties;
35 import joelib.io.types.cml.MoleculeArray;
36 import joelib.io.types.cml.MoleculeFileCDO;
37 import joelib.io.types.cml.MoleculeHuge;
38 import joelib.io.types.cml.MoleculeLarge;
39
40 import joelib.molecule.JOEMol;
41
42 import wsi.ra.tool.PropertyHolder;
43
44 /*==========================================================================*
45 * IMPORTS
46 *========================================================================== */
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.InputStreamReader;
50 import java.io.OutputStream;
51 import java.io.PrintStream;
52
53 import java.util.Vector;
54
55 import org.apache.log4j.Category;
56
57 import org.xml.sax.InputSource;
58 import org.xml.sax.SAXException;
59 import org.xml.sax.XMLReader;
60
61
62 /*==========================================================================*
63 * CLASS DECLARATION
64 *========================================================================== */
65
66 /**
67 * Reader/Writer for Chemical Markup Language (CML) files.
68 *
69 * @author wegnerj
70 * @license GPL
71 * @cvsversion $Revision: 1.25 $, $Date: 2003/08/22 15:56:17 $
72 * @cite rr99b
73 * @cite mr01
74 * @cite gmrw01
75 * @cite wil01
76 */
77 public class ChemicalMarkupLanguage implements MoleculeFileType, PropertyWriter,
78 CMLWriterProperties
79 {
80 //~ Static fields/initializers /////////////////////////////////////////////
81
82 /*-------------------------------------------------------------------------*
83 * public static member variables
84 *------------------------------------------------------------------------- */
85
86 /**
87 * Obtain a suitable logger.
88 */
89 private static Category logger = Category.getInstance(
90 "joelib.io.types.ChemicalMarkupLanguage");
91 private final static String description = new String(
92 "Chemical Markup Language (CML)");
93 private final static String[] extensions = new String[]{"cml"};
94 public static final int OUTPUT_HUGE = 0;
95 public static final int OUTPUT_LARGE = 1;
96 public static final int OUTPUT_ARRAY = 2;
97 public static final String OUTPUT_HUGE_S = "huge";
98 public static final String OUTPUT_LARGE_S = "large";
99 public static final String OUTPUT_ARRAY_S = "array";
100 public static final float CML_VERSION_1 = 1.0f;
101 public static final float CML_VERSION_2 = 2.0f;
102 public static final String DEFAULT_DELIMITER = " ";
103 private static float cmlDefaultVersion = CML_VERSION_2;
104 private static String defaultDelimiter = DEFAULT_DELIMITER;
105
106 //~ Instance fields ////////////////////////////////////////////////////////
107
108 private CMLMoleculeWriter cmlOutputWriter = null;
109
110 // private ContentHandler handler;
111 // private EntityResolver resolver;
112 private InputStreamReader isr;
113 private MoleculeFileCDO cdo;
114 private PrintStream ps;
115 private String dtdResourceDir;
116
117 /*-------------------------------------------------------------------------*
118 * private member variables
119 *------------------------------------------------------------------------- */
120 private XMLReader parser;
121 private boolean done;
122 private boolean forceFormalCharge = false;
123 private boolean fragment;
124 private boolean impliciteHydrogens = false;
125 private boolean moleculeReaded = false;
126 private boolean partialCharge = false;
127 private boolean symmetryInformations = false;
128 private int cmlOutputType = OUTPUT_HUGE;
129
130 //~ Methods ////////////////////////////////////////////////////////////////
131
132 /*-------------------------------------------------------------------------*
133 * constructor
134 *------------------------------------------------------------------------- */
135 /*-------------------------------------------------------------------------*
136 * public methods
137 *------------------------------------------------------------------------- */
138 public float getCMLversion()
139 {
140 return cmlDefaultVersion;
141 }
142
143 public static String getDefaultDelimiter()
144 {
145 return defaultDelimiter;
146 }
147
148 public void closeReader() throws IOException
149 {
150 }
151
152 public void closeWriter() throws IOException
153 {
154 ps.close();
155 }
156
157 public boolean forceWriteFormalCharge()
158 {
159 return forceFormalCharge;
160 }
161
162 /**
163 * Description of the Method
164 *
165 * @param iStream Description of the Parameter
166 * @exception IOException Description of the Exception
167 */
168 public void initReader(InputStream iStream) throws IOException
169 {
170 isr = new InputStreamReader(iStream);
171
172 boolean success = false;
173
174 // Aelfred is prefered.
175
176 /* if (!success)
177 {
178 try
179 {
180 parser = new gnu.xml.aelfred2.XmlReader();
181 logger.info("Using Aelfred2 XML parser.");
182 success = true;
183 }
184 catch (Exception e)
185 {
186 logger.warn("Could not instantiate Aelfred2 XML reader!");
187 }
188 }*/
189
190 // If Aelfred is not available try Xerces
191 if (!success)
192 {
193 try
194 {
195 parser = new org.apache.xerces.parsers.SAXParser();
196 logger.info("Using Xerces XML parser.");
197 success = true;
198 }
199 catch (Exception e)
200 {
201 logger.warn("Could not instantiate Xerces XML reader!");
202 }
203 }
204
205 if (!success)
206 {
207 throw new IOException("Could not instantiate any XML parser!");
208 }
209
210 cdo = new MoleculeFileCDO();
211
212 try
213 {
214 parser.setFeature("http://xml.org/sax/features/validation", false);
215 logger.info("Deactivated validation");
216 }
217 catch (SAXException e)
218 {
219 logger.warn("Cannot deactivate validation.");
220 }
221
222 parser.setContentHandler(new CMLHandler((CDOInterface) cdo));
223 parser.setEntityResolver(new CMLResolver());
224 parser.setErrorHandler(new CMLErrorHandler());
225 moleculeReaded = false;
226 }
227
228 /**
229 * Description of the Method
230 *
231 * @param os Description of the Parameter
232 * @exception IOException Description of the Exception
233 */
234 public void initWriter(OutputStream os) throws IOException
235 {
236 initProperties();
237
238 // ps = new OutputStreamWriter(os);
239 ps = new PrintStream(os);
240
241 done = false;
242 fragment = false;
243
244 switch (cmlOutputType)
245 {
246 case OUTPUT_HUGE:
247 cmlOutputWriter = new MoleculeHuge(this);
248
249 break;
250
251 case OUTPUT_LARGE:
252 cmlOutputWriter = new MoleculeLarge(this);
253
254 break;
255
256 case OUTPUT_ARRAY:
257 cmlOutputWriter = new MoleculeArray(this);
258
259 break;
260 }
261
262 ps.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
263
264 // ps.println("<?xml version=\""+cmlDefaultVersion+"\" encoding=\"ISO-8859-1\"?>");
265 ps.println("<!DOCTYPE molecule SYSTEM \"cml.dtd\" []>");
266 }
267
268 /**
269 * Description of the Method
270 *
271 * @return Description of the Return Value
272 */
273 public String inputDescription()
274 {
275 return description;
276 }
277
278 /**
279 * Description of the Method
280 *
281 * @return Description of the Return Value
282 */
283 public String[] inputFileExtensions()
284 {
285 return extensions;
286 }
287
288 /**
289 * Description of the Method
290 *
291 * @return Description of the Return Value
292 */
293 public String outputDescription()
294 {
295 return description;
296 }
297
298 /**
299 * Description of the Method
300 *
301 * @return Description of the Return Value
302 */
303 public String[] outputFileExtensions()
304 {
305 return extensions;
306 }
307
308 /**
309 * Reads an molecule entry as (unparsed) <tt>String</tt> representation.
310 *
311 * @return <tt>null</tt> if the reader contains no more
312 * relevant data. Otherwise the <tt>String</tt> representation of the
313 * whole molecule entry is returned.
314 * @exception IOException typical IOException
315 */
316 public String read() throws IOException
317 {
318 logger.error(
319 "Reading chemical markup language data as String representation is not implemented yet !!!");
320
321 return null;
322 }
323
324 /**
325 * Description of the Method
326 *
327 * @param mol Description of the Parameter
328 * @return Description of the Return Value
329 * @exception IOException Description of the Exception
330 */
331 public synchronized boolean read(JOEMol mol) throws IOException
332 {
333 return read(mol, null);
334 }
335
336 /**
337 * Loads an molecule in MDL SD-MOL format and sets the title.
338 * If <tt>title</tt> is <tt>null</tt> the title line in
339 * the molecule file is used.
340 *
341 * @param mol Description of the Parameter
342 * @param title Description of the Parameter
343 * @return Description of the Return Value
344 * @exception IOException Description of the Exception
345 */
346 public synchronized boolean read(JOEMol mol, String title)
347 throws IOException
348 {
349 if (moleculeReaded)
350 {
351 return false;
352 }
353
354 cdo.setMolecule(mol);
355
356 // use an own Thread to parse data !!!
357 // and modify XML end target to let the Tread wait
358 // until next read call
359
360 /* try
361 {
362 parser.parse(new InputSource(isr));
363 }
364 catch (IOException e)
365 {
366 logger.error("IOException " + e.toString());
367 throw e;
368 }
369 catch (SAXException e)
370 {
371 logger.error("SAXException " + e.toString());
372 e.printStackTrace();
373 throw new IOException("SAXException " + e.toString());
374 }*/
375 try
376 {
377 parser.parse(new InputSource(isr));
378 }
379 catch (IOException e)
380 {
381 logger.warn("IOException: " + e.toString());
382 }
383 catch (SAXException saxe)
384 {
385 logger.warn("SAXException: " + saxe.getClass().getName());
386 logger.warn(saxe.toString());
387 saxe.printStackTrace();
388 }
389
390 // and send signal for finishing outer loop
391 // in the next round!!!
392 moleculeReaded = true;
393
394 return true;
395 }
396
397 /**
398 * Description of the Method
399 *
400 * @return Description of the Return Value
401 */
402 public boolean readable()
403 {
404 return true;
405 }
406
407 public boolean skipReaderEntry() throws IOException
408 {
409 return true;
410 }
411
412 /**
413 * Description of the Method
414 *
415 * @param mol Description of the Parameter
416 * @return Description of the Return Value
417 * @exception IOException Description of the Exception
418 */
419 public boolean write(JOEMol mol) throws IOException
420 {
421 return write(mol, "Undefined");
422 }
423
424 /**
425 * Description of the Method
426 *
427 * @param mol Description of the Parameter
428 * @param title Description of the Parameter
429 * @return Description of the Return Value
430 * @exception IOException Description of the Exception
431 */
432 public boolean write(JOEMol mol, String title) throws IOException
433 {
434 return write(mol, title, true, null);
435 }
436
437 /**
438 * Writes a molecule with his <tt>JOEPairData</tt> .
439 *
440 * @param mol the molecule with additional data
441 * @param title the molecule title or <tt>null</tt> if the title
442 * from the molecule should be used
443 * @param writePairData if <tt>true</tt> then the additional molecule data
444 * is written
445 * @param attribs2write Description of the Parameter
446 * @return <tt>true</tt> if the molecule and the data has
447 * been succesfully written.
448 * @exception IOException Description of the Exception
449 */
450 public boolean write(JOEMol mol, String title, boolean writePairData,
451 Vector attribs2write) throws IOException
452 {
453 // if (!done)
454 // {
455 // if (!fragment)
456 // {
457 // ps.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
458 // ps.println("<?xml version=\""+cmlDefaultVersion+"\" encoding=\"ISO-8859-1\"?>");
459 // ps.println("<!DOCTYPE molecule SYSTEM \"cml.dtd\" []>");
460 // }
461 // if (object instanceof SetOfMolecules)
462 // {
463 // write((SetOfMolecules) object);
464 // }
465 // else if (object instanceof Molecule)
466 // {
467 // write((Molecule) object);
468 cmlOutputWriter.write(ps, mol, writePairData, attribs2write);
469
470 // }
471 // else
472 // {
473 // throw new UnsupportedChemObjectException("Only supported are SetOfMolecules and Molecule.");
474 // }
475 // if (!fragment)
476 // {
477 // done = true;
478 // }
479 // }
480 // else
481 // {
482 // }
483 return true;
484 }
485
486 public boolean writeImpliciteHydrogens()
487 {
488 return impliciteHydrogens;
489 }
490
491 public boolean writePartialCharge()
492 {
493 return partialCharge;
494 }
495
496 public boolean writeSymmetryInformations()
497 {
498 return symmetryInformations;
499 }
500
501 /**
502 * Description of the Method
503 *
504 * @return Description of the Return Value
505 */
506 public boolean writeable()
507 {
508 return true;
509 }
510
511 /*-------------------------------------------------------------------------*
512 * private methods
513 *------------------------------------------------------------------------- */
514
515 /**
516 * Description of the Method
517 *
518 * @exception IOException Description of the Exception
519 */
520 private void initProperties()
521 {
522 String value;
523
524 value = PropertyHolder.instance().getProperty(this, "output");
525
526 if (value == null)
527 {
528 cmlOutputType = OUTPUT_HUGE;
529 }
530 else if (value.equalsIgnoreCase(OUTPUT_HUGE_S))
531 {
532 cmlOutputType = OUTPUT_HUGE;
533 }
534 else if (value.equalsIgnoreCase(OUTPUT_LARGE_S))
535 {
536 cmlOutputType = OUTPUT_LARGE;
537 }
538 else if (value.equalsIgnoreCase(OUTPUT_ARRAY_S))
539 {
540 cmlOutputType = OUTPUT_ARRAY;
541 }
542 else
543 {
544 logger.error("Use output type :" + OUTPUT_HUGE_S + ", " +
545 OUTPUT_LARGE_S + " and " + OUTPUT_ARRAY_S);
546 cmlOutputType = OUTPUT_HUGE;
547 }
548
549 value = PropertyHolder.instance().getProperty(this,
550 "output.force.formalCharge");
551
552 if (((value != null) && value.equalsIgnoreCase("true")))
553 {
554 forceFormalCharge = true;
555 }
556 else
557 {
558 forceFormalCharge = false;
559 }
560
561 value = PropertyHolder.instance().getProperty(this,
562 "output.partialCharge");
563
564 if (((value != null) && value.equalsIgnoreCase("true")))
565 {
566 partialCharge = true;
567 }
568 else
569 {
570 partialCharge = false;
571 }
572
573 value = PropertyHolder.instance().getProperty(this,
574 "output.hydrogenCount");
575
576 if (((value != null) && value.equalsIgnoreCase("true")))
577 {
578 impliciteHydrogens = true;
579 }
580 else
581 {
582 impliciteHydrogens = false;
583 }
584
585 value = PropertyHolder.instance().getProperty(this,
586 "output.symmetryInformations");
587
588 if (((value != null) && value.equalsIgnoreCase("true")))
589 {
590 symmetryInformations = true;
591 }
592 else
593 {
594 symmetryInformations = false;
595 }
596
597 dtdResourceDir = PropertyHolder.instance().getProperty(this,
598 "DTD.resourceDir");
599
600 double dTmp = PropertyHolder.instance().getDouble(this,
601 "output.defaultVersion", 0);
602
603 if (!Double.isNaN(dTmp))
604 {
605 cmlDefaultVersion = (float) dTmp;
606 }
607
608 value = PropertyHolder.instance().getProperty(this, "defaultDelimiter");
609
610 if (value != null)
611 {
612 defaultDelimiter = value;
613 }
614 }
615 }
616 ///////////////////////////////////////////////////////////////////////////////
617 // END OF FILE.
618 ///////////////////////////////////////////////////////////////////////////////