/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-2009 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Id$
*/
package org.exist.xquery;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;
import org.apache.log4j.Logger;
import org.exist.dom.QName;
import org.exist.xquery.value.Sequence;
/**
* Abstract base class for an {@link org.exist.xquery.InternalModule}.
* Functions are defined in an array of {@link org.exist.xquery.FunctionDef}, which
* is passed to the constructor. A single implementation class
* can be registered for more than one function signature, given that the signatures differ
* in name or the number of expected arguments. It is thus possible to implement
* similar XQuery functions in one single class.
*
* @author Wolfgang Meier (wolfgang@exist-db.org)
* @author ljo
*
*/
public abstract class AbstractInternalModule implements InternalModule {
private final static Logger LOG = Logger.getLogger(AbstractInternalModule.class);
public static class FunctionComparator implements Comparator {
public int compare(Object o1, Object o2) {
FunctionDef d1 = (FunctionDef) o1;
return d1.getSignature().getFunctionId().compareTo(((FunctionDef) o2).getSignature().getFunctionId());
}
};
protected FunctionDef[] mFunctions;
protected boolean ordered = false;
protected final TreeMap mGlobalVariables = new TreeMap();
public AbstractInternalModule(FunctionDef[] functions) {
this(functions, false);
}
public AbstractInternalModule(FunctionDef[] functions, boolean functionsOrdered) {
this.mFunctions = functions;
this.ordered = functionsOrdered;
}
private AbstractInternalModule() {
}
/* (non-Javadoc)
* @see org.exist.xquery.Module#isInternalModule()
*/
public boolean isInternalModule() {
return true;
}
public boolean isReady() {
return true; // internal modules don't need to be compiled
}
/* (non-Javadoc)
* @see org.exist.xquery.Module#getNamespaceURI()
*/
public abstract String getNamespaceURI();
/* (non-Javadoc)
* @see org.exist.xquery.Module#getDefaultPrefix()
*/
public abstract String getDefaultPrefix();
/**
* Module description for the documentation.
* This is a short phrase describing the module, starting with:
* A module for.
*/
public abstract String getDescription();
/**
* Module release version for the documentation. Since a module can live
* in trunk a long time before it is included in a release,
* this is neccessary for the documentation.
*/
public abstract String getReleaseVersion();
/* (non-Javadoc)
* @see org.exist.xquery.Module#listFunctions()
*/
public FunctionSignature[] listFunctions() {
FunctionSignature signatures[] = new FunctionSignature[mFunctions.length];
for(int i = 0; i < signatures.length; i++)
signatures[i] = mFunctions[i].getSignature();
return signatures;
}
public Iterator getSignaturesForFunction(QName qname) {
ArrayList signatures = new ArrayList(2);
for(int i = 0; i < mFunctions.length; i++) {
FunctionSignature signature = mFunctions[i].getSignature();
if(signature.getName().compareTo(qname) == 0)
signatures.add(signature);
}
return signatures.iterator();
}
/* (non-Javadoc)
* @see org.exist.xquery.Module#getClassForFunction(org.exist.dom.QName)
*/
public FunctionDef getFunctionDef(QName qname, int arity) {
FunctionId id = new FunctionId(qname, arity);
if (ordered) {
return binarySearch(id);
} else {
for (int i = 0; i < mFunctions.length; i++) {
if (id.compareTo(mFunctions[i].getSignature().getFunctionId()) == 0)
return mFunctions[i];
}
}
return null;
}
private FunctionDef binarySearch(FunctionId id) {
int low = 0;
int high = mFunctions.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
FunctionDef midVal = mFunctions[mid];
int cmp = midVal.getSignature().getFunctionId().compareTo(id);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return midVal; // key found
}
return null; // key not found.
}
public List getFunctionsByName(QName qname) {
List funcs = new ArrayList();
for (int i = 0; i < mFunctions.length; i++) {
FunctionSignature sig = mFunctions[i].getSignature();
if (sig.getName().compareTo(qname) == 0) {
funcs.add(sig);
}
}
return funcs;
}
public Variable declareVariable(QName qname, Object value) throws XPathException {
Sequence val = XPathUtil.javaObjectToXPath(value, null);
Variable var = (Variable)mGlobalVariables.get(qname);
if(var == null) {
var = new Variable(qname);
mGlobalVariables.put(qname, var);
}
var.setValue(val);
return var;
}
public Variable declareVariable(Variable var) {
mGlobalVariables.put(var.getQName(), var);
return var;
}
/* (non-Javadoc)
* @see org.exist.xquery.Module#resolveVariable(org.exist.dom.QName)
*/
public Variable resolveVariable(QName qname) throws XPathException {
return (Variable)mGlobalVariables.get(qname);
}
public boolean isVarDeclared(QName qname) {
return mGlobalVariables.get(qname) != null;
}
public void reset(XQueryContext xqueryContext) {
}
}