dk.dma.ais.view.rest.QueryParameterHelper.java Source code

Java tutorial

Introduction

Here is the source code for dk.dma.ais.view.rest.QueryParameterHelper.java

Source

/* Copyright (c) 2011 Danish Maritime Authority.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dk.dma.ais.view.rest;

import com.google.common.collect.Range;
import com.google.common.primitives.Ints;
import dk.dma.ais.packet.AisPacket;
import dk.dma.ais.packet.AisPacketFilters;
import dk.dma.ais.packet.AisPacketFiltersStateful;
import dk.dma.ais.packet.AisPacketOutputSinkJsonObject;
import dk.dma.ais.packet.AisPacketOutputSinks;
import dk.dma.ais.packet.AisPacketSource;
import dk.dma.ais.packet.AisPacketStream;
import dk.dma.ais.tracker.TargetInfo;
import dk.dma.ais.view.common.util.TargetInfoFilters;
import dk.dma.commons.util.DateTimeUtil;
import dk.dma.commons.util.Iterables;
import dk.dma.commons.util.io.OutputStreamSink;
import dk.dma.commons.web.rest.query.QueryParameterValidators;
import dk.dma.enav.model.geometry.Area;
import dk.dma.enav.model.geometry.BoundingBox;
import dk.dma.enav.model.geometry.CoordinateSystem;
import dk.dma.enav.model.geometry.Position;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.Period;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

import static dk.dma.commons.web.rest.query.QueryParameterValidators.getParameter;
import static dk.dma.commons.web.rest.query.QueryParameterValidators.getParameterAsInt;
import static dk.dma.commons.web.rest.query.QueryParameterValidators.getParameterAsIntWithRange;
import static dk.dma.commons.web.rest.query.QueryParameterValidators.getParameterWithCustomErrorMessage;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang.StringUtils.isBlank;

/**
 * A small helper class to extract query information.
 * 
 * @author Kasper Nielsen
 */
class QueryParameterHelper {

    private static final Logger LOG = LoggerFactory.getLogger(QueryParameterHelper.class);

    /** An optional area for the query. */
    final BoundingBox area;

    /** The interval for the query */
    final Interval interval;

    /** The time for which to generate snapshot in KML queries */
    final DateTime kmlSnapshotAt;

    final Integer limit;

    final Integer minDistance;

    final Long minDuration;

    final Integer primaryMmsi;

    final Integer secondaryMmsi;

    final String title;

    final String description;

    final Integer interpolationStepSecs;

    final long timeToRun = -1;

    final int[] mmsis;

    final OutputStreamSink<AisPacket> outputSink;

    /** A predicate on the source of each packet. */
    final Predicate<? super AisPacket> sourceFilter;

    /** A general AisPacket filter on source, target and message */
    final Predicate<? super AisPacket> packetFilter;

    final UriInfo uriInfo;

    final String jobId;

    final boolean createSituationFolder;

    final boolean createMovementsFolder;

    final boolean createTracksFolder;

    public QueryParameterHelper(UriInfo uriInfo) {
        this.uriInfo = requireNonNull(uriInfo);
        this.area = findBoundingBox(uriInfo);
        this.interval = findInterval(uriInfo);

        this.createSituationFolder = findCreateSituationFolder(uriInfo);
        this.createMovementsFolder = findCreateMovementsFolder(uriInfo);
        this.createTracksFolder = findCreateTracksFolder(uriInfo);

        this.kmlSnapshotAt = findAt(uriInfo);
        String limit = getParameter(uriInfo, "limit", null);
        this.limit = limit == null ? null : Integer.parseInt(limit);
        Set<Integer> mmsi = new HashSet<>(QueryParameterValidators.getParametersAsInt(uriInfo, "mmsi"));
        this.mmsis = Ints.toArray(mmsi);
        sourceFilter = getSourceFilter(uriInfo);
        packetFilter = getPacketFilter(uriInfo);

        minDistance = getParameterAsIntWithRange(uriInfo, "minDistance", null, Range.atLeast(0));
        minDuration = findMinimumDurationMS(uriInfo);
        primaryMmsi = findPrimaryMmsi(uriInfo);
        secondaryMmsi = findSecondaryMmsi(uriInfo);
        title = getParameter(uriInfo, "title", null);
        description = getParameter(uriInfo, "description", null);
        interpolationStepSecs = getParameterAsInt(uriInfo, "interpolation", null);

        outputSink = getOutputSink(uriInfo);
        jobId = QueryParameterValidators.getParameter(uriInfo, "jobId", null);

        LOG.debug(toString());
    }

    public Iterable<AisPacket> applyAreaFilter(Iterable<AisPacket> i) {
        return area == null ? i : Iterables.filter(i, AisPacketFilters.filterOnMessagePositionWithin(area));
    }

    public Iterable<AisPacket> applyTargetFilterArea(Iterable<AisPacket> i, AisPacketFiltersStateful state) {
        return area == null ? i : Iterables.filter(i, state.filterOnTargetPositionWithin(area));
    }

