/FileExtraction/Java_unseen/dlitz_resin/modules/kernel/src/com/caucho/config/xml/XmlConfigContext.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 */

package com.caucho.config.xml;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.el.ELContext;
import javax.el.ELException;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.InjectionPoint;

import org.w3c.dom.Attr;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.caucho.config.Config;
import com.caucho.config.ConfigELContext;
import com.caucho.config.ConfigException;
import com.caucho.config.LineConfigException;
import com.caucho.config.attribute.Attribute;
import com.caucho.config.inject.CreationalContextImpl;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.program.ConfigProgram;
import com.caucho.config.program.NodeBuilderChildProgram;
import com.caucho.config.type.ConfigType;
import com.caucho.config.type.StringType;
import com.caucho.config.type.TypeFactory;
import com.caucho.el.ELParser;
import com.caucho.el.Expr;
import com.caucho.inject.Module;
import com.caucho.util.CompileException;
import com.caucho.util.DisplayableException;
import com.caucho.util.L10N;
import com.caucho.util.LineCompileException;
import com.caucho.vfs.Depend;
import com.caucho.vfs.Dependency;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.Vfs;
import com.caucho.xml.QAbstractNode;
import com.caucho.xml.QAttributedNode;
import com.caucho.xml.QDocument;
import com.caucho.xml.QElement;
import com.caucho.xml.QName;
import com.caucho.xml.QNode;
import com.caucho.xml.XmlUtil;

/**
 * The ConfigContext contains the state of the current configuration.
 */
@Module
public class XmlConfigContext {
  private final static L10N L = new L10N(XmlConfigContext.class);
  private final static Logger log
    = Logger.getLogger(XmlConfigContext.class.getName());

  public final static QName TEXT = new QName("#text");
  private final static Object NULL = new Object();

  private static ThreadLocal<XmlConfigContext> _currentBuilder
    = new ThreadLocal<XmlConfigContext>();

  private Config _config;

  private InjectionPoint _ij;
  
  private CreationalContextImpl<?> _beanStack;

  private ArrayList<Dependency> _dependList;
  private Document _dependDocument;

  private String _baseUri;

  public XmlConfigContext()
  {
  }

  public XmlConfigContext(XmlConfigContext parent)
  {
    this();
  }

  public XmlConfigContext(Config config)
  {
    this();

    _config = config;
  }

  public static XmlConfigContext create()
  {
    XmlConfigContext env = _currentBuilder.get();

    if (env != null)
      return env;
    else
      return new XmlConfigContext();
  }

  public static XmlConfigContext createForProgram()
  {
    return new XmlConfigContext();
  }

  public static XmlConfigContext getCurrentBuilder()
  {
    return _currentBuilder.get();
  }

  public static XmlConfigContext getCurrent()
  {
    return _currentBuilder.get();
  }

  // s/b private?
  static void setCurrentBuilder(XmlConfigContext builder)
  {
    _currentBuilder.set(builder);
  }

  public InjectionPoint getInjectionPoint()
  {
    return _ij;
  }

  public void setInjectionPoint(InjectionPoint ij)
  {
    _ij = ij;
  }

  /**
   * Returns the file var
   */
  public String getBaseUri()
  {
    return Vfs.decode(_baseUri);
  }

  /**
   * Returns the component value for the dependent scope
   *
   * @param aThis
   * @return
   */
  public Object get(Bean<?> bean)
  {
    return CreationalContextImpl.find(_beanStack, bean);
  }
  
  public Object findByName(String name)
  {
    return CreationalContextImpl.findByName(_beanStack, name);
  }
  
  public CreationalContext<?> setCreationalContext(CreationalContext<?> cxt)
  {
    CreationalContext<?> oldCxt = _beanStack;
    
    _beanStack = (CreationalContextImpl<?>) cxt;
    
    return oldCxt;
  }
  
  public CreationalContext<?> getCreationalContext()
  {
    return _beanStack;
  }

  public Config getConfig()
  {
    return _config;
  }

  /**
   * Returns true if EL expressions are used.
   */
  private boolean isEL()
  {
    // server/26b6
    return _config == null || _config.isEL();
  }

  public boolean isIgnoreEnvironment()
  {
    return _config != null && _config.isIgnoreEnvironment();
  }

