package gherkin;
import gherkin.formatter.model.Tag;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TagExpression {
private final Map<String, Integer> limits = new HashMap<String, Integer>();
private And and = new And();
public TagExpression(List<String> tagExpressions) {
for (String tagExpression : tagExpressions) {
add(tagExpression.split("\\s*,\\s*"));
}
}
public boolean evaluate(Collection<Tag> tags) {
return and.isEmpty() || and.eval(tags);
}
public Map<String, Integer> limits() {
return limits;
}
public boolean isEmpty() {
return and.isEmpty();
}
private void add(String[] tags) {
Or or = new Or();
for (String tag : tags) {
boolean negation;
tag = tag.trim();
if (tag.startsWith("~")) {
tag = tag.substring(1);
negation = true;
} else {
negation = false;
}
String[] tagAndLimit = tag.split(":");
if (tagAndLimit.length == 2) {
tag = tagAndLimit[0];
int limit = Integer.parseInt(tagAndLimit[1]);
if (limits.containsKey(tag) && limits.get(tag) != limit) {
throw new BadTagLimitException(tag, limits.get(tag), limit);
}
limits.put(tag, limit);
}
if (negation) {
or.add(new Not(new TagExp(tag)));
} else {
or.add(new TagExp(tag));
}
}
and.add(or);
}
private interface Expression {
boolean eval(Collection<Tag> tags);
}
private class Not implements Expression {
private final Expression expression;
public Not(Expression expression) {
this.expression = expression;
}
public boolean eval(Collection<Tag> tags) {
return !expression.eval(tags);
}
}
private class And implements Expression {
private List<Expression> expressions = new ArrayList<Expression>();
public void add(Expression expression) {
expressions.add(expression);
}
public boolean eval(Collection<Tag> tags) {
boolean result = true;
for (Expression expression : expressions) {
result = expression.eval(tags);
if (!result) break;
}
return result;
}
public boolean isEmpty() {
return expressions.isEmpty();
}
}
private class Or implements Expression {
private List<Expression> expressions = new ArrayList<Expression>();
public void add(Expression expression) {
expressions.add(expression);
}
public boolean eval(Collection<Tag> tags) {
boolean result = false;
for (Expression expression : expressions) {
result = expression.eval(tags);
if (result) break;
}
return result;
}
}
private class TagExp implements Expression {
private final String tagName;
public TagExp(String tagName) {
if (!tagName.startsWith("@")) {
throw new BadTagException(tagName);
}
this.tagName = tagName;
}
public boolean eval(Collection<Tag> tags) {
for (Tag tag : tags) {
if (tagName.equals(tag.getName())) {
return true;
}
}
return false;
}
}
private class BadTagException extends RuntimeException {
public BadTagException(String tagName) {
super("Bad tag: \"" + tagName + "\"");
}
}
private class BadTagLimitException extends RuntimeException {
public BadTagLimitException(String tag, int limitA, int limitB) {
super("Inconsistent tag limits for " + tag + ": " + limitA + " and " + limitB);
}
}
}