/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.resolver;
// Java 2 standard packages
import java.util.*;
// Third party packages
import org.apache.log4j.Logger; // Log4J
// Locally written packages
import org.mulgara.query.*;
/**
* Test case for {@link DatabaseSession}.
*
* @created 2004-06-15
* @author <a href="http://staff.pisoftware.com/andrae">Andrae Muys</a>
* @version $Revision: 1.8 $
* @modified $Date: 2005/01/05 04:58:24 $Author: newmana $
* @company <a href="mailto:info@PIsoftware.com">Plugged In Software</a>
* @copyright © 2004 <a href="http://www.PIsoftware.com/">Plugged In
* Software Pty Ltd</a>
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public class TestDef {
/** Logger. */
private static Logger logger = Logger.getLogger(TestDef.class.getName());
public String name;
public String[] resolvers;
public List<Variable> selectList;
public GraphExpression model;
public ConstraintExpression query;
public List<List<Object>> results;
public String errorString;
@SuppressWarnings("unchecked")
public TestDef(String name, String[] resolvers, TestQuery query,
GraphExpression model, List results, String errorString) {
this.name = name;
this.resolvers = resolvers;
this.model = model;
this.selectList = query.selectList;
this.query = query.query;
this.results = results;
this.errorString = errorString;
}
static private class TestQuery {
public List<Variable> selectList;
public ConstraintExpression query;
public TestQuery(List<Variable> selectList, ConstraintExpression query) {
this.selectList = selectList;
this.query = query;
}
}
static public class Parser {
private ConstraintElement[] elements;
private GraphResource[] models;
public Parser(ConstraintElement[] elements, GraphResource[] models) {
this.elements = elements;
this.models = models;
}
public TestDef parse(String name, String[] resolvers, String query,
String model, String resultDefs, String errorString) {
return new TestDef(name, resolvers,
query == null ? null : parseQuery(new StringTokenizer(query, "() ", true)),
model == null ? null : parseModel(new StringTokenizer(model, "() ", true)),
resultDefs == null ? null : parseResultDefinition(new StringTokenizer(resultDefs, "() ", true)),
errorString);
}
private String getToken(StringTokenizer tokenizer) {
String token;
do {
token = tokenizer.nextToken();
} while (token.equals(" "));
logger.debug("Returning token '" + token + "'");
return token;
}
private GraphExpression parseModel(StringTokenizer tokenizer) {
String token = getToken(tokenizer);
if ("(".equals(token)) {
return parseModelOperation(tokenizer);
}
if (token.startsWith("M")) {
return parseModelResource(token);
}
throw new IllegalArgumentException("Unrecognized token in modelExpression " + token);
}
private GraphOperation parseModelOperation(StringTokenizer tokenizer) {
String token = getToken(tokenizer);
GraphExpression lhs = parseModel(tokenizer);
GraphExpression rhs = parseModel(tokenizer);
String terminator = getToken(tokenizer);
if (!")".equals(terminator)) {
throw new IllegalArgumentException("Unterminated GraphOperation " + terminator);
}
if ("union".equals(token)) {
return new GraphUnion(lhs, rhs);
}
if ("intersect".equals(token)) {
return new GraphIntersection(lhs, rhs);
}
throw new IllegalArgumentException("Unknown GraphOperation " + token);
}
private GraphResource parseModelResource(String token) {
try {
int index = Integer.parseInt(token.substring(1));
if (index > models.length) {
throw new IllegalArgumentException("Invalid GraphResource index " + index);
}
return models[index];
} catch (NumberFormatException en) {
throw new IllegalArgumentException("Invalid GraphResource descriptor" + token);
}
}
private TestQuery parseQuery(StringTokenizer tokenizer) {
String token = getToken(tokenizer);
if ("(".equals(token)) {
return parseQueryExpression(tokenizer);
} else {
throw new IllegalArgumentException("Invalid initial token in modelExpression " + token);
}
}
private TestQuery parseQueryExpression(StringTokenizer tokenizer) {
String token = getToken(tokenizer);
if ("query".equals(token)) {
List<Variable> selectList = parseSelectList(tokenizer);
token = getToken(tokenizer);
if (!"(".equals(token)) {
throw new IllegalArgumentException("Query's ConstraintExpression must be an s-expr " + token);
}
ConstraintExpression expr = parseConstraintExpression(tokenizer);
return new TestQuery(selectList, expr);
} else {
throw new IllegalArgumentException("Expected query-expression to start with query " + token);
}
}
private List<Variable> parseSelectList(StringTokenizer tokenizer) {
String token = getToken(tokenizer);
if ("(".equals(token)) {
return parseVariableList(tokenizer);
} else {
return Collections.singletonList(parseVariable(token));
}
}
private List<Variable> parseVariableList(StringTokenizer tokenizer) {
List<Variable> variableList = new ArrayList<Variable>();
String token = getToken(tokenizer);
while (!")".equals(token)) {
variableList.add(parseVariable(token));
token = getToken(tokenizer);
}
return variableList;
}
private Variable parseVariable(String token) {
try {
int index = Integer.parseInt(token.substring(1));
if (index > elements.length) {
throw new IllegalArgumentException("Invalid Variable index " + index);
}
if (elements[index] instanceof Variable) {
return (Variable)elements[index];
} else {
throw new IllegalArgumentException("Variable reference not a variable " + token);
}
} catch (NumberFormatException en) {
throw new IllegalArgumentException("Invalid Variable descriptor" + token);
}
}
private ConstraintExpression parseConstraintExpression(StringTokenizer tokenizer) {
String token = getToken(tokenizer);
if ("and".equals(token)) {
return new ConstraintConjunction(parseConstraintArguments(tokenizer));
}
if ("or".equals(token)) {
return new ConstraintDisjunction(parseConstraintArguments(tokenizer));
}
if ("|".equals(token)) {
return parseConstraint(tokenizer);
}
throw new IllegalArgumentException("Invalid ConstraintExpression " + token);
}
private List<ConstraintExpression> parseConstraintArguments(StringTokenizer tokenizer) {
List<ConstraintExpression> arguments = new ArrayList<ConstraintExpression>();
while (true) {
String token = getToken(tokenizer);
if ("(".equals(token)) {
arguments.add(parseConstraintExpression(tokenizer));
} else if (")".equals(token)) {
break;
} else {
throw new IllegalArgumentException("Arguments ConstraintExpression must be an s-expr " + token);
}
}
return arguments;
}
private Constraint parseConstraint(StringTokenizer tokenizer) {
Constraint constraint =
new ConstraintImpl(parseConstraintElement(getToken(tokenizer)),
parseConstraintElement(getToken(tokenizer)),
parseConstraintElement(getToken(tokenizer)));
String term = getToken(tokenizer);
if (")".equals(term)) {
return constraint;
} else {
throw new IllegalArgumentException("Too many constraint elements in constraint " + term);
}
}
private ConstraintElement parseConstraintElement(String token) {
try {
int index = Integer.parseInt(token.substring(1));
if (index > elements.length) {
throw new IllegalArgumentException("Invalid ConstraintElement index " + index);
}
return elements[index];
} catch (NumberFormatException en) {
throw new IllegalArgumentException("Invalid ConstraintElement descriptor" + token);
}
}
/**
* @return A list of lists (to arbitrary depth) of result-strings.
*/
@SuppressWarnings("unchecked")
private List parseResultDefinition(StringTokenizer tokenizer) {
logger.debug("Parsing Result Definition");
String token = getToken(tokenizer);
if ("(".equals(token)) {
return parseResultExpression(tokenizer);
} else {
throw new IllegalArgumentException("Invalid initial token in modelExpression " + token);
}
}
/**
* @return A list of lists (to arbitrary depth) of result-strings.
*/
@SuppressWarnings("unchecked")
private List parseResultExpression(StringTokenizer tokenizer) {
logger.debug("parseResultExpression");
String token = getToken(tokenizer);
if ("result".equals(token)) {
List<List<String>> result = parseResultList(tokenizer);
logger.debug("returning result-list-expression: " + result);
return result;
}
if ("product".equals(token)) {
LinkedList productTerm = parseProductTerms(tokenizer);
List product = produceProduct(productTerm);
logger.debug("returning result-product-expression: " + product);
return product;
}
if ("divide".equals(token)) {
LinkedList divideTerm = parseDivideTerms(tokenizer);
List divisor = parseResultDefinition(tokenizer);
List divand = produceDivide(divideTerm, divisor);
logger.debug("returning result-divide-expression: " + divand);
return divand;
}
throw new IllegalArgumentException("Only results and products supported in result expression " + token);
}
/**
* @return A list of lists of result-strings.
*/
private List<List<String>> parseResultList(StringTokenizer tokenizer) {
logger.debug("parseResultList");
List<List<String>> result = new ArrayList<List<String>>();
String token = getToken(tokenizer);
while (!")".equals(token)) {
if (token.equals("(")) {
// If a token starts with a bracket then it is a multi part token so
// we need to store the entire string (until we reach the close bracket)
// Create a temporary token to store our tokenised string
String tempToken = "";
List<String> tempList = new ArrayList<String>();
// Get the next token
token = getToken(tokenizer);
while (!token.equals(")")) {
// While we haven't reached the end of a bracketed token, keep
// adding to the temporary token
tempToken += token + " ";
tempList.add(token);
// Get the next token
token = getToken(tokenizer);
}
// Store the completed token
token = tempToken.trim();
result.addAll(parseResult(token));
//result.add(tempList);
token = getToken(tokenizer);
} else {
result.addAll(parseResult(token));
token = getToken(tokenizer);
}
}
logger.debug("returning result-list: " + result);
return result;
}
/**
* @return A list of result-strings.
*/
private List<List<String>> parseResult(String token) {
logger.debug("parsing result " + token);
List<List<String>> result = new ArrayList<List<String>>();
if (token.startsWith("p") || token.startsWith("o") || token.startsWith("s")) {
if (token.endsWith("*")) {
token = token.substring(0, token.length() - 1);
result.add(Collections.singletonList("test:" + token + "01"));
result.add(Collections.singletonList("test:" + token + "02"));
result.add(Collections.singletonList("test:" + token + "03"));
} else {
result.add(Collections.singletonList("test:" + token));
}
} else {
result.add(Collections.singletonList(token));
}
logger.debug("returning result: " + result);
return result;
}
/**
* @return A list of lists of lists of result-strings.
*
* Specifically a list of result-expressions.
*/
@SuppressWarnings("unchecked")
private LinkedList parseProductTerms(StringTokenizer tokenizer) {
logger.debug("parseProductTerms");
LinkedList result = new LinkedList();
String token = getToken(tokenizer);
while (!")".equals(token)) {
if ("(".equals(token)) {
result.add(parseResultExpression(tokenizer));
token = getToken(tokenizer);
} else {
throw new IllegalArgumentException("Product Term must be an s-expr " + token);
}
}
return result;
}
/**
* @param productTerms A list of lists of lists of result-strings, from parseProductTerms.
* @return A list of lists of result-strings.
*/
@SuppressWarnings("unchecked")
private List produceProduct(LinkedList productTerms) {
logger.debug("produceProduct");
if (productTerms.size() == 1) {
return (List)productTerms.get(0);
} else {
List lhs = (List)productTerms.removeFirst();
logger.debug("productTerms : " + productTerms);
logger.debug("car : " + lhs);
logger.debug("cdr : " + productTerms);
List rhs = produceProduct(productTerms);
List result = new ArrayList();
Iterator i = lhs.iterator();
while (i.hasNext()) {
List leftResult = (List)i.next();
Iterator j = rhs.iterator();
while (j.hasNext()) {
List rightResult = (List)j.next();
List combined = new ArrayList(leftResult);
combined.addAll(rightResult);
result.add(combined);
}
}
return result;
}
}
@SuppressWarnings("unchecked")
private LinkedList parseDivideTerms(StringTokenizer tokenizer) {
String token = getToken(tokenizer);
if (!"(".equals(token)) {
throw new IllegalArgumentException("Divide requires s-expr for foreach " + token);
}
token = getToken(tokenizer);
if (!"foreach".equals(token)) {
throw new IllegalArgumentException("Divide requires foreach " + token);
}
return parseForeachResults(tokenizer);
}
@SuppressWarnings("unchecked")
private LinkedList parseForeachResults(StringTokenizer tokenizer) {
LinkedList result = new LinkedList();
String token = getToken(tokenizer);
while ("(".equals(token)) {
result.add(parseResultExpression(tokenizer));
token = getToken(tokenizer);
}
if (!")".equals(token)) {
throw new IllegalArgumentException("Failed to properly terminate foreach result-list " + token);
}
return result;
}
@SuppressWarnings("unchecked")
private List produceDivide(List divideTerm, List divisor) {
List result = new ArrayList();
Iterator i = divideTerm.iterator();
while (i.hasNext()) {
List divideResult = (List)i.next();
result.addAll(produceDivideResult(divideResult, divisor));
}
return result;
}
@SuppressWarnings("unchecked")
private List produceDivideResult(List divideResult, List divisor) {
List result = new ArrayList();
assert divisor.size() % divideResult.size() == 0;
int multiples = divisor.size() / divideResult.size();
Iterator iter = divisor.iterator();
int c = 0;
while (iter.hasNext()) {
List row = new ArrayList();
row.addAll((List)(divideResult.get(c++ / multiples)));
row.addAll((List)iter.next());
result.add(row);
}
return result;
}
}
}