org.jcamp.parser.CommonSpectrumJCAMPReader.java Source code

Java tutorial

Introduction

Here is the source code for org.jcamp.parser.CommonSpectrumJCAMPReader.java

Source

/**
 * *****************************************************************************
 * Copyright (c) 2015. All rights reserved. This program and the accompanying
 * materials are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * ****************************************************************************
 */
package org.jcamp.parser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;

import org.jcamp.math.IArray2D;
import org.jcamp.spectrum.ArrayData;
import org.jcamp.spectrum.Assignment;
import org.jcamp.spectrum.EquidistantData;
import org.jcamp.spectrum.IAssignmentTarget;
import org.jcamp.spectrum.IDataArray1D;
import org.jcamp.spectrum.IOrderedDataArray1D;
import org.jcamp.spectrum.ISpectrum;
import org.jcamp.spectrum.Multiplicity;
import org.jcamp.spectrum.OrderedArrayData;
import org.jcamp.spectrum.Pattern;
import org.jcamp.spectrum.Peak1D;
import org.jcamp.spectrum.Spectrum;
import org.jcamp.spectrum.Spectrum1D;
import org.jcamp.spectrum.assignments.AtomReference;
import org.jcamp.spectrum.notes.NoteDescriptor;
import org.jcamp.units.CommonUnit;
import org.jcamp.units.Unit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * common implementation of JCAMPReader, implements shorthands for common LDRs.
 *
 * @author Thomas Weber
 * @author <a href="mailto:alexander.kerner@silico-sciences.com">Alexander
 * Kerner</a>
 */
public class CommonSpectrumJCAMPReader implements ISpectrumJCAMPReader {

    private final static Logger log = LoggerFactory.getLogger(CommonSpectrumJCAMPReader.class);

    /**
     * remove any remaining <>.
     *
     * @param content
     * @return
     */
    protected static String clean(String content) {
        return StringUtils.strip(content, "<>");
    }

    /**
     * analyse assignment text for targets.
     *
     * currently assumes SpecInfo convention of a list of integer atom numbers
     *
     * @return IAssignmentTarget[]
     * @param assign String
     */
    protected static IAssignmentTarget[] parseAssignment(String assign) {
        ArrayList<AtomReference> targets = new ArrayList<AtomReference>(5);
        StringTokenizer tokenizer = new StringTokenizer(assign, ",");
        while (tokenizer.hasMoreTokens()) {
            String target = tokenizer.nextToken();
            try {
                int atomNo = Integer.parseInt(target.trim());
                targets.add(new AtomReference(null, atomNo));
            } catch (Exception e) {
            }
        }
        IAssignmentTarget[] assigns = new IAssignmentTarget[targets.size()];
        for (int i = 0; i < targets.size(); i++) {
            assigns[i] = targets.get(i);
        }
        return assigns;
    }

    /**
     * create peak spectrum from peak table. adds all intensities belonging to
     * the same x-position up
     *
     * @param peaks Peak1D[]
     * @return double[][] array of {x, y}
     */
    protected static double[][] peakTableToPeakSpectrum(Peak1D[] peaks) throws JCAMPException {
        int n = peaks.length;
        if (n == 0) {
            throw new JCAMPException("empty peak table");
        }
        Arrays.sort(peaks);
        ArrayList<Double> px = new ArrayList<Double>(n);
        ArrayList<Double> py = new ArrayList<Double>(n);
        double x0 = peaks[0].getPosition()[0];
        double y0 = peaks[0].getHeight();
        for (int i = 1; i < n; i++) {
            double x = peaks[i].getPosition()[0];
            double y = peaks[i].getHeight();
            if (x - x0 > Double.MIN_VALUE) {
                px.add(x0);
                py.add(y0);
                x0 = x;
                y0 = y;
            } else {
                y0 += y;
            }
        }
        px.add(x0);
        py.add(x0);
        double[][] xy = new double[2][px.size()];
        for (int i = 0; i < px.size(); i++) {
            xy[0][i] = px.get(i);
            xy[1][i] = py.get(i);
        }
        return xy;
    }