  /**
   * External call to configure a bean based on a top-level node.
   * The init() and replaceObject() are not called.
   *
   * @param bean the object to be configured.
   */
  public Object configure(Object bean, Node top)
    throws LineConfigException
  {
    if (bean == null)
      throw new NullPointerException(L.l("unexpected null bean at node '{0}'", top));

    XmlConfigContext oldBuilder = _currentBuilder.get();
    try {
      _currentBuilder.set(this);

      ConfigType<?> type = TypeFactory.getType(bean);

      configureBean(bean, top);

      type.init(bean);

      return type.replaceObject(bean);
    } catch (LineConfigException e) {
      throw e;
    } catch (Exception e) {
      throw error(e, top);
    } finally {
      _currentBuilder.set(oldBuilder);
    }
  }

  /**
   * External call to configure a bean based on a top-level node, calling
   * init() and replaceObject() when done.
   *
   * @param bean the bean to be configured
   * @param top the top-level XML configuration node
   * @return the configured object, or the factory generated object
   */
  public void configureBean(Object bean, Node top)
    throws LineConfigException
  {
    XmlConfigContext oldBuilder = _currentBuilder.get();
    String oldFile = _baseUri;
    ArrayList<Dependency> oldDependList = _dependList;

    try {
      _currentBuilder.set(this);

      if (top instanceof QNode) {
        QNode qNode = (QNode) top;

        _baseUri = qNode.getBaseURI();
      }

      _dependList = getDependencyList(top);

      ConfigType<?> type = TypeFactory.getType(bean);

      configureNode(top, bean, type);
    } finally {
      _currentBuilder.set(oldBuilder);

      _dependList = oldDependList;
      _baseUri = oldFile;
    }
  }

  /**
   * External call to configure a bean's attribute.
   *
   * @param bean the bean to be configured
   * @param attribute the node representing the configured attribute
   * @throws LineConfigException
   */
  public void configureAttribute(Object bean, Node attribute)
    throws LineConfigException
  {
    String attrName = attribute.getNodeName();

    if (attrName.startsWith("xmlns"))
      return;

    String oldFile = _baseUri;
    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();
    XmlConfigContext oldBuilder = getCurrentBuilder();
    try {
      setCurrentBuilder(this);

      _baseUri = attribute.getBaseURI();

      ConfigType<?> type = TypeFactory.getType(bean);

      QName qName = ((QAbstractNode) attribute).getQName();

      type.beforeConfigure(this, bean, attribute);

      configureChildNode(attribute, qName, bean, type, false);

      type.afterConfigure(this, bean);
    }
    catch (LineConfigException e) {
      throw e;
    }
    catch (Exception e) {
      throw error(e, attribute);
    } finally {
      _baseUri = oldFile;
      setCurrentBuilder(oldBuilder);
      thread.setContextClassLoader(oldLoader);
    }
  }

  /**
   * Configures a bean, calling its init() and replaceObject() methods.
   *
   * @param typeStrategy the strategy for handling the bean's type
   * @param bean the bean instance
   * @param top the configuration top
   * @return the configured bean, possibly the replaced object
   * @throws LineConfigException
   */
  public Object configureNode(Node node,
                              Object bean,
                              ConfigType<?> beanType)
    throws LineConfigException
  {
    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();

    try {
      beanType.beforeConfigure(this, bean, node);
      beanType.beforeConfigureBean(this, bean, node);

      if (log.isLoggable(Level.ALL))
        log.log(Level.ALL, "config begin " + beanType);

      configureNodeAttributes(node, bean, beanType);

      for (Node childNode = node.getFirstChild();
           childNode != null;
           childNode = childNode.getNextSibling()) {
        QName qName = ((QAbstractNode) childNode).getQName();

        configureChildNode(childNode, qName, bean, beanType, false);
      }

      if (log.isLoggable(Level.ALL))
        log.log(Level.ALL, "config end " + beanType);

      beanType.afterConfigure(this, bean);
    } catch (LineConfigException e) {
      throw e;
    } catch (Exception e) {
      throw error(e, node);
    } finally {
      thread.setContextClassLoader(oldLoader);
    }

    return bean;
  }

