changeset 8976:e541938dd3ab

Range data handled consistently as BigDecimal to minimize the fractional part of a and b
author mschaefer
date Tue, 03 Apr 2018 10:43:53 +0200
parents a0a0a7f912ab
children bf8a9df86f32
files backend/src/main/java/org/dive4elements/river/importer/ImportRange.java backend/src/main/java/org/dive4elements/river/importer/parsers/WstParser.java
diffstat 2 files changed, 164 insertions(+), 173 deletions(-) [+]
line wrap: on
line diff
--- a/backend/src/main/java/org/dive4elements/river/importer/ImportRange.java	Tue Apr 03 10:40:57 2018 +0200
+++ b/backend/src/main/java/org/dive4elements/river/importer/ImportRange.java	Tue Apr 03 10:43:53 2018 +0200
@@ -8,12 +8,11 @@
 
 package org.dive4elements.river.importer;
 
-import org.dive4elements.river.model.Range;
-import org.dive4elements.river.model.River;
-
 import java.math.BigDecimal;
 
 import org.apache.log4j.Logger;
+import org.dive4elements.river.model.Range;
+import org.dive4elements.river.model.River;
 
 /** A range that is about to be imported. */
 public class ImportRange
@@ -30,7 +29,7 @@
     public ImportRange() {
     }
 
-    public ImportRange(BigDecimal a) {
+    public ImportRange(final BigDecimal a) {
         this.a = a;
         this.b = null;
     }
@@ -55,14 +54,14 @@
         }
         else {
             if (a.compareTo(b) > 0) {
-                BigDecimal t = a; a = b; b = t;
+                final BigDecimal t = a; a = b; b = t;
             }
             this.a = a;
             this.b = b;
         }
     }
 
