# HG changeset patch # User mschaefer # Date 1522745033 -7200 # Node ID e541938dd3ab01b739eb0b3360442e57d24b68a8 # Parent a0a0a7f912abe68c7f683fa224cb37440e63b8fb Range data handled consistently as BigDecimal to minimize the fractional part of a and b diff -r a0a0a7f912ab -r e541938dd3ab backend/src/main/java/org/dive4elements/river/importer/ImportRange.java --- 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 : diff -r a0a0a7f912ab -r e541938dd3ab backend/src/main/java/org/dive4elements/river/importer/parsers/WstParser.java --- 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 kms = new HashSet(); + final HashSet 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 uniqueColumnNames = - new HashSet(); + final HashSet 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] = - ""; - ++unknownCount; - } - else { - lsBezeichner[i] = "Q="+format(q); - } + final double q = firstAbfluesse.length > i ? + firstAbfluesse[i].doubleValue() : 0d; + if (q < 0.001) { + lsBezeichner[i] = + ""; + ++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 strings = new ArrayList(); + final String line, + final int tokenCount, + final boolean bParseStation + ) throws ParseException { + final ArrayList 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()]);