  /**
   * Configures a bean, calling its init() and replaceObject() methods.
   *
   * @param typeStrategy the strategy for handling the bean's type
   * @param bean the bean instance
   * @param top the configuration top
   * @return the configured bean, possibly the replaced object
   * @throws LineConfigException
   */
  private void configureNodeAttributes(Node node,
                                       Object bean,
                                       ConfigType<?> type)
  {
    if (node instanceof QAttributedNode) {
      Node child = ((QAttributedNode) node).getFirstAttribute();

      for (; child != null; child = child.getNextSibling()) {
        Attr attr = (Attr) child;
        QName qName = ((QNode) attr).getQName();

        configureChildNode(attr, qName, bean, type, false);
      }
    }
    else {
      NamedNodeMap attrList = node.getAttributes();
      if (attrList != null) {
        int length = attrList.getLength();
        for (int i = 0; i < length; i++) {
          Attr attr = (Attr) attrList.item(i);
          QName qName = ((QNode) attr).getQName();

          configureChildNode(attr, qName, bean, type, false);
        }
      }
    }
  }

  /**
   * Configures a child node for a bean.
   * 
   * Properties and fields will be set.
   * Flow will be invoked (like resin:if)
   */
  private void configureChildNode(Node childNode,
                                  QName qName,
                                  Object parentBean,
                                  ConfigType<?> parentType,
                                  boolean allowParam)
  {
    if (qName.getName().startsWith("xmlns")) {
      return;
    }

    Attribute attrStrategy;

   try {
      attrStrategy = getAttribute(parentType, qName, childNode);
      
      if (attrStrategy == null) {
        if (qName.equals(TEXT)) {
          validateEmptyText(parentBean, childNode);
        }
      }
      else if (attrStrategy.isProgram()) {
        attrStrategy.setValue(parentBean, qName,
                              buildProgram(attrStrategy, childNode));
      }
      else if (attrStrategy.isNode()) {
        attrStrategy.setValue(parentBean, qName, childNode);
      }
      else if (configureInlineText(parentBean, childNode, qName, attrStrategy)) {
      }
      else if (configureInlineBean(parentBean, childNode, attrStrategy)) {
      }
      else {
        configureBeanProperties(childNode, qName, parentBean,
                                parentType, attrStrategy,
                                allowParam);
      }
    } catch (LineConfigException e) {
      throw e;
    } catch (Exception e) {
      throw error(e, childNode);
    }
  }
  
  private void validateEmptyText(Object bean, Node childNode)
  {
    CharacterData cData = (CharacterData) childNode;
    
    String data = cData.getData().trim();
    
    if (data.length() > 0) {
      throw new ConfigException(L.l("Unexpected text in {0} at\n  '{1}'",
                                    bean.getClass().getName(),
                                    data));
    }
  }

  private Attribute getAttribute(ConfigType<?> type,
                                 QName qName,
                                 Node childNode)
  {
    Attribute attrStrategy;

    attrStrategy = type.getAttribute(qName);
    
    if (attrStrategy == null) {
      attrStrategy = type.getDefaultAttribute(qName);
    }

    if (attrStrategy != null) {
      return attrStrategy;
    }

    if (childNode instanceof Element || childNode instanceof Attr) {
      String localName = qName.getLocalName();

      if (localName.indexOf(':') >= 0) {
        // XXX: need ioc QA
        throw error(L.l("'{0}' does not have a defined namespace for 'xmlns:{1}'.  Tags with prefixes need defined namespaces.",
                        qName.getName(),
                        localName.substring(0, localName.indexOf(':'))),
                    childNode);
      }

      // ioc/23m2
      if ("new".equals(localName))
        return null;

      throw error(L.l("'{0}' is an unknown property of '{1}'.",
                      qName.getName(), type.getTypeName()),
                  childNode);
    }

    return null;
  }

  private void setText(Object bean,
                       QName qName,
                       String text,
                       Attribute attrStrategy,
                       boolean isTrim)
  {
    ConfigType<?> attrType = attrStrategy.getConfigType();

    if (isTrim && ! attrType.isNoTrim())
      text = text.trim();

    if (isEL() && attrType.isEL() && text.indexOf("${") >= 0) {
      Object value = attrType.valueOf(evalObject(text));

      attrStrategy.setValue(bean, qName, value);
    }
    else
      attrStrategy.setText(bean, qName, text);
  }

