/*
* eXist Open Source Native XML Database
* Copyright (C) 2004-2007 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 org.exist.debuggee.DebuggeeJoint;
import org.exist.dom.DocumentSet;
import org.exist.dom.QName;
import org.exist.memtree.MemTreeBuilder;
import org.exist.storage.DBBroker;
import org.exist.storage.UpdateListener;
import org.exist.util.FileUtils;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.value.AnyURIValue;
import javax.xml.datatype.XMLGregorianCalendar;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Map;
/**
* Subclass of {@link org.exist.xquery.XQueryContext} for
* imported modules.
*
* @author wolf
*/
public class ModuleContext extends XQueryContext {
private XQueryContext parentContext;
private final String modulePrefix;
private final String moduleNamespace;
private final String location;
/**
* @param parentContext
*/
public ModuleContext(XQueryContext parentContext, String modulePrefix, String moduleNamespace, String location) {
super(parentContext.getAccessContext());
this.moduleNamespace = moduleNamespace;
this.modulePrefix = modulePrefix;
this.location = location;
setParentContext(parentContext);
loadDefaults(broker.getConfiguration());
}
String getLocation() {
return location;
}
String getModuleNamespace() {
return moduleNamespace;
}
void setModulesChanged() {
parentContext.setModulesChanged();
}
public void setParentContext(XQueryContext parentContext) {
this.parentContext = parentContext;
if (parentContext != null) {
this.broker = parentContext.broker;
baseURI = parentContext.baseURI;
try {
if (location.startsWith(XmldbURI.XMLDB_URI_PREFIX) ||
(location.indexOf(':') < 0 &&
parentContext.moduleLoadPath.startsWith(XmldbURI.XMLDB_URI_PREFIX))) {
// use XmldbURI resolution - unfortunately these are not interpretable as URIs
// because the scheme xmldb:exist: is not a valid URI scheme
XmldbURI locationUri = XmldbURI.xmldbUriFor(FileUtils.dirname(location));
if (parentContext.moduleLoadPath.equals ("."))
moduleLoadPath = locationUri.toString();
else {
XmldbURI parentLoadUri = XmldbURI.xmldbUriFor(parentContext.moduleLoadPath);
XmldbURI moduleLoadUri = parentLoadUri.resolveCollectionPath(locationUri);
moduleLoadPath = moduleLoadUri.toString();
}
} else {
String dir = FileUtils.dirname(location);
if (parentContext.moduleLoadPath.equals(".")) {
if (! dir.equals(".")) {
if (dir.startsWith("/"))
moduleLoadPath = "." + dir;
else
moduleLoadPath = "./" + dir;
}
} else {
if (dir.startsWith("/"))
moduleLoadPath = dir;
else
moduleLoadPath = FileUtils.addPaths(parentContext.moduleLoadPath, dir);
}
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
public void setModule(String namespaceURI, Module module) {
if (module == null) {
modules.remove(namespaceURI); // unbind the module
} else {
modules.put(namespaceURI, module);
if (!module.isInternalModule() && module.isReady()) {
((ModuleContext) ((ExternalModule) module).getContext()).setParentContext(parentContext);
}
}
setRootModule(namespaceURI, module);
}
XQueryContext getParentContext() {
return parentContext;
}
public boolean hasParent() {
return true;
}
public XQueryContext getRootContext() {
return parentContext.getRootContext();
}
public void updateContext(XQueryContext from) {
if (from.hasParent())
// TODO: shouldn't this call setParentContext ? - sokolov
this.parentContext = ((ModuleContext)from).parentContext;
}
public XQueryContext copyContext() {
ModuleContext ctx = new ModuleContext(parentContext, modulePrefix, moduleNamespace, location);
copyFields(ctx);
try {
ctx.declareNamespace(modulePrefix, moduleNamespace);
} catch (XPathException e) {
e.printStackTrace();
}
return ctx;
}
public void addDynamicOption(String qnameString, String contents) throws XPathException
{
parentContext.addDynamicOption(qnameString, contents);
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#getStaticallyKnownDocuments()
*/
public DocumentSet getStaticallyKnownDocuments() throws XPathException {
return parentContext.getStaticallyKnownDocuments();
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#getModule(java.lang.String)
*/
public Module getModule(String namespaceURI) {
Module module = super.getModule(namespaceURI);
// TODO: I don't think modules should be able to access their parent context's modules,
// since that breaks lexical scoping. However, it seems that some eXist modules rely on
// this so let's leave it for now. (pkaminsk2)
if(module == null)
module = parentContext.getModule(namespaceURI);
return module;
}
protected void setRootModule(String namespaceURI, Module module) {
allModules.put(namespaceURI, module);
parentContext.setRootModule(namespaceURI, module);
}
public Iterator getRootModules() {
return parentContext.getRootModules();
}
public Iterator getAllModules() {
return allModules.values().iterator();
}
public Module getRootModule(String namespaceURI) {
return parentContext.getRootModule(namespaceURI);
}
/**
* Overwritten method: the module will be loaded by the parent context, but
* we need to declare its namespace in the module context.
*/
public Module loadBuiltInModule(String namespaceURI, String moduleClass) {
Module module = getModule(namespaceURI);
if (module == null)
module = initBuiltInModule(namespaceURI, moduleClass);
if (module != null) {
try {
String defaultPrefix = module.getDefaultPrefix();
if (!"".equals(defaultPrefix))
declareNamespace(defaultPrefix, module.getNamespaceURI());
} catch (XPathException e) {
LOG.warn("error while loading builtin module class " + moduleClass, e);
}
}
return module;
}
public void updateModuleRefs(XQueryContext rootContext) {
HashMap newModules = new HashMap(modules.size());
for (Iterator i = modules.values().iterator(); i.hasNext(); ) {
Module module = (Module) i.next();
if (module.isInternalModule()) {
Module updated = rootContext.getModule(module.getNamespaceURI());
if (updated == null)
updated = module;
newModules.put(module.getNamespaceURI(), updated);
} else
newModules.put(module.getNamespaceURI(), module);
}
modules = newModules;
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#getWatchDog()
*/
public XQueryWatchDog getWatchDog() {
return parentContext.getWatchDog();
}
public Profiler getProfiler() {
return parentContext.getProfiler();
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#getCalendar()
*/
public XMLGregorianCalendar getCalendar(){
return parentContext.getCalendar();
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#getBaseURI()
*/
public AnyURIValue getBaseURI() throws XPathException {
return parentContext.getBaseURI();
}
public void setBaseURI(AnyURIValue uri) {
parentContext.setBaseURI(uri);
}
/**
* Delegate to parent context
*
* @see org.exist.xquery.XQueryContext#setXQueryContextVar(String, Object)
*/
public void setXQueryContextVar(String name, Object XQvar)
{
parentContext.setXQueryContextVar(name, XQvar);
}
/**
* Delegate to parent context
*
* @see org.exist.xquery.XQueryContext#getXQueryContextVar(String)
*/
public Object getXQueryContextVar(String name)
{
return(parentContext.getXQueryContextVar(name));
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#getBroker()
*/
public DBBroker getBroker() {
return parentContext.getBroker();
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#getDocumentBuilder()
*/
public MemTreeBuilder getDocumentBuilder() {
return parentContext.getDocumentBuilder();
}
public MemTreeBuilder getDocumentBuilder(boolean explicitCreation) {
return parentContext.getDocumentBuilder(explicitCreation);
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#pushDocumentContext()
*/
public void pushDocumentContext() {
parentContext.pushDocumentContext();
}
public LocalVariable markLocalVariables(boolean newContext) {
return parentContext.markLocalVariables(newContext);
}
public void popLocalVariables(LocalVariable var) {
parentContext.popLocalVariables(var);
}
public LocalVariable declareVariableBinding(LocalVariable var) throws XPathException {
return parentContext.declareVariableBinding(var);
}
protected Variable resolveLocalVariable(QName qname) throws XPathException {
return parentContext.resolveLocalVariable(qname);
}
/**
* Try to resolve a variable.
*
* @param qname the qualified name of the variable
* @return the declared Variable object
* @throws XPathException if the variable is unknown
*/
public Variable resolveVariable(QName qname) throws XPathException {
Variable var;
// check if the variable is declared local
var = resolveLocalVariable(qname);
// check if the variable is declared in a module
if (var == null) {
Module module;
if (moduleNamespace.equals(qname.getNamespaceURI())) {
module = getRootModule(moduleNamespace);
} else {
module = getModule(qname.getNamespaceURI());
}
if(module != null) {
var = module.resolveVariable(qname);
}
}
// check if the variable is declared global
if (var == null)
var = globalVariables.get(qname);
//if (var == null)
// throw new XPathException("variable $" + qname + " is not bound");
return var;
}
public Map<QName, Variable> getVariables() {
return parentContext.getVariables();
}
public int getCurrentStackSize() {
return parentContext.getCurrentStackSize();
}
/* (non-Javadoc)
* @see org.exist.xquery.XQueryContext#popDocumentContext()
*/
public void popDocumentContext() {
parentContext.popDocumentContext();
}
/**
* First checks the parent context for in-scope namespaces,
* then the module's static context.
*
* @param prefix the prefix to look up
* @return the namespace currently mapped to that prefix
*/
public String getURIForPrefix(String prefix) {
String uri = getInScopeNamespace(prefix);
if (uri != null)
return uri;
//TODO : test NS inheritance
uri = getInheritedNamespace(prefix);
if (uri != null)
return uri;
// Check global declarations
return (String) staticNamespaces.get(prefix);
}
/**
* First checks the parent context for in-scope namespaces,
* then the module's static context.
*
* @param uri the URI to look up
* @return a prefix for the URI
*/
public String getPrefixForURI(String uri) {
String prefix = getInScopePrefix(uri);
if (prefix != null)
return prefix;
//TODO : test the NS inheritance
prefix = getInheritedPrefix(uri);
if (prefix != null)
return prefix;
return (String) staticPrefixes.get(uri);
}
public String getInScopeNamespace(String prefix) {
return parentContext.getInScopeNamespace(prefix);
}
public String getInScopePrefix(String uri) {
return parentContext.getInScopePrefix(uri);
}
public String getInheritedNamespace(String prefix) {
return parentContext.getInheritedNamespace(prefix);
}
public String getInheritedPrefix(String uri) {
return parentContext.getInheritedPrefix(uri);
}
public void declareInScopeNamespace(String prefix, String uri) {
parentContext.declareInScopeNamespace(prefix, uri);
}
public void pushInScopeNamespaces(boolean inherit) {
parentContext.pushInScopeNamespaces(inherit);
}
public void pushInScopeNamespaces() {
parentContext.pushInScopeNamespaces();
}
public void popInScopeNamespaces() {
parentContext.popInScopeNamespaces();
}
public void registerUpdateListener(UpdateListener listener) {
parentContext.registerUpdateListener(listener);
}
protected void clearUpdateListeners() {
// will be cleared by the parent context
}
public DebuggeeJoint getDebuggeeJoint() {
return parentContext.getDebuggeeJoint();
}
public boolean isDebugMode() {
return parentContext.isDebugMode();
}
public void expressionStart(Expression expr) throws TerminatedException {
parentContext.expressionStart(expr);
}
public void expressionEnd(Expression expr) {
parentContext.expressionEnd(expr);
}
public void stackEnter(Expression expr) throws TerminatedException {
parentContext.stackEnter(expr);
}
public void stackLeave(Expression expr) {
parentContext.stackLeave(expr);
}
}