gt:         if (uri != null)
context: /*
   * 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.persistent.DocumentSet;
  import org.exist.dom.QName;
  import org.exist.dom.memtree.MemTreeBuilder;
  import org.exist.security.Subject;
  import org.exist.storage.UpdateListener;
  import org.exist.util.FileUtils;
  import org.exist.xmldb.XmldbURI;
  import org.exist.xquery.functions.response.ResponseModule;
  import org.exist.xquery.value.AnyURIValue;
  import org.exist.xquery.value.BinaryValue;
  import org.exist.xquery.value.Sequence;
  
  import javax.xml.datatype.XMLGregorianCalendar;
  
  import java.net.URISyntaxException;
  import java.util.Iterator;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import org.exist.xquery.value.StringValue;
  import org.exist.xquery.value.ValueSequence;
  
  
  /**
   * Subclass of {@link org.exist.xquery.XQueryContext} for
   * imported modules.
   * 
   * @author wolf
   */
  public class ModuleContext extends XQueryContext {
  
  	private XQueryContext parentContext;
      private String modulePrefix;
      private String moduleNamespace;
      private final String location;
  
  	/**
  	 * @param parentContext
  	 */
  	public ModuleContext(XQueryContext parentContext, String modulePrefix, String moduleNamespace, String location) {
  		super();
          this.moduleNamespace = moduleNamespace;
          this.modulePrefix = modulePrefix;
          this.location = location;
          setParentContext(parentContext);
  		loadDefaults(getBroker().getConfiguration());
  		this.profiler = new Profiler( getBroker().getBrokerPool() );
      }
  
      @Override
      public Subject getRealUser() {
          //the real and effective users are set at execution time on the root XQuery Context
          return getRootContext().getRealUser();
      }
  
      String getLocation() {
  	    return location;
  	}
     
  	String getModuleNamespace() {
  		return moduleNamespace;
  	}
  
      public void setModuleNamespace(String prefix, String namespaceURI) {
          this.modulePrefix = prefix;
          this.moduleNamespace = namespaceURI;
      }
  
  	void setModulesChanged() {
  		parentContext.setModulesChanged();
  	}
  	
  	private void setParentContext(XQueryContext parentContext) {
          this.parentContext = parentContext;
          //XXX: raise error on null!
          if (parentContext != null) {
          	this.db = parentContext.db;
  		    baseURI = parentContext.baseURI;
  		    try {
  		        if (location.startsWith(XmldbURI.XMLDB_URI_PREFIX) ||
                      (location.indexOf(':') < 0 &&
                       parentContext.getModuleLoadPath().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
                      final XmldbURI locationUri = XmldbURI.xmldbUriFor(FileUtils.dirname(location));
                      if (".".equals(parentContext.getModuleLoadPath()))
                          {setModuleLoadPath(locationUri.toString());}
                      else {
                      	try {
  	                        final XmldbURI parentLoadUri = XmldbURI.xmldbUriFor(parentContext.getModuleLoadPath());
  	                        final XmldbURI moduleLoadUri = parentLoadUri.resolveCollectionPath(locationUri);
  	                        setModuleLoadPath(moduleLoadUri.toString());
                      	} catch (final URISyntaxException e) {
                              setModuleLoadPath(locationUri.toString());
  						}
                      }
  		        } else {
                      String dir = FileUtils.dirname(location);
                      if (dir.matches("^[a-z]+:.*")) {
                          moduleLoadPath = dir;
                      } else if (".".equals(parentContext.moduleLoadPath)) {
  		                if (! ".".equals(dir)) {
  		                    if (dir.startsWith("/")) {
  		                        setModuleLoadPath("." + dir);
                                      } else {
  		                        setModuleLoadPath("./" + dir);
                                      }
  		                }
  		            } else {
  		                if (dir.startsWith("/")) {
  		                    setModuleLoadPath(dir);
                                  } else {
  		                    setModuleLoadPath(FileUtils.addPaths(parentContext.getModuleLoadPath(), dir));
                                  }
  		            }
  		        }
  		    } catch (final 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);
          }
          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;
          }
          
          //workaround for shared context issue, remove after fix
          try {
          	final Variable var = from.getRootContext().resolveVariable(ResponseModule.PREFIX + ":response");
          	if (var != null)
          		{declareVariable(
  					ResponseModule.PREFIX + ":response", 
  					var.getValue()
  				);}
  		} catch (final Exception e) {
  			//ignore if not set
  		}
  
          setModule(
      		ResponseModule.NAMESPACE_URI, 
      		from.getRootContext().getModule(ResponseModule.NAMESPACE_URI)
      	);
      }
  
      public XQueryContext copyContext() {
          final ModuleContext ctx = new ModuleContext(parentContext, modulePrefix, moduleNamespace, location);
          copyFields(ctx);
          try {
              ctx.declareNamespace(modulePrefix, moduleNamespace);
          } catch (final 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<Module> getRootModules() {
  		return parentContext.getRootModules();
  	}
  	
  	public Iterator<Module> 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 {
              	final String defaultPrefix = module.getDefaultPrefix();
              	if (!"".equals(defaultPrefix))
              		{declareNamespace(defaultPrefix, module.getNamespaceURI());}
              } catch (final XPathException e) {
                  LOG.warn("error while loading builtin module class " + moduleClass, e);
              }
          }
          return module;
      }
  
      public void updateModuleRefs(XQueryContext rootContext) {
          HashMap<String, Module> newModules = new HashMap<String, Module>(modules.size());
          for (Module module : modules.values()) {
              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;
      }
      
      @Override
      final protected XPathException moduleLoadException(final String message, final String moduleLocation) throws XPathException {
         return moduleLoadException(message, moduleLocation, null);
      }
      
      @Override
      final protected XPathException moduleLoadException(final String message, final String moduleLocation, final Exception e) throws XPathException {
          //final String dependantModule = XmldbURI.create(moduleLoadPath).append(location).toString();
          String dependantModule;
          try {
              if(location != null && location.startsWith(XmldbURI.LOCAL_DB)) {
                  dependantModule = location;
              } else {
                  dependantModule = XmldbURI.create(getParentContext().getModuleLoadPath(), false).append(location).toString();
              }
          } catch (final Exception ex) {
              dependantModule = location;
          }
          
          if(e == null) {
              return new XPathException(ErrorCodes.XQST0059, message, new ValueSequence(new StringValue(moduleLocation), new StringValue(dependantModule)));
          } else {
              return new XPathException(ErrorCodes.XQST0059, message, new ValueSequence(new StringValue(moduleLocation), new StringValue(dependantModule)), e);
          }
      }
  
  	/* (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 void popLocalVariables(LocalVariable var, Sequence resultSequence) {
          parentContext.popLocalVariables(var, resultSequence);
      }
  
      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 Map<QName, Variable> getLocalVariables() {
      	return parentContext.getLocalVariables();
      }
  
      public List<Variable> getLocalStack() {
      	return parentContext.getLocalStack();
      }
      
      public Map<QName, Variable> getGlobalVariables() {
      	return parentContext.getGlobalVariables();
      }
  
      public void restoreStack(List<Variable> stack) throws XPathException {
      	parentContext.restoreStack(stack);
      }
      
      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);
  