  private boolean configureInlineText(Object bean,
                                      Node childNode,
                                      QName qName,
                                      Attribute attrStrategy)
  {
    // ioc/2013
    if (! attrStrategy.isSetter())
      return false;

    boolean isTrim = ! attrStrategy.getConfigType().isNoTrim();
    String text = getTextValue(childNode, isTrim);

    if (text == null)
      return false;

    isTrim = isTrim(childNode);

    if (isEL() && attrStrategy.isEL()
        && (text.indexOf("#{") >= 0 || text.indexOf("${") >= 0)) {
      if (isTrim)
        text = text.trim();

      Object elValue = eval(attrStrategy.getConfigType(), text);

      // ioc/2410
      if (elValue != NULL)
        attrStrategy.setValue(bean, qName, elValue);
      else {
        try {
          attrStrategy.setValue(bean, qName, null);
        } catch (Exception e) {
          throw ConfigException.create(L.l("{0} value must not be null.\n  ", text), e);
        }
      }

      return true;
    }
    else if (attrStrategy.isAllowText()) {
      setText(bean, qName, text, attrStrategy, isTrim);

      return true;
    }
    else
      return false;
  }

  private boolean configureInlineBean(Object parent,
                                      Node node,
                                      Attribute attrStrategy)
  {
    /* server/0219
    if (! attrStrategy.isAllowInline()) {
      return false;
    }
    */

    Node childNode = getChildElement(node);

    if (childNode == null) {
      return false;
    }
    
    QName parentQname = ((QNode) node).getQName();
    QName childQname = ((QNode) childNode).getQName();

    ConfigType<?> type = TypeFactory.getFactory().getEnvironmentType(childQname);

    if (type == null || ! attrStrategy.isInlineType(type)) {
      // server/6500
      return false;
    }

    // server/0219
    // Object childBean = attrStrategy.create(parent, qName, type);

    Element childNew = getChildNewElement(childNode);

    Object childBean;

    if (childNew != null)
      childBean = createNew(type, parent, childNew);
    else if (type.isQualifier()) {
      // ioc/04f8
      Object qualifier = type.create(parent, childQname);

      ConfigType<?> qualifierType = TypeFactory.getType(qualifier);

      qualifier = configureChildBean(qualifier, qualifierType,
                                     childNode, attrStrategy);
      
      InjectManager cdiManager = InjectManager.getCurrent();
      
      Class<?> attrType = attrStrategy.getConfigType().getType();
      
      Set<Bean<?>> beans
        = cdiManager.getBeans(attrType, (Annotation) qualifier);
      
      Bean<?> bean = cdiManager.resolve(beans);
      
      CreationalContext cxt = null;
      
      childBean = cdiManager.getReference(bean, attrType, cxt); 
    }
    else
      childBean = type.create(parent, childQname);

    if (childBean == null)
      return false;

    // server/1af3
    ConfigType<?> childType = TypeFactory.getType(childBean);

    childBean = configureChildBean(childBean, childType,
                                   childNode, attrStrategy);

    // ejb/7006
    attrStrategy.setValue(parent, parentQname, childBean);

    return true;
  }

  private void configureBeanProperties(Node childNode,
                                       QName qName,
                                       Object bean,
                                       ConfigType<?> type,
                                       Attribute attrStrategy,
                                       boolean allowParam)
  {
    Object childBean = attrStrategy.create(bean, qName);

    if (childBean == null) {
      throw unableToCreateError(attrStrategy, bean, qName, childNode);
    }

    ConfigType<?> childBeanType = TypeFactory.getType(childBean);
    //ConfigType<?> childBeanType = attrStrategy.getType(childBean);

    childBean = configureChildBean(childBean, childBeanType,
                                   childNode, attrStrategy);

    if (! childBeanType.isEnvBean()) {
      attrStrategy.setValue(bean, qName, childBean);
    }
  }

  private Object configureChildBean(Object childBean,
                                    ConfigType<?> childBeanType,
                                    Node childNode,
                                    Attribute attrStrategy)
  {
    if (childNode instanceof Element) {
      configureNode(childNode, childBean, childBeanType);
    }
    else
      configureChildNode(childNode, TEXT, childBean, childBeanType, false);

    childBeanType.init(childBean);

    Object newBean = attrStrategy.replaceObject(childBean);

    if (newBean == childBean)
      return childBeanType.replaceObject(childBean);
    else
      return newBean;
  }
  
