/** * Copyright (C) 2012-2017 52°North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If the program is linked with libraries which are licensed under one of * the following licenses, the combination of the program with the linked * library is not considered a "derivative work" of the program: * * - Apache License, version 2.0 * - Apache Software License, version 1.0 * - GNU Lesser General Public License, version 3 * - Mozilla Public License, versions 1.0, 1.1 and 2.0 * - Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed * under the aforementioned licenses, is permitted by the copyright holders * if the distribution is compliant with both the GNU General Public * License version 2 and the aforementioned licenses. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. */ package org.n52.sos.ds.hibernate.util; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.hibernate.Session; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Restrictions; import org.joda.time.DateTime; import org.n52.sos.ds.FeatureQueryHandlerQueryObject; import org.n52.sos.exception.ows.NoApplicableCodeException; import org.n52.sos.exception.ows.concrete.UnsupportedOperatorException; import org.n52.sos.exception.ows.concrete.UnsupportedTimeException; import org.n52.sos.exception.ows.concrete.UnsupportedValueReferenceException; import org.n52.sos.ogc.filter.FilterConstants.TimeOperator; import org.n52.sos.ogc.filter.SpatialFilter; import org.n52.sos.ogc.filter.TemporalFilter; import org.n52.sos.ogc.gml.time.Time; import org.n52.sos.ogc.gml.time.TimeInstant; import org.n52.sos.ogc.gml.time.TimePeriod; import org.n52.sos.ogc.ows.OwsExceptionReport; import org.n52.sos.request.SpatialFeatureQueryRequest; import org.n52.sos.service.Configurator; import org.n52.sos.util.CollectionHelper; import com.google.common.collect.Lists; /** * @since 4.0.0 * */ public class QueryHelper { private static final int LIMIT_EXPRESSION_DEPTH = 1000; private QueryHelper() { } public static Set<String> getFeatures(final SpatialFeatureQueryRequest request, final Session session) throws OwsExceptionReport { if (request.hasSpatialFilteringProfileSpatialFilter()) { return QueryHelper.getFeatureIdentifier(null, request.getFeatureIdentifiers(), session); } else { return QueryHelper.getFeatureIdentifier(request.getSpatialFilter(), request.getFeatureIdentifiers(), session); } } public static Set<String> getFeatureIdentifier(SpatialFilter spatialFilter, List<String> featureIdentifier, Session session) throws OwsExceptionReport { Set<String> foiIDs = null; // spatial filter if (spatialFilter != null) { if (spatialFilter.getValueReference().contains("om:featureOfInterest") && spatialFilter.getValueReference().contains("sams:shape")) { foiIDs = new HashSet<String>(Configurator .getInstance() .getFeatureQueryHandler() .getFeatureIDs( new FeatureQueryHandlerQueryObject().addSpatialFilter(spatialFilter) .setConnection(session))); } else { throw new NoApplicableCodeException() .withMessage("The requested valueReference for spatial filters is not supported by this server!"); } } // feature of interest if (CollectionHelper.isNotEmpty(featureIdentifier)) { if (foiIDs == null) { foiIDs = new HashSet<String>(featureIdentifier); } else { Set<String> tempFoiIDs = new HashSet<String>(); for (String foiID : featureIdentifier) { if (foiIDs.contains(foiID)) { tempFoiIDs.add(foiID); } } foiIDs = tempFoiIDs; } } return foiIDs; } /** * Get Criterion for DescribeSensor validTime parameter. * * @param validTime * ValidTime parameter value * @return Criterion with temporal filters * @throws UnsupportedTimeException * If the time value is invalid * @throws UnsupportedValueReferenceException * If the valueReference is not supported * @throws UnsupportedOperatorException * If the temporal operator is not supported */ public static Criterion getValidTimeCriterion(Time validTime) throws UnsupportedTimeException, UnsupportedValueReferenceException, UnsupportedOperatorException { if (validTime instanceof TimeInstant) { return TemporalRestrictions.filter(getFiltersForTimeInstant((TimeInstant) validTime)); } else if (validTime instanceof TimePeriod) { return TemporalRestrictions.filter(getFiltersForTimePeriod(validTime)); } return null; } /** * Get temporal filters for validTime TimeInstant * * @param validTime * TimeInstant * @return Collection with temporal filters */ private static Collection<TemporalFilter> getFiltersForTimeInstant(TimeInstant validTime) { if (validTime.isSetIndeterminateValue()) { final List<TemporalFilter> filters = Lists.newLinkedList(); switch (validTime.getIndeterminateValue()) { case after: filters.add(new TemporalFilter(TimeOperator.TM_After, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE)); break; case before: filters.add(new TemporalFilter(TimeOperator.TM_Before, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE)); break; case now: validTime.setValue(new DateTime()); return getDefautlTimeInstantFilters(validTime); default: return getDefautlTimeInstantFilters(validTime); } return filters; } else { return getDefautlTimeInstantFilters(validTime); } } /** * Get default filters for valid TimeInstant * * @param validTime * TimeInstant * @return default filters */ private static Collection<TemporalFilter> getDefautlTimeInstantFilters(TimeInstant validTime) { return Lists.newArrayList(new TemporalFilter(TimeOperator.TM_EndedBy, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_Contains, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter(TimeOperator.TM_Equals, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_BegunBy, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE)); } /** * Get temporal filters for validTime TimePeriod * * @param validTime * TimePeriod * * @return Collection with temporal filters */ private static Collection<TemporalFilter> getFiltersForTimePeriod(Time validTime) { return Lists.newArrayList(new TemporalFilter(TimeOperator.TM_Meets, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_Overlaps, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter(TimeOperator.TM_Begins, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_BegunBy, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_During, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_Contains, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_Equals, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_OverlappedBy, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_Ends, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_EndedBy, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE), new TemporalFilter( TimeOperator.TM_MetBy, validTime, TemporalRestrictions.VALID_DESCRIBE_SENSOR_TIME_VALUE_REFERENCE)); } /** * Creates a criterion for identifiers, considers if size is > 1000 (Oracle expression * limit). * * @param propertyName * Column name. * @param identifiers * Identifiers list * @return Criterion. */ public static Criterion getCriterionForIdentifiers(String propertyName, Collection<String> identifiers) { if (identifiers.size() >= LIMIT_EXPRESSION_DEPTH) { List<String> identifiersList = Lists.newArrayList(identifiers); Criterion criterion = null; List<String> ids = null; for (int i = 0; i < identifiersList.size(); i++) { if (i == 0 || i % (LIMIT_EXPRESSION_DEPTH - 1) == 0) { if (criterion == null && i != 0) { criterion = Restrictions.in(propertyName, ids); } else if (criterion != null) { criterion = Restrictions.or(criterion, Restrictions.in(propertyName, ids)); } ids = Lists.newArrayList(); ids.add(identifiersList.get(i)); } else { ids.add(identifiersList.get(i)); } } return criterion; } else { return Restrictions.in(propertyName, identifiers); } } /** * Creates a criterion for objects, considers if size is > 1000 (Oracle expression * limit). * * @param propertyName * Column name. * @param identifiers * Objects list * @return Criterion. */ public static Criterion getCriterionForObjects(String propertyName, Collection<?> identifiers) { if (identifiers.size() >= LIMIT_EXPRESSION_DEPTH) { List<?> identifiersList = Lists.newArrayList(identifiers); Criterion criterion = null; List<Object> ids = null; for (int i = 0; i < identifiersList.size(); i++) { if (i == 0 || i % (LIMIT_EXPRESSION_DEPTH - 1) == 0) { if (criterion == null && i != 0) { criterion = Restrictions.in(propertyName, ids); } else if (criterion != null) { criterion = Restrictions.or(criterion, Restrictions.in(propertyName, ids)); } ids = Lists.newArrayList(); ids.add(identifiersList.get(i)); } else { ids.add(identifiersList.get(i)); } } return criterion; } else { return Restrictions.in(propertyName, identifiers); } } }