/* * eXist Open Source Native XML Database * Copyright (C) 2001-06 Wolfgang M. Meier * wolfgang@exist-db.org * http://exist.sourceforge.net * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package org.exist.xquery; import java.util.*; import org.exist.dom.QName; import org.exist.source.Source; import org.exist.xquery.value.Sequence; import org.exist.storage.DBBroker; /** * Default implementation of an {@link org.exist.xquery.ExternalModule}. * * @author Wolfgang Meier (wolfgang@exist-db.org) */ public class ExternalModuleImpl implements ExternalModule { private String mNamespaceURI; private String mPrefix; private String description = "User Defined Module"; private Map<String, String> metadata = null; private boolean isReady = false; final private TreeMap<FunctionId, UserDefinedFunction> mFunctionMap = new TreeMap<FunctionId, UserDefinedFunction>(); final private TreeMap<QName, VariableDeclaration> mGlobalVariables = new TreeMap<QName, VariableDeclaration>(); final private TreeMap<QName, Variable> mStaticVariables = new TreeMap<QName, Variable>(); private Source mSource = null; private XQueryContext mContext = null; public ExternalModuleImpl(String namespaceURI, String prefix) { mNamespaceURI = namespaceURI; mPrefix = prefix; } public void setNamespace(String prefix, String namespace) { this.mPrefix = prefix; this.mNamespaceURI = namespace; } @Override public void setContextItem(Sequence contextItem) { mContext.setContextItem(contextItem); } public void setIsReady(boolean ready) { this.isReady = ready; } public boolean isReady() { return isReady; } /* (non-Javadoc) * @see org.exist.xquery.Module#getDescription() */ public String getDescription() { return description; } public void setDescription(String desc) { this.description = desc; } public void addMetadata(String key, String value) { if (metadata == null) { metadata = new HashMap<String, String>(); } final String old = metadata.get(key); if (old != null) { value = old + ", " + value; } metadata.put(key, value); } public Map<String, String> getMetadata() { return metadata; } /* (non-Javadoc) * @see org.exist.xquery.Module#getReleaseVersion() */ public String getReleaseVersion() { return "user-defined"; } public UserDefinedFunction getFunction(QName qname, int arity, XQueryContext callerContext) throws XPathException { final FunctionId id = new FunctionId(qname, arity); final UserDefinedFunction fn = mFunctionMap.get(id); if (fn == null) {return null;} if (callerContext != getContext() && fn.getSignature().isPrivate()) { throw new XPathException(ErrorCodes.XPST0017, "Calling a private function from outside its module"); } return fn; } /* (non-Javadoc) * @see org.exist.xquery.ExternalModule#declareFunction(org.exist.xquery.UserDefinedFunction) */ public void declareFunction(UserDefinedFunction func) { mFunctionMap.put(func.getSignature().getFunctionId(), func); } /* (non-Javadoc) * @see org.exist.xquery.Module#getNamespaceURI() */ public String getNamespaceURI() { return mNamespaceURI; } /* (non-Javadoc) * @see org.exist.xquery.Module#getDefaultPrefix() */ public String getDefaultPrefix() { return mPrefix; } /* (non-Javadoc) * @see org.exist.xquery.Module#isInternalModule() */ public boolean isInternalModule() { return false; } /* (non-Javadoc) * @see org.exist.xquery.Module#listFunctions() */ public FunctionSignature[] listFunctions() { final List<FunctionSignature> signatures = new ArrayList<FunctionSignature>(mFunctionMap.size()); for (final Iterator<UserDefinedFunction> i = mFunctionMap.values().iterator(); i.hasNext(); ) { final FunctionSignature signature = i.next().getSignature(); signatures.add(signature); } final FunctionSignature[] result = new FunctionSignature[signatures.size()]; return signatures.toArray(result); } /* (non-Javadoc) * @see org.exist.xquery.Module#getSignatureForFunction(org.exist.dom.QName) */ public Iterator<FunctionSignature> getSignaturesForFunction(QName qname) { final ArrayList<FunctionSignature> signatures = new ArrayList<FunctionSignature>(2); for (final UserDefinedFunction func : mFunctionMap.values()) { if (func.getName().compareTo(qname) == 0) {signatures.add(func.getSignature());} } return signatures.iterator(); } public Iterator<QName> getGlobalVariables() { return mGlobalVariables.keySet().iterator(); } public Collection<VariableDeclaration> getVariableDeclarations() { return mGlobalVariables.values(); } /* (non-Javadoc) * @see org.exist.xquery.Module#declareVariable(org.exist.dom.QName, java.lang.Object) */ public Variable declareVariable(QName qname, Object value) throws XPathException { final Sequence val = XPathUtil.javaObjectToXPath(value, mContext); Variable var = mStaticVariables.get(qname); if (var == null) { var = new VariableImpl(qname); mStaticVariables.put(qname, var); } var.setValue(val); return var; } public Variable declareVariable(Variable var) { mStaticVariables.put(var.getQName(), var); return var; } public void declareVariable(QName qname, VariableDeclaration decl) throws XPathException { if (!qname.getNamespaceURI().equals(getNamespaceURI())) {throw new XPathException(decl, ErrorCodes.XQST0048, "It is a static error" + " if a function or variable declared in a library module is" + " not in the target namespace of the library module.");} mGlobalVariables.put(qname, decl); } public boolean isVarDeclared(QName qname) { if (mGlobalVariables.get(qname) != null) {return true;} return mStaticVariables.get(qname) != null; } /* (non-Javadoc) * @see org.exist.xquery.Module#resolveVariable(org.exist.dom.QName) */ public Variable resolveVariable(QName qname) throws XPathException { final VariableDeclaration decl = mGlobalVariables.get(qname); Variable var = mStaticVariables.get(qname); if (isReady && decl != null && (var == null || var.getValue() == null)) { decl.eval(getContext().getContextItem()); var = mStaticVariables.get(qname); } if (var == null) { // external variable may be defined in root context importing this module final Variable rootVar = getContext().getRootContext().resolveGlobalVariable(qname); if (rootVar != null) { var = declareVariable(rootVar); } } // set sequence type if decl != null (might be null if called by parser before declaration) if (var != null && decl != null) { var.setSequenceType(decl.getSequenceType()); } return var; } public void analyzeGlobalVars() throws XPathException { for (final VariableDeclaration decl : mGlobalVariables.values()) { decl.resetState(false); decl.analyze(new AnalyzeContextInfo()); } } public Source getSource() { return mSource; } public void setSource(Source source) { mSource = source; } public void setContext(XQueryContext context) { mContext = context; } public XQueryContext getContext() { return mContext; } /* (non-Javadoc) * @see org.exist.xquery.ExternalModule#moduleIsValid() */ public boolean moduleIsValid(DBBroker broker) { return mSource != null && mSource.isValid(broker) == Source.VALID; } public void reset(XQueryContext xqueryContext) { mContext.reset(); mStaticVariables.clear(); } private Expression rootExpression = null; /** * Set the root expression for this context. * * @param expr */ protected void setRootExpression(Expression expr) { rootExpression = expr; } /** * Returns the root expression associated with this context. * * @return root expression */ public Expression getRootExpression() { return rootExpression; } }