  private ConfigException unableToCreateError(Attribute attr,
                                              Object bean,
                                              QName qName,
                                              Node node)
  {
    Element child = getUniqueChildElement(node);
    
    if (child == null) {
      throw error(L.l("unable to create inline attribute '{2}' for '{1}' because no unique child Element exists.  Attribute = {0}",
                      attr, bean, qName),
                      node);
    }
    

    QName childQName = ((QNode) child).getQName();
    String uri = childQName.getNamespaceURI();

    if (uri == null || ! uri.startsWith("urn:java:")) {
      throw error(L.l("unable to create inline attribute '{0}' for '{1}' because the child <{2}> uri must start with xmlns='urn:java:...' but uri='{3}' for {4}",
                      qName.getName(), bean, childQName.getName(), uri, attr),
                      node);
    }
    
    throw throwUnableToCreateError(attr, bean, qName, node);
  }
  
  private ConfigException throwUnableToCreateError(Attribute attr,
                                                   Object bean,
                                                   QName qName,
                                                   Node childNode)
  {
    throw error(L.l("unable to create attribute {0} for {1} and {2}",
                    attr, bean, qName),
                    childNode);
  }

  private Node getChildElement(Node node)
  {
    Element child = getUniqueChildElement(node);
    
    if (child == null)
      return null;

    QName qName = ((QNode) child).getQName();
    String uri = qName.getNamespaceURI();

    if (uri == null || ! uri.startsWith("urn:java:"))
      return null;

    return child;
  }
  
  private Element getUniqueChildElement(Node node)
  {
    if (! (node instanceof Element))
      return null;

    Element elt = (Element) node;

    Node child = elt.getFirstChild();

    if (child == null)
      return null;

    if (isEmptyText(child)) {
      child = child.getNextSibling();

      if (child == null)
        return null;
    }

    Node next = child.getNextSibling();

    if (next != null) {
      if (next.getNextSibling() != null || ! isEmptyText(next))
        return null;
    }
    
    if (child instanceof Element)
      return (Element) child;
    else
      return null;
  }

  private Object createNew(ConfigType<?> type,
                           Object parent,
                           Element newNode)
  {
    boolean isTrim = true;
    
    String text = getTextValue(newNode, isTrim);

    if (text != null) {
      text = text.trim();

      return type.valueOf(create(newNode, StringType.TYPE));
    }

    int count = countNewChildren(newNode);

    Constructor<?> ctor = type.getConstructor(count);

    Class<?> []paramTypes = ctor.getParameterTypes();

    Object []args = new Object[paramTypes.length];
    int i = 0;
    for (Node child = newNode.getFirstChild();
         child != null;
         child = child.getNextSibling()) {
      if (! (child instanceof Element))
        continue;

      ConfigType<?> childType = TypeFactory.getType(paramTypes[i]);

      args[i++] = create(child, childType);
    }

    try {
      return ctor.newInstance(args);
    } catch (InvocationTargetException e) {
      throw ConfigException.create(ctor.getName(), e.getCause());
    } catch (Exception e) {
      throw ConfigException.create(ctor.getName(), e);
    }
  }

  private int countNewChildren(Element newNode)
  {
    int count = 0;

    for (Node child = newNode.getFirstChild();
         child != null;
         child = child.getNextSibling()) {
      if (child instanceof Element)
        count++;
    }

    return count;
  }

  private Element getChildNewElement(Node node)
  {
    if (! (node instanceof Element))
      return null;

    Element elt = (Element) node;

    for (Node child = elt.getFirstChild();
         child != null;
         child = child.getNextSibling()) {
      if ("new".equals(child.getLocalName()))
        return (Element) child;
    }

    return null;
  }

  private boolean isEmptyText(Node node)
  {
    if (! (node instanceof CharacterData))
      return false;

    CharacterData data = (CharacterData) node;

    return data.getData().trim().length() == 0;
  }

  private boolean isTrim(Node node)
  {
    if (node instanceof Attr)
      return false;
    else if (node instanceof Element) {
      Element elt = (Element) node;

      if (! "".equals(elt.getAttribute("xml:space")))
        return false;
    }

    return true;
  }

  private ConfigProgram buildProgram(Attribute attr, Node node)
  {
    return new NodeBuilderChildProgram(node);
  }

