| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.sis.filter; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.Date; |
| import java.util.Calendar; |
| import java.time.Instant; |
| import java.time.LocalTime; |
| import java.time.OffsetTime; |
| import java.time.LocalDateTime; |
| import java.time.OffsetDateTime; |
| import java.time.ZonedDateTime; |
| import java.time.ZoneId; |
| import java.time.chrono.ChronoLocalDate; |
| import java.time.chrono.ChronoLocalDateTime; |
| import java.time.chrono.ChronoZonedDateTime; |
| import java.time.temporal.ChronoField; |
| import java.time.temporal.Temporal; |
| import org.apache.sis.math.Fraction; |
| import org.apache.sis.util.ArgumentChecks; |
| |
| // Branch-dependent imports |
| import org.opengis.filter.MatchAction; |
| import org.opengis.filter.expression.Expression; |
| import org.opengis.filter.BinaryComparisonOperator; |
| import org.opengis.filter.FilterVisitor; |
| |
| |
| /** |
| * Comparison operators between two values. Values are converted to the same before comparison, using a widening conversion |
| * (for example from {@link Integer} to {@link Double}). If values can not be compared because they can not be converted to |
| * a common type, or because a value is null or NaN, then the comparison result if {@code false}. A consequence of this rule |
| * is that the two conditions {@literal A < B} and {@literal A ≧ B} may be false in same time. |
| * |
| * <p>If one operand is a collection, all collection elements may be compared to the other value. |
| * Null elements in the collection (not to be confused with null operands) are ignored. |
| * If both operands are collections, current implementation returns {@code false}.</p> |
| * |
| * <p>Comparisons between temporal objects are done with {@code isBefore(…)} or {@code isAfter(…)} methods when they |
| * have a different semantic than the {@code compareTo(…)} methods. If the two temporal objects are not of the same |
| * type, only the fields that are common two both types are compared. For example comparison between {@code LocalDate} |
| * and {@code LocalDateTime} ignores the time fields.</p> |
| * |
| * <p>Comparisons of numerical types shall be done by overriding one of the {@code applyAs…} methods and |
| * returning 0 if {@code false} or 1 if {@code true}. Comparisons of other types is done by overriding |
| * the {@code compare(…)} methods.</p> |
| * |
| * @author Johann Sorel (Geomatys) |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 1.1 |
| * @since 1.1 |
| * @module |
| */ |
| abstract class ComparisonFunction extends BinaryFunction implements BinaryComparisonOperator { |
| /** |
| * For cross-version compatibility. |
| */ |
| private static final long serialVersionUID = 1228683039737814926L; |
| |
| /** |
| * Specifies whether comparisons are case sensitive. |
| */ |
| private final boolean isMatchingCase; |
| |
| /** |
| * Specifies how the comparisons shall be evaluated for a collection of values. |
| * Values can be ALL, ANY or ONE. |
| */ |
| private final MatchAction matchAction; |
| |
| /** |
| * Creates a new comparator. |
| * |
| * @param expression1 the first of the two expressions to be used by this comparator. |
| * @param expression2 the second of the two expressions to be used by this comparator. |
| * @param isMatchingCase specifies whether comparisons are case sensitive. |
| * @param matchAction specifies how the comparisons shall be evaluated for a collection of values. |
| */ |
| ComparisonFunction(final Expression expression1, final Expression expression2, final boolean isMatchingCase, final MatchAction matchAction) { |
| super(expression1, expression2); |
| this.isMatchingCase = isMatchingCase; |
| this.matchAction = matchAction; |
| ArgumentChecks.ensureNonNull("matchAction", matchAction); |
| } |
| |
| /** |
| * Returns whether comparisons are case sensitive. |
| */ |
| @Override |
| public final boolean isMatchingCase() { |
| return isMatchingCase; |
| } |
| |
| /** |
| * Returns how the comparisons are evaluated for a collection of values. |
| */ |
| @Override |
| public final MatchAction getMatchAction() { |
| return matchAction; |
| } |
| |
| /** |
| * Takes in account the additional properties in hash code calculation. |
| */ |
| @Override |
| public final int hashCode() { |
| return super.hashCode() + Boolean.hashCode(isMatchingCase) + 61 * matchAction.hashCode(); |
| } |
| |
| /** |
| * Takes in account the additional properties in object comparison. |
| */ |
| @Override |
| public final boolean equals(final Object obj) { |
| if (super.equals(obj)) { |
| final ComparisonFunction other = (ComparisonFunction) obj; |
| return other.isMatchingCase == isMatchingCase && matchAction.equals(other.matchAction); |
| } |
| return false; |
| } |
| |
| /** |
| * Determines if the test(s) represented by this filter passes with the given operands. |
| * Values of {@link #expression1} and {@link #expression2} can be two single values, |
| * or at most one expression can produce a collection. |
| */ |
| @Override |
| public final boolean evaluate(final Object candidate) { |
| final Object left = expression1.evaluate(candidate); |
| if (left != null) { |
| final Object right = expression2.evaluate(candidate); |
| if (right != null) { |
| final Iterable<?> collection; |
| final boolean collectionFirst = (left instanceof Iterable<?>); |
| if (collectionFirst) { |
| if (right instanceof Iterable<?>) { |
| // Current implementation does not support collection on both sides. See class javadoc. |
| return false; |
| } |
| collection = (Iterable<?>) left; |
| } else if (right instanceof Iterable<?>) { |
| collection = (Iterable<?>) right; |
| } else { |
| return evaluate(left, right); |
| } |
| /* |
| * At this point, exactly one of the operands is a collection. It may be the left or right one. |
| * All values in the collection may be compared to the other value until match condition is met. |
| * Null elements in the collection are ignored. |
| */ |
| boolean match = false; |
| for (final Object element : collection) { |
| if (element != null) { |
| final boolean pass; |
| if (collectionFirst) { |
| pass = evaluate(element, right); |
| } else { |
| pass = evaluate(left, element); |
| } |
| switch (matchAction) { |
| default: return false; // Unknown enumeration. |
| case ALL: { |
| if (!pass) return false; |
| match = true; // Remember that we have at least 1 value. |
| break; |
| } |
| case ANY: { |
| if (pass) return true; |
| break; // `match` still false since no match. |
| } |
| case ONE: { |
| if (pass) { |
| if (match) return false; // If a value has been found previously. |
| match = true; // Remember that we have exactly one value. |
| } |
| } |
| } |
| } |
| } |
| return match; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Compares the given objects. If both values are numerical, then this method delegates to an {@code applyAs…} method. |
| * For other kind of objects, this method delegates to a {@code compare(…)} method. If the two objects are not of the |
| * same type, then the less accurate one is converted to the most accurate type if possible. |
| * |
| * @param left the first object to compare. Must be non-null. |
| * @param right the second object to compare. Must be non-null. |
| */ |
| @SuppressWarnings("null") |
| private boolean evaluate(Object left, Object right) { |
| /* |
| * For numbers, the apply(…) method inherited from parent class will delegate to specialized methods like |
| * applyAsDouble(…). All implementations of those specialized methods in ComparisonFunction return integer, |
| * so call to intValue() will not cause information lost. |
| */ |
| if (left instanceof Number && right instanceof Number) { |
| final Number r = apply((Number) left, (Number) right); |
| if (r != null) return r.intValue() != 0; |
| } |
| /* |
| * For legacy java.util.Date, the compareTo(…) method is consistent only for dates of the same class. |
| * Otherwise A.compareTo(B) and B.compareTo(A) are inconsistent if one object is a java.util.Date and |
| * the other object is a java.sql.Timestamp. In such case, we compare the dates as java.time objects. |
| */ |
| if (left instanceof Date && right instanceof Date) { |
| if (left.getClass() == right.getClass()) { |
| return fromCompareTo(((Date) left).compareTo((Date) right)); |
| } |
| left = fromLegacy((Date) left); |
| right = fromLegacy((Date) right); |
| } |
| /* |
| * Temporal objects have complex conversion rules. We take Instant as the most accurate and unambiguous type. |
| * So if at least one value is an Instant, try to unconditionally promote the other value to an Instant too. |
| * This conversion will fail if the other object has some undefined fields; for example java.sql.Date has no |
| * time fields (we do not assume that the values of those fields are zero). |
| * |
| * OffsetTime and OffsetDateTime are final classes that do not implement a java.time.chrono interface. |
| * Note that OffsetDateTime is convertible into OffsetTime by dropping the date fields, but we do not |
| * (for now) perform comparaisons that would ignore the date fields of an operand. |
| */ |
| if (left instanceof Temporal || right instanceof Temporal) { // Use || because an operand may be Date. |
| if (left instanceof Instant) { |
| final Instant t = toInstant(right); |
| if (t != null) return fromCompareTo(((Instant) left).compareTo(t)); |
| } else if (right instanceof Instant) { |
| final Instant t = toInstant(left); |
| if (t != null) return fromCompareTo(t.compareTo((Instant) right)); |
| } else if (left instanceof OffsetDateTime) { |
| final OffsetDateTime t = toOffsetDateTime(right); |
| if (t != null) return compare((OffsetDateTime) left, t); |
| } else if (right instanceof OffsetDateTime) { |
| final OffsetDateTime t = toOffsetDateTime(left); |
| if (t != null) return compare(t, (OffsetDateTime) right); |
| } else if (left instanceof OffsetTime && right instanceof OffsetTime) { |
| return compare((OffsetTime) left, (OffsetTime) right); |
| } |
| /* |
| * Comparisons of temporal objects implementing java.time.chrono interfaces. We need to check the most |
| * complete types first. If the type are different, we reduce to the type of the less smallest operand. |
| * For example if an operand is a date+time and the other operand is only a date, then the time fields |
| * will be ignored and a warning will be reported. |
| */ |
| if (left instanceof ChronoLocalDateTime<?>) { |
| final ChronoLocalDateTime<?> t = toLocalDateTime(right); |
| if (t != null) return compare((ChronoLocalDateTime<?>) left, t); |
| } else if (right instanceof ChronoLocalDateTime<?>) { |
| final ChronoLocalDateTime<?> t = toLocalDateTime(left); |
| if (t != null) return compare(t, (ChronoLocalDateTime<?>) right); |
| } |
| if (left instanceof ChronoLocalDate) { |
| final ChronoLocalDate t = toLocalDate(right); |
| if (t != null) return compare((ChronoLocalDate) left, t); |
| } else if (right instanceof ChronoLocalDate) { |
| final ChronoLocalDate t = toLocalDate(left); |
| if (t != null) return compare(t, (ChronoLocalDate) right); |
| } |
| if (left instanceof LocalTime) { |
| final LocalTime t = toLocalTime(right); |
| if (t != null) return fromCompareTo(((LocalTime) left).compareTo(t)); |
| } else if (right instanceof LocalTime) { |
| final LocalTime t = toLocalTime(left); |
| if (t != null) return fromCompareTo(t.compareTo((LocalTime) right)); |
| } |
| } |
| /* |
| * Test character strings only after all specialized types have been tested. The intent is that if an |
| * object implements both CharSequence and a specialized interface, they have been compared as value |
| * objects before to be compared as strings. |
| */ |
| if (left instanceof CharSequence || right instanceof CharSequence) { // Really ||, not &&. |
| final String s1 = left.toString(); |
| final String s2 = right.toString(); |
| final int result; |
| if (isMatchingCase) { |
| result = s1.compareTo(s2); |
| } else { |
| result = s1.compareToIgnoreCase(s2); // TODO: use Collator for taking locale in account. |
| } |
| return fromCompareTo(result); |
| } |
| /* |
| * Comparison using `compareTo` method should be last because it does not take in account |
| * the `isMatchingCase` flag and because the semantic is different than < or > comparator |
| * for numbers and dates. |
| */ |
| if (left.getClass() == right.getClass() && (left instanceof Comparable<?>)) { |
| @SuppressWarnings("unchecked") |
| final int result = ((Comparable) left).compareTo(right); |
| return fromCompareTo(result); |
| } |
| // TODO: report a warning for non-comparable objects. |
| return false; |
| } |
| |
| /** |
| * Converts a legacy {@code Date} object to an object from the {@link java.time} package. |
| * We performs this conversion before to compare to {@code Date} instances that are not of |
| * the same class, because the {@link Date#compareTo(Date)} method in such case is not well |
| * defined. |
| */ |
| private static Temporal fromLegacy(final Date value) { |
| if (value instanceof java.sql.Timestamp) { |
| return ((java.sql.Timestamp) value).toLocalDateTime(); |
| } else if (value instanceof java.sql.Date) { |
| return ((java.sql.Date) value).toLocalDate(); |
| } else if (value instanceof java.sql.Time) { |
| return ((java.sql.Time) value).toLocalTime(); |
| } else { |
| // Implementation of above toFoo() methods use system default time zone. |
| return LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault()); |
| } |
| } |
| |
| /** |
| * Converts the given object to an {@link Instant}, or returns {@code null} if unconvertible. |
| * This method handles a few types from the {@link java.time} package and legacy types like |
| * {@link Date} (with a special case for SQL dates) and {@link Calendar}. |
| */ |
| static Instant toInstant(final Object value) { |
| if (value instanceof Instant) { |
| return (Instant) value; |
| } else if (value instanceof OffsetDateTime) { |
| return ((OffsetDateTime) value).toInstant(); |
| } else if (value instanceof ChronoZonedDateTime) { |
| return ((ChronoZonedDateTime) value).toInstant(); |
| } else if (value instanceof Date) { |
| try { |
| return ((Date) value).toInstant(); |
| } catch (UnsupportedOperationException e) { |
| /* |
| * java.sql.Date and java.sql.Time can not be converted to Instant because a part |
| * of their coordinates on the timeline is undefined. For example in the case of |
| * java.sql.Date the hours, minutes and seconds are unspecified (which is not the |
| * same thing than assuming that those values are zero). |
| */ |
| } |
| } else if (value instanceof Calendar) { |
| return ((Calendar) value).toInstant(); |
| } |
| return null; |
| } |
| |
| /** |
| * Converts the given object to an {@link OffsetDateTime}, or returns {@code null} if unconvertible. |
| */ |
| private static OffsetDateTime toOffsetDateTime(final Object value) { |
| if (value instanceof OffsetDateTime) { |
| return (OffsetDateTime) value; |
| } else if (value instanceof ZonedDateTime) { |
| return ((ZonedDateTime) value).toOffsetDateTime(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Converts the given object to a {@link ChronoLocalDateTime}, or returns {@code null} if unconvertible. |
| * This method handles the case of legacy SQL {@link java.sql.Timestamp} objects. |
| * Conversion may lost timezone information. |
| */ |
| private static ChronoLocalDateTime<?> toLocalDateTime(final Object value) { |
| if (value instanceof ChronoLocalDateTime<?>) { |
| return (ChronoLocalDateTime<?>) value; |
| } else if (value instanceof ChronoZonedDateTime) { |
| ignoringField(ChronoField.OFFSET_SECONDS); |
| return ((ChronoZonedDateTime) value).toLocalDateTime(); |
| } else if (value instanceof OffsetDateTime) { |
| ignoringField(ChronoField.OFFSET_SECONDS); |
| return ((OffsetDateTime) value).toLocalDateTime(); |
| } else if (value instanceof java.sql.Timestamp) { |
| return ((java.sql.Timestamp) value).toLocalDateTime(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Converts the given object to a {@link ChronoLocalDate}, or returns {@code null} if unconvertible. |
| * This method handles the case of legacy SQL {@link java.sql.Date} objects. |
| * Conversion may lost timezone information and time fields. |
| */ |
| private static ChronoLocalDate toLocalDate(final Object value) { |
| if (value instanceof ChronoLocalDate) { |
| return (ChronoLocalDate) value; |
| } else if (value instanceof ChronoLocalDateTime) { |
| ignoringField(ChronoField.SECOND_OF_DAY); |
| return ((ChronoLocalDateTime) value).toLocalDate(); |
| } else if (value instanceof ChronoZonedDateTime) { |
| ignoringField(ChronoField.SECOND_OF_DAY); |
| return ((ChronoZonedDateTime) value).toLocalDate(); |
| } else if (value instanceof OffsetDateTime) { |
| ignoringField(ChronoField.SECOND_OF_DAY); |
| return ((OffsetDateTime) value).toLocalDate(); |
| } else if (value instanceof java.sql.Date) { |
| return ((java.sql.Date) value).toLocalDate(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Converts the given object to a {@link LocalTime}, or returns {@code null} if unconvertible. |
| * This method handles the case of legacy SQL {@link java.sql.Time} objects. |
| * Conversion may lost timezone information. |
| */ |
| private static LocalTime toLocalTime(final Object value) { |
| if (value instanceof LocalTime) { |
| return (LocalTime) value; |
| } else if (value instanceof OffsetTime) { |
| ignoringField(ChronoField.OFFSET_SECONDS); |
| return ((OffsetTime) value).toLocalTime(); |
| } else if (value instanceof java.sql.Time) { |
| return ((java.sql.Time) value).toLocalTime(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Invoked when a conversion cause a field to be ignored. For example if a "date+time" object is compared |
| * with a "date" object, the "time" field is ignored. Expected values are: |
| * |
| * <ul> |
| * <li>{@link ChronoField#OFFSET_SECONDS}: time zone is ignored.</li> |
| * <li>{@link ChronoField#SECOND_OF_DAY}: time of dat and time zone are ignored.</li> |
| * </ul> |
| * |
| * @param field the field which is ignored. |
| * |
| * @see <a href="https://issues.apache.org/jira/browse/SIS-460">SIS-460</a> |
| */ |
| private static void ignoringField(final ChronoField field) { |
| // TODO |
| } |
| |
| /** |
| * Converts the boolean result as an integer for use as a return value of the {@code applyAs…} methods. |
| * This is a helper class for subclasses. |
| */ |
| private static Number number(final boolean result) { |
| return result ? 1 : 0; |
| } |
| |
| /** |
| * Converts the result of {@link Comparable#compareTo(Object)}. |
| */ |
| protected abstract boolean fromCompareTo(int result); |
| |
| /** |
| * Compares two times with time-zone information. Implementations shall not use {@code compareTo(…)} because |
| * that method compares more information than desired in order to ensure consistency with {@code equals(…)}. |
| */ |
| protected abstract boolean compare(OffsetTime left, OffsetTime right); |
| |
| /** |
| * Compares two dates with time-zone information. Implementations shall not use {@code compareTo(…)} because |
| * that method compares more information than desired in order to ensure consistency with {@code equals(…)}. |
| */ |
| protected abstract boolean compare(OffsetDateTime left, OffsetDateTime right); |
| |
| /** |
| * Compares two dates without time-of-day and time-zone information. Implementations shall not use |
| * {@code compareTo(…)} because that method also compares chronology, which is not desired for the |
| * purpose of "is before" or "is after" comparison functions. |
| */ |
| protected abstract boolean compare(ChronoLocalDate left, ChronoLocalDate right); |
| |
| /** |
| * Compares two dates without time-zone information. Implementations shall not use {@code compareTo(…)} |
| * because that method also compares chronology, which is not desired for the purpose of "is before" or |
| * "is after" comparison functions. |
| */ |
| protected abstract boolean compare(ChronoLocalDateTime<?> left, ChronoLocalDateTime<?> right); |
| |
| /** |
| * Compares two dates with time-zone information. Implementations shall not use {@code compareTo(…)} |
| * because that method also compares chronology, which is not desired for the purpose of "is before" |
| * or "is after" comparison functions. |
| */ |
| protected abstract boolean compare(ChronoZonedDateTime<?> left, ChronoZonedDateTime<?> right); |
| |
| /** Delegates to {@link BigDecimal#compareTo(BigDecimal)} and interprets the result with {@link #fromCompareTo(int)}. */ |
| @Override protected final Number applyAsDecimal (BigDecimal left, BigDecimal right) {return number(fromCompareTo(left.compareTo(right)));} |
| @Override protected final Number applyAsInteger (BigInteger left, BigInteger right) {return number(fromCompareTo(left.compareTo(right)));} |
| @Override protected final Number applyAsFraction(Fraction left, Fraction right) {return number(fromCompareTo(left.compareTo(right)));} |
| |
| |
| /** |
| * The {@value #NAME} {@literal (<)} filter. |
| */ |
| static final class LessThan extends ComparisonFunction implements org.opengis.filter.PropertyIsLessThan { |
| /** For cross-version compatibility during (de)serialization. */ |
| private static final long serialVersionUID = 6126039112844823196L; |
| |
| /** Creates a new filter for the {@value #NAME} operation. */ |
| LessThan(Expression expression1, Expression expression2, boolean isMatchingCase, MatchAction matchAction) { |
| super(expression1, expression2, isMatchingCase, matchAction); |
| } |
| |
| /** Identification of this operation. */ |
| @Override protected String getName() {return NAME;} |
| @Override protected char symbol() {return '<';} |
| |
| /** Converts {@link Comparable#compareTo(Object)} result to this filter result. */ |
| @Override protected boolean fromCompareTo(final int result) {return result < 0;} |
| |
| /** Performs the comparison and returns the result as 0 (false) or 1 (true). */ |
| @Override protected Number applyAsDouble(double left, double right) {return number(left < right);} |
| @Override protected Number applyAsLong (long left, long right) {return number(left < right);} |
| @Override protected boolean compare (OffsetTime left, OffsetTime right) {return left.isBefore(right);} |
| @Override protected boolean compare (OffsetDateTime left, OffsetDateTime right) {return left.isBefore(right);} |
| @Override protected boolean compare (ChronoLocalDate left, ChronoLocalDate right) {return left.isBefore(right);} |
| @Override protected boolean compare (ChronoLocalDateTime<?> left, ChronoLocalDateTime<?> right) {return left.isBefore(right);} |
| @Override protected boolean compare (ChronoZonedDateTime<?> left, ChronoZonedDateTime<?> right) {return left.isBefore(right);} |
| |
| /** Implementation of the visitor pattern (not used by Apache SIS). */ |
| @Override public Object accept(FilterVisitor visitor, Object extraData) { |
| return visitor.visit(this, extraData); |
| } |
| } |
| |
| |
| /** |
| * The {@value #NAME} (≤) filter. |
| */ |
| static final class LessThanOrEqualTo extends ComparisonFunction implements org.opengis.filter.PropertyIsLessThanOrEqualTo { |
| /** For cross-version compatibility during (de)serialization. */ |
| private static final long serialVersionUID = 6357459227911760871L; |
| |
| /** Creates a new filter for the {@value #NAME} operation. */ |
| LessThanOrEqualTo(Expression expression1, Expression expression2, boolean isMatchingCase, MatchAction matchAction) { |
| super(expression1, expression2, isMatchingCase, matchAction); |
| } |
| |
| /** Identification of this operation. */ |
| @Override protected String getName() {return NAME;} |
| @Override protected char symbol() {return '≤';} |
| |
| /** Converts {@link Comparable#compareTo(Object)} result to this filter result. */ |
| @Override protected boolean fromCompareTo(final int result) {return result <= 0;} |
| |
| /** Performs the comparison and returns the result as 0 (false) or 1 (true). */ |
| @Override protected Number applyAsDouble(double left, double right) {return number(left <= right);} |
| @Override protected Number applyAsLong (long left, long right) {return number(left <= right);} |
| @Override protected boolean compare (OffsetTime left, OffsetTime right) {return !left.isAfter(right);} |
| @Override protected boolean compare (OffsetDateTime left, OffsetDateTime right) {return !left.isAfter(right);} |
| @Override protected boolean compare (ChronoLocalDate left, ChronoLocalDate right) {return !left.isAfter(right);} |
| @Override protected boolean compare (ChronoLocalDateTime<?> left, ChronoLocalDateTime<?> right) {return !left.isAfter(right);} |
| @Override protected boolean compare (ChronoZonedDateTime<?> left, ChronoZonedDateTime<?> right) {return !left.isAfter(right);} |
| |
| /** Implementation of the visitor pattern (not used by Apache SIS). */ |
| @Override public Object accept(FilterVisitor visitor, Object extraData) { |
| return visitor.visit(this, extraData); |
| } |
| } |
| |
| |
| /** |
| * The {@value #NAME} {@literal (>)} filter. |
| */ |
| static final class GreaterThan extends ComparisonFunction implements org.opengis.filter.PropertyIsGreaterThan { |
| /** For cross-version compatibility during (de)serialization. */ |
| private static final long serialVersionUID = 8605517892232632586L; |
| |
| /** Creates a new filter for the {@value #NAME} operation. */ |
| GreaterThan(Expression expression1, Expression expression2, boolean isMatchingCase, MatchAction matchAction) { |
| super(expression1, expression2, isMatchingCase, matchAction); |
| } |
| |
| /** Identification of this operation. */ |
| @Override protected String getName() {return NAME;} |
| @Override protected char symbol() {return '>';} |
| |
| /** Converts {@link Comparable#compareTo(Object)} result to this filter result. */ |
| @Override protected boolean fromCompareTo(final int result) {return result > 0;} |
| |
| /** Performs the comparison and returns the result as 0 (false) or 1 (true). */ |
| @Override protected Number applyAsDouble(double left, double right) {return number(left > right);} |
| @Override protected Number applyAsLong (long left, long right) {return number(left > right);} |
| @Override protected boolean compare (OffsetTime left, OffsetTime right) {return left.isAfter(right);} |
| @Override protected boolean compare (OffsetDateTime left, OffsetDateTime right) {return left.isAfter(right);} |
| @Override protected boolean compare (ChronoLocalDate left, ChronoLocalDate right) {return left.isAfter(right);} |
| @Override protected boolean compare (ChronoLocalDateTime<?> left, ChronoLocalDateTime<?> right) {return left.isAfter(right);} |
| @Override protected boolean compare (ChronoZonedDateTime<?> left, ChronoZonedDateTime<?> right) {return left.isAfter(right);} |
| |
| /** Implementation of the visitor pattern (not used by Apache SIS). */ |
| @Override public Object accept(FilterVisitor visitor, Object extraData) { |
| return visitor.visit(this, extraData); |
| } |
| } |
| |
| |
| /** |
| * The {@value #NAME} (≥) filter. |
| */ |
| static final class GreaterThanOrEqualTo extends ComparisonFunction implements org.opengis.filter.PropertyIsGreaterThanOrEqualTo { |
| /** For cross-version compatibility during (de)serialization. */ |
| private static final long serialVersionUID = 1514185657159141882L; |
| |
| /** Creates a new filter for the {@value #NAME} operation. */ |
| GreaterThanOrEqualTo(Expression expression1, Expression expression2, boolean isMatchingCase, MatchAction matchAction) { |
| super(expression1, expression2, isMatchingCase, matchAction); |
| } |
| |
| /** Identification of this operation. */ |
| @Override protected String getName() {return NAME;} |
| @Override protected char symbol() {return '≥';} |
| |
| /** Converts {@link Comparable#compareTo(Object)} result to this filter result. */ |
| @Override protected boolean fromCompareTo(final int result) {return result >= 0;} |
| |
| /** Performs the comparison and returns the result as 0 (false) or 1 (true). */ |
| @Override protected Number applyAsDouble(double left, double right) {return number(left >= right);} |
| @Override protected Number applyAsLong (long left, long right) {return number(left >= right);} |
| @Override protected boolean compare (OffsetTime left, OffsetTime right) {return !left.isBefore(right);} |
| @Override protected boolean compare (OffsetDateTime left, OffsetDateTime right) {return !left.isBefore(right);} |
| @Override protected boolean compare (ChronoLocalDate left, ChronoLocalDate right) {return !left.isBefore(right);} |
| @Override protected boolean compare (ChronoLocalDateTime<?> left, ChronoLocalDateTime<?> right) {return !left.isBefore(right);} |
| @Override protected boolean compare (ChronoZonedDateTime<?> left, ChronoZonedDateTime<?> right) {return !left.isBefore(right);} |
| |
| /** Implementation of the visitor pattern (not used by Apache SIS). */ |
| @Override public Object accept(FilterVisitor visitor, Object extraData) { |
| return visitor.visit(this, extraData); |
| } |
| } |
| |
| |
| /** |
| * The {@value #NAME} (=) filter. |
| */ |
| static final class EqualTo extends ComparisonFunction implements org.opengis.filter.PropertyIsEqualTo { |
| /** For cross-version compatibility during (de)serialization. */ |
| private static final long serialVersionUID = 8502612221498749667L; |
| |
| /** Creates a new filter for the {@value #NAME} operation. */ |
| EqualTo(Expression expression1, Expression expression2, boolean isMatchingCase, MatchAction matchAction) { |
| super(expression1, expression2, isMatchingCase, matchAction); |
| } |
| |
| /** Identification of this operation. */ |
| @Override protected String getName() {return NAME;} |
| @Override protected char symbol() {return '=';} |
| |
| /** Converts {@link Comparable#compareTo(Object)} result to this filter result. */ |
| @Override protected boolean fromCompareTo(final int result) {return result == 0;} |
| |
| /** Performs the comparison and returns the result as 0 (false) or 1 (true). */ |
| @Override protected Number applyAsDouble(double left, double right) {return number(left == right);} |
| @Override protected Number applyAsLong (long left, long right) {return number(left == right);} |
| @Override protected boolean compare (OffsetTime left, OffsetTime right) {return left.isEqual(right);} |
| @Override protected boolean compare (OffsetDateTime left, OffsetDateTime right) {return left.isEqual(right);} |
| @Override protected boolean compare (ChronoLocalDate left, ChronoLocalDate right) {return left.isEqual(right);} |
| @Override protected boolean compare (ChronoLocalDateTime<?> left, ChronoLocalDateTime<?> right) {return left.isEqual(right);} |
| @Override protected boolean compare (ChronoZonedDateTime<?> left, ChronoZonedDateTime<?> right) {return left.isEqual(right);} |
| |
| /** Implementation of the visitor pattern (not used by Apache SIS). */ |
| @Override public Object accept(FilterVisitor visitor, Object extraData) { |
| return visitor.visit(this, extraData); |
| } |
| } |
| |
| |
| /** |
| * The {@value #NAME} (≠) filter. |
| */ |
| static final class NotEqualTo extends ComparisonFunction implements org.opengis.filter.PropertyIsNotEqualTo { |
| /** For cross-version compatibility during (de)serialization. */ |
| private static final long serialVersionUID = -3295957142249035362L; |
| |
| /** Creates a new filter for the {@value #NAME} operation. */ |
| NotEqualTo(Expression expression1, Expression expression2, boolean isMatchingCase, MatchAction matchAction) { |
| super(expression1, expression2, isMatchingCase, matchAction); |
| } |
| |
| /** Identification of this operation. */ |
| @Override protected String getName() {return NAME;} |
| @Override protected char symbol() {return '≠';} |
| |
| /** Converts {@link Comparable#compareTo(Object)} result to this filter result. */ |
| @Override protected boolean fromCompareTo(final int result) {return result != 0;} |
| |
| /** Performs the comparison and returns the result as 0 (false) or 1 (true). */ |
| @Override protected Number applyAsDouble(double left, double right) {return number(left != right);} |
| @Override protected Number applyAsLong (long left, long right) {return number(left != right);} |
| @Override protected boolean compare (OffsetTime left, OffsetTime right) {return !left.isEqual(right);} |
| @Override protected boolean compare (OffsetDateTime left, OffsetDateTime right) {return !left.isEqual(right);} |
| @Override protected boolean compare (ChronoLocalDate left, ChronoLocalDate right) {return !left.isEqual(right);} |
| @Override protected boolean compare (ChronoLocalDateTime<?> left, ChronoLocalDateTime<?> right) {return !left.isEqual(right);} |
| @Override protected boolean compare (ChronoZonedDateTime<?> left, ChronoZonedDateTime<?> right) {return !left.isEqual(right);} |
| |
| /** Implementation of the visitor pattern (not used by Apache SIS). */ |
| @Override public Object accept(FilterVisitor visitor, Object extraData) { |
| return visitor.visit(this, extraData); |
| } |
| } |
| } |