    /**
     * CommonJCAMPAdapter constructor comment.
     */
    protected CommonSpectrumJCAMPReader() {
        super();
    }

    /**
     * createSpectrum method comment.
     */
    @Override
    public ISpectrum createSpectrum(JCAMPBlock block) throws JCAMPException {
        int expectedSpectrumType = getExpectedSpectrumType();
        if (expectedSpectrumType != -1 && block.getSpectrumType() != expectedSpectrumType) {
            throw new JCAMPException("JCAMP reader adapter missmatch");
        }
        Spectrum spectrum;
        BlockType type = block.getBlockType();
        if (type.equals(BlockType.FULLSPECTRUM)) {
            spectrum = createFS(block);
        } else if (type.equals(BlockType.PEAKTABLE)) {
            spectrum = createPeakTable(block);
        } else if (type.equals(BlockType.ASSIGNMENT)) {
            spectrum = createPeakTable(block);
        } else {
            if (!type.equals(BlockType.STRUCTURE)) {
                java.util.logging.Logger.getLogger(getClass().getName()).log(Level.WARNING, "Illegal block type");
            }
            return null;
        }
        setNotes(block, spectrum);
        return spectrum;
    }

    protected int getExpectedSpectrumType() {
        return -1;
    }

    protected Spectrum createFS(JCAMPBlock block) throws JCAMPException {
        Spectrum1D spectrum;
        Unit xUnit = null;
        try {
            xUnit = getXUnits(block);
        } catch (JCAMPException e) {
        }
        Unit yUnit = null;
        try {
            yUnit = getYUnits(block);
        } catch (JCAMPException e) {
        }
        double xFactor;
        try {
            xFactor = getXFactor(block);
        } catch (JCAMPException e) {
            xFactor = 1.0;
        }
        double yFactor;
        try {
            yFactor = getYFactor(block);
        } catch (JCAMPException e) {
            yFactor = 1.0;
        }
        int nPoints = getNPoints(block);
        if (block.getDataRecord("XYDATA") != null) {
            double firstX = getFirstX(block);
            double lastX = getLastX(block);
            double[] intensities = getXYData(block, firstX, lastX, nPoints, xFactor, yFactor);
            if (intensities.length != nPoints) {
                throw new JCAMPException("incorrect ##NPOINTS= or bad ##XYDATA=");
            }
            IOrderedDataArray1D x = new EquidistantData(firstX, lastX, nPoints, xUnit);
            IDataArray1D y = new ArrayData(intensities, yUnit);
            spectrum = new Spectrum1D(x, y, true);
        } else if (block.getDataRecord("XYPOINTS") != null) {
            double xy[][] = getXYPoints(block, nPoints, xFactor, yFactor);
            IOrderedDataArray1D x = new OrderedArrayData(xy[0], xUnit);
            IDataArray1D y = new ArrayData(xy[1], yUnit);
            spectrum = new Spectrum1D(x, y, false);
        } else {
            throw new JCAMPException("missing data: ##XYDATA= or ##XYPOINTS= required.");
        }
        return spectrum;
    }

    protected Spectrum createPeakTable(JCAMPBlock block) throws JCAMPException {
        Unit xUnit = getXUnits(block), yUnit = getYUnits(block);
        double xFactor = getXFactor(block);
        double yFactor = getYFactor(block);
        int nPoints = getNPoints(block);
        Object[] tables = getPeaktable(block, nPoints, xFactor, yFactor);
        Peak1D[] peaks = (Peak1D[]) tables[0];
        if (nPoints != peaks.length) {
            if (log.isErrorEnabled()) {
                log.error("incorrect ##NPOINTS=: expected " + Integer.toString(nPoints) + " but got "
                        + Integer.toString(peaks.length));
            }
            nPoints = peaks.length;
        }
        double[][] xy = peakTableToPeakSpectrum(peaks);
        IOrderedDataArray1D x = new OrderedArrayData(xy[0], xUnit);
        IDataArray1D y = new ArrayData(xy[1], yUnit);
        Spectrum1D spectrum = new Spectrum1D(x, y, false);
        spectrum.setPeakTable(peaks);
        if (tables.length > 1) {
            spectrum.setPatternTable((Pattern[]) tables[1]);
            if (tables.length > 2) {
                spectrum.setAssignments((Assignment[]) tables[2]);
            }
        }
        return spectrum;
    }