  //
  // Used for args
  //
  public Object create(Node childNode, ConfigType<?> type)
    throws ConfigException
  {
    try {
      Object childBean;
      String text;

      if ((text = getArgTextValue(childNode)) != null) {
        boolean isTrim = isTrim(childNode);

        if (isEL() && type.isEL()
            && (text.indexOf("#{") >= 0 || text.indexOf("${") >= 0)) {
          if (isTrim)
            text = text.trim();

          Object elValue = eval(type, text);

          // ioc/2410
          if (elValue != NULL)
            return elValue;
          else
            return null;
        }
        else {
          return type.valueOf(text);
        }
      }

      QName qName = ((QNode) childNode).getQName();

      ConfigType<?> childBeanType = type.createType(qName);

      if (childBeanType != null) {
        childBean = childBeanType.create(null, qName);

        if (childNode instanceof Element)
          configureNode(childNode, childBean, childBeanType);
        else
          configureChildNode(childNode, TEXT, childBean, childBeanType, false);

        childBeanType.init(childBean);

        return childBeanType.replaceObject(childBean);
      }
      else {
        String textValue;

        if (type.isNoTrim())
          textValue = textValueNoTrim(childNode);
        else
          textValue = textValue(childNode);

        if (isEL() && type.isEL() && textValue.indexOf("${") >= 0) {
          Object value = type.valueOf(evalObject(textValue));

          return value;
        }
        else
          return type.valueOf(textValue);
      }
    } catch (LineConfigException e) {
      throw e;
    } catch (Exception e) {
      throw error(e, childNode);
    }
  }

  Object configureValue(Node node)
  {
    String value = textValue(node);

    if (isEL() && value != null
        && value.startsWith("${") && value.endsWith("}")) {
      return evalObject(value);
    }
    else
      return value;
  }

  public ArrayList<Dependency> getDependencyList()
  {
    return _dependList;
  }

  ArrayList<Dependency> getDependencyList(Node node)
  {
    ArrayList<Dependency> dependList = null;

    if (node instanceof QElement) {
      QElement qelt = (QElement) node;

      QDocument doc = (QDocument) qelt.getOwnerDocument();

      if (doc == null)
        return null;
      else if (doc == _dependDocument)
        return _dependList;

      _dependDocument = doc;

      ArrayList<Path> pathList;
      pathList = doc.getDependList();

      if (pathList != null) {
        dependList = new ArrayList<Dependency>();

        for (int i = 0; i < pathList.size(); i++) {
          dependList.add(new Depend(pathList.get(i)));
        }
      }

      _dependList = dependList;
    }

    return dependList;
  }

  /**
   * Returns the variable resolver.
   */
  public static ConfigELContext getELContext()
  {
    return ConfigELContext.EL_CONTEXT;
  }

  /**
   * Returns the text value of the node.
   */
  private String getTextValue(Node node, boolean isTrim)
  {
    if (node instanceof Attr) {
      Attr attrNode = (Attr) node;
      String data = attrNode.getNodeValue();

      return data;
    }
    else if (node instanceof CharacterData) {
      CharacterData cData = (CharacterData) node;

      return cData.getData();
    }

    if (! (node instanceof Element))
      return null;

    QElement elt = (QElement) node;

    // ioc/2235
    for (Node attr = elt.getFirstAttribute();
         attr != null;
         attr = attr.getNextSibling()) {
      if (! "xml".equals(attr.getPrefix()))
        return null;
    }

    for (Node child = elt.getFirstChild();
         child != null;
         child = child.getNextSibling()) {
      if (child instanceof Element) {
        return null;
      }

      else if (child instanceof CharacterData) {
        String data = ((CharacterData) child).getData();

        if (child.getNextSibling() != null) {
          return null;
        }
        else if (! isTrim || ! XmlUtil.isWhitespace(data)) {
          return data;
        }
        else {
          return null;
        }
      }
    }

    return "";
  }

  /**
   * Returns the text value of the node.
   */
  private String getArgTextValue(Node node)
  {
    if (node instanceof Element && ! node.getLocalName().equals("value"))
      return null;

    boolean isTrim = true;
    return getTextValue(node, isTrim);
  }

  private Object eval(ConfigType<?> type, String data)
  {
    ELContext elContext = getELContext();

    ELParser parser = new ELParser(elContext, data);

    Expr expr = parser.parse();

    Object value = type.valueOf(elContext, expr);
    
    if (value != null)
      return value;
    else
      return NULL;
  }

