Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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