mschaefer@8971: /* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde mschaefer@8971: * Software engineering by mschaefer@8971: * Björnsen Beratende Ingenieure GmbH mschaefer@8971: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt mschaefer@8971: * mschaefer@8971: * This file is Free Software under the GNU AGPL (>=v3) mschaefer@8971: * and comes with ABSOLUTELY NO WARRANTY! Check out the mschaefer@8971: * documentation coming with Dive4Elements River for details. mschaefer@8971: */ mschaefer@8971: mschaefer@8971: package org.dive4elements.river.importer.sinfo.parsers; mschaefer@8971: mschaefer@8971: import java.io.File; mschaefer@8971: import java.text.DateFormat; mschaefer@8971: import java.text.SimpleDateFormat; mschaefer@8971: import java.util.ArrayList; mschaefer@9663: import java.util.Calendar; mschaefer@8971: import java.util.Date; mschaefer@8971: import java.util.EnumMap; mschaefer@8971: import java.util.HashMap; mschaefer@8971: import java.util.List; mschaefer@8971: import java.util.regex.Matcher; mschaefer@8971: import java.util.regex.Pattern; mschaefer@8971: mschaefer@8971: import org.apache.log4j.Logger; mschaefer@8971: import org.dive4elements.river.importer.Config; mschaefer@8971: import org.dive4elements.river.importer.ImportRiver; mschaefer@9578: import org.dive4elements.river.importer.ImporterSession; mschaefer@8971: import org.dive4elements.river.importer.common.AbstractParser; mschaefer@8971: import org.dive4elements.river.importer.common.ParsingState; mschaefer@8971: import org.dive4elements.river.importer.sinfo.importitem.CollisionKmLineImport; mschaefer@8971: import org.dive4elements.river.importer.sinfo.importitem.CollisionSeriesImport; mschaefer@8971: import org.dive4elements.river.importer.sinfo.importitem.CollisionTypeImport; mschaefer@8971: import org.dive4elements.river.model.sinfo.Collision; mschaefer@8971: import org.dive4elements.river.model.sinfo.CollisionType; mschaefer@8971: import org.dive4elements.river.model.sinfo.CollisionValue; mschaefer@9579: import org.hibernate.Session; mschaefer@8971: mschaefer@8971: /** mschaefer@8971: * Reads and parses a collision file mschaefer@8971: * mschaefer@8971: * @author Matthias Schäfer mschaefer@8971: * mschaefer@8971: */ mschaefer@8971: public class CollisionParser extends AbstractParser { mschaefer@8971: mschaefer@8971: /***** FIELDS *****/ mschaefer@8971: mschaefer@8971: private static final Logger log = Logger.getLogger(CollisionParser.class); mschaefer@8971: mschaefer@9663: private static final Pattern IMPORT_FILENAME = Pattern.compile("Grundberuehrungen_([12]\\d{3})\\.csv", Pattern.CASE_INSENSITIVE); mschaefer@9663: mschaefer@9663: private static final Pattern META_YEAR = Pattern.compile("^#\\sJahr:\\s*([12]\\d{3}).*", Pattern.CASE_INSENSITIVE); mschaefer@8971: mschaefer@8971: private enum ColTitlePattern { mschaefer@8971: DATE("Datum.*"), // mschaefer@9654: GAUGE_W("Pegelstand\\s*\\[cm\\].*"), // mschaefer@8971: GAUGE_NAME("Bezugspegel.*"), // mschaefer@8971: TYPE("Unfallart.*"); mschaefer@8971: mschaefer@8971: private final Pattern pattern; mschaefer@8971: mschaefer@8971: ColTitlePattern(final String regexp) { mschaefer@8971: this.pattern = Pattern.compile(regexp, Pattern.CASE_INSENSITIVE); mschaefer@8971: } mschaefer@8971: mschaefer@8971: public Pattern getPattern() { mschaefer@8971: return this.pattern; mschaefer@8971: } mschaefer@8971: } mschaefer@8971: mschaefer@8971: private static final DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy"); mschaefer@8971: mschaefer@8971: private final EnumMap cols = new EnumMap<>(ColTitlePattern.class); mschaefer@8971: mschaefer@8971: private final HashMap types; mschaefer@8971: mschaefer@9663: private final int filenameyear; mschaefer@9663: mschaefer@8971: mschaefer@8971: /***** CONSTRUCTORS *****/ mschaefer@8971: mschaefer@9663: public CollisionParser(final File importPath, final File rootRelativePath, final ImportRiver river, final int filenameyear) { mschaefer@8971: super(importPath, rootRelativePath, river); mschaefer@9579: logDebug("CollisionParser.new: calling ImporterSession.getInstance"); mschaefer@9579: final Session session = ImporterSession.getInstance().getDatabaseSession(); mschaefer@8971: this.types = new HashMap<>(); mschaefer@9579: for (final CollisionType type : CollisionType.getTypes(session)) mschaefer@8971: this.types.put(type.getName().trim().toLowerCase(), new CollisionTypeImport(type.getName())); mschaefer@9663: this.filenameyear = filenameyear; mschaefer@8971: } mschaefer@8971: mschaefer@8971: mschaefer@8971: /***** METHODS *****/ mschaefer@8971: mschaefer@8971: @Override mschaefer@8971: protected Logger getLog() { mschaefer@8971: return log; mschaefer@8971: } mschaefer@8971: mschaefer@8971: /** mschaefer@8971: * Whether this import type shall be skipped mschaefer@8971: */ mschaefer@8971: public static boolean shallSkip() { mschaefer@8971: return Config.INSTANCE.skipSInfoCollision(); mschaefer@8971: } mschaefer@8971: mschaefer@8971: /** mschaefer@8971: * Creates a list of parsers for all collision import files in a directory mschaefer@8971: */ mschaefer@8971: public static List createParsers(final File importDir, final File relativeDir, final ImportRiver river) { mschaefer@8971: final List parsers = new ArrayList<>(); mschaefer@8988: if (importDir.exists()) { mschaefer@9663: for (final File file : listFiles(importDir, IMPORT_FILENAME)) { mschaefer@9663: final Matcher m = IMPORT_FILENAME.matcher(file.getName()); mschaefer@9663: m.matches(); mschaefer@9663: final String fnyear = m.group(1); mschaefer@9663: parsers.add(new CollisionParser(file, new File(relativeDir, file.getName()), river, Integer.valueOf(fnyear))); mschaefer@9663: } mschaefer@8988: } mschaefer@8971: return parsers; mschaefer@8971: } mschaefer@8971: mschaefer@8971: @Override mschaefer@8971: protected CollisionSeriesImport createSeriesImport(final String filename) { mschaefer@8971: return new CollisionSeriesImport(filename); mschaefer@8971: } mschaefer@8971: mschaefer@8971: @Override mschaefer@9012: protected KmMode kmMode() { mschaefer@9012: return KmMode.DUPLICATES; mschaefer@8971: } mschaefer@8971: mschaefer@8971: @Override mschaefer@8971: protected boolean handleMetaOther() { mschaefer@8971: if (handleMetaYear()) mschaefer@8971: return true; mschaefer@8971: else mschaefer@8971: return false; mschaefer@8971: } mschaefer@8971: mschaefer@8971: private boolean handleMetaYear() { mschaefer@8971: final Matcher m = META_YEAR.matcher(this.currentLine); mschaefer@8971: if (m.matches()) { mschaefer@8971: this.metaPatternsMatched.add(META_YEAR); mschaefer@9663: final int year = Integer.parseInt(m.group(1)); mschaefer@9663: this.seriesHeader.setYear(year); mschaefer@9663: if (year != this.filenameyear) mschaefer@9663: logLineWarning("Year in file name differs from meta data year"); mschaefer@8971: return true; mschaefer@8971: } mschaefer@8971: return false; mschaefer@8971: } mschaefer@8971: mschaefer@8971: @Override mschaefer@8971: protected boolean handleMetaColumnTitles() { mschaefer@8971: if (!super.handleMetaColumnTitles()) mschaefer@8971: return false; mschaefer@8971: for (final ColTitlePattern col : ColTitlePattern.values()) mschaefer@8971: this.cols.put(col, -1); mschaefer@8971: for (int i = 1; i <= this.columnTitles.size() - 1; i++) { mschaefer@8971: for (final ColTitlePattern col : ColTitlePattern.values()) { mschaefer@8971: if (col.getPattern().matcher(this.columnTitles.get(i)).matches()) { mschaefer@8971: this.cols.put(col, i); mschaefer@8971: break; mschaefer@8971: } mschaefer@8971: } mschaefer@8971: } mschaefer@9654: if (this.cols.get(ColTitlePattern.DATE) < 0) { mschaefer@9654: logLineError("Column of the event dates could not be identified, missing column title 'Datum'"); mschaefer@9654: this.headerParsingState = ParsingState.STOP; mschaefer@9654: return true; mschaefer@9654: } mschaefer@9654: if (this.cols.get(ColTitlePattern.GAUGE_W) < 0) { mschaefer@9654: logLineError("Column of the waterlevel could not be identified, missing column title 'Pegelstand [cm]'"); mschaefer@9654: this.headerParsingState = ParsingState.STOP; mschaefer@9654: return true; mschaefer@9654: } mschaefer@9654: if (this.cols.get(ColTitlePattern.GAUGE_W) < 0) { mschaefer@9654: logLineError("Column of the reference gauge could not be identified, missing column title 'Bezugspegel'"); mschaefer@9654: this.headerParsingState = ParsingState.STOP; mschaefer@9654: return true; mschaefer@9654: } mschaefer@8971: if (this.cols.get(ColTitlePattern.TYPE) < 0) { mschaefer@9654: logLineError("Column of the collision types could not be identified, missing column title 'Unfallart'"); mschaefer@8971: this.headerParsingState = ParsingState.STOP; mschaefer@9654: return true; mschaefer@8971: } mschaefer@8971: if (!this.metaPatternsMatched.contains(META_YEAR)) { mschaefer@8971: logError("Required meta info for the year is missing"); mschaefer@8971: this.headerParsingState = ParsingState.STOP; mschaefer@9654: return true; mschaefer@8971: } mschaefer@8971: return true; mschaefer@8971: } mschaefer@8971: mschaefer@8971: @Override mschaefer@8971: protected CollisionKmLineImport createKmLineImport(final Double km, final String[] values) { mschaefer@8971: Date eventDate = null; mschaefer@8971: try { mschaefer@8971: eventDate = dateFormat.parse(values[this.cols.get(ColTitlePattern.DATE)]); mschaefer@8971: } mschaefer@8971: catch (final Exception e) { mschaefer@9654: logLineWarning("Invalid or missing date"); mschaefer@8971: return null; mschaefer@8971: } mschaefer@9663: final Calendar cal = Calendar.getInstance(); mschaefer@9663: cal.setTime(eventDate); mschaefer@9663: if (cal.get(Calendar.YEAR) != this.seriesHeader.getYear()) { mschaefer@9663: logLineWarning("Invalid or missing date"); mschaefer@9663: return null; mschaefer@9663: } mschaefer@8971: final String typeName = values[this.cols.get(ColTitlePattern.TYPE)].trim(); mschaefer@8971: final String typeKey = typeName.toLowerCase(); mschaefer@8971: CollisionTypeImport type = null; mschaefer@8971: if (this.types.containsKey(typeKey)) mschaefer@8971: type = this.types.get(typeKey); mschaefer@8971: else { mschaefer@8971: type = new CollisionTypeImport(typeName); mschaefer@8971: this.types.put(typeKey, type); mschaefer@8971: } mschaefer@8971: String gaugeName = null; mschaefer@9654: gaugeName = values[this.cols.get(ColTitlePattern.GAUGE_NAME)].trim(); mschaefer@9654: final Number gaugeW = parseDoubleCheckNull(values, this.cols.get(ColTitlePattern.GAUGE_W)); mschaefer@9654: if ((gaugeW == null) || Double.isNaN(gaugeW.doubleValue())) { mschaefer@9654: logLineWarning(INVALID_VALUE_ERROR_FORMAT, "waterlevel"); mschaefer@9654: return null; mschaefer@9654: } mschaefer@9654: return new CollisionKmLineImport(km, type, eventDate, gaugeName, gaugeW.doubleValue()); mschaefer@8971: } mschaefer@8971: }