    public AisPacketStream applyLimitFilter(AisPacketStream s) {
        return limit == null ? s : s.limit(limit);
    }

    public Iterable<AisPacket> applyLimitFilter(Iterable<AisPacket> i) {
        return limit == null ? i : com.google.common.collect.Iterables.limit(i, limit);
    }

    public Iterable<AisPacket> applyPositionSampler(Iterable<AisPacket> i) {
        if (minDistance == null && minDuration == null) {
            return i;
        }
        return Iterables.filter(i, AisPacketFilters.samplingFilter(minDistance, minDuration));
    }

    public Iterable<AisPacket> applyTargetPositionSampler(Iterable<AisPacket> i) {
        if (minDistance == null && minDuration == null) {
            return i;
        }
        return Iterables.filter(i, AisPacketFilters.targetSamplingFilter(minDistance, minDuration));
    }

    public AisPacketStream applySourceFilter(AisPacketStream s) {
        return sourceFilter == null ? s : s.filter(sourceFilter);
    }

    public Iterable<AisPacket> applySourceFilter(Iterable<AisPacket> i) {
        return sourceFilter == null ? i : Iterables.filter(i, sourceFilter);
    }

    public Iterable<AisPacket> applyPacketFilter(Iterable<AisPacket> i) {
        return packetFilter == null ? i : Iterables.filter(i, packetFilter);
    }

    public BiPredicate<AisPacketSource, TargetInfo> getSourceAndTargetPredicate() {
        final Predicate<AisPacketSource> ps = getSourcePredicate();
        final Predicate<TargetInfo> pt = getTargetPredicate();
        return new BiPredicate<AisPacketSource, TargetInfo>() {
            @Override
            public boolean test(AisPacketSource t, TargetInfo u) {
                return ps.test(t) && pt.test(u);
            }
        };
    }

    public Predicate<TargetInfo> getTargetPredicate() {
        return e -> true;
    }

    public Predicate<TargetInfo> getTargetAreaFilter() {
        return area != null ? TargetInfoFilters.filterOnBoundingBox(area) : null;
    }

    public Predicate<AisPacketSource> getSourcePredicate() {
        return getPacketSourceFilter(uriInfo);
    }

    public Predicate<AisPacket> getPacketFilter() {
        return getPacketFilter(uriInfo);
    }

    public Area getArea() {
        return area;
    }

    /**
     * @return the interval
     */
    public Interval getInterval() {
        return interval;
    }

    public int[] getMMSIs() {
        return Arrays.copyOf(mmsis, mmsis.length);
    }

    /**
     * @return the outputSink
     */
    public OutputStreamSink<AisPacket> getOutputSink() {
        return outputSink;
    }

    private static Long findMinimumDurationMS(UriInfo info) {
        String dur = QueryParameterValidators.getParameter(info, "minDuration", null);
        return dur == null ? null : Period.parse(dur).toStandardSeconds().getSeconds() * 1000L;
    }

    private static Integer findPrimaryMmsi(UriInfo info) {
        String primaryMmsi = QueryParameterValidators.getParameter(info, "primaryMmsi", null);
        return primaryMmsi == null ? null : Integer.parseInt(primaryMmsi);
    }

    private static Integer findSecondaryMmsi(UriInfo info) {
        String secondaryMmsi = QueryParameterValidators.getParameter(info, "secondaryMmsi", null);
        return secondaryMmsi == null ? null : Integer.parseInt(secondaryMmsi);
    }

    private static BoundingBox findBoundingBox(UriInfo info) {
        String box = QueryParameterValidators.getParameter(info, "box", null);
        if (box != null) {
            String[] str = box.split(",");
            if (str.length != 4) {
                throw new UnsupportedOperationException(
                        "A box must contain exactly 4 points, was " + str.length + "(" + box + ")");
            }
            double lat1 = Double.parseDouble(str[0]);
            double lon1 = Double.parseDouble(str[1]);
            double lat2 = Double.parseDouble(str[2]);
            double lon2 = Double.parseDouble(str[3]);
            Position p1 = Position.create(lat1, lon1);
            Position p2 = Position.create(lat2, lon2);
            return BoundingBox.create(p1, p2, CoordinateSystem.CARTESIAN);
        }
        return null;
    }

    private static Interval findInterval(UriInfo info) {
        String interval = QueryParameterValidators.getParameter(info, "interval", null);
        if (interval == null) {
            return null;
            // throw new
            // IllegalArgumentException("Must define kmlSnapshotAt least one interval");
        }
        return DateTimeUtil.toInterval(interval);
    }

    private static boolean findCreateSituationFolder(UriInfo info) {
        return QueryParameterValidators.getParameter(info, "situationFolderEnabled", null) != null;
    }

    private static boolean findCreateMovementsFolder(UriInfo info) {
        return QueryParameterValidators.getParameter(info, "movementsFolderEnabled", null) != null;
    }

    private static boolean findCreateTracksFolder(UriInfo info) {
        return QueryParameterValidators.getParameter(info, "tracksFolderEnabled", null) != null;
    }

