/*
* Copyright (C) 2010.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* 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 uk.me.parabola.mkgmap.osmstyle.eval;
import uk.me.parabola.mkgmap.reader.osm.Element;
/**
* This is used to implement OR expressions as if each term was a separate
* indexable expression.
*
* <p>So if you have:
* <pre>
* A=1 | B=1 | C=1 {set name='${name} ${A}' | '${B}'; }
* </pre>
* This is represented by:
* <pre>
* A=1 {set name='${name} ${A}' | '${B}'; }
* B=1 {set name='${name} ${A}' | '${B}'; }
* C=1 {set name='${name} ${A}' | '${B}'; }
* </pre>
* So that each term can be index separately. However we need to be able to
* prevent the second and/or third terms running if the first (or second) matches.
* That is what this class does. It acts in most respects like the operation
* it is wrapping, but when a successful evaluation is performed on an
* element, the test is suppressed for the subsequent terms in the chain.
*
* @author Steve Ratcliffe
*/
public class LinkedOp implements Op {
protected final Op wrapped;
private final boolean first;
private LinkedOp link;
private Element current;
protected LinkedOp(Op wrapped, boolean first) {
this.wrapped = wrapped;
this.first = first;
}
public boolean eval(Element el) {
if (el == current)
return false;
boolean b = wrapped.eval(el);
if (link != null && b)
link.setMatched(el);
return b;
}
public boolean eval(int cacheId, Element el){
if (el == current)
return false;
boolean b = wrapped.eval(cacheId, el);
if (link != null && b)
link.setMatched(el);
return b;
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (!first)
sb.append("# Part of the previous OR expression: ");
sb.append('(');
sb.append(wrapped);
LinkedOp l = link;
while (l != null) {
sb.append(" | ");
sb.append(l.wrapped);
l = l.link;
}
sb.append(')');
return sb.toString();
}
public int priority() {
return wrapped.priority();
}
public boolean hasHigherPriority(Op other) {
return this.wrapped.hasHigherPriority(other);
}
public Op getFirst() {
return wrapped.getFirst();
}
public void setFirst(Op first) {
wrapped.setFirst(first);
}
public Op getSecond() {
return null;
}
public NodeType getType() {
return wrapped.getType();
}
public String value(Element el) {
return wrapped.value(el);
}
public String getKeyValue() {
throw new UnsupportedOperationException();
}
public boolean isType(NodeType value) {
return wrapped.isType(value);
}
/**
* Set the fact that the given element has been matched already. If we
* are called to evaluate and find that the element we are given
* has already been matched by an earlier term in the OR then we just
* return without doing anything.
* @param el The element to mark as being matched.
*/
private void setMatched(Element el) {
this.current = el;
if (link != null)
link.setMatched(el);
}
public void setLink(LinkedOp link) {
if (this.link == null)
this.link = link;
else
this.link.setLink(link);
}
/**
* Create either a LinkedOp or a LinkedBinaryOp as appropriate
* for the type of operation that is passed in.
*/
public static LinkedOp create(Op op, boolean first) {
if (op instanceof BinaryOp) {
return new LinkedBinaryOp((BinaryOp) op, first);
} else {
return new LinkedOp(op, first);
}
}
public boolean isFirstPart(){
return first;
}
}