/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* !#
*/
package net.ontopia.topicmaps.query.parser;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.Collections;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.core.TMObjectIF;
import net.ontopia.topicmaps.query.core.InvalidQueryException;
import net.ontopia.topicmaps.impl.utils.Argument;
import net.ontopia.topicmaps.impl.utils.ArgumentValidator;
import net.ontopia.topicmaps.query.impl.basic.QueryMatches;
import net.ontopia.topicmaps.query.impl.basic.QueryContext;
import net.ontopia.topicmaps.query.impl.utils.QueryMatchesUtils;
/**
* INTERNAL: Represents an UPDATE or DELETE statement, since these are
* the ones that can have functions. Created so the parser can handle
* both cases with the same code.
*/
public abstract class ModificationFunctionStatement
extends ModificationStatement {
protected String funcname; // name of delete function to be called, if any
protected static Map<String, ModificationFunctionIF> functions;
static {
functions = new HashMap<String, ModificationFunctionIF>();
}
public ModificationFunctionStatement() {
super();
}
public void setFunction(String name) {
this.funcname = name;
}
public String getFunction() {
return funcname;
}
// --- Internal helpers
protected String toStringFunction() {
return funcname + "(" + toStringLitlist() + ")";
}
public int doStaticUpdates(TopicMapIF topicmap, Map arguments)
throws InvalidQueryException {
if (funcname == null)
return doLitListDeletes(true, arguments);
else {
// in order to avoid duplicating code we produce a "fake" matches
// object here, so that in effect we're simulating a one-row zero-column
// result set
QueryContext context = new QueryContext(null, null, arguments, null);
Collection columns = arguments == null ? Collections.EMPTY_SET :
arguments.values();
QueryMatches matches =
QueryMatchesUtils.createInitialMatches(context, columns);
return doFunctionUpdates(matches);
}
}
protected abstract int doLitListDeletes(boolean strict, Map arguments)
throws InvalidQueryException;
// generic method for traversing result set and calling functions
protected int doFunctionUpdates(QueryMatches matches)
throws InvalidQueryException {
int rows = 0;
ModificationFunctionIF function = makeFunction(funcname);
FunctionSignature signature = FunctionSignature.getSignature(function);
QueryContext context = matches.getQueryContext();
Map parameters = Collections.EMPTY_MAP;
if (context != null)
parameters = context.getParameters();
Object arg1 = getValue(litlist.get(0), parameters);
int varix1 = getIndex(arg1, matches);
Object arg2 = getValue(litlist.get(1), parameters);
int varix2 = getIndex(arg2, matches);
for (int row = 0; row <= matches.last; row++) {
if (varix1 != -1)
arg1 = matches.data[row][varix1];
if (varix2 != -1)
arg2 = matches.data[row][varix2];
signature.validateArguments(arg1, arg2, funcname);
function.modify((TMObjectIF) arg1, arg2);
rows++;
}
return rows;
}
// --- Functions and signatures
protected static ModificationFunctionIF makeFunction(String name)
throws InvalidQueryException {
ModificationFunctionIF function = functions.get(name);
if (function == null)
throw new InvalidQueryException("No such function: '" + name + "'");
return function;
}
interface ModificationFunctionIF {
public String getSignature();
public void modify(TMObjectIF object, Object value);
}
static class FunctionSignature extends ArgumentValidator {
private static Map cache = new HashMap(); // used to avoid having to reparse
public static FunctionSignature getSignature(ModificationFunctionIF function)
throws InvalidQueryException {
String sign = function.getSignature();
FunctionSignature signature = (FunctionSignature) cache.get(sign);
if (signature == null) {
signature = new FunctionSignature(sign);
cache.put(sign, signature);
}
return signature;
}
private FunctionSignature(String signature) {
super(signature);
}
public void validateArguments(Object arg1, Object arg2, String function)
throws InvalidQueryException {
check(arg1, getArgument(0), function, 1);
check(arg2, getArgument(1), function, 2);
}
public void check(Object arg, Argument reqarg, String function, int no)
throws InvalidQueryException {
if (!reqarg.allows(arg.getClass()))
throw new InvalidQueryException("Function " + function +
" does not accept " +
arg +
" as parameter no " + no +
", but requires " +
getClassList(reqarg.getTypes()));
}
}
}