    private static DateTime findAt(UriInfo info) {
        String at = QueryParameterValidators.getParameter(info, "at", null);
        return isBlank(at) ? null : DateTime.parse(at);
    }

    @SuppressWarnings("unchecked")
    private static OutputStreamSink<AisPacket> getOutputSink(UriInfo info) {
        String output = getParameter(info, "output", "raw").toLowerCase();
        switch (output) {
        case "raw":
            return AisPacketOutputSinks.OUTPUT_TO_TEXT;
        case "table":
            String columns = getParameterWithCustomErrorMessage(info, "columns",
                    "A query parameter (columns), must be present when using table output. Example: columns=time;mmsi;lat;lon");
            return AisPacketOutputSinks.newTableSink(columns, !info.getQueryParameters().containsKey("noHeader"),
                    getParameter(info, "separator", ";"));
        case "prefixed_sentences":
            return AisPacketOutputSinks.OUTPUT_PREFIXED_SENTENCES;
        case "output_to_kml":
            return AisPacketOutputSinks.OUTPUT_TO_KML();
        case "kml":
            return AisPacketOutputSinks.OUTPUT_TO_KML();

        case "jsonobject":
            String format = getParameter(info, "columns", "");
            if (format.equals("")) {
                format = AisPacketOutputSinkJsonObject.ALLCOLUMNS;
            }
            return AisPacketOutputSinks.jsonObjectSink(format);
        case "json":
            return AisPacketOutputSinks.jsonMessageSink();

        // this will work for static fields like OUTPUT_TO_HTML,
        // OUTPUT_TO_TEXT
        default:
            try {
                return (OutputStreamSink<AisPacket>) AisPacketOutputSinks.class.getField(output.toUpperCase())
                        .get(null);
            } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException
                    | SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
                .entity("Unknown output format [output=" + output + "]\n").type(MediaType.TEXT_PLAIN).build());
    }

    private static Predicate<AisPacketSource> getPacketSourceFilter(UriInfo info) {
        List<String> filters = QueryParameterValidators.getParameters(info, "filter");
        if (filters.isEmpty()) {
            return null;
        }

        Predicate<AisPacketSource> p = null;
        try {
            p = AisPacketSource.createPredicate(filters.get(0));
            for (int i = 1; i < filters.size(); i++) {
                p = p.and(AisPacketSource.createPredicate(filters.get(i)));
            }
        } catch (NullPointerException e) {

        }

        return p;
    }

    private static Predicate<AisPacket> getSourceFilter(UriInfo info) {
        List<String> filters = QueryParameterValidators.getParameters(info, "filter");

        if (filters.isEmpty()) {
            return null;
        }
        Predicate<AisPacket> p = AisPacketFilters.parseSourceFilter(filters.get(0));
        for (int i = 1; i < filters.size(); i++) {
            p = p.and(AisPacketFilters.parseSourceFilter(filters.get(i)));
        }
        return p;
    }

    /**
     */
    private static Predicate<AisPacket> getPacketFilter(UriInfo info) {
        List<String> filters = QueryParameterValidators.getParameters(info, "filter");
        if (filters.isEmpty()) {
            return null;
        }

        Predicate<AisPacket> p = AisPacketFilters.parseExpressionFilter(filters.get(0));
        for (int i = 1; i < filters.size(); i++) {
            p = p.and(AisPacketFilters.parseExpressionFilter(filters.get(i)));
        }
        return p;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("QueryParameterHelper{");
        sb.append("area=").append(area);
        sb.append(", interval=").append(interval);
        sb.append(", kmlSnapshotAt=").append(kmlSnapshotAt);
        sb.append(", limit=").append(limit);
        sb.append(", minDistance=").append(minDistance);
        sb.append(", minDuration=").append(minDuration);
        sb.append(", primaryMmsi=").append(primaryMmsi);
        sb.append(", secondaryMmsi=").append(secondaryMmsi);
        sb.append(", title='").append(title).append('\'');
        sb.append(", description='").append(description).append('\'');
        sb.append(", interpolationStepSecs=").append(interpolationStepSecs);
        sb.append(", timeToRun=").append(timeToRun);
        sb.append(", mmsis=");
        if (mmsis == null) {
            sb.append("null");
        } else {
            sb.append('[');
            for (int i = 0; i < mmsis.length; ++i) {
                sb.append(i == 0 ? "" : ", ").append(mmsis[i]);
            }
            sb.append(']');
        }
        sb.append(", outputSink=").append(outputSink);
        sb.append(", sourceFilter=").append(sourceFilter);
        sb.append(", uriInfo=").append(uriInfo);
        sb.append(", jobId='").append(jobId).append('\'');
        sb.append(", createSituationFolder=").append(createSituationFolder);
        sb.append(", createMovementsFolder=").append(createMovementsFolder);
        sb.append(", createTracksFolder=").append(createTracksFolder);
        sb.append('}');
        return sb.toString();
    }
}