    /**
     * gets ##LASTX= content
     *
     * @return double
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected double getFirstX(JCAMPBlock block) throws JCAMPException {
        JCAMPVariable x = block.getVariable("X");
        if (x == null || x.getFirst() == null) {
            if (log.isErrorEnabled()) {
                log.error("missing first x");
            }
        }
        return x.getFirst();
    }

    /**
     * gets ##FIRSTY= content
     *
     * @return double
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected double getFirstY(JCAMPBlock block) throws JCAMPException {
        JCAMPVariable y = block.getVariable("Y");
        if (y == null || y.getFirst() == null) {
            if (log.isErrorEnabled()) {
                log.error("missing first y");
            }
        }
        return y.getFirst();
    }

    /**
     * gets ##LASTX= content
     *
     * @return double
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected double getLastX(JCAMPBlock block) throws JCAMPException {
        JCAMPVariable x = block.getVariable("X");
        if (x == null || x.getLast() == null) {
            if (log.isErrorEnabled()) {
                log.error("missing last x");
            }
        }
        return x.getLast();
    }

    /**
     * gets ##NPOINTS= content
     *
     * @return double
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected int getNPoints(JCAMPBlock block) throws JCAMPException {
        JCAMPDataRecord ldrNPoints = block.getDataRecord("NPOINTS");
        if (ldrNPoints == null) {
            if (log.isErrorEnabled()) {
                log.error("missing required label ##NPOINTS=");
            }
        }
        String nPoints = ldrNPoints.getContent();
        return Integer.parseInt(nPoints);
    }

    /**
     * gets NTuple Page data.
     *
     * @param block com.creon.chem.jcamp.JCAMPBlock
     * @param page com.creon.chem.jcamp.JCAMPNTuplePage
     * @return IArray2D
     */
    protected IArray2D getNTuplePageData(JCAMPNTuplePage page) throws JCAMPException {
        return page.getXYData();
    }

    /**
     * gets ##DATATABLE= content
     *
     * @return double[]
     * @param block JCAMPBlock
     * @param page JCAMPNTuplePage
     * @param firstX double starting x value (from ##FIRSTX=)
     * @param lastX double ending x value (from ##LASTX=)
     * @param nPoints int number of data points (from ##NPOINTS=)
     * @param xFactor double factor to be applied to x values (from ##XFACTOR=)
     * @param yFactor double factor to be applied to y values (from ##YFACTOR=)
     * @exception JCAMPException The exception description.
     * @deprecated
     */
    @Deprecated
    protected double[] getNTupleXYData(JCAMPBlock block, JCAMPNTuplePage page, double firstX, double lastX,
            int nPoints, double xFactor, double yFactor) throws JCAMPException {

        JCAMPDataRecord ldrXYData = page.getDataRecord("DATATABLE");
        if (ldrXYData == null) {
            throw new JCAMPException("missing required label ##DATATABLE=");
        }
        DataVariableInfo varInfo = new DataVariableInfo(ldrXYData);
        if (!varInfo.isIncremental()) {
            throw new JCAMPException("data form missmatch");
        }

        double[] y = block.getASDFDecoder().decode(ldrXYData, firstX, lastX, xFactor, nPoints);
        int n = y.length;
        double[] yValues = new double[n];
        for (int i = 0; i < n; i++) {
            yValues[i] = yFactor * y[i];
        }
        return yValues;
    }

