/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2004-2008, Open Source Geospatial Foundation (OSGeo)
* (C) 2009, Geomatys
*
* 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.geotoolkit.filter.visitor;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.geotoolkit.factory.FactoryFinder;
import org.geotoolkit.filter.capability.DefaultFilterCapabilities;
import org.apache.sis.util.logging.Logging;
import org.opengis.feature.FeatureType;
import org.opengis.filter.And;
import org.opengis.filter.BinaryComparisonOperator;
import org.opengis.filter.ExcludeFilter;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.FilterVisitor;
import org.opengis.filter.Id;
import org.opengis.filter.IncludeFilter;
import org.opengis.filter.Not;
import org.opengis.filter.Or;
import org.opengis.filter.PropertyIsBetween;
import org.opengis.filter.PropertyIsEqualTo;
import org.opengis.filter.PropertyIsGreaterThan;
import org.opengis.filter.PropertyIsGreaterThanOrEqualTo;
import org.opengis.filter.PropertyIsLessThan;
import org.opengis.filter.PropertyIsLessThanOrEqualTo;
import org.opengis.filter.PropertyIsLike;
import org.opengis.filter.PropertyIsNil;
import org.opengis.filter.PropertyIsNotEqualTo;
import org.opengis.filter.PropertyIsNull;
import org.opengis.filter.capability.FilterCapabilities;
import org.opengis.filter.expression.Add;
import org.opengis.filter.expression.BinaryExpression;
import org.opengis.filter.expression.Divide;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.Multiply;
import org.opengis.filter.expression.NilExpression;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.expression.Subtract;
import org.opengis.filter.spatial.BBOX;
import org.opengis.filter.spatial.Beyond;
import org.opengis.filter.spatial.BinarySpatialOperator;
import org.opengis.filter.spatial.Contains;
import org.opengis.filter.spatial.Crosses;
import org.opengis.filter.spatial.DWithin;
import org.opengis.filter.spatial.Disjoint;
import org.opengis.filter.spatial.Equals;
import org.opengis.filter.spatial.Intersects;
import org.opengis.filter.spatial.Overlaps;
import org.opengis.filter.spatial.Touches;
import org.opengis.filter.spatial.Within;
import org.opengis.filter.temporal.After;
import org.opengis.filter.temporal.AnyInteracts;
import org.opengis.filter.temporal.Before;
import org.opengis.filter.temporal.Begins;
import org.opengis.filter.temporal.BegunBy;
import org.opengis.filter.temporal.During;
import org.opengis.filter.temporal.EndedBy;
import org.opengis.filter.temporal.Ends;
import org.opengis.filter.temporal.Meets;
import org.opengis.filter.temporal.MetBy;
import org.opengis.filter.temporal.OverlappedBy;
import org.opengis.filter.temporal.TContains;
import org.opengis.filter.temporal.TEquals;
import org.opengis.filter.temporal.TOverlaps;
/**
* Determines what queries can be processed server side and which can be processed client side.
* <p>
* IMPLEMENTATION NOTE: This class is implemented as a stack processor. If you're curious how it
* works, compare it with the old SQLUnpacker class, which did the same thing using recursion in a
* more straightforward way.
* </p>
* <p>
* Here's a non-implementors best-guess at the algorithm: Starting at the top of the filter, split
* each filter into its constituent parts. If the given FilterCapabilities support the given
* operator, then keep checking downwards.
* </p>
* <p>
* The key is in knowing whether or not something "down the tree" from you wound up being supported
* or not. This is where the stacks come in. Right before handing off to accept() the sub- filters,
* we count how many things are currently on the "can be proccessed by the underlying datastore"
* stack (the preStack) and we count how many things are currently on the "need to be post-
* processed" stack.
* </p>
* <p>
* After the accept() call returns, we look again at the preStack.size() and postStack.size(). If
* the postStack has grown, that means that there was stuff down in the accept()-ed filter that
* wasn't supportable. Usually this means that our filter isn't supportable, but not always.
*
* In some cases a sub-filter being unsupported isn't necessarily bad, as we can 'unpack' OR
* statements into AND statements (DeMorgans rule/modus poens) and still see if we can handle the
* other side of the OR. Same with NOT and certain kinds of AND statements.
* </p>
*
* @author dzwiers
* @author commented and ported from gt to ogc filters by saul.farber
* @author ported to work upon {@code org.geotoolkit.filter.Capabilities} by Gabriel Roldan
* @module
* @since 2.5.3
*/
public class CapabilitiesFilterSplitter implements FilterVisitor, ExpressionVisitor {
private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.filter.visitor");
private static final Pattern ID_PATTERN = Pattern.compile("@(\\w+:)?id");
private static final Pattern PROPERTY_PATTERN = Pattern.compile("(\\w+:)?(.+)");
private static final Class[] SPATIAL_OPS = new Class[]{Beyond.class, Contains.class, Crosses.class,
Disjoint.class, DWithin.class, Equals.class, Intersects.class, Overlaps.class,
Touches.class, Within.class};
/**
* The stack holding the bits of the filter that are not processable by something with the given
* {@link FilterCapabilities}
*/
private final Deque postStack = new ArrayDeque();
/**
* The stack holding the bits of the filter that <b>are</b> processable by something with the
* given {@link FilterCapabilities}
*/
private final Deque preStack = new ArrayDeque();
/**
* Operates similar to postStack. When a update is determined to affect an attribute expression
* the update filter is pushed on to the stack, then ored with the filter that contains the
* expression.
*/
private final Set changedStack = new HashSet();
/**
* The given filterCapabilities that we're splitting on.
*/
private final DefaultFilterCapabilities fcs;
private final FeatureType parent;
private final FilterFactory ff;
private Filter original = null;
/**
* Create a new instance.
*
* @param fcs
* The FilterCapabilties that describes what Filters/Expressions the server can
* process.
* @param parent The FeatureType that this filter involves. Why is this needed?
*/
public CapabilitiesFilterSplitter(final DefaultFilterCapabilities fcs, final FeatureType parent) {
this.ff = FactoryFinder.getFilterFactory(null);
this.fcs = fcs;
this.parent = parent;
}
/**
* Gets the filter that cannot be sent to the server and must be post-processed on the client by
* geotoolkit.
*
* @return the filter that cannot be sent to the server and must be post-processed on the client
* by geotoolkit.
*/
public Filter getPostFilter() {
if (!changedStack.isEmpty()){
// Return the original filter to ensure that
// correct features are filtered
return original;
}
if (postStack.size() > 1){
LOGGER.warning("Too many post stack items after run: " + postStack.size());
}
// JE: Changed to peek because get implies that the value can be retrieved multiple times
return (postStack.isEmpty()) ? Filter.INCLUDE : (Filter) postStack.peek();
}
/**
* Gets the filter that can be sent to the server for pre-processing.
*
* @return the filter that can be sent to the server for pre-processing.
*/
public Filter getPreFilter() {
if (preStack.isEmpty()) {
return Filter.INCLUDE;
}
if (preStack.size() > 1) {
LOGGER.warning("Too many pre stack items after run: " + preStack.size());
}
// JE: Changed to peek because get implies that the value can be retrieved multiple
// times
final Filter f = preStack.isEmpty() ? Filter.INCLUDE : (Filter) preStack.peek();
if (changedStack.isEmpty()) {
return f;
}
Iterator iter = changedStack.iterator();
Filter updateFilter = (Filter) iter.next();
while (iter.hasNext()) {
Filter next = (Filter) iter.next();
if (next == Filter.INCLUDE) {
updateFilter = next;
break;
} else {
updateFilter = (Filter) ff.or(updateFilter, next);
}
}
if (updateFilter == Filter.INCLUDE || f == Filter.INCLUDE) {
return Filter.INCLUDE;
}
return ff.or(f, updateFilter);
}
/**
* @see FilterVisitor#visit(IncludeFilter, Object)
*
* @param filter the {@link Filter} to visit
*/
public void visit(final IncludeFilter filter) {
return;
}
/**
* @see FilterVisitor#visit(ExcludeFilter, Object)
*
* @param filter the {@link Filter} to visit
*/
public void visit(final ExcludeFilter filter) {
if (fcs.supports(Filter.EXCLUDE)) {
preStack.addFirst(filter);
} else {
postStack.addFirst(filter);
}
}
/**
* @see FilterVisitor#visit(PropertyIsBetween, Object) NOTE: This method is extra documented as
* an example of how all the other methods are implemented. If you want to know how this
* class works read this method first!
*
* @param filter the {@link Filter} to visit
*/
@Override
public Object visit(final PropertyIsBetween filter, final Object extradata) {
if (original == null) {
original = filter;
}
if (!(fcs.supports(filter))) {
// No, we don't support this filter.
// So we push it onto the postStack, saying
// "Hey, here's one more filter that we don't support.
// Someone who called us may look at this and say,
// "Hmm, I called accept() on this filter and now
// the postStack is taller than it was...I guess this
// filter wasn't accepted.
postStack.addFirst(filter);
return null;
}
// Do we support this filter type at all?
// Yes, we do. Now, can we support the sub-filters?
// first, remember how big the current list of "I can't support these" filters is.
final int i = postStack.size();
final Expression lowerBound = filter.getLowerBoundary();
final Expression expr = filter.getExpression();
final Expression upperBound = filter.getUpperBoundary();
if (lowerBound == null || upperBound == null || expr == null) {
// Well, one of the boundaries is null, so I guess
// we're saying that *no* featurestore could support this.
postStack.addFirst(filter);
return null;
}
// Ok, here's the magic. We know how big our list of "can't support"
// filters is. Now we send off the lowerBound Expression to see if
// it can be supported.
lowerBound.accept(this, null);
// Now we're back, and we check. Did the postStack get bigger?
if (i < postStack.size()) {
// Yes, it did. Well, that means we can't support
// this particular filter. Let's back out anything that was
// added by the lowerBound.accept() and add ourselves.
postStack.removeFirst(); // lowerBound.accept()'s bum filter
postStack.addFirst(filter);
return null;
}
// Aha! The postStack didn't get any bigger, so we're still
// all good. Now try again with the middle expression itself...
expr.accept(this, null);
// Did postStack get bigger?
if (i < postStack.size()) {
// Yes, it did. So that means we can't support
// this particular filter. We need to back out what we've
// done, which is BOTH the lowerbounds filter *and* the
// thing that was added by expr.accept() when it failed.
preStack.removeFirst(); // lowerBound.accept()'s success
postStack.removeFirst(); // expr.accept()'s bum filter
postStack.addFirst(filter);
return null;
}
// Same deal again...
upperBound.accept(this, null);
if (i < postStack.size()) {
// post process it
postStack.removeFirst(); // upperBound.accept()'s bum filter
preStack.removeFirst(); // expr.accept()'s success
preStack.removeFirst(); // lowerBound.accept()'s success
postStack.addFirst(filter);
return null;
}
// Well, by getting here it means that postStack didn't get
// taller, even after accepting all three middle filters. This
// means that this whole filter is totally pre-filterable.
// Let's clean up the pre-stack (which got one added to it
// for the success at each of the three above .accept() calls)
// and add us to the stack.
preStack.removeFirst(); // upperBounds.accept()'s success
preStack.removeFirst(); // expr.accept()'s success
preStack.removeFirst(); // lowerBounds.accept()'s success
// finally we add ourselves to the "can be pre-proccessed" filter
// stack. Now when we return we've added exactly one thing to
// the preStack...namely, the given filter.
preStack.addFirst(filter);
return null;
}
@Override
public Object visit(final PropertyIsEqualTo filter, final Object notUsed) {
visitBinaryComparisonOperator(filter);
return null;
}
@Override
public Object visit(final PropertyIsGreaterThan filter, final Object notUsed) {
visitBinaryComparisonOperator(filter);
return null;
}
@Override
public Object visit(final PropertyIsGreaterThanOrEqualTo filter, final Object notUsed) {
visitBinaryComparisonOperator(filter);
return null;
}
@Override
public Object visit(final PropertyIsLessThan filter, final Object notUsed) {
visitBinaryComparisonOperator(filter);
return null;
}
@Override
public Object visit(final PropertyIsLessThanOrEqualTo filter, final Object notUsed) {
visitBinaryComparisonOperator(filter);
return null;
}
@Override
public Object visit(final PropertyIsNotEqualTo filter, final Object notUsed) {
visitBinaryComparisonOperator(filter);
return null;
}
private void visitBinaryComparisonOperator(final BinaryComparisonOperator filter) {
if (original == null) {
original = filter;
}
// supports it as a group -- no need to check the type
if (!fcs.supports(filter)) {
postStack.addFirst(filter);
return;
}
final int i = postStack.size();
final Expression leftValue = filter.getExpression1();
final Expression rightValue = filter.getExpression2();
if (leftValue == null || rightValue == null) {
postStack.addFirst(filter);
return;
}
leftValue.accept(this, null);
if (i < postStack.size()) {
postStack.removeFirst();
postStack.addFirst(filter);
return;
}
rightValue.accept(this, null);
if (i < postStack.size()) {
preStack.removeFirst(); // left
postStack.removeFirst();
postStack.addFirst(filter);
return;
}
preStack.removeFirst(); // left side
preStack.removeFirst(); // right side
preStack.addFirst(filter);
}
@Override
public Object visit(final BBOX filter, final Object notUsed) {
if (!fcs.supports(filter)) {
postStack.addFirst(filter);
} else {
preStack.addFirst(filter);
}
return null;
}
@Override
public Object visit(final Beyond filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final Contains filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final Crosses filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final Disjoint filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final DWithin filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final Equals filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final Intersects filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final Overlaps filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final Touches filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
@Override
public Object visit(final Within filter, final Object notUsed) {
visitBinarySpatialOperator(filter);
return null;
}
private void visitBinarySpatialOperator(final BinarySpatialOperator filter) {
if (original == null) {
original = filter;
}
for(final Class clazz : SPATIAL_OPS) {
if (clazz.isAssignableFrom(filter.getClass())) {
// if (!fcs.supports(spatialOps[i])) {
if (!fcs.supports(filter)) {
postStack.addFirst(filter);
return;
} else {
// fcs supports this filter, no need to check the rest
break;
}
}
}
final int i = postStack.size();
final Expression leftGeometry = filter.getExpression1();
final Expression rightGeometry = filter.getExpression2();
if (leftGeometry == null || rightGeometry == null) {
postStack.addFirst(filter);
return;
}
leftGeometry.accept(this, null);
if (i < postStack.size()) {
postStack.removeFirst();
postStack.addFirst(filter);
return;
}
rightGeometry.accept(this, null);
if (i < postStack.size()) {
preStack.removeFirst(); // left
postStack.removeFirst();
postStack.addFirst(filter);
return;
}
preStack.removeFirst(); // left side
preStack.removeFirst(); // right side
preStack.addFirst(filter);
}
@Override
public Object visit(After filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(AnyInteracts filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(Before filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(Begins filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(BegunBy filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(During filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(EndedBy filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(Ends filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(Meets filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(MetBy filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(OverlappedBy filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(TContains filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(TEquals filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(TOverlaps filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(final PropertyIsLike filter, final Object notUsed) {
if (original == null) {
original = filter;
}
// if (!fcs.supports(PropertyIsLike.class)) {
if (!fcs.supports(filter)) {
postStack.addFirst(filter);
return null;
}
final int i = postStack.size();
filter.getExpression().accept(this, null);
if (i < postStack.size()) {
postStack.removeFirst();
postStack.addFirst(filter);
return null;
}
preStack.removeFirst(); // value
preStack.addFirst(filter);
return null;
}
@Override
public Object visit(final And filter, final Object notUsed) {
visitLogicOperator(filter);
return null;
}
@Override
public Object visit(final Not filter, final Object notUsed) {
visitLogicOperator(filter);
return null;
}
@Override
public Object visit(final Or filter, final Object notUsed) {
visitLogicOperator(filter);
return null;
}
private void visitLogicOperator(final Filter filter) {
if (original == null) {
original = filter;
}
if (!fcs.supports(filter)) {
postStack.addFirst(filter);
return;
}
final int i = postStack.size();
final int j = preStack.size();
if (filter instanceof Not) {
final Filter subFilter = ((Not) filter).getFilter();
if (subFilter != null) {
subFilter.accept(this, null);
if (i < postStack.size()) {
// since and can split filter into both pre and post parts
// the parts have to be combined since ~(A^B) == ~A | ~B
// combining is easy since filter==combined result however both post and pre
// stacks must be cleared since both may have components of the filter
removeFirstToSize(postStack, i);
removeFirstToSize(preStack, j);
postStack.addFirst(filter);
} else {
removeFirstToSize(preStack, j);
preStack.addFirst(filter);
}
}
} else if (filter instanceof Or) {
final Filter orReplacement = translateOr((Or) filter);
orReplacement.accept(this, null);
if (postStack.size() > i) {
removeFirstToSize(postStack, i);
postStack.addFirst(filter);
return;
}
preStack.removeFirst();
preStack.addFirst(filter);
} else if (filter instanceof And) {
// it's an AND
final Iterator it = ((And) filter).getChildren().iterator();
while (it.hasNext()) {
final Filter next = (Filter) it.next();
next.accept(this, null);
}
// combine the unsupported and add to the top
if (i < postStack.size()) {
Filter f = (Filter) postStack.removeFirst();
while (postStack.size() > i) {
f = ff.and(f, (Filter) postStack.removeFirst());
}
postStack.addFirst(f);
if (j < preStack.size()) {
f = (Filter) preStack.removeFirst();
while (preStack.size() > j) {
f = ff.and(f, (Filter) preStack.removeFirst());
}
preStack.addFirst(f);
}
} else {
removeFirstToSize(preStack, j);
preStack.addFirst(filter);
}
}else{
throw new IllegalArgumentException("LogicFilter found which is not 'and, or, not : " + filter);
}
}
private void removeFirstToSize(final Deque stack, final int j) {
while (j < stack.size()) {
stack.removeFirst();
}
}
@Override
public Object visitNullFilter(final Object notUsed) {
return null;
}
@Override
public Object visit(final IncludeFilter filter, final Object notUsed) {
return null;
}
@Override
public Object visit(final ExcludeFilter filter, final Object notUsed) {
if (fcs.supports(Filter.EXCLUDE)) {
preStack.addFirst(filter);
} else {
postStack.addFirst(filter);
}
return null;
}
@Override
public Object visit(final PropertyIsNull filter, final Object notUsed) {
if (original == null) {
original = filter;
}
if (!fcs.supports(filter)) {
postStack.addFirst(filter);
return null;
}
final int i = postStack.size();
filter.getExpression().accept(this, null);
if (i < postStack.size()) {
postStack.removeFirst();
postStack.addFirst(filter);
}
preStack.removeFirst(); // null
preStack.addFirst(filter);
return null;
}
@Override
public Object visit(final PropertyIsNil filter, Object extraData) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object visit(final Id filter, final Object notUsed) {
if (original == null) {
original = filter;
}
// figure out how to check that this is top level.
// otherwise this is fine
if (!postStack.isEmpty()) {
postStack.addFirst(filter);
}
preStack.addFirst(filter);
return null;
}
@Override
public Object visit(final PropertyName expression, final Object notUsed) {
if(!support(expression.getPropertyName())){
//not a simple propert name, xpath or something else.
postStack.addFirst(expression);
return null;
}
// JD: use an expression to get at the attribute type intead of accessing directly
if (parent != null && expression.evaluate(parent) == null) {
throw new IllegalArgumentException("Property '" + expression.getPropertyName() + "' could not be found in " + parent.getName());
}
preStack.addFirst(expression);
return null;
}
private static boolean support(String xpath) {
return !xpath.startsWith("/") &&
!xpath.startsWith("*") &&
(PROPERTY_PATTERN.matcher(xpath).matches() || ID_PATTERN.matcher(xpath).matches());
}
@Override
public Object visit(final Literal expression, final Object notUsed) {
if (expression.getValue() == null) {
postStack.addFirst(expression);
}
preStack.addFirst(expression);
return null;
}
@Override
public Object visit(final Add filter, final Object notUsed) {
visitMathExpression(filter);
return null;
}
@Override
public Object visit(final Divide filter, final Object notUsed) {
visitMathExpression(filter);
return null;
}
@Override
public Object visit(final Multiply filter, final Object notUsed) {
visitMathExpression(filter);
return null;
}
@Override
public Object visit(final Subtract filter, final Object notUsed) {
visitMathExpression(filter);
return null;
}
private void visitMathExpression(final BinaryExpression expression) {
// if (!fcs.supports(Add.class) && !fcs.supports(Subtract.class)
// && !fcs.supports(Multiply.class) && !fcs.supports(Divide.class)) {
/*if (!fcs.fullySupports(expression)) {
postStack.addFirst(expression);
return;
}*/
final int i = postStack.size();
final Expression leftValue = expression.getExpression1();
final Expression rightValue = expression.getExpression2();
if (leftValue == null || rightValue == null) {
postStack.addFirst(expression);
return;
}
leftValue.accept(this, null);
if (i < postStack.size()) {
postStack.removeFirst();
postStack.addFirst(expression);
return;
}
rightValue.accept(this, null);
if (i < postStack.size()) {
preStack.removeFirst(); // left
postStack.removeFirst();
postStack.addFirst(expression);
return;
}
preStack.removeFirst(); // left side
preStack.removeFirst(); // right side
preStack.addFirst(expression);
}
/**
* {@inheritDoc }
*/
@Override
public Object visit(final Function expression, final Object notUsed) {
//we don't have informations yet on supported function so we consider they are not supported.
if(true){
postStack.addFirst(expression);
return null;
}
/*if (!fcs.fullySupports(expression)) {
postStack.addFirst(expression);
return null;
}*/
if (expression.getName() == null) {
postStack.addFirst(expression);
return null;
}
final int i = postStack.size();
final int j = preStack.size();
for (int k = 0; k < expression.getParameters().size(); k++) {
expression.getParameters().get(k).accept(this, null);
if (i < postStack.size()) {
while (j < preStack.size()) {
preStack.removeFirst();
}
postStack.removeFirst();
postStack.addFirst(expression);
return null;
}
}
while (j < preStack.size()) {
preStack.removeFirst();
}
preStack.addFirst(expression);
return null;
}
@Override
public Object visit(final NilExpression nilExpression, final Object notUsed) {
postStack.addFirst(nilExpression);
return null;
}
private Filter translateOr(final Or filter) {
// a|b == ~~(a|b) negative introduction
// ~(a|b) == (~a + ~b) modus ponens
// ~~(a|b) == ~(~a + ~b) substitution
// a|b == ~(~a + ~b) negative simpilification
final Iterator<Filter> ite = filter.getChildren().iterator();
final List translated = new ArrayList();
while (ite.hasNext()) {
Filter f = ite.next();
if (f instanceof Not) {
// simplify it
final Not logic = (Not) f;
final Filter next = logic.getFilter();
translated.add(next);
} else {
translated.add(ff.not(f));
}
}
final Filter and = ff.and(translated);
return ff.not(and);
}
}