Mercurial > dive4elements > river
view artifacts/src/main/java/org/dive4elements/river/artifacts/services/DynamicMainValuesTimeRangeDeterminationService.java @ 9410:52314c4ab3be
Fixed: computing of min and max date
author | mschaefer |
---|---|
date | Thu, 16 Aug 2018 09:43:55 +0200 |
parents | 38201f5b0dd9 |
children | b534a4f4e4f6 |
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde * Software engineering by Intevation GmbH * * This file is Free Software under the GNU AGPL (>=v3) * and comes with ABSOLUTELY NO WARRANTY! Check out the * documentation coming with Dive4Elements River for details. */ package org.dive4elements.river.artifacts.services; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import javax.xml.xpath.XPathConstants; import org.dive4elements.artifacts.CallMeta; import org.dive4elements.artifacts.GlobalContext; import org.dive4elements.artifacts.common.ArtifactNamespaceContext; import org.dive4elements.artifacts.common.utils.XMLUtils; import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator; import org.dive4elements.river.artifacts.resources.Resources; import org.dive4elements.river.artifacts.services.AbstractMainValuesService.MainValuesServiceException; import org.dive4elements.river.model.Gauge; import org.dive4elements.river.model.River; import org.dive4elements.river.model.sinfo.DailyDischargeValue; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * This service returns the list of gauges with daily discharge time periods and error messages */ public class DynamicMainValuesTimeRangeDeterminationService extends D4EService { private static final long serialVersionUID = 1L; private static final String ROOT_NODE = "dynamic-mainvalues-input"; private static final Long DATE_DELTA_ERROR_MSG = (long) (60 * 60 * 24 * 1000); public static final class ServiceException extends Exception { private static final long serialVersionUID = 1L; public ServiceException(final String message) { super(message); } } private static class GaugeInfoResult { protected final String globalErrorMsg; protected final List<GaugeInfo> gaugeInfos; protected GaugeInfoResult(final List<GaugeInfo> gaugeInfos, final String globalErrorMsg) { this.gaugeInfos = gaugeInfos; this.globalErrorMsg = globalErrorMsg; } private static class GaugeInfo { protected final String errorMsg; protected final Gauge gauge; protected final Date startdate; protected final Date enddate; public GaugeInfo(final String errorMsg, final Gauge gauge, final Date startdate, final Date enddate) { this.errorMsg = errorMsg; this.gauge = gauge; this.startdate = startdate; this.enddate = enddate; } } } /** * Queries the available daily discharge time periods of a list of gauges from the database and checks the overlapping * * @throws ServiceException */ private GaugeInfoResult getCommonTimeRangeForGauges(final List<Gauge> gauges, final Date startTime, final Date endTime, final CallMeta meta) throws ServiceException { // Query the gauge's daily Q values String globalErrorMsg = ""; final List<GaugeInfoResult.GaugeInfo> gaugeResults = new ArrayList<>(); Date min = startTime; Date max = endTime; for (final Gauge gauge : gauges) { final Date[] gaugeDates = DailyDischargeValue.getTimePeriod(gauge, startTime, endTime); if (gaugeDates[0] == null) { final String msg = Resources.getMsg(meta, "bundu.wst_no_data_at_all", "bundu.wst_no_data_at_all", gauge.getName()); final GaugeInfoResult.GaugeInfo gi = new GaugeInfoResult.GaugeInfo(msg, gauge, null, null); gaugeResults.add(gi); if (globalErrorMsg.isEmpty()) globalErrorMsg = msg; continue; } if (gaugeDates[0].getTime() > min.getTime()) min = gaugeDates[0]; if (gaugeDates[1].getTime() < max.getTime()) max = gaugeDates[1]; String errormsg = null; if ((gaugeDates[1].getTime() < endTime.getTime()) || (gaugeDates[0].getTime() > startTime.getTime())) errormsg = makeDoesNotCoverErrorMsg(gaugeDates[0], gaugeDates[1], meta); gaugeResults.add(new GaugeInfoResult.GaugeInfo(errormsg, gauge, gaugeDates[0], gaugeDates[1])); } // common Range and correct errorMsg final List<GaugeInfoResult.GaugeInfo> gaugeResultsSecondTurn = new ArrayList<>(); for (final GaugeInfoResult.GaugeInfo gi : gaugeResults) { gaugeResultsSecondTurn.add(new GaugeInfoResult.GaugeInfo(gi.errorMsg, gi.gauge, gi.startdate != null ? min : null, gi.enddate != null ? max : null)); } if (globalErrorMsg.isEmpty() && (min.getTime() > max.getTime())) globalErrorMsg = getMsg(meta, "bundu.wst.gauge_timeranges_disjoint"); final GaugeInfoResult result = new GaugeInfoResult(gaugeResultsSecondTurn, globalErrorMsg); return result; } private String makeDoesNotCoverErrorMsg(final Date start, final Date end, final CallMeta meta) { final Calendar cal = Calendar.getInstance(); cal.setTime(start); final String startyear = String.valueOf(cal.get(Calendar.YEAR)); cal.setTime(end); final String endyear = String.valueOf(cal.get(Calendar.YEAR)); return Resources.getMsg(meta, "bundu.wst.range_does_not_cover", new Object[] { startyear, endyear }); } @Override public Document doProcess(final Document data, final GlobalContext context, final CallMeta callMeta) { try { final River river = AbstractMainValuesService.getRequestedRiver(data, "/art:" + ROOT_NODE + "/art:river/text()"); final List<Gauge> gauges = getRequestedGauges(data, river, callMeta); final Date start = getRequestedStartYear(data, "/art:" + ROOT_NODE + "/art:startYear/text()"); final Date end = getRequestedEndYear(data, "/art:" + ROOT_NODE + "/art:endYear/text()"); final GaugeInfoResult result = getCommonTimeRangeForGauges(gauges, start, end, callMeta); return buildDocument(result, context, callMeta); } catch (final ServiceException | MainValuesServiceException e) { e.printStackTrace(); return AbstractMainValuesService.error(e.getMessage()); } } public static final Date getRequestedEndYear(final Document data, final String XPATH_END_YEAR) throws MainValuesServiceException { final String endStr = XMLUtils.xpathString(data, XPATH_END_YEAR, ArtifactNamespaceContext.INSTANCE); if (endStr == null) throw new MainValuesServiceException("no end year"); // should not happen try { final int year = Integer.parseInt(endStr); // FIXME: timezone? probably must match timezone of database final Calendar cal = Calendar.getInstance(); cal.clear(); cal.set(year, 11, 31); return cal.getTime(); } catch (final NumberFormatException e) { e.printStackTrace(); throw new MainValuesServiceException("invalid end year"); // should not happen } } public static final Date getRequestedStartYear(final Document data, final String XPATH_START_YEAR) throws MainValuesServiceException { final String startStr = XMLUtils.xpathString(data, XPATH_START_YEAR, ArtifactNamespaceContext.INSTANCE); if (startStr == null) throw new MainValuesServiceException("no start year");// should not happen try { final int year = Integer.parseInt(startStr); // FIXME: timezone? probably must match timezone of database final Calendar cal = Calendar.getInstance(); cal.clear(); cal.set(year, 0, 1); return cal.getTime(); } catch (final NumberFormatException e) { e.printStackTrace(); throw new MainValuesServiceException("invalid start year"); // should not happen } } private Document buildDocument(final GaugeInfoResult result, final GlobalContext context, final CallMeta meta) { final Document doc = XMLUtils.newDocument(); final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX); final Element rootEl = cr.create(ROOT_NODE); doc.appendChild(rootEl); final Element globalErrElement = cr.create("global-error-msg"); globalErrElement.setTextContent(result.globalErrorMsg); rootEl.appendChild(globalErrElement); final List<GaugeInfoResult.GaugeInfo> values = result.gaugeInfos; for (final GaugeInfoResult.GaugeInfo gauge : values) { final Element gaugeElement = cr.create("gauge"); cr.addAttr(gaugeElement, "name", gauge.gauge.getName()); if (gauge.startdate != null) cr.addAttr(gaugeElement, "date-from", String.valueOf(gauge.startdate.getTime())); if (gauge.enddate != null) cr.addAttr(gaugeElement, "date-to", String.valueOf(gauge.enddate.getTime())); if (gauge.errorMsg != null) cr.addAttr(gaugeElement, "error-message", gauge.errorMsg); rootEl.appendChild(gaugeElement); } return doc; } final Element buildElement(final ElementCreator cr, final String type, final Date date) { final Element el = cr.create(type); cr.addAttr(el, "value", String.valueOf(date.getTime())); return el; } private static final List<Gauge> getRequestedGauges(final Document data, final River river, final CallMeta meta) throws ServiceException { final NodeList gaugeNodes = data.getElementsByTagNameNS(ArtifactNamespaceContext.NAMESPACE_URI, "gauge"); final List<Gauge> gauges = new ArrayList<>(); for (int i = 0; i < gaugeNodes.getLength(); i++) { final Element gaugeElt = (Element) gaugeNodes.item(i); final String gaugeName = (String) XMLUtils.xpath(gaugeElt, "text()", XPathConstants.STRING); final Gauge gauge = Gauge.getGaugeByNameAndRiver(gaugeName, river); if (gauge != null) gauges.add(gauge); else { throw new ServiceException("bundu_wst_error_reading_gauges"); } } return gauges; } private static String getMsg(final CallMeta meta, final String key) { return Resources.getMsg(meta, key); } }