/*
* 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: 16-Nov-2008
*/
package uk.me.parabola.mkgmap.osmstyle.actions;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import uk.me.parabola.mkgmap.scan.SyntaxException;
import uk.me.parabola.mkgmap.scan.Token;
import uk.me.parabola.mkgmap.scan.TokenScanner;
import static uk.me.parabola.imgfmt.app.net.AccessTagsAndBits.*;
/**
* Read an action block. This is contained within braces and contains
* commands to change tags etc.
*
* @author Steve Ratcliffe
*/
public class ActionReader {
private final TokenScanner scanner;
private final Set<String> usedTags = new HashSet<>();
public ActionReader(TokenScanner scanner) {
this.scanner = scanner;
}
public ActionList readActions() {
List<Action> actions = new ArrayList<>();
Set<String> changeableTags = new HashSet<>();
scanner.skipSpace();
if (!scanner.checkToken("{"))
return new ActionList(actions, changeableTags);
scanner.nextToken();
while (inAction()) {
Token tok = scanner.nextToken();
if (tok.isValue(";"))
continue;
String cmd = tok.getValue();
if ("set".equals(cmd)) {
actions.add(readTagValue(true, changeableTags));
} else if ("add".equals(cmd)) {
actions.add(readTagValue(false, changeableTags));
} else if ("setaccess".equals(cmd)) {
actions.add(readAccessValue(true, changeableTags));
} else if ("addaccess".equals(cmd)) {
actions.add(readAccessValue(false, changeableTags));
} else if ("apply".equals(cmd)) {
actions.add(readAllCmd(false));
} else if ("apply_once".equals(cmd)) {
actions.add(readAllCmd(true));
} else if ("name".equals(cmd)) {
actions.add(readValueBuilder(new NameAction()));
changeableTags.add("mkgmap:label:1");
} else if ("addlabel".equals(cmd)) {
actions.add(readValueBuilder(new AddLabelAction()));
for (int labelNo = 1; labelNo <= 4; labelNo++)
changeableTags.add("mkgmap:label:"+labelNo);
} else if ("delete".equals(cmd)) {
String tag = scanner.nextWord();
actions.add(new DeleteAction(tag));
} else if ("deletealltags".equals(cmd)) {
actions.add(new DeleteAllTagsAction());
} else if ("rename".equals(cmd)) {
String from = scanner.nextWord();
String to = scanner.nextWord();
Action act = new RenameAction(from, to);
actions.add(act);
// The 'to' tag may come into existence and you may attempt
// to match on it, therefore we have to save it.
changeableTags.add(to);
// the from tag must not be dropped from the input
usedTags.add(from);
} else if ("echo".equals(cmd)) {
String str = scanner.nextWord();
actions.add(new EchoAction(str));
} else if ("echotags".equals(cmd)) {
String str = scanner.nextWord();
actions.add(new EchoTagsAction(str));
} else {
throw new SyntaxException(scanner, "Unrecognised command '" + cmd + '\'');
}
scanner.skipSpace();
}
if (scanner.checkToken("}"))
scanner.nextToken();
scanner.skipSpace();
return new ActionList(actions, changeableTags);
}
private Action readAllCmd(boolean once) {
String role = null;
if (scanner.checkToken("role")) {
scanner.nextToken();
String eq = scanner.nextValue();
if (!"=".equals(eq))
throw new SyntaxException(scanner, "Expecting '=' after role keyword");
role = scanner.nextWord();
}
SubAction subAction = new SubAction(role, once);
List<Action> actionList = readActions().getList();
for (Action a : actionList)
subAction.add(a);
return subAction;
}
/**
* A name command has a number of alternatives separated by '|' characters.
*/
private Action readValueBuilder(ValueBuildedAction action) {
while (inActionCmd()) {
if (scanner.checkToken("|")) {
scanner.nextToken();
continue;
}
String val = scanner.nextWord();
action.add(val);
}
usedTags.addAll(action.getUsedTags());
return action;
}
/**
* Read a tag/value pair. If the action is executed then the tag name
* will possibly be modified or set. If that is the case then we will
* have to make sure that we are executing rules for that tag.
*
* @param modify If true the tag value can be modified. If it is not set
* then a tag can only be added; if it already exists, then it will not
* be changed.
* @param changeableTags Tags that could be changed by the action. This is
* an output parameter, any such tags should be added to this set.
* @return The new add tag action.
*/
private AddTagAction readTagValue(boolean modify, Set<String> changeableTags) {
String key = scanner.nextWord();
if (!scanner.checkToken("="))
throw new SyntaxException(scanner, "Expecting tag=value");
scanner.nextToken();
AddTagAction action = null;
while (inActionCmd()) {
String val = scanner.nextWord();
if (action == null)
action = new AddTagAction(key, val, modify);
else
action.add(val);
// Save the tag as one that is potentially set during the operation.
// If the value contains a variable, then we do not know what the
// value will be. Otherwise save the full tag=value
if (val.contains("$")) {
changeableTags.add(key);
} else {
changeableTags.add(key + "=" + val);
}
if (scanner.checkToken("|"))
scanner.nextToken();
}
if (action != null)
usedTags.addAll(action.getUsedTags());
return action;
}
/**
* Read a tag/value pair. If the action is executed then the tag name
* will possibly be modified or set. If that is the case then we will
* have to make sure that we are executing rules for that tag.
*
* @param modify If true the tag value can be modified. If it is not set
* then a tag can only be added; if it already exists, then it will not
* be changed.
* @param changeableTags Tags that could be changed by the action. This is
* an output parameter, any such tags should be added to this set.
* @return The new add tag action.
*/
private AddAccessAction readAccessValue(boolean modify, Set<String> changeableTags) {
AddAccessAction action = null;
while (inActionCmd()) {
String val = scanner.nextWord();
if (action == null)
action = new AddAccessAction(val, modify);
else
action.add(val);
// Save the tag as one that is potentially set during the operation.
// If the value contains a variable, then we do not know what the
// value will be. Otherwise save the full tag=value
if (val.contains("$")) {
for (String accessTag : ACCESS_TAGS.keySet())
changeableTags.add(accessTag);
} else {
for (String accessTag : ACCESS_TAGS.keySet())
changeableTags.add(accessTag + "=" + val);
}
if (scanner.checkToken("|"))
scanner.nextToken();
}
if (action != null)
usedTags.addAll(action.getUsedTags());
return action;
}
private boolean inActionCmd() {
boolean end = scanner.checkToken(";");
return inAction() && !end;
}
private boolean inAction() {
return !scanner.isEndOfFile() && !scanner.checkToken("}");
}
public Set<String> getUsedTags() {
return usedTags;
}
}