/*
* Copyright (C) 2008 Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
*
* Author: Steve Ratcliffe
* Create date: 15-Nov-2008
*/
package uk.me.parabola.mkgmap.osmstyle;
import java.util.List;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.osmstyle.actions.Action;
import uk.me.parabola.mkgmap.osmstyle.eval.Op;
import uk.me.parabola.mkgmap.reader.osm.Element;
import uk.me.parabola.mkgmap.reader.osm.GType;
import uk.me.parabola.mkgmap.reader.osm.Relation;
import uk.me.parabola.mkgmap.reader.osm.Rule;
import uk.me.parabola.mkgmap.reader.osm.TypeResult;
/**
* An action rule modifies the tags on the incoming element.
*
* It can also have an expression, and does not need to have a Type. If
* there is no type then the resolve method does not send any results.
* The tags on the element may have been modified however.
*
* @author Steve Ratcliffe
*/
public class ActionRule implements Rule {
private static final Logger statsLog = Logger.getLogger(ActionRule.class.getPackage().getName()+".stats");
private Op expression;
private final List<Action> actions;
private final GType type;
private Rule finalizeRule;
private long numEval; // count how often the expression was evaluated
private long numTrue; // count how often the evaluation returned true
/** Finalize rules must not have an element type definition so the add method must never be called. */
private final static TypeResult finalizeTypeResult = new TypeResult() {
public void add(Element el, GType type) {
throw new UnsupportedOperationException("Finalize rules must not contain an action block.");
}
};
public ActionRule(Op expression, List<Action> actions, GType type) {
assert actions != null;
this.expression = expression;
this.actions = actions;
this.type = type;
}
public ActionRule(Op expression, List<Action> actions) {
assert actions != null;
this.expression = expression;
this.actions = actions;
this.type = null;
}
public int resolveType(int cacheId, Element el, TypeResult result) {
Element element = el;
if (expression != null) {
numEval++;
if (!expression.eval(cacheId, element))
return cacheId;
numTrue++;
// If this is a continue and we are not to propagate the effects
// of the action on the element to further rules, then make
// a copy of the element so that the original is unsullied.
//
// There is another reason we need to copy: since there will be
if (type != null && !type.isPropogateActions() && !(element instanceof Relation)) {
element = element.copy();
}
}
// an action will be performed, so we may have to invalidate the cache
boolean invalidate_cache = false;
for (Action a : actions){
if (a.perform(element)){
invalidate_cache = true;
}
}
if (invalidate_cache)
cacheId++;
if (type != null && finalizeRule != null) {
if (el == element && type.isContinueSearch())
// if there is a continue statement changes performed in
// the finalize block must not be persistent
element = element.copy();
// there is a type so first execute the finalize rules
if (type.getDefaultName() != null)
element.addTag("mkgmap:default_name", type.getDefaultName());
cacheId = finalizeRule.resolveType(cacheId, element, finalizeTypeResult);
}
result.add(element, type);
return cacheId;
}
public void resolveType(Element el, TypeResult result) {
Element element = el;
if (expression != null) {
numEval++;
if (!expression.eval(element))
return;
numTrue++;
// If this is a continue and we are not to propagate the effects
// of the action on the element to further rules, then make
// a copy of the element so that the original is unsullied.
//
// There is another reason we need to copy: since there will be
if (type != null && !type.isPropogateActions() && !(element instanceof Relation)) {
element = element.copy();
}
}
for (Action a : actions)
a.perform(element);
if (type != null && finalizeRule != null) {
if (el == element && type.isContinueSearch())
// if there is a continue statement changes performed in
// the finalize block must not be persistent
element = element.copy();
// there is a type so first execute the finalize rules
if (type.getDefaultName() != null)
element.addTag("mkgmap:default_name", type.getDefaultName());
finalizeRule.resolveType(element, finalizeTypeResult);
}
result.add(element, type);
}
public String toString() {
StringBuilder fmt = new StringBuilder();
if (expression != null)
fmt.append(expression);
fmt.append(" {");
for (Action a : actions)
fmt.append(a);
fmt.append("}");
if (type != null) {
fmt.append(' ');
fmt.append(type);
}
return fmt.toString();
}
public void setFinalizeRule(Rule finalizeRule) {
this.finalizeRule = finalizeRule;
}
@Override
public Rule getFinalizeRule() {
return finalizeRule;
}
public Op getOp(){
return expression;
}
public void setOp(Op expression){
this.expression = expression;
}
@Override
public void printStats(String header) {
if (statsLog.isInfoEnabled())
statsLog.info(header,"stats (rule/evals/true)", this.toString() + "/" + numEval + "/" + numTrue);
}
@Override
public boolean containsExpression(String exp) {
return expression.toString().contains(exp);
}
}