    /**
     * gets ##DATATABLE= content
     *
     * @return double[]
     * @param block JCAMPBlock
     * @param nPoints int number of data points (from ##NPOINTS=)
     * @exception JCAMPException The exception description.
     * @deprecated
     */
    @Deprecated
    protected double[][] getNTupleXYPoints(JCAMPBlock block, JCAMPNTuplePage page) throws JCAMPException {
        JCAMPDataRecord ldrPeaktable = page.getDataRecord("DATATABLE");
        if (ldrPeaktable == null) {
            throw new JCAMPException("missing required label ##DATATABLE=");
        }
        double[][] xy = new double[2][];
        ArrayList<Double> x = new ArrayList<Double>(20);
        ArrayList<Double> y = new ArrayList<Double>(20);
        AFFNTokenizer tokenizer = new AFFNTokenizer(ldrPeaktable);
        while (tokenizer.hasMoreGroups()) {
            AFFNGroup group = tokenizer.nextGroup();
            x.add(new Double(group.getValue(0)));
            y.add(new Double(group.getValue(1)));
        }
        xy[0] = new double[x.size()];
        xy[1] = new double[y.size()];
        for (int i = 0; i < x.size(); i++) {
            xy[0][i] = x.get(i);
            xy[1][i] = y.get(i);
        }
        return xy;
    }

