package org.apache.commons.digester;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.collections.ArrayStack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

public class Digester extends DefaultHandler {
  public Digester() {}
  
  public Digester(SAXParser parser) {
    this.parser = parser;
  }
  
  public Digester(XMLReader reader) {
    this.reader = reader;
  }
  
  protected StringBuffer bodyText = new StringBuffer();
  
  protected ArrayStack bodyTexts = new ArrayStack();
  
  protected ClassLoader classLoader = null;
  
  protected boolean configured = false;
  
  protected EntityResolver entityResolver;
  
  protected HashMap entityValidator = new HashMap();
  
  protected ErrorHandler errorHandler = null;
  
  protected SAXParserFactory factory = null;
  
  private static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
  
  protected String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
  
  protected Locator locator = null;
  
  protected String match = "";
  
  protected boolean namespaceAware = false;
  
  protected HashMap namespaces = new HashMap();
  
  protected ArrayStack params = new ArrayStack();
  
  protected SAXParser parser = null;
  
  protected String publicId = null;
  
  protected XMLReader reader = null;
  
  protected Object root = null;
  
  protected Rules rules = null;
  
  protected String schemaLanguage = "http://www.w3.org/2001/XMLSchema";
  
  protected String schemaLocation = null;
  
  protected ArrayStack stack = new ArrayStack();
  
  protected boolean useContextClassLoader = false;
  
  protected boolean validating = false;
  
  protected Log log = LogFactory.getLog("org.apache.commons.digester.Digester");
  
  protected Log saxLog = LogFactory.getLog("org.apache.commons.digester.Digester.sax");
  
  protected static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
  
  public String findNamespaceURI(String prefix) {
    ArrayStack stack = (ArrayStack)this.namespaces.get(prefix);
    if (stack == null)
      return null; 
    try {
      return (String)stack.peek();
    } catch (EmptyStackException e) {
      return null;
    } 
  }
  
  public ClassLoader getClassLoader() {
    if (this.classLoader != null)
      return this.classLoader; 
    if (this.useContextClassLoader) {
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      if (classLoader != null)
        return classLoader; 
    } 
    return getClass().getClassLoader();
  }
  
  public void setClassLoader(ClassLoader classLoader) {
    this.classLoader = classLoader;
  }
  
  public int getCount() {
    return this.stack.size();
  }
  
  public String getCurrentElementName() {
    String elementName = this.match;
    int lastSlash = elementName.lastIndexOf('/');
    if (lastSlash >= 0)
      elementName = elementName.substring(lastSlash + 1); 
    return elementName;
  }
  
  public int getDebug() {
    return 0;
  }
  
  public void setDebug(int debug) {}
  
  public ErrorHandler getErrorHandler() {
    return this.errorHandler;
  }
  
  public void setErrorHandler(ErrorHandler errorHandler) {
    this.errorHandler = errorHandler;
  }
  
  public SAXParserFactory getFactory() {
    if (this.factory == null) {
      this.factory = SAXParserFactory.newInstance();
      this.factory.setNamespaceAware(this.namespaceAware);
      this.factory.setValidating(this.validating);
    } 
    return this.factory;
  }
  
  public boolean getFeature(String feature) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException {
    return getFactory().getFeature(feature);
  }
  
  public void setFeature(String feature, boolean value) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException {
    getFactory().setFeature(feature, value);
  }
  
  public Log getLogger() {
    return this.log;
  }
  
  public void setLogger(Log log) {
    this.log = log;
  }
  
  public String getMatch() {
    return this.match;
  }
  
  public boolean getNamespaceAware() {
    return this.namespaceAware;
  }
  
  public void setNamespaceAware(boolean namespaceAware) {
    this.namespaceAware = namespaceAware;
  }
  
  public void setPublicId(String publicId) {
    this.publicId = publicId;
  }
  
  public String getPublicId() {
    return this.publicId;
  }
  
  public String getRuleNamespaceURI() {
    return getRules().getNamespaceURI();
  }
  
  public void setRuleNamespaceURI(String ruleNamespaceURI) {
    getRules().setNamespaceURI(ruleNamespaceURI);
  }
  