-    private static final int compare(BigDecimal a, BigDecimal b) {
+    private static final int compare(final BigDecimal a, final BigDecimal b) {
         if (a == null && b == null) {
             return 0;
         }
@@ -75,41 +74,54 @@
         return a.compareTo(b);
     }
 
-    public int compareTo(ImportRange other) {
-        int cmp = compare(a, other.a);
+    @Override
+    public int compareTo(final ImportRange other) {
+        final int cmp = compare(this.a, other.a);
         if (cmp != 0) return cmp;
-        return compare(b, other.b);
+        return compare(this.b, other.b);
     }
 
     public BigDecimal getA() {
-        return a;
+        return this.a;
     }
 
-    public void setA(BigDecimal a) {
-        if (this.b != null && a.compareTo(b) >= 0) {
+    public void setA(final BigDecimal a) {
+        if (this.b != null && a.compareTo(this.b) >= 0) {
             throw new IllegalArgumentException(
-                "a (" + a + ") must be smaller than b (" + b + ").");
+                    "a (" + a + ") must be smaller than b (" + this.b + ").");
         }
         this.a = a;
     }
 
     public BigDecimal getB() {
-        return b;
+        return this.b;
     }
 
-    public void setB(BigDecimal b) {
-        if (b != null && b.compareTo(a) <= 0) {
+    public void setB(final BigDecimal b) {
+        if (b != null && b.compareTo(this.a) <= 0) {
             throw new IllegalArgumentException(
-                "b (" + b + ") must be greater than a (" + a + ") or null.");
+                    "b (" + b + ") must be greater than a (" + this.a + ") or null.");
         }
         this.b = b;
     }
 
-    public Range getPeer(River river) {
-        if (peer == null) {
-            peer = ImporterSession.getInstance().getRange(river, a, b);
+    /**
+     * Difference of a and b
+     *
+     * @return b - a, or NaN if a or b null
+     */
+    public double difference() {
+        if ((this.a != null) && (this.b != null))
+            return this.b.subtract(this.a).doubleValue();
+        else
+            return Double.NaN;
+    }
+
+    public Range getPeer(final River river) {
+        if (this.peer == null) {
+            this.peer = ImporterSession.getInstance().getRange(river, this.a, this.b);
         }
-        return peer;
+        return this.peer;
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/backend/src/main/java/org/dive4elements/river/importer/parsers/WstParser.java	Tue Apr 03 10:40:57 2018 +0200
+++ b/backend/src/main/java/org/dive4elements/river/importer/parsers/WstParser.java	Tue Apr 03 10:43:53 2018 +0200
@@ -8,34 +8,28 @@
 
 package org.dive4elements.river.importer.parsers;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Arrays;
-
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.LineNumberReader;
 import java.io.InputStreamReader;
-import java.io.FileInputStream;
-
+import java.io.LineNumberReader;
+import java.math.BigDecimal;
 import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.log4j.Logger;
-
-import org.dive4elements.river.backend.utils.StringUtil;
 import org.dive4elements.river.backend.utils.DateGuesser;
-
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-import java.math.BigDecimal;
-
-import org.dive4elements.river.importer.ImportWstQRange;
-import org.dive4elements.river.importer.ImportWstColumn;
+import org.dive4elements.river.backend.utils.StringUtil;
+import org.dive4elements.river.importer.ImportRange;
 import org.dive4elements.river.importer.ImportTimeInterval;
-import org.dive4elements.river.importer.ImportRange;
 import org.dive4elements.river.importer.ImportUnit;
 import org.dive4elements.river.importer.ImportWst;
+import org.dive4elements.river.importer.ImportWstColumn;
+import org.dive4elements.river.importer.ImportWstQRange;
 
 public class WstParser
 {
@@ -47,43 +41,41 @@
     public static final String COLUMN_DATUM      = "column-datum";
 
     public static final BigDecimal UNDEFINED_ZERO =
-        new BigDecimal(0.0);
+            new BigDecimal(0.0);
     public static final BigDecimal MIN_RANGE =
-        new BigDecimal(-Double.MAX_VALUE);
+            new BigDecimal(-Double.MAX_VALUE);
     public static final BigDecimal MAX_RANGE =
-        new BigDecimal(Double.MAX_VALUE);
+            new BigDecimal(Double.MAX_VALUE);
 
     public static final String ENCODING = "ISO-8859-1";
 
     public static final Pattern UNIT_COMMENT =
-        Pattern.compile("\\*\\s*[kK][mM]\\s+(.+)");
+            Pattern.compile("\\*\\s*[kK][mM]\\s+(.+)");
 
     public static final Pattern UNIT =
-        Pattern.compile("[^\\[]*\\[([^]]+)\\].*");
+            Pattern.compile("[^\\[]*\\[([^]]+)\\].*");
 
     public static final Pattern YEAR_INTERVAL =
-        Pattern.compile("(\\d{4})\\s*[-/]\\s*(\\d{4})");
+            Pattern.compile("(\\d{4})\\s*[-/]\\s*(\\d{4})");
 
-    public static final double INTERVAL_GAP = 0.00001d;
+    public static final BigDecimal INTERVAL_GAP = new BigDecimal("0.00001");
 
     protected ImportWst wst;
 
     protected ImportRange lastRange;
-    protected Double lastA;
-    protected Double lastB;
 
     public WstParser() {
     }
 
-    public WstParser(ImportWst wst) {
+    public WstParser(final ImportWst wst) {
         this.wst = wst;
     }
 
     public ImportWst getWst() {
-        return wst;
+        return this.wst;
     }
 
-    public void setWst(ImportWst wst) {
+    public void setWst(final ImportWst wst) {
         this.wst = wst;
     }
 
@@ -91,46 +83,46 @@
         public ParseException() {
         }
 
-        public ParseException(String msg) {
+        public ParseException(final String msg) {
             super(msg);
         }
     } // class ParseException
 
     /** Returns a new ImportTimeInterval with a date guessed from string. */
-    public static ImportTimeInterval guessDate(String string) {
+    public static ImportTimeInterval guessDate(final String string) {
         try {
-            Matcher m = YEAR_INTERVAL.matcher(string);
+            final Matcher m = YEAR_INTERVAL.matcher(string);
             if (m.matches()) {
                 return new ImportTimeInterval(
-                    DateGuesser.guessDate(m.group(1)),
-                    DateGuesser.guessDate(m.group(2)));
+                        DateGuesser.guessDate(m.group(1)),
+                        DateGuesser.guessDate(m.group(2)));
             }
 
             return new ImportTimeInterval(
-                DateGuesser.guessDate(string));
+                    DateGuesser.guessDate(string));
         }
-        catch (IllegalArgumentException iae) {
+        catch (final IllegalArgumentException iae) {
             log.warn("WST: String '" + string +
-                     "' could not be interpreted as valid timestamp");
+                    "' could not be interpreted as valid timestamp");
         }
         return null;
     }
 
-    public void parse(File file) throws IOException, ParseException {
+    public void parse(final File file) throws IOException, ParseException {
 
         log.info("Parsing WST file '" + file + "'");
 
-        if (wst == null) {
-            wst = new ImportWst(file.getName());
+        if (this.wst == null) {
+            this.wst = new ImportWst(file.getName());
         }
         else {
-            wst.setDescription(file.getName());
+            this.wst.setDescription(file.getName());
         }
 
-        LineNumberReader in =
-            new LineNumberReader(
-            new InputStreamReader(
-            new FileInputStream(file), ENCODING));
+        final LineNumberReader in =
+                new LineNumberReader(
+                        new InputStreamReader(
+                                new FileInputStream(file), ENCODING));
         try {
             String input;
             boolean first = true;
@@ -158,7 +150,7 @@
             String einheit = "m ΓΌ. unbekannte Referenz";
             boolean unitFound = false;
 
-            HashSet<BigDecimal> kms = new HashSet<BigDecimal>();
+            final HashSet<BigDecimal> kms = new HashSet<>();
 
             while ((input = in.readLine()) != null) {
                 String line = input;
@@ -170,13 +162,13 @@
                         columnCount = Integer.parseInt(line);
                         if (columnCount <= 0) {
                             throw new NumberFormatException(
-                                "number of columns <= 0");
+                                    "number of columns <= 0");
                         }
                         log.debug("Number of columns: " + columnCount);
-                        wst.setNumberColumns(columnCount);
+                        this.wst.setNumberColumns(columnCount);
                         lsBezeichner = new String[columnCount];
                     }
-                    catch (NumberFormatException nfe) {
+                    catch (final NumberFormatException nfe) {
                         log.warn("WST: invalid number.", nfe);
                         continue;
                     }
@@ -188,15 +180,15 @@
 
                 // handle Q-lines
                 if (line.startsWith("*\u001f")) {
-                    BigDecimal [] data = parseLineAsDouble(
-                        line, columnCount, false, true);
+                    final BigDecimal [] data = parseLineAsDouble(
+                            line, columnCount, false, true);
 
                     if (aktAbfluesse != null) {
                         // add Q-ranges obtained from previous lines
                         if (kmHist1 != null && kmHist2 != null
-                        && kmHist1.compareTo(kmHist2) < 0) {
+                                && kmHist1.compareTo(kmHist2) < 0) {
                             // stations descending in file
-                            BigDecimal t = minKm; minKm = maxKm; maxKm = t;
+                            final BigDecimal t = minKm; minKm = maxKm; maxKm = t;
                         }
                         addInterval(minKm, maxKm, aktAbfluesse);
                         minKm = MAX_RANGE;
@@ -216,7 +208,7 @@
                     // remember Q-values from first Q-line
                     // for header generation
                     if (firstAbfluesse == null) {
-                        firstAbfluesse = (BigDecimal [])aktAbfluesse.clone();
+                        firstAbfluesse = aktAbfluesse.clone();
                     }
                     continue;
                 }
@@ -231,7 +223,7 @@
 
                     if (spezial.startsWith(COLUMN_BEZ_TEXT)) {
                         spezial = spezial.substring(
-                            COLUMN_BEZ_TEXT.length()).trim();
+                                COLUMN_BEZ_TEXT.length()).trim();
                         if (spezial.length() == 0) {
                             continue;
                         }
@@ -239,13 +231,13 @@
                     }
                     else if (spezial.startsWith(COLUMN_BEZ_BREITE)) {
                         spezial = spezial.substring(
-                            COLUMN_BEZ_BREITE.length()).trim();
+                                COLUMN_BEZ_BREITE.length()).trim();
 
                         if (spezial.length() == 0) {
                             continue;
                         }
 
-                        String[] split = spezial.split("\\s+");
+                        final String[] split = spezial.split("\\s+");
 
                         colNaWidths = new int[split.length];
                         for (int i=0; i < split.length; i++) {
@@ -254,7 +246,7 @@
                     }
                     else if (spezial.startsWith(COLUMN_QUELLE)) {
                         spezial = spezial.substring(
-                            COLUMN_QUELLE.length()).trim();
+                                COLUMN_QUELLE.length()).trim();
                         if (spezial.length() == 0) {
                             continue;
                         }
@@ -263,7 +255,7 @@
                     }
                     else if (spezial.startsWith(COLUMN_DATUM)) {
                         spezial = spezial.substring(
-                            COLUMN_DATUM.length()).trim();
+                                COLUMN_DATUM.length()).trim();
                         if (spezial.length() == 0) {
                             continue;
                         }
@@ -283,7 +275,7 @@
                         log.debug("unit comment found");
                         // XXX: This hack is needed because desktop
                         // FLYS is broken figuring out the unit
-                        String [] units = m.group(1).split("\\s{2,}");
+                        final String [] units = m.group(1).split("\\s{2,}");
                         m = UNIT.matcher(units[0]);
                         einheit = m.matches() ? m.group(1) : units[0];
                         log.debug("unit: " + einheit);
@@ -296,69 +288,69 @@
                 if (firstAbfluesse != null) {
                     if (!columnHeaderChecked) {
                         int unknownCount = 0;
-                        HashSet<String> uniqueColumnNames =
-                            new HashSet<String>();
+                        final HashSet<String> uniqueColumnNames =
+                                new HashSet<>();
                         if (langBezeichner != null) {
                             // use column name from '*!column-bez-text'-line
                             lsBezeichner = StringUtil.fitArray(
-                                langBezeichner, lsBezeichner);
+                                    langBezeichner, lsBezeichner);
                         }
                         for (int i = 0; i < lsBezeichner.length; ++i) {
                             if (lsBezeichner[i] == null
-                            || lsBezeichner[i].length() == 0) {
+                                    || lsBezeichner[i].length() == 0) {
                                 // generate alternative column names
-                                double q = firstAbfluesse.length > i ?
-                                    firstAbfluesse[i].doubleValue() : 0d;
-                                if (q < 0.001) {
-                                    lsBezeichner[i] =
-                                        "<unbekannt #" + unknownCount + ">";
-                                    ++unknownCount;
-                                }
-                                else {
-                                    lsBezeichner[i] = "Q="+format(q);
-                                }
+                                final double q = firstAbfluesse.length > i ?
+                                        firstAbfluesse[i].doubleValue() : 0d;
+                                        if (q < 0.001) {
+                                            lsBezeichner[i] =
+                                                    "<unbekannt #" + unknownCount + ">";
+                                            ++unknownCount;
+                                        }
+                                        else {
+                                            lsBezeichner[i] = "Q="+format(q);
+                                        }
                             }
                             String candidate = lsBezeichner[i];
                             int collision = 1;
                             while (!uniqueColumnNames.add(candidate)) {
                                 candidate = lsBezeichner[i] +
-                                    " (" + collision + ")";
+                                        " (" + collision + ")";
                                 ++collision;
                             }
-                            ImportWstColumn iwc = wst.getColumn(i);
+                            final ImportWstColumn iwc = this.wst.getColumn(i);
                             iwc.setName(candidate);
                             if (quellen != null && i < quellen.length) {
                                 iwc.setSource(quellen[i]);
                             }
-                            String potentialDate =
-                                daten != null && i < daten.length
-                                ? daten[i]
-                                : candidate;
-                            iwc.setTimeInterval(guessDate(potentialDate));
+                            final String potentialDate =
+                                    daten != null && i < daten.length
+                                    ? daten[i]
+                                            : candidate;
+                                    iwc.setTimeInterval(guessDate(potentialDate));
                         }
                         columnHeaderChecked = true;
                     }
 
-                    BigDecimal [] data = parseLineAsDouble(
-                        line, columnCount, true, false);
+                    final BigDecimal [] data = parseLineAsDouble(
+                            line, columnCount, true, false);
 
-                    BigDecimal kaem = data[0];
+                    final BigDecimal kaem = data[0];
 
                     if (!kms.add(kaem)) {
                         log.warn(
-                            "WST: km " + kaem +
-                            " (line " + in.getLineNumber() +
-                            ") found more than once. -> ignored");
+                                "WST: km " + kaem +
+                                " (line " + in.getLineNumber() +
+                                ") found more than once. -> ignored");
                         continue;
                     }
 
                     // check consistence of station ordering in file
                     if (kmHist2 != null &&
-                        kmHist2.compareTo(kmHist1) != kmHist1.compareTo(kaem)
-                    ) {
+                            kmHist2.compareTo(kmHist1) != kmHist1.compareTo(kaem)
+                            ) {
                         throw new ParseException("WST: Stations in " + file +
-                            " near line " + in.getLineNumber() +
-                            " not ordered. File rejected.");
+                                " near line " + in.getLineNumber() +
+                                " not ordered. File rejected.");
                     }
 
                     // remember stations in two previous lines
@@ -385,25 +377,25 @@
                     }
                     else if (colNaWidths != null) {
                         for (int j = 0, i = 0, N = input.length();
-                             j < colNaWidths.length && i < N;
-                             i += colNaWidths[j++]
-                        ) {
+                                j < colNaWidths.length && i < N;
+                                i += colNaWidths[j++]
+                                ) {
                             lsBezeichner[j] = input.substring(
-                                i, i+colNaWidths[j]).trim();
+                                    i, i+colNaWidths[j]).trim();
                         }
                     }
                     else { // fetch column names from non-comment header line
-                           // (above first Qs)
+                        // (above first Qs)
                         // first column begins at position 8 in line
                         for (int i = 8, col = 0; i < input.length(); i += 9) {
                             // one column header is 9 chars wide
                             // but the last one may be shorter
                             if (col < lsBezeichner.length) {
                                 lsBezeichner[col++] =
-                                    input.substring(
-                                        i,
-                                        Math.min(i + 9, input.length())
-                                    ).trim();
+                                        input.substring(
+                                                i,
+                                                Math.min(i + 9, input.length())
+                                                ).trim();
                             }
                             if (col == lsBezeichner.length) {
                                 break;
@@ -417,16 +409,16 @@
             if (!unitFound) {
                 log.warn("no unit and height reference found. Using default.");
             }
-            wst.setUnit(new ImportUnit(einheit));
+            this.wst.setUnit(new ImportUnit(einheit));
 
             // add Q-ranges obtained from previous lines
             // in case there was no further Q-line
             // but only if there were values following the last Q-line
             if (minKm != MAX_RANGE && maxKm != MIN_RANGE) {
                 if (kmHist1 != null && kmHist2 != null
-                && kmHist1.compareTo(kmHist2) < 0) {
+                        && kmHist1.compareTo(kmHist2) < 0) {
                     // stations descending in file
-                    BigDecimal t = minKm; minKm = maxKm; maxKm = t;
+                    final BigDecimal t = minKm; minKm = maxKm; maxKm = t;
                 }
                 addInterval(minKm, maxKm, aktAbfluesse);
             }
@@ -436,9 +428,9 @@
         }
     }
 
-    protected void addValue(BigDecimal km, BigDecimal w, int index) {
+    protected void addValue(final BigDecimal km, final BigDecimal w, final int index) {
         if (w != null) {
-            ImportWstColumn column = wst.getColumn(index);
+            final ImportWstColumn column = this.wst.getColumn(index);
             column.addColumnValue(km, w);
         }
     }
@@ -446,21 +438,21 @@
     private static final NumberFormat NF = getNumberFormat();
 
     private static final NumberFormat getNumberFormat() {
-        NumberFormat nf = NumberFormat.getInstance();
+        final NumberFormat nf = NumberFormat.getInstance();
         nf.setMinimumFractionDigits(2);
         nf.setMaximumFractionDigits(2);
         return nf;
     }
 
-    protected static String format(double value) {
+    protected static String format(final double value) {
         return NF.format(value);
     }
 
     protected void addInterval(
-        BigDecimal    from,
-        BigDecimal    to,
-        BigDecimal [] values
-    ) {
+            final BigDecimal from,
+            final BigDecimal to,
+            final BigDecimal[] values
+            ) {
         log.debug("addInterval: " + from + " " + to);
 
         if (values == null || from == MAX_RANGE || from == MIN_RANGE) {
@@ -468,56 +460,43 @@
         }
 
         // expand single-line i.e. 0-lenght Q-range to minimal length
+        final ImportRange range = new ImportRange(from, to);
         if (from == to) {
-            if (lastRange != null && lastA > lastB) {
-                to = new BigDecimal(from.doubleValue() - INTERVAL_GAP);
-            }
-            else {
-                to = new BigDecimal(from.doubleValue() + INTERVAL_GAP);
-            }
+            if ((this.lastRange != null) && (this.lastRange.difference() < 0.0))
+                range.setB(from.subtract(INTERVAL_GAP));
+            else
+                range.setB(from.add(INTERVAL_GAP));
         }
 
-        ImportRange range = new ImportRange(from, to);
-
         // little workaround to make the q ranges tightly fit.
         // Leave a very small gap to ensure that the range queries
         // still work.
 
-        if (lastRange != null) {
-            double a2 = range.getA().doubleValue();
-            double b2 = range.getB().doubleValue();
-
-            if (lastA < lastB) {
-                lastRange.setB(new BigDecimal(a2 - INTERVAL_GAP));
-            }
-            else { // lastA >= lastB
-                lastRange.setA(new BigDecimal(b2 + INTERVAL_GAP));
-            }
+        if (this.lastRange != null) {
+            if (this.lastRange.difference() > 0.0)
+                this.lastRange.setB(range.getA().subtract(INTERVAL_GAP));
+            else // lastA >= lastB
+                this.lastRange.setA(range.getB().add(INTERVAL_GAP));
         }
 
-        for (int i = 0; i < values.length; ++i) {
-            ImportWstColumn column = wst.getColumn(i);
-            ImportWstQRange wstQRange = new ImportWstQRange(range, values[i]);
-            column.addColumnQRange(wstQRange);
-        }
+        for (int i = 0; i < values.length; ++i)
+            this.wst.getColumn(i).addColumnQRange(new ImportWstQRange(range, values[i]));
 
-        lastA = from.doubleValue();
-        lastB = to.doubleValue();
-        lastRange = range;
+        this.lastRange = range;
     }
 
     private static final BigDecimal [] parseLineAsDouble(
-        String  line,
-        int     count,
-        boolean bStation,
-        boolean bParseEmptyAsZero
-    ) throws ParseException {
-        String [] tokens = parseLine(line, count, bStation);
+            final String  line,
+            final int     count,
+            final boolean bStation,
+            final boolean bParseEmptyAsZero
+            ) throws ParseException {
+        final String [] tokens = parseLine(line, count, bStation);
 
-        BigDecimal [] doubles = new BigDecimal[tokens.length];
+        final BigDecimal [] doubles = new BigDecimal[tokens.length];
 
         for (int i = 0; i < doubles.length; ++i) {
-            String token = tokens[i].trim();
+            final String token = tokens[i].trim();
             if (token.length() != 0) {
                 doubles[i] = new BigDecimal(token);
             }
@@ -530,11 +509,11 @@
     }
 
     private static String [] parseLine(
-        String  line,
-        int     tokenCount,
-        boolean bParseStation
-    ) throws ParseException {
-        ArrayList<String> strings = new ArrayList<String>();
+            final String  line,
+            final int     tokenCount,
+            final boolean bParseStation
+            ) throws ParseException {
+        final ArrayList<String> strings = new ArrayList<>();
 
         if (bParseStation) {
             if (line.length() < 8) {
@@ -550,7 +529,7 @@
                 break;
             }
             strings.add(line.substring(pos,
-                Math.min(pos + 8, line.length())));
+                    Math.min(pos + 8, line.length())));
         }
 
         return strings.toArray(new String[strings.size()]);

http://dive4elements.wald.intevation.org