    /**
     * gets ##ORIGIN= content
     *
     * @return String
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected String getOrigin(JCAMPBlock block) throws JCAMPException {
        JCAMPDataRecord ldrOrigin = block.getDataRecord("ORIGIN");
        if (ldrOrigin == null) {
            if (log.isWarnEnabled()) {
                log.warn("missing required label ##ORIGIN=");
            }
            return "UNKNOWN ORIGIN";
        }
        return ldrOrigin.getContent();
    }

    /**
     * gets ##OWNER= content
     *
     * @return String
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected String getOwner(JCAMPBlock block) throws JCAMPException {
        JCAMPDataRecord ldrOwner = block.getDataRecord("OWNER");
        if (ldrOwner == null) {
            if (log.isWarnEnabled()) {
                log.warn("missing required label ##OWNER=");
            }
            return "COPYRIGHT UNKNOWN";
        }
        return ldrOwner.getContent();
    }

    /**
     * gets ##PEAKTABLE= or ##PEAKASSIGNMENTS= content
     *
     * @return Object[] array of Peak[], Pattern[], Assignment[]
     * @param block JCAMPBlock
     * @param nPoints int number of data points (from ##NPOINTS=)
     * @param xFactor double factor to be applied to x values (from ##XFACTOR=)
     * @param yFactor double factor to be applied to y values (from ##YFACTOR=)
     * @exception JCAMPException The exception description.
     */
    protected Object[] getPeaktable(JCAMPBlock block, int nPoints, double xFactor, double yFactor)
            throws JCAMPException {
        JCAMPDataRecord ldrPeaktable = block.getDataRecord("PEAKTABLE");
        if (ldrPeaktable == null) {
            ldrPeaktable = block.getDataRecord("PEAKASSIGNMENTS");
        }
        if (ldrPeaktable == null) {
            ldrPeaktable = block.getDataRecord("XYPOINTS");
        }
        if (ldrPeaktable == null) {
            ldrPeaktable = block.getDataRecord("XYDATA");
        }
        if (ldrPeaktable == null) {
            throw new JCAMPException("missing peak table");
        }
        DatatableTokenizer tokenizer = new DatatableTokenizer(ldrPeaktable);
        if (tokenizer.getType().equals(DataType.XY)) {
            int i = 0;
            Peak1D[] peaks = new Peak1D[nPoints];
            while (tokenizer.hasMoreGroups()) {
                DataGroup group = tokenizer.nextGroup();
                double x = xFactor * ((Double) group.getValue(0));
                double y = yFactor * ((Double) group.getValue(1));
                peaks[i] = new Peak1D(x, y);
                i++;
            }
            return new Object[] { peaks };
        } else if (tokenizer.getType().equals(DataType.XYW)) {
            int i = 0;
            Peak1D[] peaks = new Peak1D[nPoints];
            while (tokenizer.hasMoreGroups()) {

                DataGroup group = tokenizer.nextGroup();
                double x = xFactor * ((Double) group.getValue(0));
                double y = yFactor * ((Double) group.getValue(1));
                double w = ((Double) group.getValue(2));
                peaks[i] = new Peak1D(x, y, w);
                i++;
            }
            return new Object[] { peaks };
        } else if (tokenizer.getType().equals(DataType.XYM)) {
            int i = 0;
            Peak1D[] peaks = new Peak1D[nPoints];
            Pattern[] pattern = new Pattern[nPoints];
            while (tokenizer.hasMoreGroups()) {
                DataGroup group = tokenizer.nextGroup();
                double x = xFactor * ((Double) group.getValue(0));
                double y = yFactor * ((Double) group.getValue(1));
                Multiplicity m = ((Multiplicity) group.getValue(2));
                peaks[i] = new Peak1D(x, y);
                pattern[i] = new Pattern(x, m);
                i++;
            }
            return new Object[] { peaks, pattern };
        } else if (tokenizer.getType().equals(DataType.XYA)) {
            int i = 0;
            Peak1D[] peaks = new Peak1D[nPoints];
            Pattern[] pattern = new Pattern[nPoints];
            Assignment[] assigns = new Assignment[nPoints];
            while (tokenizer.hasMoreGroups()) {
                DataGroup group = tokenizer.nextGroup();
                double x = xFactor * ((Double) group.getValue(0));
                double y = yFactor * ((Double) group.getValue(1));
                String a = (String) group.getValue(2);
                peaks[i] = new Peak1D(x, y);
                IAssignmentTarget[] targets = parseAssignment(a);
                pattern[i] = new Pattern(x, Multiplicity.UNKNOWN, new Peak1D[] { peaks[i] });
                assigns[i] = new Assignment(pattern[i], targets);
                i++;
            }
            return new Object[] { peaks, pattern, assigns };
        } else if (tokenizer.getType().equals(DataType.XYMA)) {
            int i = 0;
            Peak1D[] peaks = new Peak1D[nPoints];
            Pattern[] pattern = new Pattern[nPoints];
            Assignment[] assigns = new Assignment[nPoints];
            while (tokenizer.hasMoreGroups()) {
                DataGroup group = tokenizer.nextGroup();
                double x = xFactor * ((Double) group.getValue(0));
                double y = yFactor * ((Double) group.getValue(1));
                Multiplicity m = ((Multiplicity) group.getValue(2));
                String a = (String) group.getValue(3);
                IAssignmentTarget[] targets = parseAssignment(a);
                peaks[i] = new Peak1D(x, y);
                pattern[i] = new Pattern(x, m, new Peak1D[] { peaks[i] });
                assigns[i] = new Assignment(pattern[i], targets);
                i++;
            }
            return new Object[] { peaks, pattern, assigns };
        } else if (tokenizer.getType().equals(DataType.XYWA)) {
            int i = 0;
            Peak1D[] peaks = new Peak1D[nPoints];
            Pattern[] pattern = new Pattern[nPoints];
            Assignment[] assigns = new Assignment[nPoints];
            while (tokenizer.hasMoreGroups()) {
                DataGroup group = tokenizer.nextGroup();
                double x = xFactor * ((Double) group.getValue(0));
                double y = yFactor * ((Double) group.getValue(1));
                String a = (String) group.getValue(3);
                IAssignmentTarget[] targets = parseAssignment(a);
                peaks[i] = new Peak1D(x, y);
                pattern[i] = new Pattern(x, Multiplicity.UNKNOWN, new Peak1D[] { peaks[i] });
                assigns[i] = new Assignment(pattern[i], targets);
                i++;
            }
            return new Object[] { peaks, pattern, assigns };
        } else if (tokenizer.getType().equals(DataType.XYMWA)) {
            int i = 0;
            Peak1D[] peaks = new Peak1D[nPoints];
            Pattern[] pattern = new Pattern[nPoints];
            Assignment[] assigns = new Assignment[nPoints];
            while (tokenizer.hasMoreGroups() && i < nPoints) {
                DataGroup group = tokenizer.nextGroup();
                double x = xFactor * ((Double) group.getValue(0));
                double y = yFactor * ((Double) group.getValue(1));
                Multiplicity m = ((Multiplicity) group.getValue(2));
                double w = ((Double) group.getValue(3));
                String a = (String) group.getValue(4);
                IAssignmentTarget[] targets = parseAssignment(a);
                peaks[i] = new Peak1D(x, y, w);
                pattern[i] = new Pattern(x, m, new Peak1D[] { peaks[i] });
                assigns[i] = new Assignment(pattern[i], targets);
                i++;
            }
            return new Object[] { peaks, pattern, assigns };
        } else {
            throw new JCAMPException("unknown peaktable");
        }
    }