  public SAXParser getParser() {
    if (this.parser != null)
      return this.parser; 
    try {
      this.parser = getFactory().newSAXParser();
    } catch (Exception e) {
      this.log.error("Digester.getParser: ", e);
      return null;
    } 
    try {
      if (this.schemaLocation != null) {
        setProperty(this.JAXP_SCHEMA_LANGUAGE, this.schemaLanguage);
        setProperty("http://java.sun.com/xml/jaxp/properties/schemaSource", this.schemaLocation);
      } 
    } catch (Exception e) {
      this.log.warn("" + e);
    } 
    return this.parser;
  }
  
  public Object getProperty(String property) throws SAXNotRecognizedException, SAXNotSupportedException {
    return getParser().getProperty(property);
  }
  
  public void setProperty(String property, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
    getParser().setProperty(property, value);
  }
  
  public XMLReader getReader() {
    try {
      return getXMLReader();
    } catch (SAXException e) {
      this.log.error("Cannot get XMLReader", e);
      return null;
    } 
  }
  
  public Rules getRules() {
    if (this.rules == null) {
      this.rules = new RulesBase();
      this.rules.setDigester(this);
    } 
    return this.rules;
  }
  
  public void setRules(Rules rules) {
    this.rules = rules;
    this.rules.setDigester(this);
  }
  
  public String getSchema() {
    return this.schemaLocation;
  }
  
  public void setSchema(String schemaLocation) {
    this.schemaLocation = schemaLocation;
  }
  
  public String getSchemaLanguage() {
    return this.schemaLanguage;
  }
  
  public void setSchemaLanguage(String schemaLanguage) {
    this.schemaLanguage = schemaLanguage;
  }
  
  public boolean getUseContextClassLoader() {
    return this.useContextClassLoader;
  }
  
  public void setUseContextClassLoader(boolean use) {
    this.useContextClassLoader = use;
  }
  
  public boolean getValidating() {
    return this.validating;
  }
  
  public void setValidating(boolean validating) {
    this.validating = validating;
  }
  
  public XMLReader getXMLReader() throws SAXException {
    if (this.reader == null)
      this.reader = getParser().getXMLReader(); 
    this.reader.setDTDHandler(this);
    this.reader.setContentHandler(this);
    if (this.entityResolver == null) {
      this.reader.setEntityResolver(this);
    } else {
      this.reader.setEntityResolver(this.entityResolver);
    } 
    this.reader.setErrorHandler(this);
    return this.reader;
  }
  
  public void characters(char[] buffer, int start, int length) throws SAXException {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("characters(" + new String(buffer, start, length) + ")"); 
    this.bodyText.append(buffer, start, length);
  }
  
  public void endDocument() throws SAXException {
    if (this.saxLog.isDebugEnabled())
      if (getCount() > 1) {
        this.saxLog.debug("endDocument():  " + getCount() + " elements left");
      } else {
        this.saxLog.debug("endDocument()");
      }  
    while (getCount() > 1)
      pop(); 
    Iterator rules = getRules().rules().iterator();
    while (rules.hasNext()) {
      Rule rule = rules.next();
      try {
        rule.finish();
      } catch (Exception e) {
        this.log.error("Finish event threw exception", e);
        throw createSAXException(e);
      } catch (Error e) {
        this.log.error("Finish event threw error", e);
        throw e;
      } 
    } 
    clear();
  }
  