  /**
   * Returns the text value of the node.
   */
  private static String textValue(Node node)
  {
    if (node instanceof Attr)
      return node.getNodeValue();
    else {
      String value = XmlUtil.textValue(node);

      if (value == null || value.equals(""))
        return "";
      else if (node instanceof Element) {
        String space = ((Element) node).getAttribute("xml:space");

        if (! space.equals(""))
          return value;
      }

      return value.trim();
    }
  }

  /**
   * Returns the text value of the node.
   */
  private static String textValueNoTrim(Node node)
  {
    if (node instanceof Attr)
      return node.getNodeValue();
    else {
      String value = XmlUtil.textValue(node);

      if (value == null)
        return "";

      return value;
    }
  }

  /**
   * Evaluate as an object
   */
  private Object evalObject(String exprString)
    throws ELException
  {
    if (exprString.indexOf("${") >= 0 && isEL()) {
      ELParser parser = new ELParser(getELContext(), exprString);
      parser.setCheckEscape(true);
      Expr expr = parser.parse();

      return expr.getValue(getELContext());
    }
    else
      return exprString;
  }

  private static RuntimeException error(String msg, Node node)
  {
    String systemId = null;
    String filename = null;
    int line = 0;

    if (node instanceof QAbstractNode) {
      QAbstractNode qnode = (QAbstractNode) node;

      systemId = qnode.getBaseURI();
      filename = qnode.getFilename();
      line = qnode.getLine();
    }
    
    if (systemId != null) {
      String sourceLines = getSourceLines(systemId, line);

      msg = msg + sourceLines;
    }

    if (filename != null)
      return new LineConfigException(filename, line, msg);
    else
      return new LineConfigException(msg);
  }

  private static RuntimeException error(Throwable e, Node node)
  {
    String systemId = null;
    String filename = null;
    int line = 0;

    if (e instanceof RuntimeException
        && e instanceof DisplayableException
        && ! ConfigException.class.equals(e.getClass())) {
      return (RuntimeException) e;
    }

    if (node instanceof QAbstractNode) {
      QAbstractNode qnode = (QAbstractNode) node;

      systemId = qnode.getBaseURI();
      filename = qnode.getFilename();
      line = qnode.getLine();
    }

    for (; e.getCause() != null; e = e.getCause()) {
      if (e instanceof LineCompileException)
        break;
      else if (e instanceof LineConfigException)
        break;
      else if (e instanceof CompileException)
        break;
    }

    if (e instanceof LineConfigException)
      return (LineConfigException) e;
    else if (e instanceof LineCompileException) {
      return new LineConfigException(e.getMessage(), e);
    }
    else if (e instanceof ConfigException
             && e.getMessage() != null
             && filename != null) {
      String sourceLines = getSourceLines(systemId, line);

      return new LineConfigException(filename, line,
                                     e.getMessage() + sourceLines,
                                     e);
    }
    else if (e instanceof CompileException && e.getMessage() != null) {
      return new LineConfigException(filename, line, e);
    }
    else {
      String sourceLines = getSourceLines(systemId, line);

      String msg = filename + ":" + line + ": " + e + sourceLines;

      if (e instanceof RuntimeException) {
        throw new LineConfigException(msg, e);
      }
      else if (e instanceof Error) {
        // server/1711
        throw new LineConfigException(msg, e);
        // throw (Error) e;
      }
      else
        return new LineConfigException(msg, e);
    }
  }

  private static String getSourceLines(String systemId, int errorLine)
  {
    if (systemId == null)
      return "";

    ReadStream is = null;
    try {
      is = Vfs.lookup().lookup(systemId).openRead();
      int line = 0;
      StringBuilder sb = new StringBuilder("\n\n");
      String text;
      while ((text = is.readLine()) != null) {
        line++;

        if (errorLine - 2 <= line && line <= errorLine + 2) {
          sb.append(line);
          sb.append(": ");
          sb.append(text);
          sb.append("\n");
        }
      }

      return sb.toString();
    } catch (IOException e) {
      log.log(Level.FINEST, e.toString(), e);

      return "";
    } finally {
      if (is != null)
        is.close();
    }
  }

  @Override
  public String toString()
  {
    return getClass().getSimpleName() + "[]";
  }
}
