desruisseaux commented on a change in pull request #23: URL: https://github.com/apache/sis/pull/23#discussion_r656942594
########## File path: core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java ########## @@ -0,0 +1,297 @@ +/* + * 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.util.Map; +import java.util.List; +import java.util.IdentityHashMap; +import java.util.ConcurrentModificationException; +import org.apache.sis.util.resources.Errors; + +// Branch-dependent imports +import org.opengis.filter.Filter; +import org.opengis.filter.Literal; +import org.opengis.filter.Expression; + + +/** + * Description of optimizations or simplifications to attempt on filters and expressions. + * Optimizations can include the following changes: + * + * <ul> + * <li>Application of some logical identities such as {@code NOT(NOT(A)) == A}.</li> + * <li>Application of logical short circuits such as {@code A & FALSE == FALSE}.</li> + * <li>Immediate evaluation of expressions where all parameters are literal values.</li> + * </ul> + * + * Current version does not yet provide configuration options. + * But this class is the place where such options may be added in the future. + * + * <p>This class is <strong>not</strong> thread-safe. A new instance shall be created + * for each thread applying optimizations. Example:</p> + * + * {@preformat java + * Filter<R> filter = ...; + * filter = new Optimization().apply(filter); + * } + * + * <h2>How optimizations are applied</h2> + * Optimizations are specific to each expression and filter type. + * For optimizations to happen, classes must implement the {@link OnExpression} or {@link OnFilter} interface. + * The {@link #apply(Filter)} and {@link #apply(Expression)} methods in this {@code Optimization} class merely + * delegate to the methods defined in above-cited interfaces, with safety guards against infinite recursivity. + * + * <h2>Behavioral changes</h2> + * Optimized filters shall produce the same results than non-optimized filters. + * However side-effects may differ, in particular regarding exceptions that may be thrown. + * For example if a filter tests {@code A & B} and if {@code Optimization} determines that the {@code B} + * condition will always evaluate to {@code false}, then the {@code A} condition will never be tested. + * If that condition had side-effects or threw an exception, + * those effects will disappear in the optimized filter. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.1 + * @since 1.1 + * @module + */ +public class Optimization { + /** + * An arbitrary object meaning that a filter or expression optimization in under progress. + */ + private static final Object COMPUTING = Void.TYPE; + + /** + * Filters and expressions already optimized. Also used for avoiding never-ending loops. + * The map is created when first needed. + * + * <div class="note"><b>Note:</b> the same map is used for filters and expressions. + * It is not a problem if keys do not implement the two interfaces in same time. + * If it happens anyway, it should still be okay because the method signatures are + * the same in both interfaces (only the return type changes), so the same methods + * would be invoked no matter if we consider the keys as a filter or an expression.</div> + */ + private Map<Object,Object> done; + + /** + * Creates a new instance. + */ + public Optimization() { + } + + /** + * Optimizes or simplifies the given filter. If the given instance implements the {@link OnFilter} interface, + * then its {@code optimize(this)} method is invoked. Otherwise this method returns the given filter as-is. + * + * @param <R> the type of resources (e.g. {@code Feature}) used as inputs. + * @param filter the filter to optimize, or {@code null}. + * @return the optimized filter, or {@code null} if the given filter was null. + * May be {@code filter} if no optimization or simplification has been applied. + * @throws IllegalArgumentException if the given filter is already in process of being optimized + * (i.e. there is a recursive call to {@code apply(…)} for the same filter). + */ + public <R> Filter<? super R> apply(final Filter<R> filter) { + if (!(filter instanceof OnFilter<?>)) { + return filter; + } + final boolean isFirstCall = (done == null); + if (isFirstCall) { + done = new IdentityHashMap<>(); + } + try { + final Object previous = done.putIfAbsent(filter, COMPUTING); + if (previous == COMPUTING) { + throw new IllegalArgumentException(Errors.format(Errors.Keys.RecursiveCreateCallForKey_1, filter.getOperatorType())); + } + @SuppressWarnings("unchecked") + Filter<? super R> result = (Filter<? super R>) previous; + if (result == null) { + result = ((OnFilter<R>) filter).optimize(this); + if (done.put(filter, result) != COMPUTING) { + // Should not happen unless this `Optimization` is used concurrently in many threads. + throw new ConcurrentModificationException(); + } + } + return result; + } finally { + if (isFirstCall) { + done = null; + } + } + } + + /** + * Filter than can be optimized. Each filter implementation knows which rules can be applied. Review comment: Fixed 10 occurrences of this error in the code base. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected]
