Source code: joelib/jcamp/JCAMPParser.java
1 ///////////////////////////////////////////////////////////////////////////////
2 // Filename: $RCSfile: JCAMPParser.java,v $
3 // Purpose: Reader/Writer for SDF files.
4 // Language: Java
5 // Compiler: JDK 1.4
6 // Authors: Joerg K. Wegner
7 // Version: $Revision: 1.10 $
8 // $Date: 2003/08/22 15:56:19 $
9 // $Author: wegner $
10 //
11 // Copyright (c) Dept. Computer Architecture, University of Tuebingen, Germany
12 //
13 // This program is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation version 2 of the License.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 ///////////////////////////////////////////////////////////////////////////////
22 package joelib.jcamp;
23
24
25 /*
26 * ==========================================================================*
27 * IMPORTS
28 * ==========================================================================
29 */
30 import java.io.IOException;
31
32 import java.util.Enumeration;
33 import java.util.Hashtable;
34 import java.util.LinkedList;
35 import java.util.ListIterator;
36 import java.util.NoSuchElementException;
37 import java.util.StringTokenizer;
38
39 import org.apache.log4j.Category;
40
41
42 //import wsi.ra.tool.*;
43
44 /*
45 * ==========================================================================*
46 * CLASS DECLARATION
47 * ==========================================================================
48 */
49
50 /**
51 * A class to interpret JCAMP-DX data (JCAMP-CS is not implemented yet). The
52 * supported data types are XYPAIRS, XYDATA=(X++(Y..Y)), PEAK TABLE and LINK.
53 * <br>
54 * If you want load a file with multiple blocks or inner blocks you must use
55 * <code>JCampMultipleFile</code>.<br>
56 * This class can only load separated single blocks with one TITLE and END
57 * label !<br>
58 * <br>
59 *
60 * <ul>
61 * <li> ... The International Union of Pure and Applied Chemistry (IUPAC)
62 * took over responsibility from the Joint Commitee on Atomic and Molecular
63 * Physical Data (JCAMP) in 1995 ...<br>
64 * <a href="http://jcamp.isas-dortmund.de/">I U P A C<br>
65 * Committee on Printed and Electronic Publications <br>
66 * Working Party on Spectroscopic Data Standards (JCAMP-DX)</a> <br>
67 * <br>
68 *
69 * <li> <a href="http://wwwchem.uwimona.edu.jm:1104/software/jcampdx.html">
70 * The Department of Chemistry at the University of the West Indies</a>
71 * </ul>
72 *
73 *
74 * @author wegnerj
75 * @license GPL
76 * @cvsversion $Revision: 1.10 $, $Date: 2003/08/22 15:56:19 $
77 * @cite dl93
78 * @cite dw88
79 * @cite ghhjs91
80 * @cite lhdl94
81 * @cite dhl90
82 */
83 public class JCAMPParser
84 {
85 //~ Static fields/initializers /////////////////////////////////////////////
86
87 /**
88 * Obtain a suitable logger.
89 */
90 private static Category logger = Category.getInstance(
91 "joelib.jcamp.JCAMPParser");
92
93 /*
94 * -------------------------------------------------------------------------*
95 * public member variables
96 * -------------------------------------------------------------------------
97 */
98
99 /**
100 * Description of the Field
101 */
102 public final static int DATA_TYPE_SPECTRUM = 1;
103
104 /**
105 * Description of the Field
106 */
107 public final static int DATA_TYPE_PEAKS = 1;
108
109 /**
110 * data representation: <a href="#DEFINITIONS">XYPAIRS definition</a>
111 */
112 public final static int XYPAIRS = 0;
113
114 /**
115 * data representation: <a href="#DEFINITIONS">XYDATA=(X++(Y..Y))
116 * definition</a>
117 */
118 public final static int XYDATA_X_YY = 10;
119
120 /**
121 * data representation: <a href="#DEFINITIONS">definition for the parameter
122 * of a chemical structure</a>
123 */
124 public final static int CHEMICAL_STRUCTURE = 20;
125
126 /**
127 * data representation: <a href="#DEFINITIONS">XYPOINTS definition</a>
128 */
129 public final static int XYPOINTS = 30;
130
131 /**
132 * data representation: <a href="#DEFINITIONS">PEAK TABLE definition</a>
133 */
134 public final static int PEAK_TABLE = 40;
135
136 /**
137 * data representation: <a href="#DEFINITIONS">PEAK ASSIGNMENTS definition
138 * </a>
139 */
140 public final static int PEAK_ASSIGNMENTS = 50;
141
142 /**
143 * data representation: <a href="#DEFINITIONS">RADATA definition</a>
144 */
145 public final static int RADATA = 60;
146
147 /**
148 * data representation: <a href="#DEFINITIONS">LINK definition</a>
149 */
150 public final static int LINK = 70;
151
152 // private final int dataBlockSize = 5200;
153
154 /**
155 * An array of the commonly used LDR's (Label Data Records). <br>
156 * <br>
157 * For more information see:<BR>
158 *
159 * <UL>
160 * <LI> Robert S. Mc Donald, Paul A. Wilks, ''JCAMP-DX: A Standard Form
161 * for Exchange of Infrared Spectra in Computer Readable Form'', <EM>
162 * Appl. Spec.</EM> , <EM> 42</EM> , (1988), 151-162. <BR>
163 * URL<TT> <A HREF="http://jcamp.isas-dortmund.de/protocols/dxir01.pdf">
164 * http://jcamp.isas-dortmund.de/protocols/dxir01.pdf</A> </TT> </LI>
165 *
166 * <LI> J. Gasteiger, B. M. P. Hendriks, P. Hoever, C. Jochum, H.
167 * Somberg, ''JCAMP-CS: A Standard Form for Chemical Structure
168 * Information in Computer Readable Form'', <EM> Appl. Spec.</EM> , <EM>
169 * 45</EM> , (1991), 4-11. <BR>
170 * URL<TT> <A HREF="http://jcamp.isas-dortmund.de/protocols/dxcs01.pdf">
171 * http://jcamp.isas-dortmund.de/protocols/dxcs01.pdf</A> </TT> </LI>
172 *
173 * <LI> Peter Lampen, Heinrich Hillig, Antony N. Davies, Michael
174 * Linscheid, ''JCAMP-DX for Mass Spectrometry'', <EM> Appl. Spec.</EM> ,
175 * <EM> 48</EM> , (1994), 1545-1552. <BR>
176 * URL<TT> <A HREF="http://jcamp.isas-dortmund.de/protocols/dxms01.pdf">
177 * http://jcamp.isas-dortmund.de/protocols/dxms01.pdf</A> </TT> </LI>
178 *
179 * <LI> Antony N. Davies, Peter Lampen, ''JCAMP-DX for NMR'', <EM> Appl.
180 * Spec.</EM> , <EM> 47</EM> , (1993), 1093-1099. <BR>
181 * URL<TT> <A HREF="http://jcamp.isas-dortmund.de/protocols/dxnmr01.pdf">
182 * http://jcamp.isas-dortmund.de/protocols/dxnmr01.pdf</A> </TT> </LI>
183 *
184 * </UL>
185 *
186 */
187 public final static String[] commonLDRs =
188 {
189 ".ACCELERATING VOLTAGE", ".ACQUISITION MODE", ".ACQUISITION RANGE",
190 ".ACQUISITION TIME", ".AVERAGES", ".BASE PEAK", ".BASE PEAK INTENSITY",
191 ".COUPLING CONSTANTS", ".DECOUPLER", ".DELAY", ".DETECTOR",
192 ".DIGITISER RES", ".FIELD", ".FILTER WIDTH", ".INLET",
193 ".IONISATION ENERGY", ".IONISATION MODE", ".MIN INTENSITY",
194 ".MAX INTENSITY", ".NOMINAL MASS", ".OBSERVE 90", ".OBSERVE FREQUENCY",
195 ".OBSERVE NUCLEUS", ".PHASE 0", ".PHASE 1", ".RELAXATION TIMES", ".RIC",
196 ".SCAN NUMBER", ".SCAN RATE", ".SOLVENT REFERENCE",
197 ".SOURCE TEMPERATURE", ".SPECTROMETER TYPE", ".SPINNING RATE",
198 ".TOTAL ION CURRENT", ".ZERO FILL", "AFACTOR", "ALIAS", "ATOMLIST",
199 "AUNITS", "BEILSTEIN LAWSON NO", "BLOCK_ID", "BLOCKS", "BONDLIST", "BP",
200 "CAS NAME", "CAS REGISTRY NO", "CHARGE", "CLASS", "CONCENTRATIONS",
201 "CROSS REFERENCE", "DATA CLASS", "DATA PROCESSING", "DATA TABLE",
202 "DATA TYPE", "DATE", "DELTAR", "DELTAX", "DENSITY", "END", "END NTUPLES",
203 "FACTOR", "FIRST", "FIRSTA", "FIRSTR", "FIRSTX", "FIRSTY",
204 "INSTRUMENT PARAMETERS", "JCAMP-CS", "JCAMP-DX", "LAST", "LASTR",
205 "LASTX", "MAX", "MAX_RASTER", "MAX_XYZ", "MAXA", "MAXX", "MAXY", "MIN",
206 "MINA", "MINX", "MINY", "MOLFORM", "MP", "MW", "NPOINTS", "NTUPLES",
207 "ORIGIN", "OWNER", "PAGE", "PATH LENGTH", "PEAK ASSIGNMENTS",
208 "PEAK TABLE", "PRESSURE", "RADATA", "RADICAL", "REFRACTIVE INDEX",
209 "RESOLUTION", "RFACTOR", "RUNITS", "SAMPLE DESCRIPTION",
210 "SAMPLING PROCEDURE", "SOURCE REFERENCE", "SPECTROMETER/DATA SYSTEM",
211 "STATE", "STEREOCENTER", "STEREOMOLECULE", "STEREOPAIR", "TEMPERATURE",
212 "TIME", "TITLE", "UNITS", "VAR_DIM", "VAR_NAME", "VAR_FORM", "WISWESSER",
213 "XFACTOR", "XLABEL", "XUNITS", "XYDATA", "XY_RASTER", "XYPOINTS", "XYZ",
214 "XYZ_FACTOR", "XYZ_SOURCE", "YFACTOR", "YLABEL", "YUNITS", "ZPD"
215 };
216
217 //~ Instance fields ////////////////////////////////////////////////////////
218
219 /*
220 * -------------------------------------------------------------------------*
221 * private member variables
222 * -------------------------------------------------------------------------
223 */
224
225 /**
226 * here are the LDR's (label data records) stored
227 */
228 private Hashtable parameterTable = new Hashtable();
229
230 /**
231 * temporarily used when data is decoded
232 */
233 private LinkedList xData;
234
235 /**
236 * temporarily used when data is decoded
237 */
238 private LinkedList yData;
239
240 /**
241 * state if the <a href="#decodeData()"><code>decodeData()</code></a> -
242 * routine must estimate the DELTAX label
243 *
244 *@see #decodeData()
245 */
246 private boolean calculatedDeltaXneeded = false;
247
248 /**
249 * the state if this data was decoded. Only available after decoding the
250 * data with <a href="#decodeData()"><code>decodeData()</code></a>
251 *
252 *@see #decodeData()
253 */
254 private boolean dataDecoded = false;
255
256 /**
257 * state if the <a href="#decodeData()"><code>decodeData()</code></a> -
258 * routine must estimate the NPOINTS label
259 *
260 *@see #decodeData()
261 */
262 private boolean estimatedNPointsNeeded = false;
263
264 /**
265 * state if the separator standard is already checked
266 */
267 private boolean separatorStandardChecked = false;
268
269 /**
270 * standard separator is "," and ";", values are represanteted with ".",
271 * e.g. "123.456". Spaces can be used for a nicer look.<br>
272 * <br>
273 * Another possibility for a separator is " ", values are represanteted
274 * with ",", e.g. "123,456". That's not a standard definition !!!
275 */
276 private boolean standardSeparator = true;
277
278 /**
279 * this defines the valid difference of the original DELTAX label and the
280 * calculated DELTAX label
281 *
282 *@see #decodeData()
283 */
284 private final double DELTAX_DIFFERENCE_WARNING_VALUE = 0.001;
285 private double[] xDoubleData;
286 private double[] yDoubleData;
287
288 /**
289 * DELTAX label from this data set
290 */
291 private double deltaX = 0;
292
293 /**
294 * FIRSTX label from this data set
295 */
296 private double firstX = 0;
297
298 /**
299 * LASTX label from this data set
300 */
301 private double lastX = 0;
302
303 /**
304 * NPOINTS label from this data set
305 */
306 private double nPoints = 0;
307
308 /**
309 * XFACTOR label from this data set
310 */
311 private double xFactor = 0;
312
313 /**
314 * YFACTOR label from this data set
315 */
316 private double yFactor = 0;
317
318 /**
319 * the JCAMP data type of this data set. If the data type was not evaluated
320 * by <a href="#decodeData()"><code>decodeData()</code></a> it is -1
321 */
322 private int dataType = -1;
323
324 //~ Constructors ///////////////////////////////////////////////////////////
325
326 /*
327 * ------------------------------------------------------------------------*
328 * constructor
329 * ------------------------------------------------------------------------
330 */
331
332 /**
333 * Initializes this <code>JCampInterpreter</code> and gets the JCAMP data
334 * in the <code>String s</code>. <code>file</code> is stored, but not used.
335 * Data is decoded if <code>state</code> is true. Messages are printed to
336 * the <code>JTextComponent messages</code>
337 *
338 *@param s Description of the Parameter
339 *@param state Description of the Parameter
340 *@exception IOException Description of the Exception
341 *@exception JCAMPException Description of the Exception
342 */
343 public JCAMPParser(String s) throws IOException, JCAMPException
344 {
345 interpretLDRs(s);
346 decodeData();
347 }
348
349 //~ Methods ////////////////////////////////////////////////////////////////
350
351 /**
352 * Gets the data type of this data set. That's important for the
353 * visualisation routines.
354 *
355 *@return The dataType value
356 */
357 public final int getDataType()
358 {
359 if ((dataType == XYPAIRS) || (dataType == XYDATA_X_YY))
360 {
361 return DATA_TYPE_SPECTRUM;
362 }
363
364 if (dataType == PEAK_TABLE)
365 {
366 return DATA_TYPE_PEAKS;
367 }
368
369 return -1;
370 }
371
372 /**
373 * Gets the JCAMP data type of this data set.
374 *
375 *@return the JCAMP data type
376 */
377 public final int getJCampDataType()
378 {
379 return dataType;
380 }
381
382 /**
383 * Gets the length of the X and Y array.
384 *
385 *@return the length of the X and Y data array. If the data was not
386 * decoded it returns -1.
387 */
388 public final int getLength()
389 {
390 if (dataDecoded)
391 {
392 return xDoubleData.length;
393 }
394 else
395 {
396 return -1;
397 }
398 }
399
400 /**
401 * Set some LDR's (data label records).
402 *
403 *@param newLDR the new labels and the new data
404 */
405 public final void setParameter(LabelData[] newLDR)
406 {
407 for (int i = 0; i < newLDR.length; i++)
408 {
409 if (newLDR[i] != null)
410 {
411 parameterTable.put(newLDR[i].getLabel(), newLDR[i].getData());
412 }
413 }
414 }
415
416 /**
417 * Set one LDR (data label record).
418 *
419 *@param newLDR the new label and the new data
420 */
421 public final void setParameter(LabelData newLDR)
422 {
423 if (newLDR != null)
424 {
425 parameterTable.put(newLDR.getLabel(), newLDR.getData());
426 }
427 }
428
429 /*
430 * -------------------------------------------------------------------------*
431 * public methods
432 * -------------------------------------------------------------------------
433 */
434
435 /**
436 * Get all LDR's (data label records) that are stored.
437 *
438 *@return all labels and their corresponding data
439 */
440 public final LabelData[] getParameter()
441 {
442 Enumeration e;
443 LabelData[] back;
444 int size = parameterTable.size();
445 back = new LabelData[2 * size];
446
447 int i = 0;
448
449 for (e = parameterTable.keys(); e.hasMoreElements();)
450 {
451 back[i] = new LabelData();
452 back[i].setLabel((String) e.nextElement());
453 back[i].setData((String) parameterTable.get(back[i].getLabel()));
454 i++;
455 }
456
457 return back;
458 }
459
460 public final LabelData[] getParameter(String[] wanted)
461 {
462 if (wanted == null)
463 {
464 return null;
465 }
466
467 LabelData[] back = new LabelData[2 * wanted.length];
468
469 for (int i = 0; i < wanted.length; i++)
470 {
471 if (wanted[i] != null)
472 {
473 back[i] = new LabelData();
474 back[i].label = wanted[i];
475 back[i].data = (String) parameterTable.get(wanted[i]);
476
477 // if (back[i].data == null) {
478 // logger.warn("JCAMP label \"" + wanted + "\" does not exist");
479 // }
480 }
481 else
482 {
483 back[i] = new LabelData();
484 }
485 }
486
487 return back;
488 }
489
490 public final LabelData getParameter(String wanted)
491 {
492 LabelData back = new LabelData();
493
494 back.setLabel(wanted);
495 back.setData((String) parameterTable.get(wanted));
496
497 if (back.getData() == null)
498 {
499 // logger.warn("JCAMP label \"" + wanted + "\" does not exist");
500 return null;
501 }
502
503 return back;
504 }
505
506 /**
507 * Gets the X array.
508 *
509 *@return the X data or <code>null</code> if the data was not decoded.
510 */
511 public final double[] getXData()
512 {
513 if (dataDecoded)
514 {
515 return xDoubleData;
516 }
517 else
518 {
519 if (dataDecoded)
520 {
521 return xDoubleData;
522 }
523 else
524 {
525 return null;
526 }
527 }
528 }
529
530 /**
531 * Gets the label for the X axis. It's equivalent to <code>getParameter</code>
532 * ("XUNITS"). If the LDR is not available an empty <code>String</code> is
533 * returned.
534 *
535 *@return the label for the X axis.
536 */
537 public final String getXLabel()
538 {
539 LabelData set = new LabelData();
540
541 if (dataDecoded)
542 {
543 set = getParameter("XUNITS");
544
545 if (set == null)
546 {
547 return new String();
548 }
549
550 return set.getData();
551 }
552 else
553 {
554 logger.warn("Can't get JCAMP XUNITS label. Data not decoded.");
555
556 return null;
557 }
558 }
559
560 /**
561 * Gets the Y array.
562 *
563 *@return the Y data or <code>null</code> if the data was not decoded.
564 */
565 public final double[] getYData()
566 {
567 if (dataDecoded)
568 {
569 return yDoubleData;
570 }
571 else
572 {
573 if (dataDecoded)
574 {
575 return yDoubleData;
576 }
577 else
578 {
579 return null;
580 }
581 }
582 }
583
584 /**
585 * Gets the label for the Y axis. It's equivalent to <code>getParameter</code>
586 * ("YUNITS"). If the LDR is not available an empty <code>String</code> is
587 * returned.
588 *
589 *@return the label for the Y axis.
590 */
591 public final String getYLabel()
592 {
593 LabelData set = new LabelData();
594
595 if (dataDecoded)
596 {
597 set = getParameter("YUNITS");
598
599 if (set == null)
600 {
601 return new String();
602 }
603
604 return set.getData();
605 }
606 else
607 {
608 logger.warn("Can't get JCAMP YUNITS label. Data not decoded.");
609
610 return null;
611 }
612 }
613
614 /**
615 * Gets the label <code>infoType</code>. It's equivalent to <code>getParameter</code>
616 * (<code>infoType</code>). If the LDR is not available an empty <code>String</code>
617 * is returned.
618 *
619 *@param infoType Description of the Parameter
620 *@return the data for the requested label.
621 */
622 public String getAdditionalInformation(String infoType)
623 {
624 LabelData set = new LabelData();
625
626 set = getParameter(infoType);
627
628 if (set == null)
629 {
630 return new String();
631 }
632
633 return set.getData();
634 }
635
636 /**
637 * Returns a string of all LDR's (label data records.
638 *
639 *@return a string of all LDR's (label data records
640 */
641 public String toString()
642 {
643 // HashTable in String umwandeln;
644 String s = new String();
645 Enumeration en;
646
647 //int i=0;
648 String dummy;
649
650 LabelData dummy2 = null;
651 dummy2 = getParameter("TITLE");
652
653 if (dummy2 == null)
654 {
655 dummy2 = new LabelData();
656 dummy2.label = new String("TITLE");
657 dummy2.data = new String();
658 }
659
660 s = s.concat("##" + dummy2.label + "=" + dummy2.data + "\r\n");
661
662 for (en = parameterTable.keys(); en.hasMoreElements();)
663 {
664 dummy = (String) en.nextElement();
665
666 if (!dummy.equals("TITLE") && !dummy.equals("END"))
667 {
668 s = s.concat("##" + dummy + "=" +
669 (String) parameterTable.get(dummy) + "\r\n");
670 }
671
672 //i=i+2;
673 }
674
675 dummy2 = getParameter("END");
676
677 if (dummy2 == null)
678 {
679 dummy2 = new LabelData();
680 dummy2.label = new String("END");
681 dummy2.data = new String();
682 }
683
684 s = s.concat("##" + dummy2.label + "=" + dummy2.data + "\r\n");
685
686 return s;
687 }
688
689 /*
690 * -------------------------------------------------------------------------*
691 * protected methods
692 * -------------------------------------------------------------------------
693 */
694
695 /**
696 * Gets the <code>double</code> from <code>ld.data</code>.
697 *
698 *@param ld Description of the Parameter
699 *@return The double value
700 *@exception NumberFormatException Description of the Exception
701 */
702 protected final double getDouble(LabelData ld) throws NumberFormatException
703 {
704 if (ld == null)
705 {
706 return 0.0;
707 }
708
709 double dummy = 0;
710
711 try
712 {
713 //alle ',' gleich durch '.' ersetzen, sonst gibts eine NumberFormatException
714 dummy = (new Double(ld.getData().replace(',', '.'))).doubleValue();
715 }
716 catch (NumberFormatException e)
717 {
718 if (e.toString().indexOf("empty") >= 0)
719 {
720 return 0.0;
721 }
722 else
723 {
724 throw new NumberFormatException("The Label " + ld.getLabel() +
725 " contains not a valid double value: " + ld.getData());
726 }
727 }
728
729 return dummy;
730 }
731
732 /**
733 * Adds a Data Line to the yData- and the xData - puffer
734 *
735 *@param s The feature to be added to the
736 * XYDATADataLine attribute
737 *@exception NumberFormatException Description of the Exception
738 */
739 private final void addXYDATADataLine(String s) throws NumberFormatException
740 {
741 double xDataDummy = 0;
742 double yDataDummy = 0;
743
744 if (!separatorStandardChecked)
745 {
746 //entscheide ob , als Trennung oder fuer FlieSkommazahl verwendet wird !;-((
747 int kommaIndex = s.indexOf(",", 0);
748 int pointIndex = s.indexOf(".", 0);
749 int spaceIndex = s.indexOf(" ", 0);
750 int tabulatorIndex = s.indexOf("\t", 0);
751
752 if (pointIndex == -1)
753 {
754 //bei schlechtem Standard, kann auch Komma Fuer FlieSkommazahlen
755 //vorkommen und darf NICHT als Trennzeichen interpretiert werden
756 // Was fuer ein Mist, gibts, aber !!!
757 if ((kommaIndex != -1) && (spaceIndex != -1))
758 {
759 if (kommaIndex < spaceIndex)
760 {
761 if ((s.charAt(kommaIndex + 1) >= '0') &&
762 (s.charAt(kommaIndex + 1) <= '9'))
763 {
764 //s=s.replace(',','.');
765 standardSeparator = false;
766 }
767 }
768 }
769 else if ((kommaIndex != -1) && (tabulatorIndex != -1))
770 {
771 if (kommaIndex < tabulatorIndex)
772 {
773 if ((s.charAt(kommaIndex + 1) >= '0') &&
774 (s.charAt(kommaIndex + 1) <= '9'))
775 {
776 //s=s.replace(',','.');
777 standardSeparator = false;
778 }
779 }
780 }
781 }
782
783 separatorStandardChecked = true;
784 }
785
786 if (!standardSeparator)
787 {
788 s = s.replace(',', '.');
789 }
790
791 StringTokenizer dataLine = new StringTokenizer(s, " \t,");
792 int yDataPerLine = dataLine.countTokens() - 1;
793 int dummyIndex = 0;
794 String s1 = "";
795 String s2 = "";
796
797 yDataPerLine = 0;
798
799 do
800 {
801 try
802 {
803 s1 = dataLine.nextToken();
804 }
805 catch (NoSuchElementException e)
806 {
807 break;
808 }
809
810 int signIndex = s1.indexOf("-", 1);
811
812 //X Wert gar nicht erst auf - ueberpruefen
813 String sign = new String("-");
814
815 if (signIndex != -1)
816 {
817 int posSignIndex = s1.indexOf("+", 1);
818
819 if ((posSignIndex != -1) && (posSignIndex < signIndex))
820 {
821 signIndex = posSignIndex;
822 sign = new String("+");
823 }
824 }
825 else
826 {
827 int posSignIndex = s1.indexOf("+", 1);
828
829 if (posSignIndex != -1)
830 {
831 signIndex = posSignIndex;
832 sign = new String("+");
833 }
834 }
835
836 if (signIndex > 0)
837 {
838 if (signIndex > 0)
839 {
840 // println("sign in "+s1+ " "+signIndex);
841 s2 = s1.substring(0, signIndex);
842 yDataPerLine++;
843
844 if (yDataPerLine == 1)
845 {
846 try
847 {
848 xDataDummy = (new Double(s2)).doubleValue();
849 }
850 catch (NumberFormatException e)
851 {
852 // logger.error(s2);
853 logger.error("The data line \"" + s + "\"" +
854 " is skipped. It contains" +
855 " not a valid X value.");
856
857 return;
858 }
859
860 xDataDummy = xDataDummy * xFactor;
861
862 // println("x="+s2);
863 }
864 else
865 {
866 try
867 {
868 yDataDummy = (new Double(s2)).doubleValue();
869 }
870 catch (NumberFormatException e)
871 {
872 logger.error("The data line \"" + s + "\"" +
873 " is skipped. It contains" +
874 " not a valid Y value.");
875
876 return;
877 }
878
879 yDataDummy = yDataDummy * yFactor;
880 xData.add(new Double(xDataDummy));
881 yData.add(new Double(yDataDummy));
882 xDataDummy = xDataDummy + deltaX;
883
884 // println("y="+s2);
885 }
886
887 do
888 {
889 dummyIndex = s1.indexOf(sign, signIndex + 1);
890
891 if (dummyIndex == -1)
892 {
893 dummyIndex = s1.length();
894 }
895
896 s2 = s1.substring(signIndex, dummyIndex);
897 yDataPerLine++;
898
899 try
900 {
901 yDataDummy = (new Double(s2)).doubleValue();
902 }
903 catch (NumberFormatException e)
904 {
905 logger.error("The data line \"" + s + "\"" +
906 " is skipped. It contains" +
907 " not a valid Y value.");
908
909 return;
910 }
911
912 yDataDummy = yDataDummy * yFactor;
913 xData.add(new Double(xDataDummy));
914 yData.add(new Double(yDataDummy));
915 xDataDummy = xDataDummy + deltaX;
916
917 // println("y="+s2);
918 signIndex = s1.indexOf(sign, signIndex + 1);
919 }
920 while (dummyIndex != s1.length());
921 }
922 }
923 else
924 {
925 yDataPerLine++;
926
927 if (yDataPerLine == 1)
928 {
929 try
930 {
931 //println(s1);
932 xDataDummy = (new Double(s1)).doubleValue();
933 }
934 catch (NumberFormatException e)
935 {
936 logger.error("The data line \"" + s + "\"" +
937 " is skipped. It contains" +
938 " not a valid X value.");
939
940 return;
941 }
942
943 xDataDummy = xDataDummy * xFactor;
944
945 //println("x: "+xDataDummy);
946 }
947 else
948 {
949 try
950 {
951 yDataDummy = (new Double(s1)).doubleValue();
952 }
953 catch (NumberFormatException e)
954 {
955 logger.error("The data line \"" + s + "\"" +
956 " is skipped. It contains" +
957 " not a valid Y value.");
958
959 return;
960 }
961
962 yDataDummy = yDataDummy * yFactor;
963 xData.add(new Double(xDataDummy));
964 yData.add(new Double(yDataDummy));
965
966 //println(xDataDummy+" "+yDataDummy);
967 xDataDummy = xDataDummy + deltaX;
968 }
969 }
970 }
971 while (true);
972 }
973
974 /**
975 * Adds a Data Line to the yData- and the xData - puffer
976 *
977 *@param s The feature to be added to the
978 * XYPAIRSandPEAKTABLEdataLine attribute
979 *@exception NumberFormatException Description of the Exception
980 */
981 private final void addXYPAIRSandPEAKTABLEdataLine(String s)
982 throws NumberFormatException
983 {
984 double xDataDummy = 0;
985 double yDataDummy = 0;
986
987 if (!separatorStandardChecked)
988 {
989 //entscheide ob , als Trennung oder fuer FlieSkommazahl verwendet wird !;-((
990 int kommaIndex = s.indexOf(",", 0);
991 int pointIndex = s.indexOf(".", 0);
992 int spaceIndex = s.indexOf(" ", 0);
993 int tabulatorIndex = s.indexOf("\t", 0);
994
995 if (pointIndex == -1)
996 {
997 //bei schlechtem Standard, kann auch Komma Fuer FlieSkommazahlen
998 //vorkommen und darf NICHT als Trennzeichen interpretiert werden
999 // Was fuer ein Mist, gibts, aber !!!
1000 if ((kommaIndex != -1) && (spaceIndex != -1))
1001 {
1002 if (kommaIndex < spaceIndex)
1003 {
1004 if ((s.charAt(kommaIndex + 1) >= '0') &&
1005 (s.charAt(kommaIndex + 1) <= '9'))
1006 {
1007 //s=s.replace(',','.');
1008 standardSeparator = false;
1009 }
1010 }
1011 }
1012 else if ((kommaIndex != -1) && (tabulatorIndex != -1))
1013 {
1014 if (kommaIndex < tabulatorIndex)
1015 {
1016 if ((s.charAt(kommaIndex + 1) >= '0') &&
1017 (s.charAt(kommaIndex + 1) <= '9'))
1018 {
1019 //s=s.replace(',','.');
1020 standardSeparator = false;
1021 }
1022 }
1023 }
1024 }
1025
1026 separatorStandardChecked = true;
1027 }
1028
1029 if (!standardSeparator)
1030 {
1031 s = s.replace(',', '.');
1032 }
1033
1034 StringTokenizer dataLine = new StringTokenizer(s, " \t,");
1035 String s1 = "";
1036
1037 try
1038 {
1039 s1 = dataLine.nextToken();
1040 }
1041 catch (NoSuchElementException e)
1042 {
1043 logger.error("The data line \"" + s + "\" is skipped. It contains" +
1044 " no valid data.");
1045
1046 return;
1047 }
1048
1049 try
1050 {
1051 //println("x "+s1);
1052 xDataDummy = (new Double(s1)).doubleValue();
1053 }
1054 catch (NumberFormatException e)
1055 {
1056 logger.error("The data line \"" + s + "\" is skipped. It contains" +
1057 " not a valid X value.");
1058
1059 return;
1060 }
1061
1062 xDataDummy = xDataDummy * xFactor;
1063 xData.add(new Double(xDataDummy));
1064
1065 try
1066 {
1067 s1 = dataLine.nextToken();
1068 }
1069 catch (NoSuchElementException e)
1070 {
1071 }
1072
1073 try
1074 {
1075 //println("y "+s1);
1076 yDataDummy = (new Double(s1)).doubleValue();
1077 }
1078 catch (NumberFormatException e)
1079 {
1080 logger.error("The data line \"" + s + "\" is skipped. It contains" +
1081 " not a valid Y value.");
1082
1083 return;
1084 }
1085
1086 yDataDummy = yDataDummy * yFactor;
1087 yData.add(new Double(yDataDummy));
1088 }
1089
1090 /**
1091 * Decodes the chemical data.
1092 *
1093 *@exception JCAMPException Description of the Exception
1094 *@exception NumberFormatException Description of the Exception
1095 */
1096 private final void decodeChemicalStructure()
1097 throws JCAMPException, NumberFormatException
1098 {
1099 }
1100
1101 /**
1102 * Decide which data type this JCAMP data set contains and decodes this
1103 * data. After decoding the data is available with <code>getXData()</code>
1104 * and <code>getYData()</code>, if the data contains a spectra.<br>
1105 * Chemical structures are not supported, until now !
1106 *
1107 *@param decodeDataNow if <code>true</code> the data type is
1108 * estimated and the data is decoded. if <code>false</code> only the
1109 * data type is estimated.
1110 *@exception JCAMPException Description of the Exception
1111 *@exception NumberFormatException Description of the Exception
1112 */
1113 private void decodeData() throws JCAMPException, NumberFormatException
1114 {
1115 if (dataDecoded)
1116 {
1117 return;
1118 }
1119
1120 LabelData set;
1121 int end = 0;
1122 String s1 = "";
1123
1124 //which data type ?
1125 boolean xypairsState = true;
1126 boolean xydataState = true;
1127 set = getParameter("XYPAIRS");
1128
1129 if (set == null)
1130 {
1131 xypairsState = false;
1132 }
1133
1134 if (xypairsState)
1135 {
1136 end = set.getData().indexOf("\n", 0);
1137
1138 if (end == -1)
1139 {
1140 throw new JCAMPException(
1141 "Carriage return after \"=\" is not allowed");
1142 }
1143
1144 s1 = set.getData().substring(0, end).trim();
1145
1146 if (!s1.equals("(XY..XY)"))
1147 {
1148 xypairsState = false;
1149 }
1150 else
1151 {
1152 dataType = XYPAIRS;
1153 decodeXYPAIRSandPEAKTABLE();
1154 dataDecoded = true;
1155
1156 return;
1157 }
1158 }
1159
1160 if (!xypairsState)
1161 {
1162 set = getParameter("XYDATA");
1163
1164 if (set == null)
1165 {
1166 xydataState = false;
1167 }
1168
1169 if (xydataState)
1170 {
1171 end = set.getData().indexOf("\n", 0);
1172
1173 if (end == -1)
1174 {
1175 throw new JCAMPException(
1176 "Carriage return after \"=\" is not allowed");
1177 }
1178
1179 s1 = set.getData().substring(0, end).trim();
1180
1181 if (!s1.equals("(X++(Y..Y))"))
1182 {
1183 xydataState = false;
1184 }
1185 else
1186 {
1187 dataType = XYDATA_X_YY;
1188 decodeXYDATA();
1189 dataDecoded = true;
1190
1191 return;
1192 }
1193 }
1194 }
1195
1196 /*
1197 * if(dataType==XYPAIRS || dataType==XYDATA_X_YY){
1198 * if(decodeDataNow){
1199 * decodeXYDATAorXYPAIRS();
1200 * }
1201 * dataDecoded=true;
1202 * return;
1203 * }
1204 */
1205 boolean chemicalStructureState = true;
1206 set = getParameter("MOLFORM");
1207
1208 if (set == null)
1209 {
1210 chemicalStructureState = false;
1211 }
1212
1213 set = getParameter("ATOMLIST");
1214
1215 if (set == null)
1216 {
1217 chemicalStructureState = false;
1218 }
1219
1220 set = getParameter("BONDLIST");
1221
1222 if (set == null)
1223 {
1224 chemicalStructureState = false;
1225 }
1226
1227 if (chemicalStructureState)
1228 {
1229 dataType = CHEMICAL_STRUCTURE;
1230
1231 //if(state)decodeChemicalStructure();
1232 //dataDecoded=true;
1233 return;
1234 }
1235
1236 set = getParameter("XYPOINTS");
1237
1238 if (set != null)
1239 {
1240 throw new JCAMPException("Data XYPOINTS is not supported ");
1241
1242 //dataType=XYPOINTS;
1243 //dataDecoded=true;
1244 //return;
1245 }
1246
1247 set = getParameter("PEAK TABLE");
1248
1249 if (set != null)
1250 {
1251 end = set.getData().indexOf("\n", 0);
1252
1253 if (end == -1)
1254 {
1255 throw new JCAMPException(
1256 "Carriage return after \"=\" is not allowed");
1257 }
1258
1259 s1 = set.getData().substring(0, end).trim();
1260
1261 if (s1.equals("(XY..XY)"))
1262 {
1263 dataType = PEAK_TABLE;
1264 decodeXYPAIRSandPEAKTABLE();
1265 dataDecoded = true;
1266
1267 return;
1268 }
1269 }
1270
1271 set = getParameter("PEAK ASSIGNMENTS");
1272
1273 if (set != null)
1274 {
1275 throw new JCAMPException("Data PEAK ASSIGNMENTS is not supported ");
1276
1277 //dataType=PEAK_ASSIGNMENTS;
1278 //dataDecoded=true;
1279 //return;
1280 }
1281
1282 set = getParameter("RADATA");
1283
1284 if (set != null)
1285 {
1286 throw new JCAMPException("Data RADATA is not supported ");
1287
1288 //dataType=RADATA;
1289 //dataDecoded=true;
1290 //return;
1291 }
1292
1293 set = getParameter("DATA TYPE");
1294
1295 if (set != null)
1296 {
1297 if (set.getData().trim().equals("LINK"))
1298 {
1299 dataType = LINK;
1300
1301 //dataDecoded=true;
1302 return;
1303 }
1304 }
1305
1306 //irgend ein anderer Datentyp
1307 throw new JCAMPException("Data type not supported ");
1308 }
1309
1310 /*
1311 * -------------------------------------------------------------------------*
1312 * private methods
1313 * -------------------------------------------------------------------------
1314 */
1315
1316 /**
1317 * Decodes XYDATA. Only the uncompressed ASDF format is supported. The
1318 * compressed AFFN format is not supported.
1319 *
1320 *@exception JCAMPException Description of the Exception
1321 *@exception NumberFormatException Description of the Exception
1322 */
1323 private void decodeXYDATA() throws JCAMPException, NumberFormatException
1324 {
1325 LabelData set = new LabelData();
1326 int begin;
1327 int end = 0;
1328 String s1 = "";
1329 String s2 = "";
1330
1331 set = getParameter("DELTAX");
1332
1333 if (set == null)
1334 {
1335 logger.warn("The label DELTAX is missing. Now its calculated !;-)");
1336 calculatedDeltaXneeded = true;
1337 }
1338
1339 deltaX = getDouble(set);
1340
1341 set = getParameter("LASTX");
1342
1343 if (set == null)
1344 {
1345 String add = new String();
1346
1347 if (!calculatedDeltaXneeded)
1348 {
1349 add = new String(". The label DELTAX is missing and " +
1350 "can not be calculated");
1351 }
1352
1353 throw new JCAMPException("The label LASTX is missing" + add);
1354 }
1355
1356 lastX = getDouble(set);
1357
1358 set = getParameter("FIRSTX");
1359
1360 if (set == null)
1361 {
1362 String add = new String();
1363
1364 if (!calculatedDeltaXneeded)
1365 {
1366 add = new String(". The label DELTAX is missing and " +
1367 "can not be calculated");
1368 }
1369
1370 throw new JCAMPException("The label FIRSTX is missing" + add);
1371 }
1372
1373 firstX = getDouble(set);
1374
1375 set = getParameter("NPOINTS");
1376
1377 if (set == null)
1378 {
1379 String add = new String();
1380
1381 if (!calculatedDeltaXneeded)
1382 {
1383 add = new String(". The label NPOINTS is missing and DELTAX" +
1384 "can not be calculated");
1385 throw new JCAMPException("The label FIRSTX is missing" + add);
1386 }
1387
1388 logger.warn("The label NPOINTS is missing. Now its estimated");
1389 estimatedNPointsNeeded = true;
1390 }
1391
1392 nPoints = getDouble(set);
1393
1394 //Berechnung von deltaX;
1395 if (calculatedDeltaXneeded)
1396 {
1397 deltaX = (lastX - firstX) / (nPoints - 1);
1398 }
1399 else
1400 {
1401 double dummy = 0;
1402
1403 if (!estimatedNPointsNeeded && (nPoints != 0))
1404 {
1405 //einfach nochmals zur Kontrolle von deltaX. Eigentlich unnoetig !
1406 dummy = (lastX - firstX) / (nPoints - 1);
1407
1408 if (Math.abs(dummy - deltaX) > DELTAX_DIFFERENCE_WARNING_VALUE)
1409 {
1410 logger.warn("The calculated DELTAX=" + dummy +
1411 " (original DELTAX=" + deltaX +
1412 ") shows a difference above " +
1413 DELTAX_DIFFERENCE_WARNING_VALUE);
1414 }
1415 }
1416 }
1417
1418 set = getParameter("XFACTOR");
1419
1420 if (set == null)
1421 {
1422 throw new JCAMPException("The label XFACTOR is missing");
1423 }
1424
1425 xFactor = getDouble(set);
1426
1427 set = getParameter("YFACTOR");
1428
1429 if (set == null)
1430 {
1431 throw new JCAMPException("The label YFACTOR is missing");
1432 }
1433
1434 yFactor = getDouble(set);
1435
1436 //beginne Dekodierung
1437 set = getParameter("XYPAIRS");
1438
1439 if (set == null)
1440 {
1441 }
1442
1443 // if(dataType==XYPAIRS){
1444 // end=set.getData().indexOf("\n",0);
1445 // if (end==-1)throw new JCAMPException("Carriage return after \"=\" is not allowed");
1446 // s1=set.getData().substring(0, end).trim();
1447 // }
1448 // else
1449 if (dataType == XYDATA_X_YY)
1450 {
1451 set = getParameter("XYDATA");
1452
1453 if (set == null)
1454 {
1455 }
1456
1457 end = set.getData().indexOf("\n", 0);
1458
1459 if (end == -1)
1460 {
1461 throw new JCAMPException("Carriage return" +
1462 " after \"=\" is not allowed");
1463 }
1464
1465 s1 = set.getData().substring(0, end).trim();
1466 }
1467
1468 s1 = set.getData().substring(end + 1,
1469 set.getData().indexOf("\n", end + 1));
1470
1471 //s1=s1.trim();
1472 //hier findet keine Unterscheidung zwischen AFFN (ungepackt) oder ASDF (gepackt) statt
1473 //Pseudo-digits or ASDF forms
1474 //ASCII digits 0 1 2 3 4 5 6 7 8 9
1475 //Positive SQZ digits @ A B C D E F G H I
1476 //Negative SQZ digits a b c d e f g h i
1477 //Positive DIF digits % J K L M N O P Q R
1478 //Negative DIF digits j k l m n o p q r
1479 //Positive DUP digits S T U V W X Y Z s
1480 StringTokenizer isAFFN = new StringTokenizer(s1,
1481 "@ABCDEFGHIabcdefghi%JKLMNOPQRjklmnopqrSTUVWXYZs");
1482
1483 try
1484 {
1485 s2 = isAFFN.nextToken();
1486
1487 if (s1.equals(s2))
1488 {
1489 //ist ein Token enthalten, so ist s1 ungleich s2
1490 //OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
1491 //nur AFFN !!!
1492 //OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
1493 //wieviele Datenpunkte sind es denn etwa ?
1494 // StringTokenizer dataValue = new StringTokenizer(s1, " \t");
1495 //ruhig groS waehlen, da sie nachher eh geloescht werden.
1496 // int dataPerLine=100;
1497 //Anzahl der Datenwerte ist NPOINTS/countTokens
1498 //dataPerLine=Math.max(dataValue.countTokens(), 100);
1499 // yData= new Vector((int)nPoints, dataPerLine);
1500 // xData= new Vector((int)nPoints, dataPerLine);
1501 xData = new LinkedList();
1502 yData = new LinkedList();
1503
1504 begin = 0;
1505 begin = set.getData().indexOf("\n", begin);
1506 begin++;
1507
1508 do
1509 {
1510 end = set.getData().indexOf("\n", begin);
1511
1512 if (set.getData().indexOf(";", begin) != -1)
1513 {
1514 end = Math.min(end, set.getData().indexOf(";", begin));
1515