    /**
     * gets ##TITLE= content
     *
     * @return String
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected String getTitle(JCAMPBlock block) throws JCAMPException {
        JCAMPDataRecord ldrTitle = block.getDataRecord("TITLE");
        if (ldrTitle == null) { // should never happen here
            throw new JCAMPException("missing required label ##TITLE=");
        }
        return ldrTitle.getContent();
    }

    /**
     * gets ##XFACTOR= content
     *
     * @return double
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected double getXFactor(JCAMPBlock block) throws JCAMPException {
        JCAMPVariable x = block.getVariable("X");
        if (x == null || x.getFactor() == null) {
            if (log.isWarnEnabled()) {
                log.warn("missing x factor, assuming 1.0");
            }
            return 1.0;
        }
        return x.getFactor();
    }

    /**
     * gets ##XUNITS= content
     *
     * @return Unit
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected Unit getXUnits(JCAMPBlock block) throws JCAMPException {
        JCAMPVariable x = block.getVariable("X");
        if (x == null || x.getUnit() == null) {
            if (log.isWarnEnabled()) {
                log.warn("missing x unit");
            }
            return CommonUnit.generic;
        }
        return x.getUnit();
    }

    /**
     * gets ##XYDATA= content fast implementation assumes ASCII encoded text
     * (required by JCAMP standard)
     *
     * @return double[]
     * @param block JCAMPBlock
     * @param firstX double starting x value (from ##FIRSTX=)
     * @param lastX double ending x value (from ##LASTX=)
     * @param nPoints int number of data points (from ##NPOINTS=)
     * @param xFactor double factor to be applied to x values (from ##XFACTOR=)
     * @param yFactor double factor to be applied to y values (from ##YFACTOR=)
     * @exception JCAMPException The exception description.
     */
    protected double[] getXYData(JCAMPBlock block, double firstX, double lastX, int nPoints, double xFactor,
            double yFactor) throws JCAMPException {
        JCAMPDataRecord ldrXYData = block.getDataRecord("XYDATA");
        if (ldrXYData == null) {
            throw new JCAMPException("missing required label ##XYDATA=");

        }
        double[] y = block.getASDFDecoder().decode(ldrXYData, firstX, lastX, xFactor, nPoints);
        int n = y.length;
        double[] yValues = new double[n];
        for (int i = 0; i < n; i++) {
            yValues[i] = yFactor * y[i];
        }
        return yValues;

    }