  public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
    boolean debug = this.log.isDebugEnabled();
    if (debug) {
      if (this.saxLog.isDebugEnabled())
        this.saxLog.debug("endElement(" + namespaceURI + "," + localName + "," + qName + ")"); 
      this.log.debug("  match='" + this.match + "'");
      this.log.debug("  bodyText='" + this.bodyText + "'");
    } 
    String name = localName;
    if (name == null || name.length() < 1)
      name = qName; 
    List rules = getRules().match(namespaceURI, this.match);
    if (rules != null && rules.size() > 0) {
      String bodyText = this.bodyText.toString();
      for (int i = 0; i < rules.size(); i++) {
        try {
          Rule rule = rules.get(i);
          if (debug)
            this.log.debug("  Fire body() for " + rule); 
          rule.body(namespaceURI, name, bodyText);
        } catch (Exception e) {
          this.log.error("Body event threw exception", e);
          throw createSAXException(e);
        } catch (Error e) {
          this.log.error("Body event threw error", e);
          throw e;
        } 
      } 
    } else if (debug) {
      this.log.debug("  No rules found matching '" + this.match + "'.");
    } 
    this.bodyText = (StringBuffer)this.bodyTexts.pop();
    if (debug)
      this.log.debug("  Popping body text '" + this.bodyText.toString() + "'"); 
    if (rules != null)
      for (int i = 0; i < rules.size(); i++) {
        int j = rules.size() - i - 1;
        try {
          Rule rule = rules.get(j);
          if (debug)
            this.log.debug("  Fire end() for " + rule); 
          rule.end(namespaceURI, name);
        } catch (Exception e) {
          this.log.error("End event threw exception", e);
          throw createSAXException(e);
        } catch (Error e) {
          this.log.error("End event threw error", e);
          throw e;
        } 
      }  
    int slash = this.match.lastIndexOf('/');
    if (slash >= 0) {
      this.match = this.match.substring(0, slash);
    } else {
      this.match = "";
    } 
  }
  
  public void endPrefixMapping(String prefix) throws SAXException {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("endPrefixMapping(" + prefix + ")"); 
    ArrayStack stack = (ArrayStack)this.namespaces.get(prefix);
    if (stack == null)
      return; 
    try {
      stack.pop();
      if (stack.empty())
        this.namespaces.remove(prefix); 
    } catch (EmptyStackException e) {
      throw createSAXException("endPrefixMapping popped too many times");
    } 
  }
  
  public void ignorableWhitespace(char[] buffer, int start, int len) throws SAXException {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("ignorableWhitespace(" + new String(buffer, start, len) + ")"); 
  }
  
  public void processingInstruction(String target, String data) throws SAXException {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("processingInstruction('" + target + "','" + data + "')"); 
  }
  
  public Locator getDocumentLocator() {
    return this.locator;
  }
  
  public void setDocumentLocator(Locator locator) {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("setDocumentLocator(" + locator + ")"); 
    this.locator = locator;
  }
  
  public void skippedEntity(String name) throws SAXException {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("skippedEntity(" + name + ")"); 
  }
  
  public void startDocument() throws SAXException {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("startDocument()"); 
    configure();
  }
  
  public void startElement(String namespaceURI, String localName, String qName, Attributes list) throws SAXException {
    boolean debug = this.log.isDebugEnabled();
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + qName + ")"); 
    this.bodyTexts.push(this.bodyText);
    if (debug)
      this.log.debug("  Pushing body text '" + this.bodyText.toString() + "'"); 
    this.bodyText = new StringBuffer();
    String name = localName;
    if (name == null || name.length() < 1)
      name = qName; 
    StringBuffer sb = new StringBuffer(this.match);
    if (this.match.length() > 0)
      sb.append('/'); 
    sb.append(name);
    this.match = sb.toString();
    if (debug)
      this.log.debug("  New match='" + this.match + "'"); 
    List rules = getRules().match(namespaceURI, this.match);
    if (rules != null && rules.size() > 0) {
      String bodyText = this.bodyText.toString();
      for (int i = 0; i < rules.size(); i++) {
        try {
          Rule rule = rules.get(i);
          if (debug)
            this.log.debug("  Fire begin() for " + rule); 
          rule.begin(namespaceURI, name, list);
        } catch (Exception e) {
          this.log.error("Begin event threw exception", e);
          throw createSAXException(e);
        } catch (Error e) {
          this.log.error("Begin event threw error", e);
          throw e;
        } 
      } 
    } else if (debug) {
      this.log.debug("  No rules found matching '" + this.match + "'.");
    } 
  }
  
  public void startPrefixMapping(String prefix, String namespaceURI) throws SAXException {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")"); 
    ArrayStack stack = (ArrayStack)this.namespaces.get(prefix);
    if (stack == null) {
      stack = new ArrayStack();
      this.namespaces.put(prefix, stack);
    } 
    stack.push(namespaceURI);
  }
  
  public void notationDecl(String name, String publicId, String systemId) {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("notationDecl(" + name + "," + publicId + "," + systemId + ")"); 
  }
  
  public void unparsedEntityDecl(String name, String publicId, String systemId, String notation) {
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," + systemId + "," + notation + ")"); 
  }
  
  public void setEntityResolver(EntityResolver entityResolver) {
    this.entityResolver = entityResolver;
  }
  
  public EntityResolver getEntityResolver() {
    return this.entityResolver;
  }
  
  public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
    boolean debug = this.log.isDebugEnabled();
    if (this.saxLog.isDebugEnabled())
      this.saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')"); 
    if (publicId != null)
      this.publicId = publicId; 
    String entityURL = null;
    if (publicId != null)
      entityURL = (String)this.entityValidator.get(publicId); 
    if (this.schemaLocation != null && entityURL == null && systemId != null)
      entityURL = (String)this.entityValidator.get(systemId); 
    if (entityURL == null)
      return null; 
    if (debug)
      this.log.debug(" Resolving to alternate DTD '" + entityURL + "'"); 
    try {
      return new InputSource(entityURL);
    } catch (Exception e) {
      throw createSAXException(e);
    } 
  }
  
  public void error(SAXParseException exception) throws SAXException {
    this.log.error("Parse Error at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), exception);
    if (this.errorHandler != null)
      this.errorHandler.error(exception); 
  }
  
  public void fatalError(SAXParseException exception) throws SAXException {
    this.log.error("Parse Fatal Error at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), exception);
    if (this.errorHandler != null)
      this.errorHandler.fatalError(exception); 
  }
  
  public void warning(SAXParseException exception) throws SAXException {
    if (this.errorHandler != null) {
      this.log.warn("Parse Warning Error at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), exception);
      this.errorHandler.warning(exception);
    } 
  }
  
  public void log(String message) {
    this.log.info(message);
  }
  
  public void log(String message, Throwable exception) {
    this.log.error(message, exception);
  }
  
  public Object parse(File file) throws IOException, SAXException {
    configure();
    InputSource input = new InputSource(new FileInputStream(file));
    input.setSystemId("file://" + file.getAbsolutePath());
    getXMLReader().parse(input);
    return this.root;
  }
  
  public Object parse(InputSource input) throws IOException, SAXException {
    configure();
    getXMLReader().parse(input);
    return this.root;
  }
  
  public Object parse(InputStream input) throws IOException, SAXException {
    configure();
    InputSource is = new InputSource(input);
    getXMLReader().parse(is);
    return this.root;
  }
  
  public Object parse(Reader reader) throws IOException, SAXException {
    configure();
    InputSource is = new InputSource(reader);
    getXMLReader().parse(is);
    return this.root;
  }
  
  public Object parse(String uri) throws IOException, SAXException {
    configure();
    InputSource is = new InputSource(uri);
    getXMLReader().parse(is);
    return this.root;
  }
  
  public void register(String publicId, String entityURL) {
    if (this.log.isDebugEnabled())
      this.log.debug("register('" + publicId + "', '" + entityURL + "'"); 
    this.entityValidator.put(publicId, entityURL);
  }
  
  public void addRule(String pattern, Rule rule) {
    rule.setDigester(this);
    getRules().add(pattern, rule);
  }
  
  public void addRuleSet(RuleSet ruleSet) {
    String oldNamespaceURI = getRuleNamespaceURI();
    String newNamespaceURI = ruleSet.getNamespaceURI();
    if (this.log.isDebugEnabled())
      if (newNamespaceURI == null) {
        this.log.debug("addRuleSet() with no namespace URI");
      } else {
        this.log.debug("addRuleSet() with namespace URI " + newNamespaceURI);
      }  
    setRuleNamespaceURI(newNamespaceURI);
    ruleSet.addRuleInstances(this);
    setRuleNamespaceURI(oldNamespaceURI);
  }
  
  public void addBeanPropertySetter(String pattern) {
    addRule(pattern, new BeanPropertySetterRule());
  }
  
  public void addBeanPropertySetter(String pattern, String propertyName) {
    addRule(pattern, new BeanPropertySetterRule(propertyName));
  }
  
  public void addCallMethod(String pattern, String methodName) {
    addRule(pattern, new CallMethodRule(methodName));
  }
  
  public void addCallMethod(String pattern, String methodName, int paramCount) {
    addRule(pattern, new CallMethodRule(methodName, paramCount));
  }
  
  public void addCallMethod(String pattern, String methodName, int paramCount, String[] paramTypes) {
    addRule(pattern, new CallMethodRule(methodName, paramCount, paramTypes));
  }
  
  public void addCallMethod(String pattern, String methodName, int paramCount, Class[] paramTypes) {
    addRule(pattern, new CallMethodRule(methodName, paramCount, paramTypes));
  }
  
  public void addCallParam(String pattern, int paramIndex) {
    addRule(pattern, new CallParamRule(paramIndex));
  }
  
  public void addCallParam(String pattern, int paramIndex, String attributeName) {
    addRule(pattern, new CallParamRule(paramIndex, attributeName));
  }
  
  public void addCallParam(String pattern, int paramIndex, boolean fromStack) {
    addRule(pattern, new CallParamRule(paramIndex, fromStack));
  }
  
  public void addCallParam(String pattern, int paramIndex, int stackIndex) {
    addRule(pattern, new CallParamRule(paramIndex, stackIndex));
  }
  
  public void addFactoryCreate(String pattern, String className) {
    addFactoryCreate(pattern, className, false);
  }
  
  public void addFactoryCreate(String pattern, Class clazz) {
    addFactoryCreate(pattern, clazz, false);
  }
  
  public void addFactoryCreate(String pattern, String className, String attributeName) {
    addFactoryCreate(pattern, className, attributeName, false);
  }
  
  public void addFactoryCreate(String pattern, Class clazz, String attributeName) {
    addFactoryCreate(pattern, clazz, attributeName, false);
  }
  
  public void addFactoryCreate(String pattern, ObjectCreationFactory creationFactory) {
    addFactoryCreate(pattern, creationFactory, false);
  }
  
  public void addFactoryCreate(String pattern, String className, boolean ignoreCreateExceptions) {
    addRule(pattern, new FactoryCreateRule(className, ignoreCreateExceptions));
  }
  
  public void addFactoryCreate(String pattern, Class clazz, boolean ignoreCreateExceptions) {
    addRule(pattern, new FactoryCreateRule(clazz, ignoreCreateExceptions));
  }
  
  public void addFactoryCreate(String pattern, String className, String attributeName, boolean ignoreCreateExceptions) {
    addRule(pattern, new FactoryCreateRule(className, attributeName, ignoreCreateExceptions));
  }
  
  public void addFactoryCreate(String pattern, Class clazz, String attributeName, boolean ignoreCreateExceptions) {
    addRule(pattern, new FactoryCreateRule(clazz, attributeName, ignoreCreateExceptions));
  }
  
  public void addFactoryCreate(String pattern, ObjectCreationFactory creationFactory, boolean ignoreCreateExceptions) {
    creationFactory.setDigester(this);
    addRule(pattern, new FactoryCreateRule(creationFactory, ignoreCreateExceptions));
  }
  
  public void addObjectCreate(String pattern, String className) {
    addRule(pattern, new ObjectCreateRule(className));
  }
  
  public void addObjectCreate(String pattern, Class clazz) {
    addRule(pattern, new ObjectCreateRule(clazz));
  }
  
  public void addObjectCreate(String pattern, String className, String attributeName) {
    addRule(pattern, new ObjectCreateRule(className, attributeName));
  }
  
  public void addObjectCreate(String pattern, String attributeName, Class clazz) {
    addRule(pattern, new ObjectCreateRule(attributeName, clazz));
  }
  
  public void addSetNext(String pattern, String methodName) {
    addRule(pattern, new SetNextRule(methodName));
  }
  
  public void addSetNext(String pattern, String methodName, String paramType) {
    addRule(pattern, new SetNextRule(methodName, paramType));
  }
  
  public void addSetRoot(String pattern, String methodName) {
    addRule(pattern, new SetRootRule(methodName));
  }
  
  public void addSetRoot(String pattern, String methodName, String paramType) {
    addRule(pattern, new SetRootRule(methodName, paramType));
  }
  
  public void addSetProperties(String pattern) {
    addRule(pattern, new SetPropertiesRule());
  }
  
  public void addSetProperties(String pattern, String attributeName, String propertyName) {
    addRule(pattern, new SetPropertiesRule(attributeName, propertyName));
  }
  
  public void addSetProperties(String pattern, String[] attributeNames, String[] propertyNames) {
    addRule(pattern, new SetPropertiesRule(attributeNames, propertyNames));
  }
  
  public void addSetProperty(String pattern, String name, String value) {
    addRule(pattern, new SetPropertyRule(name, value));
  }
  
  public void addSetTop(String pattern, String methodName) {
    addRule(pattern, new SetTopRule(methodName));
  }
  
  public void addSetTop(String pattern, String methodName, String paramType) {
    addRule(pattern, new SetTopRule(methodName, paramType));
  }
  
  public void clear() {
    this.match = "";
    this.bodyTexts.clear();
    this.params.clear();
    this.publicId = null;
    this.stack.clear();
  }
  
  public Object peek() {
    try {
      return this.stack.peek();
    } catch (EmptyStackException e) {
      this.log.warn("Empty stack (returning null)");
      return null;
    } 
  }
  
  public Object peek(int n) {
    try {
      return this.stack.peek(n);
    } catch (EmptyStackException e) {
      this.log.warn("Empty stack (returning null)");
      return null;
    } 
  }
  
  public Object pop() {
    try {
      return this.stack.pop();
    } catch (EmptyStackException e) {
      this.log.warn("Empty stack (returning null)");
      return null;
    } 
  }
  
  public void push(Object object) {
    if (this.stack.size() == 0)
      this.root = object; 
    this.stack.push(object);
  }
  
  public Object getRoot() {
    return this.root;
  }
  
  protected void configure() {
    if (this.configured)
      return; 
    this.configured = true;
  }
  
  Map getRegistrations() {
    return this.entityValidator;
  }
  
  List getRules(String match) {
    return getRules().match(match);
  }
  
  Object peekParams() {
    try {
      return this.params.peek();
    } catch (EmptyStackException e) {
      this.log.warn("Empty stack (returning null)");
      return null;
    } 
  }
  
  Object peekParams(int n) {
    try {
      return this.params.peek(n);
    } catch (EmptyStackException e) {
      this.log.warn("Empty stack (returning null)");
      return null;
    } 
  }
  
  Object popParams() {
    try {
      if (this.log.isTraceEnabled())
        this.log.trace("Popping params"); 
      return this.params.pop();
    } catch (EmptyStackException e) {
      this.log.warn("Empty stack (returning null)");
      return null;
    } 
  }
  
  void pushParams(Object object) {
    if (this.log.isTraceEnabled())
      this.log.trace("Pushing params"); 
    this.params.push(object);
  }
  
  protected SAXException createSAXException(String message, Exception e) {
    if (e != null && e instanceof InvocationTargetException) {
      Throwable t = ((InvocationTargetException)e).getTargetException();
      if (t != null && t instanceof Exception)
        e = (Exception)t; 
    } 
    if (this.locator != null) {
      String error = "Error at (" + this.locator.getLineNumber() + ", " + this.locator.getColumnNumber() + ": " + message;
      if (e != null)
        return new SAXParseException(error, this.locator, e); 
      return new SAXParseException(error, this.locator);
    } 
    this.log.error("No Locator!");
    if (e != null)
      return new SAXException(message, e); 
    return new SAXException(message);
  }
  
  protected SAXException createSAXException(Exception e) {
    if (e instanceof InvocationTargetException) {
      Throwable t = ((InvocationTargetException)e).getTargetException();
      if (t != null && t instanceof Exception)
        e = (Exception)t; 
    } 
    return createSAXException(e.getMessage(), e);
  }
  
  protected SAXException createSAXException(String message) {
    return createSAXException(message, null);
  }
}
