All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.mjorm.mql.InterpreterImpl Maven / Gradle / Ivy


package com.googlecode.mjorm.mql;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonErrorNode;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeAdaptor;
import org.antlr.runtime.tree.Tree;

import com.googlecode.mjorm.ObjectMapper;
import com.googlecode.mjorm.query.DaoModifier;
import com.googlecode.mjorm.query.DaoQuery;
import com.googlecode.mjorm.query.Query;
import com.googlecode.mjorm.query.QueryGroup;
import com.googlecode.mjorm.query.criteria.AbstractQueryCriterion;
import com.googlecode.mjorm.query.criteria.Criterion;
import com.googlecode.mjorm.query.criteria.DocumentCriterion;
import com.googlecode.mjorm.query.criteria.EqualsCriterion;
import com.googlecode.mjorm.query.criteria.FieldCriterion;
import com.googlecode.mjorm.query.criteria.NotCriterion;
import com.googlecode.mjorm.query.criteria.RegexCriterion;
import com.googlecode.mjorm.query.criteria.SimpleCriterion;
import com.googlecode.mjorm.query.criteria.SimpleCriterion.Operator;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.mongodb.WriteResult;

public class InterpreterImpl
	implements Interpreter {

	private static final Map NO_PARAMS
		= Collections.unmodifiableMap(new HashMap());

	private static final CommonTreeAdaptor ADAPTER = new CommonTreeAdaptor() {
		public Object create(Token payload) {
			return new CommonTree(payload);
		}
	};

	private static final Map comparisonOperators
		= new HashMap();

	static {
		comparisonOperators.put(">", Operator.GT);
		comparisonOperators.put(">=", Operator.GTE);
		comparisonOperators.put("<", Operator.LT);
		comparisonOperators.put("<=", Operator.LTE);
		comparisonOperators.put("!=", Operator.NE);
		comparisonOperators.put("<>", Operator.NE);
	}

	private DB db;
	private ObjectMapper objectMapper;
	private Map fieldFunctions;
	private Map documentFunctions;
	private Map variableFunctions;

	/**
	 * Creates it.
	 * @param db
	 * @param objectMapper
	 */
	public InterpreterImpl(DB db, ObjectMapper objectMapper) {
		this.db 				= db;
		this.objectMapper		= objectMapper;
		this.documentFunctions	= new HashMap();
		this.fieldFunctions		= new HashMap();
		this.variableFunctions	= new HashMap();
	}

	/**
	 * Registers a field function.
	 * @param function
	 */
	public void registerFieldFunction(MqlCriterionFunction function) {
		fieldFunctions.put(function.getName().trim().toLowerCase(), function);
	}

	/**
	 * Registers a document function.
	 * @param function
	 */
	public void registerDocumentFunction(MqlCriterionFunction function) {
		documentFunctions.put(function.getName().trim().toLowerCase(), function);
	}

	/**
	 * Registers a variable function.
	 * @param function
	 */
	public void registerVariableFunction(MqlVariableFunction function) {
		variableFunctions.put(function.getName().trim().toLowerCase(), function);
	}

	/**
	 * Compiles the given code return the AST.
	 * @param ips
	 * @return
	 * @throws IOException
	 * @throws RecognitionException
	 */
	public CommonTree compile(InputStream ips)
		throws IOException,
		RecognitionException {

		// create the lexer and parser
		MqlLexer lexer 				= new MqlLexer(new ANTLRUpperCaseInputStream(ips));
		CommonTokenStream tokens 	= new CommonTokenStream(lexer);
		MqlParser parser 			= new MqlParser(tokens);

		// set adapter
		parser.setTreeAdaptor(ADAPTER);

		// parse
		MqlParser.start_return ast = parser.start();

		// verify
		CommonTree tree = CommonTree.class.cast(ast.getTree());
		verifyTree(tree);

		return tree;
	}

	private void verifyTree(CommonTree tree) {
		if (CommonErrorNode.class.isInstance(tree)) {
			throw new MqlException(tree);
		}
		for (int i=0; i interpret(Tree tree) {
		return interpret(tree, NO_PARAMS);
	}

	/**
	 * {@inheritDoc}
	 */
	public List interpret(Tree tree, Object... parameters) {
		Map params = new HashMap();
		for (int i=0; i interpret(
		Tree tree, Map parameters) {
		assertTokenType(tree, MqlParser.COMMANDS);

		// prepare an execution context
		ExecutionContext ctx = new ExecutionContext();
		ctx.params = parameters;
		ctx.currentParameterIndex = 0;

		// execute
		List ret = new ArrayList();
		for (int i=0; i query, ExecutionContext ctx) {
		assertTokenType(tree, MqlParser.CRITERIA);
		for (int i=0; i query, ExecutionContext ctx) {
		DocumentCriterion criterion = null;
		String fieldName = null;
		switch (tree.getType()) {
			case MqlParser.DOCUMENT_FUNCTION_CRITERION:
				String functionName = child(tree, 0).getChild(0).getText().trim().toLowerCase();
				Criterion c = createCriterion(tree, ctx);
				if (!DocumentCriterion.class.isInstance(c)) {
					throw new MqlException(
						"Document function '"+functionName+"' returned a Criterion other than a DocumentCriterion");
				}
				criterion = DocumentCriterion.class.cast(c);
				break;
				
			case MqlParser.FIELD_FUNCTION_CRITERION:
				fieldName = child(tree, 0).getText().trim();
				criterion = new FieldCriterion(fieldName, createCriterion(tree, ctx));
				break;
				
			case MqlParser.COMPARE_CRITERION:
				fieldName = child(tree, 0).getText().trim();
				criterion = new FieldCriterion(fieldName, createCriterion(tree, ctx));
				break;
				
			case MqlParser.NEGATED_CRITERION:
				fieldName = child(tree, 0).getChild(0).getText().trim();
				criterion = new NotCriterion(fieldName, createCriterion(child(tree, 0), ctx));
				break;
				
			default:
				assertTokenType(tree);
		}
		query.add(criterion);
	}

	/**
	 * Creates a {@link Criterion} from the given tree.
	 * @param tree
	 * @return
	 */
	private Criterion createCriterion(CommonTree tree, ExecutionContext ctx) {
		switch (tree.getType()) {
			case MqlParser.DOCUMENT_FUNCTION_CRITERION:
				return readCriterionForFunctionCall(child(tree, 0), documentFunctions, ctx);
				
			case MqlParser.FIELD_FUNCTION_CRITERION:
				return readCriterionForFunctionCall(child(tree, 1), fieldFunctions, ctx);
				
			case MqlParser.COMPARE_CRITERION:
				String op = child(tree, 1).getText();
				Object value = readVariableLiteral(child(tree, 2), ctx);
				if (op.equals("=")) {
					return new EqualsCriterion(value);
				} else if (op.equals("=~")) {
					assertType(value, tree, Pattern.class);
					return new RegexCriterion(Pattern.class.cast(value));
				}
				return new SimpleCriterion(comparisonOperators.get(op), value);
				
			case MqlParser.NEGATED_CRITERION:
				Criterion c = createCriterion(child(tree, 0), ctx);
				if (!FieldCriterion.class.isInstance(c)) {
					throw new MqlException(
						"NOT requires FieldCriteiron");
				}
				return new NotCriterion(FieldCriterion.class.cast(c));
				
			default:
				assertTokenType(tree);
				return null;
		}
	}

	/**
	 * Creates a {@link Criterion} for the given function call.
	 * @param tree
	 * @param functionTable
	 * @return
	 */
	private Criterion readCriterionForFunctionCall(
		CommonTree tree, Map functionTable, ExecutionContext ctx) {
		assertTokenType(tree, MqlParser.FUNCTION_CALL);

		// get the function name
		String functionName = child(tree, 0).getText().trim().toLowerCase();
		Criterion ret = null;

		// function not found
		if (!functionTable.containsKey(functionName)) {
			throw new MqlException(
				"Unknown function: "+functionName);

		// no arguments
		} else if (tree.getChildCount()==1) {
			ret = functionTable.get(functionName).createForNoArguments();

		// criteria arguments
		} else if (child(tree, 1).getType()==MqlParser.CRITERIA) {
			Query query = new Query();
			readCriteria(child(tree, 1), query, ctx);
			ret = functionTable.get(functionName).createForQuery(query);

		// criteria arguments
		} else if (child(tree, 1).getType()==MqlParser.CRITERIA_GROUP_LIST) {
			QueryGroup queryGroup = new QueryGroup();
			readCriteriaGroupList(child(tree, 1), queryGroup, ctx);
			ret = functionTable.get(functionName).createForQueryGroup(queryGroup);

		// variable list arguments
		} else if (child(tree, 1).getType()==MqlParser.VARIABLE_LIST) {
			Object[] arguments = readVariableList(child(tree, 1), ctx);
			ret = functionTable.get(functionName).createForArguments(arguments);
		}

		// return it
		return ret;
	}

	private QueryGroup readCriteriaGroupList(CommonTree tree, QueryGroup queryGroup, ExecutionContext ctx) {
		assertTokenType(tree, MqlParser.CRITERIA_GROUP_LIST);
		if (queryGroup==null) {
			queryGroup = new QueryGroup();
		}
		for (int i=0; i1
					? readVariableList(child(tree, 1), ctx) : new Object[0];
				return variableFunctions.get(functionName).invoke(args);
			case MqlParser.PARAMETER:
				String name = child(tree, 0).getText();
				return ctx.getParameter(name, tree);
			case MqlParser.REGEX:
				return Pattern.compile(text.substring(1, text.length()-1));
			case MqlParser.INTEGER:
				return new Integer(text);
			case MqlParser.DECIMAL:
				return new Double(text);
			case MqlParser.DOUBLE_QUOTED_STRING:
				return evaluateString(text);
			case MqlParser.SINGLE_QUOTED_STRING:
				return evaluateString(text);
			case MqlParser.TRUE:
				return Boolean.TRUE;
			case MqlParser.FALSE:
				return Boolean.FALSE;
			case MqlParser.ARRAY:
				Object[] vars = new Object[child(tree, 0).getChildCount()];
				for (int i=0; i... types) {
		if (value==null) {
			return;
		}
		for (Class type : types) {
			if (type.isInstance(value)) {
				return;
			}
		}
		throw new MqlException(tree, "Unexpected variable type");
	}

	/**
	 * A simple execution context.
	 */
	private class ExecutionContext {
		private Map params		= NO_PARAMS;
		private int currentParameterIndex 		= 0;
		private Object getParameter(String name, CommonTree tree) {
			if (name.equals("?")) {
				name = currentParameterIndex+"";
				currentParameterIndex++;
			}
			if (!params.containsKey(name)) {
				throw new MqlException(tree, "Parameter "+name+" was not found");
			}
			return params.get(name);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy