view artifacts/src/main/java/org/dive4elements/river/artifacts/services/AbstractMainValuesService.java @ 9404:bc9a45d2b1fa

common time range for gauges incl. error messages
author gernotbelger
date Wed, 15 Aug 2018 13:59:09 +0200
parents 82c67b859aa7
children
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 static org.dive4elements.river.backend.utils.EpsilonComparator.CMP;

import java.util.List;

import org.apache.log4j.Logger;
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.model.RiverFactory;
import org.dive4elements.river.model.Gauge;
import org.dive4elements.river.model.MainValue;
import org.dive4elements.river.model.MainValueType;
import org.dive4elements.river.model.NamedMainValue;
import org.dive4elements.river.model.OfficialLine;
import org.dive4elements.river.model.Range;
import org.dive4elements.river.model.River;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
abstract class AbstractMainValuesService extends D4EService {

    private static final long serialVersionUID = 1L;

    public static final class MainValuesServiceException extends Exception {

        private static final long serialVersionUID = 1L;

        public MainValuesServiceException(final String message) {
            super(message);
        }
    }

    /** The log that is used by this service. */
    private static Logger log = Logger.getLogger(AbstractMainValuesService.class);

    /** XPath that points to the river definition of the incoming request. */

    /** XPath that points to the start definition of the incoming request. */
    private static final String XPATH_START = "/art:mainvalues/art:start/text()";

    /** The XPath that points to the end definition of the incoming request. */
    private static final String XPATH_END = "/art:mainvalues/art:end/text()";

    protected static final Document error(final String msg) {
        log.debug(msg);
        return XMLUtils.newDocument();
    }

    /**
     * This method extracts the river from the incoming request. If no river
     * string was found or no river is found in the database based on this
     * string a NullPointerException is thrown.
     *
     * @param data
     *            The incoming request data.
     *
     * @return the River object.
     */
    protected static final River getRequestedRiver(final Document data, final String XPATH_RIVER) throws MainValuesServiceException {
        log.debug("MainValuesService.getRequestedRiver");

        String riverStr = XMLUtils.xpathString(data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);

        if (riverStr != null && (riverStr = riverStr.trim()).length() > 0)
            return RiverFactory.getRiver(riverStr);

        throw new MainValuesServiceException("no river found.");
    }

    protected static final Gauge getRequestedGauge(final Document data, final River river) throws MainValuesServiceException {

        final double[] minmax = getRequestedStartEnd(data, river);
        final Gauge gauge = river.determineRefGauge(minmax, CMP.compare(minmax[0], minmax[1]) != 0);
        if (gauge == null)
            throw new MainValuesServiceException("no gauge found.");

        return gauge;
    }

    /**
     * This method extracts the start and end point from incoming request
     * document and returns both values in an array.
     * If no start and end strings
     * are found in the document, the min/max values of the <i>river</i> are
     * returned.
     *
     * @param data
     *            The incoming request data.
     * @param river
     *            The river of the request.
     *
     * @return the start and end point.
     */
    public static double[] getRequestedStartEnd(final Document data, final River river) {
        log.debug("MainValuesService.getStartEnd");

        final String startStr = XMLUtils.xpathString(data, XPATH_START, ArtifactNamespaceContext.INSTANCE);

        final String endStr = XMLUtils.xpathString(data, XPATH_END, ArtifactNamespaceContext.INSTANCE);

        if (startStr == null || endStr == null) {
            return river.determineMinMaxDistance();
        }

        try {
            final double start = Double.parseDouble(startStr);
            final double end = Double.parseDouble(endStr);

            if (log.isDebugEnabled()) {
                log.debug("Found start: " + start);
                log.debug("Found end: " + end);
            }

            return new double[] { start, end };
        }
        catch (final NumberFormatException nfe) {
            log.warn(nfe, nfe);
            return river.determineMinMaxDistance();
        }
    }

    protected final Document buildDocument(final River river, final Gauge gauge, final List<MainValue> mainValues, final Object context) {
        log.debug("MainValuesService.buildDocument");

        final Document doc = XMLUtils.newDocument();

        final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);

        final Element rootEl = cr.create("service");
        cr.addAttr(rootEl, "name", "mainvalues");

        doc.appendChild(rootEl);

        appendMetaInformation(doc, rootEl, river, gauge, context);
        appendMainValues(doc, rootEl, mainValues, river.getId(), context);

        return doc;
    }

    /**
     * This method appends some meta information to the result document.
     * Currently, the river's and gauge's names and the gauge's range are
     * appended.
     *
     * @param root
     *            The root element of the result document.
     * @param river
     *            The river.
     * @param gauge
     *            The gauge.
     * @param context
     *            The context object.
     */
    static void appendMetaInformation(final Document doc, final Element root, final River river, final Gauge gauge, final Object context) {
        log.debug("MainValuesService.appendMetaInformation");

        final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);

        final Range range = gauge.getRange();

        final Element riverEl = cr.create("river");
        cr.addAttr(riverEl, "name", river.getName());

        final Element gaugeEl = cr.create("gauge");
        cr.addAttr(gaugeEl, "name", gauge.getName());
        cr.addAttr(gaugeEl, "from", range.getA().toString());
        cr.addAttr(gaugeEl, "to", range.getB().toString());

        root.appendChild(riverEl);
        root.appendChild(gaugeEl);
    }

    /** Checks i a main value has an official associated, */
    private static boolean hasOfficialLine(final NamedMainValue nmv, final Integer riverId) {
        for (final OfficialLine ol : nmv.getOfficialLines()) {
            if (ol.getWstColumn().getWst().getRiver().getId().equals(riverId)) {
                return true;
            }
        }
        return false;
    }

    /** Append xml representation of main values to document. */
    private void appendMainValues(final Document doc, final Element root, final List<MainValue> mainValues, final Integer riverId, final Object context) {
        log.debug("MainValuesService.appendMainValues");

        final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);

        final Element list = cr.create("mainvalues");

        for (final MainValue mainValue : mainValues) {
            final Element newEl = buildMainValueElement(doc, mainValue, riverId, context);

            if (newEl != null) {
                list.appendChild(newEl);
            }
        }

        root.appendChild(list);
    }

    /**
     * This method builds a concrete mainvalue element. This element consists of
     * three attributes: the value, its name and its type.
     *
     * @param doc
     *            The owner document.
     * @param mainValue
     *            The mainvalue.
     * @param context
     *            The context object.
     *
     * @return a mainvalue element.
     */
    private Element buildMainValueElement(final Document doc, final MainValue mainValue, final Integer riverId, final Object context) {
        final ElementCreator cr = new ElementCreator(doc, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX);

        final NamedMainValue namedMainValue = mainValue.getMainValue();
        final MainValueType mainValueType = namedMainValue.getType();

        final Element el = cr.create("mainvalue");

        cr.addAttr(el, "value", mainValue.getValue().toString());
        cr.addAttr(el, "name", namedMainValue.getName());
        cr.addAttr(el, "type", mainValueType.getName());
        if (mainValue.getTimeInterval() != null) {
            if (mainValue.getTimeInterval().getStartTime() != null) {
                cr.addAttr(el, "starttime", Long.toString(mainValue.getTimeInterval().getStartTime().getTime()));
            }
            if (mainValue.getTimeInterval().getStopTime() != null) {
                cr.addAttr(el, "stoptime", Long.toString(mainValue.getTimeInterval().getStopTime().getTime()));
            }
        }

        if (hasOfficialLine(namedMainValue, riverId)) {
            cr.addAttr(el, "official", "true");
        }

        return el;
    }
}

http://dive4elements.wald.intevation.org