package org.araqne.logdb.query.parser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Pattern;
import org.araqne.log.api.WildcardMatcher;
import org.araqne.logdb.QueryContext;
import org.araqne.logdb.QueryParseException;
import org.araqne.logdb.Row;
import org.araqne.logdb.query.command.StorageObjectName;
import org.araqne.logdb.query.expr.Comma;
import org.araqne.logdb.query.expr.Expression;
import org.araqne.logdb.query.parser.ExpressionParser.FuncTerm;
import org.araqne.logdb.query.parser.ExpressionParser.TokenTerm;
import org.araqne.logstorage.LogTableRegistry;
import org.araqne.logstorage.TableSchema;
public class MetadataMatcher<T extends StorageObjectSpec> {
public static final String IDFACTORY_KEY = "identifier_factory";
private List<T> specs;
private TableMatcher matcherExpr;
public static interface IdentifierFactory {
Object create(String concreteString);
}
public MetadataMatcher(String predicate, List<T> specs) {
this.specs = specs;
OpEmitterFactory of = new OpEmitter();
FuncEmitterFactory ff = new FuncEmitter();
TermEmitterFactory tf = new TermEmitter();
Expression pred = ExpressionParser.parse(null, predicate, new ParsingRule(Ops.NOP, of, ff, tf));
matcherExpr = ((TableMatcher) pred.eval(new Row()));
}
public List<StorageObjectName> match(LogTableRegistry tableRegistry) {
ArrayList<StorageObjectName> result = new ArrayList<StorageObjectName>();
for (T spec : specs) {
List<StorageObjectName> match = spec.match(tableRegistry);
for (StorageObjectName n : match) {
if (matcherExpr.match(tableRegistry, n)) {
result.add(n);
}
}
}
return result;
}
private static enum Ops implements OpTerm {
Eq("==", 400), Neq("!=", 400), And("and", 310, true, false, true), Or("or", 300, true, false, true), Not("not", 320,
false, true, true), Comma(",", 200), ListEndComma(",", 200), NOP("", 0, true, false, true);
Ops(String symbol, int precedence) {
this(symbol, precedence, true, false, false);
}
Ops(String symbol, int precedence, boolean leftAssoc, boolean unary, boolean isAlpha) {
this.symbol = symbol;
this.precedence = precedence;
this.leftAssoc = leftAssoc;
this.unary = unary;
this.isAlpha = isAlpha;
}
public String symbol;
public int precedence;
public boolean leftAssoc;
public boolean unary;
public boolean isAlpha;
@Override
public String toString() {
return symbol;
}
@Override
public OpTerm parse(String token) {
for (Ops op : values()) {
if (op.symbol.equals(token) && op != NOP) {
return op;
}
}
return null;
}
@Override
public boolean isInstance(Object o) {
return o instanceof Ops;
}
@Override
public boolean isUnary() {
return unary && this.equals(Not);
}
@Override
public boolean isAlpha() {
return isAlpha;
}
@Override
public boolean isLeftAssoc() {
return leftAssoc;
}
@Override
public boolean isDelimiter(String s) {
for (Ops op : values()) {
if (!op.isAlpha && op.symbol.equals(s)) {
return true;
}
}
return false;
}
@Override
public String getSymbol() {
return symbol;
}
@Override
public int getPrecedence() {
return precedence;
}
@Override
public List<OpTerm> delimiters() {
List<OpTerm> delims = new ArrayList<OpTerm>();
for (Ops op : values()) {
if (!op.isAlpha()) {
delims.add(op);
}
}
return delims;
}
@Override
public OpTerm postProcessCloseParen() {
if (!this.equals(Comma)) {
return this;
}
return ListEndComma;
}
@Override
public boolean hasAltOp() {
return false;
}
@Override
public OpTerm getAltOp() {
return null;
}
}
private static interface TableMatcher {
boolean match(LogTableRegistry tr, StorageObjectName tableName);
}
private static abstract class BinaryExpression implements Expression {
protected Expression lhs;
protected Expression rhs;
public BinaryExpression(Expression lhs, Expression rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public Object eval(Row map) {
return eval(lhs.eval(map), rhs.eval(map));
}
public abstract TableMatcher eval(Object lhs, Object rhs);
}
private static class TableMetadataMatcher implements TableMatcher {
private String key;
private String value;
private Pattern pattern = null;
public TableMetadataMatcher(String key, String value) {
this.key = key;
this.value = value;
this.pattern = WildcardMatcher.buildPattern(value);
}
@Override
public boolean match(LogTableRegistry tr, StorageObjectName o) {
TableSchema schema = tr.getTableSchema(o.getTable(), true);
String rv = schema.getMetadata().get(key);
if (pattern != null) {
if (rv == null)
return false;
else
return pattern.matcher(rv).matches();
} else {
if (value.equals(rv))
return true;
else
return false;
}
}
}
private static class Eq extends BinaryExpression {
public Eq(Expression lhs, Expression rhs) {
super(lhs, rhs);
}
@Override
public TableMatcher eval(Object lhs, Object rhs) {
return new TableMetadataMatcher(lhs.toString(), rhs.toString());
}
}
private static class BooleanMatcher implements TableMatcher {
static enum Type {
And, Or, Not
}
Type type;
TableMatcher lhs;
TableMatcher rhs;
BooleanMatcher(Type type, TableMatcher lhs, TableMatcher rhs) {
this.type = type;
this.lhs = lhs;
this.rhs = rhs;
}
BooleanMatcher(Type type, TableMatcher operand) {
if (!Type.Not.equals(type))
throw new IllegalArgumentException();
this.type = Type.Not;
this.lhs = operand;
}
@Override
public boolean match(LogTableRegistry tr, StorageObjectName o) {
switch (type) {
case And:
return lhs.match(tr, o) && rhs.match(tr, o);
case Or:
return lhs.match(tr, o) || rhs.match(tr, o);
case Not:
return !lhs.match(tr, o);
default:
throw new IllegalStateException(type.toString());
}
}
}
private static class And extends BinaryExpression {
public And(Expression lhs, Expression rhs) {
super(lhs, rhs);
}
@Override
public TableMatcher eval(Object lhs, Object rhs) {
return new BooleanMatcher(BooleanMatcher.Type.And, (TableMatcher) lhs, (TableMatcher) rhs);
}
}
private static class Or extends BinaryExpression {
public Or(Expression lhs, Expression rhs) {
super(lhs, rhs);
}
@Override
public TableMatcher eval(Object lhs, Object rhs) {
return new BooleanMatcher(BooleanMatcher.Type.Or, (TableMatcher) lhs, (TableMatcher) rhs);
}
}
private static class Not implements Expression {
private Expression operand;
public Not(Expression operand) {
this.operand = operand;
}
@Override
public Object eval(Row map) {
return new BooleanMatcher(BooleanMatcher.Type.Not, (TableMatcher) operand.eval(map));
}
}
private static class OpEmitter implements OpEmitterFactory {
@Override
public void emit(Stack<Expression> exprStack, Term term) {
Ops op = (Ops) term;
if (op.isUnary()) {
Expression expr = exprStack.pop();
switch (op) {
case Not: {
exprStack.add(new Not(expr));
break;
}
default:
throw new IllegalStateException("unimplemented unary operator " + op.toString());
}
return;
}
// reversed order by stack
if (exprStack.size() < 2){
// throw new QueryParseException("broken-expression", -1, "operator is [" + op + "]");
Map<String, String> params = new HashMap<String, String>();
params.put("option", op.toString());
throw new QueryParseException("90300", -1 , -1, params);
}
Expression rhs = exprStack.pop();
Expression lhs = exprStack.pop();
switch (op) {
case And:
exprStack.add(new And(lhs, rhs));
break;
case Or:
exprStack.add(new Or(lhs, rhs));
break;
case Eq:
exprStack.add(new Eq(lhs, rhs));
break;
case Neq:
exprStack.add(new Not(new Eq(lhs, rhs)));
break;
case Comma:
exprStack.add(new Comma(lhs, rhs));
break;
case ListEndComma:
exprStack.add(new Comma(lhs, rhs, true));
break;
default:
throw new IllegalStateException("unsupported operator " + op.toString());
}
}
}
private static class TermExpr implements Expression {
private String token;
public TermExpr(String token) {
this.token = token;
}
@Override
public Object eval(Row map) {
return token;
}
}
private static class TermEmitter implements TermEmitterFactory {
@Override
public void emit(Stack<Expression> exprStack, TokenTerm t) {
String token = ((TokenTerm) t).getText().trim();
if (token.startsWith("\"") && token.endsWith("\"")) {
exprStack.add(new TermExpr(token.substring(1, token.length() - 1)));
} else {
exprStack.add(new TermExpr(token));
}
}
}
private static class FuncEmitter implements FuncEmitterFactory {
@Override
public void emit(QueryContext context, Stack<Expression> exprStack, FuncTerm f) {
}
}
}