/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2014, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotools.styling.css; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import org.geotools.styling.css.util.UnboundSimplifyingFilterVisitor; import org.opengis.feature.type.FeatureType; import org.opengis.filter.And; import org.opengis.filter.Filter; import org.opengis.filter.Not; import org.opengis.filter.Or; /** * A simplifying filter visitor that caches the results, to avoid repeating their computation over * and over * * @author Andrea Aime - GeoSolutions */ class CachedSimplifyingFilterVisitor extends UnboundSimplifyingFilterVisitor { // filters we know are already simplified Map<Filter, Filter> cache = new WeakHashMap<Filter, Filter>(); public CachedSimplifyingFilterVisitor(FeatureType ft) { setFeatureType(ft); setRangeSimplicationEnabled(true); } @Override public Object visit(And filter, Object extraData) { Filter result = cache.get(filter); if (result == null) { result = (Filter) super.visit(filter, extraData); cache.put(filter, result); } return result; } @Override public Object visit(Or filter, Object extraData) { Filter result = cache.get(filter); if (result == null) { result = (Filter) super.visit(filter, extraData); cache.put(filter, result); } return result; } @Override public Object visit(Not filter, Object extraData) { Filter result = cache.get(filter); if (result == null) { result = (Filter) super.visit(filter, extraData); cache.put(filter, result); } return result; } protected List<Filter> extraAndSimplification(Object extraData, List<Filter> filters) { if (filters.size() > 1) { // if there are nested ors and top level filters, try factoring out common expression, // e.g., (A | B) & A -> A Set<Filter> topLevel = new HashSet<>(); for (Filter filter : filters) { if (!(filter instanceof Or)) { topLevel.add(filter); } } for (int i = 0; i < filters.size();) { Filter f = filters.get(i); boolean skip = false; if (f instanceof Or) { Or or = ((Or) f); for (Filter child : or.getChildren()) { if (topLevel.contains(child)) { skip = true; break; } } } if (skip) { filters.remove(i); } else { i++; } } } // if there are nested Ors, try distribution, see if this helps reduce the // overall expression if (filters.size() > 1) { for (int i = 0; i < filters.size(); i++) { Filter f = filters.get(i); if (f instanceof Or) { Or or = ((Or) f); Filter reduced = null; boolean twoOrMore = false; for (Filter child : or.getChildren()) { List<Filter> newList = new ArrayList<>(filters); newList.remove(or); newList.add(child); And and = getFactory(extraData).and(newList); Filter simplified = (Filter) and.accept(this, extraData); if (simplified == Filter.EXCLUDE) { continue; } else if (simplified == Filter.INCLUDE) { return Collections.singletonList((Filter) Filter.INCLUDE); } else if (reduced == null) { reduced = simplified; } else if (!simplified.equals(reduced)) { twoOrMore = true; break; } } if (reduced == null) { return Collections.singletonList((Filter) Filter.EXCLUDE); } else if (!twoOrMore) { filters.clear(); if (!(reduced instanceof And)) { return Collections.singletonList(reduced); } else { filters.addAll(((And) reduced).getChildren()); filters = basicAndSimplification(filters); // this assumes we'll never stumble into a single children "or", // because those are simplified out at the beginning of this procedure i = 0; } } } } } return filters; } protected List<Filter> extraOrSimplification(Object extraData, List<Filter> filters) { if (filters.size() > 1) { // if there are nested ands and top level filters, try factoring out common expression, // e.g., (A & B) | A -> A Set<Filter> topLevel = new HashSet<>(); for (Filter filter : filters) { if (!(filter instanceof And)) { topLevel.add(filter); } } for (int i = 0; i < filters.size();) { Filter f = filters.get(i); boolean skip = false; if (f instanceof And) { And and = ((And) f); for (Filter child : and.getChildren()) { if (topLevel.contains(child)) { skip = true; break; } } } if (skip) { filters.remove(i); } else { i++; } } } if (filters.size() > 1) { // if there are nested Ands, try distribution, see if this helps reduce the // overall expression for (int i = 0; i < filters.size(); i++) { Filter f = filters.get(i); if (f instanceof And) { And and = ((And) f); Filter reduced = null; boolean twoOrMore = false; for (Filter child : and.getChildren()) { List<Filter> newList = new ArrayList<>(filters); newList.remove(and); newList.add(child); Or or = getFactory(extraData).or(newList); Filter simplified = (Filter) or.accept(this, extraData); if (simplified == Filter.EXCLUDE) { return Collections.singletonList((Filter) Filter.EXCLUDE); } else if (simplified == Filter.INCLUDE) { continue; } else if (reduced == null) { reduced = simplified; } else if (!simplified.equals(reduced)) { twoOrMore = true; break; } } if (reduced == null) { return Collections.singletonList((Filter) Filter.INCLUDE); } else if (!twoOrMore) { filters.clear(); if (!(reduced instanceof Or)) { return Collections.singletonList(reduced); } else { filters.addAll(((Or) reduced).getChildren()); filters = basicOrSimplification(filters); // this assumes we'll never stumble into a single children "or", // because those are simplified out at the beginning of this procedure i = 0; } } } } } return filters; } }