    /**
     * gets ##XYPOINTS= content
     *
     * @return double[]
     * @param block JCAMPBlock
     * @param nPoints int number of data points (from ##NPOINTS=)
     * @exception JCAMPException The exception description.
     */
    protected double[][] getXYPoints(JCAMPBlock block, int nPoints, double xFactor, double yFactor)
            throws JCAMPException {
        class XYPair implements Comparable<XYPair> {

            public double x;
            public double y;

            public XYPair(double x, double y) {
                this.x = x;
                this.y = y;
            }

            @Override
            public int compareTo(XYPair o) {
                XYPair p = o;
                if (this.x < p.x) {
                    return -1;
                }
                if (this.x > p.x) {
                    return 1;
                }
                return 0;
            }
        }
        ;
        JCAMPDataRecord ldrXYPoints = block.getDataRecord("XYPOINTS");
        if (ldrXYPoints == null) {
            throw new JCAMPException("missing required label ##XYPOINTS=");

        }

        int i = 0;
        AFFNTokenizer tokenizer = new AFFNTokenizer(ldrXYPoints);
        TreeSet<XYPair> data = new TreeSet<XYPair>();
        while (tokenizer.hasMoreGroups()) {
            AFFNGroup group = tokenizer.nextGroup();
            data.add(new XYPair(xFactor * group.getValue(0), yFactor * group.getValue(1)));
        }
        if (data.size() != nPoints) {
            if (log.isErrorEnabled()) {
                log.error("bad ##NPOINTS= or duplicate X values");
            }
        }
        double[][] xy = new double[2][data.size()];
        for (Iterator<XYPair> it = data.iterator(); it.hasNext();) {
            XYPair p = it.next();
            xy[0][i] = p.x;
            xy[1][i] = p.y;
        }
        return xy;
    }

    /**
     * gets ##XFACTOR= content
     *
     * @return double
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected double getYFactor(JCAMPBlock block) throws JCAMPException {
        JCAMPVariable y = block.getVariable("Y");
        if (y == null || y.getFactor() == null) {
            if (log.isWarnEnabled()) {
                log.warn("missing y factor, assuming 1.0");
            }
            return 1.0;
        }
        return y.getFactor();
    }

    /**
     * gets ##YUNITS= content
     *
     * @return Unit
     * @param block JCAMPBlock
     * @exception JCAMPException The exception description.
     */
    protected Unit getYUnits(JCAMPBlock block) throws JCAMPException {
        JCAMPVariable y = block.getVariable("Y");
        if (y == null || y.getUnit() == null) {
            if (log.isWarnEnabled()) {
                log.warn("missing y unit");
            }
            return CommonUnit.generic;
        }
        return y.getUnit();
    }

    /**
     * set spectrum note
     *
     * @param block JCAMPBlock
     * @param ldr JCAMPDataRecord
     * @param spectrum com.creon.chem.spectrum.Spectrum
     */
    protected void setNote(JCAMPBlock block, JCAMPDataRecord ldr, Spectrum spectrum) throws JCAMPException {
        String key = ldr.getKey();
        if (key.length() == 0) // comment
        {
            return;
        }

        NoteDescriptor descr = NoteDescriptorFactory.getInstance().findByJCAMPKey(key);
        if (descr.equals(NoteDescriptor.IGNORE)) {
            return;
        }

        Object content;
        try {
            content = descr.getNoteContentParser().parseContent(ldr.getContent(), descr.getNoteContentClass());
            spectrum.setNote(descr, content);
        } catch (org.jcamp.spectrum.notes.BadContentException ex) {
            StringBuilder msg = new StringBuilder("bad ").append(descr.getName()).append(" note:\n")
                    .append(ex.getMessage());
            if (log.isWarnEnabled()) {
                log.warn(msg.toString());
            }
        }
    }

    /**
     * after creation of spectrum, add other notes
     *
     * @param block com.creon.chem.jcamp.JCAMPBlock
     * @param spectrum com.creon.chem.spectrum.Spectrum
     */
    protected void setNotes(JCAMPBlock block, Spectrum spectrum) throws JCAMPException {
        String title = getTitle(block);
        spectrum.setTitle(title);
        /*
         * String owner = getOwner(block); spectrum.setOwner(owner); String
         * origin = getOrigin(block); spectrum.setOrigin(origin);
         */
        int n = block.numDataRecords();
        for (int i = 0; i < n; i++) {
            JCAMPDataRecord ldr = block.getDataRecord(i);
            setNote(block, ldr, spectrum);
        }
    }
}