Reconcile with honeycomb-release

Change-Id: I2716b91f5b133ffd4b908227315129a5041fb3c6
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..8449e9d
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+apache-xml_src_files := $(call all-java-files-under,src/main/java)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := apache-xml
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(apache-xml_src_files)
+LOCAL_JAVACFLAGS := -encoding UTF-8
+LOCAL_JAVA_LIBRARIES := core
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_RESOURCE_DIRS := src/main/java
+include $(BUILD_JAVA_LIBRARY)
+
+ifeq ($(WITH_HOST_DALVIK),true)
+    include $(CLEAR_VARS)
+    LOCAL_MODULE := apache-xml-hostdex
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_SRC_FILES := $(apache-xml_src_files)
+    LOCAL_JAVACFLAGS := -encoding UTF-8
+    LOCAL_JAVA_LIBRARIES := core-hostdex
+    LOCAL_NO_STANDARD_LIBRARIES := true
+    LOCAL_BUILD_HOST_DEX := true
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_JAVA_RESOURCE_DIRS := src/main/java
+    include $(BUILD_HOST_JAVA_LIBRARY)
+endif
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..097bafb
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,80 @@
+   =========================================================================
+   ==  NOTICE file corresponding to section 4(d) of the Apache License,   ==
+   ==  Version 2.0, in this case for the Apache Xalan Java distribution.  ==
+   =========================================================================
+
+   Apache Xalan (Xalan XSLT processor)
+   Copyright 1999-2006 The Apache Software Foundation
+
+   Apache Xalan (Xalan serializer)
+   Copyright 1999-2006 The Apache Software Foundation
+
+   This product includes software developed at
+   The Apache Software Foundation (http://www.apache.org/).
+
+   =========================================================================
+   Portions of this software was originally based on the following:
+     - software copyright (c) 1999-2002, Lotus Development Corporation.,
+       http://www.lotus.com.
+     - software copyright (c) 2001-2002, Sun Microsystems.,
+       http://www.sun.com.
+     - software copyright (c) 2003, IBM Corporation., 
+       http://www.ibm.com.
+       
+   =========================================================================
+   The binary distribution package (ie. jars, samples and documentation) of
+   this product includes software developed by the following:
+       
+     - The Apache Software Foundation 
+         - Xerces Java - see LICENSE.txt 
+         - JAXP 1.3 APIs - see LICENSE.txt
+         - Bytecode Engineering Library - see LICENSE.txt
+         - Regular Expression - see LICENSE.txt
+       
+     - Scott Hudson, Frank Flannery, C. Scott Ananian 
+         - CUP Parser Generator runtime (javacup\runtime) - see LICENSE.txt 
+ 
+   ========================================================================= 
+   The source distribution package (ie. all source and tools required to build
+   Xalan Java) of this product includes software developed by the following:
+       
+     - The Apache Software Foundation
+         - Xerces Java - see LICENSE.txt 
+         - JAXP 1.3 APIs - see LICENSE.txt
+         - Bytecode Engineering Library - see LICENSE.txt
+         - Regular Expression - see LICENSE.txt
+         - Ant - see LICENSE.txt
+         - Stylebook doc tool - see LICENSE.txt    
+       
+     - Elliot Joel Berk and C. Scott Ananian 
+         - Lexical Analyzer Generator (JLex) - see LICENSE.txt
+
+   =========================================================================       
+   Apache Xerces Java
+   Copyright 1999-2006 The Apache Software Foundation
+
+   This product includes software developed at
+   The Apache Software Foundation (http://www.apache.org/).
+
+   Portions of Apache Xerces Java in xercesImpl.jar and xml-apis.jar
+   were originally based on the following:
+     - software copyright (c) 1999, IBM Corporation., http://www.ibm.com.
+     - software copyright (c) 1999, Sun Microsystems., http://www.sun.com.
+     - voluntary contributions made by Paul Eng on behalf of the 
+       Apache Software Foundation that were originally developed at iClick, Inc.,
+       software copyright (c) 1999.    
+
+   =========================================================================   
+   Apache xml-commons xml-apis (redistribution of xml-apis.jar)
+
+   Apache XML Commons
+   Copyright 2001-2003,2006 The Apache Software Foundation.
+
+   This product includes software developed at
+   The Apache Software Foundation (http://www.apache.org/).
+
+   Portions of this software were originally based on the following:
+     - software copyright (c) 1999, IBM Corporation., http://www.ibm.com.
+     - software copyright (c) 1999, Sun Microsystems., http://www.sun.com.
+     - software copyright (c) 2000 World Wide Web Consortium, http://www.w3.org
+
diff --git a/ThirdPartyProject.prop b/ThirdPartyProject.prop
new file mode 100644
index 0000000..0657faa
--- /dev/null
+++ b/ThirdPartyProject.prop
@@ -0,0 +1,12 @@
+currentVersion=889881,901014
+version=889881,901014
+isNative=false
+name=apache-xml
+keywords=apache xml commons xalan xslt xpath validation
+onDevice=true
+homepage=http://xml.apache.org/xalan-j/
+xalanSvn=http://svn.apache.org/repos/asf/xalan/java/trunk
+xalanSvnRev=889881
+xmlCommonsSvn=http://svn.apache.org/repos/asf/xml/commons/trunk
+xmlCommonsSvnRef=901014
+
diff --git a/src/main/java/org/apache/xalan/Version.java b/src/main/java/org/apache/xalan/Version.java
new file mode 100644
index 0000000..23e8fbe
--- /dev/null
+++ b/src/main/java/org/apache/xalan/Version.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Version.java 577939 2007-09-20 21:45:37Z minchau $
+ */
+package org.apache.xalan;
+
+/**
+ * Administrative class to keep track of the version number of
+ * the Xalan release.
+ * <P>This class implements the upcoming standard of having
+ * org.apache.project-name.Version.getVersion() be a standard way 
+ * to get version information.  This class will replace the older 
+ * org.apache.xalan.processor.Version class.</P>
+ * <P>See also: org/apache/xalan/res/XSLTInfo.properties for 
+ * information about the version of the XSLT spec we support.</P>
+ * @xsl.usage general
+ */
+public class Version
+{
+
+  /**
+   * Get the basic version string for the current Xalan release.
+   * Version String formatted like 
+   * <CODE>"<B>Xalan</B> <B>Java</B> v.r[.dd| <B>D</B>nn]"</CODE>.
+   *
+   * Futurework: have this read version info from jar manifest.
+   *
+   * @return String denoting our current version
+   */
+  public static String getVersion()
+  {
+     return getProduct()+" "+getImplementationLanguage()+" "
+           +getMajorVersionNum()+"."+getReleaseVersionNum()+"."
+           +( (getDevelopmentVersionNum() > 0) ? 
+               ("D"+getDevelopmentVersionNum()) : (""+getMaintenanceVersionNum()));  
+  }
+
+  /**
+   * Print the processor version to the command line.
+   *
+   * @param argv command line arguments, unused.
+   */
+  public static void main(String argv[])
+  {
+    System.out.println(getVersion());
+  }
+  
+  /**
+   * Name of product: Xalan.
+   */
+  public static String getProduct()
+  {
+    return "Xalan";
+  }
+
+  /**
+   * Implementation Language: Java.
+   */
+  public static String getImplementationLanguage()
+  {
+    return "Java";
+  }
+  
+  
+  /**
+   * Major version number.
+   * Version number. This changes only when there is a
+   *          significant, externally apparent enhancement from
+   *          the previous release. 'n' represents the n'th
+   *          version.
+   *
+   *          Clients should carefully consider the implications
+   *          of new versions as external interfaces and behaviour
+   *          may have changed.
+   */
+  public static int getMajorVersionNum()
+  {
+    return 2;
+    
+  }  
+
+  /**
+   * Release Number.
+   * Release number. This changes when:
+   *            -  a new set of functionality is to be added, eg,
+   *               implementation of a new W3C specification.
+   *            -  API or behaviour change.
+   *            -  its designated as a reference release.
+   */
+  public static int getReleaseVersionNum()
+  {
+    return 7;
+  }
+  
+  /**
+   * Maintenance Drop Number.
+   * Optional identifier used to designate maintenance
+   *          drop applied to a specific release and contains
+   *          fixes for defects reported. It maintains compatibility
+   *          with the release and contains no API changes.
+   *          When missing, it designates the final and complete
+   *          development drop for a release.
+   */
+  public static int getMaintenanceVersionNum()
+  {
+    return 1;
+  }
+
+  /**
+   * Development Drop Number.
+   * Optional identifier designates development drop of
+   *          a specific release. D01 is the first development drop
+   *          of a new release.
+   *
+   *          Development drops are works in progress towards a
+   *          compeleted, final release. A specific development drop
+   *          may not completely implement all aspects of a new
+   *          feature, which may take several development drops to
+   *          complete. At the point of the final drop for the
+   *          release, the D suffix will be omitted.
+   *
+   *          Each 'D' drops can contain functional enhancements as
+   *          well as defect fixes. 'D' drops may not be as stable as
+   *          the final releases.
+   */
+  public static int getDevelopmentVersionNum()
+  { 
+    try {   
+        if ((new String("")).length() == 0)
+          return 0;
+        else  
+          return Integer.parseInt("");
+    } catch (NumberFormatException nfe) {
+           return 0;
+    }    
+  }      
+}
diff --git a/src/main/java/org/apache/xalan/Version.src b/src/main/java/org/apache/xalan/Version.src
new file mode 100644
index 0000000..f1fb77e
--- /dev/null
+++ b/src/main/java/org/apache/xalan/Version.src
@@ -0,0 +1,153 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id$

+ */

+package org.apache.xalan;

+

+/**

+ * Administrative class to keep track of the version number of

+ * the Xalan release.

+ * <P>This class implements the upcoming standard of having

+ * org.apache.project-name.Version.getVersion() be a standard way 

+ * to get version information.  This class will replace the older 

+ * org.apache.xalan.processor.Version class.</P>

+ * <P>See also: org/apache/xalan/res/XSLTInfo.properties for 

+ * information about the version of the XSLT spec we support.</P>

+ * @xsl.usage general

+ */

+public class Version

+{

+

+  /**

+   * Get the basic version string for the current Xalan release.

+   * Version String formatted like 

+   * <CODE>"<B>Xalan</B> <B>Java</B> v.r[.dd| <B>D</B>nn]"</CODE>.

+   *

+   * Futurework: have this read version info from jar manifest.

+   *

+   * @return String denoting our current version

+   */

+  public static String getVersion()

+  {

+     return getProduct()+" "+getImplementationLanguage()+" "

+           +getMajorVersionNum()+"."+getReleaseVersionNum()+"."

+           +( (getDevelopmentVersionNum() > 0) ? 

+               ("D"+getDevelopmentVersionNum()) : (""+getMaintenanceVersionNum()));  

+  }

+

+  /**

+   * Print the processor version to the command line.

+   *

+   * @param argv command line arguments, unused.

+   */

+  public static void main(String argv[])

+  {

+    System.out.println(getVersion());

+  }

+  

+  /**

+   * Name of product: Xalan.

+   */

+  public static String getProduct()

+  {

+    return "Xalan";

+  }

+

+  /**

+   * Implementation Language: Java.

+   */

+  public static String getImplementationLanguage()

+  {

+    return "Java";

+  }

+  

+  

+  /**

+   * Major version number.

+   * Version number. This changes only when there is a

+   *          significant, externally apparent enhancement from

+   *          the previous release. 'n' represents the n'th

+   *          version.

+   *

+   *          Clients should carefully consider the implications

+   *          of new versions as external interfaces and behaviour

+   *          may have changed.

+   */

+  public static int getMajorVersionNum()

+  {

+    return @version.VERSION@;

+    

+  }  

+

+  /**

+   * Release Number.

+   * Release number. This changes when:

+   *            -  a new set of functionality is to be added, eg,

+   *               implementation of a new W3C specification.

+   *            -  API or behaviour change.

+   *            -  its designated as a reference release.

+   */

+  public static int getReleaseVersionNum()

+  {

+    return @version.RELEASE@;

+  }

+  

+  /**

+   * Maintenance Drop Number.

+   * Optional identifier used to designate maintenance

+   *          drop applied to a specific release and contains

+   *          fixes for defects reported. It maintains compatibility

+   *          with the release and contains no API changes.

+   *          When missing, it designates the final and complete

+   *          development drop for a release.

+   */

+  public static int getMaintenanceVersionNum()

+  {

+    return @version.MINOR@;

+  }

+

+  /**

+   * Development Drop Number.

+   * Optional identifier designates development drop of

+   *          a specific release. D01 is the first development drop

+   *          of a new release.

+   *

+   *          Development drops are works in progress towards a

+   *          compeleted, final release. A specific development drop

+   *          may not completely implement all aspects of a new

+   *          feature, which may take several development drops to

+   *          complete. At the point of the final drop for the

+   *          release, the D suffix will be omitted.

+   *

+   *          Each 'D' drops can contain functional enhancements as

+   *          well as defect fixes. 'D' drops may not be as stable as

+   *          the final releases.

+   */

+  public static int getDevelopmentVersionNum()

+  { 

+    try {   

+        if ((new String("@version.DEVELOPER@")).length() == 0)

+          return 0;

+        else  

+          return Integer.parseInt("@version.DEVELOPER@");

+    } catch (NumberFormatException nfe) {

+           return 0;

+    }    

+  }      

+}

diff --git a/src/main/java/org/apache/xalan/extensions/ExpressionContext.java b/src/main/java/org/apache/xalan/extensions/ExpressionContext.java
new file mode 100644
index 0000000..b40bb9a
--- /dev/null
+++ b/src/main/java/org/apache/xalan/extensions/ExpressionContext.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExpressionContext.java 468637 2006-10-28 06:51:02Z minchau $
+ */
+package org.apache.xalan.extensions;
+
+import javax.xml.transform.ErrorListener;
+
+import org.apache.xpath.objects.XObject;
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * An object that implements this interface can supply
+ * information about the current XPath expression context.
+ */
+public interface ExpressionContext
+{
+
+  /**
+   * Get the current context node.
+   * @return The current context node.
+   */
+  public Node getContextNode();
+
+  /**
+   * Get the current context node list.
+   * @return An iterator for the current context list, as
+   * defined in XSLT.
+   */
+  public NodeIterator getContextNodes();
+  
+  /**
+   * Get the error listener.
+   * @return The registered error listener.
+   */
+  public ErrorListener getErrorListener();
+
+  /**
+   * Get the value of a node as a number.
+   * @param n Node to be converted to a number.  May be null.
+   * @return value of n as a number.
+   */
+  public double toNumber(Node n);
+
+  /**
+   * Get the value of a node as a string.
+   * @param n Node to be converted to a string.  May be null.
+   * @return value of n as a string, or an empty string if n is null.
+   */
+  public String toString(Node n);
+
+  /**
+   * Get a variable based on it's qualified name.
+   *
+   * @param qname The qualified name of the variable.
+   *
+   * @return The evaluated value of the variable.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject getVariableOrParam(org.apache.xml.utils.QName qname)
+            throws javax.xml.transform.TransformerException;
+  
+  /**
+   * Get the XPathContext that owns this ExpressionContext.
+   * 
+   * Note: exslt:function requires the XPathContext to access
+   * the variable stack and TransformerImpl.
+   * 
+   * @return The current XPathContext.
+   * @throws javax.xml.transform.TransformerException
+   */
+  public org.apache.xpath.XPathContext getXPathContext()
+            throws javax.xml.transform.TransformerException;
+
+}
diff --git a/src/main/java/org/apache/xalan/extensions/ExpressionVisitor.java b/src/main/java/org/apache/xalan/extensions/ExpressionVisitor.java
new file mode 100644
index 0000000..cd895df
--- /dev/null
+++ b/src/main/java/org/apache/xalan/extensions/ExpressionVisitor.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExpressionVisitor.java 468637 2006-10-28 06:51:02Z minchau $
+ */
+package org.apache.xalan.extensions;
+
+import org.apache.xalan.templates.StylesheetRoot;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.functions.FuncExtFunction;
+import org.apache.xpath.functions.FuncExtFunctionAvailable;
+import org.apache.xpath.functions.Function;
+
+/**
+ * When {@link org.apache.xalan.processor.StylesheetHandler} creates 
+ * an {@link org.apache.xpath.XPath}, the ExpressionVisitor
+ * visits the XPath expression. For any extension functions it 
+ * encounters, it instructs StylesheetRoot to register the
+ * extension namespace. 
+ * 
+ * This mechanism is required to locate extension functions
+ * that may be embedded within an expression.
+ */
+public class ExpressionVisitor extends XPathVisitor
+{
+  private StylesheetRoot m_sroot;
+  
+  /**
+   * The constructor sets the StylesheetRoot variable which
+   * is used to register extension namespaces.
+   * @param sroot the StylesheetRoot that is being constructed.
+   */
+  public ExpressionVisitor (StylesheetRoot sroot)
+  {
+    m_sroot = sroot;
+  }
+  
+  /**
+   * If the function is an extension function, register the namespace.
+   * 
+   * @param owner The current XPath object that owns the expression.
+   * @param func The function currently being visited.
+   * 
+   * @return true to continue the visit in the subtree, if any.
+   */
+  public boolean visitFunction(ExpressionOwner owner, Function func)
+  {
+    if (func instanceof FuncExtFunction)
+    {
+      String namespace = ((FuncExtFunction)func).getNamespace();
+      m_sroot.getExtensionNamespacesManager().registerExtension(namespace);      
+    }
+    else if (func instanceof FuncExtFunctionAvailable)
+    {
+      String arg = ((FuncExtFunctionAvailable)func).getArg0().toString();
+      if (arg.indexOf(":") > 0)
+      {
+      	String prefix = arg.substring(0,arg.indexOf(":"));
+      	String namespace = this.m_sroot.getNamespaceForPrefix(prefix);
+      	m_sroot.getExtensionNamespacesManager().registerExtension(namespace);
+      }
+    }
+    return true;
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/extensions/ExtensionHandler.java b/src/main/java/org/apache/xalan/extensions/ExtensionHandler.java
new file mode 100644
index 0000000..9c46e8a
--- /dev/null
+++ b/src/main/java/org/apache/xalan/extensions/ExtensionHandler.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExtensionHandler.java 468637 2006-10-28 06:51:02Z minchau $
+ */
+package org.apache.xalan.extensions;
+
+import java.io.IOException;
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.Stylesheet;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.functions.FuncExtFunction;
+
+/**
+ * Abstract base class for handling an extension namespace for XPath.
+ * Provides functions to test a function's existence and call a function.
+ * Also provides functions for calling an element and testing for
+ * an element's existence.
+ *
+ * @author Sanjiva Weerawarana (sanjiva@watson.ibm.com)
+ * @xsl.usage internal
+ */
+public abstract class ExtensionHandler
+{
+
+  /** uri of the extension namespace */
+  protected String m_namespaceUri; 
+
+  /** scripting language of implementation */
+  protected String m_scriptLang;
+
+  /**
+   * This method loads a class using the context class loader if we're
+   * running under Java2 or higher.
+   * 
+   * @param className Name of the class to load
+   */
+  static Class getClassForName(String className)
+      throws ClassNotFoundException
+  {
+    // Hack for backwards compatibility with XalanJ1 stylesheets
+    if(className.equals("org.apache.xalan.xslt.extensions.Redirect")) {
+      className = "org.apache.xalan.lib.Redirect";
+    }
+
+    return ObjectFactory.findProviderClass(
+        className, ObjectFactory.findClassLoader(), true);
+  }
+
+  /**
+   * Construct a new extension namespace handler given all the information
+   * needed.
+   *
+   * @param namespaceUri the extension namespace URI that I'm implementing
+   * @param scriptLang   language of code implementing the extension
+   */
+  protected ExtensionHandler(String namespaceUri, String scriptLang)
+  {
+    m_namespaceUri = namespaceUri;
+    m_scriptLang = scriptLang;
+  }
+
+  /**
+   * Tests whether a certain function name is known within this namespace.
+   * @param function name of the function being tested
+   * @return true if its known, false if not.
+   */
+  public abstract boolean isFunctionAvailable(String function);
+
+  /**
+   * Tests whether a certain element name is known within this namespace.
+   * @param element Name of element to check
+   * @return true if its known, false if not.
+   */
+  public abstract boolean isElementAvailable(String element);
+
+  /**
+   * Process a call to a function.
+   *
+   * @param funcName Function name.
+   * @param args     The arguments of the function call.
+   * @param methodKey A key that uniquely identifies this class and method call.
+   * @param exprContext The context in which this expression is being executed.
+   *
+   * @return the return value of the function evaluation.
+   *
+   * @throws TransformerException          if parsing trouble
+   */
+  public abstract Object callFunction(
+    String funcName, Vector args, Object methodKey,
+      ExpressionContext exprContext) throws TransformerException;
+
+  /**
+   * Process a call to a function.
+   *
+   * @param extFunction The XPath extension function.
+   * @param args     The arguments of the function call.
+   * @param exprContext The context in which this expression is being executed.
+   *
+   * @return the return value of the function evaluation.
+   *
+   * @throws TransformerException          if parsing trouble
+   */
+  public abstract Object callFunction(
+    FuncExtFunction extFunction, Vector args,
+      ExpressionContext exprContext) throws TransformerException;
+
+  /**
+   * Process a call to this extension namespace via an element. As a side
+   * effect, the results are sent to the TransformerImpl's result tree.
+   *
+   * @param localPart      Element name's local part.
+   * @param element        The extension element being processed.
+   * @param transformer    Handle to TransformerImpl.
+   * @param stylesheetTree The compiled stylesheet tree.
+   * @param methodKey      A key that uniquely identifies this class and method call.
+   *
+   * @throws XSLProcessorException thrown if something goes wrong
+   *            while running the extension handler.
+   * @throws MalformedURLException if loading trouble
+   * @throws FileNotFoundException if loading trouble
+   * @throws IOException           if loading trouble
+   * @throws TransformerException  if parsing trouble
+   */
+  public abstract void processElement(
+    String localPart, ElemTemplateElement element, TransformerImpl transformer,
+      Stylesheet stylesheetTree, Object methodKey) throws TransformerException, IOException;
+}
diff --git a/src/main/java/org/apache/xalan/extensions/ExtensionNamespaceSupport.java b/src/main/java/org/apache/xalan/extensions/ExtensionNamespaceSupport.java
new file mode 100644
index 0000000..8ad232f
--- /dev/null
+++ b/src/main/java/org/apache/xalan/extensions/ExtensionNamespaceSupport.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExtensionNamespaceSupport.java 468637 2006-10-28 06:51:02Z minchau $
+ */
+package org.apache.xalan.extensions;
+
+import java.lang.reflect.Constructor;
+
+import javax.xml.transform.TransformerException;
+
+/**
+ * During styleseet composition, an ExtensionNamespaceSupport object is created for each extension 
+ * namespace the stylesheet uses. At the beginning of a transformation, TransformerImpl generates
+ * an ExtensionHandler for each of these objects and adds an entry to the ExtensionsTable hashtable.
+ */
+public class ExtensionNamespaceSupport
+{
+  // Namespace, ExtensionHandler class name, constructor signature 
+  // and arguments.
+  String m_namespace = null;
+  String m_handlerClass = null;
+  Class [] m_sig = null;  
+  Object [] m_args = null;
+ 
+  public ExtensionNamespaceSupport(String namespace, 
+                                   String handlerClass, 
+                                   Object[] constructorArgs)
+  {
+    m_namespace = namespace;
+    m_handlerClass = handlerClass;
+    m_args = constructorArgs;
+    // Create the constructor signature.
+    m_sig = new Class[m_args.length];
+    for (int i = 0; i < m_args.length; i++)
+    {
+      if (m_args[i] != null)
+        m_sig[i] = m_args[i].getClass();//System.out.println("arg class " + i + " " +m_sig[i]);
+      else // If an arguments is null, pick the constructor later.
+      {
+        m_sig = null;
+        break;
+      }
+    }
+  }
+  
+  public String getNamespace()
+  {
+    return m_namespace;
+  }
+  
+  /**
+   * Launch the ExtensionHandler that this ExtensionNamespaceSupport object defines.
+   */
+  public ExtensionHandler launch()
+    throws TransformerException
+  {
+    ExtensionHandler handler = null;
+    try
+    {
+      Class cl = ExtensionHandler.getClassForName(m_handlerClass);
+      Constructor con = null;
+      //System.out.println("class " + cl + " " + m_args + " " + m_args.length + " " + m_sig);
+      if (m_sig != null)
+        con = cl.getConstructor(m_sig);
+      else // Pick the constructor based on number of args.
+      {
+        Constructor[] cons = cl.getConstructors();
+        for (int i = 0; i < cons.length; i ++)
+        {
+          if (cons[i].getParameterTypes().length == m_args.length)
+          {
+            con = cons[i];
+            break;
+          }
+        }
+      }
+      // System.out.println("constructor " + con);
+      if (con != null)
+        handler = (ExtensionHandler)con.newInstance(m_args);
+      else
+        throw new TransformerException("ExtensionHandler constructor not found");
+    }
+    catch (Exception e)
+    {
+      throw new TransformerException(e);
+    }
+    return handler;
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/extensions/ExtensionNamespacesManager.java b/src/main/java/org/apache/xalan/extensions/ExtensionNamespacesManager.java
new file mode 100644
index 0000000..867b14c
--- /dev/null
+++ b/src/main/java/org/apache/xalan/extensions/ExtensionNamespacesManager.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExtensionNamespacesManager.java 469672 2006-10-31 21:56:19Z minchau $
+ */
+package org.apache.xalan.extensions;
+
+import java.util.Vector;
+
+import org.apache.xalan.templates.Constants;
+
+/**
+ * Used during assembly of a stylesheet to collect the information for each 
+ * extension namespace that is required during the transformation process 
+ * to generate an {@link ExtensionHandler}.
+ * 
+ */
+public class ExtensionNamespacesManager
+{
+  /**
+   * Vector of ExtensionNamespaceSupport objects to be used to generate ExtensionHandlers.
+   */
+  private Vector m_extensions = new Vector();
+  /**
+   * Vector of ExtensionNamespaceSupport objects for predefined ExtensionNamespaces. Elements
+   * from this vector are added to the m_extensions vector when encountered in the stylesheet.
+   */
+  private Vector m_predefExtensions = new Vector(7);
+  /**
+   * Vector of extension namespaces for which sufficient information is not yet available to
+   * complete the registration process.
+   */
+  private Vector m_unregisteredExtensions = new Vector();
+  
+  /**
+   * An ExtensionNamespacesManager is instantiated the first time an extension function or
+   * element is found in the stylesheet. During initialization, a vector of ExtensionNamespaceSupport
+   * objects is created, one for each predefined extension namespace.
+   */
+  public ExtensionNamespacesManager()
+  {
+    setPredefinedNamespaces();
+  }
+  
+  /**
+   * If necessary, register the extension namespace found compiling a function or 
+   * creating an extension element. 
+   * 
+   * If it is a predefined namespace, create a
+   * support object to simplify the instantiate of an appropriate ExtensionHandler
+   * during transformation runtime. Otherwise, add the namespace, if necessary,
+   * to a vector of undefined extension namespaces, to be defined later.
+   * 
+   */
+  public void registerExtension(String namespace)
+  {
+    if (namespaceIndex(namespace, m_extensions) == -1)
+    {
+      int predef = namespaceIndex(namespace, m_predefExtensions);
+      if (predef !=-1)
+        m_extensions.add(m_predefExtensions.get(predef));
+      else if (!(m_unregisteredExtensions.contains(namespace)))
+        m_unregisteredExtensions.add(namespace);       
+    }
+  }
+  
+  /**
+   * Register the extension namespace for an ElemExtensionDecl or ElemFunction,
+   * and prepare a support object to launch the appropriate ExtensionHandler at 
+   * transformation runtime.
+   */  
+  public void registerExtension(ExtensionNamespaceSupport extNsSpt)
+  {
+    String namespace = extNsSpt.getNamespace();
+    if (namespaceIndex(namespace, m_extensions) == -1)
+    {
+      m_extensions.add(extNsSpt);
+      if (m_unregisteredExtensions.contains(namespace))
+        m_unregisteredExtensions.remove(namespace);
+    }
+    
+  }
+  
+  /**
+   * Get the index for a namespace entry in the extension namespace Vector, -1 if
+   * no such entry yet exists.
+   */
+  public int namespaceIndex(String namespace, Vector extensions)
+  {
+    for (int i = 0; i < extensions.size(); i++)
+    {
+      if (((ExtensionNamespaceSupport)extensions.get(i)).getNamespace().equals(namespace))
+        return i;
+    }
+    return -1;
+  }
+  
+    
+  /**
+   * Get the vector of extension namespaces. Used to provide
+   * the extensions table access to a list of extension
+   * namespaces encountered during composition of a stylesheet.
+   */
+  public Vector getExtensions()
+  {
+    return m_extensions;
+  }
+  
+  /**
+   * Attempt to register any unregistered extension namespaces.
+   */
+  public void registerUnregisteredNamespaces()
+  {
+    for (int i = 0; i < m_unregisteredExtensions.size(); i++)
+    {
+      String ns = (String)m_unregisteredExtensions.get(i);
+      ExtensionNamespaceSupport extNsSpt = defineJavaNamespace(ns);
+      if (extNsSpt != null)
+        m_extensions.add(extNsSpt);
+    }    
+  }
+  
+    /**
+   * For any extension namespace that is not either predefined or defined 
+   * by a "component" declaration or exslt function declaration, attempt 
+   * to create an ExtensionNamespaceSuport object for the appropriate 
+   * Java class or Java package Extension Handler.
+   * 
+   * Called by StylesheetRoot.recompose(), after all ElemTemplate compose()
+   * operations have taken place, in order to set up handlers for
+   * the remaining extension namespaces.
+   * 
+   * @param ns The extension namespace URI.
+   * @return   An ExtensionNamespaceSupport object for this namespace
+   * (which defines the ExtensionHandler to be used), or null if such 
+   * an object cannot be created. 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public ExtensionNamespaceSupport defineJavaNamespace(String ns)
+  {
+    return defineJavaNamespace(ns, ns);
+  }
+  public ExtensionNamespaceSupport defineJavaNamespace(String ns, String classOrPackage)
+  {
+    if(null == ns || ns.trim().length() == 0) // defensive. I don't think it's needed.  -sb
+      return null;
+
+    // Prepare the name of the actual class or package, stripping
+    // out any leading "class:".  Next, see if there is a /.  If so,
+    // only look at the text to the right of the rightmost /.
+    String className = classOrPackage;
+    if (className.startsWith("class:"))
+      className = className.substring(6);
+
+    int lastSlash = className.lastIndexOf("/");
+    if (-1 != lastSlash)
+      className = className.substring(lastSlash + 1);
+      
+    // The className can be null here, and can cause an error in getClassForName
+    // in JDK 1.8.
+    if(null == className || className.trim().length() == 0) 
+      return null;
+    
+    try
+    {
+      ExtensionHandler.getClassForName(className);
+      return new ExtensionNamespaceSupport(
+                           ns, 
+                           "org.apache.xalan.extensions.ExtensionHandlerJavaClass",                                         
+                           new Object[]{ns, "javaclass", className});
+    }
+    catch (ClassNotFoundException e)
+    {
+      return new ExtensionNamespaceSupport(
+                            ns, 
+                            "org.apache.xalan.extensions.ExtensionHandlerJavaPackage",
+                            new Object[]{ns, "javapackage", className + "."});
+    }
+  }
+  
+/*
+  public ExtensionNamespaceSupport getSupport(int index, Vector extensions)
+  {
+    return (ExtensionNamespaceSupport)extensions.elementAt(index);
+  }
+*/
+  
+  
+  /**
+   * Set up a Vector for predefined extension namespaces.
+   */
+  private void setPredefinedNamespaces()
+  {    
+    String uri = Constants.S_EXTENSIONS_JAVA_URL;
+    String handlerClassName = "org.apache.xalan.extensions.ExtensionHandlerJavaPackage";
+    String lang = "javapackage";
+    String lib = "";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+   
+    uri = Constants.S_EXTENSIONS_OLD_JAVA_URL;
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+    
+    uri = Constants.S_EXTENSIONS_LOTUSXSL_JAVA_URL;
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+    
+    uri = Constants.S_BUILTIN_EXTENSIONS_URL;
+    handlerClassName = "org.apache.xalan.extensions.ExtensionHandlerJavaClass";
+    lang = "javaclass"; // for remaining predefined extension namespaces.    
+    lib = "org.apache.xalan.lib.Extensions";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+    
+    uri = Constants.S_BUILTIN_OLD_EXTENSIONS_URL;
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+    
+    // Xalan extension namespaces (redirect, pipe and SQL).
+    uri = Constants.S_EXTENSIONS_REDIRECT_URL;
+    lib = "org.apache.xalan.lib.Redirect";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+ 
+    uri = Constants.S_EXTENSIONS_PIPE_URL;
+    lib = "org.apache.xalan.lib.PipeDocument";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+ 
+    uri = Constants.S_EXTENSIONS_SQL_URL;
+    lib = "org.apache.xalan.lib.sql.XConnection";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+ 
+    
+    //EXSLT namespaces (not including EXSLT function namespaces which are
+    // registered by the associated ElemFunction.
+    uri = Constants.S_EXSLT_COMMON_URL;
+    lib = "org.apache.xalan.lib.ExsltCommon";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+
+    uri = Constants.S_EXSLT_MATH_URL;
+    lib = "org.apache.xalan.lib.ExsltMath";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+    
+    uri = Constants.S_EXSLT_SETS_URL;
+    lib = "org.apache.xalan.lib.ExsltSets";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+    
+    uri = Constants.S_EXSLT_DATETIME_URL;
+    lib = "org.apache.xalan.lib.ExsltDatetime";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+                                             
+    uri = Constants.S_EXSLT_DYNAMIC_URL;
+    lib = "org.apache.xalan.lib.ExsltDynamic";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));
+
+    uri = Constants.S_EXSLT_STRINGS_URL;
+    lib = "org.apache.xalan.lib.ExsltStrings";
+    m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
+                                             new Object[]{uri, lang, lib}));                                             
+  }    
+  
+}
diff --git a/src/main/java/org/apache/xalan/extensions/ExtensionsTable.java b/src/main/java/org/apache/xalan/extensions/ExtensionsTable.java
new file mode 100644
index 0000000..cb5cc48
--- /dev/null
+++ b/src/main/java/org/apache/xalan/extensions/ExtensionsTable.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExtensionsTable.java 469672 2006-10-31 21:56:19Z minchau $
+ */
+package org.apache.xalan.extensions;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.StylesheetRoot;
+import org.apache.xpath.XPathProcessorException;
+import org.apache.xpath.functions.FuncExtFunction;
+
+/**
+ * Class holding a table registered extension namespace handlers
+ * @xsl.usage internal
+ */
+public class ExtensionsTable
+{  
+  /**
+   * Table of extensions that may be called from the expression language
+   * via the call(name, ...) function.  Objects are keyed on the call
+   * name.
+   * @xsl.usage internal
+   */
+  public Hashtable m_extensionFunctionNamespaces = new Hashtable();
+  
+  /**
+   * The StylesheetRoot associated with this extensions table.
+   */
+  private StylesheetRoot m_sroot;
+  
+  /**
+   * The constructor (called from TransformerImpl) registers the
+   * StylesheetRoot for the transformation and instantiates an
+   * ExtensionHandler for each extension namespace.
+   * @xsl.usage advanced
+   */
+  public ExtensionsTable(StylesheetRoot sroot)
+    throws javax.xml.transform.TransformerException
+  {
+    m_sroot = sroot;
+    Vector extensions = m_sroot.getExtensions();
+    for (int i = 0; i < extensions.size(); i++)
+    {
+      ExtensionNamespaceSupport extNamespaceSpt = 
+                 (ExtensionNamespaceSupport)extensions.get(i);
+      ExtensionHandler extHandler = extNamespaceSpt.launch();
+        if (extHandler != null)
+          addExtensionNamespace(extNamespaceSpt.getNamespace(), extHandler);
+      }
+    }
+       
+  /**
+   * Get an ExtensionHandler object that represents the
+   * given namespace.
+   * @param extns A valid extension namespace.
+   *
+   * @return ExtensionHandler object that represents the
+   * given namespace.
+   */
+  public ExtensionHandler get(String extns)
+  {
+    return (ExtensionHandler) m_extensionFunctionNamespaces.get(extns);
+  }
+
+  /**
+   * Register an extension namespace handler. This handler provides
+   * functions for testing whether a function is known within the
+   * namespace and also for invoking the functions.
+   *
+   * @param uri the URI for the extension.
+   * @param extNS the extension handler.
+   * @xsl.usage advanced
+   */
+  public void addExtensionNamespace(String uri, ExtensionHandler extNS)
+  {
+    m_extensionFunctionNamespaces.put(uri, extNS);
+  }
+
+  /**
+   * Execute the function-available() function.
+   * @param ns       the URI of namespace in which the function is needed
+   * @param funcName the function name being tested
+   *
+   * @return whether the given function is available or not.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean functionAvailable(String ns, String funcName)
+          throws javax.xml.transform.TransformerException
+  {
+    boolean isAvailable = false;
+    
+    if (null != ns)
+    {
+      ExtensionHandler extNS = 
+           (ExtensionHandler) m_extensionFunctionNamespaces.get(ns);
+      if (extNS != null)
+        isAvailable = extNS.isFunctionAvailable(funcName);
+    }
+    return isAvailable;
+  }
+  
+  /**
+   * Execute the element-available() function.
+   * @param ns       the URI of namespace in which the function is needed
+   * @param elemName name of element being tested
+   *
+   * @return whether the given element is available or not.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean elementAvailable(String ns, String elemName)
+          throws javax.xml.transform.TransformerException
+  {
+    boolean isAvailable = false;
+    if (null != ns)
+    {
+      ExtensionHandler extNS = 
+               (ExtensionHandler) m_extensionFunctionNamespaces.get(ns);
+      if (extNS != null) // defensive
+        isAvailable = extNS.isElementAvailable(elemName);
+    } 
+    return isAvailable;        
+  }  
+  
+  /**
+   * Handle an extension function.
+   * @param ns        the URI of namespace in which the function is needed
+   * @param funcName  the function name being called
+   * @param argVec    arguments to the function in a vector
+   * @param methodKey a unique key identifying this function instance in the
+   *                  stylesheet
+   * @param exprContext a context which may be passed to an extension function
+   *                  and provides callback functions to access various
+   *                  areas in the environment
+   *
+   * @return result of executing the function
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public Object extFunction(String ns, String funcName, 
+                            Vector argVec, Object methodKey, 
+                            ExpressionContext exprContext)
+            throws javax.xml.transform.TransformerException
+  {
+    Object result = null;
+    if (null != ns)
+    {
+      ExtensionHandler extNS =
+        (ExtensionHandler) m_extensionFunctionNamespaces.get(ns);
+      if (null != extNS)
+      {
+        try
+        {
+          result = extNS.callFunction(funcName, argVec, methodKey,
+                                      exprContext);
+        }
+        catch (javax.xml.transform.TransformerException e)
+        {
+          throw e;
+        }
+        catch (Exception e)
+        {
+          throw new javax.xml.transform.TransformerException(e);
+        }
+      }
+      else
+      {
+        throw new XPathProcessorException(XSLMessages.createMessage(XSLTErrorResources.ER_EXTENSION_FUNC_UNKNOWN, new Object[]{ns, funcName })); 
+        //"Extension function '" + ns + ":" + funcName + "' is unknown");
+      }
+    }
+    return result;    
+  }
+  
+  /**
+   * Handle an extension function.
+   * @param extFunction  the extension function
+   * @param argVec    arguments to the function in a vector
+   * @param exprContext a context which may be passed to an extension function
+   *                  and provides callback functions to access various
+   *                  areas in the environment
+   *
+   * @return result of executing the function
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public Object extFunction(FuncExtFunction extFunction, Vector argVec, 
+                            ExpressionContext exprContext)
+         throws javax.xml.transform.TransformerException
+  {
+    Object result = null;
+    String ns = extFunction.getNamespace();
+    if (null != ns)
+    {
+      ExtensionHandler extNS =
+        (ExtensionHandler) m_extensionFunctionNamespaces.get(ns);
+      if (null != extNS)
+      {
+        try
+        {
+          result = extNS.callFunction(extFunction, argVec, exprContext);
+        }
+        catch (javax.xml.transform.TransformerException e)
+        {
+          throw e;
+        }
+        catch (Exception e)
+        {
+          throw new javax.xml.transform.TransformerException(e);
+        }
+      }
+      else
+      {
+        throw new XPathProcessorException(XSLMessages.createMessage(XSLTErrorResources.ER_EXTENSION_FUNC_UNKNOWN, 
+                                          new Object[]{ns, extFunction.getFunctionName()})); 
+      }
+    }
+    return result;        
+  }
+}
diff --git a/src/main/java/org/apache/xalan/extensions/ObjectFactory.java b/src/main/java/org/apache/xalan/extensions/ObjectFactory.java
new file mode 100755
index 0000000..3772cb7
--- /dev/null
+++ b/src/main/java/org/apache/xalan/extensions/ObjectFactory.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ObjectFactory.java 468637 2006-10-28 06:51:02Z minchau $
+ */
+
+package org.apache.xalan.extensions;
+
+/**
+ * This class is duplicated for each JAXP subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the JAXP
+ * API.
+ * <p>
+ * This code is designed to implement the JAXP 1.1 spec pluggability
+ * feature and is designed to run on JDK version 1.1 and
+ * later, and to compile on JDK 1.2 and onward.  
+ * The code also runs both as part of an unbundled jar file and
+ * when bundled as part of the JDK.
+ * <p>
+ * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
+ * class and modified to be used as a general utility for creating objects 
+ * dynamically.
+ *
+ * @version $Id: ObjectFactory.java 468637 2006-10-28 06:51:02Z minchau $
+ */
+class ObjectFactory {
+
+    /**
+     * Figure out which ClassLoader to use.  For JDK 1.2 and later use
+     * the context ClassLoader.
+     */
+    static ClassLoader findClassLoader()
+        throws ConfigurationError
+    {
+        // BEGIN android-changed
+        //     the context class loader is always sufficient
+        return Thread.currentThread().getContextClassLoader();
+        // END android-changed
+    } // findClassLoader():ClassLoader
+
+    /**
+     * Find a Class using the specified ClassLoader
+     */ 
+    static Class findProviderClass(String className, ClassLoader cl,
+                                           boolean doFallback)
+        throws ClassNotFoundException, ConfigurationError
+    {   
+        //throw security exception if the calling thread is not allowed to access the
+        //class. Restrict the access to the package classes as specified in java.security policy.
+        SecurityManager security = System.getSecurityManager();
+        try{
+                if (security != null){
+                    final int lastDot = className.lastIndexOf(".");
+                    String packageName = className;
+                    if (lastDot != -1) packageName = className.substring(0, lastDot);
+                    security.checkPackageAccess(packageName);
+                 }   
+        }catch(SecurityException e){
+            throw e;
+        }
+        
+        Class providerClass;
+        if (cl == null) {
+            // XXX Use the bootstrap ClassLoader.  There is no way to
+            // load a class using the bootstrap ClassLoader that works
+            // in both JDK 1.1 and Java 2.  However, this should still
+            // work b/c the following should be true:
+            //
+            // (cl == null) iff current ClassLoader == null
+            //
+            // Thus Class.forName(String) will use the current
+            // ClassLoader which will be the bootstrap ClassLoader.
+            providerClass = Class.forName(className);
+        } else {
+            try {
+                providerClass = cl.loadClass(className);
+            } catch (ClassNotFoundException x) {
+                if (doFallback) {
+                    // Fall back to current classloader
+                    ClassLoader current = ObjectFactory.class.getClassLoader();
+                    if (current == null) {
+                        providerClass = Class.forName(className);
+                    } else if (cl != current) {
+                        cl = current;
+                        providerClass = cl.loadClass(className);
+                    } else {
+                        throw x;
+                    }
+                } else {
+                    throw x;
+                }
+            }
+        }
+
+        return providerClass;
+    }
+
+    //
+    // Classes
+    //
+
+    /**
+     * A configuration error.
+     */
+    static class ConfigurationError 
+        extends Error {
+                static final long serialVersionUID = 8564305128443551853L;
+        //
+        // Data
+        //
+
+        /** Exception. */
+        private Exception exception;
+
+        //
+        // Constructors
+        //
+
+        /**
+         * Construct a new instance with the specified detail string and
+         * exception.
+         */
+        ConfigurationError(String msg, Exception x) {
+            super(msg);
+            this.exception = x;
+        } // <init>(String,Exception)
+
+        //
+        // Public methods
+        //
+
+        /** Returns the exception associated to this error. */
+        Exception getException() {
+            return exception;
+        } // getException():Exception
+
+    } // class ConfigurationError
+
+} // class ObjectFactory
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorAttributeSet.java b/src/main/java/org/apache/xalan/processor/ProcessorAttributeSet.java
new file mode 100644
index 0000000..97f1660
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorAttributeSet.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorAttributeSet.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.templates.ElemAttributeSet;
+import org.apache.xalan.templates.ElemTemplateElement;
+
+import org.xml.sax.Attributes;
+
+/**
+ * This class processes parse events for an xsl:attribute-set.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
+ */
+class ProcessorAttributeSet extends XSLTElementProcessor
+{
+    static final long serialVersionUID = -6473739251316787552L;
+
+  /**
+   * Receive notification of the start of an xsl:attribute-set element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   * 
+   * @see org.apache.xalan.processor.StylesheetHandler#startElement
+   * @see org.xml.sax.ContentHandler#startElement
+   * @see org.xml.sax.ContentHandler#endElement
+   * @see org.xml.sax.Attributes
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+
+    ElemAttributeSet eat = new ElemAttributeSet();
+
+    eat.setLocaterInfo(handler.getLocator());
+    try
+    {
+      eat.setPrefixes(handler.getNamespaceSupport());
+    }
+    catch(TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+
+    eat.setDOMBackPointer(handler.getOriginatingNode());
+    setPropertiesFromAttributes(handler, rawName, attributes, eat);
+    handler.getStylesheet().setAttributeSet(eat);
+
+    // handler.pushElemTemplateElement(eat);
+    ElemTemplateElement parent = handler.getElemTemplateElement();
+
+    parent.appendChild(eat);
+    handler.pushElemTemplateElement(eat);
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * @param name The element type name.
+   * @param attributes The specified or defaulted attributes.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+    handler.popElemTemplateElement();
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorCharacters.java b/src/main/java/org/apache/xalan/processor/ProcessorCharacters.java
new file mode 100644
index 0000000..b318f39
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorCharacters.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorCharacters.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.ElemText;
+import org.apache.xalan.templates.ElemTextLiteral;
+import org.apache.xml.utils.XMLCharacterRecognizer;
+
+import org.w3c.dom.Node;
+
+/**
+ * This class processes character events for a XSLT template element.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Creating-the-Result-Tree">section-Creating-the-Result-Tree in XSLT Specification</a>
+ */
+public class ProcessorCharacters extends XSLTElementProcessor
+{
+    static final long serialVersionUID = 8632900007814162650L;
+
+  /**
+   * Receive notification of the start of the non-text event.  This
+   * is sent to the current processor when any non-text event occurs.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   */
+  public void startNonText(StylesheetHandler handler) throws org.xml.sax.SAXException
+  {
+    if (this == handler.getCurrentProcessor())
+    {
+      handler.popProcessor();
+    }
+
+    int nChars = m_accumulator.length();
+
+    if ((nChars > 0)
+            && ((null != m_xslTextElement)
+                ||!XMLCharacterRecognizer.isWhiteSpace(m_accumulator)) 
+                || handler.isSpacePreserve())
+    {
+      ElemTextLiteral elem = new ElemTextLiteral();
+
+      elem.setDOMBackPointer(m_firstBackPointer);
+      elem.setLocaterInfo(handler.getLocator());
+      try
+      {
+        elem.setPrefixes(handler.getNamespaceSupport());
+      }
+      catch(TransformerException te)
+      {
+        throw new org.xml.sax.SAXException(te);
+      }
+
+      boolean doe = (null != m_xslTextElement)
+                    ? m_xslTextElement.getDisableOutputEscaping() : false;
+
+      elem.setDisableOutputEscaping(doe);
+      elem.setPreserveSpace(true);
+
+      char[] chars = new char[nChars];
+
+      m_accumulator.getChars(0, nChars, chars, 0);
+      elem.setChars(chars);
+
+      ElemTemplateElement parent = handler.getElemTemplateElement();
+
+      parent.appendChild(elem);
+    }
+
+    m_accumulator.setLength(0);
+    m_firstBackPointer = null;
+  }
+  
+  protected Node m_firstBackPointer = null;
+
+  /**
+   * Receive notification of character data inside an element.
+   *
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param ch The characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#characters
+   */
+  public void characters(
+          StylesheetHandler handler, char ch[], int start, int length)
+            throws org.xml.sax.SAXException
+  {
+
+    m_accumulator.append(ch, start, length);
+    
+    if(null == m_firstBackPointer)
+      m_firstBackPointer = handler.getOriginatingNode();
+
+    // Catch all events until a non-character event.
+    if (this != handler.getCurrentProcessor())
+      handler.pushProcessor(this);
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @see org.apache.xalan.processor.StylesheetHandler#startElement
+   * @see org.apache.xalan.processor.StylesheetHandler#endElement
+   * @see org.xml.sax.ContentHandler#startElement
+   * @see org.xml.sax.ContentHandler#endElement
+   * @see org.xml.sax.Attributes
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+
+    // Since this has been installed as the current processor, we 
+    // may get and end element event, in which case, we pop and clear 
+    // and then call the real element processor.
+    startNonText(handler);
+    handler.getCurrentProcessor().endElement(handler, uri, localName,
+                                             rawName);
+    handler.popProcessor();
+  }
+
+  /**
+   * Accumulate characters, until a non-whitespace event has
+   * occured.
+   */
+  private StringBuffer m_accumulator = new StringBuffer();
+
+  /**
+   * The xsl:text processor will call this to set a
+   * preserve space state.
+   */
+  private ElemText m_xslTextElement;
+
+  /**
+   * Set the current setXslTextElement. The xsl:text 
+   * processor will call this to set a preserve space state.
+   *
+   * @param xslTextElement The current xslTextElement that 
+   *                       is preserving state, or null.
+   */
+  void setXslTextElement(ElemText xslTextElement)
+  {
+    m_xslTextElement = xslTextElement;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorDecimalFormat.java b/src/main/java/org/apache/xalan/processor/ProcessorDecimalFormat.java
new file mode 100644
index 0000000..43250a6
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorDecimalFormat.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorDecimalFormat.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.templates.DecimalFormatProperties;
+import org.xml.sax.Attributes;
+
+/**
+ * Process xsl:decimal-format by creating a DecimalFormatProperties 
+ * object and passing it to the stylesheet.
+ * 
+ * @see org.apache.xalan.templates.Stylesheet#setDecimalFormat
+ * @see org.apache.xalan.templates.DecimalFormatProperties
+ * @see <a href="http://www.w3.org/TR/xslt#format-number">format-number in XSLT Specification</a>
+ * @xsl.usage internal
+ */
+class ProcessorDecimalFormat extends XSLTElementProcessor
+{
+    static final long serialVersionUID = -5052904382662921627L;
+
+  /**
+   * Receive notification of the start of an element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   * @see org.apache.xalan.processor.StylesheetHandler#startElement
+   * @see org.apache.xalan.processor.StylesheetHandler#endElement
+   * @see org.xml.sax.ContentHandler#startElement
+   * @see org.xml.sax.ContentHandler#endElement
+   * @see org.xml.sax.Attributes
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+
+    DecimalFormatProperties dfp = new DecimalFormatProperties(handler.nextUid());
+    
+    dfp.setDOMBackPointer(handler.getOriginatingNode());
+    dfp.setLocaterInfo(handler.getLocator());
+    
+    setPropertiesFromAttributes(handler, rawName, attributes, dfp);
+    handler.getStylesheet().setDecimalFormat(dfp);
+    
+    handler.getStylesheet().appendChild(dfp);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorExsltFuncResult.java b/src/main/java/org/apache/xalan/processor/ProcessorExsltFuncResult.java
new file mode 100644
index 0000000..3a909fa
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorExsltFuncResult.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorExsltFuncResult.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.templates.ElemExsltFuncResult;
+import org.apache.xalan.templates.ElemExsltFunction;
+import org.apache.xalan.templates.ElemParam;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.ElemVariable;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * This class processes parse events for an exslt func:result element.
+ * @xsl.usage internal
+ */
+public class ProcessorExsltFuncResult extends ProcessorTemplateElem
+{
+    static final long serialVersionUID = 6451230911473482423L;
+  
+  /**
+   * Verify that the func:result element does not appear within a variable,
+   * parameter, or another func:result, and that it belongs to a func:function 
+   * element.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws SAXException
+  {
+    String msg = "";
+
+    super.startElement(handler, uri, localName, rawName, attributes);
+    ElemTemplateElement ancestor = handler.getElemTemplateElement().getParentElem();
+    while (ancestor != null && !(ancestor instanceof ElemExsltFunction))
+    {
+      if (ancestor instanceof ElemVariable 
+          || ancestor instanceof ElemParam
+          || ancestor instanceof ElemExsltFuncResult)
+      {
+        msg = "func:result cannot appear within a variable, parameter, or another func:result.";
+        handler.error(msg, new SAXException(msg));
+      }
+      ancestor = ancestor.getParentElem();
+    }
+    if (ancestor == null)
+    {
+      msg = "func:result must appear in a func:function element";
+      handler.error(msg, new SAXException(msg));
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorExsltFunction.java b/src/main/java/org/apache/xalan/processor/ProcessorExsltFunction.java
new file mode 100644
index 0000000..8eb4e41
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorExsltFunction.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorExsltFunction.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.templates.ElemApplyImport;
+import org.apache.xalan.templates.ElemApplyTemplates;
+import org.apache.xalan.templates.ElemAttribute;
+import org.apache.xalan.templates.ElemCallTemplate;
+import org.apache.xalan.templates.ElemComment;
+import org.apache.xalan.templates.ElemCopy;
+import org.apache.xalan.templates.ElemCopyOf;
+import org.apache.xalan.templates.ElemElement;
+import org.apache.xalan.templates.ElemExsltFuncResult;
+import org.apache.xalan.templates.ElemExsltFunction;
+import org.apache.xalan.templates.ElemFallback;
+import org.apache.xalan.templates.ElemLiteralResult;
+import org.apache.xalan.templates.ElemMessage;
+import org.apache.xalan.templates.ElemNumber;
+import org.apache.xalan.templates.ElemPI;
+import org.apache.xalan.templates.ElemParam;
+import org.apache.xalan.templates.ElemTemplate;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.ElemText;
+import org.apache.xalan.templates.ElemTextLiteral;
+import org.apache.xalan.templates.ElemValueOf;
+import org.apache.xalan.templates.ElemVariable;
+import org.apache.xalan.templates.Stylesheet;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+
+/**
+ * This class processes parse events for an exslt func:function element.
+ * @xsl.usage internal
+ */
+public class ProcessorExsltFunction extends ProcessorTemplateElem
+{
+    static final long serialVersionUID = 2411427965578315332L;
+  /**
+   * Start an ElemExsltFunction. Verify that it is top level and that it has a name attribute with a
+   * namespace.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws SAXException
+  {
+    //System.out.println("ProcessorFunction.startElement()");
+    String msg = "";
+    if (!(handler.getElemTemplateElement() instanceof Stylesheet))
+    {
+      msg = "func:function element must be top level.";
+      handler.error(msg, new SAXException(msg));
+    }
+    super.startElement(handler, uri, localName, rawName, attributes);
+       
+    String val = attributes.getValue("name");
+    int indexOfColon = val.indexOf(":");
+    if (indexOfColon > 0)
+    {
+      //String prefix = val.substring(0, indexOfColon);
+      //String localVal = val.substring(indexOfColon + 1);
+      //String ns = handler.getNamespaceSupport().getURI(prefix);
+      //if (ns.length() > 0)
+      //  System.out.println("fullfuncname " + ns + localVal);
+    }
+    else
+    {
+      msg = "func:function name must have namespace";
+      handler.error(msg, new SAXException(msg));
+    }
+  }
+  
+  /**
+   * Must include; super doesn't suffice!
+   */
+  protected void appendAndPush(
+          StylesheetHandler handler, ElemTemplateElement elem)
+            throws SAXException
+  {
+    //System.out.println("ProcessorFunction appendAndPush()" + elem);
+    super.appendAndPush(handler, elem);
+    //System.out.println("originating node " + handler.getOriginatingNode());
+    elem.setDOMBackPointer(handler.getOriginatingNode());
+    handler.getStylesheet().setTemplate((ElemTemplate) elem);
+  }
+    
+  /**
+   * End an ElemExsltFunction, and verify its validity.
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws SAXException
+  {
+   ElemTemplateElement function = handler.getElemTemplateElement();
+   validate(function, handler); // may throw exception
+   super.endElement(handler, uri, localName, rawName);   
+  }
+  
+  /**
+   * Non-recursive traversal of FunctionElement tree based on TreeWalker to verify that
+   * there are no literal result elements except within a func:result element and that
+   * the func:result element does not contain any following siblings except xsl:fallback.
+   */
+  public void validate(ElemTemplateElement elem, StylesheetHandler handler)
+    throws SAXException
+  {
+    String msg = "";
+    while (elem != null)
+    { 
+      //System.out.println("elem " + elem);
+      if (elem instanceof ElemExsltFuncResult 
+          && elem.getNextSiblingElem() != null 
+          && !(elem.getNextSiblingElem() instanceof ElemFallback))
+      {
+        msg = "func:result has an illegal following sibling (only xsl:fallback allowed)";
+        handler.error(msg, new SAXException(msg));
+      }
+      
+      if((elem instanceof ElemApplyImport
+	 || elem instanceof ElemApplyTemplates
+	 || elem instanceof ElemAttribute
+	 || elem instanceof ElemCallTemplate
+	 || elem instanceof ElemComment
+	 || elem instanceof ElemCopy
+	 || elem instanceof ElemCopyOf
+	 || elem instanceof ElemElement
+	 || elem instanceof ElemLiteralResult
+	 || elem instanceof ElemNumber
+	 || elem instanceof ElemPI
+	 || elem instanceof ElemText
+	 || elem instanceof ElemTextLiteral
+	 || elem instanceof ElemValueOf)
+	&& !(ancestorIsOk(elem)))
+      {
+        msg ="misplaced literal result in a func:function container.";
+        handler.error(msg, new SAXException(msg));
+      }
+      ElemTemplateElement nextElem = elem.getFirstChildElem();
+      while (nextElem == null)
+      {
+        nextElem = elem.getNextSiblingElem();
+        if (nextElem == null)
+          elem = elem.getParentElem();
+        if (elem == null || elem instanceof ElemExsltFunction)
+          return; // ok
+      }  
+      elem = nextElem;
+    }
+  }
+  
+  /**
+   * Verify that a literal result belongs to a result element, a variable, 
+   * or a parameter.
+   */
+  
+  boolean ancestorIsOk(ElemTemplateElement child)
+  {
+    while (child.getParentElem() != null && !(child.getParentElem() instanceof ElemExsltFunction))
+    {
+      ElemTemplateElement parent = child.getParentElem();
+      if (parent instanceof ElemExsltFuncResult 
+          || parent instanceof ElemVariable
+          || parent instanceof ElemParam
+          || parent instanceof ElemMessage)
+        return true;
+      child = parent;      
+    }
+    return false;
+  }
+  
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorGlobalParamDecl.java b/src/main/java/org/apache/xalan/processor/ProcessorGlobalParamDecl.java
new file mode 100644
index 0000000..3fa4cc3
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorGlobalParamDecl.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorGlobalParamDecl.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.templates.ElemParam;
+import org.apache.xalan.templates.ElemTemplateElement;
+
+/**
+ * This class processes parse events for an xsl:param element.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+ */
+class ProcessorGlobalParamDecl extends ProcessorTemplateElem
+{
+    static final long serialVersionUID = 1900450872353587350L;
+
+  /**
+   * Append the current template element to the current
+   * template element, and then push it onto the current template
+   * element stack.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param elem The non-null reference to the ElemParam element.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  protected void appendAndPush(
+          StylesheetHandler handler, ElemTemplateElement elem)
+            throws org.xml.sax.SAXException
+  {
+
+    // Just push, but don't append.
+    handler.pushElemTemplateElement(elem);
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * @param name The element type name.
+   * @param attributes The specified or defaulted attributes.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+
+    ElemParam v = (ElemParam) handler.getElemTemplateElement();
+
+    handler.getStylesheet().appendChild(v);
+    handler.getStylesheet().setParam(v);
+    super.endElement(handler, uri, localName, rawName);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorGlobalVariableDecl.java b/src/main/java/org/apache/xalan/processor/ProcessorGlobalVariableDecl.java
new file mode 100644
index 0000000..8caacbd
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorGlobalVariableDecl.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorGlobalVariableDecl.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.ElemVariable;
+
+/**
+ * This class processes parse events for an xsl:variable element.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+ */
+class ProcessorGlobalVariableDecl extends ProcessorTemplateElem
+{
+    static final long serialVersionUID = -5954332402269819582L;
+
+  /**
+   * Append the current template element to the current
+   * template element, and then push it onto the current template
+   * element stack.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param elem The non-null reference to the ElemVariable element.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  protected void appendAndPush(
+          StylesheetHandler handler, ElemTemplateElement elem)
+            throws org.xml.sax.SAXException
+  {
+
+    // Just push, but don't append.
+    handler.pushElemTemplateElement(elem);
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * @param name The element type name.
+   * @param attributes The specified or defaulted attributes.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+
+    ElemVariable v = (ElemVariable) handler.getElemTemplateElement();
+
+    handler.getStylesheet().appendChild(v);
+    handler.getStylesheet().setVariable(v);
+    super.endElement(handler, uri, localName, rawName);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorImport.java b/src/main/java/org/apache/xalan/processor/ProcessorImport.java
new file mode 100644
index 0000000..c044bcf
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorImport.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorImport.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.res.XSLTErrorResources;
+
+/**
+ * This class processes parse events for an xsl:import element.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#import">import in XSLT Specification</a>
+ * 
+ * @xsl.usage internal
+ */
+public class ProcessorImport extends ProcessorInclude
+{
+    static final long serialVersionUID = -8247537698214245237L;
+
+  /**
+   * Get the stylesheet type associated with an imported stylesheet
+   *
+   * @return the type of the stylesheet
+   */
+  protected int getStylesheetType()
+  {
+    return StylesheetHandler.STYPE_IMPORT;
+  }
+
+  /**
+   * Get the error number associated with this type of stylesheet importing itself
+   *
+   * @return the appropriate error number
+   */
+  protected String getStylesheetInclErr()
+  {
+    return XSLTErrorResources.ER_IMPORTING_ITSELF;
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorInclude.java b/src/main/java/org/apache/xalan/processor/ProcessorInclude.java
new file mode 100644
index 0000000..814a0f3
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorInclude.java
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorInclude.java 469349 2006-10-31 03:06:50Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.io.IOException;
+
+import javax.xml.XMLConstants;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.utils.SystemIDResolver;
+import org.apache.xml.utils.TreeWalker;
+
+import org.w3c.dom.Node;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * TransformerFactory class for xsl:include markup.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#include">include in XSLT Specification</a>
+ * 
+ * @xsl.usage internal
+ */
+public class ProcessorInclude extends XSLTElementProcessor
+{
+    static final long serialVersionUID = -4570078731972673481L;
+
+  /**
+   * The base URL of the XSL document.
+   * @serial
+   */
+  private String m_href = null;
+
+  /**
+   * Get the base identifier with which this stylesheet is associated.
+   *
+   * @return non-null reference to the href attribute string, or 
+   *         null if setHref has not been called.
+   */
+  public String getHref()
+  {
+    return m_href;
+  }
+
+  /**
+   * Get the base identifier with which this stylesheet is associated.
+   *
+   * @param baseIdent Should be a non-null reference to a valid URL string.
+   */
+  public void setHref(String baseIdent)
+  {
+    // Validate?
+    m_href = baseIdent;
+  }
+
+  /**
+   * Get the stylesheet type associated with an included stylesheet
+   *
+   * @return the type of the stylesheet
+   */
+  protected int getStylesheetType()
+  {
+    return StylesheetHandler.STYPE_INCLUDE;
+  }
+
+  /**
+   * Get the error number associated with this type of stylesheet including itself
+   *
+   * @return the appropriate error number
+   */
+  protected String getStylesheetInclErr()
+  {
+    return XSLTErrorResources.ER_STYLESHEET_INCLUDES_ITSELF;
+  }
+
+  /**
+   * Receive notification of the start of an xsl:include element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+
+
+    setPropertiesFromAttributes(handler, rawName, attributes, this);
+
+    try
+    {
+
+      // Get the Source from the user's URIResolver (if any).
+      Source sourceFromURIResolver = getSourceFromUriResolver(handler);
+      // Get the system ID of the included/imported stylesheet module
+      String hrefUrl = getBaseURIOfIncludedStylesheet(handler, sourceFromURIResolver);
+
+      if (handler.importStackContains(hrefUrl))
+      {
+        throw new org.xml.sax.SAXException(
+          XSLMessages.createMessage(
+          getStylesheetInclErr(), new Object[]{ hrefUrl }));  //"(StylesheetHandler) "+hrefUrl+" is directly or indirectly importing itself!");
+      }
+
+      // Push the system ID and corresponding Source
+      // on some stacks for later retrieval during parse() time.
+      handler.pushImportURL(hrefUrl);
+      handler.pushImportSource(sourceFromURIResolver);
+
+      int savedStylesheetType = handler.getStylesheetType();
+
+      handler.setStylesheetType(this.getStylesheetType());
+      handler.pushNewNamespaceSupport();
+
+      try
+      {
+        parse(handler, uri, localName, rawName, attributes);
+      }
+      finally
+      {
+        handler.setStylesheetType(savedStylesheetType);
+        handler.popImportURL();
+        handler.popImportSource();
+        handler.popNamespaceSupport();
+      }
+    }
+    catch(TransformerException te)
+    {
+      handler.error(te.getMessage(), te);
+    }
+  }
+
+  /**
+   * Set off a new parse for an included or imported stylesheet.  This will 
+   * set the {@link StylesheetHandler} to a new state, and recurse in with 
+   * a new set of parse events.  Once this function returns, the state of 
+   * the StylesheetHandler should be restored.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, which should be the XSLT namespace.
+   * @param localName The local name (without prefix), which should be "include" or "import".
+   * @param rawName The qualified name (with prefix).
+   * @param attributes The list of attributes on the xsl:include or xsl:import element.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  protected void parse(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+    TransformerFactoryImpl processor = handler.getStylesheetProcessor();
+    URIResolver uriresolver = processor.getURIResolver();
+
+    try
+    {
+      Source source = null;
+      
+      // The base identifier, an aboslute URI
+      // that is associated with the included/imported
+      // stylesheet module is known in this method,
+      // so this method does the pushing of the
+      // base ID onto the stack.
+     
+      if (null != uriresolver)
+      {
+        // There is a user provided URI resolver.
+        // At the startElement() call we would
+        // have tried to obtain a Source from it
+        // which we now retrieve
+        source = handler.peekSourceFromURIResolver();
+
+        if (null != source && source instanceof DOMSource)
+        {
+          Node node = ((DOMSource)source).getNode();
+          
+          // There is a user provided URI resolver.
+          // At the startElement() call we would
+          // have already pushed the system ID, obtained
+          // from either the source.getSystemId(), if non-null
+          // or from SystemIDResolver.getAbsoluteURI() as a backup
+          // which we now retrieve.
+          String systemId = handler.peekImportURL();
+          
+          // Push the absolute URI of the included/imported
+          // stylesheet module onto the stack.
+          if (systemId != null)
+              handler.pushBaseIndentifier(systemId);
+        
+          TreeWalker walker = new TreeWalker(handler, new org.apache.xml.utils.DOM2Helper(), systemId);
+
+          try
+          {
+            walker.traverse(node);
+          }
+          catch(org.xml.sax.SAXException se)
+          {
+            throw new TransformerException(se);
+          }
+          if (systemId != null)
+            handler.popBaseIndentifier();
+          return;
+        }
+      }
+      
+      if(null == source)
+      {
+        String absURL = SystemIDResolver.getAbsoluteURI(getHref(),
+                          handler.getBaseIdentifier());
+
+        source = new StreamSource(absURL);
+      }
+      
+      // possible callback to a class that over-rides this method.
+      source = processSource(handler, source);
+      
+      XMLReader reader = null;
+      
+      if(source instanceof SAXSource)
+      {
+        SAXSource saxSource = (SAXSource)source;
+        reader = saxSource.getXMLReader(); // may be null
+      }
+      
+      InputSource inputSource = SAXSource.sourceToInputSource(source);
+
+      if (null == reader)
+      {  
+        // Use JAXP1.1 ( if possible )
+        try {
+          javax.xml.parsers.SAXParserFactory factory=
+                                                     javax.xml.parsers.SAXParserFactory.newInstance();
+          factory.setNamespaceAware( true );
+          
+          if (handler.getStylesheetProcessor().isSecureProcessing())
+          {
+            try
+            {
+              factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            }
+            catch (org.xml.sax.SAXException se) {}
+          }
+          
+          javax.xml.parsers.SAXParser jaxpParser=
+                                                 factory.newSAXParser();
+          reader=jaxpParser.getXMLReader();
+          
+        } catch( javax.xml.parsers.ParserConfigurationException ex ) {
+          throw new org.xml.sax.SAXException( ex );
+        } catch( javax.xml.parsers.FactoryConfigurationError ex1 ) {
+            throw new org.xml.sax.SAXException( ex1.toString() );
+        } 
+        catch( NoSuchMethodError ex2 ) 
+        {
+        }
+        catch (AbstractMethodError ame){}
+      }
+      if (null == reader)
+        reader = XMLReaderFactory.createXMLReader();
+
+      if (null != reader)
+      {
+        reader.setContentHandler(handler);
+        
+        // Push the absolute URI of the included/imported
+        // stylesheet module onto the stack.
+        handler.pushBaseIndentifier(inputSource.getSystemId());
+
+        try
+        {
+          reader.parse(inputSource);
+        }
+        finally
+        {
+          handler.popBaseIndentifier();
+        }
+      }
+    }
+    catch (IOException ioe)
+    {
+      handler.error(XSLTErrorResources.ER_IOEXCEPTION,
+                    new Object[]{ getHref() }, ioe);
+    }
+    catch(TransformerException te)
+    {
+      handler.error(te.getMessage(), te);
+    }
+  }
+
+  /**
+   * This method does nothing, but a class that extends this class could
+   * over-ride it and do some processing of the source.
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param source The source of the included stylesheet.
+   * @return the same or an equivalent source to what was passed in.
+   */
+  protected Source processSource(StylesheetHandler handler, Source source)
+  {
+      return source;
+  }
+  
+  /**
+   * Get the Source object for the included or imported stylesheet module
+   * obtained from the user's URIResolver, if there is no user provided 
+   * URIResolver null is returned.
+   */
+  private Source getSourceFromUriResolver(StylesheetHandler handler)
+            throws TransformerException {
+        Source s = null;
+            TransformerFactoryImpl processor = handler.getStylesheetProcessor();
+            URIResolver uriresolver = processor.getURIResolver();
+            if (uriresolver != null) {
+                String href = getHref();
+                String base = handler.getBaseIdentifier();
+                s = uriresolver.resolve(href,base);
+            }
+
+        return s;
+    }
+
+    /**
+     * Get the base URI of the included or imported stylesheet,
+     * if the user provided a URIResolver, then get the Source
+     * object for the stylsheet from it, and get the systemId 
+     * from that Source object, otherwise try to recover by
+     * using the SysteIDResolver to figure out the base URI.
+     * @param handler The handler that processes the stylesheet as SAX events,
+     * and maintains state
+     * @param s The Source object from a URIResolver, for the included stylesheet module,
+     * so this will be null if there is no URIResolver set.
+     */
+    private String getBaseURIOfIncludedStylesheet(StylesheetHandler handler, Source s)
+            throws TransformerException {
+        
+
+        
+        String baseURI;
+        String idFromUriResolverSource;
+        if (s != null && (idFromUriResolverSource = s.getSystemId()) != null) {
+            // We have a Source obtained from a users's URIResolver,
+            // and the system ID is set on it, so return that as the base URI
+            baseURI = idFromUriResolverSource;
+        } else {
+            // The user did not provide a URIResolver, or it did not 
+            // return a Source for the included stylesheet module, or
+            // the Source has no system ID set, so we fall back to using
+            // the system ID Resolver to take the href and base
+            // to generate the baseURI of the included stylesheet.
+            baseURI = SystemIDResolver.getAbsoluteURI(getHref(), handler
+                    .getBaseIdentifier());
+        }
+
+        return baseURI;
+    }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorKey.java b/src/main/java/org/apache/xalan/processor/ProcessorKey.java
new file mode 100644
index 0000000..2e4cd2a
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorKey.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorKey.java 469688 2006-10-31 22:39:43Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.KeyDeclaration;
+import org.xml.sax.Attributes;
+
+/**
+ * TransformerFactory for xsl:key markup.
+ * <pre>
+ * <!ELEMENT xsl:key EMPTY>
+ * <!ATTLIST xsl:key
+ *   name %qname; #REQUIRED
+ *   match %pattern; #REQUIRED
+ *   use %expr; #REQUIRED
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a>
+ */
+class ProcessorKey extends XSLTElementProcessor
+{
+    static final long serialVersionUID = 4285205417566822979L;
+
+  /**
+   * Receive notification of the start of an xsl:key element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+
+    KeyDeclaration kd = new KeyDeclaration(handler.getStylesheet(), handler.nextUid());
+
+    kd.setDOMBackPointer(handler.getOriginatingNode());
+    kd.setLocaterInfo(handler.getLocator());
+    setPropertiesFromAttributes(handler, rawName, attributes, kd);
+    handler.getStylesheet().setKey(kd);
+  }
+
+  /**
+   * Set the properties of an object from the given attribute list.
+   * @param handler The stylesheet's Content handler, needed for
+   *                error reporting.
+   * @param rawName The raw name of the owner element, needed for
+   *                error reporting.
+   * @param attributes The list of attributes.
+   * @param target The target element where the properties will be set.
+   */
+  void setPropertiesFromAttributes(
+          StylesheetHandler handler, String rawName, Attributes attributes, 
+          org.apache.xalan.templates.ElemTemplateElement target)
+            throws org.xml.sax.SAXException
+  {
+
+    XSLTElementDef def = getElemDef();
+
+    // Keep track of which XSLTAttributeDefs have been processed, so 
+    // I can see which default values need to be set.
+    List processedDefs = new ArrayList();
+    int nAttrs = attributes.getLength();
+
+    for (int i = 0; i < nAttrs; i++)
+    {
+      String attrUri = attributes.getURI(i);
+      String attrLocalName = attributes.getLocalName(i);
+      XSLTAttributeDef attrDef = def.getAttributeDef(attrUri, attrLocalName);
+
+      if (null == attrDef)
+      {
+
+        // Then barf, because this element does not allow this attribute.
+        handler.error(attributes.getQName(i)
+                      + "attribute is not allowed on the " + rawName
+                      + " element!", null);
+      }
+      else
+      {
+        String valueString = attributes.getValue(i);
+
+        if (valueString.indexOf(org.apache.xpath.compiler.Keywords.FUNC_KEY_STRING
+                                + "(") >= 0)
+          handler.error(
+            XSLMessages.createMessage(
+            XSLTErrorResources.ER_INVALID_KEY_CALL, null), null);
+
+        processedDefs.add(attrDef);
+        attrDef.setAttrValue(handler, attrUri, attrLocalName,
+                             attributes.getQName(i), attributes.getValue(i),
+                             target);
+      }
+    }
+
+    XSLTAttributeDef[] attrDefs = def.getAttributes();
+    int nAttrDefs = attrDefs.length;
+
+    for (int i = 0; i < nAttrDefs; i++)
+    {
+      XSLTAttributeDef attrDef = attrDefs[i];
+      String defVal = attrDef.getDefault();
+
+      if (null != defVal)
+      {
+        if (!processedDefs.contains(attrDef))
+        {
+          attrDef.setDefAttrValue(handler, target);
+        }
+      }
+
+      if (attrDef.getRequired())
+      {
+        if (!processedDefs.contains(attrDef))
+          handler.error(
+            XSLMessages.createMessage(
+              XSLTErrorResources.ER_REQUIRES_ATTRIB, new Object[]{ rawName,
+                                                                   attrDef.getName() }), null);
+      }
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorLRE.java b/src/main/java/org/apache/xalan/processor/ProcessorLRE.java
new file mode 100644
index 0000000..baf9f34
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorLRE.java
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorLRE.java 475981 2006-11-16 23:35:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.List;
+
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.Constants;
+import org.apache.xalan.templates.ElemExtensionCall;
+import org.apache.xalan.templates.ElemLiteralResult;
+import org.apache.xalan.templates.ElemTemplate;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.Stylesheet;
+import org.apache.xalan.templates.StylesheetRoot;
+import org.apache.xalan.templates.XMLNSDecl;
+import org.apache.xml.utils.SAXSourceLocator;
+import org.apache.xpath.XPath;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Processes an XSLT literal-result-element, or something that looks 
+ * like one.  The actual {@link org.apache.xalan.templates.ElemTemplateElement}
+ * produced may be a {@link org.apache.xalan.templates.ElemLiteralResult}, 
+ * a {@link org.apache.xalan.templates.StylesheetRoot}, or a 
+ * {@link org.apache.xalan.templates.ElemExtensionCall}.
+ * 
+ * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+ * @see org.apache.xalan.templates.ElemLiteralResult
+ * @xsl.usage internal
+ */
+public class ProcessorLRE extends ProcessorTemplateElem
+{
+    static final long serialVersionUID = -1490218021772101404L;
+  /**
+   * Receive notification of the start of an element.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param attributes The specified or defaulted attributes.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      ElemTemplateElement p = handler.getElemTemplateElement();
+      boolean excludeXSLDecl = false;
+      boolean isLREAsStyleSheet = false;
+
+      if (null == p)
+      {
+
+        // Literal Result Template as stylesheet.
+        XSLTElementProcessor lreProcessor = handler.popProcessor();
+        XSLTElementProcessor stylesheetProcessor =
+                                                  handler.getProcessorFor(Constants.S_XSLNAMESPACEURL, "stylesheet",
+                                                                          "xsl:stylesheet");
+
+        handler.pushProcessor(lreProcessor);
+
+        Stylesheet stylesheet;
+        try
+        {
+          stylesheet = getStylesheetRoot(handler);
+        }
+        catch(TransformerConfigurationException tfe)
+        {
+          throw new TransformerException(tfe);
+        }
+
+        // stylesheet.setDOMBackPointer(handler.getOriginatingNode());
+        // ***** Note that we're assigning an empty locator. Is this necessary?
+        SAXSourceLocator slocator = new SAXSourceLocator();
+        Locator locator = handler.getLocator();
+        if(null != locator)
+        {
+          slocator.setLineNumber(locator.getLineNumber());
+          slocator.setColumnNumber(locator.getColumnNumber());
+          slocator.setPublicId(locator.getPublicId());
+          slocator.setSystemId(locator.getSystemId());
+        }
+        stylesheet.setLocaterInfo(slocator);
+        stylesheet.setPrefixes(handler.getNamespaceSupport());
+        handler.pushStylesheet(stylesheet);
+
+        isLREAsStyleSheet = true;
+
+        AttributesImpl stylesheetAttrs = new AttributesImpl();
+        AttributesImpl lreAttrs = new AttributesImpl();
+        int n = attributes.getLength();
+
+        for (int i = 0; i < n; i++)
+        {
+          String attrLocalName = attributes.getLocalName(i);
+          String attrUri = attributes.getURI(i);
+          String value = attributes.getValue(i);
+
+          if ((null != attrUri) && attrUri.equals(Constants.S_XSLNAMESPACEURL))
+          {
+            stylesheetAttrs.addAttribute(null, attrLocalName, attrLocalName,
+                                         attributes.getType(i),
+                                         attributes.getValue(i));
+          }
+          else if ((attrLocalName.startsWith("xmlns:") || attrLocalName.equals(
+                                                                               "xmlns")) && value.equals(Constants.S_XSLNAMESPACEURL))
+          {
+
+            // ignore
+          }
+          else
+          {
+            lreAttrs.addAttribute(attrUri, attrLocalName,
+                                  attributes.getQName(i),
+                                  attributes.getType(i),
+                                  attributes.getValue(i));
+          }
+        }
+
+        attributes = lreAttrs;
+
+        // Set properties from the attributes, but don't throw 
+        // an error if there is an attribute defined that is not 
+        // allowed on a stylesheet.
+				try{
+        stylesheetProcessor.setPropertiesFromAttributes(handler, "stylesheet",
+                                                        stylesheetAttrs, stylesheet);
+				}
+				catch (Exception e)
+				{
+					// This is pretty ugly, but it will have to do for now. 
+					// This is just trying to append some text specifying that
+					// this error came from a missing or invalid XSLT namespace
+					// declaration.
+					// If someone comes up with a better solution, please feel 
+					// free to contribute it. -mm
+         
+					if (stylesheet.getDeclaredPrefixes() == null || 
+						!declaredXSLNS(stylesheet))
+					{
+						throw new org.xml.sax.SAXException(XSLMessages.createWarning(XSLTErrorResources.WG_OLD_XSLT_NS, null));
+					}
+					else
+                    {
+						throw new org.xml.sax.SAXException(e);
+                    }
+				}
+        handler.pushElemTemplateElement(stylesheet);
+
+        ElemTemplate template = new ElemTemplate();
+        if (slocator != null)
+            template.setLocaterInfo(slocator);
+
+        appendAndPush(handler, template);
+
+        XPath rootMatch = new XPath("/", stylesheet, stylesheet, XPath.MATCH, 
+             handler.getStylesheetProcessor().getErrorListener());
+
+        template.setMatch(rootMatch);
+
+        // template.setDOMBackPointer(handler.getOriginatingNode());
+        stylesheet.setTemplate(template);
+
+        p = handler.getElemTemplateElement();
+        excludeXSLDecl = true;
+      }
+
+      XSLTElementDef def = getElemDef();
+      Class classObject = def.getClassObject();
+      boolean isExtension = false;
+      boolean isComponentDecl = false;
+      boolean isUnknownTopLevel = false;
+
+      while (null != p)
+      {
+
+        // System.out.println("Checking: "+p);
+        if (p instanceof ElemLiteralResult)
+        {
+          ElemLiteralResult parentElem = (ElemLiteralResult) p;
+
+          isExtension = parentElem.containsExtensionElementURI(uri);
+        }
+        else if (p instanceof Stylesheet)
+        {
+          Stylesheet parentElem = (Stylesheet) p;
+
+          isExtension = parentElem.containsExtensionElementURI(uri);
+
+          if ((false == isExtension) && (null != uri)
+              && (uri.equals(Constants.S_BUILTIN_EXTENSIONS_URL)
+                  || uri.equals(Constants.S_BUILTIN_OLD_EXTENSIONS_URL)))
+          {
+            isComponentDecl = true;
+          }
+          else
+          {
+            isUnknownTopLevel = true;
+          }
+        }
+
+        if (isExtension)
+          break;
+
+        p = p.getParentElem();
+      }
+
+      ElemTemplateElement elem = null;
+
+      try
+      {
+        if (isExtension)
+        {
+
+          // System.out.println("Creating extension(1): "+uri);
+          elem = new ElemExtensionCall();
+        }
+        else if (isComponentDecl)
+        {
+          elem = (ElemTemplateElement) classObject.newInstance();
+        }
+        else if (isUnknownTopLevel)
+        {
+
+          // TBD: Investigate, not sure about this.  -sb
+          elem = (ElemTemplateElement) classObject.newInstance();
+        }
+        else
+        {
+          elem = (ElemTemplateElement) classObject.newInstance();
+        }
+
+        elem.setDOMBackPointer(handler.getOriginatingNode());
+        elem.setLocaterInfo(handler.getLocator());
+        elem.setPrefixes(handler.getNamespaceSupport(), excludeXSLDecl);
+
+        if (elem instanceof ElemLiteralResult)
+        {
+          ((ElemLiteralResult) elem).setNamespace(uri);
+          ((ElemLiteralResult) elem).setLocalName(localName);
+          ((ElemLiteralResult) elem).setRawName(rawName);
+          ((ElemLiteralResult) elem).setIsLiteralResultAsStylesheet(
+                                                                    isLREAsStyleSheet);
+        }
+      }
+      catch (InstantiationException ie)
+      {
+        handler.error(XSLTErrorResources.ER_FAILED_CREATING_ELEMLITRSLT, null, ie);//"Failed creating ElemLiteralResult instance!", ie);
+      }
+      catch (IllegalAccessException iae)
+      {
+        handler.error(XSLTErrorResources.ER_FAILED_CREATING_ELEMLITRSLT, null, iae);//"Failed creating ElemLiteralResult instance!", iae);
+      }
+
+      setPropertiesFromAttributes(handler, rawName, attributes, elem);
+
+      // bit of a hack here...
+      if (!isExtension && (elem instanceof ElemLiteralResult))
+      {
+        isExtension =
+                     ((ElemLiteralResult) elem).containsExtensionElementURI(uri);
+
+        if (isExtension)
+        {
+
+          // System.out.println("Creating extension(2): "+uri);
+          elem = new ElemExtensionCall();
+
+          elem.setLocaterInfo(handler.getLocator());
+          elem.setPrefixes(handler.getNamespaceSupport());
+          ((ElemLiteralResult) elem).setNamespace(uri);
+          ((ElemLiteralResult) elem).setLocalName(localName);
+          ((ElemLiteralResult) elem).setRawName(rawName);
+          setPropertiesFromAttributes(handler, rawName, attributes, elem);
+        }
+      }
+
+      appendAndPush(handler, elem);
+    }
+    catch(TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * This method could be over-ridden by a class that extends this class.
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @return an object that represents the stylesheet element.
+   */
+  protected Stylesheet getStylesheetRoot(StylesheetHandler handler) throws TransformerConfigurationException
+  {
+    StylesheetRoot stylesheet;
+    stylesheet = new StylesheetRoot(handler.getSchema(), handler.getStylesheetProcessor().getErrorListener());
+    if (handler.getStylesheetProcessor().isSecureProcessing())
+      stylesheet.setSecureProcessing(true);
+    
+    return stylesheet;
+  }
+  
+
+/**
+   * Receive notification of the end of an element.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+
+    ElemTemplateElement elem = handler.getElemTemplateElement();
+
+    if (elem instanceof ElemLiteralResult)
+    {
+      if (((ElemLiteralResult) elem).getIsLiteralResultAsStylesheet())
+      {
+        handler.popStylesheet();
+      }
+    }
+
+    super.endElement(handler, uri, localName, rawName);
+  }
+	
+	private boolean declaredXSLNS(Stylesheet stylesheet)
+	{
+		List declaredPrefixes = stylesheet.getDeclaredPrefixes();
+		int n = declaredPrefixes.size();
+
+		for (int i = 0; i < n; i++)
+		{
+			XMLNSDecl decl = (XMLNSDecl) declaredPrefixes.get(i);
+			if(decl.getURI().equals(Constants.S_XSLNAMESPACEURL))
+				return true;
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorNamespaceAlias.java b/src/main/java/org/apache/xalan/processor/ProcessorNamespaceAlias.java
new file mode 100644
index 0000000..5ba7d19
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorNamespaceAlias.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorNamespaceAlias.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.NamespaceAlias;
+import org.xml.sax.Attributes;
+
+/**
+ * TransformerFactory for xsl:namespace-alias markup.
+ * A stylesheet can use the xsl:namespace-alias element to
+ * declare that one namespace URI is an alias for another namespace URI.
+ * <pre>
+ * <!ELEMENT xsl:namespace-alias EMPTY>
+ * <!ATTLIST xsl:namespace-alias
+ *   stylesheet-prefix CDATA #REQUIRED
+ *   result-prefix CDATA #REQUIRED
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+ */
+class ProcessorNamespaceAlias extends XSLTElementProcessor
+{
+    static final long serialVersionUID = -6309867839007018964L;
+
+  /**
+   * Receive notification of the start of an xsl:namespace-alias element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+    final String resultNS;
+    NamespaceAlias na = new NamespaceAlias(handler.nextUid());
+
+    setPropertiesFromAttributes(handler, rawName, attributes, na);
+    String prefix = na.getStylesheetPrefix();
+    if(prefix.equals("#default"))
+    {
+      prefix = "";
+      na.setStylesheetPrefix(prefix);
+    }
+    String stylesheetNS = handler.getNamespaceForPrefix(prefix);
+    na.setStylesheetNamespace(stylesheetNS);
+    prefix = na.getResultPrefix();
+    if(prefix.equals("#default"))
+    {
+      prefix = "";
+      na.setResultPrefix(prefix);
+      resultNS = handler.getNamespaceForPrefix(prefix);
+      if(null == resultNS)
+        handler.error(XSLTErrorResources.ER_INVALID_NAMESPACE_URI_VALUE_FOR_RESULT_PREFIX_FOR_DEFAULT, null, null);
+    }
+    else
+    {
+        resultNS = handler.getNamespaceForPrefix(prefix);
+        if(null == resultNS)
+         handler.error(XSLTErrorResources.ER_INVALID_NAMESPACE_URI_VALUE_FOR_RESULT_PREFIX, new Object[] {prefix}, null);
+    }
+   
+    na.setResultNamespace(resultNS);
+    handler.getStylesheet().setNamespaceAlias(na);
+    handler.getStylesheet().appendChild(na);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorOutputElem.java b/src/main/java/org/apache/xalan/processor/ProcessorOutputElem.java
new file mode 100644
index 0000000..cb07b43
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorOutputElem.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorOutputElem.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.OutputProperties;
+import org.apache.xml.serializer.OutputPropertiesFactory;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.SystemIDResolver;
+import org.xml.sax.Attributes;
+
+/**
+ * TransformerFactory for xsl:output markup.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT Specification</a>
+ */
+class ProcessorOutputElem extends XSLTElementProcessor
+{
+    static final long serialVersionUID = 3513742319582547590L;
+
+  /** The output properties, set temporarily while the properties are 
+   *  being set from the attributes, and then nulled after that operation 
+   *  is completed.  */
+  private OutputProperties m_outputProperties;
+
+  /**
+   * Set the cdata-section-elements property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#CDATA_SECTION_ELEMENTS
+   * @param newValue non-null reference to processed attribute value.
+   */
+  public void setCdataSectionElements(java.util.Vector newValue)
+  {
+    m_outputProperties.setQNameProperties(OutputKeys.CDATA_SECTION_ELEMENTS, newValue);
+  }
+
+  /**
+   * Set the doctype-public property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#DOCTYPE_PUBLIC
+   * @param newValue non-null reference to processed attribute value.
+   */
+  public void setDoctypePublic(String newValue)
+  {
+    m_outputProperties.setProperty(OutputKeys.DOCTYPE_PUBLIC, newValue);
+  }
+
+  /**
+   * Set the doctype-system property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#DOCTYPE_SYSTEM
+   * @param newValue non-null reference to processed attribute value.
+   */
+  public void setDoctypeSystem(String newValue)
+  {
+    m_outputProperties.setProperty(OutputKeys.DOCTYPE_SYSTEM, newValue);
+  }
+
+  /**
+   * Set the encoding property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#ENCODING
+   * @param newValue non-null reference to processed attribute value.
+   */
+  public void setEncoding(String newValue)
+  {
+    m_outputProperties.setProperty(OutputKeys.ENCODING, newValue);
+  }
+
+  /**
+   * Set the indent property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#INDENT
+   * @param newValue non-null reference to processed attribute value.
+   */
+  public void setIndent(boolean newValue)
+  {
+    m_outputProperties.setBooleanProperty(OutputKeys.INDENT, newValue);
+  }
+
+  /**
+   * Set the media type property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#MEDIA_TYPE
+   * @param newValue non-null reference to processed attribute value.
+   */
+  public void setMediaType(String newValue)
+  {
+    m_outputProperties.setProperty(OutputKeys.MEDIA_TYPE, newValue);
+  }
+
+  /**
+   * Set the method property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#METHOD
+   * @param newValue non-null reference to processed attribute value.
+   */
+  public void setMethod(org.apache.xml.utils.QName newValue)
+  {
+    m_outputProperties.setQNameProperty(OutputKeys.METHOD, newValue);
+  }
+
+  /**
+   * Set the omit-xml-declaration property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#OMIT_XML_DECLARATION
+   * @param newValue processed attribute value.
+   */
+  public void setOmitXmlDeclaration(boolean newValue)
+  {
+    m_outputProperties.setBooleanProperty(OutputKeys.OMIT_XML_DECLARATION, newValue);
+  }
+
+  /**
+   * Set the standalone property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#STANDALONE
+   * @param newValue processed attribute value.
+   */
+  public void setStandalone(boolean newValue)
+  {
+    m_outputProperties.setBooleanProperty(OutputKeys.STANDALONE, newValue);
+  }
+
+  /**
+   * Set the version property from the attribute value.
+   * @see javax.xml.transform.OutputKeys#VERSION
+   * @param newValue non-null reference to processed attribute value.
+   */
+  public void setVersion(String newValue)
+  {
+    m_outputProperties.setProperty(OutputKeys.VERSION, newValue);
+  }
+  
+  /**
+   * Set a foreign property from the attribute value.
+   * @param newValue non-null reference to attribute value.
+   */
+  public void setForeignAttr(String attrUri, String attrLocalName, String attrRawName, String attrValue)
+  {
+    QName key = new QName(attrUri, attrLocalName);
+    m_outputProperties.setProperty(key, attrValue);
+  }
+  
+  /**
+   * Set a foreign property from the attribute value.
+   * @param newValue non-null reference to attribute value.
+   */
+  public void addLiteralResultAttribute(String attrUri, String attrLocalName, String attrRawName, String attrValue)
+  {
+    QName key = new QName(attrUri, attrLocalName);
+    m_outputProperties.setProperty(key, attrValue);
+  }
+
+  /**
+   * Receive notification of the start of an xsl:output element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+    // Hmmm... for the moment I don't think I'll have default properties set for this. -sb
+    m_outputProperties = new OutputProperties();
+
+    m_outputProperties.setDOMBackPointer(handler.getOriginatingNode());
+    m_outputProperties.setLocaterInfo(handler.getLocator());
+    m_outputProperties.setUid(handler.nextUid());
+    setPropertiesFromAttributes(handler, rawName, attributes, this);
+    
+    // Access this only from the Hashtable level... we don't want to 
+    // get default properties.
+    String entitiesFileName =
+      (String) m_outputProperties.getProperties().get(OutputPropertiesFactory.S_KEY_ENTITIES);
+
+    if (null != entitiesFileName)
+    {
+      try
+      {
+        String absURL = SystemIDResolver.getAbsoluteURI(entitiesFileName,
+                    handler.getBaseIdentifier());
+        m_outputProperties.getProperties().put(OutputPropertiesFactory.S_KEY_ENTITIES, absURL);
+      }
+      catch(TransformerException te)
+      {
+        handler.error(te.getMessage(), te);
+      }
+    }
+    
+    handler.getStylesheet().setOutput(m_outputProperties);
+    
+    ElemTemplateElement parent = handler.getElemTemplateElement();
+    parent.appendChild(m_outputProperties);
+    
+    m_outputProperties = null;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorPreserveSpace.java b/src/main/java/org/apache/xalan/processor/ProcessorPreserveSpace.java
new file mode 100644
index 0000000..ba2e443
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorPreserveSpace.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorPreserveSpace.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.Vector;
+
+import org.apache.xalan.templates.Stylesheet;
+import org.apache.xalan.templates.WhiteSpaceInfo;
+import org.apache.xpath.XPath;
+
+import org.xml.sax.Attributes;
+
+/**
+ * TransformerFactory for xsl:preserve-space markup.
+ * <pre>
+ * <!ELEMENT xsl:preserve-space EMPTY>
+ * <!ATTLIST xsl:preserve-space elements CDATA #REQUIRED>
+ * </pre>
+ */
+class ProcessorPreserveSpace extends XSLTElementProcessor
+{
+    static final long serialVersionUID = -5552836470051177302L;
+
+  /**
+   * Receive notification of the start of an preserve-space element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, 
+          Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+    Stylesheet thisSheet = handler.getStylesheet();
+	WhitespaceInfoPaths paths = new WhitespaceInfoPaths(thisSheet);
+    setPropertiesFromAttributes(handler, rawName, attributes, paths);
+
+    Vector xpaths = paths.getElements();
+
+    for (int i = 0; i < xpaths.size(); i++)
+    {
+      WhiteSpaceInfo wsi = new WhiteSpaceInfo((XPath) xpaths.elementAt(i), false, thisSheet);
+      wsi.setUid(handler.nextUid());
+
+      thisSheet.setPreserveSpaces(wsi);
+    }
+    paths.clearElements();
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorStripSpace.java b/src/main/java/org/apache/xalan/processor/ProcessorStripSpace.java
new file mode 100644
index 0000000..1a6d5d7
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorStripSpace.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorStripSpace.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.Vector;
+
+import org.apache.xalan.templates.Stylesheet;
+import org.apache.xalan.templates.WhiteSpaceInfo;
+import org.apache.xpath.XPath;
+
+import org.xml.sax.Attributes;
+
+/**
+ * TransformerFactory for xsl:strip-space markup.
+ * <pre>
+ * <!ELEMENT xsl:strip-space EMPTY>
+ * <!ATTLIST xsl:strip-space elements CDATA #REQUIRED>
+ * </pre>
+ */
+class ProcessorStripSpace extends ProcessorPreserveSpace
+{
+    static final long serialVersionUID = -5594493198637899591L;
+
+  /**
+   * Receive notification of the start of an strip-space element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+    Stylesheet thisSheet = handler.getStylesheet();
+	WhitespaceInfoPaths paths = new WhitespaceInfoPaths(thisSheet);
+    setPropertiesFromAttributes(handler, rawName, attributes, paths);
+
+    Vector xpaths = paths.getElements();
+
+    for (int i = 0; i < xpaths.size(); i++)
+    {
+      WhiteSpaceInfo wsi = new WhiteSpaceInfo((XPath) xpaths.elementAt(i), true, thisSheet);
+      wsi.setUid(handler.nextUid());
+
+      thisSheet.setStripSpaces(wsi);
+    }
+    paths.clearElements();
+
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorStylesheetDoc.java b/src/main/java/org/apache/xalan/processor/ProcessorStylesheetDoc.java
new file mode 100644
index 0000000..169b1d3
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorStylesheetDoc.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorStylesheetDoc.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+/**
+ * This class processes the xsl:stylesheet element.  At the 
+ * moment, it defers all methods to it's superclass.
+ * @xsl.usage internal
+ */
+public class ProcessorStylesheetDoc extends XSLTElementProcessor
+{
+    static final long serialVersionUID = -1661497592836231844L;
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorStylesheetElement.java b/src/main/java/org/apache/xalan/processor/ProcessorStylesheetElement.java
new file mode 100644
index 0000000..9314ebd
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorStylesheetElement.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorStylesheetElement.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.templates.Stylesheet;
+import org.apache.xalan.templates.StylesheetComposed;
+import org.apache.xalan.templates.StylesheetRoot;
+
+import org.xml.sax.Attributes;
+
+/**
+ * TransformerFactory for xsl:stylesheet or xsl:transform markup.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#stylesheet-element">stylesheet-element in XSLT Specification</a>
+ * 
+ * @xsl.usage internal
+ */
+public class ProcessorStylesheetElement extends XSLTElementProcessor
+{
+    static final long serialVersionUID = -877798927447840792L;
+
+  /**
+   * Receive notification of the start of an strip-space element.
+   *
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param rawName The raw XML 1.0 name (with prefix), or the
+   *        empty string if raw names are not available.
+   * @param attributes The attributes attached to the element.  If
+   *        there are no attributes, it shall be an empty
+   *        Attributes object.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+
+		super.startElement(handler, uri, localName, rawName, attributes);
+    try
+    {
+      int stylesheetType = handler.getStylesheetType();
+      Stylesheet stylesheet;
+
+      if (stylesheetType == StylesheetHandler.STYPE_ROOT)
+      {
+        try
+        {
+          stylesheet = getStylesheetRoot(handler);
+        }
+        catch(TransformerConfigurationException tfe)
+        {
+          throw new TransformerException(tfe);
+        }
+      }
+      else
+      {
+        Stylesheet parent = handler.getStylesheet();
+
+        if (stylesheetType == StylesheetHandler.STYPE_IMPORT)
+        {
+          StylesheetComposed sc = new StylesheetComposed(parent);
+
+          parent.setImport(sc);
+
+          stylesheet = sc;
+        }
+        else
+        {
+          stylesheet = new Stylesheet(parent);
+
+          parent.setInclude(stylesheet);
+        }
+      }
+
+      stylesheet.setDOMBackPointer(handler.getOriginatingNode());
+      stylesheet.setLocaterInfo(handler.getLocator());
+
+      stylesheet.setPrefixes(handler.getNamespaceSupport());
+      handler.pushStylesheet(stylesheet);
+      setPropertiesFromAttributes(handler, rawName, attributes,
+                                  handler.getStylesheet());
+      handler.pushElemTemplateElement(handler.getStylesheet());
+    }
+    catch(TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * This method can be over-ridden by a class that extends this one.
+   * @param handler The calling StylesheetHandler/TemplatesBuilder.
+   */
+  protected Stylesheet getStylesheetRoot(StylesheetHandler handler) throws TransformerConfigurationException
+  {
+    StylesheetRoot stylesheet;
+    stylesheet = new StylesheetRoot(handler.getSchema(), handler.getStylesheetProcessor().getErrorListener());
+    
+    if (handler.getStylesheetProcessor().isSecureProcessing())
+      stylesheet.setSecureProcessing(true);
+    
+    return stylesheet;
+  }
+
+/**
+   * Receive notification of the end of an element.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+		super.endElement(handler, uri, localName, rawName);
+    handler.popElemTemplateElement();
+    handler.popStylesheet();
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorTemplate.java b/src/main/java/org/apache/xalan/processor/ProcessorTemplate.java
new file mode 100644
index 0000000..cd11ecd
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorTemplate.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorTemplate.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.templates.ElemTemplate;
+import org.apache.xalan.templates.ElemTemplateElement;
+
+/**
+ * TransformerFactory for xsl:template markup.
+ */
+class ProcessorTemplate extends ProcessorTemplateElem
+{
+    static final long serialVersionUID = -8457812845473603860L;
+  
+  /**
+   * Append the current template element to the current
+   * template element, and then push it onto the current template
+   * element stack.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param elem Must be a non-null reference to a {@link org.apache.xalan.templates.ElemTemplate} object.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  protected void appendAndPush(
+          StylesheetHandler handler, ElemTemplateElement elem)
+            throws org.xml.sax.SAXException
+  {
+
+    super.appendAndPush(handler, elem);
+    elem.setDOMBackPointer(handler.getOriginatingNode());
+    handler.getStylesheet().setTemplate((ElemTemplate) elem);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorTemplateElem.java b/src/main/java/org/apache/xalan/processor/ProcessorTemplateElem.java
new file mode 100644
index 0000000..d3331bd
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorTemplateElem.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorTemplateElem.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.ElemTemplateElement;
+
+import org.xml.sax.Attributes;
+
+/**
+ * This class processes parse events for an XSLT template element.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Creating-the-Result-Tree">section-Creating-the-Result-Tree in XSLT Specification</a>
+ */
+public class ProcessorTemplateElem extends XSLTElementProcessor
+{
+    static final long serialVersionUID = 8344994001943407235L;
+
+  /**
+   * Receive notification of the start of an element.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param attributes The specified or defaulted attributes.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+
+    super.startElement(handler, uri, localName, rawName, attributes);
+    try
+    {
+      // ElemTemplateElement parent = handler.getElemTemplateElement();
+      XSLTElementDef def = getElemDef();
+      Class classObject = def.getClassObject();
+      ElemTemplateElement elem = null;
+
+      try
+      {
+        elem = (ElemTemplateElement) classObject.newInstance();
+
+        elem.setDOMBackPointer(handler.getOriginatingNode());
+        elem.setLocaterInfo(handler.getLocator());
+        elem.setPrefixes(handler.getNamespaceSupport());
+      }
+      catch (InstantiationException ie)
+      {
+        handler.error(XSLTErrorResources.ER_FAILED_CREATING_ELEMTMPL, null, ie);//"Failed creating ElemTemplateElement instance!", ie);
+      }
+      catch (IllegalAccessException iae)
+      {
+        handler.error(XSLTErrorResources.ER_FAILED_CREATING_ELEMTMPL, null, iae);//"Failed creating ElemTemplateElement instance!", iae);
+      }
+
+      setPropertiesFromAttributes(handler, rawName, attributes, elem);
+      appendAndPush(handler, elem);
+    }
+    catch(TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * Append the current template element to the current
+   * template element, and then push it onto the current template
+   * element stack.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param elem non-null reference to a the current template element.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  protected void appendAndPush(
+          StylesheetHandler handler, ElemTemplateElement elem)
+            throws org.xml.sax.SAXException
+  {
+
+    ElemTemplateElement parent = handler.getElemTemplateElement();
+    if(null != parent)  // defensive, for better multiple error reporting. -sb
+    {
+      parent.appendChild(elem);
+      handler.pushElemTemplateElement(elem);
+    }
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+    super.endElement(handler, uri, localName, rawName);
+    handler.popElemTemplateElement().setEndLocaterInfo(handler.getLocator());
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorText.java b/src/main/java/org/apache/xalan/processor/ProcessorText.java
new file mode 100644
index 0000000..5111a41
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorText.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorText.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.ElemText;
+
+/**
+ * Process xsl:text.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#element-text">element-text in XSLT Specification</a>
+ */
+public class ProcessorText extends ProcessorTemplateElem
+{
+    static final long serialVersionUID = 5170229307201307523L;
+
+  /**
+   * Append the current template element to the current
+   * template element, and then push it onto the current template
+   * element stack.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param elem non-null reference to a {@link org.apache.xalan.templates.ElemText}.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  protected void appendAndPush(
+          StylesheetHandler handler, ElemTemplateElement elem)
+            throws org.xml.sax.SAXException
+  {
+
+    // Don't push this element onto the element stack.
+    ProcessorCharacters charProcessor =
+      (ProcessorCharacters) handler.getProcessorFor(null, "text()", "text");
+
+    charProcessor.setXslTextElement((ElemText) elem);
+
+    ElemTemplateElement parent = handler.getElemTemplateElement();
+
+    parent.appendChild(elem);
+    elem.setDOMBackPointer(handler.getOriginatingNode());
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+
+    ProcessorCharacters charProcessor 
+      = (ProcessorCharacters) handler.getProcessorFor(null, "text()", "text");
+
+    charProcessor.setXslTextElement(null);
+
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/ProcessorUnknown.java b/src/main/java/org/apache/xalan/processor/ProcessorUnknown.java
new file mode 100644
index 0000000..8aed015
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/ProcessorUnknown.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ProcessorUnknown.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import org.xml.sax.Attributes;
+
+/**
+ * This class processes an unknown template element.  It is used both 
+ * for unknown top-level elements, and for elements in the 
+ * xslt namespace when the version is higher than the version 
+ * of XSLT that we are set up to process.
+ * @xsl.usage internal
+ */
+public class ProcessorUnknown extends ProcessorLRE
+{
+    static final long serialVersionUID = 600521151487682248L;
+
+}
diff --git a/src/main/java/org/apache/xalan/processor/StylesheetHandler.java b/src/main/java/org/apache/xalan/processor/StylesheetHandler.java
new file mode 100644
index 0000000..dba47ac
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/StylesheetHandler.java
@@ -0,0 +1,1727 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StylesheetHandler.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.Stack;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.TemplatesHandler;
+
+import org.apache.xalan.extensions.ExpressionVisitor;
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.Constants;
+import org.apache.xalan.templates.ElemForEach;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.Stylesheet;
+import org.apache.xalan.templates.StylesheetRoot;
+import org.apache.xml.utils.BoolStack;
+import org.apache.xml.utils.NamespaceSupport2;
+import org.apache.xml.utils.NodeConsumer;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.SAXSourceLocator;
+import org.apache.xml.utils.XMLCharacterRecognizer;
+import org.apache.xpath.XPath;
+import org.apache.xpath.compiler.FunctionTable;
+import org.apache.xpath.functions.Function;
+
+import org.w3c.dom.Node;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.NamespaceSupport;
+
+/**
+ * Initializes and processes a stylesheet via SAX events.
+ * This class acts as essentially a state machine, maintaining
+ * a ContentHandler stack, and pushing appropriate content
+ * handlers as parse events occur.
+ * @xsl.usage advanced
+ */
+public class StylesheetHandler extends DefaultHandler
+        implements TemplatesHandler, PrefixResolver, NodeConsumer
+{
+
+
+  /**
+   * The function table of XPath and XSLT;
+   */
+  private FunctionTable m_funcTable = new FunctionTable();
+  
+  /**
+   * The flag for the setting of the optimize feature;
+   */
+  private boolean m_optimize = true;
+  
+  /**
+   * The flag for the setting of the incremental feature;
+   */
+  private boolean m_incremental = false;
+  
+  /**
+   * The flag for the setting of the source_location feature;
+   */
+  private boolean m_source_location = false;
+  
+  /**
+   * Create a StylesheetHandler object, creating a root stylesheet
+   * as the target.
+   *
+   * @param processor non-null reference to the transformer factory that owns this handler.
+   *
+   * @throws TransformerConfigurationException if a StylesheetRoot
+   * can not be constructed for some reason.
+   */
+  public StylesheetHandler(TransformerFactoryImpl processor)
+          throws TransformerConfigurationException
+  {
+    Class func = org.apache.xalan.templates.FuncDocument.class;
+    m_funcTable.installFunction("document", func);
+
+    // func = new org.apache.xalan.templates.FuncKey();
+    // FunctionTable.installFunction("key", func);
+    func = org.apache.xalan.templates.FuncFormatNumb.class;
+
+    m_funcTable.installFunction("format-number", func);
+
+    m_optimize =((Boolean) processor.getAttribute(
+            TransformerFactoryImpl.FEATURE_OPTIMIZE)).booleanValue();
+    m_incremental = ((Boolean) processor.getAttribute(
+            TransformerFactoryImpl.FEATURE_INCREMENTAL)).booleanValue();
+    m_source_location = ((Boolean) processor.getAttribute(
+            TransformerFactoryImpl.FEATURE_SOURCE_LOCATION)).booleanValue();
+    // m_schema = new XSLTSchema();
+    init(processor);
+    
+  }
+
+  /**
+   * Do common initialization.
+   *
+   * @param processor non-null reference to the transformer factory that owns this handler.
+   */
+  void init(TransformerFactoryImpl processor)
+  {
+    m_stylesheetProcessor = processor;
+
+    // Set the initial content handler.
+    m_processors.push(m_schema.getElementProcessor());
+    this.pushNewNamespaceSupport();
+
+    // m_includeStack.push(SystemIDResolver.getAbsoluteURI(this.getBaseIdentifier(), null));
+    // initXPath(processor, null);
+  }
+
+  /**
+   * Process an expression string into an XPath.
+   * Must be public for access by the AVT class.
+   *
+   * @param str A non-null reference to a valid or invalid XPath expression string.
+   *
+   * @return A non-null reference to an XPath object that represents the string argument.
+   *
+   * @throws javax.xml.transform.TransformerException if the expression can not be processed.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Expressions">Section 4 Expressions in XSLT Specification</a>
+   */
+  public XPath createXPath(String str, ElemTemplateElement owningTemplate)
+          throws javax.xml.transform.TransformerException
+  {
+    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
+    XPath xpath = new XPath(str, owningTemplate, this, XPath.SELECT, handler, 
+            m_funcTable);
+    // Visit the expression, registering namespaces for any extension functions it includes.
+    xpath.callVisitors(xpath, new ExpressionVisitor(getStylesheetRoot()));
+    return xpath;
+  }
+
+  /**
+   * Process an expression string into an XPath.
+   *
+   * @param str A non-null reference to a valid or invalid match pattern string.
+   *
+   * @return A non-null reference to an XPath object that represents the string argument.
+   *
+   * @throws javax.xml.transform.TransformerException if the pattern can not be processed.
+   * @see <a href="http://www.w3.org/TR/xslt#patterns">Section 5.2 Patterns in XSLT Specification</a>
+   */
+  XPath createMatchPatternXPath(String str, ElemTemplateElement owningTemplate)
+          throws javax.xml.transform.TransformerException
+  {
+    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
+    XPath xpath = new XPath(str, owningTemplate, this, XPath.MATCH, handler, 
+        m_funcTable);
+    // Visit the expression, registering namespaces for any extension functions it includes.
+    xpath.callVisitors(xpath, new ExpressionVisitor(getStylesheetRoot()));
+    return xpath;    
+  }
+
+  /**
+   * Given a namespace, get the corrisponding prefix from the current
+   * namespace support context.
+   *
+   * @param prefix The prefix to look up, which may be an empty string ("") for the default Namespace.
+   *
+   * @return The associated Namespace URI, or null if the prefix
+   *         is undeclared in this context.
+   */
+  public String getNamespaceForPrefix(String prefix)
+  {
+    return this.getNamespaceSupport().getURI(prefix);
+  }
+
+  /**
+   * Given a namespace, get the corrisponding prefix.  This is here only
+   * to support the {@link org.apache.xml.utils.PrefixResolver} interface,
+   * and will throw an error if invoked on this object.
+   *
+   * @param prefix The prefix to look up, which may be an empty string ("") for the default Namespace.
+   * @param context The node context from which to look up the URI.
+   *
+   * @return The associated Namespace URI, or null if the prefix
+   *         is undeclared in this context.
+   */
+  public String getNamespaceForPrefix(String prefix, org.w3c.dom.Node context)
+  {
+
+    // Don't need to support this here.  Return the current URI for the prefix,
+    // ignoring the context.
+    assertion(true, "can't process a context node in StylesheetHandler!");
+
+    return null;
+  }
+
+  /**
+   * Utility function to see if the stack contains the given URL.
+   *
+   * @param stack non-null reference to a Stack.
+   * @param url URL string on which an equality test will be performed.
+   *
+   * @return true if the stack contains the url argument.
+   */
+  private boolean stackContains(Stack stack, String url)
+  {
+
+    int n = stack.size();
+    boolean contains = false;
+
+    for (int i = 0; i < n; i++)
+    {
+      String url2 = (String) stack.elementAt(i);
+
+      if (url2.equals(url))
+      {
+        contains = true;
+
+        break;
+      }
+    }
+
+    return contains;
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of the TRAX TemplatesBuilder interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * When this object is used as a ContentHandler or ContentHandler, it will
+   * create a Templates object, which the caller can get once
+   * the SAX events have been completed.
+   * @return The stylesheet object that was created during
+   * the SAX event process, or null if no stylesheet has
+   * been created.
+   * 
+   * Author <a href="mailto:scott_boag@lotus.com">Scott Boag</a>
+   *
+   *
+   */
+  public Templates getTemplates()
+  {
+    return getStylesheetRoot();
+  }
+
+  /**
+   * Set the base ID (URL or system ID) for the stylesheet
+   * created by this builder.  This must be set in order to
+   * resolve relative URLs in the stylesheet.
+   *
+   * @param baseID Base URL for this stylesheet.
+   */
+  public void setSystemId(String baseID)
+  {
+    pushBaseIndentifier(baseID);
+  }
+
+  /**
+   * Get the base ID (URI or system ID) from where relative
+   * URLs will be resolved.
+   *
+   * @return The systemID that was set with {@link #setSystemId}.
+   */
+  public String getSystemId()
+  {
+    return this.getBaseIdentifier();
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of the EntityResolver interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Resolve an external entity.
+   *
+   * @param publicId The public identifer, or null if none is
+   *                 available.
+   * @param systemId The system identifier provided in the XML
+   *                 document.
+   * @return The new input source, or null to require the
+   *         default behaviour.
+   *
+   * @throws org.xml.sax.SAXException if the entity can not be resolved.
+   */
+  public InputSource resolveEntity(String publicId, String systemId)
+          throws org.xml.sax.SAXException
+  {
+    return getCurrentProcessor().resolveEntity(this, publicId, systemId);
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of DTDHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Receive notification of a notation declaration.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass if they wish to keep track of the notations
+   * declared in a document.</p>
+   *
+   * @param name The notation name.
+   * @param publicId The notation public identifier, or null if not
+   *                 available.
+   * @param systemId The notation system identifier.
+   * @see org.xml.sax.DTDHandler#notationDecl
+   */
+  public void notationDecl(String name, String publicId, String systemId)
+  {
+    getCurrentProcessor().notationDecl(this, name, publicId, systemId);
+  }
+
+  /**
+   * Receive notification of an unparsed entity declaration.
+   *
+   * @param name The entity name.
+   * @param publicId The entity public identifier, or null if not
+   *                 available.
+   * @param systemId The entity system identifier.
+   * @param notationName The name of the associated notation.
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void unparsedEntityDecl(String name, String publicId,
+                                 String systemId, String notationName)
+  {
+    getCurrentProcessor().unparsedEntityDecl(this, name, publicId, systemId,
+                                             notationName);
+  }
+
+  /**
+   * Given a namespace URI, and a local name or a node type, get the processor
+   * for the element, or return null if not allowed.
+   *
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   *
+   * @return A non-null reference to a element processor.
+   *
+   * @throws org.xml.sax.SAXException if the element is not allowed in the
+   * found position in the stylesheet.
+   */
+  XSLTElementProcessor getProcessorFor(
+          String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+
+    XSLTElementProcessor currentProcessor = getCurrentProcessor();
+    XSLTElementDef def = currentProcessor.getElemDef();
+    XSLTElementProcessor elemProcessor = def.getProcessorFor(uri, localName);
+
+    if (null == elemProcessor
+            && !(currentProcessor instanceof ProcessorStylesheetDoc)
+            && ((null == getStylesheet()
+                || Double.valueOf(getStylesheet().getVersion()).doubleValue()
+                   > Constants.XSLTVERSUPPORTED) 
+                ||(!uri.equals(Constants.S_XSLNAMESPACEURL) &&
+                            currentProcessor instanceof ProcessorStylesheetElement)
+                || getElemVersion() > Constants.XSLTVERSUPPORTED
+        ))
+    {
+      elemProcessor = def.getProcessorForUnknown(uri, localName);
+    }
+
+    if (null == elemProcessor)
+      error(XSLMessages.createMessage(XSLTErrorResources.ER_NOT_ALLOWED_IN_POSITION, new Object[]{rawName}),null);//rawName + " is not allowed in this position in the stylesheet!",
+            
+                
+    return elemProcessor;
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of ContentHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Receive a Locator object for document events.
+   * This is called by the parser to push a locator for the
+   * stylesheet being parsed. The stack needs to be popped
+   * after the stylesheet has been parsed. We pop in
+   * popStylesheet.
+   *
+   * @param locator A locator for all SAX document events.
+   * @see org.xml.sax.ContentHandler#setDocumentLocator
+   * @see org.xml.sax.Locator
+   */
+  public void setDocumentLocator(Locator locator)
+  {
+
+    // System.out.println("pushing locator for: "+locator.getSystemId());
+    m_stylesheetLocatorStack.push(new SAXSourceLocator(locator));
+  }
+
+  /**
+   * The level of the stylesheet we are at.
+   */
+  private int m_stylesheetLevel = -1;
+
+  /**
+   * Receive notification of the beginning of the document.
+   *
+   * @see org.xml.sax.ContentHandler#startDocument
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void startDocument() throws org.xml.sax.SAXException
+  {
+    m_stylesheetLevel++;
+    pushSpaceHandling(false);
+  }
+
+  /** m_parsingComplete becomes true when the top-level stylesheet and all
+   * its included/imported stylesheets have been been fully parsed, as an
+   * indication that composition/optimization/compilation can begin.
+   * @see isStylesheetParsingComplete  */
+  private boolean m_parsingComplete = false;
+
+  /**
+   * Test whether the _last_ endDocument() has been processed.
+   * This is needed as guidance for stylesheet optimization
+   * and compilation engines, which generally don't want to start
+   * until all included and imported stylesheets have been fully
+   * parsed.
+   *
+   * @return true iff the complete stylesheet tree has been built.
+   */
+  public boolean isStylesheetParsingComplete()
+  {
+    return m_parsingComplete;
+  }
+
+  /**
+   * Receive notification of the end of the document.
+   *
+   * @see org.xml.sax.ContentHandler#endDocument
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void endDocument() throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      if (null != getStylesheetRoot())
+      {
+        if (0 == m_stylesheetLevel)
+          getStylesheetRoot().recompose();        
+      }
+      else
+        throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_STYLESHEETROOT, null)); //"Did not find the stylesheet root!");
+
+      XSLTElementProcessor elemProcessor = getCurrentProcessor();
+
+      if (null != elemProcessor)
+        elemProcessor.startNonText(this);
+
+      m_stylesheetLevel--;			
+      
+      popSpaceHandling();
+
+      // WARNING: This test works only as long as stylesheets are parsed
+      // more or less recursively. If we switch to an iterative "work-list"
+      // model, this will become true prematurely. In that case,
+      // isStylesheetParsingComplete() will have to be adjusted to be aware
+      // of the worklist.
+      m_parsingComplete = (m_stylesheetLevel < 0);
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+  
+  private java.util.Vector m_prefixMappings = new java.util.Vector();
+
+  /**
+   * Receive notification of the start of a Namespace mapping.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the start of
+   * each element (such as allocating a new tree node or writing
+   * output to a file).</p>
+   *
+   * @param prefix The Namespace prefix being declared.
+   * @param uri The Namespace URI mapped to the prefix.
+   * @see org.xml.sax.ContentHandler#startPrefixMapping
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void startPrefixMapping(String prefix, String uri)
+          throws org.xml.sax.SAXException
+  {
+
+    // m_nsSupport.pushContext();
+    // this.getNamespaceSupport().declarePrefix(prefix, uri);
+    //m_prefixMappings.add(prefix); // JDK 1.2+ only -sc
+    //m_prefixMappings.add(uri); // JDK 1.2+ only -sc
+    m_prefixMappings.addElement(prefix); // JDK 1.1.x compat -sc
+    m_prefixMappings.addElement(uri); // JDK 1.1.x compat -sc
+  }
+
+  /**
+   * Receive notification of the end of a Namespace mapping.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the start of
+   * each element (such as allocating a new tree node or writing
+   * output to a file).</p>
+   *
+   * @param prefix The Namespace prefix being declared.
+   * @see org.xml.sax.ContentHandler#endPrefixMapping
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException
+  {
+
+    // m_nsSupport.popContext();
+  }
+
+  /**
+   * Flush the characters buffer.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  private void flushCharacters() throws org.xml.sax.SAXException
+  {
+
+    XSLTElementProcessor elemProcessor = getCurrentProcessor();
+
+    if (null != elemProcessor)
+      elemProcessor.startNonText(this);
+  }
+
+  /**
+   * Receive notification of the start of an element.
+   *
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param attributes The specified or defaulted attributes.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void startElement(
+          String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+    NamespaceSupport nssupport = this.getNamespaceSupport();
+    nssupport.pushContext();
+    
+    int n = m_prefixMappings.size();
+
+    for (int i = 0; i < n; i++) 
+    {
+      String prefix = (String)m_prefixMappings.elementAt(i++);
+      String nsURI = (String)m_prefixMappings.elementAt(i);
+      nssupport.declarePrefix(prefix, nsURI);
+    }
+    //m_prefixMappings.clear(); // JDK 1.2+ only -sc
+    m_prefixMappings.removeAllElements(); // JDK 1.1.x compat -sc
+
+    m_elementID++;
+
+    // This check is currently done for all elements.  We should possibly consider
+    // limiting this check to xsl:stylesheet elements only since that is all it really
+    // applies to.  Also, it could be bypassed if m_shouldProcess is already true.
+    // In other words, the next two statements could instead look something like this:
+    // if (!m_shouldProcess)
+    // {
+    //   if (localName.equals(Constants.ELEMNAME_STYLESHEET_STRING) &&
+    //       url.equals(Constants.S_XSLNAMESPACEURL))
+    //   {
+    //     checkForFragmentID(attributes);
+    //     if (!m_shouldProcess)
+    //       return;
+    //   }
+    //   else
+    //     return;
+    // } 
+    // I didn't include this code statement at this time because in practice 
+    // it is a small performance hit and I was waiting to see if its absence
+    // caused a problem. - GLP
+
+    checkForFragmentID(attributes);
+
+    if (!m_shouldProcess)
+      return;
+
+    flushCharacters();
+    
+    pushSpaceHandling(attributes);
+
+    XSLTElementProcessor elemProcessor = getProcessorFor(uri, localName,
+                                           rawName);
+
+    if(null != elemProcessor)  // defensive, for better multiple error reporting. -sb
+    {
+      this.pushProcessor(elemProcessor);
+      elemProcessor.startElement(this, uri, localName, rawName, attributes);
+    }
+    else
+    {
+      m_shouldProcess = false;
+      popSpaceHandling();
+    }
+                
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @see org.xml.sax.ContentHandler#endElement
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void endElement(String uri, String localName, String rawName)
+          throws org.xml.sax.SAXException
+  {
+
+    m_elementID--;
+
+    if (!m_shouldProcess)
+      return;
+
+    if ((m_elementID + 1) == m_fragmentID)
+      m_shouldProcess = false;
+
+    flushCharacters();
+    
+    popSpaceHandling();
+
+    XSLTElementProcessor p = getCurrentProcessor();
+
+    p.endElement(this, uri, localName, rawName);
+    this.popProcessor();
+    this.getNamespaceSupport().popContext();
+  }
+
+  /**
+   * Receive notification of character data inside an element.
+   *
+   * @param ch The characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   * @see org.xml.sax.ContentHandler#characters
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void characters(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+
+    if (!m_shouldProcess)
+      return;
+
+    XSLTElementProcessor elemProcessor = getCurrentProcessor();
+    XSLTElementDef def = elemProcessor.getElemDef();
+
+    if (def.getType() != XSLTElementDef.T_PCDATA)
+      elemProcessor = def.getProcessorFor(null, "text()");
+
+    if (null == elemProcessor)
+    {
+
+      // If it's whitespace, just ignore it, otherwise flag an error.
+      if (!XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
+        error(
+          XSLMessages.createMessage(XSLTErrorResources.ER_NONWHITESPACE_NOT_ALLOWED_IN_POSITION, null),null);//"Non-whitespace text is not allowed in this position in the stylesheet!",
+          
+    }
+    else
+      elemProcessor.characters(this, ch, start, length);
+  }
+
+  /**
+   * Receive notification of ignorable whitespace in element content.
+   *
+   * @param ch The whitespace characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   * @see org.xml.sax.ContentHandler#ignorableWhitespace
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void ignorableWhitespace(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+
+    if (!m_shouldProcess)
+      return;
+
+    getCurrentProcessor().ignorableWhitespace(this, ch, start, length);
+  }
+
+  /**
+   * Receive notification of a processing instruction.
+   *
+   * <p>The Parser will invoke this method once for each processing
+   * instruction found: note that processing instructions may occur
+   * before or after the main document element.</p>
+   *
+   * <p>A SAX parser should never report an XML declaration (XML 1.0,
+   * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
+   * using this method.</p>
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions for each
+   * processing instruction, such as setting status variables or
+   * invoking other methods.</p>
+   *
+   * @param target The processing instruction target.
+   * @param data The processing instruction data, or null if
+   *             none is supplied.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void processingInstruction(String target, String data)
+          throws org.xml.sax.SAXException
+  {
+    if (!m_shouldProcess)
+      return;
+
+    // Recreating Scott's kluge:
+    // A xsl:for-each or xsl:apply-templates may have a special 
+    // PI that tells us not to cache the document.  This PI 
+    // should really be namespaced.
+    //    String localName = getLocalName(target);
+    //    String ns = m_stylesheet.getNamespaceFromStack(target);
+    //
+    // %REVIEW%: We need a better PI architecture
+    
+    String prefix="",ns="", localName=target;
+    int colon=target.indexOf(':');
+    if(colon>=0)
+    {
+      ns=getNamespaceForPrefix(prefix=target.substring(0,colon));
+      localName=target.substring(colon+1);
+    }
+
+    try
+    {
+      // A xsl:for-each or xsl:apply-templates may have a special 
+      // PI that tells us not to cache the document.  This PI 
+      // should really be namespaced... but since the XML Namespaces
+      // spec never defined namespaces as applying to PI's, and since
+      // the testcase we're trying to support is inconsistant in whether
+      // it binds the prefix, I'm going to make this sloppy for
+      // testing purposes.
+      if(
+        "xalan-doc-cache-off".equals(target) ||
+        "xalan:doc-cache-off".equals(target) ||
+	   ("doc-cache-off".equals(localName) &&
+	    ns.equals("org.apache.xalan.xslt.extensions.Redirect") )
+	 )
+      {
+	if(!(m_elems.peek() instanceof ElemForEach))
+          throw new TransformerException
+	    ("xalan:doc-cache-off not allowed here!", 
+	     getLocator());
+        ElemForEach elem = (ElemForEach)m_elems.peek();
+
+        elem.m_doc_cache_off = true;
+
+	//System.out.println("JJK***** Recognized <? {"+ns+"}"+prefix+":"+localName+" "+data+"?>");
+      }
+    }
+    catch(Exception e)
+    {
+      // JJK: Officially, unknown PIs can just be ignored.
+      // Do we want to issue a warning?
+    }
+
+
+    flushCharacters();
+    getCurrentProcessor().processingInstruction(this, target, data);
+  }
+
+  /**
+   * Receive notification of a skipped entity.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions for each
+   * processing instruction, such as setting status variables or
+   * invoking other methods.</p>
+   *
+   * @param name The name of the skipped entity.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void skippedEntity(String name) throws org.xml.sax.SAXException
+  {
+
+    if (!m_shouldProcess)
+      return;
+
+    getCurrentProcessor().skippedEntity(this, name);
+  }
+
+  /**
+   * Warn the user of an problem.
+   *
+   * @param msg An key into the {@link org.apache.xalan.res.XSLTErrorResources}
+   * table, that is one of the WG_ prefixed definitions.
+   * @param args An array of arguments for the given warning.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if the current
+   * {@link javax.xml.transform.ErrorListener#warning}
+   * method chooses to flag this condition as an error.
+   * @xsl.usage internal
+   */
+  public void warn(String msg, Object args[]) throws org.xml.sax.SAXException
+  {
+
+    String formattedMsg = XSLMessages.createWarning(msg, args);
+    SAXSourceLocator locator = getLocator();
+    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
+
+    try
+    {
+      if (null != handler)
+        handler.warning(new TransformerException(formattedMsg, locator));
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * Assert that a condition is true.  If it is not true, throw an error.
+   *
+   * @param condition false if an error should not be thrown, otherwise true.
+   * @param msg Error message to be passed to the RuntimeException as an
+   * argument.
+   * @throws RuntimeException if the condition is not true.
+   * @xsl.usage internal
+   */
+  private void assertion(boolean condition, String msg) throws RuntimeException
+  {
+    if (!condition)
+      throw new RuntimeException(msg);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg An error message.
+   * @param e An error which the SAXException should wrap.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if the current
+   * {@link javax.xml.transform.ErrorListener#error}
+   * method chooses to flag this condition as an error.
+   * @xsl.usage internal
+   */
+  protected void error(String msg, Exception e)
+          throws org.xml.sax.SAXException
+  {
+
+    SAXSourceLocator locator = getLocator();
+    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
+    TransformerException pe;
+
+    if (!(e instanceof TransformerException))
+    {
+      pe = (null == e)
+           ? new TransformerException(msg, locator)
+           : new TransformerException(msg, locator, e);
+    }
+    else
+      pe = (TransformerException) e;
+
+    if (null != handler)
+    {
+      try
+      {
+        handler.error(pe);
+      }
+      catch (TransformerException te)
+      {
+        throw new org.xml.sax.SAXException(te);
+      }
+    }
+    else
+      throw new org.xml.sax.SAXException(pe);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg A key into the {@link org.apache.xalan.res.XSLTErrorResources}
+   * table, that is one of the WG_ prefixed definitions.
+   * @param args An array of arguments for the given warning.
+   * @param e An error which the SAXException should wrap.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if the current
+   * {@link javax.xml.transform.ErrorListener#error}
+   * method chooses to flag this condition as an error.
+   * @xsl.usage internal
+   */
+  protected void error(String msg, Object args[], Exception e)
+          throws org.xml.sax.SAXException
+  {
+
+    String formattedMsg = XSLMessages.createMessage(msg, args);
+
+    error(formattedMsg, e);
+  }
+
+  /**
+   * Receive notification of a XSLT processing warning.
+   *
+   * @param e The warning information encoded as an exception.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if the current
+   * {@link javax.xml.transform.ErrorListener#warning}
+   * method chooses to flag this condition as an error.
+   */
+  public void warning(org.xml.sax.SAXParseException e)
+          throws org.xml.sax.SAXException
+  {
+
+    String formattedMsg = e.getMessage();
+    SAXSourceLocator locator = getLocator();
+    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
+
+    try
+    {
+      handler.warning(new TransformerException(formattedMsg, locator));
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * Receive notification of a recoverable XSLT processing error.
+   *
+   * @param e The error information encoded as an exception.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if the current
+   * {@link javax.xml.transform.ErrorListener#error}
+   * method chooses to flag this condition as an error.
+   */
+  public void error(org.xml.sax.SAXParseException e)
+          throws org.xml.sax.SAXException
+  {
+
+    String formattedMsg = e.getMessage();
+    SAXSourceLocator locator = getLocator();
+    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
+
+    try
+    {
+      handler.error(new TransformerException(formattedMsg, locator));
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * Report a fatal XSLT processing error.
+   *
+   * @param e The error information encoded as an exception.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if the current
+   * {@link javax.xml.transform.ErrorListener#fatalError}
+   * method chooses to flag this condition as an error.
+   */
+  public void fatalError(org.xml.sax.SAXParseException e)
+          throws org.xml.sax.SAXException
+  {
+
+    String formattedMsg = e.getMessage();
+    SAXSourceLocator locator = getLocator();
+    ErrorListener handler = m_stylesheetProcessor.getErrorListener();
+
+    try
+    {
+      handler.fatalError(new TransformerException(formattedMsg, locator));
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * If we have a URL to a XML fragment, this is set
+   * to false until the ID is found.
+   * (warning: I worry that this should be in a stack).
+   */
+  private boolean m_shouldProcess = true;
+
+  /**
+   * If we have a URL to a XML fragment, the value is stored
+   * in this string, and the m_shouldProcess flag is set to
+   * false until we match an ID with this string.
+   * (warning: I worry that this should be in a stack).
+   */
+  private String m_fragmentIDString;
+
+  /**
+   * Keep track of the elementID, so we can tell when
+   * is has completed.  This isn't a real ID, but rather
+   * a nesting level.  However, it's good enough for
+   * our purposes.
+   * (warning: I worry that this should be in a stack).
+   */
+  private int m_elementID = 0;
+
+  /**
+   * The ID of the fragment that has been found
+   * (warning: I worry that this should be in a stack).
+   */
+  private int m_fragmentID = 0;
+
+  /**
+   * Check to see if an ID attribute matched the #id, called
+   * from startElement.
+   *
+   * @param attributes The specified or defaulted attributes.
+   */
+  private void checkForFragmentID(Attributes attributes)
+  {
+
+    if (!m_shouldProcess)
+    {
+      if ((null != attributes) && (null != m_fragmentIDString))
+      {
+        int n = attributes.getLength();
+
+        for (int i = 0; i < n; i++)
+        {
+          String name = attributes.getQName(i);
+
+          if (name.equals(Constants.ATTRNAME_ID))
+          {
+            String val = attributes.getValue(i);
+
+            if (val.equalsIgnoreCase(m_fragmentIDString))
+            {
+              m_shouldProcess = true;
+              m_fragmentID = m_elementID;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   *  The XSLT TransformerFactory for needed services.
+   */
+  private TransformerFactoryImpl m_stylesheetProcessor;
+
+  /**
+   * Get the XSLT TransformerFactoryImpl for needed services.
+   * TODO: This method should be renamed.
+   *
+   * @return The TransformerFactoryImpl that owns this handler.
+   */
+  public TransformerFactoryImpl getStylesheetProcessor()
+  {
+    return m_stylesheetProcessor;
+  }
+
+  /**
+   * If getStylesheetType returns this value, the current stylesheet
+   *  is a root stylesheet.
+   * @xsl.usage internal
+   */
+  public static final int STYPE_ROOT = 1;
+
+  /**
+   * If getStylesheetType returns this value, the current stylesheet
+   *  is an included stylesheet.
+   * @xsl.usage internal
+   */
+  public static final int STYPE_INCLUDE = 2;
+
+  /**
+   * If getStylesheetType returns this value, the current stylesheet
+   *  is an imported stylesheet.
+   * @xsl.usage internal
+   */
+  public static final int STYPE_IMPORT = 3;
+
+  /** The current stylesheet type. */
+  private int m_stylesheetType = STYPE_ROOT;
+
+  /**
+   * Get the type of stylesheet that should be built
+   * or is being processed.
+   *
+   * @return one of STYPE_ROOT, STYPE_INCLUDE, or STYPE_IMPORT.
+   */
+  int getStylesheetType()
+  {
+    return m_stylesheetType;
+  }
+
+  /**
+   * Set the type of stylesheet that should be built
+   * or is being processed.
+   *
+   * @param type Must be one of STYPE_ROOT, STYPE_INCLUDE, or STYPE_IMPORT.
+   */
+  void setStylesheetType(int type)
+  {
+    m_stylesheetType = type;
+  }
+
+  /**
+   * The stack of stylesheets being processed.
+   */
+  private Stack m_stylesheets = new Stack();
+
+  /**
+   * Return the stylesheet that this handler is constructing.
+   *
+   * @return The current stylesheet that is on top of the stylesheets stack,
+   *  or null if no stylesheet is on the stylesheets stack.
+   */
+  Stylesheet getStylesheet()
+  {
+    return (m_stylesheets.size() == 0)
+           ? null : (Stylesheet) m_stylesheets.peek();
+  }
+
+  /**
+   * Return the last stylesheet that was popped off the stylesheets stack.
+   *
+   * @return The last popped stylesheet, or null.
+   */
+  Stylesheet getLastPoppedStylesheet()
+  {
+    return m_lastPoppedStylesheet;
+  }
+
+  /**
+   * Return the stylesheet root that this handler is constructing.
+   *
+   * @return The root stylesheet of the stylesheets tree.
+   */
+  public StylesheetRoot getStylesheetRoot()
+  {
+    if (m_stylesheetRoot != null){
+        m_stylesheetRoot.setOptimizer(m_optimize);
+        m_stylesheetRoot.setIncremental(m_incremental);
+        m_stylesheetRoot.setSource_location(m_source_location);  		
+    }
+    return m_stylesheetRoot;
+  }
+
+  /** The root stylesheet of the stylesheets tree. */
+  StylesheetRoot m_stylesheetRoot;
+        
+        /** The last stylesheet that was popped off the stylesheets stack. */
+  Stylesheet m_lastPoppedStylesheet;
+
+  /**
+   * Push the current stylesheet being constructed. If no other stylesheets
+   * have been pushed onto the stack, assume the argument is a stylesheet
+   * root, and also set the stylesheet root member.
+   *
+   * @param s non-null reference to a stylesheet.
+   */
+  public void pushStylesheet(Stylesheet s)
+  {
+
+    if (m_stylesheets.size() == 0)
+      m_stylesheetRoot = (StylesheetRoot) s;
+
+    m_stylesheets.push(s);
+  }
+
+  /**
+   * Pop the last stylesheet pushed, and return the stylesheet that this
+   * handler is constructing, and set the last popped stylesheet member.
+   * Also pop the stylesheet locator stack.
+   *
+   * @return The stylesheet popped off the stack, or the last popped stylesheet.
+   */
+  Stylesheet popStylesheet()
+  {
+
+    // The stylesheetLocatorStack needs to be popped because
+    // a locator was pushed in for this stylesheet by the SAXparser by calling
+    // setDocumentLocator().
+    if (!m_stylesheetLocatorStack.isEmpty())
+      m_stylesheetLocatorStack.pop();
+
+    if (!m_stylesheets.isEmpty())
+      m_lastPoppedStylesheet = (Stylesheet) m_stylesheets.pop();
+
+    // Shouldn't this be null if stylesheets is empty?  -sb
+    return m_lastPoppedStylesheet;
+  }
+
+  /**
+   * The stack of current processors.
+   */
+  private Stack m_processors = new Stack();
+
+  /**
+   * Get the current XSLTElementProcessor at the top of the stack.
+   *
+   * @return Valid XSLTElementProcessor, which should never be null.
+   */
+  XSLTElementProcessor getCurrentProcessor()
+  {
+    return (XSLTElementProcessor) m_processors.peek();
+  }
+
+  /**
+   * Push the current XSLTElementProcessor onto the top of the stack.
+   *
+   * @param processor non-null reference to the current element processor.
+   */
+  void pushProcessor(XSLTElementProcessor processor)
+  {
+    m_processors.push(processor);
+  }
+
+  /**
+   * Pop the current XSLTElementProcessor from the top of the stack.
+   * @return the XSLTElementProcessor which was popped.
+   */
+  XSLTElementProcessor popProcessor()
+  {
+    return (XSLTElementProcessor) m_processors.pop();
+  }
+
+  /**
+   * The root of the XSLT Schema, which tells us how to
+   * transition content handlers, create elements, etc.
+   * For the moment at least, this can't be static, since
+   * the processors store state.
+   */
+  private XSLTSchema m_schema = new XSLTSchema();
+
+  /**
+   * Get the root of the XSLT Schema, which tells us how to
+   * transition content handlers, create elements, etc.
+   *
+   * @return The root XSLT Schema, which should never be null.
+   * @xsl.usage internal
+   */
+  public XSLTSchema getSchema()
+  {
+    return m_schema;
+  }
+
+  /**
+   * The stack of elements, pushed and popped as events occur.
+   */
+  private Stack m_elems = new Stack();
+
+  /**
+   * Get the current ElemTemplateElement at the top of the stack.
+   * @return Valid ElemTemplateElement, which may be null.
+   */
+  ElemTemplateElement getElemTemplateElement()
+  {
+
+    try
+    {
+      return (ElemTemplateElement) m_elems.peek();
+    }
+    catch (java.util.EmptyStackException ese)
+    {
+      return null;
+    }
+  }  
+
+  /** An increasing number that is used to indicate the order in which this element
+   *  was encountered during the parse of the XSLT tree.
+   */
+  private int m_docOrderCount = 0;
+
+  /**
+   * Returns the next m_docOrderCount number and increments the number for future use.
+   */
+  int nextUid()
+  {
+    return m_docOrderCount++;
+  }
+
+  /**
+   * Push the current XSLTElementProcessor to the top of the stack.  As a
+   * side-effect, set the document order index (simply because this is a
+   * convenient place to set it).
+   *
+   * @param elem Should be a non-null reference to the intended current
+   * template element.
+   */
+  void pushElemTemplateElement(ElemTemplateElement elem)
+  {
+
+    if (elem.getUid() == -1)
+      elem.setUid(nextUid());
+
+    m_elems.push(elem);
+  }
+
+  /**
+   * Get the current XSLTElementProcessor from the top of the stack.
+   * @return the ElemTemplateElement which was popped.
+   */
+  ElemTemplateElement popElemTemplateElement()
+  {
+    return (ElemTemplateElement) m_elems.pop();
+  }
+
+  /**
+   * This will act as a stack to keep track of the
+   * current include base.
+   */
+  Stack m_baseIdentifiers = new Stack();
+
+  /**
+   * Push a base identifier onto the base URI stack.
+   *
+   * @param baseID The current base identifier for this position in the
+   * stylesheet, which may be a fragment identifier, or which may be null.
+   * @see <a href="http://www.w3.org/TR/xslt#base-uri">
+   * Section 3.2 Base URI of XSLT specification.</a>
+   */
+  void pushBaseIndentifier(String baseID)
+  {
+
+    if (null != baseID)
+    {
+      int posOfHash = baseID.indexOf('#');
+
+      if (posOfHash > -1)
+      {
+        m_fragmentIDString = baseID.substring(posOfHash + 1);
+        m_shouldProcess = false;
+      }
+      else
+        m_shouldProcess = true;
+    }
+    else
+      m_shouldProcess = true;
+
+    m_baseIdentifiers.push(baseID);
+  }
+
+  /**
+   * Pop a base URI from the stack.
+   * @return baseIdentifier.
+   */
+  String popBaseIndentifier()
+  {
+    return (String) m_baseIdentifiers.pop();
+  }
+
+  /**
+   * Return the base identifier.
+   *
+   * @return The base identifier of the current stylesheet.
+   */
+  public String getBaseIdentifier()
+  {
+
+    // Try to get the baseIdentifier from the baseIdentifier's stack,
+    // which may not be the same thing as the value found in the
+    // SourceLocators stack.
+    String base = (String) (m_baseIdentifiers.isEmpty()
+                            ? null : m_baseIdentifiers.peek());
+
+    // Otherwise try the stylesheet.
+    if (null == base)
+    {
+      SourceLocator locator = getLocator();
+
+      base = (null == locator) ? "" : locator.getSystemId();
+    }
+
+    return base;
+  }
+
+  /**
+   * The top of this stack should contain the currently processed
+   * stylesheet SAX locator object.
+   */
+  private Stack m_stylesheetLocatorStack = new Stack();
+
+  /**
+   * Get the current stylesheet Locator object.
+   *
+   * @return non-null reference to the current locator object.
+   */
+  public SAXSourceLocator getLocator()
+  {
+
+    if (m_stylesheetLocatorStack.isEmpty())
+    {
+      SAXSourceLocator locator = new SAXSourceLocator();
+
+      locator.setSystemId(this.getStylesheetProcessor().getDOMsystemID());
+
+      return locator;
+
+      // m_stylesheetLocatorStack.push(locator);
+    }
+
+    return ((SAXSourceLocator) m_stylesheetLocatorStack.peek());
+  }
+
+  /**
+   * A stack of URL hrefs for imported stylesheets.  This is
+   * used to diagnose circular imports.
+   */
+  private Stack m_importStack = new Stack();
+  
+  /**
+   * A stack of Source objects obtained from a URIResolver,
+   * for each element in this stack there is a 1-1 correspondence
+   * with an element in the m_importStack.
+   */
+  private Stack m_importSourceStack = new Stack();
+
+  /**
+   * Push an import href onto the stylesheet stack.
+   *
+   * @param hrefUrl non-null reference to the URL for the current imported
+   * stylesheet.
+   */
+  void pushImportURL(String hrefUrl)
+  {
+    m_importStack.push(hrefUrl);
+  }
+  
+  /**
+   * Push the Source of an import href onto the stylesheet stack,
+   * obtained from a URIResolver, null if there is no URIResolver,
+   * or if that resolver returned null.
+   */
+  void pushImportSource(Source sourceFromURIResolver)
+  {
+    m_importSourceStack.push(sourceFromURIResolver);
+  }
+
+  /**
+   * See if the imported stylesheet stack already contains
+   * the given URL.  Used to test for recursive imports.
+   *
+   * @param hrefUrl non-null reference to a URL string.
+   *
+   * @return true if the URL is on the import stack.
+   */
+  boolean importStackContains(String hrefUrl)
+  {
+    return stackContains(m_importStack, hrefUrl);
+  }
+
+  /**
+   * Pop an import href from the stylesheet stack.
+   *
+   * @return non-null reference to the import URL that was popped.
+   */
+  String popImportURL()
+  {
+    return (String) m_importStack.pop();
+  }
+  
+  String peekImportURL()
+  {
+    return (String) m_importStack.peek();
+  }
+  
+  Source peekSourceFromURIResolver()
+  {
+    return (Source) m_importSourceStack.peek();
+  }
+  
+  /**
+   * Pop a Source from a user provided URIResolver, corresponding
+   * to the URL popped from the m_importStack.
+   */
+  Source popImportSource()
+  {
+    return (Source) m_importSourceStack.pop();
+  }
+
+  /**
+   * If this is set to true, we've already warned about using the
+   * older XSLT namespace URL.
+   */
+  private boolean warnedAboutOldXSLTNamespace = false;
+
+  /** Stack of NamespaceSupport objects. */
+  Stack m_nsSupportStack = new Stack();
+
+  /**
+   * Push a new NamespaceSupport instance.
+   */
+  void pushNewNamespaceSupport()
+  {
+    m_nsSupportStack.push(new NamespaceSupport2());
+  }
+
+  /**
+   * Pop the current NamespaceSupport object.
+   *
+   */
+  void popNamespaceSupport()
+  {
+    m_nsSupportStack.pop();
+  }
+
+  /**
+   * Get the current NamespaceSupport object.
+   *
+   * @return a non-null reference to the current NamespaceSupport object,
+   * which is the top of the namespace support stack.
+   */
+  NamespaceSupport getNamespaceSupport()
+  {
+    return (NamespaceSupport) m_nsSupportStack.peek();
+  }
+
+  /**
+   * The originating node if the current stylesheet is being created
+   *  from a DOM.
+   *  @see org.apache.xml.utils.NodeConsumer
+   */
+  private Node m_originatingNode;
+
+  /**
+   * Set the node that is originating the SAX event.
+   *
+   * @param n Reference to node that originated the current event.
+   * @see org.apache.xml.utils.NodeConsumer
+   */
+  public void setOriginatingNode(Node n)
+  {
+    m_originatingNode = n;
+  }
+
+  /**
+   * Set the node that is originating the SAX event.
+   *
+   * @return Reference to node that originated the current event.
+   * @see org.apache.xml.utils.NodeConsumer
+   */
+  public Node getOriginatingNode()
+  {
+    return m_originatingNode;
+  }
+  
+  /**
+   * Stack of booleans that are pushed and popped in start/endElement depending 
+   * on the value of xml:space=default/preserve.
+   */
+  private BoolStack m_spacePreserveStack = new BoolStack();
+  
+  /**
+   * Return boolean value from the spacePreserve stack depending on the value 
+   * of xml:space=default/preserve.
+   * 
+   * @return true if space should be preserved, false otherwise.
+   */
+  boolean isSpacePreserve()
+  {
+    return m_spacePreserveStack.peek();
+  }
+  
+  /**
+   * Pop boolean value from the spacePreserve stack.
+   */
+  void popSpaceHandling()
+  {
+    m_spacePreserveStack.pop();
+  }
+  
+  /**
+   * Push boolean value on to the spacePreserve stack.
+   * 
+   * @param b true if space should be preserved, false otherwise.
+   */
+  void pushSpaceHandling(boolean b)
+    throws org.xml.sax.SAXParseException
+  {
+    m_spacePreserveStack.push(b);
+  }
+  
+  /**
+   * Push boolean value on to the spacePreserve stack depending on the value 
+   * of xml:space=default/preserve.
+   * 
+   * @param attrs list of attributes that were passed to startElement.
+   */
+  void pushSpaceHandling(Attributes attrs)
+    throws org.xml.sax.SAXParseException
+  {    
+    String value = attrs.getValue("xml:space");
+    if(null == value)
+    {
+      m_spacePreserveStack.push(m_spacePreserveStack.peekOrFalse());
+    }
+    else if(value.equals("preserve"))
+    {
+      m_spacePreserveStack.push(true);
+    }
+    else if(value.equals("default"))
+    {
+      m_spacePreserveStack.push(false);
+    }
+    else
+    {
+      SAXSourceLocator locator = getLocator();
+      ErrorListener handler = m_stylesheetProcessor.getErrorListener();
+  
+      try
+      {
+        handler.error(new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_ILLEGAL_XMLSPACE_VALUE, null), locator)); //"Illegal value for xml:space", locator));
+      }
+      catch (TransformerException te)
+      {
+        throw new org.xml.sax.SAXParseException(te.getMessage(), locator, te);
+      }
+      m_spacePreserveStack.push(m_spacePreserveStack.peek());
+    }
+  }
+  
+  private double getElemVersion()
+  {
+    ElemTemplateElement elem = getElemTemplateElement();
+    double version = -1; 
+    while ((version == -1 || version == Constants.XSLTVERSUPPORTED) && elem != null)
+    {
+      try{
+      version = Double.valueOf(elem.getXmlVersion()).doubleValue();
+      }
+      catch (Exception ex)
+      {
+        version = -1;
+      }
+      elem = elem.getParentElem();
+      }
+    return (version == -1)? Constants.XSLTVERSUPPORTED : version;
+  }
+    /**
+     * @see PrefixResolver#handlesNullPrefixes()
+     */
+    public boolean handlesNullPrefixes() {
+        return false;
+    }
+
+    /**
+     * @return Optimization flag
+     */
+    public boolean getOptimize() {
+        return m_optimize;
+    }
+
+    /**
+     * @return Incremental flag
+     */
+    public boolean getIncremental() {
+        return m_incremental;
+    }
+
+    /**
+     * @return Source Location flag
+     */
+    public boolean getSource_location() {
+        return m_source_location;
+    }
+
+}
+
+
+
diff --git a/src/main/java/org/apache/xalan/processor/TransformerFactoryImpl.java b/src/main/java/org/apache/xalan/processor/TransformerFactoryImpl.java
new file mode 100644
index 0000000..618b412
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/TransformerFactoryImpl.java
@@ -0,0 +1,1043 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TransformerFactoryImpl.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import javax.xml.XMLConstants;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TemplatesHandler;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TrAXFilter;
+import org.apache.xalan.transformer.TransformerIdentityImpl;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xalan.transformer.XalanProperties;
+
+import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
+import org.apache.xml.utils.DefaultErrorHandler;
+import org.apache.xml.utils.SystemIDResolver;
+import org.apache.xml.utils.TreeWalker;
+import org.apache.xml.utils.StylesheetPIHandler;
+import org.apache.xml.utils.StopParseException;
+
+import org.w3c.dom.Node;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * The TransformerFactoryImpl, which implements the TRaX TransformerFactory
+ * interface, processes XSLT stylesheets into a Templates object
+ * (a StylesheetRoot).
+ */
+public class TransformerFactoryImpl extends SAXTransformerFactory
+{
+  /** 
+   * The path/filename of the property file: XSLTInfo.properties  
+   * Maintenance note: see also
+   * <code>org.apache.xpath.functions.FuncSystemProperty.XSLT_PROPERTIES</code>
+   */
+  public static final String XSLT_PROPERTIES =
+    "org/apache/xalan/res/XSLTInfo.properties";
+
+  /**
+   * <p>State of secure processing feature.</p>
+   */
+  private boolean m_isSecureProcessing = false;
+
+  /**
+   * Constructor TransformerFactoryImpl
+   *
+   */
+  public TransformerFactoryImpl()
+  {
+  }
+
+  /** Static string to be used for incremental feature */
+  public static final String FEATURE_INCREMENTAL =
+                             "http://xml.apache.org/xalan/features/incremental";
+
+  /** Static string to be used for optimize feature */
+  public static final String FEATURE_OPTIMIZE =
+                             "http://xml.apache.org/xalan/features/optimize";
+
+  /** Static string to be used for source_location feature */
+  public static final String FEATURE_SOURCE_LOCATION =
+                             XalanProperties.SOURCE_LOCATION;
+
+  public javax.xml.transform.Templates processFromNode(Node node)
+          throws TransformerConfigurationException
+  {
+
+    try
+    {
+      TemplatesHandler builder = newTemplatesHandler();
+      TreeWalker walker = new TreeWalker(builder,
+                                         new org.apache.xml.utils.DOM2Helper(),
+                                         builder.getSystemId());
+
+      walker.traverse(node);
+
+      return builder.getTemplates();
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      if (m_errorListener != null)
+      {
+        try
+        {
+          m_errorListener.fatalError(new TransformerException(se));
+        }
+        catch (TransformerConfigurationException ex)
+        {
+          throw ex;
+        }
+        catch (TransformerException ex)
+        {
+          throw new TransformerConfigurationException(ex);
+        }
+
+        return null;
+      }
+      else
+      {
+
+        // Should remove this later... but right now diagnostics from 
+        // TransformerConfigurationException are not good.
+        // se.printStackTrace();
+        throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_PROCESSFROMNODE_FAILED, null), se); 
+        //"processFromNode failed", se);
+      }
+    }
+    catch (TransformerConfigurationException tce)
+    {
+      // Assume it's already been reported to the error listener.
+      throw tce;
+    }
+   /* catch (TransformerException tce)
+    {
+      // Assume it's already been reported to the error listener.
+      throw new TransformerConfigurationException(tce.getMessage(), tce);
+    }*/
+    catch (Exception e)
+    {
+      if (m_errorListener != null)
+      {
+        try
+        {
+          m_errorListener.fatalError(new TransformerException(e));
+        }
+        catch (TransformerConfigurationException ex)
+        {
+          throw ex;
+        }
+        catch (TransformerException ex)
+        {
+          throw new TransformerConfigurationException(ex);
+        }
+
+        return null;
+      }
+      else
+      {
+        // Should remove this later... but right now diagnostics from 
+        // TransformerConfigurationException are not good.
+        // se.printStackTrace();
+        throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_PROCESSFROMNODE_FAILED, null), e); //"processFromNode failed",
+                                                    //e);
+      }
+    }
+  }
+
+  /**
+   * The systemID that was specified in
+   * processFromNode(Node node, String systemID).
+   */
+  private String m_DOMsystemID = null;
+
+  /**
+   * The systemID that was specified in
+   * processFromNode(Node node, String systemID).
+   *
+   * @return The systemID, or null.
+   */
+  String getDOMsystemID()
+  {
+    return m_DOMsystemID;
+  }
+
+  /**
+   * Process the stylesheet from a DOM tree, if the
+   * processor supports the "http://xml.org/trax/features/dom/input"
+   * feature.
+   *
+   * @param node A DOM tree which must contain
+   * valid transform instructions that this processor understands.
+   * @param systemID The systemID from where xsl:includes and xsl:imports
+   * should be resolved from.
+   *
+   * @return A Templates object capable of being used for transformation purposes.
+   *
+   * @throws TransformerConfigurationException
+   */
+  javax.xml.transform.Templates processFromNode(Node node, String systemID)
+          throws TransformerConfigurationException
+  {
+
+    m_DOMsystemID = systemID;
+
+    return processFromNode(node);
+  }
+
+  /**
+   * Get InputSource specification(s) that are associated with the
+   * given document specified in the source param,
+   * via the xml-stylesheet processing instruction
+   * (see http://www.w3.org/TR/xml-stylesheet/), and that matches
+   * the given criteria.  Note that it is possible to return several stylesheets
+   * that match the criteria, in which case they are applied as if they were
+   * a list of imports or cascades.
+   *
+   * <p>Note that DOM2 has it's own mechanism for discovering stylesheets.
+   * Therefore, there isn't a DOM version of this method.</p>
+   *
+   *
+   * @param source The XML source that is to be searched.
+   * @param media The media attribute to be matched.  May be null, in which
+   *              case the prefered templates will be used (i.e. alternate = no).
+   * @param title The value of the title attribute to match.  May be null.
+   * @param charset The value of the charset attribute to match.  May be null.
+   *
+   * @return A Source object capable of being used to create a Templates object.
+   *
+   * @throws TransformerConfigurationException
+   */
+  public Source getAssociatedStylesheet(
+          Source source, String media, String title, String charset)
+            throws TransformerConfigurationException
+  {
+
+    String baseID;
+    InputSource isource = null;
+    Node node = null;
+    XMLReader reader = null;
+
+    if (source instanceof DOMSource)
+    {
+      DOMSource dsource = (DOMSource) source;
+
+      node = dsource.getNode();
+      baseID = dsource.getSystemId();
+    }
+    else
+    {
+      isource = SAXSource.sourceToInputSource(source);
+      baseID = isource.getSystemId();
+    }
+
+    // What I try to do here is parse until the first startElement
+    // is found, then throw a special exception in order to terminate 
+    // the parse.
+    StylesheetPIHandler handler = new StylesheetPIHandler(baseID, media,
+                                    title, charset);
+    
+    // Use URIResolver. Patch from Dmitri Ilyin 
+    if (m_uriResolver != null) 
+    {
+      handler.setURIResolver(m_uriResolver); 
+    }
+
+    try
+    {
+      if (null != node)
+      {
+        TreeWalker walker = new TreeWalker(handler, new org.apache.xml.utils.DOM2Helper(), baseID);
+
+        walker.traverse(node);
+      }
+      else
+      {
+
+        // Use JAXP1.1 ( if possible )
+        try
+        {
+          javax.xml.parsers.SAXParserFactory factory =
+            javax.xml.parsers.SAXParserFactory.newInstance();
+
+          factory.setNamespaceAware(true);
+
+          if (m_isSecureProcessing)
+          {
+            try
+            {
+              factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            }
+            catch (org.xml.sax.SAXException e) {}
+          }
+
+          javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser();
+
+          reader = jaxpParser.getXMLReader();
+        }
+        catch (javax.xml.parsers.ParserConfigurationException ex)
+        {
+          throw new org.xml.sax.SAXException(ex);
+        }
+        catch (javax.xml.parsers.FactoryConfigurationError ex1)
+        {
+          throw new org.xml.sax.SAXException(ex1.toString());
+        }
+        catch (NoSuchMethodError ex2){}
+        catch (AbstractMethodError ame){}
+
+        if (null == reader)
+        {
+          reader = XMLReaderFactory.createXMLReader();
+        }
+
+        // Need to set options!
+        reader.setContentHandler(handler);
+        reader.parse(isource);
+      }
+    }
+    catch (StopParseException spe)
+    {
+
+      // OK, good.
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      throw new TransformerConfigurationException(
+        "getAssociatedStylesheets failed", se);
+    }
+    catch (IOException ioe)
+    {
+      throw new TransformerConfigurationException(
+        "getAssociatedStylesheets failed", ioe);
+    }
+
+    return handler.getAssociatedStylesheet();
+  }
+
+  /**
+   * Create a new Transformer object that performs a copy
+   * of the source to the result.
+   *
+   * @return A Transformer object that may be used to perform a transformation
+   * in a single thread, never null.
+   *
+   * @throws TransformerConfigurationException May throw this during
+   *            the parse when it is constructing the
+   *            Templates object and fails.
+   */
+  public TemplatesHandler newTemplatesHandler()
+          throws TransformerConfigurationException
+  {
+    return new StylesheetHandler(this);
+  }
+
+  /**
+   * <p>Set a feature for this <code>TransformerFactory</code> and <code>Transformer</code>s
+   * or <code>Template</code>s created by this factory.</p>
+   * 
+   * <p>
+   * Feature names are fully qualified {@link java.net.URI}s.
+   * Implementations may define their own features.
+   * An {@link TransformerConfigurationException} is thrown if this <code>TransformerFactory</code> or the
+   * <code>Transformer</code>s or <code>Template</code>s it creates cannot support the feature.
+   * It is possible for an <code>TransformerFactory</code> to expose a feature value but be unable to change its state.
+   * </p>
+   * 
+   * <p>See {@link javax.xml.transform.TransformerFactory} for full documentation of specific features.</p>
+   * 
+   * @param name Feature name.
+   * @param value Is feature state <code>true</code> or <code>false</code>.
+   *  
+   * @throws TransformerConfigurationException if this <code>TransformerFactory</code>
+   *   or the <code>Transformer</code>s or <code>Template</code>s it creates cannot support this feature.
+   * @throws NullPointerException If the <code>name</code> parameter is null.
+   */
+  public void setFeature(String name, boolean value)
+	  throws TransformerConfigurationException {
+  
+  	// feature name cannot be null
+  	if (name == null) {
+  	    throw new NullPointerException(
+                  XSLMessages.createMessage(
+                      XSLTErrorResources.ER_SET_FEATURE_NULL_NAME, null));    
+  	}
+  		
+  	// secure processing?
+  	if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
+  	    m_isSecureProcessing = value;			
+  	}
+  	// This implementation does not support the setting of a feature other than
+  	// the secure processing feature.
+  	else
+    {
+      throw new TransformerConfigurationException(
+          XSLMessages.createMessage(
+            XSLTErrorResources.ER_UNSUPPORTED_FEATURE, 
+            new Object[] {name}));
+    }
+  }
+
+  /**
+   * Look up the value of a feature.
+   * <p>The feature name is any fully-qualified URI.  It is
+   * possible for an TransformerFactory to recognize a feature name but
+   * to be unable to return its value; this is especially true
+   * in the case of an adapter for a SAX1 Parser, which has
+   * no way of knowing whether the underlying parser is
+   * validating, for example.</p>
+   *
+   * @param name The feature name, which is a fully-qualified URI.
+   * @return The current state of the feature (true or false).
+   */
+  public boolean getFeature(String name) {
+  	
+    // feature name cannot be null
+    if (name == null) 
+    {
+    	throw new NullPointerException(
+            XSLMessages.createMessage(
+            XSLTErrorResources.ER_GET_FEATURE_NULL_NAME, null));    
+    }
+	  	
+    // Try first with identity comparison, which 
+    // will be faster.
+    if ((DOMResult.FEATURE == name) || (DOMSource.FEATURE == name)
+            || (SAXResult.FEATURE == name) || (SAXSource.FEATURE == name)
+            || (StreamResult.FEATURE == name)
+            || (StreamSource.FEATURE == name)
+            || (SAXTransformerFactory.FEATURE == name)
+            || (SAXTransformerFactory.FEATURE_XMLFILTER == name))
+      return true;
+    else if ((DOMResult.FEATURE.equals(name))
+             || (DOMSource.FEATURE.equals(name))
+             || (SAXResult.FEATURE.equals(name))
+             || (SAXSource.FEATURE.equals(name))
+             || (StreamResult.FEATURE.equals(name))
+             || (StreamSource.FEATURE.equals(name))
+             || (SAXTransformerFactory.FEATURE.equals(name))
+             || (SAXTransformerFactory.FEATURE_XMLFILTER.equals(name)))
+      return true;	      
+    // secure processing?
+    else if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING))
+      return m_isSecureProcessing;
+    else      
+      // unknown feature
+      return false;
+  }
+  
+  /**
+   * Flag set by FEATURE_OPTIMIZE.
+   * This feature specifies whether to Optimize stylesheet processing. By
+   * default it is set to true.
+   */
+  private boolean m_optimize = true;
+  
+  /** Flag set by FEATURE_SOURCE_LOCATION.
+   * This feature specifies whether the transformation phase should
+   * keep track of line and column numbers for the input source
+   * document. Note that this works only when that
+   * information is available from the source -- in other words, if you
+   * pass in a DOM, there's little we can do for you.
+   * 
+   * The default is false. Setting it true may significantly
+   * increase storage cost per node. 
+   */
+  private boolean m_source_location = false;
+  
+  /**
+   * Flag set by FEATURE_INCREMENTAL.
+   * This feature specifies whether to produce output incrementally, rather than
+   * waiting to finish parsing the input before generating any output. By 
+   * default this attribute is set to false. 
+   */
+  private boolean m_incremental = false;
+  
+  /**
+   * Allows the user to set specific attributes on the underlying
+   * implementation.
+   *
+   * @param name The name of the attribute.
+   * @param value The value of the attribute; Boolean or String="true"|"false"
+   *
+   * @throws IllegalArgumentException thrown if the underlying
+   * implementation doesn't recognize the attribute.
+   */
+  public void setAttribute(String name, Object value)
+          throws IllegalArgumentException
+  {
+    if (name.equals(FEATURE_INCREMENTAL))
+    {
+      if(value instanceof Boolean)
+      {
+        // Accept a Boolean object..
+        m_incremental = ((Boolean)value).booleanValue();
+      }
+      else if(value instanceof String)
+      {
+        // .. or a String object
+        m_incremental = (new Boolean((String)value)).booleanValue();
+      }
+      else
+      {
+        // Give a more meaningful error message
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
+      }
+	}
+    else if (name.equals(FEATURE_OPTIMIZE))
+    {
+      if(value instanceof Boolean)
+      {
+        // Accept a Boolean object..
+        m_optimize = ((Boolean)value).booleanValue();
+      }
+      else if(value instanceof String)
+      {
+        // .. or a String object
+        m_optimize = (new Boolean((String)value)).booleanValue();
+      }
+      else
+      {
+        // Give a more meaningful error message
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
+      }
+    }
+    
+    // Custom Xalan feature: annotate DTM with SAX source locator fields.
+    // This gets used during SAX2DTM instantiation. 
+    //
+    // %REVIEW% Should the name of this field really be in XalanProperties?
+    // %REVIEW% I hate that it's a global static, but didn't want to change APIs yet.
+    else if(name.equals(FEATURE_SOURCE_LOCATION))
+    {
+      if(value instanceof Boolean)
+      {
+        // Accept a Boolean object..
+        m_source_location = ((Boolean)value).booleanValue();
+      }
+      else if(value instanceof String)
+      {
+        // .. or a String object
+        m_source_location = (new Boolean((String)value)).booleanValue();
+      }
+      else
+      {
+        // Give a more meaningful error message
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BAD_VALUE, new Object[]{name, value})); //name + " bad value " + value);
+      }
+    }
+    
+    else
+    {
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NOT_SUPPORTED, new Object[]{name})); //name + "not supported");
+    }
+  }
+
+  /**
+   * Allows the user to retrieve specific attributes on the underlying
+   * implementation.
+   *
+   * @param name The name of the attribute.
+   * @return value The value of the attribute.
+   *
+   * @throws IllegalArgumentException thrown if the underlying
+   * implementation doesn't recognize the attribute.
+   */
+  public Object getAttribute(String name) throws IllegalArgumentException
+  {
+    if (name.equals(FEATURE_INCREMENTAL))
+    {
+      return new Boolean(m_incremental);            
+    }
+    else if (name.equals(FEATURE_OPTIMIZE))
+    {
+      return new Boolean(m_optimize);
+    }
+    else if (name.equals(FEATURE_SOURCE_LOCATION))
+    {
+      return new Boolean(m_source_location);
+    }
+    else
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ATTRIB_VALUE_NOT_RECOGNIZED, new Object[]{name})); //name + " attribute not recognized");
+  }
+
+  /**
+   * Create an XMLFilter that uses the given source as the
+   * transformation instructions.
+   *
+   * @param src The source of the transformation instructions.
+   *
+   * @return An XMLFilter object, or null if this feature is not supported.
+   *
+   * @throws TransformerConfigurationException
+   */
+  public XMLFilter newXMLFilter(Source src)
+          throws TransformerConfigurationException
+  {
+
+    Templates templates = newTemplates(src);
+    if( templates==null ) return null;
+    
+    return newXMLFilter(templates);
+  }
+
+  /**
+   * Create an XMLFilter that uses the given source as the
+   * transformation instructions.
+   *
+   * @param templates non-null reference to Templates object.
+   *
+   * @return An XMLFilter object, or null if this feature is not supported.
+   *
+   * @throws TransformerConfigurationException
+   */
+  public XMLFilter newXMLFilter(Templates templates)
+          throws TransformerConfigurationException
+  {
+    try 
+    {
+      return new TrAXFilter(templates);
+    } 
+    catch( TransformerConfigurationException ex ) 
+    {
+      if( m_errorListener != null) 
+      {
+        try 
+        {
+          m_errorListener.fatalError( ex );
+          return null;
+        } 
+        catch( TransformerConfigurationException ex1 ) 
+        {
+          throw ex1;
+        }
+        catch( TransformerException ex1 ) 
+        {
+          throw new TransformerConfigurationException(ex1);
+        }
+      }
+      throw ex;
+    }
+  }
+
+  /**
+   * Get a TransformerHandler object that can process SAX
+   * ContentHandler events into a Result, based on the transformation
+   * instructions specified by the argument.
+   *
+   * @param src The source of the transformation instructions.
+   *
+   * @return TransformerHandler ready to transform SAX events.
+   *
+   * @throws TransformerConfigurationException
+   */
+  public TransformerHandler newTransformerHandler(Source src)
+          throws TransformerConfigurationException
+  {
+
+    Templates templates = newTemplates(src);
+    if( templates==null ) return null;
+    
+    return newTransformerHandler(templates);
+  }
+
+  /**
+   * Get a TransformerHandler object that can process SAX
+   * ContentHandler events into a Result, based on the Templates argument.
+   *
+   * @param templates The source of the transformation instructions.
+   *
+   * @return TransformerHandler ready to transform SAX events.
+   * @throws TransformerConfigurationException
+   */
+  public TransformerHandler newTransformerHandler(Templates templates)
+          throws TransformerConfigurationException
+  {
+    try {
+      TransformerImpl transformer =
+        (TransformerImpl) templates.newTransformer();
+      transformer.setURIResolver(m_uriResolver);
+      TransformerHandler th =
+        (TransformerHandler) transformer.getInputContentHandler(true);
+
+      return th;
+    } 
+    catch( TransformerConfigurationException ex ) 
+    {
+      if( m_errorListener != null ) 
+      {
+        try 
+        {
+          m_errorListener.fatalError( ex );
+          return null;
+        } 
+        catch (TransformerConfigurationException ex1 ) 
+        {
+          throw ex1;
+        }
+        catch (TransformerException ex1 ) 
+        {
+          throw new TransformerConfigurationException(ex1);
+        }
+      }
+      
+      throw ex;
+    }
+    
+  }
+
+//  /** The identity transform string, for support of newTransformerHandler()
+//   *  and newTransformer().  */
+//  private static final String identityTransform =
+//    "<xsl:stylesheet " + "xmlns:xsl='http://www.w3.org/1999/XSL/Transform' "
+//    + "version='1.0'>" + "<xsl:template match='/|node()'>"
+//    + "<xsl:copy-of select='.'/>" + "</xsl:template>" + "</xsl:stylesheet>";
+//
+//  /** The identity transform Templates, built from identityTransform, 
+//   *  for support of newTransformerHandler() and newTransformer().  */
+//  private static Templates m_identityTemplate = null;
+
+  /**
+   * Get a TransformerHandler object that can process SAX
+   * ContentHandler events into a Result.
+   *
+   * @return TransformerHandler ready to transform SAX events.
+   *
+   * @throws TransformerConfigurationException
+   */
+  public TransformerHandler newTransformerHandler()
+          throws TransformerConfigurationException
+  {
+    return new TransformerIdentityImpl(m_isSecureProcessing);
+  }
+
+  /**
+   * Process the source into a Transformer object.  Care must
+   * be given to know that this object can not be used concurrently
+   * in multiple threads.
+   *
+   * @param source An object that holds a URL, input stream, etc.
+   *
+   * @return A Transformer object capable of
+   * being used for transformation purposes in a single thread.
+   *
+   * @throws TransformerConfigurationException May throw this during the parse when it
+   *            is constructing the Templates object and fails.
+   */
+  public Transformer newTransformer(Source source)
+          throws TransformerConfigurationException
+  {
+    try 
+    {
+      Templates tmpl=newTemplates( source );
+      /* this can happen if an ErrorListener is present and it doesn't
+         throw any exception in fatalError. 
+         The spec says: "a Transformer must use this interface
+         instead of throwing an exception" - the newTemplates() does
+         that, and returns null.
+      */
+      if( tmpl==null ) return null;
+      Transformer transformer = tmpl.newTransformer();
+      transformer.setURIResolver(m_uriResolver);
+      return transformer;
+    } 
+    catch( TransformerConfigurationException ex ) 
+    {
+      if( m_errorListener != null ) 
+      {
+        try 
+        {
+          m_errorListener.fatalError( ex );
+          return null; // TODO: but the API promises to never return null...
+        } 
+        catch( TransformerConfigurationException ex1 ) 
+        {
+          throw ex1;
+        }
+        catch( TransformerException ex1 ) 
+        {
+          throw new TransformerConfigurationException( ex1 );
+        }
+      }
+      throw ex;
+    }
+  }
+
+  /**
+   * Create a new Transformer object that performs a copy
+   * of the source to the result.
+   *
+   * @return A Transformer object capable of
+   * being used for transformation purposes in a single thread.
+   *
+   * @throws TransformerConfigurationException May throw this during
+   *            the parse when it is constructing the
+   *            Templates object and it fails.
+   */
+  public Transformer newTransformer() throws TransformerConfigurationException
+  {
+      return new TransformerIdentityImpl(m_isSecureProcessing);
+  }
+
+  /**
+   * Process the source into a Templates object, which is likely
+   * a compiled representation of the source. This Templates object
+   * may then be used concurrently across multiple threads.  Creating
+   * a Templates object allows the TransformerFactory to do detailed
+   * performance optimization of transformation instructions, without
+   * penalizing runtime transformation.
+   *
+   * @param source An object that holds a URL, input stream, etc.
+   * @return A Templates object capable of being used for transformation purposes.
+   *
+   * @throws TransformerConfigurationException May throw this during the parse when it
+   *            is constructing the Templates object and fails.
+   */
+  public Templates newTemplates(Source source)
+          throws TransformerConfigurationException
+  {
+
+    String baseID = source.getSystemId();
+
+    if (null != baseID) {
+       baseID = SystemIDResolver.getAbsoluteURI(baseID);
+    }
+
+
+    if (source instanceof DOMSource)
+    {
+      DOMSource dsource = (DOMSource) source;
+      Node node = dsource.getNode();
+
+      if (null != node)
+        return processFromNode(node, baseID);
+      else
+      {
+        String messageStr = XSLMessages.createMessage(
+          XSLTErrorResources.ER_ILLEGAL_DOMSOURCE_INPUT, null);
+
+        throw new IllegalArgumentException(messageStr);
+      }
+    }
+
+    TemplatesHandler builder = newTemplatesHandler();
+    builder.setSystemId(baseID);
+    
+    try
+    {
+      InputSource isource = SAXSource.sourceToInputSource(source);
+      isource.setSystemId(baseID);
+      XMLReader reader = null;
+
+      if (source instanceof SAXSource)
+        reader = ((SAXSource) source).getXMLReader();
+        
+      if (null == reader)
+      {
+
+        // Use JAXP1.1 ( if possible )
+        try
+        {
+          javax.xml.parsers.SAXParserFactory factory =
+            javax.xml.parsers.SAXParserFactory.newInstance();
+
+          factory.setNamespaceAware(true);
+
+          if (m_isSecureProcessing)
+          {
+            try
+            {
+              factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            }
+            catch (org.xml.sax.SAXException se) {}
+          }
+
+          javax.xml.parsers.SAXParser jaxpParser = factory.newSAXParser();
+
+          reader = jaxpParser.getXMLReader();
+        }
+        catch (javax.xml.parsers.ParserConfigurationException ex)
+        {
+          throw new org.xml.sax.SAXException(ex);
+        }
+        catch (javax.xml.parsers.FactoryConfigurationError ex1)
+        {
+          throw new org.xml.sax.SAXException(ex1.toString());
+        }
+        catch (NoSuchMethodError ex2){}
+        catch (AbstractMethodError ame){}
+      }
+
+      if (null == reader)
+        reader = XMLReaderFactory.createXMLReader();
+
+      // If you set the namespaces to true, we'll end up getting double 
+      // xmlns attributes.  Needs to be fixed.  -sb
+      // reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+      reader.setContentHandler(builder);
+      reader.parse(isource);
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      if (m_errorListener != null)
+      {
+        try
+        {
+          m_errorListener.fatalError(new TransformerException(se));
+        }
+        catch (TransformerConfigurationException ex1)
+        {
+          throw ex1;
+        }
+        catch (TransformerException ex1)
+        {
+          throw new TransformerConfigurationException(ex1);
+        }
+      }
+      else
+      {
+        throw new TransformerConfigurationException(se.getMessage(), se);
+      }
+    }
+    catch (Exception e)
+    {
+      if (m_errorListener != null)
+      {
+        try
+        {
+          m_errorListener.fatalError(new TransformerException(e));
+          return null;
+        }
+        catch (TransformerConfigurationException ex1)
+        {
+          throw ex1;
+        }
+        catch (TransformerException ex1)
+        {
+          throw new TransformerConfigurationException(ex1);
+        }
+      }
+      else
+      {
+        throw new TransformerConfigurationException(e.getMessage(), e);
+      }
+    }
+
+    return builder.getTemplates();
+  }
+
+  /**
+   * The object that implements the URIResolver interface,
+   * or null.
+   */
+  URIResolver m_uriResolver;
+
+  /**
+   * Set an object that will be used to resolve URIs used in
+   * xsl:import, etc.  This will be used as the default for the
+   * transformation.
+   * @param resolver An object that implements the URIResolver interface,
+   * or null.
+   */
+  public void setURIResolver(URIResolver resolver)
+  {
+    m_uriResolver = resolver;
+  }
+
+  /**
+   * Get the object that will be used to resolve URIs used in
+   * xsl:import, etc.  This will be used as the default for the
+   * transformation.
+   *
+   * @return The URIResolver that was set with setURIResolver.
+   */
+  public URIResolver getURIResolver()
+  {
+    return m_uriResolver;
+  }
+
+  /** The error listener.   */
+  private ErrorListener m_errorListener = new org.apache.xml.utils.DefaultErrorHandler(false);
+
+  /**
+   * Get the error listener in effect for the TransformerFactory.
+   *
+   * @return A non-null reference to an error listener.
+   */
+  public ErrorListener getErrorListener()
+  {
+    return m_errorListener;
+  }
+
+  /**
+   * Set an error listener for the TransformerFactory.
+   *
+   * @param listener Must be a non-null reference to an ErrorListener.
+   *
+   * @throws IllegalArgumentException if the listener argument is null.
+   */
+  public void setErrorListener(ErrorListener listener)
+          throws IllegalArgumentException
+  {
+
+    if (null == listener)
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_ERRORLISTENER, null));
+      // "ErrorListener");
+
+    m_errorListener = listener;
+  }
+  
+  /**
+   * Return the state of the secure processing feature.
+   * 
+   * @return state of the secure processing feature.
+   */
+  public boolean isSecureProcessing()
+  {
+    return m_isSecureProcessing;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/WhitespaceInfoPaths.java b/src/main/java/org/apache/xalan/processor/WhitespaceInfoPaths.java
new file mode 100644
index 0000000..086345c
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/WhitespaceInfoPaths.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WhitespaceInfoPaths.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.Vector;
+
+import org.apache.xalan.templates.Stylesheet;
+import org.apache.xalan.templates.WhiteSpaceInfo;
+
+public class WhitespaceInfoPaths extends WhiteSpaceInfo
+{
+    static final long serialVersionUID = 5954766719577516723L;
+	
+  /**
+   * Bean property to allow setPropertiesFromAttributes to
+   * get the elements attribute.
+   */
+  private Vector m_elements;
+
+  /**
+   * Set from the elements attribute.  This is a list of 
+   * whitespace delimited element qualified names that specify
+   * preservation of whitespace.
+   *
+   * @param elems Should be a non-null reference to a list 
+   *              of {@link org.apache.xpath.XPath} objects.
+   */
+  public void setElements(Vector elems)
+  {
+    m_elements = elems;
+  }
+
+  /**
+   * Get the property set by setElements().  This is a list of 
+   * whitespace delimited element qualified names that specify
+   * preservation of whitespace.
+   *
+   * @return A reference to a list of {@link org.apache.xpath.XPath} objects, 
+   *         or null.
+   */
+  Vector getElements()
+  {
+    return m_elements;
+  }
+  
+  public void clearElements()
+  {
+  	m_elements = null;
+  }
+
+ /**
+   * Constructor WhitespaceInfoPaths
+   *
+   * @param thisSheet The current stylesheet
+   */
+  public WhitespaceInfoPaths(Stylesheet thisSheet)
+  {
+  	super(thisSheet);
+  	setStylesheet(thisSheet);
+  }
+
+
+}
+
diff --git a/src/main/java/org/apache/xalan/processor/XSLProcessorVersion.src b/src/main/java/org/apache/xalan/processor/XSLProcessorVersion.src
new file mode 100644
index 0000000..caae25d
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/XSLProcessorVersion.src
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLProcessorVersion.src 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+/**
+ * Administrative class to keep track of the version number of
+ * the Xalan release.
+ * <P>See also: org/apache/xalan/res/XSLTInfo.properties</P>
+ * @deprecated To be replaced by org.apache.xalan.Version.getVersion()
+ * @xsl.usage general
+ */
+public class XSLProcessorVersion
+{
+
+  /**
+   * Print the processor version to the command line.
+   *
+   * @param argv command line arguments, unused.
+   */
+  public static void main(String argv[])
+  {
+    System.out.println(S_VERSION);
+  }
+
+  /**
+   * Constant name of product.
+   */
+  public static final String PRODUCT = "Xalan";
+
+  /**
+   * Implementation Language.
+   */
+  public static final String LANGUAGE = "Java";
+
+  /**
+   * Major version number.
+   * Version number. This changes only when there is a
+   *          significant, externally apparent enhancement from
+   *          the previous release. 'n' represents the n'th
+   *          version.
+   *
+   *          Clients should carefully consider the implications
+   *          of new versions as external interfaces and behaviour
+   *          may have changed.
+   */
+  public static final int VERSION = @version.VERSION@;
+
+  /**
+   * Release Number.
+   * Release number. This changes when:
+   *            -  a new set of functionality is to be added, eg,
+   *               implementation of a new W3C specification.
+   *            -  API or behaviour change.
+   *            -  its designated as a reference release.
+   */
+  public static final int RELEASE = @version.RELEASE@;
+
+  /**
+   * Maintenance Drop Number.
+   * Optional identifier used to designate maintenance
+   *          drop applied to a specific release and contains
+   *          fixes for defects reported. It maintains compatibility
+   *          with the release and contains no API changes.
+   *          When missing, it designates the final and complete
+   *          development drop for a release.
+   */
+  public static final int MAINTENANCE = @version.MINOR@;
+
+  /**
+   * Development Drop Number.
+   * Optional identifier designates development drop of
+   *          a specific release. D01 is the first development drop
+   *          of a new release.
+   *
+   *          Development drops are works in progress towards a
+   *          compeleted, final release. A specific development drop
+   *          may not completely implement all aspects of a new
+   *          feature, which may take several development drops to
+   *          complete. At the point of the final drop for the
+   *          release, the D suffix will be omitted.
+   *
+   *          Each 'D' drops can contain functional enhancements as
+   *          well as defect fixes. 'D' drops may not be as stable as
+   *          the final releases.
+   */
+  public static final int DEVELOPMENT = 0;
+  
+  /**
+   * Version String like <CODE>"<B>Xalan</B> <B>Language</B>
+   * v.r[.dd| <B>D</B>nn]"</CODE>.
+   * <P>Semantics of the version string are identical to the Xerces project.</P>
+   */
+  public static final String S_VERSION = PRODUCT+" "+LANGUAGE+" "
+                                   +VERSION+"."+RELEASE+"."
+                                   +(DEVELOPMENT > 0 ? ("D"+DEVELOPMENT)
+                                     : (""+MAINTENANCE));
+
+}
diff --git a/src/main/java/org/apache/xalan/processor/XSLTAttributeDef.java b/src/main/java/org/apache/xalan/processor/XSLTAttributeDef.java
new file mode 100644
index 0000000..6e00c88
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/XSLTAttributeDef.java
@@ -0,0 +1,1665 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLTAttributeDef.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.AVT;
+import org.apache.xalan.templates.Constants;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.StringToIntTable;
+import org.apache.xml.utils.StringVector;
+import org.apache.xml.utils.XML11Char;
+import org.apache.xpath.XPath;
+
+ 
+/**
+ * This class defines an attribute for an element in a XSLT stylesheet,
+ * is meant to reflect the structure defined in http://www.w3.org/TR/xslt#dtd, and the
+ * mapping between Xalan classes and the markup attributes in the element.
+ */
+public class XSLTAttributeDef
+{
+   // How to handle invalid values for this attribute 
+   static final int FATAL = 0;
+   static final int ERROR = 1;
+   static final int WARNING = 2;
+   
+   
+  /**
+   * Construct an instance of XSLTAttributeDef.
+   *
+   * @param namespace The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param type One of T_CDATA, T_URL, T_AVT, T_PATTERN, T_EXPR, T_CHAR,
+   * T_NUMBER, T_YESNO, T_QNAME, T_QNAMES, T_ENUM, T_SIMPLEPATTERNLIST,
+   * T_NMTOKEN, T_STRINGLIST, T_PREFIX_URLLIST, T_ENUM_OR_PQNAME, T_NCNAME.
+   * @param required true if this is attribute is required by the XSLT specification.
+   * @param supportsAVT true if this attribute supports AVT's.
+   * @param errorType the type of error to issue if validation fails.  One of FATAL, ERROR, WARNING. 
+   */
+  XSLTAttributeDef(String namespace, String name, int type, boolean required, boolean supportsAVT, int errorType)
+  {
+    this.m_namespace = namespace;
+    this.m_name = name;
+    this.m_type = type;
+    this.m_required = required;
+    this.m_supportsAVT = supportsAVT;
+    this.m_errorType = errorType;
+  }
+
+  /**
+   * Construct an instance of XSLTAttributeDef.
+   *
+   * @param namespace The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param type One of T_CDATA, T_URL, T_AVT, T_PATTERN, T_EXPR,
+   * T_CHAR, T_NUMBER, T_YESNO, T_QNAME, T_QNAMES, T_ENUM,
+   * T_SIMPLEPATTERNLIST, T_NMTOKEN, T_STRINGLIST, T_PREFIX_URLLIST, 
+   * T_ENUM_OR_PQNAME, T_NCNAME.
+   * @param supportsAVT true if this attribute supports AVT's. 
+   * @param errorType the type of error to issue if validation fails.  One of FATAL, ERROR, WARNING. 
+   * @param defaultVal The default value for this attribute.
+   */
+  XSLTAttributeDef(String namespace, String name, int type, boolean supportsAVT, int errorType, String defaultVal)
+  {
+
+    this.m_namespace = namespace;
+    this.m_name = name;
+    this.m_type = type;
+    this.m_required = false;
+    this.m_supportsAVT = supportsAVT;  
+    this.m_errorType = errorType;      
+    this.m_default = defaultVal;
+   }
+
+  /**
+   * Construct an instance of XSLTAttributeDef that uses two
+   * enumerated values.
+   *
+   * @param namespace The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param required true if this attribute is required by the XSLT specification.
+   * @param supportsAVT true if this attribute supports AVT's.  
+   * @param prefixedQNameValAllowed If true, the type is T_ENUM_OR_PQNAME       
+   * @param errorType the type of error to issue if validation fails.  One of FATAL, ERROR, WARNING. 
+   * @param k1 The XSLT name of the enumerated value.
+   * @param v1 An integer representation of k1.
+   * @param k2 The XSLT name of the enumerated value.
+   * @param v2 An integer representation of k2.
+    */
+  XSLTAttributeDef(String namespace, String name, boolean required, boolean supportsAVT, 
+                    boolean prefixedQNameValAllowed, int errorType, String k1, int v1, String k2, int v2)
+  {
+
+    this.m_namespace = namespace;
+    this.m_name = name;
+	this.m_type = prefixedQNameValAllowed ? this.T_ENUM_OR_PQNAME : this.T_ENUM;    
+    this.m_required = required;
+    this.m_supportsAVT = supportsAVT;    
+    this.m_errorType = errorType;    
+    m_enums = new StringToIntTable(2);
+
+    m_enums.put(k1, v1);
+    m_enums.put(k2, v2);
+  }
+
+  /**
+   * Construct an instance of XSLTAttributeDef that uses three
+   * enumerated values.
+   *
+   * @param namespace The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param required true if this attribute is required by the XSLT specification.
+   * @param supportsAVT true if this attribute supports AVT's.
+   * @param prefixedQNameValAllowed If true, the type is T_ENUM_OR_PQNAME
+   * @param errorType the type of error to issue if validation fails.  One of FATAL, ERROR, WARNING.    * 
+   * @param k1 The XSLT name of the enumerated value.
+   * @param v1 An integer representation of k1.
+   * @param k2 The XSLT name of the enumerated value.
+   * @param v2 An integer representation of k2.
+   * @param k3 The XSLT name of the enumerated value.
+   * @param v3 An integer representation of k3.
+   */
+  XSLTAttributeDef(String namespace, String name, boolean required, boolean supportsAVT,
+                    boolean prefixedQNameValAllowed, int errorType, String k1, int v1, String k2, int v2, String k3, int v3)
+  {
+
+    this.m_namespace = namespace;
+    this.m_name = name;
+	this.m_type = prefixedQNameValAllowed ? this.T_ENUM_OR_PQNAME : this.T_ENUM;    
+    this.m_required = required;
+    this.m_supportsAVT = supportsAVT; 
+    this.m_errorType = errorType;      
+    m_enums = new StringToIntTable(3);
+
+    m_enums.put(k1, v1);
+    m_enums.put(k2, v2);
+    m_enums.put(k3, v3);
+  }
+
+  /**
+   * Construct an instance of XSLTAttributeDef that uses three
+   * enumerated values.
+   *
+   * @param namespace The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param required true if this attribute is required by the XSLT specification.
+   * @param supportsAVT true if this attribute supports AVT's.
+   * @param prefixedQNameValAllowed If true, the type is T_ENUM_OR_PQNAME
+   * @param errorType the type of error to issue if validation fails.  One of FATAL, ERROR, WARNING.    * @param k1 The XSLT name of the enumerated value.
+   * @param v1 An integer representation of k1.
+   * @param k2 The XSLT name of the enumerated value.
+   * @param v2 An integer representation of k2.
+   * @param k3 The XSLT name of the enumerated value.
+   * @param v3 An integer representation of k3.
+   * @param k4 The XSLT name of the enumerated value.
+   * @param v4 An integer representation of k4.
+   */
+  XSLTAttributeDef(String namespace, String name, boolean required, boolean supportsAVT,
+                   boolean prefixedQNameValAllowed, int errorType, String k1, int v1, String k2, int v2, 
+                   String k3, int v3, String k4, int v4)
+  {
+
+    this.m_namespace = namespace;
+    this.m_name = name;
+	this.m_type = prefixedQNameValAllowed ? this.T_ENUM_OR_PQNAME : this.T_ENUM;    
+    this.m_required = required;
+    this.m_supportsAVT = supportsAVT;      
+    this.m_errorType = errorType; 
+    m_enums = new StringToIntTable(4);
+
+    m_enums.put(k1, v1);
+    m_enums.put(k2, v2);
+    m_enums.put(k3, v3);
+    m_enums.put(k4, v4);
+  }
+
+  /** Type values that represent XSLT attribute types. */
+  static final int T_CDATA = 1,
+
+  // <!-- Used for the type of an attribute value that is a URI reference.-->
+  T_URL = 2,
+
+  // <!-- Used for the type of an attribute value that is an
+  // attribute value template.-->
+  T_AVT = 3,  // Attribute Value Template
+
+  // <!-- Used for the type of an attribute value that is a pattern.-->
+  T_PATTERN = 4,
+
+  // <!-- Used for the type of an attribute value that is an expression.-->
+  T_EXPR = 5,
+
+  // <!-- Used for the type of an attribute value that consists
+  // of a single character.-->
+  T_CHAR = 6,
+
+  // <!-- Used for the type of an attribute value that is a number. -->
+  T_NUMBER = 7,
+
+  // Used for boolean values
+  T_YESNO = 8,
+
+  // <!-- Used for the type of an attribute value that is a QName; the prefix
+  // gets expanded by the XSLT processor. -->
+  T_QNAME = 9,
+
+  // <!--Used for a whitespace-separated list of QNames where the non-prefixed
+  // entries are not to be placed in the default namespace. -->
+  T_QNAMES = 10,
+
+  // <!-- Used for enumerated values -->
+  T_ENUM = 11,
+
+  // Used for simple match patterns, i.e. xsl:strip-space spec.
+  T_SIMPLEPATTERNLIST = 12,
+
+  // Used for a known token.
+  T_NMTOKEN = 13,
+
+  // Used for a list of white-space delimited strings.
+  T_STRINGLIST = 14,
+
+  // Used for a list of white-space delimited strings.
+  // Prefixes are checked to make sure they refer to 
+  // valid namespaces, and are resolved when processed
+  T_PREFIX_URLLIST = 15,
+  
+  // Used for enumerated values, one of which could be a qname-but-not-ncname
+  T_ENUM_OR_PQNAME = 16,
+
+  // Used for the type of an attribute value that is a NCName
+  T_NCNAME = 17,
+  
+  // Used for QName attributes that are always AVT.  Prefix isn't resolved.
+  T_AVT_QNAME = 18,
+  
+  // Used for a list of QNames where non-prefixed items are to be resolved
+  // using the default namespace (This is only true for cdata-section-elements)
+  T_QNAMES_RESOLVE_NULL = 19,
+  
+  // Used for a list of white-space delimited strings.
+  // strings are checked to make sure they are valid 
+  // prefixes, and are not expanded when processed. 
+  T_PREFIXLIST = 20;
+
+  /** Representation for an attribute in a foreign namespace. */
+  static final XSLTAttributeDef m_foreignAttr = new XSLTAttributeDef("*", "*",
+                                            XSLTAttributeDef.T_CDATA,false, false, WARNING);
+
+  /** Method name that objects may implement if they wish to have forein attributes set. */
+  static final String S_FOREIGNATTR_SETTER = "setForeignAttr";
+
+  /**
+   * The allowed namespace for this element.
+   */
+  private String m_namespace;
+
+  /**
+   * Get the allowed namespace for this attribute.
+   *
+   * @return The allowed namespace for this attribute, which may be null, or may be "*".
+   */
+  String getNamespace()
+  {
+    return m_namespace;
+  }
+
+  /**
+   * The name of this element.
+   */
+  private String m_name;
+
+  /**
+   * Get the name of this attribute.
+   *
+   * @return non-null reference to the name of this attribute, which may be "*".
+   */
+  String getName()
+  {
+    return m_name;
+  }
+
+  /**
+   * The type of this attribute value.
+   */
+  private int m_type;
+
+  /**
+   * Get the type of this attribute value.
+   *
+   * @return One of T_CDATA, T_URL, T_AVT, T_PATTERN, T_EXPR, T_CHAR,
+   * T_NUMBER, T_YESNO, T_QNAME, T_QNAMES, T_ENUM, T_SIMPLEPATTERNLIST,
+   * T_NMTOKEN, T_STRINGLIST, T_PREFIX_URLLIST, T_ENUM_OR_PQNAME.
+   */
+  int getType()
+  {
+    return m_type;
+  }
+
+  /**
+   * If this element is of type T_ENUM, this will contain
+   * a map from the attribute string to the Xalan integer
+   * value.
+   */
+  private StringToIntTable m_enums;
+
+  /**
+   * If this element is of type T_ENUM, this will return
+   * a map from the attribute string to the Xalan integer
+   * value.
+   * @param key The XSLT attribute value.
+   *
+   * @return The integer representation of the enumerated value for this attribute.
+   * @throws Throws NullPointerException if m_enums is null.
+   */
+  private int getEnum(String key)
+  {
+    return m_enums.get(key);
+  }
+
+ /**
+   * If this element is of type T_ENUM, this will return
+   * an array of strings - the values in the enumeration
+   *
+   * @return An array of the enumerated values permitted for this attribute.
+   *
+   * @throws Throws NullPointerException if m_enums is null.
+   */
+  private String[] getEnumNames()
+  {
+    return m_enums.keys();
+  }
+
+  /**
+   * The default value for this attribute.
+   */
+  private String m_default;
+
+  /**
+   * Get the default value for this attribute.
+   *
+   * @return The default value for this attribute, or null.
+   */
+  String getDefault()
+  {
+    return m_default;
+  }
+
+  /**
+   * Set the default value for this attribute.
+   *
+   * @param def String representation of the default value for this attribute.
+   */
+  void setDefault(String def)
+  {
+    m_default = def;
+  }
+
+  /**
+   * If true, this is a required attribute.
+   */
+  private boolean m_required;
+
+  /**
+   * Get whether or not this is a required attribute.
+   *
+   * @return true if this is a required attribute.
+   */
+  boolean getRequired()
+  {
+    return m_required;
+  }
+
+  /**
+   * If true, this is attribute supports AVT's.
+   */
+  private boolean m_supportsAVT;
+
+  /**
+   * Get whether or not this attribute supports AVT's.
+   *
+   * @return true if this attribute supports AVT's.
+   */
+  boolean getSupportsAVT()
+  {
+    return m_supportsAVT;
+  }
+  
+  int m_errorType = this.WARNING;
+  
+  /**
+   * Get the type of error message to use if the attribute value is invalid.
+   *
+   * @return one of XSLAttributeDef.FATAL, XSLAttributeDef.ERROR, XSLAttributeDef.WARNING
+   */
+  int getErrorType()
+  {
+    return m_errorType;
+  }
+  /**
+   * String that should represent the setter method which which
+   * may be used on objects to set a value that represents this attribute  
+   */
+  String m_setterString = null;
+
+  /**
+   * Return a string that should represent the setter method.
+   * The setter method name will be created algorithmically the
+   * first time this method is accessed, and then cached for return
+   * by subsequent invocations of this method.
+   *
+   * @return String that should represent the setter method which which
+   * may be used on objects to set a value that represents this attribute,
+   * of null if no setter method should be called.
+   */
+  public String getSetterMethodName()
+  {
+
+    if (null == m_setterString)
+    {
+      if (m_foreignAttr == this)
+      {
+        return S_FOREIGNATTR_SETTER;
+      }
+      else if (m_name.equals("*"))
+      {
+        m_setterString = "addLiteralResultAttribute";
+
+        return m_setterString;
+      }
+
+      StringBuffer outBuf = new StringBuffer();
+
+      outBuf.append("set");
+
+      if ((m_namespace != null)
+              && m_namespace.equals(Constants.S_XMLNAMESPACEURI))
+      {
+        outBuf.append("Xml");
+      }
+
+      int n = m_name.length();
+
+      for (int i = 0; i < n; i++)
+      {
+        char c = m_name.charAt(i);
+
+        if ('-' == c)
+        {
+          i++;
+
+          c = m_name.charAt(i);
+          c = Character.toUpperCase(c);
+        }
+        else if (0 == i)
+        {
+          c = Character.toUpperCase(c);
+        }
+
+        outBuf.append(c);
+      }
+
+      m_setterString = outBuf.toString();
+    }
+
+    return m_setterString;
+  }
+
+  /**
+   * Process an attribute string of type T_AVT into
+   * a AVT value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value Should be an Attribute Value Template string.
+   *
+   * @return An AVT object that may be used to evaluate the Attribute Value Template.
+   *
+   * @throws org.xml.sax.SAXException which will wrap a
+   * {@link javax.xml.transform.TransformerException}, if there is a syntax error
+   * in the attribute value template string.
+   */
+  AVT processAVT(
+          StylesheetHandler handler, String uri, String name, String rawName, String value,
+          ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      AVT avt = new AVT(handler, uri, name, rawName, value, owner);
+
+      return avt;
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * Process an attribute string of type T_CDATA into
+   * a String value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value non-null string reference.
+   *
+   * @return The value argument.
+   * 
+   * @throws org.xml.sax.SAXException.
+   */
+  Object processCDATA(StylesheetHandler handler, String uri, String name,
+                      String rawName, String value, ElemTemplateElement owner)
+                      throws org.xml.sax.SAXException
+  {
+  	if (getSupportsAVT()) {
+	    try
+	    {
+	      AVT avt = new AVT(handler, uri, name, rawName, value, owner);
+	      return avt;
+	    }
+	    catch (TransformerException te)
+	    {
+	      throw new org.xml.sax.SAXException(te);
+	    }  		
+  	} else {  	  	
+	    return value;
+  	}
+  }
+
+  /**
+   * Process an attribute string of type T_CHAR into
+   * a Character value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value Should be a string with a length of 1.
+   *
+   * @return Character object.
+   *
+   * @throws org.xml.sax.SAXException if the string is not a length of 1.
+   */
+  Object processCHAR(
+          StylesheetHandler handler, String uri, String name, String rawName, String value, ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+	if (getSupportsAVT()) {
+	    try
+	    {
+	      AVT avt = new AVT(handler, uri, name, rawName, value, owner);
+	
+		  // If an AVT wasn't used, validate the value
+		  if ((avt.isSimple()) && (value.length() != 1)) {
+		  	handleError(handler, XSLTErrorResources.INVALID_TCHAR, new Object[] {name, value},null);
+            return null;
+		  }	
+	      return avt;
+	    }
+	    catch (TransformerException te)
+	    {
+	      throw new org.xml.sax.SAXException(te);
+	    }
+	} else {    
+	    if (value.length() != 1)
+	    {
+            handleError(handler, XSLTErrorResources.INVALID_TCHAR, new Object[] {name, value},null);
+            return null;
+	    }
+
+	    return new Character(value.charAt(0));
+	}
+  }
+
+  /**
+   * Process an attribute string of type T_ENUM into a int value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value non-null string that represents an enumerated value that is
+   * valid for this element.
+   * @param owner
+   *
+   * @return An Integer representation of the enumerated value if this attribute does not support
+   *         AVT.  Otherwise, and AVT is returned.
+   */
+  Object processENUM(StylesheetHandler handler, String uri, String name,
+                     String rawName, String value, ElemTemplateElement owner)
+                     throws org.xml.sax.SAXException
+  {
+
+	AVT avt = null;
+	if (getSupportsAVT()) {
+	    try
+	    {
+	      avt = new AVT(handler, uri, name, rawName, value, owner);
+	      
+	      // If this attribute used an avt, then we can't validate at this time.
+	      if (!avt.isSimple()) return avt;
+	    }
+	    catch (TransformerException te)
+	    {
+	      throw new org.xml.sax.SAXException(te);
+	    }
+	}    
+	
+    int retVal = this.getEnum(value);
+    
+	if (retVal == StringToIntTable.INVALID_KEY) 
+    {
+       StringBuffer enumNamesList = getListOfEnums();
+       handleError(handler, XSLTErrorResources.INVALID_ENUM,new Object[]{name, value, enumNamesList.toString() },null);
+       return null;
+    }
+
+	if (getSupportsAVT()) return avt;
+	else return new Integer(retVal);	
+
+  }
+
+  /**
+   * Process an attribute string of that is either an enumerated value or a qname-but-not-ncname.
+   * Returns an AVT, if this attribute support AVT; otherwise returns int or qname.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value non-null string that represents an enumerated value that is
+   * valid for this element.
+   * @param owner
+   *
+   * @return AVT if attribute supports AVT. An Integer representation of the enumerated value if
+   *         attribute does not support AVT and an enumerated value was used.  Otherwise a qname
+   *         is returned.
+   */
+  Object processENUM_OR_PQNAME(StylesheetHandler handler, String uri, String name,
+                     String rawName, String value, ElemTemplateElement owner)
+                     throws org.xml.sax.SAXException
+  {
+
+	Object objToReturn = null;
+	
+	if (getSupportsAVT()) {
+	    try
+	    {
+	      AVT avt = new AVT(handler, uri, name, rawName, value, owner);
+	      if (!avt.isSimple()) return avt;
+	      else objToReturn = avt;
+	    }  
+	    catch (TransformerException te)
+	    {
+	      throw new org.xml.sax.SAXException(te);
+	    }
+	}    
+	
+    // An avt wasn't used.
+  	int key = this.getEnum(value);
+    
+    if (key != StringToIntTable.INVALID_KEY) 
+    {
+        if (objToReturn == null) objToReturn = new Integer(key);
+    }
+
+    // enum not used.  Validate qname-but-not-ncname.
+    else
+    {
+        try 
+        {
+			QName qname = new QName(value, handler, true);
+            if (objToReturn == null) objToReturn = qname;	
+	        
+			if (qname.getPrefix() == null) {
+	           StringBuffer enumNamesList = getListOfEnums();
+
+ 	           enumNamesList.append(" <qname-but-not-ncname>");
+               handleError(handler,XSLTErrorResources.INVALID_ENUM,new Object[]{name, value, enumNamesList.toString() },null); 
+               return null;
+        
+	        }            
+        }
+        catch (IllegalArgumentException ie) 
+        {
+           StringBuffer enumNamesList = getListOfEnums();
+           enumNamesList.append(" <qname-but-not-ncname>");
+           
+           handleError(handler,XSLTErrorResources.INVALID_ENUM,new Object[]{name, value, enumNamesList.toString() },ie); 
+           return null;
+
+        }
+        catch (RuntimeException re)
+        {
+           StringBuffer enumNamesList = getListOfEnums();
+           enumNamesList.append(" <qname-but-not-ncname>");
+
+           handleError(handler,XSLTErrorResources.INVALID_ENUM,new Object[]{name, value, enumNamesList.toString() },re); 
+           return null;
+        }    
+  	}
+  	
+  	return objToReturn;
+  }
+
+  /**
+   * Process an attribute string of type T_EXPR into
+   * an XPath value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value An XSLT expression string.
+   *
+   * @return an XPath object that may be used for evaluation.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if the expression
+   * string contains a syntax error.
+   */
+  Object processEXPR(
+          StylesheetHandler handler, String uri, String name, String rawName, String value,
+          ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      XPath expr = handler.createXPath(value, owner);
+
+      return expr;
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * Process an attribute string of type T_NMTOKEN into
+   * a String value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A NMTOKEN string.
+   *
+   * @return the value argument or an AVT if this attribute supports AVTs.
+   * 
+   * @throws org.xml.sax.SAXException if the value is not a valid nmtoken
+   */
+  Object processNMTOKEN(StylesheetHandler handler, String uri, String name,
+                        String rawName, String value, ElemTemplateElement owner)
+             throws org.xml.sax.SAXException
+  {
+  	
+  	if (getSupportsAVT()) {
+	    try
+	    {
+	      AVT avt = new AVT(handler, uri, name, rawName, value, owner);
+	
+		  // If an AVT wasn't used, validate the value
+		  if ((avt.isSimple()) && (!XML11Char.isXML11ValidNmtoken(value))) {
+            handleError(handler,XSLTErrorResources.INVALID_NMTOKEN, new Object[] {name,value},null);
+            return null;
+		  }	
+	      return avt;
+	    }
+	    catch (TransformerException te)
+	    {
+	      throw new org.xml.sax.SAXException(te);
+	    }  		
+  	} else {
+  		if (!XML11Char.isXML11ValidNmtoken(value)) {
+            handleError(handler,XSLTErrorResources.INVALID_NMTOKEN, new Object[] {name,value},null);
+            return null;
+  		}
+  	}	  			
+    return value;
+  }
+
+  /**
+   * Process an attribute string of type T_PATTERN into
+   * an XPath match pattern value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A match pattern string.
+   *
+   * @return An XPath pattern that may be used to evaluate the XPath.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if the match pattern
+   * string contains a syntax error.
+   */
+  Object processPATTERN(
+          StylesheetHandler handler, String uri, String name, String rawName, String value,
+          ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      XPath pattern = handler.createMatchPatternXPath(value, owner);
+
+      return pattern;
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * Process an attribute string of type T_NUMBER into
+   * a double value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A string that can be parsed into a double value.
+   * @param number
+   *
+   * @return A Double object.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException}
+   * if the string does not contain a parsable number.
+   */
+  Object processNUMBER(
+          StylesheetHandler handler, String uri, String name, String rawName, String value, ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+
+	if (getSupportsAVT()) 
+	{
+		Double val;
+		AVT avt = null;
+	    try
+	    {
+	      avt = new AVT(handler, uri, name, rawName, value, owner);
+	      
+	      // If this attribute used an avt, then we can't validate at this time.
+	      if (avt.isSimple()) 
+	      {
+	      	val = Double.valueOf(value);
+	      }
+	    }
+	    catch (TransformerException te)
+	    {
+	      throw new org.xml.sax.SAXException(te);
+	    } 
+	    catch (NumberFormatException nfe)
+	    {
+	     	handleError(handler,XSLTErrorResources.INVALID_NUMBER, new Object[] {name, value}, nfe);
+            return null;
+	    }
+	    return avt;
+	
+	} 
+	else
+    {
+	    try
+	    {
+	      return Double.valueOf(value);
+	    }
+	    catch (NumberFormatException nfe)
+	    {
+            handleError(handler,XSLTErrorResources.INVALID_NUMBER, new Object[] {name, value}, nfe);
+            return null;
+	    }
+    }    
+  }
+
+  /**
+   * Process an attribute string of type T_QNAME into a QName value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A string that represents a potentially prefix qualified name.
+   * @param owner
+   *
+   * @return A QName object if this attribute does not support AVT's.  Otherwise, an AVT
+   *         is returned.
+   *
+   * @throws org.xml.sax.SAXException if the string contains a prefix that can not be
+   * resolved, or the string contains syntax that is invalid for a qualified name.
+   */
+  Object processQNAME(
+          StylesheetHandler handler, String uri, String name, String rawName, String value, ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+     try 
+        {	
+   	      QName qname = new QName(value, handler, true);
+          return qname;
+        }
+        catch (IllegalArgumentException ie)
+        {
+            // thrown by QName constructor
+            handleError(handler,XSLTErrorResources.INVALID_QNAME, new Object[] {name, value},ie);
+            return null;
+        }
+        catch (RuntimeException re) {
+            // thrown by QName constructor
+            handleError(handler,XSLTErrorResources.INVALID_QNAME, new Object[] {name, value},re);
+            return null;
+        }
+  	}
+ 
+
+  /**
+   * Process an attribute string of type T_QNAME into a QName value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A string that represents a potentially prefix qualified name.
+   * @param owner
+   *
+   * @return An AVT is returned.
+   *
+   * @throws org.xml.sax.SAXException if the string contains a prefix that can not be
+   * resolved, or the string contains syntax that is invalid for a qualified name.
+   */
+  Object processAVT_QNAME(
+          StylesheetHandler handler, String uri, String name, String rawName, String value, ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+       AVT avt = null;
+       try
+       {
+          avt = new AVT(handler, uri, name, rawName, value, owner);
+    
+          // If an AVT wasn't used, validate the value
+          if (avt.isSimple())
+          {
+             int indexOfNSSep = value.indexOf(':');
+
+             if (indexOfNSSep >= 0) 
+             {   
+                  String prefix = value.substring(0, indexOfNSSep);
+                  if (!XML11Char.isXML11ValidNCName(prefix))
+                  {
+                     handleError(handler,XSLTErrorResources.INVALID_QNAME,new Object[]{name,value },null);
+                     return null;
+                  }
+             }
+                 
+             String localName =  (indexOfNSSep < 0)
+                 ? value : value.substring(indexOfNSSep + 1); 
+             
+             if ((localName == null) || (localName.length() == 0) ||
+                 (!XML11Char.isXML11ValidNCName(localName)))
+             {    
+                     handleError(handler,XSLTErrorResources.INVALID_QNAME,new Object[]{name,value },null );
+                     return null;
+             }
+          }  
+        }
+        catch (TransformerException te)
+        {
+           // thrown by AVT constructor
+          throw new org.xml.sax.SAXException(te);
+        } 
+    
+    return avt;
+ }
+
+  /**
+   * Process an attribute string of type NCName into a String
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A string that represents a potentially prefix qualified name.
+   * @param owner
+   *
+   * @return A String object if this attribute does not support AVT's.  Otherwise, an AVT
+   *         is returned.
+   *
+   * @throws org.xml.sax.SAXException if the string contains a prefix that can not be
+   * resolved, or the string contains syntax that is invalid for a NCName.
+   */
+  Object processNCNAME(
+          StylesheetHandler handler, String uri, String name, String rawName, String value, ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+    
+    if (getSupportsAVT()) 
+    {
+        AVT avt = null;
+        try
+        {
+          avt = new AVT(handler, uri, name, rawName, value, owner);
+    
+          // If an AVT wasn't used, validate the value
+          if ((avt.isSimple()) &&  (!XML11Char.isXML11ValidNCName(value))) 
+          {
+             handleError(handler,XSLTErrorResources.INVALID_NCNAME,new Object[] {name,value},null);
+             return null;
+          }      
+          return avt;
+        }
+        catch (TransformerException te)
+        {
+           // thrown by AVT constructor
+          throw new org.xml.sax.SAXException(te);
+        } 
+        
+    } else {
+        if (!XML11Char.isXML11ValidNCName(value)) 
+        {
+            handleError(handler,XSLTErrorResources.INVALID_NCNAME,new Object[] {name,value},null);
+            return null;
+        }
+        return value;
+    }
+ }
+
+  /**
+   * Process an attribute string of type T_QNAMES into a vector of QNames where
+   * the specification requires that non-prefixed elements not be placed in a
+   * namespace.  (See section 2.4 of XSLT 1.0.)
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A whitespace delimited list of qualified names.
+   *
+   * @return a Vector of QName objects.
+   *
+   * @throws org.xml.sax.SAXException if the one of the qualified name strings
+   * contains a prefix that can not be
+   * resolved, or a qualified name contains syntax that is invalid for a qualified name.
+   */
+  Vector processQNAMES(
+          StylesheetHandler handler, String uri, String name, String rawName, String value)
+            throws org.xml.sax.SAXException
+  {
+
+    StringTokenizer tokenizer = new StringTokenizer(value, " \t\n\r\f");
+    int nQNames = tokenizer.countTokens();
+    Vector qnames = new Vector(nQNames);
+
+    for (int i = 0; i < nQNames; i++)
+    {
+      // Fix from Alexander Rudnev
+      qnames.addElement(new QName(tokenizer.nextToken(), handler));
+    }
+
+    return qnames;
+  }
+
+ /**
+   * Process an attribute string of type T_QNAMES_RESOLVE_NULL into a vector
+   * of QNames where the specification requires non-prefixed elements to be
+   * placed in the default namespace.  (See section 16 of XSLT 1.0; the
+   * <em>only</em> time that this will get called is for the
+   * <code>cdata-section-elements</code> attribute on <code>xsl:output</code>.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A whitespace delimited list of qualified names.
+   *
+   * @return a Vector of QName objects.
+   *
+   * @throws org.xml.sax.SAXException if the one of the qualified name strings
+   * contains a prefix that can not be resolved, or a qualified name contains
+   * syntax that is invalid for a qualified name.
+   */
+  final Vector processQNAMESRNU(StylesheetHandler handler, String uri,
+    String name, String rawName, String value)
+    throws org.xml.sax.SAXException
+  {
+
+    StringTokenizer tokenizer = new StringTokenizer(value, " \t\n\r\f");
+    int nQNames = tokenizer.countTokens();
+    Vector qnames = new Vector(nQNames);
+
+    String defaultURI = handler.getNamespaceForPrefix("");
+    for (int i = 0; i < nQNames; i++)
+    {
+      String tok = tokenizer.nextToken();
+      if (tok.indexOf(':') == -1) {
+        qnames.addElement(new QName(defaultURI,tok));
+      } else {
+        qnames.addElement(new QName(tok, handler));
+      }
+    }
+    return qnames;
+  }
+
+  /**
+   * Process an attribute string of type T_SIMPLEPATTERNLIST into
+   * a vector of XPath match patterns.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A whitespace delimited list of simple match patterns.
+   *
+   * @return A Vector of XPath objects.
+   *
+   * @throws org.xml.sax.SAXException that wraps a
+   * {@link javax.xml.transform.TransformerException} if one of the match pattern
+   * strings contains a syntax error.
+   */
+  Vector processSIMPLEPATTERNLIST(
+          StylesheetHandler handler, String uri, String name, String rawName, String value,
+          ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      StringTokenizer tokenizer = new StringTokenizer(value, " \t\n\r\f");
+      int nPatterns = tokenizer.countTokens();
+      Vector patterns = new Vector(nPatterns);
+
+      for (int i = 0; i < nPatterns; i++)
+      {
+        XPath pattern =
+          handler.createMatchPatternXPath(tokenizer.nextToken(), owner);
+
+        patterns.addElement(pattern);
+      }
+
+      return patterns;
+    }
+    catch (TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+
+  /**
+   * Process an attribute string of type T_STRINGLIST into
+   * a vector of XPath match patterns.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value a whitespace delimited list of string values.
+   *
+   * @return A StringVector of the tokenized strings.
+   */
+  StringVector processSTRINGLIST(StylesheetHandler handler, String uri,
+                                 String name, String rawName, String value)
+  {
+
+    StringTokenizer tokenizer = new StringTokenizer(value, " \t\n\r\f");
+    int nStrings = tokenizer.countTokens();
+    StringVector strings = new StringVector(nStrings);
+
+    for (int i = 0; i < nStrings; i++)
+    {
+      strings.addElement(tokenizer.nextToken());
+    }
+
+    return strings;
+  }
+
+  /**
+   * Process an attribute string of type T_URLLIST into
+   * a vector of prefixes that may be resolved to URLs.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A list of whitespace delimited prefixes.
+   *
+   * @return A vector of strings that may be resolved to URLs.
+   *
+   * @throws org.xml.sax.SAXException if one of the prefixes can not be resolved.
+   */
+  StringVector processPREFIX_URLLIST(
+          StylesheetHandler handler, String uri, String name, String rawName, String value)
+            throws org.xml.sax.SAXException
+  {
+
+    StringTokenizer tokenizer = new StringTokenizer(value, " \t\n\r\f");
+    int nStrings = tokenizer.countTokens();
+    StringVector strings = new StringVector(nStrings);
+
+    for (int i = 0; i < nStrings; i++)
+    {
+      String prefix = tokenizer.nextToken();
+      String url = handler.getNamespaceForPrefix(prefix);
+
+      if (url != null)
+        strings.addElement(url);
+      else
+        throw new org.xml.sax.SAXException(XSLMessages.createMessage(XSLTErrorResources.ER_CANT_RESOLVE_NSPREFIX, new Object[] {prefix}));
+    
+    }
+
+    return strings;
+  }
+
+  /**
+    * Process an attribute string of type T_PREFIXLIST into
+    * a vector of prefixes that may be resolved to URLs.
+    *
+    * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+    * @param uri The Namespace URI, or an empty string.
+    * @param name The local name (without prefix), or empty string if not namespace processing.
+    * @param rawName The qualified name (with prefix).
+    * @param value A list of whitespace delimited prefixes.
+    *
+    * @return A vector of strings that may be resolved to URLs.
+    *
+    * @throws org.xml.sax.SAXException if one of the prefixes can not be resolved.
+    */
+   StringVector processPREFIX_LIST(
+           StylesheetHandler handler, String uri, String name, 
+           String rawName, String value) throws org.xml.sax.SAXException
+   {
+    
+     StringTokenizer tokenizer = new StringTokenizer(value, " \t\n\r\f");
+     int nStrings = tokenizer.countTokens();
+     StringVector strings = new StringVector(nStrings);
+
+     for (int i = 0; i < nStrings; i++)
+     {
+       String prefix = tokenizer.nextToken();
+       String url = handler.getNamespaceForPrefix(prefix);
+       if (prefix.equals(Constants.ATTRVAL_DEFAULT_PREFIX) || url != null)
+         strings.addElement(prefix);
+       else
+         throw new org.xml.sax.SAXException(
+              XSLMessages.createMessage(
+                   XSLTErrorResources.ER_CANT_RESOLVE_NSPREFIX, 
+                   new Object[] {prefix}));
+    
+     }
+
+     return strings;
+   }
+
+
+  /**
+   * Process an attribute string of type T_URL into
+   * a URL value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value non-null string that conforms to the URL syntax.
+   *
+   * @return The non-absolutized URL argument, in other words, the value argument.  If this 
+   *         attribute supports AVT, an AVT is returned.
+   *
+   * @throws org.xml.sax.SAXException if the URL does not conform to the URL syntax.
+   */
+  Object processURL(
+          StylesheetHandler handler, String uri, String name, String rawName, String value, ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+    if (getSupportsAVT()) {
+	    try
+	    {
+	      AVT avt = new AVT(handler, uri, name, rawName, value, owner);
+	
+		  // If an AVT wasn't used, validate the value
+		 // if (avt.getSimpleString() != null) {
+			   // TODO: syntax check URL value.
+			    // return SystemIDResolver.getAbsoluteURI(value, 
+			    //                                         handler.getBaseIdentifier());
+		  //}	
+	      return avt;
+	    }
+	    catch (TransformerException te)
+	    {
+	      throw new org.xml.sax.SAXException(te);
+	    }  		
+     } else {
+    // TODO: syntax check URL value.
+    // return SystemIDResolver.getAbsoluteURI(value, 
+    //                                         handler.getBaseIdentifier());
+     	
+	    return value;
+    }
+  }
+
+  /**
+   * Process an attribute string of type T_YESNO into
+   * a Boolean value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value A string that should be "yes" or "no".
+   *
+   * @return Boolean object representation of the value.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  private Boolean processYESNO(
+          StylesheetHandler handler, String uri, String name, String rawName, String value)
+            throws org.xml.sax.SAXException
+  {
+
+    // Is this already checked somewhere else?  -sb
+    if (!(value.equals("yes") || value.equals("no")))
+    {
+      handleError(handler, XSLTErrorResources.INVALID_BOOLEAN, new Object[] {name,value}, null);
+      return null;
+   }
+ 
+     return new Boolean(value.equals("yes") ? true : false);
+  }
+
+  /**
+   * Process an attribute value.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param name The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param value The unprocessed string value of the attribute.
+   *
+   * @return The processed Object representation of the attribute.
+   *
+   * @throws org.xml.sax.SAXException if the attribute value can not be processed.
+   */
+  Object processValue(
+          StylesheetHandler handler, String uri, String name, String rawName, String value,
+          ElemTemplateElement owner)
+            throws org.xml.sax.SAXException
+  {
+
+    int type = getType();
+    Object processedValue = null;
+
+    switch (type)
+    {
+    case T_AVT :
+      processedValue = processAVT(handler, uri, name, rawName, value, owner);
+      break;
+    case T_CDATA :
+      processedValue = processCDATA(handler, uri, name, rawName, value, owner);
+      break;
+    case T_CHAR :
+      processedValue = processCHAR(handler, uri, name, rawName, value, owner);
+      break;
+    case T_ENUM :
+      processedValue = processENUM(handler, uri, name, rawName, value, owner);
+      break;
+    case T_EXPR :
+      processedValue = processEXPR(handler, uri, name, rawName, value, owner);
+      break;
+    case T_NMTOKEN :
+      processedValue = processNMTOKEN(handler, uri, name, rawName, value, owner);
+      break;
+    case T_PATTERN :
+      processedValue = processPATTERN(handler, uri, name, rawName, value, owner);
+      break;
+    case T_NUMBER :
+      processedValue = processNUMBER(handler, uri, name, rawName, value, owner);
+      break;
+    case T_QNAME :
+      processedValue = processQNAME(handler, uri, name, rawName, value, owner);
+      break;
+    case T_QNAMES :
+      processedValue = processQNAMES(handler, uri, name, rawName, value);
+      break;
+	case T_QNAMES_RESOLVE_NULL:
+      processedValue = processQNAMESRNU(handler, uri, name, rawName, value);
+      break;
+    case T_SIMPLEPATTERNLIST :
+      processedValue = processSIMPLEPATTERNLIST(handler, uri, name, rawName,
+                                                value, owner);
+      break;
+    case T_URL :
+      processedValue = processURL(handler, uri, name, rawName, value, owner);
+      break;
+    case T_YESNO :
+      processedValue = processYESNO(handler, uri, name, rawName, value);
+      break;
+    case T_STRINGLIST :
+      processedValue = processSTRINGLIST(handler, uri, name, rawName, value);
+      break;
+    case T_PREFIX_URLLIST :
+      processedValue = processPREFIX_URLLIST(handler, uri, name, rawName,
+                                             value);
+      break;
+    case T_ENUM_OR_PQNAME :
+    	processedValue = processENUM_OR_PQNAME(handler, uri, name, rawName, value, owner);
+    	break;
+    case T_NCNAME :
+        processedValue = processNCNAME(handler, uri, name, rawName, value, owner);
+        break;
+    case T_AVT_QNAME :
+        processedValue = processAVT_QNAME(handler, uri, name, rawName, value, owner);
+        break;
+    case T_PREFIXLIST :
+      processedValue = processPREFIX_LIST(handler, uri, name, rawName,
+                                             value);
+      break;
+
+    default :
+    }
+
+    return processedValue;
+  }
+
+  /**
+   * Set the default value of an attribute.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param elem The object on which the property will be set.
+   *
+   * @throws org.xml.sax.SAXException wraps an invocation exception if the
+   * setter method can not be invoked on the object.
+   */
+  void setDefAttrValue(StylesheetHandler handler, ElemTemplateElement elem)
+          throws org.xml.sax.SAXException
+  {
+    setAttrValue(handler, this.getNamespace(), this.getName(),
+                 this.getName(), this.getDefault(), elem);
+  }
+
+  /**
+   * Get the primative type for the class, if there
+   * is one.  If the class is a Double, for instance,
+   * this will return double.class.  If the class is not one
+   * of the 9 primative types, it will return the same
+   * class that was passed in.
+   *
+   * @param obj The object which will be resolved to a primative class object if possible.
+   *
+   * @return The most primative class representation possible for the object, never null.
+   */
+  private Class getPrimativeClass(Object obj)
+  {
+
+    if (obj instanceof XPath)
+      return XPath.class;
+
+    Class cl = obj.getClass();
+
+    if (cl == Double.class)
+    {
+      cl = double.class;
+    }
+
+    if (cl == Float.class)
+    {
+      cl = float.class;
+    }
+    else if (cl == Boolean.class)
+    {
+      cl = boolean.class;
+    }
+    else if (cl == Byte.class)
+    {
+      cl = byte.class;
+    }
+    else if (cl == Character.class)
+    {
+      cl = char.class;
+    }
+    else if (cl == Short.class)
+    {
+      cl = short.class;
+    }
+    else if (cl == Integer.class)
+    {
+      cl = int.class;
+    }
+    else if (cl == Long.class)
+    {
+      cl = long.class;
+    }
+
+    return cl;
+  }
+  
+  /**
+   * StringBuffer containing comma delimited list of valid values for ENUM type.
+   * Used to build error message.
+   */
+  private StringBuffer getListOfEnums() 
+  {
+     StringBuffer enumNamesList = new StringBuffer();            
+     String [] enumValues = this.getEnumNames();
+
+     for (int i = 0; i < enumValues.length; i++)
+     {
+        if (i > 0)
+        {
+           enumNamesList.append(' ');
+        }
+        enumNamesList.append(enumValues[i]);
+    }        
+    return enumNamesList;
+  }
+
+  /**
+   * Set a value on an attribute.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param attrUri The Namespace URI of the attribute, or an empty string.
+   * @param attrLocalName The local name (without prefix), or empty string if not namespace processing.
+   * @param attrRawName The raw name of the attribute, including possible prefix.
+   * @param attrValue The attribute's value.
+   * @param elem The object that should contain a property that represents the attribute.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  boolean setAttrValue(
+          StylesheetHandler handler, String attrUri, String attrLocalName, 
+          String attrRawName, String attrValue, ElemTemplateElement elem)
+            throws org.xml.sax.SAXException
+  {
+    if(attrRawName.equals("xmlns") || attrRawName.startsWith("xmlns:"))
+      return true;
+      
+    String setterString = getSetterMethodName();
+
+    // If this is null, then it is a foreign namespace and we 
+    // do not process it.
+    if (null != setterString)
+    {
+      try
+      {
+        Method meth;
+        Object[] args;
+
+        if(setterString.equals(S_FOREIGNATTR_SETTER))
+        {
+          // workaround for possible crimson bug
+          if( attrUri==null) attrUri="";
+          // First try to match with the primative value.
+          Class sclass = attrUri.getClass();
+          Class[] argTypes = new Class[]{ sclass, sclass,
+                                      sclass, sclass };
+  
+          meth = elem.getClass().getMethod(setterString, argTypes);
+  
+          args = new Object[]{ attrUri, attrLocalName,
+                                      attrRawName, attrValue };
+        }
+        else
+        {
+          Object value = processValue(handler, attrUri, attrLocalName,
+                                      attrRawName, attrValue, elem);
+          // If a warning was issued because the value for this attribute was
+          // invalid, then the value will be null.  Just return
+          if (null == value) return false;
+                                      
+          // First try to match with the primative value.
+          Class[] argTypes = new Class[]{ getPrimativeClass(value) };
+  
+          try
+          {
+            meth = elem.getClass().getMethod(setterString, argTypes);
+          }
+          catch (NoSuchMethodException nsme)
+          {
+            Class cl = ((Object) value).getClass();
+  
+            // If this doesn't work, try it with the non-primative value;
+            argTypes[0] = cl;
+            meth = elem.getClass().getMethod(setterString, argTypes);
+          }
+  
+          args = new Object[]{ value };
+        }
+
+        meth.invoke(elem, args);
+      }
+      catch (NoSuchMethodException nsme)
+      {
+        if (!setterString.equals(S_FOREIGNATTR_SETTER)) 
+        {
+          handler.error(XSLTErrorResources.ER_FAILED_CALLING_METHOD, new Object[]{setterString}, nsme);//"Failed calling " + setterString + " method!", nsme);
+          return false;
+        }
+      }
+      catch (IllegalAccessException iae)
+      {
+        handler.error(XSLTErrorResources.ER_FAILED_CALLING_METHOD, new Object[]{setterString}, iae);//"Failed calling " + setterString + " method!", iae);
+        return false;
+      }
+      catch (InvocationTargetException nsme)
+      {
+        handleError(handler, XSLTErrorResources.WG_ILLEGAL_ATTRIBUTE_VALUE,
+            new Object[]{ Constants.ATTRNAME_NAME, getName()}, nsme);
+        return false;
+      }
+    }
+    
+    return true;
+  }
+  
+  private void handleError(StylesheetHandler handler, String msg, Object [] args, Exception exc) throws org.xml.sax.SAXException
+  {
+    switch (getErrorType()) 
+    {
+        case (FATAL):
+        case (ERROR):
+                handler.error(msg, args, exc);          
+                break;
+        case (WARNING):
+                handler.warn(msg, args);       
+        default: break;
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/XSLTElementDef.java b/src/main/java/org/apache/xalan/processor/XSLTElementDef.java
new file mode 100644
index 0000000..1129639
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/XSLTElementDef.java
@@ -0,0 +1,833 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLTElementDef.java 468640 2006-10-28 06:53:53Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.xalan.templates.Constants;
+import org.apache.xml.utils.QName;
+
+/**
+ * This class defines the allowed structure for an element in a XSLT stylesheet,
+ * is meant to reflect the structure defined in http://www.w3.org/TR/xslt#dtd, and the
+ * mapping between Xalan classes and the markup elements in the XSLT instance.
+ * This actually represents both text nodes and elements.
+ */
+public class XSLTElementDef
+{
+
+  /**
+   * Construct an instance of XSLTElementDef.  This must be followed by a
+   * call to build().
+   */
+  XSLTElementDef(){}
+
+  /**
+   * Construct an instance of XSLTElementDef.
+   *
+   * @param namespace  The Namespace URI, "*", or null.
+   * @param name The local name (without prefix), "*", or null.
+   * @param nameAlias A potential alias for the name, or null.
+   * @param elements An array of allowed child element defs, or null.
+   * @param attributes An array of allowed attribute defs, or null.
+   * @param contentHandler The element processor for this element.
+   * @param classObject The class of the object that this element def should produce.
+   */
+  XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
+                 XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
+                 XSLTElementProcessor contentHandler, Class classObject)
+  {
+    build(namespace, name, nameAlias, elements, attributes, contentHandler,
+          classObject);
+    if ( (null != namespace)
+    &&  (namespace.equals(Constants.S_XSLNAMESPACEURL)
+        || namespace.equals(Constants.S_BUILTIN_EXTENSIONS_URL)
+        || namespace.equals(Constants.S_BUILTIN_OLD_EXTENSIONS_URL)))
+    {
+      schema.addAvailableElement(new QName(namespace, name));
+      if(null != nameAlias)
+        schema.addAvailableElement(new QName(namespace, nameAlias));
+    } 
+  }
+	
+	/**
+   * Construct an instance of XSLTElementDef.
+   *
+   * @param namespace  The Namespace URI, "*", or null.
+   * @param name The local name (without prefix), "*", or null.
+   * @param nameAlias A potential alias for the name, or null.
+   * @param elements An array of allowed child element defs, or null.
+   * @param attributes An array of allowed attribute defs, or null.
+   * @param contentHandler The element processor for this element.
+   * @param classObject The class of the object that this element def should produce.
+   * @param has_required true if this element has required elements by the XSLT specification.
+   */
+  XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
+                 XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
+                 XSLTElementProcessor contentHandler, Class classObject, boolean has_required)
+  {
+		this.m_has_required = has_required;
+    build(namespace, name, nameAlias, elements, attributes, contentHandler,
+          classObject);
+    if ( (null != namespace)
+    &&  (namespace.equals(Constants.S_XSLNAMESPACEURL)
+        || namespace.equals(Constants.S_BUILTIN_EXTENSIONS_URL)
+        || namespace.equals(Constants.S_BUILTIN_OLD_EXTENSIONS_URL)))
+    {
+      schema.addAvailableElement(new QName(namespace, name));
+      if(null != nameAlias)
+        schema.addAvailableElement(new QName(namespace, nameAlias));
+    } 
+		
+  }
+	
+	/**
+   * Construct an instance of XSLTElementDef.
+   *
+   * @param namespace  The Namespace URI, "*", or null.
+   * @param name The local name (without prefix), "*", or null.
+   * @param nameAlias A potential alias for the name, or null.
+   * @param elements An array of allowed child element defs, or null.
+   * @param attributes An array of allowed attribute defs, or null.
+   * @param contentHandler The element processor for this element.
+   * @param classObject The class of the object that this element def should produce.
+   * @param has_required true if this element has required elements by the XSLT specification.
+   * @param required true if this element is required by the XSLT specification.
+   */
+  XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
+                 XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
+                 XSLTElementProcessor contentHandler, Class classObject, 
+								 boolean has_required, boolean required)
+  {
+    this(schema, namespace, name,  nameAlias,
+                 elements, attributes,
+                 contentHandler, classObject, has_required);
+		this.m_required = required;
+  }
+	
+	/**
+   * Construct an instance of XSLTElementDef.
+   *
+   * @param namespace  The Namespace URI, "*", or null.
+   * @param name The local name (without prefix), "*", or null.
+   * @param nameAlias A potential alias for the name, or null.
+   * @param elements An array of allowed child element defs, or null.
+   * @param attributes An array of allowed attribute defs, or null.
+   * @param contentHandler The element processor for this element.
+   * @param classObject The class of the object that this element def should produce.
+   * @param has_required true if this element has required elements by the XSLT specification.
+   * @param required true if this element is required by the XSLT specification.
+   * @param order the order this element should appear according to the XSLT specification.   
+   * @param multiAllowed whether this element is allowed more than once
+   */
+  XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
+                 XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
+                 XSLTElementProcessor contentHandler, Class classObject, 
+								 boolean has_required, boolean required, int order, 
+								 boolean multiAllowed)
+  {
+		this(schema, namespace, name,  nameAlias,
+                 elements, attributes,
+                 contentHandler, classObject, has_required, required);    
+		this.m_order = order;
+		this.m_multiAllowed = multiAllowed;
+  }
+	
+	/**
+   * Construct an instance of XSLTElementDef.
+   *
+   * @param namespace  The Namespace URI, "*", or null.
+   * @param name The local name (without prefix), "*", or null.
+   * @param nameAlias A potential alias for the name, or null.
+   * @param elements An array of allowed child element defs, or null.
+   * @param attributes An array of allowed attribute defs, or null.
+   * @param contentHandler The element processor for this element.
+   * @param classObject The class of the object that this element def should produce.
+   * @param has_required true if this element has required elements by the XSLT specification.
+   * @param required true if this element is required by the XSLT specification.
+   * @param has_order whether this element has ordered child elements
+   * @param order the order this element should appear according to the XSLT specification.   
+   * @param multiAllowed whether this element is allowed more than once
+   */
+  XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
+                 XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
+                 XSLTElementProcessor contentHandler, Class classObject, 
+								 boolean has_required, boolean required, boolean has_order, int order, 
+								 boolean multiAllowed)
+  {
+		this(schema, namespace, name,  nameAlias,
+                 elements, attributes,
+                 contentHandler, classObject, has_required, required);    
+		this.m_order = order;
+		this.m_multiAllowed = multiAllowed;
+    this.m_isOrdered = has_order;		
+  }
+	
+	/**
+   * Construct an instance of XSLTElementDef.
+   *
+   * @param namespace  The Namespace URI, "*", or null.
+   * @param name The local name (without prefix), "*", or null.
+   * @param nameAlias A potential alias for the name, or null.
+   * @param elements An array of allowed child element defs, or null.
+   * @param attributes An array of allowed attribute defs, or null.
+   * @param contentHandler The element processor for this element.
+   * @param classObject The class of the object that this element def should produce.
+   * @param has_order whether this element has ordered child elements
+   * @param order the order this element should appear according to the XSLT specification.   
+   * @param multiAllowed whether this element is allowed more than once
+   */
+  XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
+                 XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
+                 XSLTElementProcessor contentHandler, Class classObject, 
+								 boolean has_order, int order, boolean multiAllowed)
+  {
+    this(schema, namespace, name,  nameAlias,
+                 elements, attributes,
+                 contentHandler, classObject, 
+								 order, multiAllowed);
+		this.m_isOrdered = has_order;		
+  }
+	
+	/**
+   * Construct an instance of XSLTElementDef.
+   *
+   * @param namespace  The Namespace URI, "*", or null.
+   * @param name The local name (without prefix), "*", or null.
+   * @param nameAlias A potential alias for the name, or null.
+   * @param elements An array of allowed child element defs, or null.
+   * @param attributes An array of allowed attribute defs, or null.
+   * @param contentHandler The element processor for this element.
+   * @param classObject The class of the object that this element def should produce.
+   * @param order the order this element should appear according to the XSLT specification.   
+   * @param multiAllowed whether this element is allowed more than once
+   */
+  XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
+                 XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
+                 XSLTElementProcessor contentHandler, Class classObject, 
+								 int order, boolean multiAllowed)
+  {
+    this(schema, namespace, name, nameAlias, elements, attributes, contentHandler,
+          classObject);
+    this.m_order = order;
+		this.m_multiAllowed = multiAllowed;
+  }
+
+  /**
+   * Construct an instance of XSLTElementDef that represents text.
+   *
+   * @param classObject The class of the object that this element def should produce.
+   * @param contentHandler The element processor for this element.
+   * @param type Content type, one of T_ELEMENT, T_PCDATA, or T_ANY.
+   */
+  XSLTElementDef(Class classObject, XSLTElementProcessor contentHandler,
+                 int type)
+  {
+
+    this.m_classObject = classObject;
+    this.m_type = type;
+
+    setElementProcessor(contentHandler);
+  }
+
+  /**
+   * Construct an instance of XSLTElementDef.
+   *
+   * @param namespace  The Namespace URI, "*", or null.
+   * @param name The local name (without prefix), "*", or null.
+   * @param nameAlias A potential alias for the name, or null.
+   * @param elements An array of allowed child element defs, or null.
+   * @param attributes An array of allowed attribute defs, or null.
+   * @param contentHandler The element processor for this element.
+   * @param classObject The class of the object that this element def should produce.
+   */
+  void build(String namespace, String name, String nameAlias,
+             XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
+             XSLTElementProcessor contentHandler, Class classObject)
+  {
+
+    this.m_namespace = namespace;
+    this.m_name = name;
+    this.m_nameAlias = nameAlias;
+    this.m_elements = elements;
+    this.m_attributes = attributes;
+
+    setElementProcessor(contentHandler);
+
+    this.m_classObject = classObject;
+		
+		if (hasRequired() && m_elements != null)
+		{
+			int n = m_elements.length;
+			for (int i = 0; i < n; i++)
+			{
+				XSLTElementDef def = m_elements[i];
+				
+				if (def != null && def.getRequired())
+				{
+					if (m_requiredFound == null)			
+						m_requiredFound = new Hashtable();
+					m_requiredFound.put(def.getName(), "xsl:" +def.getName()); 
+				}
+			}
+		}
+  }
+
+  /**
+   * Tell if two objects are equal, when either one may be null.
+   * If both are null, they are considered equal.
+   *
+   * @param obj1 A reference to the first object, or null.
+   * @param obj2 A reference to the second object, or null.
+   *
+   * @return true if the to objects are equal by both being null or 
+   * because obj2.equals(obj1) returns true.
+   */
+  private static boolean equalsMayBeNull(Object obj1, Object obj2)
+  {
+    return (obj2 == obj1)
+           || ((null != obj1) && (null != obj2) && obj2.equals(obj1));
+  }
+
+  /**
+   * Tell if the two string refs are equal,
+   * equality being defined as:
+   * 1) Both strings are null.
+   * 2) One string is null and the other is empty.
+   * 3) Both strings are non-null, and equal.
+   *
+   * @param s1 A reference to the first string, or null.
+   * @param s2 A reference to the second string, or null.
+   *
+   * @return true if Both strings are null, or if 
+   * one string is null and the other is empty, or if 
+   * both strings are non-null, and equal because 
+   * s1.equals(s2) returns true.
+   */
+  private static boolean equalsMayBeNullOrZeroLen(String s1, String s2)
+  {
+
+    int len1 = (s1 == null) ? 0 : s1.length();
+    int len2 = (s2 == null) ? 0 : s2.length();
+
+    return (len1 != len2) ? false 
+						 : (len1 == 0) ? true 
+								 : s1.equals(s2);
+  }
+
+  /** Content type enumerations    */
+  static final int T_ELEMENT = 1, T_PCDATA = 2, T_ANY = 3;
+
+  /**
+   * The type of this element.
+   */
+  private int m_type = T_ELEMENT;
+
+  /**
+   * Get the type of this element.
+   *
+   * @return Content type, one of T_ELEMENT, T_PCDATA, or T_ANY.
+   */
+  int getType()
+  {
+    return m_type;
+  }
+
+  /**
+   * Set the type of this element.
+   *
+   * @param t Content type, one of T_ELEMENT, T_PCDATA, or T_ANY.
+   */
+  void setType(int t)
+  {
+    m_type = t;
+  }
+
+  /**
+   * The allowed namespace for this element.
+   */
+  private String m_namespace;
+
+  /**
+   * Get the allowed namespace for this element.
+   *
+   * @return The Namespace URI, "*", or null.
+   */
+  String getNamespace()
+  {
+    return m_namespace;
+  }
+
+  /**
+   * The name of this element.
+   */
+  private String m_name;
+
+  /**
+   * Get the local name of this element.
+   *
+   * @return The local name of this element, "*", or null.
+   */
+  String getName()
+  {
+    return m_name;
+  }
+
+  /**
+   * The name of this element.
+   */
+  private String m_nameAlias;
+
+  /**
+   * Get the name of this element.
+   *
+   * @return A potential alias for the name, or null.
+   */
+  String getNameAlias()
+  {
+    return m_nameAlias;
+  }
+
+  /**
+   * The allowed elements for this type.
+   */
+  private XSLTElementDef[] m_elements;
+
+  /**
+   * Get the allowed elements for this type.
+   *
+   * @return An array of allowed child element defs, or null.
+   * @xsl.usage internal
+   */
+  public XSLTElementDef[] getElements()
+  {
+    return m_elements;
+  }
+
+  /**
+   * Set the allowed elements for this type.
+   *
+   * @param defs An array of allowed child element defs, or null.
+   */
+  void setElements(XSLTElementDef[] defs)
+  {
+    m_elements = defs;
+  }
+
+  /**
+   * Tell if the namespace URI and local name match this
+   * element.
+   * @param uri The namespace uri, which may be null.
+   * @param localName The local name of an element, which may be null.
+   *
+   * @return true if the uri and local name arguments are considered 
+   * to match the uri and local name of this element def.
+   */
+  private boolean QNameEquals(String uri, String localName)
+  {
+
+    return (equalsMayBeNullOrZeroLen(m_namespace, uri)
+            && (equalsMayBeNullOrZeroLen(m_name, localName)
+                || equalsMayBeNullOrZeroLen(m_nameAlias, localName)));
+  }
+
+  /**
+   * Given a namespace URI, and a local name, get the processor
+   * for the element, or return null if not allowed.
+   *
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   *
+   * @return The element processor that matches the arguments, or null.
+   */
+  XSLTElementProcessor getProcessorFor(String uri, String localName) 
+	{
+
+    XSLTElementProcessor elemDef = null;  // return value
+
+    if (null == m_elements)
+      return null;
+
+    int n = m_elements.length;
+    int order = -1;
+		boolean multiAllowed = true;
+    for (int i = 0; i < n; i++)
+    {
+      XSLTElementDef def = m_elements[i];
+
+      // A "*" signals that the element allows literal result
+      // elements, so just assign the def, and continue to  
+      // see if anything else matches.
+      if (def.m_name.equals("*"))
+      {
+				
+        // Don't allow xsl elements
+        if (!equalsMayBeNullOrZeroLen(uri, Constants.S_XSLNAMESPACEURL))
+				{
+          elemDef = def.m_elementProcessor;
+				  order = def.getOrder();
+					multiAllowed = def.getMultiAllowed();
+				}
+      }
+			else if (def.QNameEquals(uri, localName))
+			{	
+				if (def.getRequired())
+					this.setRequiredFound(def.getName(), true);
+				order = def.getOrder();
+				multiAllowed = def.getMultiAllowed();
+				elemDef = def.m_elementProcessor;
+				break;
+			}
+		}		
+		
+		if (elemDef != null && this.isOrdered())
+		{			
+			int lastOrder = getLastOrder();
+			if (order > lastOrder)
+				setLastOrder(order);
+			else if (order == lastOrder && !multiAllowed)
+			{
+				return null;
+			}
+			else if (order < lastOrder && order > 0)
+			{
+				return null;
+			}
+		}
+
+    return elemDef;
+  }
+
+  /**
+   * Given an unknown element, get the processor
+   * for the element.
+   *
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   *
+   * @return normally a {@link ProcessorUnknown} reference.
+   * @see ProcessorUnknown
+   */
+  XSLTElementProcessor getProcessorForUnknown(String uri, String localName)
+  {
+
+    // XSLTElementProcessor lreDef = null; // return value
+    if (null == m_elements)
+      return null;
+
+    int n = m_elements.length;
+
+    for (int i = 0; i < n; i++)
+    {
+      XSLTElementDef def = m_elements[i];
+
+      if (def.m_name.equals("unknown") && uri.length() > 0)
+      {
+        return def.m_elementProcessor;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * The allowed attributes for this type.
+   */
+  private XSLTAttributeDef[] m_attributes;
+
+  /**
+   * Get the allowed attributes for this type.
+   *
+   * @return An array of allowed attribute defs, or null.
+   */
+  XSLTAttributeDef[] getAttributes()
+  {
+    return m_attributes;
+  }
+
+  /**
+   * Given a namespace URI, and a local name, return the element's
+   * attribute definition, if it has one.
+   *
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   *
+   * @return The attribute def that matches the arguments, or null.
+   */
+  XSLTAttributeDef getAttributeDef(String uri, String localName)
+  {
+
+    XSLTAttributeDef defaultDef = null;
+    XSLTAttributeDef[] attrDefs = getAttributes();
+    int nAttrDefs = attrDefs.length;
+
+    for (int k = 0; k < nAttrDefs; k++)
+    {
+      XSLTAttributeDef attrDef = attrDefs[k];
+      String uriDef = attrDef.getNamespace();
+      String nameDef = attrDef.getName();
+      
+      if (nameDef.equals("*") && (equalsMayBeNullOrZeroLen(uri, uriDef) || 
+          (uriDef != null && uriDef.equals("*") && uri!=null && uri.length() > 0 )))
+      {
+        return attrDef;
+      }
+      else if (nameDef.equals("*") && (uriDef == null))
+      {
+
+        // In this case, all attributes are legal, so return 
+        // this as the last resort.
+        defaultDef = attrDef;
+      }
+      else if (equalsMayBeNullOrZeroLen(uri, uriDef)
+               && localName.equals(nameDef))
+      {
+        return attrDef;
+      }
+    }
+
+    if (null == defaultDef)
+    {
+      if (uri.length() > 0 && !equalsMayBeNullOrZeroLen(uri, Constants.S_XSLNAMESPACEURL))
+      {
+        return XSLTAttributeDef.m_foreignAttr;
+      }
+    }
+
+    return defaultDef;
+  }
+
+  /**
+   * If non-null, the ContentHandler/TransformerFactory for this element.
+   */
+  private XSLTElementProcessor m_elementProcessor;
+
+  /**
+   * Return the XSLTElementProcessor for this element.
+   *
+   * @return The element processor for this element.
+   * @xsl.usage internal
+   */
+  public XSLTElementProcessor getElementProcessor()
+  {
+    return m_elementProcessor;
+  }
+
+  /**
+   * Set the XSLTElementProcessor for this element.
+   *
+   * @param handler The element processor for this element.
+   * @xsl.usage internal
+   */
+  public void setElementProcessor(XSLTElementProcessor handler)
+  {
+
+    if (handler != null)
+    {
+      m_elementProcessor = handler;
+
+      m_elementProcessor.setElemDef(this);
+    }
+  }
+
+  /**
+   * If non-null, the class object that should in instantiated for
+   * a Xalan instance of this element.
+   */
+  private Class m_classObject;
+
+  /**
+   * Return the class object that should in instantiated for
+   * a Xalan instance of this element.
+   *
+   * @return The class of the object that this element def should produce, or null.
+   */
+  Class getClassObject()
+  {
+    return m_classObject;
+  }
+	
+	/**
+   * If true, this has a required element.
+   */
+  private boolean m_has_required = false;
+
+  /**
+   * Get whether or not this has a required element.
+   *
+   * @return true if this this has a required element.
+   */
+  boolean hasRequired()
+  {
+    return m_has_required;
+  }
+	
+	/**
+   * If true, this is a required element.
+   */
+  private boolean m_required = false;
+
+  /**
+   * Get whether or not this is a required element.
+   *
+   * @return true if this is a required element.
+   */
+  boolean getRequired()
+  {
+    return m_required;
+  }
+	
+	Hashtable m_requiredFound;
+	
+	/**
+   * Set this required element found.
+   *
+   */
+  void setRequiredFound(String elem, boolean found)
+  {
+   if (m_requiredFound.get(elem) != null) 
+		 m_requiredFound.remove(elem);
+  }
+	
+	/**
+   * Get whether all required elements were found.
+   *
+   * @return true if all required elements were found.
+   */
+  boolean getRequiredFound()
+  {
+		if (m_requiredFound == null)
+			return true;
+    return m_requiredFound.isEmpty();
+  }
+	
+	/**
+   * Get required elements that were not found.
+   *
+   * @return required elements that were not found.
+   */
+  String getRequiredElem()
+  {
+		if (m_requiredFound == null)
+			return null;
+		Enumeration elems = m_requiredFound.elements();
+		String s = "";
+		boolean first = true;
+		while (elems.hasMoreElements())
+		{
+			if (first)
+				first = false;
+			else
+			 s = s + ", ";
+			s = s + (String)elems.nextElement();
+		}
+    return s;
+  }
+	
+	boolean m_isOrdered = false;	
+	
+	/**
+   * Get whether this element requires ordered children.
+   *
+   * @return true if this element requires ordered children.
+   */
+  boolean isOrdered()
+  {
+		/*if (!m_CheckedOrdered)
+		{
+			m_CheckedOrdered = true;
+			m_isOrdered = false;
+			if (null == m_elements)
+				return false;
+
+			int n = m_elements.length;
+
+			for (int i = 0; i < n; i++)
+			{
+				if (m_elements[i].getOrder() > 0)
+				{
+					m_isOrdered = true;
+					return true;
+				}
+			}
+			return false;
+		}
+		else*/
+			return m_isOrdered;
+  }
+	
+	/**
+   * the order that this element should appear, or -1 if not ordered
+   */
+  private int m_order = -1;
+	
+	/**
+   * Get the order that this element should appear .
+   *
+   * @return the order that this element should appear.
+   */
+  int getOrder()
+  {
+    return m_order;
+  }
+	
+	/**
+   * the highest order of child elements have appeared so far, 
+   * or -1 if not ordered
+   */
+  private int m_lastOrder = -1;
+	
+	/**
+   * Get the highest order of child elements have appeared so far .
+   *
+   * @return the highest order of child elements have appeared so far.
+   */
+  int getLastOrder()
+  {
+    return m_lastOrder;
+  }
+	
+	/**
+   * Set the highest order of child elements have appeared so far .
+   *
+   * @param order the highest order of child elements have appeared so far.
+   */
+  void setLastOrder(int order)
+  {
+    m_lastOrder = order ;
+  }
+	
+	/**
+   * True if this element can appear multiple times
+   */
+  private boolean m_multiAllowed = true;
+	
+	/**
+   * Get whether this element can appear multiple times
+   *
+   * @return true if this element can appear multiple times
+   */
+  boolean getMultiAllowed()
+  {
+    return m_multiAllowed;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/XSLTElementProcessor.java b/src/main/java/org/apache/xalan/processor/XSLTElementProcessor.java
new file mode 100644
index 0000000..7858b42
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/XSLTElementProcessor.java
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLTElementProcessor.java 469688 2006-10-31 22:39:43Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xml.utils.IntStack;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * This class acts as the superclass for all stylesheet element
+ * processors, and deals with things that are common to all elements.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ */
+public class XSLTElementProcessor extends ElemTemplateElement
+{
+    static final long serialVersionUID = 5597421564955304421L;
+
+  /**
+   * Construct a processor for top-level elements.
+   * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+   */
+  XSLTElementProcessor(){}
+	
+	private IntStack m_savedLastOrder;
+
+  /**
+   * The element definition that this processor conforms to.
+   */
+  private XSLTElementDef m_elemDef;
+
+  /**
+   * Get the element definition that belongs to this element.
+   *
+   * @return The element definition object that produced and constrains this element.
+   */
+  XSLTElementDef getElemDef()
+  {
+    return m_elemDef;
+  }
+
+  /**
+   * Set the element definition that belongs to this element.
+   *
+   * @param def The element definition object that produced and constrains this element.
+   */
+  void setElemDef(XSLTElementDef def)
+  {
+    m_elemDef = def;
+  }
+
+  /**
+   * Resolve an external entity.
+   *
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param publicId The public identifer, or null if none is
+   *                 available.
+   * @param systemId The system identifier provided in the XML
+   *                 document.
+   * @return The new input source, or null to require the
+   *         default behaviour.
+   */
+  public InputSource resolveEntity(
+          StylesheetHandler handler, String publicId, String systemId)
+            throws org.xml.sax.SAXException
+  {
+    return null;
+  }
+
+  /**
+   * Receive notification of a notation declaration.
+   *
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param name The notation name.
+   * @param publicId The notation public identifier, or null if not
+   *                 available.
+   * @param systemId The notation system identifier.
+   * @see org.xml.sax.DTDHandler#notationDecl
+   */
+  public void notationDecl(StylesheetHandler handler, String name,
+                           String publicId, String systemId)
+  {
+
+    // no op
+  }
+
+  /**
+   * Receive notification of an unparsed entity declaration.
+   *
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param name The entity name.
+   * @param publicId The entity public identifier, or null if not
+   *                 available.
+   * @param systemId The entity system identifier.
+   * @param notationName The name of the associated notation.
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void unparsedEntityDecl(StylesheetHandler handler, String name,
+                                 String publicId, String systemId,
+                                 String notationName)
+  {
+
+    // no op
+  }
+
+  /**
+   * Receive notification of the start of the non-text event.  This
+   * is sent to the current processor when any non-text event occurs.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   */
+  public void startNonText(StylesheetHandler handler) throws org.xml.sax.SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Receive notification of the start of an element.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   * @param attributes The specified or defaulted attributes.
+   */
+  public void startElement(
+          StylesheetHandler handler, String uri, String localName, String rawName, Attributes attributes)
+            throws org.xml.sax.SAXException
+  {
+
+    if (m_savedLastOrder == null)
+				m_savedLastOrder = new IntStack();
+			m_savedLastOrder.push(getElemDef().getLastOrder());
+			getElemDef().setLastOrder(-1);
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param uri The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param rawName The qualified name (with prefix).
+   */
+  public void endElement(
+          StylesheetHandler handler, String uri, String localName, String rawName)
+            throws org.xml.sax.SAXException
+  {
+		if (m_savedLastOrder != null && !m_savedLastOrder.empty())
+			getElemDef().setLastOrder(m_savedLastOrder.pop());
+		
+		if (!getElemDef().getRequiredFound())
+			handler.error(XSLTErrorResources.ER_REQUIRED_ELEM_NOT_FOUND, new Object[]{getElemDef().getRequiredElem()}, null);
+  }
+
+  /**
+   * Receive notification of character data inside an element.
+   *
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param ch The characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   */
+  public void characters(
+          StylesheetHandler handler, char ch[], int start, int length)
+            throws org.xml.sax.SAXException
+  {
+    handler.error(XSLTErrorResources.ER_CHARS_NOT_ALLOWED, null, null);//"Characters are not allowed at this point in the document!",
+                  //null);
+  }
+
+  /**
+   * Receive notification of ignorable whitespace in element content.
+   *
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param ch The whitespace characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   */
+  public void ignorableWhitespace(
+          StylesheetHandler handler, char ch[], int start, int length)
+            throws org.xml.sax.SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Receive notification of a processing instruction.
+   *
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param target The processing instruction target.
+   * @param data The processing instruction data, or null if
+   *             none is supplied.
+   */
+  public void processingInstruction(
+          StylesheetHandler handler, String target, String data)
+            throws org.xml.sax.SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Receive notification of a skipped entity.
+   *
+   *
+   * @param handler non-null reference to current StylesheetHandler that is constructing the Templates.
+   * @param name The name of the skipped entity.
+   */
+  public void skippedEntity(StylesheetHandler handler, String name)
+          throws org.xml.sax.SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Set the properties of an object from the given attribute list.
+   * @param handler The stylesheet's Content handler, needed for
+   *                error reporting.
+   * @param rawName The raw name of the owner element, needed for
+   *                error reporting.
+   * @param attributes The list of attributes.
+   * @param target The target element where the properties will be set.
+   */
+  void setPropertiesFromAttributes(
+          StylesheetHandler handler, String rawName, Attributes attributes, 
+          ElemTemplateElement target)
+            throws org.xml.sax.SAXException
+  {
+    setPropertiesFromAttributes(handler, rawName, attributes, target, true);
+  }
+
+  /**
+   * Set the properties of an object from the given attribute list.
+   * @param handler The stylesheet's Content handler, needed for
+   *                error reporting.
+   * @param rawName The raw name of the owner element, needed for
+   *                error reporting.
+   * @param attributes The list of attributes.
+   * @param target The target element where the properties will be set.
+   * @param throwError True if it should throw an error if an
+   * attribute is not defined.
+   * @return the attributes not allowed on this element.
+   *
+   * @throws TransformerException
+   */
+  Attributes setPropertiesFromAttributes(
+          StylesheetHandler handler, String rawName, Attributes attributes, 
+          ElemTemplateElement target, boolean throwError)
+            throws org.xml.sax.SAXException
+  {
+
+    XSLTElementDef def = getElemDef();
+    AttributesImpl undefines = null;
+    boolean isCompatibleMode = ((null != handler.getStylesheet() 
+                                 && handler.getStylesheet().getCompatibleMode())
+                                || !throwError);
+    if (isCompatibleMode)
+      undefines = new AttributesImpl();
+
+
+    // Keep track of which XSLTAttributeDefs have been processed, so 
+    // I can see which default values need to be set.
+    List processedDefs = new ArrayList();
+
+    // Keep track of XSLTAttributeDefs that were invalid
+    List errorDefs = new ArrayList();    
+    int nAttrs = attributes.getLength();
+
+    for (int i = 0; i < nAttrs; i++)
+    {
+      String attrUri = attributes.getURI(i);
+      // Hack for Crimson.  -sb
+      if((null != attrUri) && (attrUri.length() == 0)
+                           && (attributes.getQName(i).startsWith("xmlns:") || 
+                               attributes.getQName(i).equals("xmlns")))
+      {
+        attrUri = org.apache.xalan.templates.Constants.S_XMLNAMESPACEURI;
+      }
+      String attrLocalName = attributes.getLocalName(i);
+      XSLTAttributeDef attrDef = def.getAttributeDef(attrUri, attrLocalName);
+
+      if (null == attrDef)
+      {
+        if (!isCompatibleMode)
+        {
+
+          // Then barf, because this element does not allow this attribute.
+          handler.error(XSLTErrorResources.ER_ATTR_NOT_ALLOWED, new Object[]{attributes.getQName(i), rawName}, null);//"\""+attributes.getQName(i)+"\""
+                        //+ " attribute is not allowed on the " + rawName
+                       // + " element!", null);
+        }
+        else
+        {
+          undefines.addAttribute(attrUri, attrLocalName,
+                                 attributes.getQName(i),
+                                 attributes.getType(i),
+                                 attributes.getValue(i));
+        }
+      }
+      else
+      {
+        // Can we switch the order here:
+
+        boolean success = attrDef.setAttrValue(handler, attrUri, attrLocalName,
+                             attributes.getQName(i), attributes.getValue(i),
+                             target);
+                             
+        // Now we only add the element if it passed a validation check
+        if (success)
+            processedDefs.add(attrDef);
+        else
+            errorDefs.add(attrDef);
+      }
+    }
+
+    XSLTAttributeDef[] attrDefs = def.getAttributes();
+    int nAttrDefs = attrDefs.length;
+
+    for (int i = 0; i < nAttrDefs; i++)
+    {
+      XSLTAttributeDef attrDef = attrDefs[i];
+      String defVal = attrDef.getDefault();
+
+      if (null != defVal)
+      {
+        if (!processedDefs.contains(attrDef))
+        {
+          attrDef.setDefAttrValue(handler, target);
+        }
+      }
+
+      if (attrDef.getRequired())
+      {
+        if ((!processedDefs.contains(attrDef)) && (!errorDefs.contains(attrDef)))
+          handler.error(
+            XSLMessages.createMessage(
+              XSLTErrorResources.ER_REQUIRES_ATTRIB, new Object[]{ rawName,
+                                                                   attrDef.getName() }), null);
+      }
+    }
+
+    return undefines;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/processor/XSLTSchema.java b/src/main/java/org/apache/xalan/processor/XSLTSchema.java
new file mode 100644
index 0000000..8f6eb2e
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/XSLTSchema.java
@@ -0,0 +1,922 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLTSchema.java 476466 2006-11-18 08:22:31Z minchau $
+ */
+package org.apache.xalan.processor;
+
+import java.util.HashMap;
+
+import org.apache.xalan.templates.Constants;
+import org.apache.xalan.templates.ElemApplyImport;
+import org.apache.xalan.templates.ElemApplyTemplates;
+import org.apache.xalan.templates.ElemAttribute;
+import org.apache.xalan.templates.ElemCallTemplate;
+import org.apache.xalan.templates.ElemChoose;
+import org.apache.xalan.templates.ElemComment;
+import org.apache.xalan.templates.ElemCopy;
+import org.apache.xalan.templates.ElemCopyOf;
+import org.apache.xalan.templates.ElemElement;
+import org.apache.xalan.templates.ElemExsltFuncResult;
+import org.apache.xalan.templates.ElemExsltFunction;
+import org.apache.xalan.templates.ElemExtensionDecl;
+import org.apache.xalan.templates.ElemExtensionScript;
+import org.apache.xalan.templates.ElemFallback;
+import org.apache.xalan.templates.ElemForEach;
+import org.apache.xalan.templates.ElemIf;
+import org.apache.xalan.templates.ElemLiteralResult;
+import org.apache.xalan.templates.ElemMessage;
+import org.apache.xalan.templates.ElemNumber;
+import org.apache.xalan.templates.ElemOtherwise;
+import org.apache.xalan.templates.ElemPI;
+import org.apache.xalan.templates.ElemParam;
+import org.apache.xalan.templates.ElemSort;
+import org.apache.xalan.templates.ElemTemplate;
+import org.apache.xalan.templates.ElemText;
+import org.apache.xalan.templates.ElemTextLiteral;
+import org.apache.xalan.templates.ElemUnknown;
+import org.apache.xalan.templates.ElemValueOf;
+import org.apache.xalan.templates.ElemVariable;
+import org.apache.xalan.templates.ElemWhen;
+import org.apache.xalan.templates.ElemWithParam;
+import org.apache.xml.utils.QName;
+
+/**
+ * This class defines the allowed structure for a stylesheet, and the
+ * mapping between Xalan classes and the markup elements in the stylesheet.
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ */
+public class XSLTSchema extends XSLTElementDef
+{
+
+  /**
+   * Construct a XSLTSchema which represents the XSLT "schema".
+   */
+  XSLTSchema()
+  {
+    build();
+  }
+
+  /**
+   * This method builds an XSLT "schema" according to http://www.w3.org/TR/xslt#dtd.  This
+   * schema provides instructions for building the Xalan Stylesheet (Templates) structure.
+   */
+  void build()
+  {
+	// xsl:import, xsl:include
+    XSLTAttributeDef hrefAttr = new XSLTAttributeDef(null, "href",
+                                  XSLTAttributeDef.T_URL, true, false,XSLTAttributeDef.ERROR);
+                                  
+	// xsl:preserve-space, xsl:strip-space
+    XSLTAttributeDef elementsAttr = new XSLTAttributeDef(null, "elements",
+                                      XSLTAttributeDef.T_SIMPLEPATTERNLIST,
+                                      true, false, XSLTAttributeDef.ERROR);
+                                      
+    // XSLTAttributeDef anyNamespacedAttr = new XSLTAttributeDef("*", "*",
+    //                                XSLTAttributeDef.T_CDATA, false);
+    
+    // xsl:output
+    XSLTAttributeDef methodAttr = new XSLTAttributeDef(null, "method",
+                                    XSLTAttributeDef.T_QNAME, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef versionAttr = new XSLTAttributeDef(null, "version",
+                                     XSLTAttributeDef.T_NMTOKEN, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef encodingAttr = new XSLTAttributeDef(null, "encoding",
+                                      XSLTAttributeDef.T_CDATA, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef omitXmlDeclarationAttr = new XSLTAttributeDef(null,
+                                                "omit-xml-declaration",
+                                                XSLTAttributeDef.T_YESNO,
+                                                false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef standaloneAttr = new XSLTAttributeDef(null,
+                                        "standalone",
+                                        XSLTAttributeDef.T_YESNO, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef doctypePublicAttr = new XSLTAttributeDef(null,
+                                           "doctype-public",
+                                           XSLTAttributeDef.T_CDATA, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef doctypeSystemAttr = new XSLTAttributeDef(null,
+                                           "doctype-system",
+                                           XSLTAttributeDef.T_CDATA, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef cdataSectionElementsAttr = new XSLTAttributeDef(null,
+                                                  "cdata-section-elements",
+                                                  XSLTAttributeDef.T_QNAMES_RESOLVE_NULL,
+                                                  false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef indentAttr = new XSLTAttributeDef(null, "indent",
+                                    XSLTAttributeDef.T_YESNO, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef mediaTypeAttr = new XSLTAttributeDef(null, "media-type",
+                                       XSLTAttributeDef.T_CDATA, false, false,XSLTAttributeDef.ERROR);
+                                       
+                  
+    // Required.
+    // It is an error if the name attribute is invalid on any of these elements
+    // xsl:key, xsl:attribute-set, xsl:call-template, xsl:with-param, xsl:variable, xsl:param
+    XSLTAttributeDef nameAttrRequired = new XSLTAttributeDef(null, "name",
+                                          XSLTAttributeDef.T_QNAME, true, false,XSLTAttributeDef.ERROR);
+	// Required.
+    // Support AVT
+    // xsl:element, xsl:attribute                                    
+    XSLTAttributeDef nameAVTRequired = new XSLTAttributeDef(null, "name",
+                                         XSLTAttributeDef.T_AVT_QNAME, true, true,XSLTAttributeDef.WARNING);
+            
+
+    // Required.
+    // Support AVT
+    // xsl:processing-instruction                                     
+    XSLTAttributeDef nameAVT_NCNAMERequired = new XSLTAttributeDef(null, "name",
+                                         XSLTAttributeDef.T_NCNAME, true, true,XSLTAttributeDef.WARNING);
+                                        
+    // Optional.
+    // Static error if invalid
+    // xsl:template, xsl:decimal-format                                      
+    XSLTAttributeDef nameAttrOpt_ERROR = new XSLTAttributeDef(null, "name",
+                                     XSLTAttributeDef.T_QNAME, false, false,XSLTAttributeDef.ERROR);
+
+    // xsl:key                                 
+    XSLTAttributeDef useAttr = new XSLTAttributeDef(null, "use",
+                                 XSLTAttributeDef.T_EXPR, true, false,XSLTAttributeDef.ERROR);
+           
+    // xsl:element, xsl:attribute                              
+    XSLTAttributeDef namespaceAVTOpt = new XSLTAttributeDef(null,
+                                         "namespace",XSLTAttributeDef.T_URL,
+                                         false, true,XSLTAttributeDef.WARNING);
+    // xsl:decimal-format                                     
+    XSLTAttributeDef decimalSeparatorAttr = new XSLTAttributeDef(null,
+                                              "decimal-separator",
+                                              XSLTAttributeDef.T_CHAR, false,XSLTAttributeDef.ERROR, ".");
+    XSLTAttributeDef infinityAttr = new XSLTAttributeDef(null, "infinity",
+                                      XSLTAttributeDef.T_CDATA, false,XSLTAttributeDef.ERROR,"Infinity");
+    XSLTAttributeDef minusSignAttr = new XSLTAttributeDef(null, "minus-sign",
+                                       XSLTAttributeDef.T_CHAR, false,XSLTAttributeDef.ERROR,"-");
+    XSLTAttributeDef NaNAttr = new XSLTAttributeDef(null, "NaN",
+                                 XSLTAttributeDef.T_CDATA, false,XSLTAttributeDef.ERROR, "NaN");
+    XSLTAttributeDef percentAttr = new XSLTAttributeDef(null, "percent",
+                                     XSLTAttributeDef.T_CHAR, false,XSLTAttributeDef.ERROR, "%");
+    XSLTAttributeDef perMilleAttr = new XSLTAttributeDef(null, "per-mille",
+                                      XSLTAttributeDef.T_CHAR,
+                                      false, false,XSLTAttributeDef.ERROR /* ,"&#x2030;" */);
+    XSLTAttributeDef zeroDigitAttr = new XSLTAttributeDef(null, "zero-digit",
+                                       XSLTAttributeDef.T_CHAR, false,XSLTAttributeDef.ERROR, "0");
+    XSLTAttributeDef digitAttr = new XSLTAttributeDef(null, "digit",
+                                   XSLTAttributeDef.T_CHAR, false,XSLTAttributeDef.ERROR, "#");
+    XSLTAttributeDef patternSeparatorAttr = new XSLTAttributeDef(null,
+                                              "pattern-separator",
+                                              XSLTAttributeDef.T_CHAR, false,XSLTAttributeDef.ERROR, ";");
+    // xsl:decimal-format                                         
+    XSLTAttributeDef groupingSeparatorAttr = new XSLTAttributeDef(null,
+                                               "grouping-separator",
+                                               XSLTAttributeDef.T_CHAR, false,XSLTAttributeDef.ERROR,",");
+
+                                              
+    // xsl:element, xsl:attribute-set, xsl:copy                                           
+    XSLTAttributeDef useAttributeSetsAttr = new XSLTAttributeDef(null,
+                                              "use-attribute-sets",
+                                              XSLTAttributeDef.T_QNAMES,
+                                              false, false, XSLTAttributeDef.ERROR);
+
+    // xsl:if, xsl:when         
+    XSLTAttributeDef testAttrRequired = new XSLTAttributeDef(null, "test",   
+                                          XSLTAttributeDef.T_EXPR, true, false,XSLTAttributeDef.ERROR);
+      
+      
+    // Required.                                       
+    // xsl:value-of, xsl:for-each, xsl:copy-of                             
+    XSLTAttributeDef selectAttrRequired = new XSLTAttributeDef(null,
+                                            "select",
+                                            XSLTAttributeDef.T_EXPR, true, false,XSLTAttributeDef.ERROR);
+
+    // Optional.                                          
+    // xsl:variable, xsl:param, xsl:with-param                                       
+    XSLTAttributeDef selectAttrOpt = new XSLTAttributeDef(null, "select",
+                                       XSLTAttributeDef.T_EXPR, false, false,XSLTAttributeDef.ERROR);
+
+    // Optional.
+    // Default: "node()"
+    // xsl:apply-templates                                           
+    XSLTAttributeDef selectAttrDefNode = new XSLTAttributeDef(null, "select",
+                                           XSLTAttributeDef.T_EXPR, false,XSLTAttributeDef.ERROR, "node()");
+    // Optional.
+    // Default: "."
+    // xsl:sort                                        
+    XSLTAttributeDef selectAttrDefDot = new XSLTAttributeDef(null, "select",
+                                          XSLTAttributeDef.T_EXPR, false,XSLTAttributeDef.ERROR, ".");
+    // xsl:key                                      
+    XSLTAttributeDef matchAttrRequired = new XSLTAttributeDef(null, "match",
+                                           XSLTAttributeDef.T_PATTERN, true, false,XSLTAttributeDef.ERROR);
+    // xsl:template                                       
+    XSLTAttributeDef matchAttrOpt = new XSLTAttributeDef(null, "match",
+                                      XSLTAttributeDef.T_PATTERN, false, false,XSLTAttributeDef.ERROR);
+    // xsl:template                                  
+    XSLTAttributeDef priorityAttr = new XSLTAttributeDef(null, "priority",
+                                     XSLTAttributeDef.T_NUMBER, false, false,XSLTAttributeDef.ERROR);
+                                     
+    // xsl:template, xsl:apply-templates                                 
+    XSLTAttributeDef modeAttr = new XSLTAttributeDef(null, "mode",
+                                     XSLTAttributeDef.T_QNAME, false, false,XSLTAttributeDef.ERROR);
+   
+    XSLTAttributeDef spaceAttr =
+      new XSLTAttributeDef(Constants.S_XMLNAMESPACEURI, "space", false, false, false, XSLTAttributeDef.WARNING,
+                           "default", Constants.ATTRVAL_STRIP, "preserve",
+                           Constants.ATTRVAL_PRESERVE);
+                           
+                         
+    XSLTAttributeDef spaceAttrLiteral =
+      new XSLTAttributeDef(Constants.S_XMLNAMESPACEURI, "space", 
+                                          XSLTAttributeDef.T_URL, false, true,XSLTAttributeDef.ERROR);
+    // xsl:namespace-alias                                      
+    XSLTAttributeDef stylesheetPrefixAttr = new XSLTAttributeDef(null,
+                                              "stylesheet-prefix",
+                                              XSLTAttributeDef.T_CDATA, true, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef resultPrefixAttr = new XSLTAttributeDef(null,
+                                          "result-prefix",
+                                          XSLTAttributeDef.T_CDATA, true, false,XSLTAttributeDef.ERROR);
+                                          
+    // xsl:text, xsl:value-of                                      
+    XSLTAttributeDef disableOutputEscapingAttr = new XSLTAttributeDef(null,
+                                                   "disable-output-escaping",
+                                                   XSLTAttributeDef.T_YESNO,
+                                                   false, false,XSLTAttributeDef.ERROR);
+                                                   
+	// xsl:number                                                   
+    XSLTAttributeDef levelAttr = new XSLTAttributeDef(null, "level", false, false, false, XSLTAttributeDef.ERROR,
+                                   "single", Constants.NUMBERLEVEL_SINGLE,
+                                   "multiple", Constants.NUMBERLEVEL_MULTI,
+                                   "any", Constants.NUMBERLEVEL_ANY);
+    levelAttr.setDefault("single");
+    XSLTAttributeDef countAttr = new XSLTAttributeDef(null, "count",
+                                   XSLTAttributeDef.T_PATTERN, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef fromAttr = new XSLTAttributeDef(null, "from",
+                                  XSLTAttributeDef.T_PATTERN, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef valueAttr = new XSLTAttributeDef(null, "value",
+                                   XSLTAttributeDef.T_EXPR, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef formatAttr = new XSLTAttributeDef(null, "format",
+                                    XSLTAttributeDef.T_CDATA, false, true,XSLTAttributeDef.ERROR);
+    formatAttr.setDefault("1");
+    
+    // xsl:number, xsl:sort
+    XSLTAttributeDef langAttr = new XSLTAttributeDef(null, "lang",
+                                  XSLTAttributeDef.T_NMTOKEN, false, true,XSLTAttributeDef.ERROR);
+   
+    // xsl:number
+    XSLTAttributeDef letterValueAttr = new XSLTAttributeDef(null,
+                                         "letter-value",
+                                         false, true, false, XSLTAttributeDef.ERROR,
+                                         "alphabetic", Constants.NUMBERLETTER_ALPHABETIC,
+                                         "traditional", Constants.NUMBERLETTER_TRADITIONAL);
+    // xsl:number
+    XSLTAttributeDef groupingSeparatorAVT = new XSLTAttributeDef(null,
+                                              "grouping-separator",
+                                              XSLTAttributeDef.T_CHAR, false, true,XSLTAttributeDef.ERROR);
+    // xsl:number
+    XSLTAttributeDef groupingSizeAttr = new XSLTAttributeDef(null,
+                                          "grouping-size",
+                                          XSLTAttributeDef.T_NUMBER, false, true,XSLTAttributeDef.ERROR);
+   
+   // xsl:sort
+    XSLTAttributeDef dataTypeAttr = new XSLTAttributeDef(null, "data-type", false, true, true, XSLTAttributeDef.ERROR,
+                                    "text", Constants.SORTDATATYPE_TEXT ,"number", Constants.SORTDATATYPE_TEXT);
+	dataTypeAttr.setDefault("text");
+	
+	// xsl:sort
+    XSLTAttributeDef orderAttr = new XSLTAttributeDef(null, "order", false, true, false,XSLTAttributeDef.ERROR,
+                                    "ascending", Constants.SORTORDER_ASCENDING, 
+                                    "descending", Constants.SORTORDER_DESCENDING);
+    orderAttr.setDefault("ascending");
+
+    // xsl:sort                             
+    XSLTAttributeDef caseOrderAttr = new XSLTAttributeDef(null, "case-order", false, true, false,XSLTAttributeDef.ERROR,
+                                       "upper-first", Constants.SORTCASEORDER_UPPERFIRST ,
+                                       "lower-first", Constants.SORTCASEORDER_LOWERFIRST);
+	    
+    // xsl:message                                   
+    XSLTAttributeDef terminateAttr = new XSLTAttributeDef(null, "terminate",
+                                       XSLTAttributeDef.T_YESNO, false, false,XSLTAttributeDef.ERROR);
+    terminateAttr.setDefault("no");
+
+	// top level attributes
+    XSLTAttributeDef xslExcludeResultPrefixesAttr =
+      new XSLTAttributeDef(Constants.S_XSLNAMESPACEURL,
+                           "exclude-result-prefixes",
+                           XSLTAttributeDef.T_PREFIXLIST, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef xslExtensionElementPrefixesAttr =
+      new XSLTAttributeDef(Constants.S_XSLNAMESPACEURL,
+                           "extension-element-prefixes",
+                           XSLTAttributeDef.T_PREFIX_URLLIST, false, false,XSLTAttributeDef.ERROR);
+    // result-element-atts                       
+    XSLTAttributeDef xslUseAttributeSetsAttr =
+      new XSLTAttributeDef(Constants.S_XSLNAMESPACEURL, "use-attribute-sets",
+                           XSLTAttributeDef.T_QNAMES, false, false,XSLTAttributeDef.ERROR);
+    XSLTAttributeDef xslVersionAttr =
+      new XSLTAttributeDef(Constants.S_XSLNAMESPACEURL, "version",
+                           XSLTAttributeDef.T_NMTOKEN, false, false,XSLTAttributeDef.ERROR);
+                           
+    XSLTElementDef charData = new XSLTElementDef(this, null, "text()",
+                                null /*alias */, null /* elements */, null,  /* attributes */
+                                new ProcessorCharacters(),
+                                ElemTextLiteral.class /* class object */);
+
+    charData.setType(XSLTElementDef.T_PCDATA);
+
+    XSLTElementDef whiteSpaceOnly = new XSLTElementDef(this, null, "text()",
+                                      null /*alias */, null /* elements */,
+                                      null,  /* attributes */
+                                      null,
+                                      ElemTextLiteral.class /* should be null? -sb */);
+
+    charData.setType(XSLTElementDef.T_PCDATA);
+
+    XSLTAttributeDef resultAttr = new XSLTAttributeDef(null, "*",
+                                    XSLTAttributeDef.T_AVT, false, true,XSLTAttributeDef.WARNING);
+    XSLTAttributeDef xslResultAttr =
+      new XSLTAttributeDef(Constants.S_XSLNAMESPACEURL, "*",
+                           XSLTAttributeDef.T_CDATA, false, false,XSLTAttributeDef.WARNING);
+                           
+    XSLTElementDef[] templateElements = new XSLTElementDef[23];
+    XSLTElementDef[] templateElementsAndParams = new XSLTElementDef[24];
+    XSLTElementDef[] templateElementsAndSort = new XSLTElementDef[24];
+    //exslt
+    XSLTElementDef[] exsltFunctionElements = new XSLTElementDef[24];
+    
+    XSLTElementDef[] charTemplateElements = new XSLTElementDef[15];
+    XSLTElementDef resultElement = new XSLTElementDef(this, null, "*",
+                                     null /*alias */,
+                                     templateElements /* elements */,
+                                     new XSLTAttributeDef[]{
+                                       spaceAttrLiteral, // special
+                                       xslExcludeResultPrefixesAttr,
+                                       xslExtensionElementPrefixesAttr,
+                                       xslUseAttributeSetsAttr,
+                                       xslVersionAttr,
+                                       xslResultAttr,
+                                       resultAttr }, 
+                                        new ProcessorLRE(),
+                                     ElemLiteralResult.class /* class object */, 20, true);
+    XSLTElementDef unknownElement =
+      new XSLTElementDef(this, "*", "unknown", null /*alias */,
+                         templateElementsAndParams /* elements */,
+                         new XSLTAttributeDef[]{ xslExcludeResultPrefixesAttr,
+                                                 xslExtensionElementPrefixesAttr,
+                                                 xslUseAttributeSetsAttr,
+                                                 xslVersionAttr,
+                                                 xslResultAttr,
+                                                 resultAttr }, 
+                                                                                                 new ProcessorUnknown(),
+                         ElemUnknown.class /* class object */, 20, true);
+    XSLTElementDef xslValueOf = new XSLTElementDef(this,
+                                  Constants.S_XSLNAMESPACEURL, "value-of",
+                                  null /*alias */, null /* elements */,
+                                  new XSLTAttributeDef[]{ selectAttrRequired,
+                                                          disableOutputEscapingAttr }, 
+                                               new ProcessorTemplateElem(),
+                                  ElemValueOf.class /* class object */, 20, true);
+    XSLTElementDef xslCopyOf = new XSLTElementDef(this,
+                                 Constants.S_XSLNAMESPACEURL, "copy-of",
+                                 null /*alias */, null /* elements */,
+                                 new XSLTAttributeDef[]{ selectAttrRequired },
+                                 new ProcessorTemplateElem(),
+                                 ElemCopyOf.class /* class object */, 20, true);
+    XSLTElementDef xslNumber = new XSLTElementDef(this,
+                                 Constants.S_XSLNAMESPACEURL, "number",
+                                 null /*alias */, null /* elements */,
+                                 new XSLTAttributeDef[]{ levelAttr,
+                                                         countAttr,
+                                                         fromAttr,
+                                                         valueAttr,
+                                                         formatAttr,
+                                                         langAttr,
+                                                         letterValueAttr,
+                                                         groupingSeparatorAVT,
+                                                         groupingSizeAttr }, 
+                                        new ProcessorTemplateElem(),
+                                 ElemNumber.class /* class object */, 20, true);
+
+    // <!-- xsl:sort cannot occur after any other elements or
+    // any non-whitespace character -->
+    XSLTElementDef xslSort = new XSLTElementDef(this,
+                                                Constants.S_XSLNAMESPACEURL,
+                                                "sort", null /*alias */,
+                                                null /* elements */,
+                                                new XSLTAttributeDef[]{
+                                                  selectAttrDefDot,
+                                                  langAttr,
+                                                  dataTypeAttr,
+                                                  orderAttr,
+                                                  caseOrderAttr }, 
+                                       new ProcessorTemplateElem(),
+                                                ElemSort.class/* class object */, 19, true );
+    XSLTElementDef xslWithParam = new XSLTElementDef(this,
+                                    Constants.S_XSLNAMESPACEURL,
+                                    "with-param", null /*alias */,
+                                    templateElements /* elements */,  // %template;>
+                                    new XSLTAttributeDef[]{ nameAttrRequired,
+                                                            selectAttrOpt }, new ProcessorTemplateElem(),
+                                                                             ElemWithParam.class /* class object */, 19, true);
+    XSLTElementDef xslApplyTemplates = new XSLTElementDef(this,
+                                         Constants.S_XSLNAMESPACEURL,
+                                         "apply-templates", null /*alias */,
+                                         new XSLTElementDef[]{ xslSort,
+                                                               xslWithParam } /* elements */, new XSLTAttributeDef[]{
+                                                                 selectAttrDefNode,
+                                                                 modeAttr }, 
+                                                                        new ProcessorTemplateElem(),
+                                         ElemApplyTemplates.class /* class object */, 20, true);
+    XSLTElementDef xslApplyImports =
+      new XSLTElementDef(this, Constants.S_XSLNAMESPACEURL, "apply-imports",
+                         null /*alias */, null /* elements */,
+                         new XSLTAttributeDef[]{},
+                         new ProcessorTemplateElem(),
+                         ElemApplyImport.class /* class object */);
+    XSLTElementDef xslForEach = new XSLTElementDef(this,
+                                  Constants.S_XSLNAMESPACEURL, "for-each",
+                                  null /*alias */, templateElementsAndSort,  // (#PCDATA %instructions; %result-elements; | xsl:sort)*
+                                  new XSLTAttributeDef[]{ selectAttrRequired,
+                                                          spaceAttr }, 
+                                               new ProcessorTemplateElem(),
+                                  ElemForEach.class /* class object */, true, false, true, 20, true);
+    XSLTElementDef xslIf = new XSLTElementDef(this,
+                                              Constants.S_XSLNAMESPACEURL,
+                                              "if", null /*alias */,
+                                              templateElements /* elements */,  // %template;
+                                              new XSLTAttributeDef[]{
+                                                testAttrRequired,
+                                                spaceAttr }, new ProcessorTemplateElem(),
+                                                             ElemIf.class /* class object */, 20, true);
+    XSLTElementDef xslWhen =
+      new XSLTElementDef(this, Constants.S_XSLNAMESPACEURL, "when",
+                         null /*alias */, templateElements /* elements */,  // %template;>
+                                                new XSLTAttributeDef[]{
+                                                  testAttrRequired,
+                                                  spaceAttr }, new ProcessorTemplateElem(),
+                                                               ElemWhen.class /* class object */,
+                                                                                                false, true, 1, true);
+    XSLTElementDef xslOtherwise = new XSLTElementDef(this,
+                                    Constants.S_XSLNAMESPACEURL, "otherwise",
+                                    null /*alias */,
+                                    templateElements /* elements */,  // %template;>
+                                    new XSLTAttributeDef[]{ spaceAttr },
+                                    new ProcessorTemplateElem(),
+                                    ElemOtherwise.class /* class object */,
+                                                       false, false, 2, false);
+    XSLTElementDef xslChoose = new XSLTElementDef(this,
+                                 Constants.S_XSLNAMESPACEURL, "choose",
+                                 null /*alias */,
+                                 new XSLTElementDef[]{ xslWhen,
+                                                       xslOtherwise } /* elements */, 
+                                        new XSLTAttributeDef[]{ spaceAttr },
+                                 new ProcessorTemplateElem(),
+                                 ElemChoose.class /* class object */, true, false, true, 20, true);                                
+    XSLTElementDef xslAttribute = new XSLTElementDef(this,
+                                    Constants.S_XSLNAMESPACEURL, "attribute",
+                                    null /*alias */,
+                                    charTemplateElements /* elements */,  // %char-template;>
+                                    new XSLTAttributeDef[]{ nameAVTRequired,
+                                                            namespaceAVTOpt,
+                                                            spaceAttr }, 
+                                    new ProcessorTemplateElem(),
+                                    ElemAttribute.class /* class object */, 20, true);
+    XSLTElementDef xslCallTemplate =
+      new XSLTElementDef(this, Constants.S_XSLNAMESPACEURL, "call-template",
+                         null /*alias */,
+                         new XSLTElementDef[]{ xslWithParam } /* elements */,
+                         new XSLTAttributeDef[]{ nameAttrRequired },
+                         new ProcessorTemplateElem(),
+                         ElemCallTemplate.class /* class object */, 20, true);
+    XSLTElementDef xslVariable = new XSLTElementDef(this,
+                                   Constants.S_XSLNAMESPACEURL, "variable",
+                                   null /*alias */,
+                                   templateElements /* elements */,  // %template;>
+                                   new XSLTAttributeDef[]{ nameAttrRequired,
+                                                           selectAttrOpt }, 
+                                  new ProcessorTemplateElem(),
+                                   ElemVariable.class /* class object */, 20, true);
+    XSLTElementDef xslParam = new XSLTElementDef(this,
+                                Constants.S_XSLNAMESPACEURL, "param",
+                                null /*alias */,
+                                templateElements /* elements */,  // %template;>
+                                new XSLTAttributeDef[]{ nameAttrRequired,
+                                                        selectAttrOpt }, 
+                                       new ProcessorTemplateElem(),
+                                ElemParam.class /* class object */, 19, true);
+    XSLTElementDef xslText =
+      new XSLTElementDef(this, Constants.S_XSLNAMESPACEURL, "text",
+                         null /*alias */,
+                         new XSLTElementDef[]{ charData } /* elements */,
+                         new XSLTAttributeDef[]{ disableOutputEscapingAttr },
+                         new ProcessorText(),
+                         ElemText.class /* class object */, 20, true);
+    XSLTElementDef xslProcessingInstruction =
+      new XSLTElementDef(this, Constants.S_XSLNAMESPACEURL,
+                         "processing-instruction", null /*alias */,
+                         charTemplateElements /* elements */,  // %char-template;>
+                         new XSLTAttributeDef[]{
+                                                  nameAVT_NCNAMERequired,
+                                                  spaceAttr }, 
+                                        new ProcessorTemplateElem(),
+                          ElemPI.class /* class object */, 20, true);
+    XSLTElementDef xslElement = new XSLTElementDef(this,
+                                  Constants.S_XSLNAMESPACEURL, "element",
+                                  null /*alias */,
+                                  templateElements /* elements */,  // %template;
+                                  new XSLTAttributeDef[]{ nameAVTRequired,
+                                                          namespaceAVTOpt,
+                                                          useAttributeSetsAttr,
+                                                          spaceAttr }, 
+                                               new ProcessorTemplateElem(),
+                                  ElemElement.class /* class object */, 20, true);
+    XSLTElementDef xslComment = new XSLTElementDef(this,
+                                  Constants.S_XSLNAMESPACEURL, "comment",
+                                  null /*alias */,
+                                  charTemplateElements /* elements */,  // %char-template;>
+                                  new XSLTAttributeDef[]{ spaceAttr },
+                                  new ProcessorTemplateElem(),
+                                  ElemComment.class /* class object */, 20, true);
+    XSLTElementDef xslCopy =
+      new XSLTElementDef(this, Constants.S_XSLNAMESPACEURL, "copy",
+                         null /*alias */, templateElements /* elements */,  // %template;>
+                          new XSLTAttributeDef[]{
+                                                  spaceAttr,
+                                                  useAttributeSetsAttr }, 
+                                        new ProcessorTemplateElem(),
+                          ElemCopy.class /* class object */, 20, true);
+    XSLTElementDef xslMessage = new XSLTElementDef(this,
+                                  Constants.S_XSLNAMESPACEURL, "message",
+                                  null /*alias */,
+                                  templateElements /* elements */,  // %template;>
+                                  new XSLTAttributeDef[]{ terminateAttr },
+                                  new ProcessorTemplateElem(),
+                                  ElemMessage.class /* class object */, 20, true);
+    XSLTElementDef xslFallback = new XSLTElementDef(this,
+                                   Constants.S_XSLNAMESPACEURL, "fallback",
+                                   null /*alias */,
+                                   templateElements /* elements */,  // %template;>
+                                   new XSLTAttributeDef[]{ spaceAttr },
+                                   new ProcessorTemplateElem(),
+                                   ElemFallback.class /* class object */, 20, true);
+    //exslt
+    XSLTElementDef exsltFunction =
+                                  new XSLTElementDef(this, 
+                                  Constants.S_EXSLT_FUNCTIONS_URL, 
+                                  "function",
+                                  null /*alias */,
+                                  exsltFunctionElements /* elements */,
+                                  new XSLTAttributeDef[]{ nameAttrRequired },
+                                  new ProcessorExsltFunction(),
+                                  ElemExsltFunction.class /* class object */);
+    XSLTElementDef exsltResult =
+                                  new XSLTElementDef(this, 
+                                  Constants.S_EXSLT_FUNCTIONS_URL, 
+                                  "result",
+                                  null /*alias */,
+                                  templateElements /* elements */,
+                                  new XSLTAttributeDef[]{ selectAttrOpt },
+                                  new ProcessorExsltFuncResult(),
+                                  ElemExsltFuncResult.class  /* class object */);            
+    
+
+    int i = 0;
+
+    templateElements[i++] = charData;  // #PCDATA
+
+    // char-instructions
+    templateElements[i++] = xslApplyTemplates;
+    templateElements[i++] = xslCallTemplate;
+    templateElements[i++] = xslApplyImports;
+    templateElements[i++] = xslForEach;
+    templateElements[i++] = xslValueOf;
+    templateElements[i++] = xslCopyOf;
+    templateElements[i++] = xslNumber;
+    templateElements[i++] = xslChoose;
+    templateElements[i++] = xslIf;
+    templateElements[i++] = xslText;
+    templateElements[i++] = xslCopy;
+    templateElements[i++] = xslVariable;
+    templateElements[i++] = xslMessage;
+    templateElements[i++] = xslFallback;
+
+    // instructions
+    templateElements[i++] = xslProcessingInstruction;
+    templateElements[i++] = xslComment;
+    templateElements[i++] = xslElement;
+    templateElements[i++] = xslAttribute;
+    templateElements[i++] = resultElement;
+    templateElements[i++] = unknownElement;
+    templateElements[i++] = exsltFunction;
+    templateElements[i++] = exsltResult;
+
+    System.arraycopy(templateElements, 0, templateElementsAndParams, 0, i);
+    System.arraycopy(templateElements, 0, templateElementsAndSort, 0, i);
+    System.arraycopy(templateElements, 0, exsltFunctionElements, 0, i);
+    
+    templateElementsAndParams[i] = xslParam;
+    templateElementsAndSort[i] = xslSort;
+    exsltFunctionElements[i]   = xslParam;
+
+    i = 0;
+    charTemplateElements[i++] = charData;  // #PCDATA
+
+    // char-instructions
+    charTemplateElements[i++] = xslApplyTemplates;
+    charTemplateElements[i++] = xslCallTemplate;
+    charTemplateElements[i++] = xslApplyImports;
+    charTemplateElements[i++] = xslForEach;
+    charTemplateElements[i++] = xslValueOf;
+    charTemplateElements[i++] = xslCopyOf;
+    charTemplateElements[i++] = xslNumber;
+    charTemplateElements[i++] = xslChoose;
+    charTemplateElements[i++] = xslIf;
+    charTemplateElements[i++] = xslText;
+    charTemplateElements[i++] = xslCopy;
+    charTemplateElements[i++] = xslVariable;
+    charTemplateElements[i++] = xslMessage;
+    charTemplateElements[i++] = xslFallback;
+
+    XSLTElementDef importDef = new XSLTElementDef(this,
+                                 Constants.S_XSLNAMESPACEURL, "import",
+                                 null /*alias */, null /* elements */,
+                                 new XSLTAttributeDef[]{ hrefAttr },  // EMPTY
+                                 new ProcessorImport(),
+                                 null /* class object */,
+                                        1, true);
+    XSLTElementDef includeDef = new XSLTElementDef(this,
+                                  Constants.S_XSLNAMESPACEURL, "include",
+                                  null /*alias */, null /* elements */,  // EMPTY
+                                  new XSLTAttributeDef[]{ hrefAttr },
+                                  new ProcessorInclude(),
+                                  null /* class object */,
+                                               20, true);
+    
+    XSLTAttributeDef[] scriptAttrs = new XSLTAttributeDef[]{
+    					    new XSLTAttributeDef(null, "lang", XSLTAttributeDef.T_NMTOKEN,
+                                                                 true, false,XSLTAttributeDef.WARNING),
+                                            new XSLTAttributeDef(null, "src", XSLTAttributeDef.T_URL, 
+                                            			 false, false,XSLTAttributeDef.WARNING)};
+
+    XSLTAttributeDef[] componentAttrs = new XSLTAttributeDef[]{ 
+                                            new XSLTAttributeDef(null, "prefix", XSLTAttributeDef.T_NMTOKEN, 
+                                            			 true, false,XSLTAttributeDef.WARNING),
+                                            new XSLTAttributeDef(null, "elements", XSLTAttributeDef.T_STRINGLIST, 
+                                            			 false, false,XSLTAttributeDef.WARNING),
+                                            new XSLTAttributeDef(null, "functions", XSLTAttributeDef.T_STRINGLIST, 
+                                            			 false, false,XSLTAttributeDef.WARNING) };
+
+    XSLTElementDef[] topLevelElements = new XSLTElementDef[]
+                                 {includeDef,
+                                  importDef,
+                                  // resultElement,
+                                  whiteSpaceOnly,
+                                  unknownElement,
+                                  new XSLTElementDef(
+                                         this,
+                                         Constants.S_XSLNAMESPACEURL,
+                                         "strip-space",
+                                         null /*alias */,
+                                         null /* elements */,
+                                         new XSLTAttributeDef[]{
+                                                elementsAttr },
+                                                new ProcessorStripSpace(),
+                                         null /* class object */, 20, true),
+                                  new XSLTElementDef(
+                                         this,
+                                         Constants.S_XSLNAMESPACEURL,
+                                         "preserve-space",
+                                         null /*alias */,
+                                         null /* elements */,
+                                         new XSLTAttributeDef[]{
+                                                 elementsAttr },
+                                                 new ProcessorPreserveSpace(),
+                                         null /* class object */, 20, true),
+                                  new XSLTElementDef(
+                                         this,
+                                         Constants.S_XSLNAMESPACEURL,
+                                         "output",
+                                         null /*alias */,
+                                         null /* elements */,
+                                         new XSLTAttributeDef[]{
+                                                  methodAttr,
+                                                  versionAttr,
+                                                  encodingAttr,
+                                                  omitXmlDeclarationAttr,
+                                                  standaloneAttr,
+                                                  doctypePublicAttr,
+                                                  doctypeSystemAttr,
+                                                  cdataSectionElementsAttr,
+                                                  indentAttr,
+                                                  mediaTypeAttr,
+                                                  XSLTAttributeDef.m_foreignAttr }, 
+                                          new ProcessorOutputElem(), null /* class object */, 20, true), 
+                                  new XSLTElementDef(
+                                          this,
+                                          Constants.S_XSLNAMESPACEURL,
+                                          "key",
+                                          null /*alias */,
+                                          null /* elements */,  // EMPTY
+                                          new XSLTAttributeDef[]{ nameAttrRequired,
+                                                  matchAttrRequired,
+                                                  useAttr }, 
+                                          new ProcessorKey(), null /* class object */, 20, true),
+                                  new XSLTElementDef(
+                                          this,
+                                          Constants.S_XSLNAMESPACEURL,
+                                          "decimal-format",
+                                          null /*alias */,
+                                          null /* elements */,  // EMPTY
+                                          new XSLTAttributeDef[]{
+                                                  nameAttrOpt_ERROR,
+                                                  decimalSeparatorAttr,
+                                                  groupingSeparatorAttr,
+                                                  infinityAttr,
+                                                  minusSignAttr,
+                                                  NaNAttr,
+                                                  percentAttr,
+                                                  perMilleAttr,
+                                                  zeroDigitAttr,
+                                                  digitAttr,
+                                                  patternSeparatorAttr }, 
+                                           new ProcessorDecimalFormat(),
+                                           null /* class object */, 20, true),
+                                  new XSLTElementDef(
+                                           this,
+                                           Constants.S_XSLNAMESPACEURL,
+                                           "attribute-set",
+                                           null /*alias */,
+                                           new XSLTElementDef[]{
+                                                   xslAttribute } /* elements */,
+                                           new XSLTAttributeDef[]{
+                                                   nameAttrRequired,
+                                                   useAttributeSetsAttr }, 
+                                           new ProcessorAttributeSet(),
+                                           null /* class object */, 20, true),
+                                  new XSLTElementDef(
+                                           this,
+                                           Constants.S_XSLNAMESPACEURL,
+                                           "variable",
+                                           null /*alias */,
+                                           templateElements /* elements */,
+                                           new XSLTAttributeDef[]{
+                                                   nameAttrRequired,
+                                                   selectAttrOpt }, 
+                                           new ProcessorGlobalVariableDecl(),
+                                           ElemVariable.class /* class object */, 20, true),
+                                  new XSLTElementDef(
+                                           this,
+                                           Constants.S_XSLNAMESPACEURL,
+                                           "param",
+                                           null /*alias */,
+                                           templateElements /* elements */,
+                                           new XSLTAttributeDef[]{
+                                                   nameAttrRequired,
+                                                   selectAttrOpt }, 
+                                           new ProcessorGlobalParamDecl(),
+                                           ElemParam.class /* class object */, 20, true),
+                                  new XSLTElementDef(
+                                           this,
+                                           Constants.S_XSLNAMESPACEURL,
+                                           "template",
+                                           null /*alias */,
+                                           templateElementsAndParams /* elements */,
+                                           new XSLTAttributeDef[]{
+                                                   matchAttrOpt,
+                                                   nameAttrOpt_ERROR,
+                                                   priorityAttr,
+                                                   modeAttr,
+                                                   spaceAttr }, 
+                                           new ProcessorTemplate(), ElemTemplate.class /* class object */, true, 20, true), 
+                                  new XSLTElementDef(
+                                           this,
+                                           Constants.S_XSLNAMESPACEURL,
+                                           "namespace-alias",
+                                           null /*alias */,
+                                          null /* elements */,  // EMPTY
+                                           new XSLTAttributeDef[]{ 
+                                                   stylesheetPrefixAttr,
+                                                   resultPrefixAttr }, 
+                                           new ProcessorNamespaceAlias(), null /* class object */, 20, true),
+                                  new XSLTElementDef(
+                                           this,
+                                           Constants.S_BUILTIN_EXTENSIONS_URL,
+                                           "component",
+                                           null /*alias */,
+                                           new XSLTElementDef[]{
+                                                    new XSLTElementDef(
+                                                        this,
+                                                        Constants.S_BUILTIN_EXTENSIONS_URL,
+                                                        "script",
+                                                        null /*alias */,
+                                                    	new XSLTElementDef[]{ 
+                                                        charData } /* elements */,
+                                                        scriptAttrs, 
+                                                        new ProcessorLRE(),
+                                                        ElemExtensionScript.class /* class object */, 20, true) },  // EMPTY
+                                           componentAttrs, 
+                                           new ProcessorLRE(), ElemExtensionDecl.class /* class object */),
+                                  new XSLTElementDef(
+                                           this,
+                                           Constants.S_BUILTIN_OLD_EXTENSIONS_URL,
+                                           "component",
+                                           null /*alias */,
+                                           new XSLTElementDef[]{
+                                                    new XSLTElementDef(
+                                                        this,
+                                                        Constants.S_BUILTIN_OLD_EXTENSIONS_URL,
+                                                        "script",
+                                                        null /*alias */,
+                                                    	new XSLTElementDef[]{ 
+                                                        charData } /* elements */,
+                                                        scriptAttrs, 
+                                                        new ProcessorLRE(),
+                                                        ElemExtensionScript.class /* class object */, 20, true) },  // EMPTY
+                                           componentAttrs, 
+                                           new ProcessorLRE(), ElemExtensionDecl.class /* class object */),
+                                  exsltFunction}/* exslt */;  //end of topevelElements
+    
+    XSLTAttributeDef excludeResultPrefixesAttr =
+      new XSLTAttributeDef(null, "exclude-result-prefixes",
+                           XSLTAttributeDef.T_PREFIXLIST, false,false,XSLTAttributeDef.WARNING);
+    XSLTAttributeDef extensionElementPrefixesAttr =
+      new XSLTAttributeDef(null, "extension-element-prefixes",
+                           XSLTAttributeDef.T_PREFIX_URLLIST, false,false,XSLTAttributeDef.WARNING);
+    XSLTAttributeDef idAttr = new XSLTAttributeDef(null, "id",
+                                XSLTAttributeDef.T_CDATA, false,false,XSLTAttributeDef.WARNING);
+    XSLTAttributeDef versionAttrRequired = new XSLTAttributeDef(null,
+                                             "version",
+                                             XSLTAttributeDef.T_NMTOKEN,
+                                             true,false,XSLTAttributeDef.WARNING);
+    XSLTElementDef stylesheetElemDef = new XSLTElementDef(this,
+                                         Constants.S_XSLNAMESPACEURL,
+                                         "stylesheet", "transform",
+                                         topLevelElements,
+                                         new XSLTAttributeDef[]{
+                                           extensionElementPrefixesAttr,
+                                           excludeResultPrefixesAttr,
+                                           idAttr,
+                                           versionAttrRequired,
+                                           spaceAttr }, new ProcessorStylesheetElement(),  /* ContentHandler */
+                                         null  /* class object */,
+                                         true, -1, false);
+
+    importDef.setElements(new XSLTElementDef[]{ stylesheetElemDef,
+                                                resultElement,
+                                                unknownElement });
+    includeDef.setElements(new XSLTElementDef[]{ stylesheetElemDef,
+                                                 resultElement,
+                                                 unknownElement });
+    build(null, null, null, new XSLTElementDef[]{ stylesheetElemDef,
+                                                  whiteSpaceOnly,
+                                                  resultElement,
+                                                  unknownElement }, null,
+                                                                    new ProcessorStylesheetDoc(),  /* ContentHandler */
+                                                                    null  /* class object */
+                                                                      );
+  }
+
+  /**
+   * A hashtable of all available built-in elements for use by the element-available
+   * function.
+   * TODO:  When we convert to Java2, this should be a Set.
+   */
+  private HashMap m_availElems = new HashMap();
+  
+  /**
+   * Get the table of available elements.
+   * 
+   * @return table of available elements, keyed by qualified names, and with 
+   * values of the same qualified names.
+   */
+  public HashMap getElemsAvailable() 
+  {
+    return m_availElems;
+  }
+
+  /**
+   * Adds a new element name to the Hashtable of available elements.
+   * @param elemName The name of the element to add to the Hashtable of available elements.
+   */
+  void addAvailableElement(QName elemName)
+  {
+    m_availElems.put(elemName, elemName);
+  }
+
+  /**
+   * Determines whether the passed element name is present in the list of available elements.
+   * @param elemName The name of the element to look up.
+   *
+   * @return true if an element corresponding to elemName is available.
+   */
+  public boolean elementAvailable(QName elemName)
+  {
+    return m_availElems.containsKey(elemName);
+  }
+}
+
diff --git a/src/main/java/org/apache/xalan/processor/package.html b/src/main/java/org/apache/xalan/processor/package.html
new file mode 100644
index 0000000..6d1dd4e
--- /dev/null
+++ b/src/main/java/org/apache/xalan/processor/package.html
@@ -0,0 +1,41 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468640 2006-10-28 06:53:53Z minchau $ -->
+<html>
+  <title>Xalan Processor Package.</title>
+  <body>
+    <p>Parses an XSLT stylesheet document (which may include and import other stylesheet documents) and produces a StylesheetRoot
+    (a TRaX Templates object).</p>
+    
+    <p>StylesheetProcessor implements the TRaX {@link javax.xml.transform.TransformerFactory} interface,
+    as well as the {@link javax.xml.transform.sax.SAXTransformerFactory} interface. 
+    It registers the {@link org.apache.xalan.processor.StylesheetHandler} object 
+    (a TrAX {@link javax.xml.transform.sax.TemplatesHandler} implementation) 
+    as the SAX ContentHandler for an XMLReader, and uses the XMLReader to parse 
+    the stylesheet document.</p>
+    <p>Before parsing the XSLT input, StylesheetHandler assembles an {@link org.apache.xalan.processor.XSLTSchema}, 
+    which uses {@link org.apache.xalan.processor.XSLTElementDef}
+    and {@link org.apache.xalan.processor.XSLTAttributeDef} objects to 
+    recursively define the elements and attributes that an XSLT stylesheet may 
+    contain. The StylesheetHandler then passes on each parse event to the 
+    {@link org.apache.xalan.processor.XSLTElementProcessor} which the 
+    XSLTElementDef assigned to the element associated with that event.</p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xalan/res/XSLMessages.java b/src/main/java/org/apache/xalan/res/XSLMessages.java
new file mode 100644
index 0000000..cbbfd88
--- /dev/null
+++ b/src/main/java/org/apache/xalan/res/XSLMessages.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLMessages.java 468641 2006-10-28 06:54:42Z minchau $
+ */
+package org.apache.xalan.res;
+
+import java.util.ListResourceBundle;
+
+import org.apache.xpath.res.XPATHMessages;
+
+/**
+ * Sets things up for issuing error messages.  This class is misnamed, and
+ * should be called XalanMessages, or some such.
+ * @xsl.usage internal
+ */
+public class XSLMessages extends XPATHMessages
+{
+
+  /** The language specific resource object for Xalan messages.  */
+  private static ListResourceBundle XSLTBundle = new XSLTErrorResources(); // android-changed
+
+  /**
+   * Creates a message from the specified key and replacement
+   * arguments, localized to the given locale.
+   *
+   * @param msgKey    The key for the message text.
+   * @param args      The arguments to be used as replacement text
+   *                  in the message created.
+   *
+   * @return The formatted message string.
+   */
+  public static final String createMessage(String msgKey, Object args[])  //throws Exception
+  {
+      // BEGIN android-changed
+      //     don't localize resources
+      return createMsg(XSLTBundle, msgKey, args);
+      // END android-changed
+  }
+  
+  /**
+   * Creates a message from the specified key and replacement
+   * arguments, localized to the given locale.
+   *
+   * @param msgKey    The key for the message text.
+   * @param args      The arguments to be used as replacement text
+   *                  in the message created.
+   *
+   * @return The formatted warning string.
+   */
+  public static final String createWarning(String msgKey, Object args[])  //throws Exception
+  {
+      // BEGIN android-changed
+      //     don't localize exception messages
+      return createMsg(XSLTBundle, msgKey, args);
+      // END android-changed
+  }
+}
diff --git a/src/main/java/org/apache/xalan/res/XSLTErrorResources.java b/src/main/java/org/apache/xalan/res/XSLTErrorResources.java
new file mode 100644
index 0000000..8310540
--- /dev/null
+++ b/src/main/java/org/apache/xalan/res/XSLTErrorResources.java
@@ -0,0 +1,1511 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLTErrorResources.java 468641 2006-10-28 06:54:42Z minchau $
+ */
+package org.apache.xalan.res;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Set up error messages.
+ * We build a two dimensional array of message keys and
+ * message strings. In order to add a new message here,
+ * you need to first add a String constant. And 
+ *  you need to enter key , value pair as part of contents
+ * Array. You also need to update MAX_CODE for error strings
+ * and MAX_WARNING for warnings ( Needed for only information
+ * purpose )
+ */
+public class XSLTErrorResources extends ListResourceBundle
+{
+
+/*
+ * This file contains error and warning messages related to Xalan Error
+ * Handling.
+ *
+ *  General notes to translators:
+ *
+ *  1) Xalan (or more properly, Xalan-interpretive) and XSLTC are names of
+ *     components.
+ *     XSLT is an acronym for "XML Stylesheet Language: Transformations".
+ *     XSLTC is an acronym for XSLT Compiler.
+ *
+ *  2) A stylesheet is a description of how to transform an input XML document
+ *     into a resultant XML document (or HTML document or text).  The
+ *     stylesheet itself is described in the form of an XML document.
+ *
+ *  3) A template is a component of a stylesheet that is used to match a
+ *     particular portion of an input document and specifies the form of the
+ *     corresponding portion of the output document.
+ *
+ *  4) An element is a mark-up tag in an XML document; an attribute is a
+ *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+ *     "elem" is an element name, "attr" and "attr2" are attribute names with
+ *     the values "val" and "val2", respectively.
+ *
+ *  5) A namespace declaration is a special attribute that is used to associate
+ *     a prefix with a URI (the namespace).  The meanings of element names and
+ *     attribute names that use that prefix are defined with respect to that
+ *     namespace.
+ *
+ *  6) "Translet" is an invented term that describes the class file that
+ *     results from compiling an XML stylesheet into a Java class.
+ *
+ *  7) XPath is a specification that describes a notation for identifying
+ *     nodes in a tree-structured representation of an XML document.  An
+ *     instance of that notation is referred to as an XPath expression.
+ *
+ */
+
+  /* 
+   * Static variables
+   */
+  public static final String ER_INVALID_NAMESPACE_URI_VALUE_FOR_RESULT_PREFIX = 
+	"ER_INVALID_SET_NAMESPACE_URI_VALUE_FOR_RESULT_PREFIX"; 
+	
+  public static final String ER_INVALID_NAMESPACE_URI_VALUE_FOR_RESULT_PREFIX_FOR_DEFAULT =
+	"ER_INVALID_NAMESPACE_URI_VALUE_FOR_RESULT_PREFIX_FOR_DEFAULT";
+   
+  public static final String ER_NO_CURLYBRACE = "ER_NO_CURLYBRACE";
+  public static final String ER_FUNCTION_NOT_SUPPORTED = "ER_FUNCTION_NOT_SUPPORTED";
+  public static final String ER_ILLEGAL_ATTRIBUTE = "ER_ILLEGAL_ATTRIBUTE";
+  public static final String ER_NULL_SOURCENODE_APPLYIMPORTS = "ER_NULL_SOURCENODE_APPLYIMPORTS";
+  public static final String ER_CANNOT_ADD = "ER_CANNOT_ADD"; 
+  public static final String ER_NULL_SOURCENODE_HANDLEAPPLYTEMPLATES="ER_NULL_SOURCENODE_HANDLEAPPLYTEMPLATES";
+  public static final String ER_NO_NAME_ATTRIB = "ER_NO_NAME_ATTRIB";
+  public static final String ER_TEMPLATE_NOT_FOUND = "ER_TEMPLATE_NOT_FOUND";
+  public static final String ER_CANT_RESOLVE_NAME_AVT = "ER_CANT_RESOLVE_NAME_AVT";
+  public static final String ER_REQUIRES_ATTRIB = "ER_REQUIRES_ATTRIB";
+  public static final String ER_MUST_HAVE_TEST_ATTRIB = "ER_MUST_HAVE_TEST_ATTRIB";
+  public static final String ER_BAD_VAL_ON_LEVEL_ATTRIB =
+	 "ER_BAD_VAL_ON_LEVEL_ATTRIB";
+  public static final String ER_PROCESSINGINSTRUCTION_NAME_CANT_BE_XML =
+	 "ER_PROCESSINGINSTRUCTION_NAME_CANT_BE_XML";
+  public static final String ER_PROCESSINGINSTRUCTION_NOTVALID_NCNAME = 
+	 "ER_PROCESSINGINSTRUCTION_NOTVALID_NCNAME";
+  public static final String ER_NEED_MATCH_ATTRIB = "ER_NEED_MATCH_ATTRIB";
+  public static final String ER_NEED_NAME_OR_MATCH_ATTRIB = 
+	 "ER_NEED_NAME_OR_MATCH_ATTRIB";
+  public static final String ER_CANT_RESOLVE_NSPREFIX =
+	 "ER_CANT_RESOLVE_NSPREFIX";
+  public static final String ER_ILLEGAL_VALUE = "ER_ILLEGAL_VALUE";
+  public static final String ER_NO_OWNERDOC = "ER_NO_OWNERDOC";
+  public static final String ER_ELEMTEMPLATEELEM_ERR ="ER_ELEMTEMPLATEELEM_ERR";
+  public static final String ER_NULL_CHILD = "ER_NULL_CHILD";
+  public static final String ER_NEED_SELECT_ATTRIB = "ER_NEED_SELECT_ATTRIB";
+  public static final String ER_NEED_TEST_ATTRIB = "ER_NEED_TEST_ATTRIB";
+  public static final String ER_NEED_NAME_ATTRIB = "ER_NEED_NAME_ATTRIB";
+  public static final String ER_NO_CONTEXT_OWNERDOC = "ER_NO_CONTEXT_OWNERDOC";
+  public static final String ER_COULD_NOT_CREATE_XML_PROC_LIAISON = 
+	 "ER_COULD_NOT_CREATE_XML_PROC_LIAISON";
+  public static final String ER_PROCESS_NOT_SUCCESSFUL = 
+	 "ER_PROCESS_NOT_SUCCESSFUL";
+  public static final String ER_NOT_SUCCESSFUL = "ER_NOT_SUCCESSFUL";
+  public static final String ER_ENCODING_NOT_SUPPORTED = 
+	 "ER_ENCODING_NOT_SUPPORTED";
+  public static final String ER_COULD_NOT_CREATE_TRACELISTENER = 
+	 "ER_COULD_NOT_CREATE_TRACELISTENER";
+  public static final String ER_KEY_REQUIRES_NAME_ATTRIB = 
+	 "ER_KEY_REQUIRES_NAME_ATTRIB";
+  public static final String ER_KEY_REQUIRES_MATCH_ATTRIB = 
+	 "ER_KEY_REQUIRES_MATCH_ATTRIB";
+  public static final String ER_KEY_REQUIRES_USE_ATTRIB = 
+	 "ER_KEY_REQUIRES_USE_ATTRIB";
+  public static final String ER_REQUIRES_ELEMENTS_ATTRIB = 
+	 "ER_REQUIRES_ELEMENTS_ATTRIB";
+  public static final String ER_MISSING_PREFIX_ATTRIB = 
+	 "ER_MISSING_PREFIX_ATTRIB";
+  public static final String ER_BAD_STYLESHEET_URL = "ER_BAD_STYLESHEET_URL";
+  public static final String ER_FILE_NOT_FOUND = "ER_FILE_NOT_FOUND";
+  public static final String ER_IOEXCEPTION = "ER_IOEXCEPTION";
+  public static final String ER_NO_HREF_ATTRIB = "ER_NO_HREF_ATTRIB";
+  public static final String ER_STYLESHEET_INCLUDES_ITSELF = 
+	 "ER_STYLESHEET_INCLUDES_ITSELF";
+  public static final String ER_PROCESSINCLUDE_ERROR ="ER_PROCESSINCLUDE_ERROR";
+  public static final String ER_MISSING_LANG_ATTRIB = "ER_MISSING_LANG_ATTRIB";
+  public static final String ER_MISSING_CONTAINER_ELEMENT_COMPONENT = 
+	 "ER_MISSING_CONTAINER_ELEMENT_COMPONENT";
+  public static final String ER_CAN_ONLY_OUTPUT_TO_ELEMENT = 
+	 "ER_CAN_ONLY_OUTPUT_TO_ELEMENT";
+  public static final String ER_PROCESS_ERROR = "ER_PROCESS_ERROR";
+  public static final String ER_UNIMPLNODE_ERROR = "ER_UNIMPLNODE_ERROR";
+  public static final String ER_NO_SELECT_EXPRESSION ="ER_NO_SELECT_EXPRESSION";
+  public static final String ER_CANNOT_SERIALIZE_XSLPROCESSOR = 
+	 "ER_CANNOT_SERIALIZE_XSLPROCESSOR";
+  public static final String ER_NO_INPUT_STYLESHEET = "ER_NO_INPUT_STYLESHEET";
+  public static final String ER_FAILED_PROCESS_STYLESHEET = 
+	 "ER_FAILED_PROCESS_STYLESHEET";
+  public static final String ER_COULDNT_PARSE_DOC = "ER_COULDNT_PARSE_DOC";
+  public static final String ER_COULDNT_FIND_FRAGMENT = 
+	 "ER_COULDNT_FIND_FRAGMENT";
+  public static final String ER_NODE_NOT_ELEMENT = "ER_NODE_NOT_ELEMENT";
+  public static final String ER_FOREACH_NEED_MATCH_OR_NAME_ATTRIB = 
+	 "ER_FOREACH_NEED_MATCH_OR_NAME_ATTRIB";
+  public static final String ER_TEMPLATES_NEED_MATCH_OR_NAME_ATTRIB = 
+	 "ER_TEMPLATES_NEED_MATCH_OR_NAME_ATTRIB";
+  public static final String ER_NO_CLONE_OF_DOCUMENT_FRAG = 
+	 "ER_NO_CLONE_OF_DOCUMENT_FRAG";
+  public static final String ER_CANT_CREATE_ITEM = "ER_CANT_CREATE_ITEM";
+  public static final String ER_XMLSPACE_ILLEGAL_VALUE = 
+	 "ER_XMLSPACE_ILLEGAL_VALUE";
+  public static final String ER_NO_XSLKEY_DECLARATION = 
+	 "ER_NO_XSLKEY_DECLARATION";
+  public static final String ER_CANT_CREATE_URL = "ER_CANT_CREATE_URL";
+  public static final String ER_XSLFUNCTIONS_UNSUPPORTED = 
+	 "ER_XSLFUNCTIONS_UNSUPPORTED";
+  public static final String ER_PROCESSOR_ERROR = "ER_PROCESSOR_ERROR";
+  public static final String ER_NOT_ALLOWED_INSIDE_STYLESHEET = 
+	 "ER_NOT_ALLOWED_INSIDE_STYLESHEET";
+  public static final String ER_RESULTNS_NOT_SUPPORTED = 
+	 "ER_RESULTNS_NOT_SUPPORTED";
+  public static final String ER_DEFAULTSPACE_NOT_SUPPORTED = 
+	 "ER_DEFAULTSPACE_NOT_SUPPORTED";
+  public static final String ER_INDENTRESULT_NOT_SUPPORTED = 
+	 "ER_INDENTRESULT_NOT_SUPPORTED";
+  public static final String ER_ILLEGAL_ATTRIB = "ER_ILLEGAL_ATTRIB";
+  public static final String ER_UNKNOWN_XSL_ELEM = "ER_UNKNOWN_XSL_ELEM";
+  public static final String ER_BAD_XSLSORT_USE = "ER_BAD_XSLSORT_USE";
+  public static final String ER_MISPLACED_XSLWHEN = "ER_MISPLACED_XSLWHEN";
+  public static final String ER_XSLWHEN_NOT_PARENTED_BY_XSLCHOOSE = 
+	 "ER_XSLWHEN_NOT_PARENTED_BY_XSLCHOOSE";
+  public static final String ER_MISPLACED_XSLOTHERWISE = 
+	 "ER_MISPLACED_XSLOTHERWISE";
+  public static final String ER_XSLOTHERWISE_NOT_PARENTED_BY_XSLCHOOSE = 
+	 "ER_XSLOTHERWISE_NOT_PARENTED_BY_XSLCHOOSE";
+  public static final String ER_NOT_ALLOWED_INSIDE_TEMPLATE = 
+	 "ER_NOT_ALLOWED_INSIDE_TEMPLATE";
+  public static final String ER_UNKNOWN_EXT_NS_PREFIX = 
+	 "ER_UNKNOWN_EXT_NS_PREFIX";
+  public static final String ER_IMPORTS_AS_FIRST_ELEM = 
+	 "ER_IMPORTS_AS_FIRST_ELEM";
+  public static final String ER_IMPORTING_ITSELF = "ER_IMPORTING_ITSELF";
+  public static final String ER_XMLSPACE_ILLEGAL_VAL ="ER_XMLSPACE_ILLEGAL_VAL";
+  public static final String ER_PROCESSSTYLESHEET_NOT_SUCCESSFUL = 
+	 "ER_PROCESSSTYLESHEET_NOT_SUCCESSFUL";
+  public static final String ER_SAX_EXCEPTION = "ER_SAX_EXCEPTION";
+  public static final String ER_XSLT_ERROR = "ER_XSLT_ERROR";
+  public static final String ER_CURRENCY_SIGN_ILLEGAL=
+	 "ER_CURRENCY_SIGN_ILLEGAL";
+  public static final String ER_DOCUMENT_FUNCTION_INVALID_IN_STYLESHEET_DOM = 
+	 "ER_DOCUMENT_FUNCTION_INVALID_IN_STYLESHEET_DOM";
+  public static final String ER_CANT_RESOLVE_PREFIX_OF_NON_PREFIX_RESOLVER = 
+	 "ER_CANT_RESOLVE_PREFIX_OF_NON_PREFIX_RESOLVER";
+  public static final String ER_REDIRECT_COULDNT_GET_FILENAME = 
+	 "ER_REDIRECT_COULDNT_GET_FILENAME";
+  public static final String ER_CANNOT_BUILD_FORMATTERLISTENER_IN_REDIRECT = 
+	 "ER_CANNOT_BUILD_FORMATTERLISTENER_IN_REDIRECT";
+  public static final String ER_INVALID_PREFIX_IN_EXCLUDERESULTPREFIX = 
+	 "ER_INVALID_PREFIX_IN_EXCLUDERESULTPREFIX";
+  public static final String ER_MISSING_NS_URI = "ER_MISSING_NS_URI";
+  public static final String ER_MISSING_ARG_FOR_OPTION = 
+	 "ER_MISSING_ARG_FOR_OPTION";
+  public static final String ER_INVALID_OPTION = "ER_INVALID_OPTION";
+  public static final String ER_MALFORMED_FORMAT_STRING = 
+	 "ER_MALFORMED_FORMAT_STRING";
+  public static final String ER_STYLESHEET_REQUIRES_VERSION_ATTRIB = 
+	 "ER_STYLESHEET_REQUIRES_VERSION_ATTRIB";
+  public static final String ER_ILLEGAL_ATTRIBUTE_VALUE = 
+	 "ER_ILLEGAL_ATTRIBUTE_VALUE";
+  public static final String ER_CHOOSE_REQUIRES_WHEN ="ER_CHOOSE_REQUIRES_WHEN";
+  public static final String ER_NO_APPLY_IMPORT_IN_FOR_EACH = 
+	 "ER_NO_APPLY_IMPORT_IN_FOR_EACH";
+  public static final String ER_CANT_USE_DTM_FOR_OUTPUT = 
+	 "ER_CANT_USE_DTM_FOR_OUTPUT";
+  public static final String ER_CANT_USE_DTM_FOR_INPUT = 
+	 "ER_CANT_USE_DTM_FOR_INPUT";
+  public static final String ER_CALL_TO_EXT_FAILED = "ER_CALL_TO_EXT_FAILED";
+  public static final String ER_PREFIX_MUST_RESOLVE = "ER_PREFIX_MUST_RESOLVE";
+  public static final String ER_INVALID_UTF16_SURROGATE = 
+	 "ER_INVALID_UTF16_SURROGATE";
+  public static final String ER_XSLATTRSET_USED_ITSELF = 
+	 "ER_XSLATTRSET_USED_ITSELF";
+  public static final String ER_CANNOT_MIX_XERCESDOM ="ER_CANNOT_MIX_XERCESDOM";
+  public static final String ER_TOO_MANY_LISTENERS = "ER_TOO_MANY_LISTENERS";
+  public static final String ER_IN_ELEMTEMPLATEELEM_READOBJECT = 
+	 "ER_IN_ELEMTEMPLATEELEM_READOBJECT";
+  public static final String ER_DUPLICATE_NAMED_TEMPLATE = 
+	 "ER_DUPLICATE_NAMED_TEMPLATE";
+  public static final String ER_INVALID_KEY_CALL = "ER_INVALID_KEY_CALL";
+  public static final String ER_REFERENCING_ITSELF = "ER_REFERENCING_ITSELF";
+  public static final String ER_ILLEGAL_DOMSOURCE_INPUT = 
+	 "ER_ILLEGAL_DOMSOURCE_INPUT";
+  public static final String ER_CLASS_NOT_FOUND_FOR_OPTION = 
+	 "ER_CLASS_NOT_FOUND_FOR_OPTION";
+  public static final String ER_REQUIRED_ELEM_NOT_FOUND = 
+	 "ER_REQUIRED_ELEM_NOT_FOUND";
+  public static final String ER_INPUT_CANNOT_BE_NULL ="ER_INPUT_CANNOT_BE_NULL";
+  public static final String ER_URI_CANNOT_BE_NULL = "ER_URI_CANNOT_BE_NULL";
+  public static final String ER_FILE_CANNOT_BE_NULL = "ER_FILE_CANNOT_BE_NULL";
+  public static final String ER_SOURCE_CANNOT_BE_NULL = 
+	 "ER_SOURCE_CANNOT_BE_NULL";
+  public static final String ER_CANNOT_INIT_BSFMGR = "ER_CANNOT_INIT_BSFMGR";
+  public static final String ER_CANNOT_CMPL_EXTENSN = "ER_CANNOT_CMPL_EXTENSN";
+  public static final String ER_CANNOT_CREATE_EXTENSN = 
+	 "ER_CANNOT_CREATE_EXTENSN";
+  public static final String ER_INSTANCE_MTHD_CALL_REQUIRES = 
+	 "ER_INSTANCE_MTHD_CALL_REQUIRES";
+  public static final String ER_INVALID_ELEMENT_NAME ="ER_INVALID_ELEMENT_NAME";
+  public static final String ER_ELEMENT_NAME_METHOD_STATIC = 
+	 "ER_ELEMENT_NAME_METHOD_STATIC";
+  public static final String ER_EXTENSION_FUNC_UNKNOWN = 
+	 "ER_EXTENSION_FUNC_UNKNOWN";
+  public static final String ER_MORE_MATCH_CONSTRUCTOR = 
+	 "ER_MORE_MATCH_CONSTRUCTOR";
+  public static final String ER_MORE_MATCH_METHOD = "ER_MORE_MATCH_METHOD";
+  public static final String ER_MORE_MATCH_ELEMENT = "ER_MORE_MATCH_ELEMENT";
+  public static final String ER_INVALID_CONTEXT_PASSED = 
+	 "ER_INVALID_CONTEXT_PASSED";
+  public static final String ER_POOL_EXISTS = "ER_POOL_EXISTS";
+  public static final String ER_NO_DRIVER_NAME = "ER_NO_DRIVER_NAME";
+  public static final String ER_NO_URL = "ER_NO_URL";
+  public static final String ER_POOL_SIZE_LESSTHAN_ONE = 
+	 "ER_POOL_SIZE_LESSTHAN_ONE";
+  public static final String ER_INVALID_DRIVER = "ER_INVALID_DRIVER";
+  public static final String ER_NO_STYLESHEETROOT = "ER_NO_STYLESHEETROOT";
+  public static final String ER_ILLEGAL_XMLSPACE_VALUE = 
+	 "ER_ILLEGAL_XMLSPACE_VALUE";
+  public static final String ER_PROCESSFROMNODE_FAILED = 
+	 "ER_PROCESSFROMNODE_FAILED";
+  public static final String ER_RESOURCE_COULD_NOT_LOAD = 
+	 "ER_RESOURCE_COULD_NOT_LOAD";
+  public static final String ER_BUFFER_SIZE_LESSTHAN_ZERO = 
+	 "ER_BUFFER_SIZE_LESSTHAN_ZERO";
+  public static final String ER_UNKNOWN_ERROR_CALLING_EXTENSION = 
+	 "ER_UNKNOWN_ERROR_CALLING_EXTENSION";
+  public static final String ER_NO_NAMESPACE_DECL = "ER_NO_NAMESPACE_DECL";
+  public static final String ER_ELEM_CONTENT_NOT_ALLOWED = 
+	 "ER_ELEM_CONTENT_NOT_ALLOWED";
+  public static final String ER_STYLESHEET_DIRECTED_TERMINATION = 
+	 "ER_STYLESHEET_DIRECTED_TERMINATION";
+  public static final String ER_ONE_OR_TWO = "ER_ONE_OR_TWO";
+  public static final String ER_TWO_OR_THREE = "ER_TWO_OR_THREE";
+  public static final String ER_COULD_NOT_LOAD_RESOURCE = 
+	 "ER_COULD_NOT_LOAD_RESOURCE";
+  public static final String ER_CANNOT_INIT_DEFAULT_TEMPLATES = 
+	 "ER_CANNOT_INIT_DEFAULT_TEMPLATES";
+  public static final String ER_RESULT_NULL = "ER_RESULT_NULL";
+  public static final String ER_RESULT_COULD_NOT_BE_SET = 
+	 "ER_RESULT_COULD_NOT_BE_SET";
+  public static final String ER_NO_OUTPUT_SPECIFIED = "ER_NO_OUTPUT_SPECIFIED";
+  public static final String ER_CANNOT_TRANSFORM_TO_RESULT_TYPE = 
+	 "ER_CANNOT_TRANSFORM_TO_RESULT_TYPE";
+  public static final String ER_CANNOT_TRANSFORM_SOURCE_TYPE = 
+	 "ER_CANNOT_TRANSFORM_SOURCE_TYPE";
+  public static final String ER_NULL_CONTENT_HANDLER ="ER_NULL_CONTENT_HANDLER";
+  public static final String ER_NULL_ERROR_HANDLER = "ER_NULL_ERROR_HANDLER";
+  public static final String ER_CANNOT_CALL_PARSE = "ER_CANNOT_CALL_PARSE";
+  public static final String ER_NO_PARENT_FOR_FILTER ="ER_NO_PARENT_FOR_FILTER";
+  public static final String ER_NO_STYLESHEET_IN_MEDIA = 
+	 "ER_NO_STYLESHEET_IN_MEDIA";
+  public static final String ER_NO_STYLESHEET_PI = "ER_NO_STYLESHEET_PI";
+  public static final String ER_NOT_SUPPORTED = "ER_NOT_SUPPORTED";
+  public static final String ER_PROPERTY_VALUE_BOOLEAN = 
+	 "ER_PROPERTY_VALUE_BOOLEAN";
+  public static final String ER_COULD_NOT_FIND_EXTERN_SCRIPT = 
+	 "ER_COULD_NOT_FIND_EXTERN_SCRIPT";
+  public static final String ER_RESOURCE_COULD_NOT_FIND = 
+	 "ER_RESOURCE_COULD_NOT_FIND";
+  public static final String ER_OUTPUT_PROPERTY_NOT_RECOGNIZED = 
+	 "ER_OUTPUT_PROPERTY_NOT_RECOGNIZED";
+  public static final String ER_FAILED_CREATING_ELEMLITRSLT = 
+	 "ER_FAILED_CREATING_ELEMLITRSLT";
+  public static final String ER_VALUE_SHOULD_BE_NUMBER = 
+	 "ER_VALUE_SHOULD_BE_NUMBER";
+  public static final String ER_VALUE_SHOULD_EQUAL = "ER_VALUE_SHOULD_EQUAL";
+  public static final String ER_FAILED_CALLING_METHOD = 
+	 "ER_FAILED_CALLING_METHOD";
+  public static final String ER_FAILED_CREATING_ELEMTMPL = 
+	 "ER_FAILED_CREATING_ELEMTMPL";
+  public static final String ER_CHARS_NOT_ALLOWED = "ER_CHARS_NOT_ALLOWED";
+  public static final String ER_ATTR_NOT_ALLOWED = "ER_ATTR_NOT_ALLOWED";
+  public static final String ER_BAD_VALUE = "ER_BAD_VALUE";
+  public static final String ER_ATTRIB_VALUE_NOT_FOUND = 
+	 "ER_ATTRIB_VALUE_NOT_FOUND";
+  public static final String ER_ATTRIB_VALUE_NOT_RECOGNIZED = 
+	 "ER_ATTRIB_VALUE_NOT_RECOGNIZED";
+  public static final String ER_NULL_URI_NAMESPACE = "ER_NULL_URI_NAMESPACE";
+  public static final String ER_NUMBER_TOO_BIG = "ER_NUMBER_TOO_BIG";
+  public static final String  ER_CANNOT_FIND_SAX1_DRIVER = 
+	 "ER_CANNOT_FIND_SAX1_DRIVER";
+  public static final String  ER_SAX1_DRIVER_NOT_LOADED = 
+	 "ER_SAX1_DRIVER_NOT_LOADED";
+  public static final String  ER_SAX1_DRIVER_NOT_INSTANTIATED = 
+	 "ER_SAX1_DRIVER_NOT_INSTANTIATED" ;
+  public static final String ER_SAX1_DRIVER_NOT_IMPLEMENT_PARSER = 
+	 "ER_SAX1_DRIVER_NOT_IMPLEMENT_PARSER";
+  public static final String  ER_PARSER_PROPERTY_NOT_SPECIFIED = 
+	 "ER_PARSER_PROPERTY_NOT_SPECIFIED";
+  public static final String  ER_PARSER_ARG_CANNOT_BE_NULL = 
+	 "ER_PARSER_ARG_CANNOT_BE_NULL" ;
+  public static final String  ER_FEATURE = "ER_FEATURE";
+  public static final String ER_PROPERTY = "ER_PROPERTY" ;
+  public static final String ER_NULL_ENTITY_RESOLVER ="ER_NULL_ENTITY_RESOLVER";
+  public static final String  ER_NULL_DTD_HANDLER = "ER_NULL_DTD_HANDLER" ;
+  public static final String ER_NO_DRIVER_NAME_SPECIFIED = 
+	 "ER_NO_DRIVER_NAME_SPECIFIED";
+  public static final String ER_NO_URL_SPECIFIED = "ER_NO_URL_SPECIFIED";
+  public static final String ER_POOLSIZE_LESS_THAN_ONE = 
+	 "ER_POOLSIZE_LESS_THAN_ONE";
+  public static final String ER_INVALID_DRIVER_NAME = "ER_INVALID_DRIVER_NAME";
+  public static final String ER_ERRORLISTENER = "ER_ERRORLISTENER";
+  public static final String ER_ASSERT_NO_TEMPLATE_PARENT = 
+	 "ER_ASSERT_NO_TEMPLATE_PARENT";
+  public static final String ER_ASSERT_REDUNDENT_EXPR_ELIMINATOR = 
+	 "ER_ASSERT_REDUNDENT_EXPR_ELIMINATOR";
+  public static final String ER_NOT_ALLOWED_IN_POSITION = 
+	 "ER_NOT_ALLOWED_IN_POSITION";
+  public static final String ER_NONWHITESPACE_NOT_ALLOWED_IN_POSITION = 
+	 "ER_NONWHITESPACE_NOT_ALLOWED_IN_POSITION";
+  public static final String ER_NAMESPACE_CONTEXT_NULL_NAMESPACE =
+  	 "ER_NAMESPACE_CONTEXT_NULL_NAMESPACE";
+  public static final String ER_NAMESPACE_CONTEXT_NULL_PREFIX =
+ 	 "ER_NAMESPACE_CONTEXT_NULL_PREFIX";
+  public static final String ER_XPATH_RESOLVER_NULL_QNAME =
+	 "ER_XPATH_RESOLVER_NULL_QNAME";
+  public static final String ER_XPATH_RESOLVER_NEGATIVE_ARITY =
+	 "ER_XPATH_RESOLVER_NEGATIVE_ARITY";
+  public static final String INVALID_TCHAR = "INVALID_TCHAR";
+  public static final String INVALID_QNAME = "INVALID_QNAME";
+  public static final String INVALID_ENUM = "INVALID_ENUM";
+  public static final String INVALID_NMTOKEN = "INVALID_NMTOKEN";
+  public static final String INVALID_NCNAME = "INVALID_NCNAME";
+  public static final String INVALID_BOOLEAN = "INVALID_BOOLEAN";
+  public static final String INVALID_NUMBER = "INVALID_NUMBER";
+  public static final String ER_ARG_LITERAL = "ER_ARG_LITERAL";
+  public static final String ER_DUPLICATE_GLOBAL_VAR ="ER_DUPLICATE_GLOBAL_VAR";
+  public static final String ER_DUPLICATE_VAR = "ER_DUPLICATE_VAR";
+  public static final String ER_TEMPLATE_NAME_MATCH = "ER_TEMPLATE_NAME_MATCH";
+  public static final String ER_INVALID_PREFIX = "ER_INVALID_PREFIX";
+  public static final String ER_NO_ATTRIB_SET = "ER_NO_ATTRIB_SET";
+  public static final String ER_FUNCTION_NOT_FOUND = 
+	 "ER_FUNCTION_NOT_FOUND"; 
+  public static final String ER_CANT_HAVE_CONTENT_AND_SELECT = 
+     "ER_CANT_HAVE_CONTENT_AND_SELECT";
+  public static final String ER_INVALID_SET_PARAM_VALUE = "ER_INVALID_SET_PARAM_VALUE";    
+  public static final String ER_SET_FEATURE_NULL_NAME =
+  	"ER_SET_FEATURE_NULL_NAME";
+  public static final String ER_GET_FEATURE_NULL_NAME =
+  	"ER_GET_FEATURE_NULL_NAME";
+  public static final String ER_UNSUPPORTED_FEATURE =
+  	"ER_UNSUPPORTED_FEATURE";
+  public static final String ER_EXTENSION_ELEMENT_NOT_ALLOWED_IN_SECURE_PROCESSING =
+  	"ER_EXTENSION_ELEMENT_NOT_ALLOWED_IN_SECURE_PROCESSING";
+     
+  public static final String WG_FOUND_CURLYBRACE = "WG_FOUND_CURLYBRACE";
+  public static final String WG_COUNT_ATTRIB_MATCHES_NO_ANCESTOR = 
+	 "WG_COUNT_ATTRIB_MATCHES_NO_ANCESTOR";
+  public static final String WG_EXPR_ATTRIB_CHANGED_TO_SELECT = 
+	 "WG_EXPR_ATTRIB_CHANGED_TO_SELECT";
+  public static final String WG_NO_LOCALE_IN_FORMATNUMBER = 
+	 "WG_NO_LOCALE_IN_FORMATNUMBER";
+  public static final String WG_LOCALE_NOT_FOUND = "WG_LOCALE_NOT_FOUND";
+  public static final String WG_CANNOT_MAKE_URL_FROM ="WG_CANNOT_MAKE_URL_FROM";
+  public static final String WG_CANNOT_LOAD_REQUESTED_DOC = 
+	 "WG_CANNOT_LOAD_REQUESTED_DOC";
+  public static final String WG_CANNOT_FIND_COLLATOR ="WG_CANNOT_FIND_COLLATOR";
+  public static final String WG_FUNCTIONS_SHOULD_USE_URL = 
+	 "WG_FUNCTIONS_SHOULD_USE_URL";
+  public static final String WG_ENCODING_NOT_SUPPORTED_USING_UTF8 = 
+	 "WG_ENCODING_NOT_SUPPORTED_USING_UTF8";
+  public static final String WG_ENCODING_NOT_SUPPORTED_USING_JAVA = 
+	 "WG_ENCODING_NOT_SUPPORTED_USING_JAVA";
+  public static final String WG_SPECIFICITY_CONFLICTS = 
+	 "WG_SPECIFICITY_CONFLICTS";
+  public static final String WG_PARSING_AND_PREPARING = 
+	 "WG_PARSING_AND_PREPARING";
+  public static final String WG_ATTR_TEMPLATE = "WG_ATTR_TEMPLATE";
+  public static final String WG_CONFLICT_BETWEEN_XSLSTRIPSPACE_AND_XSLPRESERVESPACE = "WG_CONFLICT_BETWEEN_XSLSTRIPSPACE_AND_XSLPRESERVESP";
+  public static final String WG_ATTRIB_NOT_HANDLED = "WG_ATTRIB_NOT_HANDLED";
+  public static final String WG_NO_DECIMALFORMAT_DECLARATION = 
+	 "WG_NO_DECIMALFORMAT_DECLARATION";
+  public static final String WG_OLD_XSLT_NS = "WG_OLD_XSLT_NS";
+  public static final String WG_ONE_DEFAULT_XSLDECIMALFORMAT_ALLOWED = 
+	 "WG_ONE_DEFAULT_XSLDECIMALFORMAT_ALLOWED";
+  public static final String WG_XSLDECIMALFORMAT_NAMES_MUST_BE_UNIQUE = 
+	 "WG_XSLDECIMALFORMAT_NAMES_MUST_BE_UNIQUE";
+  public static final String WG_ILLEGAL_ATTRIBUTE = "WG_ILLEGAL_ATTRIBUTE";
+  public static final String WG_COULD_NOT_RESOLVE_PREFIX = 
+	 "WG_COULD_NOT_RESOLVE_PREFIX";
+  public static final String WG_STYLESHEET_REQUIRES_VERSION_ATTRIB = 
+	 "WG_STYLESHEET_REQUIRES_VERSION_ATTRIB";
+  public static final String WG_ILLEGAL_ATTRIBUTE_NAME = 
+	 "WG_ILLEGAL_ATTRIBUTE_NAME";
+  public static final String WG_ILLEGAL_ATTRIBUTE_VALUE = 
+	 "WG_ILLEGAL_ATTRIBUTE_VALUE";
+  public static final String WG_EMPTY_SECOND_ARG = "WG_EMPTY_SECOND_ARG";
+  public static final String WG_PROCESSINGINSTRUCTION_NAME_CANT_BE_XML = 
+	 "WG_PROCESSINGINSTRUCTION_NAME_CANT_BE_XML";
+  public static final String WG_PROCESSINGINSTRUCTION_NOTVALID_NCNAME = 
+	 "WG_PROCESSINGINSTRUCTION_NOTVALID_NCNAME";
+  public static final String WG_ILLEGAL_ATTRIBUTE_POSITION = 
+	 "WG_ILLEGAL_ATTRIBUTE_POSITION";
+  public static final String NO_MODIFICATION_ALLOWED_ERR = 
+         "NO_MODIFICATION_ALLOWED_ERR";
+
+  /*
+   * Now fill in the message text.
+   * Then fill in the message text for that message code in the
+   * array. Use the new error code as the index into the array.
+   */
+
+  // Error messages...
+
+  /** Get the lookup table for error messages.   
+   *
+   * @return The int to message lookup table.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][] {
+
+  /** Error message ID that has a null message, but takes in a single object.    */
+  {"ER0000" , "{0}" },
+
+    { ER_NO_CURLYBRACE,                            
+      "Error: Can not have '{' within expression"},
+
+    { ER_ILLEGAL_ATTRIBUTE , 
+     "{0} has an illegal attribute: {1}"},
+
+  {ER_NULL_SOURCENODE_APPLYIMPORTS ,
+      "sourceNode is null in xsl:apply-imports!"},
+
+  {ER_CANNOT_ADD,
+      "Can not add {0} to {1}"},
+
+    { ER_NULL_SOURCENODE_HANDLEAPPLYTEMPLATES, 
+      "sourceNode is null in handleApplyTemplatesInstruction!"},
+
+    { ER_NO_NAME_ATTRIB, 
+     "{0} must have a name attribute."},
+
+    {ER_TEMPLATE_NOT_FOUND,
+     "Could not find template named: {0}"},
+
+    {ER_CANT_RESOLVE_NAME_AVT,
+      "Could not resolve name AVT in xsl:call-template."},
+
+    {ER_REQUIRES_ATTRIB,
+     "{0} requires attribute: {1}"},
+
+    { ER_MUST_HAVE_TEST_ATTRIB, 
+      "{0} must have a ''test'' attribute."},
+
+    {ER_BAD_VAL_ON_LEVEL_ATTRIB,
+      "Bad value on level attribute: {0}"},
+
+    {ER_PROCESSINGINSTRUCTION_NAME_CANT_BE_XML, 
+      "processing-instruction name can not be 'xml'"},
+
+    { ER_PROCESSINGINSTRUCTION_NOTVALID_NCNAME,
+      "processing-instruction name must be a valid NCName: {0}"},
+
+    { ER_NEED_MATCH_ATTRIB,
+      "{0} must have a match attribute if it has a mode."},
+
+    { ER_NEED_NAME_OR_MATCH_ATTRIB,
+      "{0} requires either a name or a match attribute."},
+
+    {ER_CANT_RESOLVE_NSPREFIX,
+      "Can not resolve namespace prefix: {0}"},
+
+    { ER_ILLEGAL_VALUE,
+     "xml:space has an illegal value: {0}"},
+
+    { ER_NO_OWNERDOC,
+      "Child node does not have an owner document!"},
+
+    { ER_ELEMTEMPLATEELEM_ERR,
+     "ElemTemplateElement error: {0}"},
+
+    { ER_NULL_CHILD,
+     "Trying to add a null child!"},
+
+    { ER_NEED_SELECT_ATTRIB,
+     "{0} requires a select attribute."},
+
+    { ER_NEED_TEST_ATTRIB ,
+      "xsl:when must have a 'test' attribute."},
+
+    { ER_NEED_NAME_ATTRIB,
+      "xsl:with-param must have a 'name' attribute."},
+
+    { ER_NO_CONTEXT_OWNERDOC,
+      "context does not have an owner document!"},
+
+    {ER_COULD_NOT_CREATE_XML_PROC_LIAISON,
+      "Could not create XML TransformerFactory Liaison: {0}"},
+
+    {ER_PROCESS_NOT_SUCCESSFUL,
+      "Xalan: Process was not successful."},
+
+    { ER_NOT_SUCCESSFUL,
+     "Xalan: was not successful."},
+
+    { ER_ENCODING_NOT_SUPPORTED,
+     "Encoding not supported: {0}"},
+
+    {ER_COULD_NOT_CREATE_TRACELISTENER,
+      "Could not create TraceListener: {0}"},
+
+    {ER_KEY_REQUIRES_NAME_ATTRIB,
+      "xsl:key requires a 'name' attribute!"},
+
+    { ER_KEY_REQUIRES_MATCH_ATTRIB,
+      "xsl:key requires a 'match' attribute!"},
+
+    { ER_KEY_REQUIRES_USE_ATTRIB,
+      "xsl:key requires a 'use' attribute!"},
+
+    { ER_REQUIRES_ELEMENTS_ATTRIB,
+      "(StylesheetHandler) {0} requires an ''elements'' attribute!"},
+
+    { ER_MISSING_PREFIX_ATTRIB,
+      "(StylesheetHandler) {0} attribute ''prefix'' is missing"},
+
+    { ER_BAD_STYLESHEET_URL,
+     "Stylesheet URL is bad: {0}"},
+
+    { ER_FILE_NOT_FOUND,
+     "Stylesheet file was not found: {0}"},
+
+    { ER_IOEXCEPTION,
+      "Had IO Exception with stylesheet file: {0}"},
+
+    { ER_NO_HREF_ATTRIB, 
+      "(StylesheetHandler) Could not find href attribute for {0}"},
+
+    { ER_STYLESHEET_INCLUDES_ITSELF, 
+      "(StylesheetHandler) {0} is directly or indirectly including itself!"},
+
+    { ER_PROCESSINCLUDE_ERROR,
+      "StylesheetHandler.processInclude error, {0}"},
+
+    { ER_MISSING_LANG_ATTRIB,
+      "(StylesheetHandler) {0} attribute ''lang'' is missing"},
+
+    { ER_MISSING_CONTAINER_ELEMENT_COMPONENT,
+      "(StylesheetHandler) misplaced {0} element?? Missing container element ''component''"},
+
+    { ER_CAN_ONLY_OUTPUT_TO_ELEMENT,
+      "Can only output to an Element, DocumentFragment, Document, or PrintWriter."},
+
+    { ER_PROCESS_ERROR,
+     "StylesheetRoot.process error"},
+
+    { ER_UNIMPLNODE_ERROR,
+     "UnImplNode error: {0}"},
+
+    { ER_NO_SELECT_EXPRESSION,
+      "Error! Did not find xpath select expression (-select)."},
+
+    { ER_CANNOT_SERIALIZE_XSLPROCESSOR, 
+      "Can not serialize an XSLProcessor!"},
+
+    { ER_NO_INPUT_STYLESHEET,
+      "Stylesheet input was not specified!"},
+
+    { ER_FAILED_PROCESS_STYLESHEET,
+      "Failed to process stylesheet!"},
+
+    { ER_COULDNT_PARSE_DOC,       
+     "Could not parse {0} document!"},
+
+    { ER_COULDNT_FIND_FRAGMENT,
+     "Could not find fragment: {0}"},
+
+    { ER_NODE_NOT_ELEMENT,
+      "Node pointed to by fragment identifier was not an element: {0}"},
+
+    { ER_FOREACH_NEED_MATCH_OR_NAME_ATTRIB,
+      "for-each must have either a match or name attribute"},
+
+    { ER_TEMPLATES_NEED_MATCH_OR_NAME_ATTRIB, 
+      "templates must have either a match or name attribute"},
+
+    { ER_NO_CLONE_OF_DOCUMENT_FRAG,
+      "No clone of a document fragment!"},
+
+    { ER_CANT_CREATE_ITEM,
+      "Can not create item in result tree: {0}"},
+
+    { ER_XMLSPACE_ILLEGAL_VALUE,
+      "xml:space in the source XML has an illegal value: {0}"},
+
+    { ER_NO_XSLKEY_DECLARATION,
+      "There is no xsl:key declaration for {0}!"},
+
+    { ER_CANT_CREATE_URL, 
+     "Error! Cannot create url for: {0}"},
+
+    { ER_XSLFUNCTIONS_UNSUPPORTED,
+     "xsl:functions is unsupported"},
+
+    { ER_PROCESSOR_ERROR, 
+     "XSLT TransformerFactory Error"},
+
+    { ER_NOT_ALLOWED_INSIDE_STYLESHEET,
+      "(StylesheetHandler) {0} not allowed inside a stylesheet!"},
+
+    { ER_RESULTNS_NOT_SUPPORTED, 
+      "result-ns no longer supported!  Use xsl:output instead."},
+
+    { ER_DEFAULTSPACE_NOT_SUPPORTED, 
+      "default-space no longer supported!  Use xsl:strip-space or xsl:preserve-space instead."},
+
+    { ER_INDENTRESULT_NOT_SUPPORTED,
+      "indent-result no longer supported!  Use xsl:output instead."},
+
+    { ER_ILLEGAL_ATTRIB,
+      "(StylesheetHandler) {0} has an illegal attribute: {1}"},
+
+    { ER_UNKNOWN_XSL_ELEM,
+     "Unknown XSL element: {0}"},
+
+    { ER_BAD_XSLSORT_USE,
+      "(StylesheetHandler) xsl:sort can only be used with xsl:apply-templates or xsl:for-each."},
+
+    { ER_MISPLACED_XSLWHEN,
+      "(StylesheetHandler) misplaced xsl:when!"},
+
+    { ER_XSLWHEN_NOT_PARENTED_BY_XSLCHOOSE,
+      "(StylesheetHandler) xsl:when not parented by xsl:choose!"},
+
+    { ER_MISPLACED_XSLOTHERWISE,
+      "(StylesheetHandler) misplaced xsl:otherwise!"},
+
+    { ER_XSLOTHERWISE_NOT_PARENTED_BY_XSLCHOOSE,
+      "(StylesheetHandler) xsl:otherwise not parented by xsl:choose!"},
+
+    { ER_NOT_ALLOWED_INSIDE_TEMPLATE,
+      "(StylesheetHandler) {0} is not allowed inside a template!"},
+
+    { ER_UNKNOWN_EXT_NS_PREFIX, 
+      "(StylesheetHandler) {0} extension namespace prefix {1} unknown"},
+
+    { ER_IMPORTS_AS_FIRST_ELEM, 
+      "(StylesheetHandler) Imports can only occur as the first elements in the stylesheet!"},
+
+    { ER_IMPORTING_ITSELF,
+      "(StylesheetHandler) {0} is directly or indirectly importing itself!"},
+
+    { ER_XMLSPACE_ILLEGAL_VAL,
+      "(StylesheetHandler) " + "xml:space has an illegal value: {0}"},
+
+    { ER_PROCESSSTYLESHEET_NOT_SUCCESSFUL,
+      "processStylesheet not succesfull!"},
+
+    { ER_SAX_EXCEPTION, 
+     "SAX Exception"},
+
+//  add this message to fix bug 21478
+    { ER_FUNCTION_NOT_SUPPORTED, 
+     "Function not supported!"},
+
+    { ER_XSLT_ERROR,
+     "XSLT Error"},
+
+    { ER_CURRENCY_SIGN_ILLEGAL,
+      "currency sign is not allowed in format pattern string"},
+
+    { ER_DOCUMENT_FUNCTION_INVALID_IN_STYLESHEET_DOM,
+      "Document function not supported in Stylesheet DOM!"},
+
+    { ER_CANT_RESOLVE_PREFIX_OF_NON_PREFIX_RESOLVER,
+      "Can't resolve prefix of non-Prefix resolver!"},
+
+    { ER_REDIRECT_COULDNT_GET_FILENAME,
+      "Redirect extension: Could not get filename - file or select attribute must return vald string."},
+
+    { ER_CANNOT_BUILD_FORMATTERLISTENER_IN_REDIRECT,
+      "Can not build FormatterListener in Redirect extension!"},
+
+    { ER_INVALID_PREFIX_IN_EXCLUDERESULTPREFIX,
+      "Prefix in exclude-result-prefixes is not valid: {0}"},
+
+    { ER_MISSING_NS_URI, 
+      "Missing namespace URI for specified prefix"},
+
+    { ER_MISSING_ARG_FOR_OPTION,
+      "Missing argument for option: {0}"},
+
+    { ER_INVALID_OPTION,
+     "Invalid option: {0}"},
+
+    { ER_MALFORMED_FORMAT_STRING,
+     "Malformed format string: {0}"},
+
+    { ER_STYLESHEET_REQUIRES_VERSION_ATTRIB,
+      "xsl:stylesheet requires a 'version' attribute!"},
+
+    { ER_ILLEGAL_ATTRIBUTE_VALUE,
+      "Attribute: {0} has an illegal value: {1}"},
+
+    { ER_CHOOSE_REQUIRES_WHEN,
+     "xsl:choose requires an xsl:when"},
+
+    { ER_NO_APPLY_IMPORT_IN_FOR_EACH,
+      "xsl:apply-imports not allowed in a xsl:for-each"},
+
+    { ER_CANT_USE_DTM_FOR_OUTPUT,
+      "Cannot use a DTMLiaison for an output DOM node... pass a org.apache.xpath.DOM2Helper instead!"},
+
+    { ER_CANT_USE_DTM_FOR_INPUT,
+      "Cannot use a DTMLiaison for a input DOM node... pass a org.apache.xpath.DOM2Helper instead!"},
+
+    { ER_CALL_TO_EXT_FAILED,
+      "Call to extension element failed: {0}"},
+
+    { ER_PREFIX_MUST_RESOLVE,
+      "Prefix must resolve to a namespace: {0}"},
+
+    { ER_INVALID_UTF16_SURROGATE,
+      "Invalid UTF-16 surrogate detected: {0} ?"},
+
+    { ER_XSLATTRSET_USED_ITSELF,
+      "xsl:attribute-set {0} used itself, which will cause an infinite loop."},
+
+    { ER_CANNOT_MIX_XERCESDOM,
+      "Can not mix non Xerces-DOM input with Xerces-DOM output!"},
+
+    { ER_TOO_MANY_LISTENERS,
+      "addTraceListenersToStylesheet - TooManyListenersException"},
+
+    { ER_IN_ELEMTEMPLATEELEM_READOBJECT,
+      "In ElemTemplateElement.readObject: {0}"},
+
+    { ER_DUPLICATE_NAMED_TEMPLATE,
+      "Found more than one template named: {0}"},
+
+    { ER_INVALID_KEY_CALL,
+      "Invalid function call: recursive key() calls are not allowed"},
+
+    { ER_REFERENCING_ITSELF,
+      "Variable {0} is directly or indirectly referencing itself!"},
+
+    { ER_ILLEGAL_DOMSOURCE_INPUT,
+      "The input node can not be null for a DOMSource for newTemplates!"},
+
+    { ER_CLASS_NOT_FOUND_FOR_OPTION,
+	"Class file not found for option {0}"},
+
+    { ER_REQUIRED_ELEM_NOT_FOUND,
+	"Required Element not found: {0}"},
+
+    { ER_INPUT_CANNOT_BE_NULL,
+	"InputStream cannot be null"},
+
+    { ER_URI_CANNOT_BE_NULL,
+	"URI cannot be null"},
+
+    { ER_FILE_CANNOT_BE_NULL,
+	"File cannot be null"},
+
+    { ER_SOURCE_CANNOT_BE_NULL,
+		"InputSource cannot be null"},
+
+    { ER_CANNOT_INIT_BSFMGR,
+		"Could not initialize BSF Manager"},
+
+    { ER_CANNOT_CMPL_EXTENSN,
+		"Could not compile extension"},
+
+    { ER_CANNOT_CREATE_EXTENSN,
+      "Could not create extension: {0} because of: {1}"},
+
+    { ER_INSTANCE_MTHD_CALL_REQUIRES,
+      "Instance method call to method {0} requires an Object instance as first argument"},
+
+    { ER_INVALID_ELEMENT_NAME,
+      "Invalid element name specified {0}"},
+
+    { ER_ELEMENT_NAME_METHOD_STATIC,
+      "Element name method must be static {0}"},
+
+    { ER_EXTENSION_FUNC_UNKNOWN,
+             "Extension function {0} : {1} is unknown"},
+
+    { ER_MORE_MATCH_CONSTRUCTOR,
+             "More than one best match for constructor for {0}"},
+
+    { ER_MORE_MATCH_METHOD,
+             "More than one best match for method {0}"},
+
+    { ER_MORE_MATCH_ELEMENT,
+             "More than one best match for element method {0}"},
+
+    { ER_INVALID_CONTEXT_PASSED,
+             "Invalid context passed to evaluate {0}"},
+
+    { ER_POOL_EXISTS,
+             "Pool already exists"},
+
+    { ER_NO_DRIVER_NAME,
+             "No driver Name specified"},
+
+    { ER_NO_URL,
+             "No URL specified"},
+
+    { ER_POOL_SIZE_LESSTHAN_ONE,
+             "Pool size is less than one!"},
+
+    { ER_INVALID_DRIVER,
+             "Invalid driver name specified!"},
+
+    { ER_NO_STYLESHEETROOT,
+             "Did not find the stylesheet root!"},
+
+    { ER_ILLEGAL_XMLSPACE_VALUE,
+         "Illegal value for xml:space"},
+
+    { ER_PROCESSFROMNODE_FAILED,
+         "processFromNode failed"},
+
+    { ER_RESOURCE_COULD_NOT_LOAD,
+        "The resource [ {0} ] could not load: {1} \n {2} \t {3}"},
+
+    { ER_BUFFER_SIZE_LESSTHAN_ZERO,
+        "Buffer size <=0"},
+
+    { ER_UNKNOWN_ERROR_CALLING_EXTENSION,
+        "Unknown error when calling extension"},
+
+    { ER_NO_NAMESPACE_DECL,
+        "Prefix {0} does not have a corresponding namespace declaration"},
+
+    { ER_ELEM_CONTENT_NOT_ALLOWED,
+        "Element content not allowed for lang=javaclass {0}"},
+
+    { ER_STYLESHEET_DIRECTED_TERMINATION,
+        "Stylesheet directed termination"},
+
+    { ER_ONE_OR_TWO,
+        "1 or 2"},
+
+    { ER_TWO_OR_THREE,
+        "2 or 3"},
+
+    { ER_COULD_NOT_LOAD_RESOURCE,
+        "Could not load {0} (check CLASSPATH), now using just the defaults"},
+
+    { ER_CANNOT_INIT_DEFAULT_TEMPLATES,
+        "Cannot initialize default templates"},
+
+    { ER_RESULT_NULL,
+        "Result should not be null"},
+
+    { ER_RESULT_COULD_NOT_BE_SET,
+        "Result could not be set"},
+
+    { ER_NO_OUTPUT_SPECIFIED,
+        "No output specified"},
+
+    { ER_CANNOT_TRANSFORM_TO_RESULT_TYPE,
+        "Can''t transform to a Result of type {0}"},
+
+    { ER_CANNOT_TRANSFORM_SOURCE_TYPE,
+        "Can''t transform a Source of type {0}"},
+
+    { ER_NULL_CONTENT_HANDLER,
+        "Null content handler"},
+
+    { ER_NULL_ERROR_HANDLER,
+        "Null error handler"},
+
+    { ER_CANNOT_CALL_PARSE,
+        "parse can not be called if the ContentHandler has not been set"},
+
+    { ER_NO_PARENT_FOR_FILTER,
+        "No parent for filter"},
+
+    { ER_NO_STYLESHEET_IN_MEDIA,
+         "No stylesheet found in: {0}, media= {1}"},
+
+    { ER_NO_STYLESHEET_PI,
+         "No xml-stylesheet PI found in: {0}"},
+
+    { ER_NOT_SUPPORTED,
+       "Not supported: {0}"},
+
+    { ER_PROPERTY_VALUE_BOOLEAN,
+       "Value for property {0} should be a Boolean instance"},
+
+    { ER_COULD_NOT_FIND_EXTERN_SCRIPT,
+         "Could not get to external script at {0}"},
+
+    { ER_RESOURCE_COULD_NOT_FIND,
+        "The resource [ {0} ] could not be found.\n {1}"},
+
+    { ER_OUTPUT_PROPERTY_NOT_RECOGNIZED,
+        "Output property not recognized: {0}"},
+
+    { ER_FAILED_CREATING_ELEMLITRSLT,
+        "Failed creating ElemLiteralResult instance"},
+
+  //Earlier (JDK 1.4 XALAN 2.2-D11) at key code '204' the key name was ER_PRIORITY_NOT_PARSABLE
+  // In latest Xalan code base key name is  ER_VALUE_SHOULD_BE_NUMBER. This should also be taken care
+  //in locale specific files like XSLTErrorResources_de.java, XSLTErrorResources_fr.java etc.
+  //NOTE: Not only the key name but message has also been changed. 
+    { ER_VALUE_SHOULD_BE_NUMBER,
+        "Value for {0} should contain a parsable number"},
+
+    { ER_VALUE_SHOULD_EQUAL,
+        "Value for {0} should equal yes or no"},
+
+    { ER_FAILED_CALLING_METHOD,
+        "Failed calling {0} method"},
+
+    { ER_FAILED_CREATING_ELEMTMPL,
+        "Failed creating ElemTemplateElement instance"},
+
+    { ER_CHARS_NOT_ALLOWED,
+        "Characters are not allowed at this point in the document"},
+
+    { ER_ATTR_NOT_ALLOWED,
+        "\"{0}\" attribute is not allowed on the {1} element!"},
+
+    { ER_BAD_VALUE,
+     "{0} bad value {1} "},
+
+    { ER_ATTRIB_VALUE_NOT_FOUND,
+     "{0} attribute value not found "},
+
+    { ER_ATTRIB_VALUE_NOT_RECOGNIZED,
+     "{0} attribute value not recognized "},
+
+    { ER_NULL_URI_NAMESPACE,
+     "Attempting to generate a namespace prefix with a null URI"},
+
+    { ER_NUMBER_TOO_BIG,
+     "Attempting to format a number bigger than the largest Long integer"},
+
+    { ER_CANNOT_FIND_SAX1_DRIVER,
+     "Cannot find SAX1 driver class {0}"},
+
+    { ER_SAX1_DRIVER_NOT_LOADED,
+     "SAX1 driver class {0} found but cannot be loaded"},
+
+    { ER_SAX1_DRIVER_NOT_INSTANTIATED,
+     "SAX1 driver class {0} loaded but cannot be instantiated"},
+
+    { ER_SAX1_DRIVER_NOT_IMPLEMENT_PARSER,
+     "SAX1 driver class {0} does not implement org.xml.sax.Parser"},
+
+    { ER_PARSER_PROPERTY_NOT_SPECIFIED,
+     "System property org.xml.sax.parser not specified"},
+
+    { ER_PARSER_ARG_CANNOT_BE_NULL,
+     "Parser argument must not be null"},
+
+    { ER_FEATURE,
+     "Feature: {0}"},
+
+    { ER_PROPERTY,
+     "Property: {0}"},
+
+    { ER_NULL_ENTITY_RESOLVER,
+     "Null entity resolver"},
+
+    { ER_NULL_DTD_HANDLER,
+     "Null DTD handler"},
+
+    { ER_NO_DRIVER_NAME_SPECIFIED,
+     "No Driver Name Specified!"},
+
+    { ER_NO_URL_SPECIFIED,
+     "No URL Specified!"},
+
+    { ER_POOLSIZE_LESS_THAN_ONE,
+     "Pool size is less than 1!"},
+
+    { ER_INVALID_DRIVER_NAME,
+     "Invalid Driver Name Specified!"},
+
+    { ER_ERRORLISTENER,
+     "ErrorListener"},
+
+
+// Note to translators:  The following message should not normally be displayed
+//   to users.  It describes a situation in which the processor has detected
+//   an internal consistency problem in itself, and it provides this message
+//   for the developer to help diagnose the problem.  The name
+//   'ElemTemplateElement' is the name of a class, and should not be
+//   translated.
+    { ER_ASSERT_NO_TEMPLATE_PARENT,
+     "Programmer's error! The expression has no ElemTemplateElement parent!"},
+
+
+// Note to translators:  The following message should not normally be displayed
+//   to users.  It describes a situation in which the processor has detected
+//   an internal consistency problem in itself, and it provides this message
+//   for the developer to help diagnose the problem.  The substitution text
+//   provides further information in order to diagnose the problem.  The name
+//   'RedundentExprEliminator' is the name of a class, and should not be
+//   translated.
+    { ER_ASSERT_REDUNDENT_EXPR_ELIMINATOR,
+     "Programmer''s assertion in RedundentExprEliminator: {0}"},
+
+    { ER_NOT_ALLOWED_IN_POSITION,
+     "{0} is not allowed in this position in the stylesheet!"},
+
+    { ER_NONWHITESPACE_NOT_ALLOWED_IN_POSITION,
+     "Non-whitespace text is not allowed in this position in the stylesheet!"},
+
+  // This code is shared with warning codes.
+  // SystemId Unknown
+    { INVALID_TCHAR,
+     "Illegal value: {1} used for CHAR attribute: {0}.  An attribute of type CHAR must be only 1 character!"},
+
+    // Note to translators:  The following message is used if the value of
+    // an attribute in a stylesheet is invalid.  "QNAME" is the XML data-type of
+    // the attribute, and should not be translated.  The substitution text {1} is
+    // the attribute value and {0} is the attribute name.
+  //The following codes are shared with the warning codes...
+    { INVALID_QNAME,
+     "Illegal value: {1} used for QNAME attribute: {0}"},
+
+    // Note to translators:  The following message is used if the value of
+    // an attribute in a stylesheet is invalid.  "ENUM" is the XML data-type of
+    // the attribute, and should not be translated.  The substitution text {1} is
+    // the attribute value, {0} is the attribute name, and {2} is a list of valid
+    // values.
+    { INVALID_ENUM,
+     "Illegal value: {1} used for ENUM attribute: {0}.  Valid values are: {2}."},
+
+// Note to translators:  The following message is used if the value of
+// an attribute in a stylesheet is invalid.  "NMTOKEN" is the XML data-type
+// of the attribute, and should not be translated.  The substitution text {1} is
+// the attribute value and {0} is the attribute name.
+    { INVALID_NMTOKEN,
+     "Illegal value: {1} used for NMTOKEN attribute: {0} "},
+
+// Note to translators:  The following message is used if the value of
+// an attribute in a stylesheet is invalid.  "NCNAME" is the XML data-type
+// of the attribute, and should not be translated.  The substitution text {1} is
+// the attribute value and {0} is the attribute name.
+    { INVALID_NCNAME,
+     "Illegal value: {1} used for NCNAME attribute: {0} "},
+
+// Note to translators:  The following message is used if the value of
+// an attribute in a stylesheet is invalid.  "boolean" is the XSLT data-type
+// of the attribute, and should not be translated.  The substitution text {1} is
+// the attribute value and {0} is the attribute name.
+    { INVALID_BOOLEAN,
+     "Illegal value: {1} used for boolean attribute: {0} "},
+
+// Note to translators:  The following message is used if the value of
+// an attribute in a stylesheet is invalid.  "number" is the XSLT data-type
+// of the attribute, and should not be translated.  The substitution text {1} is
+// the attribute value and {0} is the attribute name.
+     { INVALID_NUMBER,
+     "Illegal value: {1} used for number attribute: {0} "},
+
+
+  // End of shared codes...
+
+// Note to translators:  A "match pattern" is a special form of XPath expression
+// that is used for matching patterns.  The substitution text is the name of
+// a function.  The message indicates that when this function is referenced in
+// a match pattern, its argument must be a string literal (or constant.)
+// ER_ARG_LITERAL - new error message for bugzilla //5202
+    { ER_ARG_LITERAL,
+     "Argument to {0} in match pattern must be a literal."},
+
+// Note to translators:  The following message indicates that two definitions of
+// a variable.  A "global variable" is a variable that is accessible everywher
+// in the stylesheet.
+// ER_DUPLICATE_GLOBAL_VAR - new error message for bugzilla #790
+    { ER_DUPLICATE_GLOBAL_VAR,
+     "Duplicate global variable declaration."},
+
+
+// Note to translators:  The following message indicates that two definitions of
+// a variable were encountered.
+// ER_DUPLICATE_VAR - new error message for bugzilla #790
+    { ER_DUPLICATE_VAR,
+     "Duplicate variable declaration."},
+
+    // Note to translators:  "xsl:template, "name" and "match" are XSLT keywords
+    // which must not be translated.
+    // ER_TEMPLATE_NAME_MATCH - new error message for bugzilla #789
+    { ER_TEMPLATE_NAME_MATCH,
+     "xsl:template must have a name or match attribute (or both)"},
+
+    // Note to translators:  "exclude-result-prefixes" is an XSLT keyword which
+    // should not be translated.  The message indicates that a namespace prefix
+    // encountered as part of the value of the exclude-result-prefixes attribute
+    // was in error.
+    // ER_INVALID_PREFIX - new error message for bugzilla #788
+    { ER_INVALID_PREFIX,
+     "Prefix in exclude-result-prefixes is not valid: {0}"},
+
+    // Note to translators:  An "attribute set" is a set of attributes that can
+    // be added to an element in the output document as a group.  The message
+    // indicates that there was a reference to an attribute set named {0} that
+    // was never defined.
+    // ER_NO_ATTRIB_SET - new error message for bugzilla #782
+    { ER_NO_ATTRIB_SET,
+     "attribute-set named {0} does not exist"},
+     
+    // Note to translators:  This message indicates that there was a reference
+    // to a function named {0} for which no function definition could be found.
+    { ER_FUNCTION_NOT_FOUND,
+     "The function named {0} does not exist"},
+
+    // Note to translators:  This message indicates that the XSLT instruction
+    // that is named by the substitution text {0} must not contain other XSLT
+    // instructions (content) or a "select" attribute.  The word "select" is
+    // an XSLT keyword in this case and must not be translated.
+    { ER_CANT_HAVE_CONTENT_AND_SELECT,
+     "The {0} element must not have both content and a select attribute."},
+
+    // Note to translators:  This message indicates that the value argument
+    // of setParameter must be a valid Java Object.
+    { ER_INVALID_SET_PARAM_VALUE,
+     "The value of param {0} must be a valid Java Object"},
+
+    { ER_INVALID_NAMESPACE_URI_VALUE_FOR_RESULT_PREFIX_FOR_DEFAULT,
+      "The result-prefix attribute of an xsl:namespace-alias element has the value '#default', but there is no declaration of the default namespace in scope for the element"},
+
+    { ER_INVALID_NAMESPACE_URI_VALUE_FOR_RESULT_PREFIX,
+      "The result-prefix attribute of an xsl:namespace-alias element has the value ''{0}'', but there is no namespace declaration for the prefix ''{0}'' in scope for the element."},
+
+    { ER_SET_FEATURE_NULL_NAME,
+      "The feature name cannot be null in TransformerFactory.setFeature(String name, boolean value)."},
+    
+    { ER_GET_FEATURE_NULL_NAME,
+      "The feature name cannot be null in TransformerFactory.getFeature(String name)."},
+    
+    { ER_UNSUPPORTED_FEATURE,
+      "Cannot set the feature ''{0}'' on this TransformerFactory."},
+    
+    { ER_EXTENSION_ELEMENT_NOT_ALLOWED_IN_SECURE_PROCESSING,
+  	  "Use of the extension element ''{0}'' is not allowed when the secure processing feature is set to true."},
+    
+    { ER_NAMESPACE_CONTEXT_NULL_NAMESPACE, 		
+      "Cannot get the prefix for a null namespace uri."},
+
+    { ER_NAMESPACE_CONTEXT_NULL_PREFIX, 		
+      "Cannot get the namespace uri for null prefix."},
+
+    { ER_XPATH_RESOLVER_NULL_QNAME, 		
+      "The function name cannot be null."},
+
+    { ER_XPATH_RESOLVER_NEGATIVE_ARITY, 		
+      "The arity cannot be negative."},
+  // Warnings...
+
+    { WG_FOUND_CURLYBRACE,
+      "Found '}' but no attribute template open!"},
+
+    { WG_COUNT_ATTRIB_MATCHES_NO_ANCESTOR,
+      "Warning: count attribute does not match an ancestor in xsl:number! Target = {0}"},
+
+    { WG_EXPR_ATTRIB_CHANGED_TO_SELECT,
+      "Old syntax: The name of the 'expr' attribute has been changed to 'select'."},
+
+    { WG_NO_LOCALE_IN_FORMATNUMBER,
+      "Xalan doesn't yet handle the locale name in the format-number function."},
+
+    { WG_LOCALE_NOT_FOUND,
+      "Warning: Could not find locale for xml:lang={0}"},
+
+    { WG_CANNOT_MAKE_URL_FROM,
+      "Can not make URL from: {0}"},
+
+    { WG_CANNOT_LOAD_REQUESTED_DOC,
+      "Can not load requested doc: {0}"},
+
+    { WG_CANNOT_FIND_COLLATOR,
+      "Could not find Collator for <sort xml:lang={0}"},
+
+    { WG_FUNCTIONS_SHOULD_USE_URL,
+      "Old syntax: the functions instruction should use a url of {0}"},
+
+    { WG_ENCODING_NOT_SUPPORTED_USING_UTF8,
+      "encoding not supported: {0}, using UTF-8"},
+
+    { WG_ENCODING_NOT_SUPPORTED_USING_JAVA,
+      "encoding not supported: {0}, using Java {1}"},
+
+    { WG_SPECIFICITY_CONFLICTS,
+      "Specificity conflicts found: {0} Last found in stylesheet will be used."},
+
+    { WG_PARSING_AND_PREPARING,
+      "========= Parsing and preparing {0} =========="},
+
+    { WG_ATTR_TEMPLATE,
+     "Attr Template, {0}"},
+
+    { WG_CONFLICT_BETWEEN_XSLSTRIPSPACE_AND_XSLPRESERVESPACE,
+      "Match conflict between xsl:strip-space and xsl:preserve-space"},
+
+    { WG_ATTRIB_NOT_HANDLED,
+      "Xalan does not yet handle the {0} attribute!"},
+
+    { WG_NO_DECIMALFORMAT_DECLARATION,
+      "No declaration found for decimal format: {0}"},
+
+    { WG_OLD_XSLT_NS,
+     "Missing or incorrect XSLT Namespace. "},
+
+    { WG_ONE_DEFAULT_XSLDECIMALFORMAT_ALLOWED,
+      "Only one default xsl:decimal-format declaration is allowed."},
+
+    { WG_XSLDECIMALFORMAT_NAMES_MUST_BE_UNIQUE,
+      "xsl:decimal-format names must be unique. Name \"{0}\" has been duplicated."},
+
+    { WG_ILLEGAL_ATTRIBUTE,
+      "{0} has an illegal attribute: {1}"},
+
+    { WG_COULD_NOT_RESOLVE_PREFIX,
+      "Could not resolve namespace prefix: {0}. The node will be ignored."},
+
+    { WG_STYLESHEET_REQUIRES_VERSION_ATTRIB,
+      "xsl:stylesheet requires a 'version' attribute!"},
+
+    { WG_ILLEGAL_ATTRIBUTE_NAME,
+      "Illegal attribute name: {0}"},
+
+    { WG_ILLEGAL_ATTRIBUTE_VALUE,
+      "Illegal value used for attribute {0}: {1}"},
+
+    { WG_EMPTY_SECOND_ARG,
+      "Resulting nodeset from second argument of document function is empty. Return an empty node-set."},
+
+  //Following are the new WARNING keys added in XALAN code base after Jdk 1.4 (Xalan 2.2-D11)
+
+    // Note to translators:  "name" and "xsl:processing-instruction" are keywords
+    // and must not be translated.
+    { WG_PROCESSINGINSTRUCTION_NAME_CANT_BE_XML,
+      "The value of the 'name' attribute of xsl:processing-instruction name must not be 'xml'"},
+
+    // Note to translators:  "name" and "xsl:processing-instruction" are keywords
+    // and must not be translated.  "NCName" is an XML data-type and must not be
+    // translated.
+    { WG_PROCESSINGINSTRUCTION_NOTVALID_NCNAME,
+      "The value of the ''name'' attribute of xsl:processing-instruction must be a valid NCName: {0}"},
+
+    // Note to translators:  This message is reported if the stylesheet that is
+    // being processed attempted to construct an XML document with an attribute in a
+    // place other than on an element.  The substitution text specifies the name of
+    // the attribute.
+    { WG_ILLEGAL_ATTRIBUTE_POSITION,
+      "Cannot add attribute {0} after child nodes or before an element is produced.  Attribute will be ignored."},
+
+    { NO_MODIFICATION_ALLOWED_ERR,
+      "An attempt is made to modify an object where modifications are not allowed."
+    },
+
+    //Check: WHY THERE IS A GAP B/W NUMBERS in the XSLTErrorResources properties file?
+
+  // Other miscellaneous text used inside the code...
+  { "ui_language", "en"},
+  {  "help_language",  "en" },
+  {  "language",  "en" },
+  { "BAD_CODE", "Parameter to createMessage was out of bounds"},
+  {  "FORMAT_FAILED", "Exception thrown during messageFormat call"},
+  {  "version", ">>>>>>> Xalan Version "},
+  {  "version2",  "<<<<<<<"},
+  {  "yes", "yes"},
+  { "line", "Line #"},
+  { "column","Column #"},
+  { "xsldone", "XSLProcessor: done"},
+
+
+  // Note to translators:  The following messages provide usage information
+  // for the Xalan Process command line.  "Process" is the name of a Java class,
+  // and should not be translated.
+  { "xslProc_option", "Xalan-J command line Process class options:"},
+  { "xslProc_option", "Xalan-J command line Process class options\u003a"},
+  { "xslProc_invalid_xsltc_option", "The option {0} is not supported in XSLTC mode."},
+  { "xslProc_invalid_xalan_option", "The option {0} can only be used with -XSLTC."},
+  { "xslProc_no_input", "Error: No stylesheet or input xml is specified. Run this command without any option for usage instructions."},
+  { "xslProc_common_options", "-Common Options-"},
+  { "xslProc_xalan_options", "-Options for Xalan-"},
+  { "xslProc_xsltc_options", "-Options for XSLTC-"},
+  { "xslProc_return_to_continue", "(press <return> to continue)"},
+
+   // Note to translators: The option name and the parameter name do not need to
+   // be translated. Only translate the messages in parentheses.  Note also that
+   // leading whitespace in the messages is used to indent the usage information
+   // for each option in the English messages.
+   // Do not translate the keywords: XSLTC, SAX, DOM and DTM.
+  { "optionXSLTC", "   [-XSLTC (use XSLTC for transformation)]"},
+  { "optionIN", "   [-IN inputXMLURL]"},
+  { "optionXSL", "   [-XSL XSLTransformationURL]"},
+  { "optionOUT",  "   [-OUT outputFileName]"},
+  { "optionLXCIN", "   [-LXCIN compiledStylesheetFileNameIn]"},
+  { "optionLXCOUT", "   [-LXCOUT compiledStylesheetFileNameOutOut]"},
+  { "optionPARSER", "   [-PARSER fully qualified class name of parser liaison]"},
+  {  "optionE", "   [-E (Do not expand entity refs)]"},
+  {  "optionV",  "   [-E (Do not expand entity refs)]"},
+  {  "optionQC", "   [-QC (Quiet Pattern Conflicts Warnings)]"},
+  {  "optionQ", "   [-Q  (Quiet Mode)]"},
+  {  "optionLF", "   [-LF (Use linefeeds only on output {default is CR/LF})]"},
+  {  "optionCR", "   [-CR (Use carriage returns only on output {default is CR/LF})]"},
+  { "optionESCAPE", "   [-ESCAPE (Which characters to escape {default is <>&\"\'\\r\\n}]"},
+  { "optionINDENT", "   [-INDENT (Control how many spaces to indent {default is 0})]"},
+  { "optionTT", "   [-TT (Trace the templates as they are being called.)]"},
+  { "optionTG", "   [-TG (Trace each generation event.)]"},
+  { "optionTS", "   [-TS (Trace each selection event.)]"},
+  {  "optionTTC", "   [-TTC (Trace the template children as they are being processed.)]"},
+  { "optionTCLASS", "   [-TCLASS (TraceListener class for trace extensions.)]"},
+  { "optionVALIDATE", "   [-VALIDATE (Set whether validation occurs.  Validation is off by default.)]"},
+  { "optionEDUMP", "   [-EDUMP {optional filename} (Do stackdump on error.)]"},
+  {  "optionXML", "   [-XML (Use XML formatter and add XML header.)]"},
+  {  "optionTEXT", "   [-TEXT (Use simple Text formatter.)]"},
+  {  "optionHTML", "   [-HTML (Use HTML formatter.)]"},
+  {  "optionPARAM", "   [-PARAM name expression (Set a stylesheet parameter)]"},
+  {  "noParsermsg1", "XSL Process was not successful."},
+  {  "noParsermsg2", "** Could not find parser **"},
+  { "noParsermsg3",  "Please check your classpath."},
+  { "noParsermsg4", "If you don't have IBM's XML Parser for Java, you can download it from"},
+  { "noParsermsg5", "IBM's AlphaWorks: http://www.alphaworks.ibm.com/formula/xml"},
+  { "optionURIRESOLVER", "   [-URIRESOLVER full class name (URIResolver to be used to resolve URIs)]"},
+  { "optionENTITYRESOLVER",  "   [-ENTITYRESOLVER full class name (EntityResolver to be used to resolve entities)]"},
+  { "optionCONTENTHANDLER",  "   [-CONTENTHANDLER full class name (ContentHandler to be used to serialize output)]"},
+  {  "optionLINENUMBERS",  "   [-L use line numbers for source document]"},
+  { "optionSECUREPROCESSING", "   [-SECURE (set the secure processing feature to true.)]"},
+
+    // Following are the new options added in XSLTErrorResources.properties files after Jdk 1.4 (Xalan 2.2-D11)
+
+
+  {  "optionMEDIA",  "   [-MEDIA mediaType (use media attribute to find stylesheet associated with a document.)]"},
+  {  "optionFLAVOR",  "   [-FLAVOR flavorName (Explicitly use s2s=SAX or d2d=DOM to do transform.)] "}, // Added by sboag/scurcuru; experimental
+  { "optionDIAG", "   [-DIAG (Print overall milliseconds transform took.)]"},
+  { "optionINCREMENTAL",  "   [-INCREMENTAL (request incremental DTM construction by setting http://xml.apache.org/xalan/features/incremental true.)]"},
+  {  "optionNOOPTIMIMIZE",  "   [-NOOPTIMIMIZE (request no stylesheet optimization processing by setting http://xml.apache.org/xalan/features/optimize false.)]"},
+  { "optionRL",  "   [-RL recursionlimit (assert numeric limit on stylesheet recursion depth.)]"},
+  {   "optionXO",  "   [-XO [transletName] (assign the name to the generated translet)]"},
+  {  "optionXD", "   [-XD destinationDirectory (specify a destination directory for translet)]"},
+  {  "optionXJ",  "   [-XJ jarfile (packages translet classes into a jar file of name <jarfile>)]"},
+  {   "optionXP",  "   [-XP package (specifies a package name prefix for all generated translet classes)]"},
+
+  //AddITIONAL  STRINGS that need L10n
+  // Note to translators:  The following message describes usage of a particular
+  // command-line option that is used to enable the "template inlining"
+  // optimization.  The optimization involves making a copy of the code
+  // generated for a template in another template that refers to it.
+  { "optionXN",  "   [-XN (enables template inlining)]" },
+  { "optionXX",  "   [-XX (turns on additional debugging message output)]"},
+  { "optionXT" , "   [-XT (use translet to transform if possible)]"},
+  { "diagTiming"," --------- Transform of {0} via {1} took {2} ms" },
+  { "recursionTooDeep","Template nesting too deep. nesting = {0}, template {1} {2}" },
+  { "nameIs", "name is" },
+  { "matchPatternIs", "match pattern is" }
+
+  };
+  }
+  // ================= INFRASTRUCTURE ======================
+
+  /** String for use when a bad error code was encountered.    */
+  public static final String BAD_CODE = "BAD_CODE";
+
+  /** String for use when formatting of the error string failed.   */
+  public static final String FORMAT_FAILED = "FORMAT_FAILED";
+
+  /** General error string.   */
+  public static final String ERROR_STRING = "#error";
+
+  /** String to prepend to error messages.  */
+  public static final String ERROR_HEADER = "Error: ";
+
+  /** String to prepend to warning messages.    */
+  public static final String WARNING_HEADER = "Warning: ";
+
+  /** String to specify the XSLT module.  */
+  public static final String XSL_HEADER = "XSLT ";
+
+  /** String to specify the XML parser module.  */
+  public static final String XML_HEADER = "XML ";
+
+  /** I don't think this is used any more.
+   * @deprecated  */
+  public static final String QUERY_HEADER = "PATTERN ";
+
+
+  /**
+   *   Return a named ResourceBundle for a particular locale.  This method mimics the behavior
+   *   of ResourceBundle.getBundle().
+   *
+   *   @param className the name of the class that implements the resource bundle.
+   *   @return the ResourceBundle
+   *   @throws MissingResourceException
+   */
+  public static final XSLTErrorResources loadResourceBundle(String className)
+          throws MissingResourceException
+  {
+
+    Locale locale = Locale.getDefault();
+    String suffix = getResourceSuffix(locale);
+
+    try
+    {
+
+      // first try with the given locale
+      return (XSLTErrorResources) ResourceBundle.getBundle(className
+              + suffix, locale);
+    }
+    catch (MissingResourceException e)
+    {
+      try  // try to fall back to en_US if we can't load
+      {
+
+        // Since we can't find the localized property file,
+        // fall back to en_US.
+        return (XSLTErrorResources) ResourceBundle.getBundle(className,
+                new Locale("en", "US"));
+      }
+      catch (MissingResourceException e2)
+      {
+
+        // Now we are really in trouble.
+        // very bad, definitely very bad...not going to get very far
+        throw new MissingResourceException(
+          "Could not load any resource bundles.", className, "");
+      }
+    }
+  }
+
+  /**
+   * Return the resource file suffic for the indicated locale
+   * For most locales, this will be based the language code.  However
+   * for Chinese, we do distinguish between Taiwan and PRC
+   *
+   * @param locale the locale
+   * @return an String suffix which canbe appended to a resource name
+   */
+  private static final String getResourceSuffix(Locale locale)
+  {
+
+    String suffix = "_" + locale.getLanguage();
+    String country = locale.getCountry();
+
+    if (country.equals("TW"))
+      suffix += "_" + country;
+
+    return suffix;
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xalan/res/XSLTInfo.properties b/src/main/java/org/apache/xalan/res/XSLTInfo.properties
new file mode 100644
index 0000000..309ac32
--- /dev/null
+++ b/src/main/java/org/apache/xalan/res/XSLTInfo.properties
@@ -0,0 +1,30 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the  "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+#
+# $Id: XSLTInfo.properties 468641 2006-10-28 06:54:42Z minchau $
+#
+#  XSLT Resource File
+#
+
+vendor=Apache Software Foundation
+vendor-url=http://xml.apache.org/xalan-j
+
+# Product Version: Xalan-Java @impl.version@
+
+# W3C XSL Transformations (XSLT) Version 1.0
+version=1.0
diff --git a/src/main/java/org/apache/xalan/res/package.html b/src/main/java/org/apache/xalan/res/package.html
new file mode 100644
index 0000000..b4acbad
--- /dev/null
+++ b/src/main/java/org/apache/xalan/res/package.html
@@ -0,0 +1,26 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468641 2006-10-28 06:54:42Z minchau $ -->
+<html>
+  <title>Xalan resource Package.</title>
+  <body>
+    <p>Contains strings that require internationalization.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xalan/serialize/SerializerUtils.java b/src/main/java/org/apache/xalan/serialize/SerializerUtils.java
new file mode 100644
index 0000000..5127989
--- /dev/null
+++ b/src/main/java/org/apache/xalan/serialize/SerializerUtils.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerUtils.java 468642 2006-10-28 06:55:10Z minchau $
+ */
+package org.apache.xalan.serialize;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.serializer.NamespaceMappings;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.xml.sax.SAXException;
+
+/**
+ * Class that contains only static methods that are used to "serialize",
+ * these methods are used by Xalan and are not in org.apache.xml.serializer
+ * because they have dependancies on the packages org.apache.xpath or org.
+ * apache.xml.dtm or org.apache.xalan.transformer. The package org.apache.xml.
+ * serializer should not depend on Xalan or XSLTC.
+ * @xsl.usage internal
+ */
+public class SerializerUtils
+{
+
+    /**
+     * Copy an DOM attribute to the created output element, executing
+     * attribute templates as need be, and processing the xsl:use
+     * attribute.
+     *
+     * @param handler SerializationHandler to which the attributes are added.
+     * @param attr Attribute node to add to SerializationHandler.
+     *
+     * @throws TransformerException
+     */
+    public static void addAttribute(SerializationHandler handler, int attr)
+        throws TransformerException
+    {
+
+        TransformerImpl transformer =
+            (TransformerImpl) handler.getTransformer();
+        DTM dtm = transformer.getXPathContext().getDTM(attr);
+
+        if (SerializerUtils.isDefinedNSDecl(handler, attr, dtm))
+            return;
+
+        String ns = dtm.getNamespaceURI(attr);
+
+        if (ns == null)
+            ns = "";
+
+        // %OPT% ...can I just store the node handle?
+        try
+        {
+            handler.addAttribute(
+                ns,
+                dtm.getLocalName(attr),
+                dtm.getNodeName(attr),
+                "CDATA",
+                dtm.getNodeValue(attr), false);
+        }
+        catch (SAXException e)
+        {
+            // do something?
+        }
+    } // end copyAttributeToTarget method
+
+    /**
+     * Copy DOM attributes to the result element.
+     *
+     * @param src Source node with the attributes
+     *
+     * @throws TransformerException
+     */
+    public static void addAttributes(SerializationHandler handler, int src)
+        throws TransformerException
+    {
+
+        TransformerImpl transformer =
+            (TransformerImpl) handler.getTransformer();
+        DTM dtm = transformer.getXPathContext().getDTM(src);
+
+        for (int node = dtm.getFirstAttribute(src);
+            DTM.NULL != node;
+            node = dtm.getNextAttribute(node))
+        {
+            addAttribute(handler, node);
+        }
+    }
+
+    /**
+     * Given a result tree fragment, walk the tree and
+     * output it to the SerializationHandler.
+     *
+     * @param obj Result tree fragment object
+     * @param support XPath context for the result tree fragment
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public static void outputResultTreeFragment(
+        SerializationHandler handler,
+        XObject obj,
+        XPathContext support)
+        throws org.xml.sax.SAXException
+    {
+
+        int doc = obj.rtf();
+        DTM dtm = support.getDTM(doc);
+
+        if (null != dtm)
+        {
+            for (int n = dtm.getFirstChild(doc);
+                DTM.NULL != n;
+                n = dtm.getNextSibling(n))
+            {
+                handler.flushPending();
+
+                // I think. . . . This used to have a (true) arg
+                // to flush prefixes, will that cause problems ???
+                if (dtm.getNodeType(n) == DTM.ELEMENT_NODE
+                        && dtm.getNamespaceURI(n) == null)
+                    handler.startPrefixMapping("", "");
+                dtm.dispatchToEvents(n, handler);
+            }
+        }
+    }
+
+    /**
+     * Copy <KBD>xmlns:</KBD> attributes in if not already in scope.
+     *
+     * As a quick hack to support ClonerToResultTree, this can also be used
+     * to copy an individual namespace node.
+     *
+     * @param src Source Node
+     * NEEDSDOC @param type
+     * NEEDSDOC @param dtm
+     *
+     * @throws TransformerException
+     */
+    public static void processNSDecls(
+        SerializationHandler handler,
+        int src,
+        int type,
+        DTM dtm)
+        throws TransformerException
+    {
+
+        try
+        {
+            if (type == DTM.ELEMENT_NODE)
+            {
+                for (int namespace = dtm.getFirstNamespaceNode(src, true);
+                    DTM.NULL != namespace;
+                    namespace = dtm.getNextNamespaceNode(src, namespace, true))
+                {
+
+                    // String prefix = dtm.getPrefix(namespace);
+                    String prefix = dtm.getNodeNameX(namespace);
+                    String desturi = handler.getNamespaceURIFromPrefix(prefix);
+                    //            String desturi = getURI(prefix);
+                    String srcURI = dtm.getNodeValue(namespace);
+
+                    if (!srcURI.equalsIgnoreCase(desturi))
+                    {
+                        handler.startPrefixMapping(prefix, srcURI, false);
+                    }
+                }
+            }
+            else if (type == DTM.NAMESPACE_NODE)
+            {
+                String prefix = dtm.getNodeNameX(src);
+                // Brian M. - some changes here to get desturi
+                String desturi = handler.getNamespaceURIFromPrefix(prefix);
+                String srcURI = dtm.getNodeValue(src);
+
+                if (!srcURI.equalsIgnoreCase(desturi))
+                {
+                    handler.startPrefixMapping(prefix, srcURI, false);
+                }
+            }
+        }
+        catch (org.xml.sax.SAXException se)
+        {
+            throw new TransformerException(se);
+        }
+    }
+
+    /**
+     * Returns whether a namespace is defined
+     *
+     *
+     * @param attr Namespace attribute node
+     * @param dtm The DTM that owns attr.
+     *
+     * @return True if the namespace is already defined in
+     * list of namespaces
+     */
+    public static boolean isDefinedNSDecl(
+        SerializationHandler serializer,
+        int attr,
+        DTM dtm)
+    {
+
+        if (DTM.NAMESPACE_NODE == dtm.getNodeType(attr))
+        {
+
+            // String prefix = dtm.getPrefix(attr);
+            String prefix = dtm.getNodeNameX(attr);
+            String uri = serializer.getNamespaceURIFromPrefix(prefix);
+            //      String uri = getURI(prefix);
+
+            if ((null != uri) && uri.equals(dtm.getStringValue(attr)))
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * This function checks to make sure a given prefix is really
+     * declared.  It might not be, because it may be an excluded prefix.
+     * If it's not, it still needs to be declared at this point.
+     * TODO: This needs to be done at an earlier stage in the game... -sb
+     *
+     * NEEDSDOC @param dtm
+     * NEEDSDOC @param namespace
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public static void ensureNamespaceDeclDeclared(
+        SerializationHandler handler,
+        DTM dtm,
+        int namespace)
+        throws org.xml.sax.SAXException
+    {
+
+        String uri = dtm.getNodeValue(namespace);
+        String prefix = dtm.getNodeNameX(namespace);
+
+        if ((uri != null && uri.length() > 0) && (null != prefix))
+        {
+            String foundURI;
+            NamespaceMappings ns = handler.getNamespaceMappings();
+            if (ns != null)
+            {
+
+                foundURI = ns.lookupNamespace(prefix);
+                if ((null == foundURI) || !foundURI.equals(uri))
+                {
+                    handler.startPrefixMapping(prefix, uri, false);
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/xalan/templates/AVT.java b/src/main/java/org/apache/xalan/templates/AVT.java
new file mode 100644
index 0000000..a8247e1
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/AVT.java
@@ -0,0 +1,614 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AVT.java 469221 2006-10-30 18:26:44Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.processor.StylesheetHandler;
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.StringBufferPool;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+
+/**
+ * Class to hold an Attribute Value Template.
+ * @xsl.usage advanced
+ */
+public class AVT implements java.io.Serializable, XSLTVisitable
+{
+    static final long serialVersionUID = 5167607155517042691L;
+  
+  /**
+    *We are not going to use the object pool if USE_OBJECT_POOL == false.
+  */
+  private final static boolean USE_OBJECT_POOL = false; 
+  
+  /**
+    * INIT_BUFFER_CHUNK_BITS is used to set initial size of
+    * of the char m_array in FastStringBuffer if USE_OBJECT_POOL == false. 
+    * size = 2^ INIT_BUFFER_CHUNK_BITS, INIT_BUFFER_CHUNK_BITS = 7 
+    * corresponds size = 256. 
+  */
+  private final static int INIT_BUFFER_CHUNK_BITS = 8; 
+  
+  /**
+   * If the AVT is not complex, just hold the simple string.
+   * @serial
+   */
+  private String m_simpleString = null;
+
+  /**
+   * If the AVT is complex, hold a Vector of AVTParts.
+   * @serial
+   */
+  private Vector m_parts = null;
+  
+   
+
+  /**
+   * The name of the attribute.
+   * @serial
+   */
+  private String m_rawName;
+
+  /**
+   * Get the raw name of the attribute, with the prefix unprocessed.
+   *
+   * @return non-null reference to prefixed name.
+   */
+  public String getRawName()
+  {
+    return m_rawName;
+  }
+
+  /**
+   * Get the raw name of the attribute, with the prefix unprocessed.
+   *
+   * @param rawName non-null reference to prefixed name.
+   */
+  public void setRawName(String rawName)
+  {
+    m_rawName = rawName;
+  }
+
+  /**
+   * The name of the attribute.
+   * @serial
+   */
+  private String m_name;
+
+  /**
+   * Get the local name of the attribute.
+   *
+   * @return non-null reference to name string.
+   */
+  public String getName()
+  {
+    return m_name;
+  }
+
+  /**
+   * Set the local name of the attribute.
+   *
+   * @param name non-null reference to name string.
+   */
+  public void setName(String name)
+  {
+    m_name = name;
+  }
+
+  /**
+   * The namespace URI of the owning attribute.
+   * @serial
+   */
+  private String m_uri;
+
+  /**
+   * Get the namespace URI of the attribute.
+   *
+   * @return non-null reference to URI, "" if null namespace.
+   */
+  public String getURI()
+  {
+    return m_uri;
+  }
+
+  /**
+   * Get the namespace URI of the attribute.
+   *
+   * @param uri non-null reference to URI, "" if null namespace.
+   */
+  public void setURI(String uri)
+  {
+    m_uri = uri;
+  }
+
+  /**
+   * Construct an AVT by parsing the string, and either
+   * constructing a vector of AVTParts, or simply hold
+   * on to the string if the AVT is simple.
+   *
+   * @param handler non-null reference to StylesheetHandler that is constructing.
+   * @param uri non-null reference to URI, "" if null namespace.
+   * @param name  non-null reference to name string.
+   * @param rawName prefixed name.
+   * @param stringedValue non-null raw string value.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public AVT(StylesheetHandler handler, String uri, String name, 
+             String rawName, String stringedValue,
+             ElemTemplateElement owner)
+          throws javax.xml.transform.TransformerException
+  {
+
+    m_uri = uri;
+    m_name = name;
+    m_rawName = rawName;
+
+    StringTokenizer tokenizer = new StringTokenizer(stringedValue, "{}\"\'",
+                                  true);
+    int nTokens = tokenizer.countTokens();
+
+    if (nTokens < 2)
+    {
+      m_simpleString = stringedValue;  // then do the simple thing
+    }
+    else
+    {
+      FastStringBuffer buffer = null;
+      FastStringBuffer exprBuffer = null;
+      if(USE_OBJECT_POOL){
+        buffer = StringBufferPool.get(); 
+        exprBuffer = StringBufferPool.get();
+      }else{
+        buffer = new FastStringBuffer(6);
+        exprBuffer = new FastStringBuffer(6);
+      }
+      try
+      {
+        m_parts = new Vector(nTokens + 1);
+
+        String t = null;  // base token
+        String lookahead = null;  // next token
+        String error = null;  // if non-null, break from loop
+
+        while (tokenizer.hasMoreTokens())
+        {
+          if (lookahead != null)
+          {
+            t = lookahead;
+            lookahead = null;
+          }
+          else
+            t = tokenizer.nextToken();
+
+          if (t.length() == 1)
+          {
+            switch (t.charAt(0))
+            {
+            case ('\"') :
+            case ('\'') :
+            {
+
+              // just keep on going, since we're not in an attribute template
+              buffer.append(t);
+
+              break;
+            }
+            case ('{') :
+            {
+
+              try
+              {
+                // Attribute Value Template start
+                lookahead = tokenizer.nextToken();
+
+                if (lookahead.equals("{"))
+                {
+
+                  // Double curlys mean escape to show curly
+                  buffer.append(lookahead);
+
+                  lookahead = null;
+
+                  break;  // from switch
+                }
+
+                /*
+                else if(lookahead.equals("\"") || lookahead.equals("\'"))
+                {
+                // Error. Expressions can not begin with quotes.
+                error = "Expressions can not begin with quotes.";
+                break; // from switch
+                }
+                */
+                else
+                {
+                  if (buffer.length() > 0)
+                  {
+                    m_parts.addElement(new AVTPartSimple(buffer.toString()));
+                    buffer.setLength(0);
+                  }
+
+                  exprBuffer.setLength(0);
+
+                  while (null != lookahead)
+                  {
+                    if (lookahead.length() == 1)
+                    {
+                      switch (lookahead.charAt(0))
+                      {
+                      case '\'' :
+                      case '\"' :
+                        {
+
+                          // String start
+                          exprBuffer.append(lookahead);
+
+                          String quote = lookahead;
+
+                          // Consume stuff 'till next quote
+                          lookahead = tokenizer.nextToken();
+
+                          while (!lookahead.equals(quote))
+                          {
+                            exprBuffer.append(lookahead);
+
+                            lookahead = tokenizer.nextToken();
+                          }
+
+                          exprBuffer.append(lookahead);
+
+                          lookahead = tokenizer.nextToken();
+
+                          break;
+                        }
+                      case '{' :
+                        {
+
+                          // What's another curly doing here?
+                          error = XSLMessages.createMessage(
+                                                            XSLTErrorResources.ER_NO_CURLYBRACE, null);  //"Error: Can not have \"{\" within expression.";
+                          
+                          lookahead = null;  // breaks out of inner while loop
+
+                          break;
+                        }
+                      case '}' :
+                        {
+
+                          // Proper close of attribute template.
+                          // Evaluate the expression.
+                          buffer.setLength(0);
+
+                          XPath xpath =
+                                       handler.createXPath(exprBuffer.toString(), owner);
+
+                          m_parts.addElement(new AVTPartXPath(xpath));
+
+                          lookahead = null;  // breaks out of inner while loop
+
+                          break;
+                        }
+                      default :
+                        {
+
+                          // part of the template stuff, just add it.
+                          exprBuffer.append(lookahead);
+
+                          lookahead = tokenizer.nextToken();
+                        }
+                      }  // end inner switch
+                    }  // end if lookahead length == 1
+                    else
+                    {
+
+                      // part of the template stuff, just add it.
+                      exprBuffer.append(lookahead);
+
+                      lookahead = tokenizer.nextToken();
+                    }
+                  }  // end while(!lookahead.equals("}"))
+
+                  if (error != null)
+                  {
+                    break;  // from inner while loop
+                  }
+                }
+
+                break;
+              }
+              catch (java.util.NoSuchElementException ex)
+              {
+                error = XSLMessages.createMessage(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE, new Object[]{ name, stringedValue }); 
+                break;
+              }
+            }
+            case ('}') :
+            {
+              lookahead = tokenizer.nextToken();
+
+              if (lookahead.equals("}"))
+              {
+
+                // Double curlys mean escape to show curly
+                buffer.append(lookahead);
+
+                lookahead = null;  // swallow
+              }
+              else
+              {
+
+                // Illegal, I think...
+                try
+                {
+                  handler.warn(XSLTErrorResources.WG_FOUND_CURLYBRACE, null);  //"Found \"}\" but no attribute template open!");
+                }
+                catch (org.xml.sax.SAXException se)
+                {
+                  throw new TransformerException(se);
+                }
+
+                buffer.append("}");
+
+                // leave the lookahead to be processed by the next round.
+              }
+
+              break;
+            }
+            default :
+            {
+
+              // Anything else just add to string.
+              buffer.append(t);
+            }
+            }  // end switch t
+          }  // end if length == 1
+          else
+          {
+
+            // Anything else just add to string.
+            buffer.append(t);
+          }
+
+          if (null != error)
+          {
+            try
+            {
+              handler.warn(XSLTErrorResources.WG_ATTR_TEMPLATE,
+                           new Object[]{ error });  //"Attr Template, "+error);
+            }
+            catch (org.xml.sax.SAXException se)
+            {
+              throw new TransformerException(se);
+            }
+
+            break;
+          }
+        }  // end while(tokenizer.hasMoreTokens())
+
+        if (buffer.length() > 0)
+        {
+          m_parts.addElement(new AVTPartSimple(buffer.toString()));
+          buffer.setLength(0);
+        }
+      }
+      finally
+      {
+        if(USE_OBJECT_POOL){
+             StringBufferPool.free(buffer);
+             StringBufferPool.free(exprBuffer);
+         }else{
+            buffer = null;
+            exprBuffer = null;
+         };
+      }
+    }  // end else nTokens > 1
+
+    if (null == m_parts && (null == m_simpleString))
+    {
+
+      // Error?
+      m_simpleString = "";
+    }
+  }
+
+  /**
+   * Get the AVT as the original string.
+   *
+   * @return The AVT as the original string
+   */
+  public String getSimpleString()
+  {
+
+    if (null != m_simpleString){
+      return m_simpleString;
+    }
+    else if (null != m_parts){
+     final FastStringBuffer buf = getBuffer();
+     String out = null;
+
+    int n = m_parts.size();
+    try{
+      for (int i = 0; i < n; i++){
+        AVTPart part = (AVTPart) m_parts.elementAt(i);
+        buf.append(part.getSimpleString());
+      }
+      out = buf.toString();
+    }finally{
+      if(USE_OBJECT_POOL){
+         StringBufferPool.free(buf);
+     }else{
+        buf.setLength(0); 
+     };
+    }
+    return out;
+  }else{
+      return "";
+  }
+}
+
+  /**
+   * Evaluate the AVT and return a String.
+   *
+   * @param xctxt Te XPathContext to use to evaluate this.
+   * @param context The current source tree context.
+   * @param nsNode The current namespace context (stylesheet tree context).
+   *
+   * @return The AVT evaluated as a string
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public String evaluate(
+          XPathContext xctxt, int context, org.apache.xml.utils.PrefixResolver nsNode)
+            throws javax.xml.transform.TransformerException
+  {
+    if (null != m_simpleString){
+        return m_simpleString;
+    }else if (null != m_parts){
+      final FastStringBuffer buf =getBuffer();
+      String out = null;
+      int n = m_parts.size();
+      try{
+        for (int i = 0; i < n; i++){
+          AVTPart part = (AVTPart) m_parts.elementAt(i);  
+          part.evaluate(xctxt, buf, context, nsNode);
+        }
+       out = buf.toString();
+      }finally{
+          if(USE_OBJECT_POOL){
+             StringBufferPool.free(buf);
+         }else{
+           buf.setLength(0); 
+         }
+      }
+     return out;
+    }else{
+      return "";
+    }
+  }
+
+  /**
+   * Test whether the AVT is insensitive to the context in which
+   *  it is being evaluated. This is intended to facilitate
+   *  compilation of templates, by allowing simple AVTs to be
+   *  converted back into strings.
+   *
+   *  Currently the only case we recognize is simple strings.
+   * ADDED 9/5/2000 to support compilation experiment
+   *
+   * @return True if the m_simpleString member of this AVT is not null
+   */
+  public boolean isContextInsensitive()
+  {
+    return null != m_simpleString;
+  }
+
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside
+   * the current subtree.
+   *
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+  public boolean canTraverseOutsideSubtree()
+  {
+
+    if (null != m_parts)
+    {
+      int n = m_parts.size();
+
+      for (int i = 0; i < n; i++)
+      {
+        AVTPart part = (AVTPart) m_parts.elementAt(i);
+
+        if (part.canTraverseOutsideSubtree())
+          return true;
+      }
+    }
+
+    return false;
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    if (null != m_parts)
+    {
+      int n = m_parts.size();
+
+      for (int i = 0; i < n; i++)
+      {
+        AVTPart part = (AVTPart) m_parts.elementAt(i);
+
+        part.fixupVariables(vars, globalsSize);
+      }
+    }
+  }
+  
+  /**
+   * @see XSLTVisitable#callVisitors(XSLTVisitor)
+   */
+  public void callVisitors(XSLTVisitor visitor)
+  {
+  	if(visitor.visitAVT(this) && (null != m_parts))
+  	{
+      int n = m_parts.size();
+
+      for (int i = 0; i < n; i++)
+      {
+        AVTPart part = (AVTPart) m_parts.elementAt(i);
+
+        part.callVisitors(visitor);
+      }  		
+  	}
+  }
+
+
+  /**
+   * Returns true if this AVT is simple
+   */
+  public boolean isSimple() {
+  	return m_simpleString != null;
+  }
+  
+  private final FastStringBuffer getBuffer(){
+    if(USE_OBJECT_POOL){
+      return StringBufferPool.get();
+    }else{
+      return new FastStringBuffer(INIT_BUFFER_CHUNK_BITS);
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/AVTPart.java b/src/main/java/org/apache/xalan/templates/AVTPart.java
new file mode 100644
index 0000000..4d9a02d
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/AVTPart.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AVTPart.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xpath.XPathContext;
+
+/**
+ * Class to hold a part, either a string or XPath,
+ * of an Attribute Value Template.
+ * @xsl.usage internal
+ */
+public abstract class AVTPart implements java.io.Serializable, XSLTVisitable
+{
+    static final long serialVersionUID = -1747749903613916025L;
+
+  /**
+   * Construct a part.
+   */
+  public AVTPart(){}
+
+  /**
+   * Get the AVT part as the original string.
+   *
+   * @return the AVT part as the original string.
+   */
+  public abstract String getSimpleString();
+
+  /**
+   * Write the evaluated value into the given
+   * string buffer.
+   *
+   * @param xctxt The XPath context to use to evaluate this AVT.
+   * @param buf Buffer to write into.
+   * @param context The current source tree context.
+   * @param nsNode The current namespace context (stylesheet tree context).
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public abstract void evaluate(
+    XPathContext xctxt, FastStringBuffer buf, int context,
+      org.apache.xml.utils.PrefixResolver nsNode)
+        throws javax.xml.transform.TransformerException;
+
+  /**
+   * Set the XPath support.
+   *
+   * @param support XPathContext to set. 
+   */
+  public void setXPathSupport(XPathContext support){}
+  
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside 
+   * the current subtree.
+   * 
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+   public boolean canTraverseOutsideSubtree()
+   {
+    return false;
+   }
+   
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public abstract void fixupVariables(java.util.Vector vars, int globalsSize);
+
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/AVTPartSimple.java b/src/main/java/org/apache/xalan/templates/AVTPartSimple.java
new file mode 100644
index 0000000..0e489d3
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/AVTPartSimple.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AVTPartSimple.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xpath.XPathContext;
+
+/**
+ * Simple string part of a complex AVT.
+ * @xsl.usage internal
+ */
+public class AVTPartSimple extends AVTPart
+{
+    static final long serialVersionUID = -3744957690598727913L;
+
+  /**
+   * Simple string value;
+   * @serial
+   */
+  private String m_val;
+
+  /**
+   * Construct a simple AVT part.
+   * @param val A pure string section of an AVT.
+   */
+  public AVTPartSimple(String val)
+  {
+    m_val = val;
+  }
+
+  /**
+   * Get the AVT part as the original string.
+   *
+   * @return the AVT part as the original string.
+   */
+  public String getSimpleString()
+  {
+    return m_val;
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    // no-op
+  }
+
+
+  /**
+   * Write the value into the buffer.
+   *
+   * @param xctxt An XPathContext object, providing infomation specific
+   * to this invocation and this thread. Maintains SAX state, variables, 
+   * error handler and  so on, so the transformation/XPath object itself
+   * can be simultaneously invoked from multiple threads.
+   * @param buf Buffer to write into.
+   * @param context The current source tree context.
+   * @param nsNode The current namespace context (stylesheet tree context).
+   */
+  public void evaluate(XPathContext xctxt, FastStringBuffer buf,
+                       int context,
+                       org.apache.xml.utils.PrefixResolver nsNode)
+  {
+    buf.append(m_val);
+  }
+  /**
+   * @see XSLTVisitable#callVisitors(XSLTVisitor)
+   */
+  public void callVisitors(XSLTVisitor visitor)
+  {
+  	// Don't do anything for the subpart for right now.
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/AVTPartXPath.java b/src/main/java/org/apache/xalan/templates/AVTPartXPath.java
new file mode 100644
index 0000000..dde5f31
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/AVTPartXPath.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AVTPartXPath.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathFactory;
+import org.apache.xpath.compiler.XPathParser;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Simple string part of a complex AVT.
+ * @xsl.usage internal
+ */
+public class AVTPartXPath extends AVTPart
+{
+    static final long serialVersionUID = -4460373807550527675L;
+
+  /**
+   * The XPath object contained in this part.
+   * @serial
+   */
+  private XPath m_xpath;
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    m_xpath.fixupVariables(vars, globalsSize);
+  }
+  
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside 
+   * the current subtree.
+   * 
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+   public boolean canTraverseOutsideSubtree()
+   {
+    return m_xpath.getExpression().canTraverseOutsideSubtree();
+   }
+
+  /**
+   * Construct a simple AVT part.
+   *
+   * @param xpath Xpath section of AVT 
+   */
+  public AVTPartXPath(XPath xpath)
+  {
+    m_xpath = xpath;
+  }
+
+  /**
+   * Construct a simple AVT part.
+   * 
+   * @param val A pure string section of an AVT.
+   * @param nsNode An object which can be used to determine the
+   * Namespace Name (URI) for any Namespace prefix used in the XPath. 
+   * Usually this is based on the context where the XPath was specified,
+   * such as a node within a Stylesheet.
+   * @param xpathProcessor XPath parser
+   * @param factory XPath factory
+   * @param liaison An XPathContext object, providing infomation specific
+   * to this invocation and this thread. Maintains SAX output state, 
+   * variables, error handler and so on, so the transformation/XPath 
+   * object itself can be simultaneously invoked from multiple threads.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * TODO: Fix or remove this unused c'tor.
+   */
+  public AVTPartXPath(
+          String val, org.apache.xml.utils.PrefixResolver nsNode, 
+          XPathParser xpathProcessor, XPathFactory factory, 
+          XPathContext liaison)
+            throws javax.xml.transform.TransformerException
+  {
+    m_xpath = new XPath(val, null, nsNode, XPath.SELECT, liaison.getErrorListener());
+  }
+
+  /**
+   * Get the AVT part as the original string.
+   *
+   * @return the AVT part as the original string.
+   */
+  public String getSimpleString()
+  {
+    return "{" + m_xpath.getPatternString() + "}";
+  }
+
+  /**
+   * Write the value into the buffer.
+   *
+   * @param xctxt An XPathContext object, providing infomation specific
+   * to this invocation and this thread. Maintains SAX state, variables, 
+   * error handler and  so on, so the transformation/XPath object itself
+   * can be simultaneously invoked from multiple threads.
+   * @param buf Buffer to write into.
+   * @param context The current source tree context.
+   * @param nsNode The current namespace context (stylesheet tree context).
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void evaluate(
+          XPathContext xctxt, FastStringBuffer buf, int context, org.apache.xml.utils.PrefixResolver nsNode)
+            throws javax.xml.transform.TransformerException
+  {
+
+    XObject xobj = m_xpath.execute(xctxt, context, nsNode);
+
+    if (null != xobj)
+    {
+      xobj.appendToFsb(buf);
+    }
+  }
+  
+  /**
+   * @see XSLTVisitable#callVisitors(XSLTVisitor)
+   */
+  public void callVisitors(XSLTVisitor visitor)
+  {
+  	m_xpath.getExpression().callVisitors(m_xpath, visitor);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/AbsPathChecker.java b/src/main/java/org/apache/xalan/templates/AbsPathChecker.java
new file mode 100644
index 0000000..9303d30
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/AbsPathChecker.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AbsPathChecker.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.functions.FuncCurrent;
+import org.apache.xpath.functions.FuncExtFunction;
+import org.apache.xpath.functions.Function;
+import org.apache.xpath.operations.Variable;
+
+/**
+ * This class runs over a path expression that is assumed to be absolute, and 
+ * checks for variables and the like that may make it context dependent.
+ */
+public class AbsPathChecker extends XPathVisitor
+{
+	private boolean m_isAbs = true;
+	
+	/**
+	 * Process the LocPathIterator to see if it contains variables 
+	 * or functions that may make it context dependent.
+	 * @param path LocPathIterator that is assumed to be absolute, but needs checking.
+	 * @return true if the path is confirmed to be absolute, false if it 
+	 * may contain context dependencies.
+	 */
+	public boolean checkAbsolute(LocPathIterator path)
+	{
+		m_isAbs = true;
+		path.callVisitors(null, this);
+		return m_isAbs;
+	}
+	
+	/**
+	 * Visit a function.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param func The function reference object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitFunction(ExpressionOwner owner, Function func)
+	{
+		if((func instanceof FuncCurrent) ||
+		   (func instanceof FuncExtFunction))
+			m_isAbs = false;
+		return true;
+	}
+	
+	/**
+	 * Visit a variable reference.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param var The variable reference object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitVariableRef(ExpressionOwner owner, Variable var)
+	{
+		m_isAbs = false;
+		return true;
+	}
+}
+
diff --git a/src/main/java/org/apache/xalan/templates/Constants.java b/src/main/java/org/apache/xalan/templates/Constants.java
new file mode 100644
index 0000000..717fa35
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/Constants.java
@@ -0,0 +1,399 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Constants.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+
+/**
+ * Primary constants used in the TransformerImpl classes.
+ * @xsl.usage advanced
+ */
+public class Constants extends org.apache.xml.utils.Constants
+{
+
+  /**
+   * IDs for XSL element types. These are associated
+   * with the string literals in the TransformerImpl class.
+   * Don't change the numbers. NOTE THAT THESE ARE NOT IN
+   * ALPHABETICAL ORDER!
+   * (It's a pity Java doesn't have a real Enumerated Mnemonic
+   * datatype... or a C-like preprocessor in lieu thereof which
+   * could be used to generate and maintain synch between these lists.)
+   */
+  public static final int ELEMNAME_UNDEFINED = -1, ELEMNAME_WITHPARAM = 2,
+                          ELEMNAME_ADDATTRIBUTE = 4, ELEMNAME_ANCHOR = 22,
+
+  //  ELEMNAME_ANCHOR_PATTERN = 23,
+  ELEMNAME_APPLY_TEMPLATES = 50, ELEMNAME_USE = 34, ELEMNAME_CHILDREN = 6,
+                                 ELEMNAME_CHOOSE = 37, ELEMNAME_COMMENT = 59,  // my own
+                                 ELEMNAME_CONSTRUCT = 7,  // my own
+                                 ELEMNAME_CONTENTS = 8, ELEMNAME_COPY = 9,
+                                 ELEMNAME_COPY_OF = 74,
+                                 ELEMNAME_DECIMALFORMAT = 83,
+                                 ELEMNAME_DEFINEATTRIBUTESET = 40,
+
+  //  ELEMNAME_DEFINECONSTANT = 29,
+  //  ELEMNAME_DEFINEMACRO = 10,
+  ELEMNAME_DEFINESCRIPT = 11, ELEMNAME_DISPLAYIF = 12,  // my own
+                              ELEMNAME_EMPTY = 14, ELEMNAME_EVAL = 15,
+                              ELEMNAME_EXPECTEDCHILDREN = 16,
+                              ELEMNAME_EXTENSION = 54,
+                              ELEMNAME_EXTENSIONHANDLER = 63,
+                              ELEMNAME_FOREACH = 28, ELEMNAME_KEY = 31,
+                              ELEMNAME_IF = 36, ELEMNAME_IMPORT = 26,
+                              ELEMNAME_INCLUDE = 27,
+                              ELEMNAME_CALLTEMPLATE = 17,
+                              ELEMNAME_PARAMVARIABLE = 41,
+                              ELEMNAME_NUMBER = 35, ELEMNAME_NSALIAS = 84,
+                              ELEMNAME_OTHERWISE = 39, ELEMNAME_PI = 58,
+                              ELEMNAME_PRESERVESPACE = 33,
+                              ELEMNAME_REMOVEATTRIBUTE = 5,
+                              ELEMNAME_TEMPLATE = 19, ELEMNAME_SORT = 64,
+                              ELEMNAME_STRIPSPACE = 32,
+                              ELEMNAME_STYLESHEET = 25, ELEMNAME_TEXT = 42,
+                              ELEMNAME_VALUEOF = 30, ELEMNAME_WHEN = 38,
+
+  // Pattern by example support  
+  ELEMNAME_ROOT = 44, ELEMNAME_ANY = 45, ELEMNAME_ELEMENT = 46,
+                      ELEMNAME_TARGETELEMENT = 47, ELEMNAME_ATTRIBUTE = 48,
+                      ELEMNAME_TARGETATTRIBUTE = 49, ELEMNAME_URL = 52,  // my own
+                      ELEMNAME_CALL = 55,  // my own
+
+  //  ELEMNAME_WITHPARAM = 56,
+  ELEMNAME_FALLBACK = 57,  // my own
+  ELEMNAME_TARGETPI = 60,  // my own
+  ELEMNAME_TARGETCOMMENT = 61,  // my own
+  ELEMNAME_TARGETTEXT = 62,  // my own
+  ELEMNAME_CSSSTYLECONVERSION = 65,  // my own
+  ELEMNAME_COUNTER = 66, ELEMNAME_COUNTERS = 67,
+  ELEMNAME_COUNTERINCREMENT = 68, ELEMNAME_COUNTERRESET = 69,
+  ELEMNAME_COUNTERSCOPE = 71, ELEMNAME_APPLY_IMPORTS = 72,
+  ELEMNAME_VARIABLE = 73, ELEMNAME_MESSAGE = 75, ELEMNAME_LOCALE = 76,
+  ELEMNAME_LITERALRESULT = 77, ELEMNAME_TEXTLITERALRESULT = 78,
+  ELEMNAME_EXTENSIONCALL = 79, ELEMNAME_EXTENSIONDECL = 85,
+  ELEMNAME_EXTENSIONSCRIPT = 86, ELEMNAME_OUTPUT = 80,
+  ELEMNAME_COMPONENT = 81, ELEMNAME_SCRIPT = 82;
+
+  // Next free number: 90 (88 and 89 used for EXSLT elements);
+
+  /**
+   * Literals for XSL element names.  Note that there are more
+   * names than IDs, because some names map to the same ID.
+   */
+  public static final String       
+	  ELEMNAME_ANCHOR_STRING = "anchor",
+      ELEMNAME_ANY_STRING = "any",  // pattern-by-example support
+      ELEMNAME_APPLY_IMPORTS_STRING = "apply-imports",
+      ELEMNAME_APPLY_TEMPLATES_STRING = "apply-templates",
+      ELEMNAME_ARG_STRING = "arg",
+      ELEMNAME_ATTRIBUTESET_STRING = "attribute-set",
+      ELEMNAME_ATTRIBUTE_STRING = "attribute",  // pattern-by-example support
+      ELEMNAME_CALLTEMPLATEARG_STRING = "invoke-arg",
+      ELEMNAME_CALLTEMPLATE_STRING = "call-template",
+      ELEMNAME_CALL_STRING = "call",
+      ELEMNAME_CHILDREN_STRING = "children",
+      ELEMNAME_CHOOSE_STRING = "choose",
+      ELEMNAME_COMMENT_STRING = "comment",
+      ELEMNAME_COMPONENT_STRING = "component",
+      ELEMNAME_CONSTRUCT_STRING = "construct",  // my own
+      ELEMNAME_CONTENTS_STRING = "contents", 
+      ELEMNAME_COPY_OF_STRING ="copy-of",
+      ELEMNAME_COPY_STRING = "copy",
+      ELEMNAME_COUNTERINCREMENT_STRING = "counter-increment",
+      ELEMNAME_COUNTERRESET_STRING = "counter-reset",
+      ELEMNAME_COUNTERSCOPE_STRING = "counter-scope",
+      ELEMNAME_COUNTERS_STRING = "counters",
+      ELEMNAME_COUNTER_STRING = "counter",
+      ELEMNAME_CSSSTYLECONVERSION_STRING = "css-style-conversion",
+      ELEMNAME_DECIMALFORMAT_STRING = "decimal-format",
+      ELEMNAME_DISPLAYIF_STRING = "display-if",  // my own
+      ELEMNAME_ELEMENT_STRING = "element",  // pattern-by-example support
+      ELEMNAME_EMPTY_STRING = "empty",
+      ELEMNAME_EVAL_STRING = "eval",
+      ELEMNAME_EXPECTEDCHILDREN_STRING = "expectedchildren",
+      ELEMNAME_EXTENSIONHANDLER_STRING = "code-dispatcher",
+      ELEMNAME_EXTENSION_STRING = "functions",
+      ELEMNAME_FALLBACK_STRING = "fallback",
+      ELEMNAME_FOREACH_STRING = "for-each",
+      ELEMNAME_IF_STRING = "if",
+      ELEMNAME_IMPORT_STRING = "import",
+      ELEMNAME_INCLUDE_STRING = "include",
+      ELEMNAME_KEY_STRING = "key",
+      ELEMNAME_LOCALE_STRING = "locale",
+      ELEMNAME_MESSAGE_STRING = "message",
+      ELEMNAME_NSALIAS_STRING = "namespace-alias",
+      ELEMNAME_NUMBER_STRING = "number",
+      ELEMNAME_OTHERWISE_STRING = "otherwise",
+      ELEMNAME_OUTPUT_STRING = "output",
+      ELEMNAME_PARAMVARIABLE_STRING = "param",
+      ELEMNAME_PI_OLD_STRING = "pi",
+      ELEMNAME_PI_STRING = "processing-instruction",
+      ELEMNAME_PRESERVESPACE_STRING = "preserve-space",
+      ELEMNAME_ROOT_STRING = "root",  // pattern-by-example support
+      ELEMNAME_SCRIPT_STRING = "script",
+      ELEMNAME_SORT_STRING = "sort",
+      ELEMNAME_STRIPSPACE_STRING = "strip-space",
+      ELEMNAME_STYLESHEET_STRING = "stylesheet",
+      ELEMNAME_TARGETATTRIBUTE_STRING = "target-attribute",  // pattern-by-example support
+      ELEMNAME_TARGETCOMMENT_STRING = "target-comment",
+      ELEMNAME_TARGETELEMENT_STRING = "target-element",  // pattern-by-example support
+      ELEMNAME_TARGETPI_STRING = "target-pi",
+      ELEMNAME_TARGETTEXT_STRING = "target-text",
+      ELEMNAME_TEMPLATE_STRING = "template",
+      ELEMNAME_TEXT_STRING = "text",
+      ELEMNAME_TRANSFORM_STRING = "transform",
+      ELEMNAME_URL_STRING = "uri",  // pattern-by-example support
+      ELEMNAME_USE_STRING = "use",
+      ELEMNAME_VALUEOF_STRING = "value-of",
+      ELEMNAME_VARIABLE_STRING = "variable",
+      ELEMNAME_WHEN_STRING = "when",
+      ELEMNAME_WITHPARAM_STRING = "with-param";
+  
+  /**
+   * Literals for EXSLT function elements.
+   */
+  public static final String
+    EXSLT_ELEMNAME_FUNCTION_STRING = "function",
+    EXSLT_ELEMNAME_FUNCRESULT_STRING = "result";
+  public static final int
+    EXSLT_ELEMNAME_FUNCTION = 88,
+    EXSLT_ELEMNAME_FUNCRESULT = 89;
+  
+  
+  /**
+   * Literals for XSL attribute names.  Note that there may be more
+   * names than IDs, because some names may map to the same ID.
+   */
+  public static final String
+	  ATTRNAME_AMOUNT = "amount",
+      ATTRNAME_ANCESTOR = "ancestor",
+      ATTRNAME_ARCHIVE = "archive",
+      ATTRNAME_ATTRIBUTE = "attribute",
+      ATTRNAME_ATTRIBUTE_SET = "attribute-set",
+      ATTRNAME_CASEORDER = "case-order",
+      ATTRNAME_CLASS = "class",
+      ATTRNAME_CLASSID = "classid",
+      ATTRNAME_CODEBASE = "codebase",
+      ATTRNAME_CODETYPE = "type",
+      ATTRNAME_CONDITION = "condition",
+      ATTRNAME_COPYTYPE = "copy-type",
+      ATTRNAME_COUNT = "count",
+      ATTRNAME_DATATYPE = "data-type",
+      ATTRNAME_DECIMALSEPARATOR = "decimal-separator",
+      ATTRNAME_DEFAULT = "default",
+      ATTRNAME_DEFAULTSPACE = "default-space",
+      ATTRNAME_DEPTH = "with-children",
+      ATTRNAME_DIGIT = "digit",
+      ATTRNAME_DIGITGROUPSEP = "digit-group-sep",
+      ATTRNAME_DISABLE_OUTPUT_ESCAPING = "disable-output-escaping",
+      ATTRNAME_ELEMENT = "element",
+      ATTRNAME_ELEMENTS = "elements",
+      ATTRNAME_EXCLUDE_RESULT_PREFIXES ="exclude-result-prefixes",
+      ATTRNAME_EXPR = "expr",
+      ATTRNAME_EXTENSIONELEMENTPREFIXES = "extension-element-prefixes",
+      ATTRNAME_FORMAT = "format",
+      ATTRNAME_FROM = "from",
+      ATTRNAME_GROUPINGSEPARATOR = "grouping-separator",
+      ATTRNAME_GROUPINGSIZE = "grouping-size",
+      ATTRNAME_HREF = "href",
+      ATTRNAME_ID = "id",
+      ATTRNAME_IMPORTANCE = "importance",
+      ATTRNAME_INDENTRESULT = "indent-result",
+      ATTRNAME_INFINITY = "infinity",
+      ATTRNAME_LANG = "lang",
+      ATTRNAME_LETTERVALUE = "letter-value",
+      ATTRNAME_LEVEL = "level",
+      ATTRNAME_MATCH = "match",
+      ATTRNAME_METHOD = "calls",
+      ATTRNAME_MINUSSIGN = "minus-sign",
+      ATTRNAME_MODE = "mode",
+      ATTRNAME_NAME = "name",
+      ATTRNAME_NAMESPACE = "namespace",
+      ATTRNAME_NAN = "NaN",
+      ATTRNAME_NDIGITSPERGROUP = "n-digits-per-group",
+      ATTRNAME_NS = "ns",
+      ATTRNAME_ONLY = "only",
+      ATTRNAME_ORDER = "order",
+      ATTRNAME_OUTPUT_CDATA_SECTION_ELEMENTS = "cdata-section-elements",
+      ATTRNAME_OUTPUT_DOCTYPE_PUBLIC = "doctype-public",
+      ATTRNAME_OUTPUT_DOCTYPE_SYSTEM = "doctype-system",
+      ATTRNAME_OUTPUT_ENCODING = "encoding",
+      ATTRNAME_OUTPUT_INDENT = "indent",
+      ATTRNAME_OUTPUT_MEDIATYPE = "media-type",
+      ATTRNAME_OUTPUT_METHOD = "method",  // qname, 
+      ATTRNAME_OUTPUT_OMITXMLDECL = "omit-xml-declaration",
+      ATTRNAME_OUTPUT_STANDALONE = "standalone",
+      ATTRNAME_OUTPUT_VERSION = "version",
+      ATTRNAME_PATTERNSEPARATOR = "pattern-separator",
+      ATTRNAME_PERCENT = "percent",
+      ATTRNAME_PERMILLE = "per-mille",
+      ATTRNAME_PRIORITY = "priority",
+      ATTRNAME_REFID = "refID",
+      ATTRNAME_RESULTNS = "result-ns",
+      ATTRNAME_RESULT_PREFIX = "result-prefix",
+      ATTRNAME_SELECT = "select",
+      ATTRNAME_SEQUENCESRC = "sequence-src",
+      ATTRNAME_STYLE = "style",
+      ATTRNAME_STYLESHEET_PREFIX = "stylesheet-prefix",
+      ATTRNAME_TERMINATE = "terminate",
+      ATTRNAME_TEST = "test",
+      ATTRNAME_TOSTRING = "to-string",
+      ATTRNAME_TYPE = "type",
+      ATTRNAME_USE = "use",
+      ATTRNAME_USEATTRIBUTESETS = "use-attribute-sets",
+      ATTRNAME_VALUE = "value",
+      ATTRNAME_VERSION = "version",
+      ATTRNAME_XMLNS = "xmlns:", // namespace declaration prefix -- NOT an attribute by itself
+      ATTRNAME_XMLNSDEF = "xmlns", // default namespace
+      ATTRNAME_XMLSPACE = "xml:space", 
+      ATTRNAME_ZERODIGIT = "zero-digit";
+
+  /** IDs for XSL attribute types. These are associated
+   * with the string literals in the TransformerImpl class.
+   * Don't change the numbers. NOTE THAT THESE ARE NOT IN
+   * ALPHABETICAL ORDER!
+   */
+  public static final int TATTRNAME_OUTPUT_METHOD = 1, TATTRNAME_AMOUNT = 2,
+                          TATTRNAME_ANCESTOR = 3, TATTRNAME_ARCHIVE = 4,
+                          TATTRNAME_ATTRIBUTE = 5,
+                          TATTRNAME_ATTRIBUTE_SET = 6,
+                          TATTRNAME_CASEORDER = 7, TATTRNAME_CLASS = 8,
+                          TATTRNAME_CLASSID = 9, TATTRNAME_CODEBASE = 10,
+                          TATTRNAME_CODETYPE = 11, TATTRNAME_CONDITION = 12,
+                          TATTRNAME_COPYTYPE = 13, TATTRNAME_COUNT = 14,
+                          TATTRNAME_DATATYPE = 15, TATTRNAME_DEFAULT = 16,
+                          TATTRNAME_DEFAULTSPACE = 17, TATTRNAME_DEPTH = 18,
+                          TATTRNAME_DIGITGROUPSEP = 19,
+                          TATTRNAME_DISABLE_OUTPUT_ESCAPING = 20,
+                          TATTRNAME_ELEMENT = 21, TATTRNAME_ELEMENTS = 22,
+                          TATTRNAME_EXPR = 23,
+                          TATTRNAME_EXTENSIONELEMENTPREFIXES = 24,
+                          TATTRNAME_FORMAT = 25, TATTRNAME_FROM = 26,
+                          TATTRNAME_GROUPINGSEPARATOR = 27,
+                          TATTRNAME_GROUPINGSIZE = 28, TATTRNAME_HREF = 29,
+                          TATTRNAME_ID = 30, TATTRNAME_IMPORTANCE = 31,
+                          TATTRNAME_INDENTRESULT = 32, TATTRNAME_LANG = 33,
+                          TATTRNAME_LETTERVALUE = 34, TATTRNAME_LEVEL = 35,
+                          TATTRNAME_MATCH = 36, TATTRNAME_METHOD = 37,
+                          TATTRNAME_MODE = 38, TATTRNAME_NAME = 39,
+                          TATTRNAME_NAMESPACE = 40,
+                          TATTRNAME_NDIGITSPERGROUP = 41, TATTRNAME_NS = 42,
+                          TATTRNAME_ONLY = 43, TATTRNAME_ORDER = 44,
+                          TATTRNAME_OUTPUT_CDATA_SECTION_ELEMENTS = 45,
+                          TATTRNAME_OUTPUT_DOCTYPE_PUBLIC = 46,
+                          TATTRNAME_OUTPUT_DOCTYPE_SYSTEM = 47,
+                          TATTRNAME_OUTPUT_ENCODING = 48,
+                          TATTRNAME_OUTPUT_INDENT = 49,
+                          TATTRNAME_OUTPUT_MEDIATYPE = 50,
+                          TATTRNAME_OUTPUT_STANDALONE = 51,
+                          TATTRNAME_OUTPUT_VERSION = 52,
+                          TATTRNAME_OUTPUT_OMITXMLDECL = 53,
+                          TATTRNAME_PRIORITY = 54, TATTRNAME_REFID = 55,
+                          TATTRNAME_RESULTNS = 56, TATTRNAME_SELECT = 57,
+                          TATTRNAME_SEQUENCESRC = 58, TATTRNAME_STYLE = 59,
+                          TATTRNAME_TEST = 60, TATTRNAME_TOSTRING = 61,
+                          TATTRNAME_TYPE = 62, TATTRNAME_USE = 63,
+                          TATTRNAME_USEATTRIBUTESETS = 64,
+                          TATTRNAME_VALUE = 65, TATTRNAME_XMLNSDEF = 66,
+                          TATTRNAME_XMLNS = 67, TATTRNAME_XMLSPACE = 68,
+                          TATTRNAME_EXCLUDE_RESULT_PREFIXES = 69;
+
+  /** Mnemonics for the possible values of the xsl:output element's
+   * method= attribute:
+   * <ul>
+   * <li>ATTRVAL_OUTPUT_METHOD_XML = Use an XML formatter to
+   * produce the output document (basic XSLT operation).</li>
+   * <li>ATTRVAL_OUTPUT_METHOD_HTML: Use an HTML formatter to
+   * produce the output document. When generating HTML documents,
+   * this may yield better results; it does things like escaping
+   * characters in href attributes.</li>
+   * </li>ATTRVAL_OUTPUT_METHOD_TEXT:  Use a Text formatter to
+   * produce the output document. Generally the right choice if your
+   * stylesheet wants to take over _all_ the details of formatting,
+   * most often when producing something that isn't an XML or HTML
+   * document.</li>
+   * </ul> 
+   * */
+  public static final String ATTRVAL_OUTPUT_METHOD_HTML = "html",
+                             ATTRVAL_OUTPUT_METHOD_XML = "xml",
+                             ATTRVAL_OUTPUT_METHOD_TEXT = "text";
+
+  
+  /* For space-att*/
+  public static final int ATTRVAL_PRESERVE = 1, ATTRVAL_STRIP = 2;
+
+  
+  /** For indent-result          */
+  public static final boolean ATTRVAL_YES = true, ATTRVAL_NO = false;
+
+  
+  /** For letter-value attribute (part of conversion attributes).          */
+  public static final String ATTRVAL_ALPHABETIC = "alphabetic",
+                             ATTRVAL_OTHER = "other",
+                             ATTRVAL_TRADITIONAL = "traditional";
+
+  
+  /** For level attribute in xsl:number.          */
+  public static final String ATTRVAL_SINGLE = "single",
+                             ATTRVAL_MULTI = "multiple", ATTRVAL_ANY = "any";
+
+  
+  /** For Stylesheet-prefix and result-prefix in xsl:namespace-alias          */
+  public static final String ATTRVAL_DEFAULT_PREFIX = "#default";
+
+ /** Integer equivalents for above        */
+  public static final int NUMBERLETTER_ALPHABETIC = 1, NUMBERLETTER_TRADITIONAL = 2;
+  
+  /** Integer equivelents for above        */
+  public static final int NUMBERLEVEL_SINGLE = 1, NUMBERLEVEL_MULTI = 2,
+                          NUMBERLEVEL_ANY = 3, MAX_MULTI_COUNTING_DEPTH = 32;
+
+  
+  /** some stuff for my patterns-by-example         */
+  public static final String ATTRVAL_THIS = ".", ATTRVAL_PARENT = "..",
+                             ATTRVAL_ANCESTOR = "ancestor", ATTRVAL_ID = "id";
+
+  
+  /** Stuff for sorting      */
+  public static final String ATTRVAL_DATATYPE_TEXT = "text",
+                             ATTRVAL_DATATYPE_NUMBER = "number",
+                             ATTRVAL_ORDER_ASCENDING = "ascending",
+                             ATTRVAL_ORDER_DESCENDING = "descending",
+                             ATTRVAL_CASEORDER_UPPER = "upper-first",
+                             ATTRVAL_CASEORDER_LOWER = "lower-first";
+
+ /** Integer equivelents for DATATYPE attribute */
+  public static final int SORTDATATYPE_TEXT = 1, SORTDATATYPE_NUMBER = 2;
+
+  /** Integer equivelents for ORDER attribute */
+  public static final int SORTORDER_ASCENDING = 1, SORTORDER_DESCENDING = 2;
+  
+  /** Integer equivelents for CASE-ORDER attribute */
+  public static final int SORTCASEORDER_UPPERFIRST = 1, SORTCASEORDER_LOWERFIRST = 2;
+  
+  /** some stuff for Decimal-format       */
+  public static final String ATTRVAL_INFINITY = "Infinity",
+                             ATTRVAL_NAN = "NaN",
+                             DEFAULT_DECIMAL_FORMAT = "#default";
+
+  
+  /** temp dummy         */
+  public static final String ATTRNAME_XXXX = "XXXX";
+}
diff --git a/src/main/java/org/apache/xalan/templates/DecimalFormatProperties.java b/src/main/java/org/apache/xalan/templates/DecimalFormatProperties.java
new file mode 100644
index 0000000..7291484
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/DecimalFormatProperties.java
@@ -0,0 +1,404 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DecimalFormatProperties.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.text.DecimalFormatSymbols;
+
+import org.apache.xml.utils.QName;
+
+/**
+ * Implement xsl:decimal-format.
+ * <pre>
+ * <!ELEMENT xsl:decimal-format EMPTY>
+ * <!ATTLIST xsl:decimal-format
+ *   name %qname; #IMPLIED
+ *   decimal-separator %char; "."
+ *   grouping-separator %char; ","
+ *   infinity CDATA "Infinity"
+ *   minus-sign %char; "-"
+ *   NaN CDATA "NaN"
+ *   percent %char; "%"
+ *   per-mille %char; "&#x2030;"
+ *   zero-digit %char; "0"
+ *   digit %char; "#"
+ *   pattern-separator %char; ";"
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#format-number">format-number in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class DecimalFormatProperties extends ElemTemplateElement
+{
+    static final long serialVersionUID = -6559409339256269446L;
+
+  /** An instance of DecimalFormatSymbols for this element.
+   *  @serial       */
+  DecimalFormatSymbols m_dfs;
+
+  /**
+   * Constructor DecimalFormatProperties
+   *
+   */
+  public DecimalFormatProperties(int docOrderNumber)
+  {
+
+    m_dfs = new java.text.DecimalFormatSymbols();
+
+    // Set default values, they can be overiden if necessary.  
+    m_dfs.setInfinity(Constants.ATTRVAL_INFINITY);
+    m_dfs.setNaN(Constants.ATTRVAL_NAN);
+
+    m_docOrderNumber = docOrderNumber;
+  }
+
+  /**
+   * Return the decimal format Symbols for this element.
+   * <p>The xsl:decimal-format element declares a decimal-format,
+   * which controls the interpretation of a format pattern used by
+   * the format-number function. If there is a name attribute, then
+   * the element declares a named decimal-format; otherwise, it
+   * declares the default decimal-format. The value of the name
+   * attribute is a QName, which is expanded as described in [2.4 Qualified Names].
+   * It is an error to declare either the default decimal-format or a
+   * decimal-format with a given name more than once (even with different
+   * import precedence), unless it is declared every time with the same
+   * value for all attributes (taking into account any default values).</p>
+   * <p>The other attributes on xsl:decimal-format correspond to the
+   * methods on the JDK 1.1 DecimalFormatSymbols class. For each get/set
+   * method pair there is an attribute defined for the xsl:decimal-format
+   * element.</p>
+   *
+   * @return the decimal format Symbols for this element.
+   */
+  public DecimalFormatSymbols getDecimalFormatSymbols()
+  {
+    return m_dfs;
+  }
+
+  /**
+   * If there is a name attribute, then the element declares a named
+   * decimal-format; otherwise, it declares the default decimal-format.
+   * @serial
+   */
+  private QName m_qname = null;
+
+  /**
+   * Set the "name" attribute.
+   * If there is a name attribute, then the element declares a named
+   * decimal-format; otherwise, it declares the default decimal-format.
+   *
+   * @param qname The name to set as the "name" attribute.
+   */
+  public void setName(QName qname)
+  {
+    m_qname = qname;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * If there is a name attribute, then the element declares a named
+   * decimal-format; otherwise, it declares the default decimal-format.
+   *
+   * @return the value of the "name" attribute.
+   */
+  public QName getName()
+  {
+
+    if (m_qname == null)
+      return new QName("");
+    else
+      return m_qname;
+  }
+
+  /**
+   * Set the "decimal-separator" attribute.
+   * decimal-separator specifies the character used for the decimal sign;
+   * the default value is the period character (.).
+   *
+   * @param ds Character to set as decimal separator 
+   */
+  public void setDecimalSeparator(char ds)
+  {
+    m_dfs.setDecimalSeparator(ds);
+  }
+
+  /**
+   * Get the "decimal-separator" attribute.
+   * decimal-separator specifies the character used for the decimal sign;
+   * the default value is the period character (.).
+   *
+   * @return the character to use as decimal separator
+   */
+  public char getDecimalSeparator()
+  {
+    return m_dfs.getDecimalSeparator();
+  }
+
+  /**
+   * Set the "grouping-separator" attribute.
+   * grouping-separator specifies the character used as a grouping
+   * (e.g. thousands) separator; the default value is the comma character (,).
+   *
+   * @param gs Character to use a grouping separator 
+   */
+  public void setGroupingSeparator(char gs)
+  {
+    m_dfs.setGroupingSeparator(gs);
+  }
+
+  /**
+   * Get the "grouping-separator" attribute.
+   * grouping-separator specifies the character used as a grouping
+   * (e.g. thousands) separator; the default value is the comma character (,).
+   *
+   * @return Character to use a grouping separator 
+   */
+  public char getGroupingSeparator()
+  {
+    return m_dfs.getGroupingSeparator();
+  }
+
+  /**
+   * Set the "infinity" attribute.
+   * infinity specifies the string used to represent infinity;
+   * the default value is the string Infinity.
+   *
+   * @param inf String to use as the "infinity" attribute.
+   */
+  public void setInfinity(String inf)
+  {
+    m_dfs.setInfinity(inf);
+  }
+
+  /**
+   * Get the "infinity" attribute.
+   * infinity specifies the string used to represent infinity;
+   * the default value is the string Infinity.
+   *
+   * @return String to use as the "infinity" attribute.
+   */
+  public String getInfinity()
+  {
+    return m_dfs.getInfinity();
+  }
+
+  /**
+   * Set the "minus-sign" attribute.
+   * minus-sign specifies the character used as the default minus sign; the
+   * default value is the hyphen-minus character (-, #x2D).
+   *
+   * @param v Character to use as minus sign
+   */
+  public void setMinusSign(char v)
+  {
+    m_dfs.setMinusSign(v);
+  }
+
+  /**
+   * Get the "minus-sign" attribute.
+   * minus-sign specifies the character used as the default minus sign; the
+   * default value is the hyphen-minus character (-, #x2D).
+   *
+   * @return Character to use as minus sign
+   */
+  public char getMinusSign()
+  {
+    return m_dfs.getMinusSign();
+  }
+
+  /**
+   * Set the "NaN" attribute.
+   * NaN specifies the string used to represent the NaN value;
+   * the default value is the string NaN.
+   *
+   * @param v String to use as the "NaN" attribute.
+   */
+  public void setNaN(String v)
+  {
+    m_dfs.setNaN(v);
+  }
+
+  /**
+   * Get the "NaN" attribute.
+   * NaN specifies the string used to represent the NaN value;
+   * the default value is the string NaN.
+   *
+   * @return String to use as the "NaN" attribute.
+   */
+  public String getNaN()
+  {
+    return m_dfs.getNaN();
+  }
+  
+  /**
+   * Return the node name.
+   *
+   * @return the element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_DECIMALFORMAT_STRING;
+  }
+
+  /**
+   * Set the "percent" attribute.
+   * percent specifies the character used as a percent sign; the default
+   * value is the percent character (%).
+   *
+   * @param v Character to use as percent 
+   */
+  public void setPercent(char v)
+  {
+    m_dfs.setPercent(v);
+  }
+
+  /**
+   * Get the "percent" attribute.
+   * percent specifies the character used as a percent sign; the default
+   * value is the percent character (%).
+   *
+   * @return Character to use as percent 
+   */
+  public char getPercent()
+  {
+    return m_dfs.getPercent();
+  }
+
+  /**
+   * Set the "per-mille" attribute.
+   * per-mille specifies the character used as a per mille sign; the default
+   * value is the Unicode per-mille character (#x2030).
+   *
+   * @param v Character to use as per-mille
+   */
+  public void setPerMille(char v)
+  {
+    m_dfs.setPerMill(v);
+  }
+
+  /**
+   * Get the "per-mille" attribute.
+   * per-mille specifies the character used as a per mille sign; the default
+   * value is the Unicode per-mille character (#x2030).
+   *
+   * @return Character to use as per-mille 
+   */
+  public char getPerMille()
+  {
+    return m_dfs.getPerMill();
+  }
+  
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_DECIMALFORMAT;
+  }
+  
+  /**
+   * Set the "zero-digit" attribute.
+   * zero-digit specifies the character used as the digit zero; the default
+   * value is the digit zero (0).
+   *
+   * @param v Character to use as the digit zero
+   */
+  public void setZeroDigit(char v)
+  {
+    m_dfs.setZeroDigit(v);
+  }
+
+  /**
+   * Get the "zero-digit" attribute.
+   * zero-digit specifies the character used as the digit zero; the default
+   * value is the digit zero (0).
+   *
+   * @return Character to use as the digit zero
+   */
+  public char getZeroDigit()
+  {
+    return m_dfs.getZeroDigit();
+  }
+
+  /**
+   * Set the "digit" attribute.
+   * digit specifies the character used for a digit in the format pattern;
+   * the default value is the number sign character (#).
+   *
+   * @param v Character to use for a digit in format pattern
+   */
+  public void setDigit(char v)
+  {
+    m_dfs.setDigit(v);
+  }
+
+  /**
+   * Get the "digit" attribute.
+   * digit specifies the character used for a digit in the format pattern;
+   * the default value is the number sign character (#).
+   *
+   * @return Character to use for a digit in format pattern
+   */
+  public char getDigit()
+  {
+    return m_dfs.getDigit();
+  }
+
+  /**
+   * Set the "pattern-separator" attribute.
+   * pattern-separator specifies the character used to separate positive
+   * and negative sub patterns in a pattern; the default value is the
+   * semi-colon character (;).
+   *
+   * @param v Character to use as a pattern separator
+   */
+  public void setPatternSeparator(char v)
+  {
+    m_dfs.setPatternSeparator(v);
+  }
+
+  /**
+   * Get the "pattern-separator" attribute.
+   * pattern-separator specifies the character used to separate positive
+   * and negative sub patterns in a pattern; the default value is the
+   * semi-colon character (;).
+   *
+   * @return Character to use as a pattern separator
+   */
+  public char getPatternSeparator()
+  {
+    return m_dfs.getPatternSeparator();
+  }
+
+  /**
+   * This function is called to recompose() all of the decimal format properties elements.
+   * 
+   * @param root Stylesheet root
+   */
+  public void recompose(StylesheetRoot root)
+  {
+    root.recomposeDecimalFormats(this);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemApplyImport.java b/src/main/java/org/apache/xalan/templates/ElemApplyImport.java
new file mode 100644
index 0000000..39b9919
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemApplyImport.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemApplyImport.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+
+/**
+ * Implement xsl:apply-imports.
+ * <pre>
+ * <!ELEMENT xsl:apply-imports EMPTY>
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#apply-imports">apply-imports in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemApplyImport extends ElemTemplateElement
+{
+    static final long serialVersionUID = 3764728663373024038L;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return Token ID for xsl:apply-imports element types
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_APPLY_IMPORTS;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return Element name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_APPLY_IMPORTS_STRING;
+  }
+
+  /**
+   * Execute the xsl:apply-imports transformation.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    if (transformer.currentTemplateRuleIsNull())
+    {
+      transformer.getMsgMgr().error(this,
+        XSLTErrorResources.ER_NO_APPLY_IMPORT_IN_FOR_EACH);  //"xsl:apply-imports not allowed in a xsl:for-each");
+    }
+
+    int sourceNode = transformer.getXPathContext().getCurrentNode();
+    if (DTM.NULL != sourceNode)
+    {
+      // supply the current templated (matched, not named)        
+      ElemTemplate matchTemplate = transformer.getMatchedTemplate();
+      transformer.applyTemplateToNode(this, matchTemplate, sourceNode);
+    }
+    else  // if(null == sourceNode)
+    {
+      transformer.getMsgMgr().error(this,
+        XSLTErrorResources.ER_NULL_SOURCENODE_APPLYIMPORTS);  //"sourceNode is null in xsl:apply-imports!");
+    }
+  }
+
+  /**
+   * Add a child to the child list.
+   * <!ELEMENT xsl:apply-imports EMPTY>
+   *
+   * @param newChild New element to append to this element's children list
+   *
+   * @return null, xsl:apply-Imports cannot have children 
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    error(XSLTErrorResources.ER_CANNOT_ADD,
+          new Object[]{ newChild.getNodeName(),
+                        this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    return null;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemApplyTemplates.java b/src/main/java/org/apache/xalan/templates/ElemApplyTemplates.java
new file mode 100644
index 0000000..e332ff0
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemApplyTemplates.java
@@ -0,0 +1,421 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemApplyTemplates.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xml.utils.IntStack;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.xml.sax.SAXException;
+
+/**
+ * Implement xsl:apply-templates.
+ * <pre>
+ * &amp;!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
+ * &amp;!ATTLIST xsl:apply-templates
+ *   select %expr; "node()"
+ *   mode %qname; #IMPLIED
+ * &amp;
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemApplyTemplates extends ElemCallTemplate
+{
+    static final long serialVersionUID = 2903125371542621004L;
+
+  /**
+   * mode %qname; #IMPLIED
+   * @serial
+   */
+  private QName m_mode = null;
+
+  /**
+   * Set the mode attribute for this element.
+   *
+   * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
+   */
+  public void setMode(QName mode)
+  {
+    m_mode = mode;
+  }
+
+  /**
+   * Get the mode attribute for this element.
+   *
+   * @return The mode attribute for this element
+   */
+  public QName getMode()
+  {
+    return m_mode;
+  }
+
+  /**
+   * Tells if this belongs to a default template,
+   * in which case it will act different with
+   * regard to processing modes.
+   * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a>
+   * @serial
+   */
+  private boolean m_isDefaultTemplate = false;
+  
+//  /**
+//   * List of namespace/localname IDs, for identification of xsl:with-param to 
+//   * xsl:params.  Initialized in the compose() method.
+//   */
+//  private int[] m_paramIDs;
+
+  /**
+   * Set if this belongs to a default template,
+   * in which case it will act different with
+   * regard to processing modes.
+   * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a>
+   *
+   * @param b boolean value to set.
+   */
+  public void setIsDefaultTemplate(boolean b)
+  {
+    m_isDefaultTemplate = b;
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return Token ID for this element types
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_APPLY_TEMPLATES;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return Element name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_APPLY_TEMPLATES_STRING;
+  }
+
+  /**
+   * Apply the context node to the matching templates.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {
+
+    transformer.pushCurrentTemplateRuleIsNull(false);
+
+    boolean pushMode = false;
+
+    try
+    {
+      // %REVIEW% Do we need this check??
+      //      if (null != sourceNode)
+      //      {
+      // boolean needToTurnOffInfiniteLoopCheck = false;
+      QName mode = transformer.getMode();
+
+      if (!m_isDefaultTemplate)
+      {
+        if (((null == mode) && (null != m_mode))
+                || ((null != mode) &&!mode.equals(m_mode)))
+        {
+          pushMode = true;
+
+          transformer.pushMode(m_mode);
+        }
+      }
+
+      transformSelectedNodes(transformer);
+    }
+    finally
+    {
+      if (pushMode)
+        transformer.popMode();
+
+      transformer.popCurrentTemplateRuleIsNull();
+    }
+  }
+
+  
+  /**
+   * Perform a query if needed, and call transformNode for each child.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException Thrown in a variety of circumstances.
+   * @xsl.usage advanced
+   */
+  public void transformSelectedNodes(TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    final XPathContext xctxt = transformer.getXPathContext();
+    final int sourceNode = xctxt.getCurrentNode();
+    DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt, sourceNode);
+    VariableStack vars = xctxt.getVarStack();
+    int nParams = getParamElemCount();
+    int thisframe = vars.getStackFrame();
+
+    boolean pushContextNodeListFlag = false;
+      
+    try
+    {
+
+            xctxt.pushCurrentNode(DTM.NULL);
+            xctxt.pushCurrentExpressionNode(DTM.NULL);
+            xctxt.pushSAXLocatorNull();
+            transformer.pushElemTemplateElement(null);
+      final Vector keys = (m_sortElems == null)
+                          ? null
+                          : transformer.processSortKeys(this, sourceNode);
+
+      // Sort if we need to.
+      if (null != keys)
+        sourceNodes = sortNodes(xctxt, keys, sourceNodes);
+            
+      final SerializationHandler rth = transformer.getSerializationHandler();
+//      ContentHandler chandler = rth.getContentHandler();
+      final StylesheetRoot sroot = transformer.getStylesheet();
+      final TemplateList tl = sroot.getTemplateListComposed();
+      final boolean quiet = transformer.getQuietConflictWarnings();
+      
+      // Should be able to get this from the iterator but there must be a bug.
+      DTM dtm = xctxt.getDTM(sourceNode);
+      
+      int argsFrame = -1;
+      if(nParams > 0)
+      {
+        // This code will create a section on the stack that is all the 
+        // evaluated arguments.  These will be copied into the real params 
+        // section of each called template.
+        argsFrame = vars.link(nParams);
+        vars.setStackFrame(thisframe);
+        
+        for (int i = 0; i < nParams; i++) 
+        {
+          ElemWithParam ewp = m_paramElems[i];
+          XObject obj = ewp.getValue(transformer, sourceNode);
+
+          vars.setLocalVariable(i, obj, argsFrame);
+        }
+        vars.setStackFrame(argsFrame);
+      }
+      
+      xctxt.pushContextNodeList(sourceNodes);
+      pushContextNodeListFlag = true;
+      
+      IntStack currentNodes = xctxt.getCurrentNodeStack();
+      
+      IntStack currentExpressionNodes = xctxt.getCurrentExpressionNodeStack();     
+      
+      // pushParams(transformer, xctxt);
+      
+      int child;
+      while (DTM.NULL != (child = sourceNodes.nextNode()))
+      {
+        currentNodes.setTop(child);
+        currentExpressionNodes.setTop(child);
+
+        if(xctxt.getDTM(child) != dtm)
+        {
+          dtm = xctxt.getDTM(child);
+        }
+        
+        final int exNodeType = dtm.getExpandedTypeID(child);
+
+        final int nodeType = dtm.getNodeType(child);
+
+        final QName mode = transformer.getMode();
+
+        ElemTemplate template = tl.getTemplateFast(xctxt, child, exNodeType, mode, 
+                                      -1, quiet, dtm);
+
+        // If that didn't locate a node, fall back to a default template rule.
+        // See http://www.w3.org/TR/xslt#built-in-rule.
+        if (null == template)
+        {
+          switch (nodeType)
+          {
+          case DTM.DOCUMENT_FRAGMENT_NODE :
+          case DTM.ELEMENT_NODE :
+            template = sroot.getDefaultRule();
+            // %OPT% direct faster?
+            break;
+          case DTM.ATTRIBUTE_NODE :
+          case DTM.CDATA_SECTION_NODE :
+          case DTM.TEXT_NODE :
+            // if(rth.m_elemIsPending || rth.m_docPending)
+            //  rth.flushPending(true);
+            transformer.pushPairCurrentMatched(sroot.getDefaultTextRule(), child);
+            transformer.setCurrentElement(sroot.getDefaultTextRule());
+            // dtm.dispatchCharactersEvents(child, chandler, false);
+            dtm.dispatchCharactersEvents(child, rth, false);
+            transformer.popCurrentMatched();
+            continue;
+          case DTM.DOCUMENT_NODE :
+            template = sroot.getDefaultRootRule();
+            break;
+          default :
+
+            // No default rules for processing instructions and the like.
+            continue;
+          }
+        }
+        else
+        {
+        	transformer.setCurrentElement(template);
+        }
+                
+        transformer.pushPairCurrentMatched(template, child);
+
+        int currentFrameBottom;  // See comment with unlink, below
+        if(template.m_frameSize > 0)
+        {
+          xctxt.pushRTFContext();
+          currentFrameBottom = vars.getStackFrame();  // See comment with unlink, below
+          vars.link(template.m_frameSize);
+          // You can't do the check for nParams here, otherwise the 
+          // xsl:params might not be nulled.
+          if(/* nParams > 0 && */ template.m_inArgsSize > 0)
+          {
+            int paramIndex = 0;
+            for (ElemTemplateElement elem = template.getFirstChildElem(); 
+                 null != elem; elem = elem.getNextSiblingElem()) 
+            {
+              if(Constants.ELEMNAME_PARAMVARIABLE == elem.getXSLToken())
+              {
+                ElemParam ep = (ElemParam)elem;
+                
+                int i;
+                for (i = 0; i < nParams; i++) 
+                {
+                  ElemWithParam ewp = m_paramElems[i];
+                  if(ewp.m_qnameID == ep.m_qnameID)
+                  {
+                    XObject obj = vars.getLocalVariable(i, argsFrame);
+                    vars.setLocalVariable(paramIndex, obj);
+                    break;
+                  }
+                }
+                if(i == nParams)
+                  vars.setLocalVariable(paramIndex, null);
+              }
+              else
+                break;
+              paramIndex++;
+            }
+            
+          }
+        }
+        else
+        	currentFrameBottom = 0;
+
+        // And execute the child templates.
+        // Loop through the children of the template, calling execute on 
+        // each of them.
+        for (ElemTemplateElement t = template.m_firstChild; 
+             t != null; t = t.m_nextSibling)
+        {
+          xctxt.setSAXLocator(t);
+          try
+          {
+          	transformer.pushElemTemplateElement(t);
+          	t.execute(transformer);
+          }
+          finally
+          {
+          	transformer.popElemTemplateElement();
+          }
+        }
+        
+        if(template.m_frameSize > 0)
+        {
+          // See Frank Weiss bug around 03/19/2002 (no Bugzilla report yet).
+          // While unlink will restore to the proper place, the real position 
+          // may have been changed for xsl:with-param, so that variables 
+          // can be accessed.  
+          // of right now.
+          // More:
+          // When we entered this function, the current 
+          // frame buffer (cfb) index in the variable stack may 
+          // have been manually set.  If we just call 
+          // unlink(), however, it will restore the cfb to the 
+          // previous link index from the link stack, rather than 
+          // the manually set cfb.  So, 
+          // the only safe solution is to restore it back 
+          // to the same position it was on entry, since we're 
+          // really not working in a stack context here. (Bug4218)
+          vars.unlink(currentFrameBottom);
+          xctxt.popRTFContext();
+        }
+          
+        transformer.popCurrentMatched();
+        
+      } // end while (DTM.NULL != (child = sourceNodes.nextNode()))
+    }
+    catch (SAXException se)
+    {
+      transformer.getErrorListener().fatalError(new TransformerException(se));
+    }
+    finally
+    {
+      // Unlink to the original stack frame
+      if(nParams > 0)
+        vars.unlink(thisframe);
+      xctxt.popSAXLocator();
+      if (pushContextNodeListFlag) xctxt.popContextNodeList();
+      transformer.popElemTemplateElement();
+      xctxt.popCurrentExpressionNode();
+      xctxt.popCurrentNode();
+      sourceNodes.detach();
+    }
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemAttribute.java b/src/main/java/org/apache/xalan/templates/ElemAttribute.java
new file mode 100644
index 0000000..a6ca934
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemAttribute.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemAttribute.java 469304 2006-10-30 22:29:47Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.serializer.NamespaceMappings;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.XML11Char;
+
+import org.xml.sax.SAXException;
+
+/**
+ * Implement xsl:attribute.
+ * <pre>
+ * &amp;!ELEMENT xsl:attribute %char-template;>
+ * &amp;!ATTLIST xsl:attribute
+ *   name %avt; #REQUIRED
+ *   namespace %avt; #IMPLIED
+ *   %space-att;
+ * &amp;
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#creating-attributes">creating-attributes in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemAttribute extends ElemElement
+{
+    static final long serialVersionUID = 8817220961566919187L;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_ATTRIBUTE;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element name 
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_ATTRIBUTE_STRING;
+  }
+
+  /**
+   * Create an attribute in the result tree.
+   * @see <a href="http://www.w3.org/TR/xslt#creating-attributes">creating-attributes in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+//  public void execute(
+//          TransformerImpl transformer)
+//            throws TransformerException
+//  {
+    //SerializationHandler rhandler = transformer.getSerializationHandler();
+
+    // If they are trying to add an attribute when there isn't an 
+    // element pending, it is an error.
+    // I don't think we need this check here because it is checked in 
+    // ResultTreeHandler.addAttribute.  (is)
+//    if (!rhandler.isElementPending())
+//    {
+//      // Make sure the trace event is sent.
+//      if (TransformerImpl.S_DEBUG)
+//        transformer.getTraceManager().fireTraceEvent(this);
+//
+//      XPathContext xctxt = transformer.getXPathContext();
+//      int sourceNode = xctxt.getCurrentNode();
+//      String attrName = m_name_avt.evaluate(xctxt, sourceNode, this);
+//      transformer.getMsgMgr().warn(this,
+//                                   XSLTErrorResources.WG_ILLEGAL_ATTRIBUTE_POSITION,
+//                                   new Object[]{ attrName });
+//
+//      if (TransformerImpl.S_DEBUG)
+//        transformer.getTraceManager().fireTraceEndEvent(this);
+//      return;
+//
+//      // warn(templateChild, sourceNode, "Trying to add attribute after element child has been added, ignoring...");
+//    }
+    
+//    super.execute(transformer);
+    
+//  }
+  
+  /**
+   * Resolve the namespace into a prefix.  At this level, if no prefix exists, 
+   * then return a manufactured prefix.
+   *
+   * @param rhandler The current result tree handler.
+   * @param prefix The probable prefix if already known.
+   * @param nodeNamespace  The namespace, which should not be null.
+   *
+   * @return The prefix to be used.
+   */
+  protected String resolvePrefix(SerializationHandler rhandler,
+                                 String prefix, String nodeNamespace)
+    throws TransformerException
+  {
+
+    if (null != prefix && (prefix.length() == 0 || prefix.equals("xmlns")))
+    {
+      // Since we can't use default namespace, in this case we try and 
+      // see if a prefix has already been defined or this namespace.
+      prefix = rhandler.getPrefix(nodeNamespace);
+
+      // System.out.println("nsPrefix: "+nsPrefix);           
+      if (null == prefix || prefix.length() == 0 || prefix.equals("xmlns"))
+      {
+        if(nodeNamespace.length() > 0)
+        {
+            NamespaceMappings prefixMapping = rhandler.getNamespaceMappings();
+            prefix = prefixMapping.generateNextPrefix();
+        }
+        else
+          prefix = "";
+      }
+    }
+    return prefix;
+  }
+  
+  /**
+   * Validate that the node name is good.
+   * 
+   * @param nodeName Name of the node being constructed, which may be null.
+   * 
+   * @return true if the node name is valid, false otherwise.
+   */
+   protected boolean validateNodeName(String nodeName)
+   {
+      if(null == nodeName)
+        return false;
+      if(nodeName.equals("xmlns"))
+        return false;
+      return XML11Char.isXML11ValidQName(nodeName);
+   }
+  
+  /**
+   * Construct a node in the result tree.  This method is overloaded by 
+   * xsl:attribute. At this class level, this method creates an element.
+   *
+   * @param nodeName The name of the node, which may be null.
+   * @param prefix The prefix for the namespace, which may be null.
+   * @param nodeNamespace The namespace of the node, which may be null.
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
+   * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
+   *
+   * @throws TransformerException
+   */
+  void constructNode(
+          String nodeName, String prefix, String nodeNamespace, TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    if(null != nodeName && nodeName.length() > 0)
+    {
+      SerializationHandler rhandler = transformer.getSerializationHandler();
+
+      // Evaluate the value of this attribute
+      String val = transformer.transformToString(this);
+      try 
+      {
+        // Let the result tree handler add the attribute and its String value.
+        String localName = QName.getLocalPart(nodeName);
+        if(prefix != null && prefix.length() > 0){
+            rhandler.addAttribute(nodeNamespace, localName, nodeName, "CDATA", val, true);
+        }else{
+            rhandler.addAttribute("", localName, nodeName, "CDATA", val, true);
+        }
+      }
+      catch (SAXException e)
+      {
+      }
+    }
+  }
+
+
+  /**
+   * Add a child to the child list.
+   * <!ELEMENT xsl:attribute %char-template;>
+   * <!ATTLIST xsl:attribute
+   *   name %avt; #REQUIRED
+   *   namespace %avt; #IMPLIED
+   *   %space-att;
+   * >
+   *
+   * @param newChild Child to append to the list of this node's children
+   *
+   * @return The node we just appended to the children list 
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    int type = ((ElemTemplateElement) newChild).getXSLToken();
+
+    switch (type)
+    {
+
+    // char-instructions 
+    case Constants.ELEMNAME_TEXTLITERALRESULT :
+    case Constants.ELEMNAME_APPLY_TEMPLATES :
+    case Constants.ELEMNAME_APPLY_IMPORTS :
+    case Constants.ELEMNAME_CALLTEMPLATE :
+    case Constants.ELEMNAME_FOREACH :
+    case Constants.ELEMNAME_VALUEOF :
+    case Constants.ELEMNAME_COPY_OF :
+    case Constants.ELEMNAME_NUMBER :
+    case Constants.ELEMNAME_CHOOSE :
+    case Constants.ELEMNAME_IF :
+    case Constants.ELEMNAME_TEXT :
+    case Constants.ELEMNAME_COPY :
+    case Constants.ELEMNAME_VARIABLE :
+    case Constants.ELEMNAME_MESSAGE :
+
+      // instructions 
+      // case Constants.ELEMNAME_PI:
+      // case Constants.ELEMNAME_COMMENT:
+      // case Constants.ELEMNAME_ELEMENT:
+      // case Constants.ELEMNAME_ATTRIBUTE:
+      break;
+    default :
+      error(XSLTErrorResources.ER_CANNOT_ADD,
+            new Object[]{ newChild.getNodeName(),
+                          this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    }
+
+    return super.appendChild(newChild);
+  }
+	/**
+	 * @see ElemElement#setName(AVT)
+	 */
+	public void setName(AVT v) {
+        if (v.isSimple())
+        {
+            if (v.getSimpleString().equals("xmlns"))
+            {
+                throw new IllegalArgumentException();
+            }
+        }
+		super.setName(v);
+	}
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemAttributeSet.java b/src/main/java/org/apache/xalan/templates/ElemAttributeSet.java
new file mode 100644
index 0000000..8f85122
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemAttributeSet.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemAttributeSet.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.QName;
+
+/**
+ * Implement xsl:attribute-set.
+ * <pre>
+ * &amp;!ELEMENT xsl:attribute-set (xsl:attribute)*>
+ * &amp;!ATTLIST xsl:attribute-set
+ *   name %qname; #REQUIRED
+ *   use-attribute-sets %qnames; #IMPLIED
+ * &amp;
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemAttributeSet extends ElemUse
+{
+    static final long serialVersionUID = -426740318278164496L;
+
+  /**
+   * The name attribute specifies the name of the attribute set.
+   * @serial
+   */
+  public QName m_qname = null;
+
+  /**
+   * Set the "name" attribute.
+   * The name attribute specifies the name of the attribute set.
+   *
+   * @param name Name attribute to set
+   */
+  public void setName(QName name)
+  {
+    m_qname = name;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * The name attribute specifies the name of the attribute set.
+   *
+   * @return The name attribute of the attribute set
+   */
+  public QName getName()
+  {
+    return m_qname;
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return Token ID of the element 
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_DEFINEATTRIBUTESET;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The name of this element
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_ATTRIBUTESET_STRING;
+  }
+
+  /**
+   * Apply a set of attributes to the element.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    if (transformer.isRecursiveAttrSet(this))
+    {
+      throw new TransformerException(
+        XSLMessages.createMessage(
+          XSLTErrorResources.ER_XSLATTRSET_USED_ITSELF,
+          new Object[]{ m_qname.getLocalPart() }));  //"xsl:attribute-set '"+m_qname.m_localpart+
+    }
+
+    transformer.pushElemAttributeSet(this);
+    super.execute(transformer);
+
+    ElemAttribute attr = (ElemAttribute) getFirstChildElem();
+
+    while (null != attr)
+    {
+      attr.execute(transformer);
+
+      attr = (ElemAttribute) attr.getNextSiblingElem();
+    }
+
+    transformer.popElemAttributeSet();
+  }
+
+  /**
+   * Add a child to the child list.
+   * <!ELEMENT xsl:attribute-set (xsl:attribute)*>
+   * <!ATTLIST xsl:attribute-set
+   *   name %qname; #REQUIRED
+   *   use-attribute-sets %qnames; #IMPLIED
+   * >
+   *
+   * @param newChild Child to be added to this node's list of children
+   *
+   * @return The child that was just added to the list of children
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChildElem(ElemTemplateElement newChild)
+  {
+
+    int type = ((ElemTemplateElement) newChild).getXSLToken();
+
+    switch (type)
+    {
+    case Constants.ELEMNAME_ATTRIBUTE :
+      break;
+    default :
+      error(XSLTErrorResources.ER_CANNOT_ADD,
+            new Object[]{ newChild.getNodeName(),
+                          this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    }
+
+    return super.appendChild(newChild);
+  }
+
+  /**
+   * This function is called during recomposition to
+   * control how this element is composed.
+   * @param root The root stylesheet for this transformation.
+   */
+  public void recompose(StylesheetRoot root)
+  {
+    root.recomposeAttributeSets(this);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemCallTemplate.java b/src/main/java/org/apache/xalan/templates/ElemCallTemplate.java
new file mode 100644
index 0000000..8bcb37c
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemCallTemplate.java
@@ -0,0 +1,364 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemCallTemplate.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Implement xsl:call-template.
+ * <pre>
+ * &amp;!ELEMENT xsl:call-template (xsl:with-param)*>
+ * &amp;!ATTLIST xsl:call-template
+ *   name %qname; #REQUIRED
+ * &amp;
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemCallTemplate extends ElemForEach
+{
+    static final long serialVersionUID = 5009634612916030591L;
+
+  /**
+   * An xsl:call-template element invokes a template by name;
+   * it has a required name attribute that identifies the template to be invoked.
+   * @serial
+   */
+  public QName m_templateName = null;
+
+  /**
+   * Set the "name" attribute.
+   * An xsl:call-template element invokes a template by name;
+   * it has a required name attribute that identifies the template to be invoked.
+   *
+   * @param name Name attribute to set
+   */
+  public void setName(QName name)
+  {
+    m_templateName = name;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * An xsl:call-template element invokes a template by name;
+   * it has a required name attribute that identifies the template to be invoked.
+   *
+   * @return Name attribute of this element
+   */
+  public QName getName()
+  {
+    return m_templateName;
+  }
+
+  /**
+   * The template which is named by QName.
+   * @serial
+   */
+  private ElemTemplate m_template = null;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element 
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_CALLTEMPLATE;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The name of this element
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_CALLTEMPLATE_STRING;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    
+    // Call compose on each param no matter if this is apply-templates 
+    // or call templates.
+    int length = getParamElemCount();
+    for (int i = 0; i < length; i++) 
+    {
+      ElemWithParam ewp = getParamElem(i);
+      ewp.compose(sroot);
+    }
+    
+	if ((null != m_templateName) && (null == m_template)) {
+		m_template =
+			this.getStylesheetRoot().getTemplateComposed(m_templateName);
+
+		if (null == m_template) {
+			String themsg =
+				XSLMessages.createMessage(
+					XSLTErrorResources.ER_ELEMTEMPLATEELEM_ERR,
+					new Object[] { m_templateName });
+
+			throw new TransformerException(themsg, this);
+			//"Could not find template named: '"+templateName+"'");
+		}
+    
+      length = getParamElemCount();
+      for (int i = 0; i < length; i++) 
+      {
+        ElemWithParam ewp = getParamElem(i);
+        ewp.m_index = -1;
+        // Find the position of the param in the template being called, 
+        // and set the index of the param slot.
+        int etePos = 0;
+        for (ElemTemplateElement ete = m_template.getFirstChildElem(); 
+             null != ete; ete = ete.getNextSiblingElem()) 
+        {
+          if(ete.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE)
+          {
+            ElemParam ep = (ElemParam)ete;
+            if(ep.getName().equals(ewp.getName()))
+            {
+              ewp.m_index = etePos;
+            }
+          }
+          else
+            break;
+          etePos++;
+        }
+        
+      }
+    }
+  }
+  
+  /**
+   * This after the template's children have been composed.
+   */
+  public void endCompose(StylesheetRoot sroot) throws TransformerException
+  {
+    int length = getParamElemCount();
+    for (int i = 0; i < length; i++) 
+    {
+      ElemWithParam ewp = getParamElem(i);
+      ewp.endCompose(sroot);
+    }    
+    
+    super.endCompose(sroot);
+  }
+
+  /**
+   * Invoke a named template.
+   * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    if (null != m_template)
+    {
+      XPathContext xctxt = transformer.getXPathContext();
+      VariableStack vars = xctxt.getVarStack();
+
+      int thisframe = vars.getStackFrame();
+      int nextFrame = vars.link(m_template.m_frameSize);
+      
+      // We have to clear the section of the stack frame that has params 
+      // so that the default param evaluation will work correctly.
+      if(m_template.m_inArgsSize > 0)
+      {
+        vars.clearLocalSlots(0, m_template.m_inArgsSize);
+      
+        if(null != m_paramElems)
+        {
+          int currentNode = xctxt.getCurrentNode();
+          vars.setStackFrame(thisframe);
+          int size = m_paramElems.length;
+          
+          for (int i = 0; i < size; i++) 
+          {
+            ElemWithParam ewp = m_paramElems[i];
+            if(ewp.m_index >= 0)
+            {
+              XObject obj = ewp.getValue(transformer, currentNode);
+
+              // Note here that the index for ElemWithParam must have been
+              // statically made relative to the xsl:template being called, 
+              // NOT this xsl:template.
+              vars.setLocalVariable(ewp.m_index, obj, nextFrame);
+            }
+          }
+          vars.setStackFrame(nextFrame);
+        }
+      }
+      
+      SourceLocator savedLocator = xctxt.getSAXLocator();
+
+      try
+      {
+        xctxt.setSAXLocator(m_template);
+
+        // template.executeChildTemplates(transformer, sourceNode, mode, true);
+        transformer.pushElemTemplateElement(m_template);
+        m_template.execute(transformer);
+      }
+      finally
+      {
+        transformer.popElemTemplateElement();
+        xctxt.setSAXLocator(savedLocator);
+        // When we entered this function, the current 
+        // frame buffer (cfb) index in the variable stack may 
+        // have been manually set.  If we just call 
+        // unlink(), however, it will restore the cfb to the 
+        // previous link index from the link stack, rather than 
+        // the manually set cfb.  So, 
+        // the only safe solution is to restore it back 
+        // to the same position it was on entry, since we're 
+        // really not working in a stack context here. (Bug4218)
+        vars.unlink(thisframe);
+      }
+    }
+    else
+    {
+      transformer.getMsgMgr().error(this, XSLTErrorResources.ER_TEMPLATE_NOT_FOUND,
+                                    new Object[]{ m_templateName });  //"Could not find template named: '"+templateName+"'");
+    }
+    
+  }
+  
+  /** Vector of xsl:param elements associated with this element. 
+   *  @serial */
+  protected ElemWithParam[] m_paramElems = null;
+
+  /**
+   * Get the count xsl:param elements associated with this element.
+   * @return The number of xsl:param elements.
+   */
+  public int getParamElemCount()
+  {
+    return (m_paramElems == null) ? 0 : m_paramElems.length;
+  }
+
+  /**
+   * Get a xsl:param element associated with this element.
+   *
+   * @param i Index of element to find
+   *
+   * @return xsl:param element at given index
+   */
+  public ElemWithParam getParamElem(int i)
+  {
+    return m_paramElems[i];
+  }
+
+  /**
+   * Set a xsl:param element associated with this element.
+   *
+   * @param ParamElem xsl:param element to set. 
+   */
+  public void setParamElem(ElemWithParam ParamElem)
+  {
+    if (null == m_paramElems)
+    {
+      m_paramElems = new ElemWithParam[1];
+      m_paramElems[0] = ParamElem;
+    }
+    else
+    {
+      // Expensive 1 at a time growth, but this is done at build time, so 
+      // I think it's OK.
+      int length = m_paramElems.length;
+      ElemWithParam[] ewp = new ElemWithParam[length + 1];
+      System.arraycopy(m_paramElems, 0, ewp, 0, length);
+      m_paramElems = ewp;
+      ewp[length] = ParamElem;
+    }
+  }
+
+  /**
+   * Add a child to the child list.
+   * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
+   * <!ATTLIST xsl:apply-templates
+   *   select %expr; "node()"
+   *   mode %qname; #IMPLIED
+   * >
+   *
+   * @param newChild Child to add to this node's children list
+   *
+   * @return The child that was just added the children list 
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    int type = ((ElemTemplateElement) newChild).getXSLToken();
+
+    if (Constants.ELEMNAME_WITHPARAM == type)
+    {
+      setParamElem((ElemWithParam) newChild);
+    }
+
+    // You still have to append, because this element can
+    // contain a for-each, and other elements.
+    return super.appendChild(newChild);
+  }
+  
+    /**
+     * Call the children visitors.
+     * @param visitor The visitor whose appropriate method will be called.
+     */
+    public void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+    {
+//      if (null != m_paramElems)
+//      {
+//        int size = m_paramElems.length;
+//
+//        for (int i = 0; i < size; i++)
+//        {
+//          ElemWithParam ewp = m_paramElems[i];
+//          ewp.callVisitors(visitor);
+//        }
+//      }
+
+      super.callChildVisitors(visitor, callAttrs);
+    }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemChoose.java b/src/main/java/org/apache/xalan/templates/ElemChoose.java
new file mode 100644
index 0000000..caa44f3
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemChoose.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemChoose.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Implement xsl:choose.
+ * <pre>
+ * <!ELEMENT xsl:choose (xsl:when+, xsl:otherwise?)>
+ * <!ATTLIST xsl:choose %space-att;>
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl:choose">XXX in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemChoose extends ElemTemplateElement
+{
+    static final long serialVersionUID = -3070117361903102033L;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_CHOOSE;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_CHOOSE_STRING;
+  }
+
+  /**
+   * Constructor ElemChoose
+   *
+   */
+  public ElemChoose(){}
+
+  /**
+   * Execute the xsl:choose transformation.
+   *
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {
+
+    boolean found = false;
+
+    for (ElemTemplateElement childElem = getFirstChildElem();
+            childElem != null; childElem = childElem.getNextSiblingElem())
+    {
+      int type = childElem.getXSLToken();
+
+      if (Constants.ELEMNAME_WHEN == type)
+      {
+        found = true;
+
+        ElemWhen when = (ElemWhen) childElem;
+
+        // must be xsl:when
+        XPathContext xctxt = transformer.getXPathContext();
+        int sourceNode = xctxt.getCurrentNode();
+        
+        // System.err.println("\""+when.getTest().getPatternString()+"\"");
+        
+        // if(when.getTest().getPatternString().equals("COLLECTION/icuser/ictimezone/LITERAL='GMT +13:00 Pacific/Tongatapu'"))
+        // 	System.err.println("Found COLLECTION/icuser/ictimezone/LITERAL");
+
+          if (when.getTest().bool(xctxt, sourceNode, when)) {
+              transformer.executeChildTemplates(when, true);
+
+              return;
+          }
+      }
+      else if (Constants.ELEMNAME_OTHERWISE == type)
+      {
+        found = true;
+
+        // xsl:otherwise
+        transformer.executeChildTemplates(childElem, true);
+
+        return;
+      }
+    }
+
+    if (!found)
+      transformer.getMsgMgr().error(
+        this, XSLTErrorResources.ER_CHOOSE_REQUIRES_WHEN);
+  }
+
+  /**
+   * Add a child to the child list.
+   *
+   * @param newChild Child to add to this node's child list
+   *
+   * @return The child that was just added to the child list
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    int type = ((ElemTemplateElement) newChild).getXSLToken();
+
+    switch (type)
+    {
+    case Constants.ELEMNAME_WHEN :
+    case Constants.ELEMNAME_OTHERWISE :
+
+      // TODO: Positional checking
+      break;
+    default :
+      error(XSLTErrorResources.ER_CANNOT_ADD,
+            new Object[]{ newChild.getNodeName(),
+                          this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    }
+
+    return super.appendChild(newChild);
+  }
+  
+  /**
+   * Tell if this element can accept variable declarations.
+   * @return true if the element can accept and process variable declarations.
+   */
+  public boolean canAcceptVariables()
+  {
+  	return false;
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemComment.java b/src/main/java/org/apache/xalan/templates/ElemComment.java
new file mode 100644
index 0000000..49e9311
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemComment.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemComment.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+
+/**
+ * Implement xsl:comment.
+ * <pre>
+ * <!ELEMENT xsl:comment %char-template;>
+ * <!ATTLIST xsl:comment %space-att;>
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Comments">section-Creating-Comments in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemComment extends ElemTemplateElement
+{
+    static final long serialVersionUID = -8813199122875770142L;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_COMMENT;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return This element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_COMMENT_STRING;
+  }
+
+  /**
+   * Execute the xsl:comment transformation 
+   *
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+    try
+    {
+      // Note the content model is:
+      // <!ENTITY % instructions "
+      // %char-instructions;
+      // | xsl:processing-instruction
+      // | xsl:comment
+      // | xsl:element
+      // | xsl:attribute
+      // ">
+      String data = transformer.transformToString(this);
+
+      transformer.getResultTreeHandler().comment(data);
+    }
+    catch(org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+  }
+
+  /**
+   * Add a child to the child list.
+   *
+   * @param newChild Child to add to this node's child list
+   *
+   * @return Child that was just added to child list
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    int type = ((ElemTemplateElement) newChild).getXSLToken();
+
+    switch (type)
+    {
+
+    // char-instructions 
+    case Constants.ELEMNAME_TEXTLITERALRESULT :
+    case Constants.ELEMNAME_APPLY_TEMPLATES :
+    case Constants.ELEMNAME_APPLY_IMPORTS :
+    case Constants.ELEMNAME_CALLTEMPLATE :
+    case Constants.ELEMNAME_FOREACH :
+    case Constants.ELEMNAME_VALUEOF :
+    case Constants.ELEMNAME_COPY_OF :
+    case Constants.ELEMNAME_NUMBER :
+    case Constants.ELEMNAME_CHOOSE :
+    case Constants.ELEMNAME_IF :
+    case Constants.ELEMNAME_TEXT :
+    case Constants.ELEMNAME_COPY :
+    case Constants.ELEMNAME_VARIABLE :
+    case Constants.ELEMNAME_MESSAGE :
+
+      // instructions 
+      // case Constants.ELEMNAME_PI:
+      // case Constants.ELEMNAME_COMMENT:
+      // case Constants.ELEMNAME_ELEMENT:
+      // case Constants.ELEMNAME_ATTRIBUTE:
+      break;
+    default :
+      error(XSLTErrorResources.ER_CANNOT_ADD,
+            new Object[]{ newChild.getNodeName(),
+                          this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    }
+
+    return super.appendChild(newChild);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemCopy.java b/src/main/java/org/apache/xalan/templates/ElemCopy.java
new file mode 100644
index 0000000..344c13d
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemCopy.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemCopy.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.ClonerToResultTree;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+import org.apache.xalan.serialize.SerializerUtils;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xpath.XPathContext;
+
+/**
+ * Implement xsl:copy.
+ * <pre>
+ * <!ELEMENT xsl:copy %template;>
+ * <!ATTLIST xsl:copy
+ *   %space-att;
+ *   use-attribute-sets %qnames; #IMPLIED
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#copying">copying in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemCopy extends ElemUse
+{
+    static final long serialVersionUID = 5478580783896941384L;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element 
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_COPY;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return This element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_COPY_STRING;
+  }
+
+  /**
+   * The xsl:copy element provides an easy way of copying the current node.
+   * Executing this function creates a copy of the current node into the
+   * result tree.
+   * <p>The namespace nodes of the current node are automatically
+   * copied as well, but the attributes and children of the node are not
+   * automatically copied. The content of the xsl:copy element is a
+   * template for the attributes and children of the created node;
+   * the content is instantiated only for nodes of types that can have
+   * attributes or children (i.e. root nodes and element nodes).</p>
+   * <p>The root node is treated specially because the root node of the
+   * result tree is created implicitly. When the current node is the
+   * root node, xsl:copy will not create a root node, but will just use
+   * the content template.</p>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+                XPathContext xctxt = transformer.getXPathContext();
+      
+    try
+    {
+      int sourceNode = xctxt.getCurrentNode();
+      xctxt.pushCurrentNode(sourceNode);
+      DTM dtm = xctxt.getDTM(sourceNode);
+      short nodeType = dtm.getNodeType(sourceNode);
+
+      if ((DTM.DOCUMENT_NODE != nodeType) && (DTM.DOCUMENT_FRAGMENT_NODE != nodeType))
+      {
+        SerializationHandler rthandler = transformer.getSerializationHandler();
+
+        // TODO: Process the use-attribute-sets stuff
+        ClonerToResultTree.cloneToResultTree(sourceNode, nodeType, dtm, 
+                                             rthandler, false);
+
+        if (DTM.ELEMENT_NODE == nodeType)
+        {
+          super.execute(transformer);
+          SerializerUtils.processNSDecls(rthandler, sourceNode, nodeType, dtm);
+          transformer.executeChildTemplates(this, true);
+          
+          String ns = dtm.getNamespaceURI(sourceNode);
+          String localName = dtm.getLocalName(sourceNode);
+          transformer.getResultTreeHandler().endElement(ns, localName,
+                                                        dtm.getNodeName(sourceNode));
+        }
+      }
+      else
+      {
+        super.execute(transformer);
+        transformer.executeChildTemplates(this, true);
+      }
+    }
+    catch(org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemCopyOf.java b/src/main/java/org/apache/xalan/templates/ElemCopyOf.java
new file mode 100644
index 0000000..8423992
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemCopyOf.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemCopyOf.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xalan.transformer.TreeWalker2Result;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.ref.DTMTreeWalker;
+import org.apache.xalan.serialize.SerializerUtils;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Implement xsl:copy-of.
+ * <pre>
+ * <!ELEMENT xsl:copy-of EMPTY>
+ * <!ATTLIST xsl:copy-of select %expr; #REQUIRED>
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#copy-of">copy-of in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemCopyOf extends ElemTemplateElement
+{
+    static final long serialVersionUID = -7433828829497411127L;
+
+  /**
+   * The required select attribute contains an expression.
+   * @serial
+   */
+  public XPath m_selectExpression = null;
+
+  /**
+   * Set the "select" attribute.
+   * The required select attribute contains an expression.
+   *
+   * @param expr Expression for select attribute 
+   */
+  public void setSelect(XPath expr)
+  {
+    m_selectExpression = expr;
+  }
+
+  /**
+   * Get the "select" attribute.
+   * The required select attribute contains an expression.
+   *
+   * @return Expression for select attribute 
+   */
+  public XPath getSelect()
+  {
+    return m_selectExpression;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    m_selectExpression.fixupVariables(cstate.getVariableNames(), cstate.getGlobalsSize());
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_COPY_OF;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_COPY_OF_STRING;
+  }
+
+  /**
+   * The xsl:copy-of element can be used to insert a result tree
+   * fragment into the result tree, without first converting it to
+   * a string as xsl:value-of does (see [7.6.1 Generating Text with
+   * xsl:value-of]).
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+    try
+    {
+      XPathContext xctxt = transformer.getXPathContext();
+      int sourceNode = xctxt.getCurrentNode();
+      XObject value = m_selectExpression.execute(xctxt, sourceNode, this);
+
+      SerializationHandler handler = transformer.getSerializationHandler();
+
+      if (null != value)
+                        {
+        int type = value.getType();
+        String s;
+
+        switch (type)
+        {
+        case XObject.CLASS_BOOLEAN :
+        case XObject.CLASS_NUMBER :
+        case XObject.CLASS_STRING :
+          s = value.str();
+
+          handler.characters(s.toCharArray(), 0, s.length());
+          break;
+        case XObject.CLASS_NODESET :
+
+          // System.out.println(value);
+          DTMIterator nl = value.iter();
+
+          // Copy the tree.
+          DTMTreeWalker tw = new TreeWalker2Result(transformer, handler);
+          int pos;
+
+          while (DTM.NULL != (pos = nl.nextNode()))
+          {
+            DTM dtm = xctxt.getDTMManager().getDTM(pos);
+            short t = dtm.getNodeType(pos);
+
+            // If we just copy the whole document, a startDoc and endDoc get 
+            // generated, so we need to only walk the child nodes.
+            if (t == DTM.DOCUMENT_NODE)
+            {
+              for (int child = dtm.getFirstChild(pos); child != DTM.NULL;
+                   child = dtm.getNextSibling(child))
+              {
+                tw.traverse(child);
+              }
+            }
+            else if (t == DTM.ATTRIBUTE_NODE)
+            {
+              SerializerUtils.addAttribute(handler, pos);
+            }
+            else
+            {
+              tw.traverse(pos);
+            }
+          }
+          // nl.detach();
+          break;
+        case XObject.CLASS_RTREEFRAG :
+          SerializerUtils.outputResultTreeFragment(
+            handler, value, transformer.getXPathContext());
+          break;
+        default :
+          
+          s = value.str();
+
+          handler.characters(s.toCharArray(), 0, s.length());
+          break;
+        }
+      }
+                        
+      // I don't think we want this.  -sb
+      //  if (transformer.getDebug())
+      //  transformer.getTraceManager().fireSelectedEvent(sourceNode, this,
+      //  "endSelect", m_selectExpression, value);
+
+    }
+    catch(org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+
+  }
+
+  /**
+   * Add a child to the child list.
+   *
+   * @param newChild Child to add to this node's child list
+   *
+   * @return Child just added to child list
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    error(XSLTErrorResources.ER_CANNOT_ADD,
+          new Object[]{ newChild.getNodeName(),
+                        this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    return null;
+  }
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+  {
+  	if(callAttrs)
+  		m_selectExpression.getExpression().callVisitors(m_selectExpression, visitor);
+    super.callChildVisitors(visitor, callAttrs);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemElement.java b/src/main/java/org/apache/xalan/templates/ElemElement.java
new file mode 100644
index 0000000..3f13eb4
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemElement.java
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemElement.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.XML11Char;
+import org.apache.xpath.XPathContext;
+import org.xml.sax.SAXException;
+
+/**
+ * Implement xsl:element
+ * <pre>
+ * <!ELEMENT xsl:element %template;>
+ * <!ATTLIST xsl:element
+ *   name %avt; #REQUIRED
+ *   namespace %avt; #IMPLIED
+ *   use-attribute-sets %qnames; #IMPLIED
+ *   %space-att;
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Elements-with-xsl:element">XXX in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemElement extends ElemUse
+{
+    static final long serialVersionUID = -324619535592435183L;
+
+  /**
+   * The name attribute is interpreted as an attribute value template.
+   * It is an error if the string that results from instantiating the
+   * attribute value template is not a QName.
+   * @serial
+   */
+  protected AVT m_name_avt = null;
+
+  /**
+   * Set the "name" attribute.
+   * The name attribute is interpreted as an attribute value template.
+   * It is an error if the string that results from instantiating the
+   * attribute value template is not a QName.
+   *
+   * @param v Name attribute to set for this element
+   */
+  public void setName(AVT v)
+  {
+    m_name_avt = v;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * The name attribute is interpreted as an attribute value template.
+   * It is an error if the string that results from instantiating the
+   * attribute value template is not a QName.
+   *
+   * @return Name attribute for this element
+   */
+  public AVT getName()
+  {
+    return m_name_avt;
+  }
+
+  /**
+   * If the namespace attribute is present, then it also is interpreted
+   * as an attribute value template. The string that results from
+   * instantiating the attribute value template should be a URI reference.
+   * It is not an error if the string is not a syntactically legal URI reference.
+   * @serial
+   */
+  protected AVT m_namespace_avt = null;
+
+  /**
+   * Set the "namespace" attribute.
+   * If the namespace attribute is present, then it also is interpreted
+   * as an attribute value template. The string that results from
+   * instantiating the attribute value template should be a URI reference.
+   * It is not an error if the string is not a syntactically legal URI reference.
+   *
+   * @param v NameSpace attribute to set for this element
+   */
+  public void setNamespace(AVT v)
+  {
+    m_namespace_avt = v;
+  }
+
+  /**
+   * Get the "namespace" attribute.
+   * If the namespace attribute is present, then it also is interpreted
+   * as an attribute value template. The string that results from
+   * instantiating the attribute value template should be a URI reference.
+   * It is not an error if the string is not a syntactically legal URI reference.
+   *
+   * @return Namespace attribute for this element
+   */
+  public AVT getNamespace()
+  {
+    return m_namespace_avt;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    java.util.Vector vnames = cstate.getVariableNames();
+    if(null != m_name_avt)
+      m_name_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_namespace_avt)
+      m_namespace_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+  }
+
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_ELEMENT;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return This element's name 
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_ELEMENT_STRING;
+  }
+   
+  /**
+   * Resolve the namespace into a prefix.  Meant to be
+   * overidded by elemAttribute if this class is derived.
+   *
+   * @param rhandler The current result tree handler.
+   * @param prefix The probable prefix if already known.
+   * @param nodeNamespace  The namespace.
+   *
+   * @return The prefix to be used.
+   */
+  protected String resolvePrefix(SerializationHandler rhandler,
+                                 String prefix, String nodeNamespace)
+    throws TransformerException
+  {
+
+//    if (null != prefix && prefix.length() == 0)
+//    {
+//      String foundPrefix = rhandler.getPrefix(nodeNamespace);
+//
+//      // System.out.println("nsPrefix: "+nsPrefix);           
+//      if (null == foundPrefix)
+//        foundPrefix = "";
+//    }
+    return prefix;
+  }
+    
+  /**
+   * Create an element in the result tree.
+   * The xsl:element element allows an element to be created with a
+   * computed name. The expanded-name of the element to be created
+   * is specified by a required name attribute and an optional namespace
+   * attribute. The content of the xsl:element element is a template
+   * for the attributes and children of the created element.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+ 	SerializationHandler rhandler = transformer.getSerializationHandler();
+    XPathContext xctxt = transformer.getXPathContext();
+    int sourceNode = xctxt.getCurrentNode();
+    
+    
+    String nodeName = m_name_avt == null ? null : m_name_avt.evaluate(xctxt, sourceNode, this);
+
+    String prefix = null;
+    String nodeNamespace = "";
+
+    // Only validate if an AVT was used.
+    if ((nodeName != null) && (!m_name_avt.isSimple()) && (!XML11Char.isXML11ValidQName(nodeName)))
+    {
+      transformer.getMsgMgr().warn(
+        this, XSLTErrorResources.WG_ILLEGAL_ATTRIBUTE_VALUE,
+        new Object[]{ Constants.ATTRNAME_NAME, nodeName });
+
+      nodeName = null;
+    }
+
+    else if (nodeName != null)
+    {
+      prefix = QName.getPrefixPart(nodeName);
+
+      if (null != m_namespace_avt)
+      {
+        nodeNamespace = m_namespace_avt.evaluate(xctxt, sourceNode, this);
+        if (null == nodeNamespace || 
+            (prefix != null && prefix.length()>0 && nodeNamespace.length()== 0) )
+          transformer.getMsgMgr().error(
+              this, XSLTErrorResources.ER_NULL_URI_NAMESPACE);
+        else
+        {
+        // Determine the actual prefix that we will use for this nodeNamespace
+
+        prefix = resolvePrefix(rhandler, prefix, nodeNamespace);
+        if (null == prefix)
+          prefix = "";
+
+        if (prefix.length() > 0)
+          nodeName = (prefix + ":" + QName.getLocalPart(nodeName));
+        else
+          nodeName = QName.getLocalPart(nodeName);
+        }
+      }
+
+      // No namespace attribute was supplied. Use the namespace declarations
+      // currently in effect for the xsl:element element.
+      else    
+      {
+        try
+        {
+          // Maybe temporary, until I get this worked out.  test: axes59
+          nodeNamespace = getNamespaceForPrefix(prefix);
+
+          // If we get back a null nodeNamespace, that means that this prefix could
+          // not be found in the table.  This is okay only for a default namespace
+          // that has never been declared.
+
+          if ( (null == nodeNamespace) && (prefix.length() == 0) )
+            nodeNamespace = "";
+          else if (null == nodeNamespace)
+          {
+            transformer.getMsgMgr().warn(
+              this, XSLTErrorResources.WG_COULD_NOT_RESOLVE_PREFIX,
+              new Object[]{ prefix });
+
+            nodeName = null;
+          }
+
+        }
+        catch (Exception ex)
+        {
+          transformer.getMsgMgr().warn(
+            this, XSLTErrorResources.WG_COULD_NOT_RESOLVE_PREFIX,
+            new Object[]{ prefix });
+
+          nodeName = null;
+        }
+      }
+    }
+
+    constructNode(nodeName, prefix, nodeNamespace, transformer);
+  }
+  
+  /**
+   * Construct a node in the result tree.  This method is overloaded by 
+   * xsl:attribute. At this class level, this method creates an element.
+   * If the node is null, we instantiate only the content of the node in accordance
+   * with section 7.1.2 of the XSLT 1.0 Recommendation.
+   *
+   * @param nodeName The name of the node, which may be <code>null</code>.  If <code>null</code>,
+   *                 only the non-attribute children of this node will be processed.
+   * @param prefix The prefix for the namespace, which may be <code>null</code>.
+   *               If not <code>null</code>, this prefix will be mapped and unmapped.
+   * @param nodeNamespace The namespace of the node, which may be not be <code>null</code>.
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  void constructNode(
+          String nodeName, String prefix, String nodeNamespace, TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    boolean shouldAddAttrs;
+
+    try
+    {
+      SerializationHandler rhandler = transformer.getResultTreeHandler();
+
+      if (null == nodeName)
+      {
+        shouldAddAttrs = false;
+      }
+      else
+      {
+        if (null != prefix)
+        {
+          rhandler.startPrefixMapping(prefix, nodeNamespace, true);
+        }
+
+        rhandler.startElement(nodeNamespace, QName.getLocalPart(nodeName),
+                              nodeName);
+
+        super.execute(transformer);
+
+        shouldAddAttrs = true;
+      }
+
+      transformer.executeChildTemplates(this, shouldAddAttrs);
+
+      // Now end the element if name was valid
+      if (null != nodeName)
+      {
+        rhandler.endElement(nodeNamespace, QName.getLocalPart(nodeName),
+                            nodeName);
+        if (null != prefix)
+        {
+          rhandler.endPrefixMapping(prefix);
+        }
+      }
+    }
+    catch (SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+  }
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+  {
+  	if(callAttrs)
+  	{
+  	  if(null != m_name_avt)
+  		m_name_avt.callVisitors(visitor);
+  		
+  	  if(null != m_namespace_avt)
+  		m_namespace_avt.callVisitors(visitor);
+  	}
+  		
+    super.callChildVisitors(visitor, callAttrs);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemEmpty.java b/src/main/java/org/apache/xalan/templates/ElemEmpty.java
new file mode 100644
index 0000000..ea4662f
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemEmpty.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemEmpty.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+
+/**
+ * Simple empty elem to push on the stack when nothing
+ * else got pushed, so that pop() works correctly.
+ * @xsl.usage internal
+ */
+public class ElemEmpty extends ElemTemplateElement
+{
+    static final long serialVersionUID = 7544753713671472252L;
+
+  /**
+   * Constructor ElemEmpty
+   *
+   */
+  public ElemEmpty(){}
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemExsltFuncResult.java b/src/main/java/org/apache/xalan/templates/ElemExsltFuncResult.java
new file mode 100644
index 0000000..32bdbf0
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemExsltFuncResult.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemExsltFuncResult.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Handles the EXSLT result element within an EXSLT function element.
+ */
+public class ElemExsltFuncResult extends ElemVariable
+{
+    static final long serialVersionUID = -3478311949388304563L;
+    /*
+     * To keep the binary compatibility put those three private global 
+     * variables back, although they are never used in this verison
+     */
+    // A flag indicating whether the return result is set
+    private boolean m_isResultSet = false;
+  
+    // The return result
+    private XObject m_result = null;
+  
+    // The frame size of the current caller
+    private int m_callerFrameSize = 0;
+ 
+  /**
+   * Generate the EXSLT function return value, and assign it to the variable
+   * index slot assigned for it in ElemExsltFunction compose().
+   * 
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {    
+    XPathContext context = transformer.getXPathContext();
+
+    // Verify that result has not already been set by another result
+    // element. Recursion is allowed: intermediate results are cleared 
+    // in the owner ElemExsltFunction execute().
+    if (transformer.currentFuncResultSeen()) {
+        throw new TransformerException("An EXSLT function cannot set more than one result!");
+    }
+
+    int sourceNode = context.getCurrentNode();
+
+    // Set the return value;
+    XObject var = getValue(transformer, sourceNode);
+    transformer.popCurrentFuncResult();
+    transformer.pushCurrentFuncResult(var);
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.EXSLT_ELEMNAME_FUNCRESULT;
+  }
+  
+  /**
+   * Return the node name, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   * @return The node name
+   * 
+   */
+   public String getNodeName()
+  {
+    return Constants.EXSLT_ELEMNAME_FUNCRESULT_STRING;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemExsltFunction.java b/src/main/java/org/apache/xalan/templates/ElemExsltFunction.java
new file mode 100644
index 0000000..acf3374
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemExsltFunction.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemExsltFunction.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.extensions.ExtensionNamespaceSupport;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+/**
+ * Implement func:function.
+ * @xsl.usage advanced
+ */
+public class ElemExsltFunction extends ElemTemplate
+{
+    static final long serialVersionUID = 272154954793534771L;
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.EXSLT_ELEMNAME_FUNCTION;
+  }
+
+   /**
+   * Return the node name, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   * @return The node name
+   * 
+   */ 
+  public String getNodeName()
+  {
+    return Constants.EXSLT_ELEMNAME_FUNCTION_STRING;
+  }
+  
+  public void execute(TransformerImpl transformer, XObject[] args)
+          throws TransformerException
+  {
+    XPathContext xctxt = transformer.getXPathContext();
+    VariableStack vars = xctxt.getVarStack();
+    
+    // Increment the frame bottom of the variable stack by the
+    // frame size
+    int thisFrame = vars.getStackFrame();
+    int nextFrame = vars.link(m_frameSize);
+
+    if (m_inArgsSize < args.length) {
+      throw new TransformerException ("function called with too many args");
+    }
+    
+    // Set parameters,
+    // have to clear the section of the stack frame that has params.
+    if (m_inArgsSize > 0) {
+      vars.clearLocalSlots(0, m_inArgsSize);
+
+      if (args.length > 0) {
+        vars.setStackFrame(thisFrame);
+        NodeList children = this.getChildNodes();
+        
+        for (int i = 0; i < args.length; i ++) {
+          Node child = children.item(i);
+          if (children.item(i) instanceof ElemParam) {
+            ElemParam param = (ElemParam)children.item(i);
+            vars.setLocalVariable(param.getIndex(), args[i], nextFrame);
+          }
+        }
+        
+        vars.setStackFrame(nextFrame);
+      }
+    }
+
+    //  Removed ElemTemplate 'push' and 'pop' of RTFContext, in order to avoid losing the RTF context 
+    //  before a value can be returned. ElemExsltFunction operates in the scope of the template that called 
+    //  the function.
+    //  xctxt.pushRTFContext();
+    
+    vars.setStackFrame(nextFrame);
+    transformer.executeChildTemplates(this, true);
+    
+    // Reset the stack frame after the function call
+    vars.unlink(thisFrame);
+
+    // Following ElemTemplate 'pop' removed -- see above.
+    // xctxt.popRTFContext(); 
+    
+  }
+        
+  /**
+   * Called after everything else has been
+   * recomposed, and allows the function to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    
+    // Register the function namespace (if not already registered).
+    String namespace = getName().getNamespace();
+    String handlerClass = sroot.getExtensionHandlerClass();
+    Object[] args ={namespace, sroot};
+    ExtensionNamespaceSupport extNsSpt = 
+                         new ExtensionNamespaceSupport(namespace, handlerClass, args);
+    sroot.getExtensionNamespacesManager().registerExtension(extNsSpt);
+    // Make sure there is a handler for the EXSLT functions namespace
+    // -- for isElementAvailable().    
+    if (!(namespace.equals(Constants.S_EXSLT_FUNCTIONS_URL)))
+    {
+      namespace = Constants.S_EXSLT_FUNCTIONS_URL;
+      args = new Object[]{namespace, sroot};
+      extNsSpt = new ExtensionNamespaceSupport(namespace, handlerClass, args);
+      sroot.getExtensionNamespacesManager().registerExtension(extNsSpt);
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemExtensionCall.java b/src/main/java/org/apache/xalan/templates/ElemExtensionCall.java
new file mode 100644
index 0000000..cdbd069
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemExtensionCall.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemExtensionCall.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.extensions.ExtensionHandler;
+import org.apache.xalan.extensions.ExtensionsTable;
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.XPathContext;
+import org.xml.sax.SAXException;
+
+/**
+ * Implement an extension element.
+ * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemExtensionCall extends ElemLiteralResult
+{
+    static final long serialVersionUID = 3171339708500216920L;
+
+  /** The Namespace URI for this extension call element.
+   *  @serial          */
+  String m_extns;
+
+  /** Language used by extension.
+   *  @serial          */
+  String m_lang;
+
+  /** URL pointing to extension.
+   *  @serial          */
+  String m_srcURL;
+
+  /** Source for script.
+   *  @serial          */
+  String m_scriptSrc;
+
+  /** Declaration for Extension element. 
+   *  @serial          */
+  ElemExtensionDecl m_decl = null;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   *@return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_EXTENSIONCALL;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+
+  // public String getNodeName()
+  // {
+  // TODO: Need prefix.
+  // return localPart;
+  // }
+
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    m_extns = this.getNamespace();   
+    m_decl = getElemExtensionDecl(sroot, m_extns);
+    // Register the extension namespace if the extension does not have
+    // an ElemExtensionDecl ("component").
+    if (m_decl == null)
+      sroot.getExtensionNamespacesManager().registerExtension(m_extns);
+  }
+ 
+  /**
+   * Return the ElemExtensionDecl for this extension element 
+   *
+   *
+   * @param stylesheet Stylesheet root associated with this extension element
+   * @param namespace Namespace associated with this extension element
+   *
+   * @return the ElemExtensionDecl for this extension element. 
+   */
+  private ElemExtensionDecl getElemExtensionDecl(StylesheetRoot stylesheet,
+          String namespace)
+  {
+
+    ElemExtensionDecl decl = null;
+    int n = stylesheet.getGlobalImportCount();
+
+    for (int i = 0; i < n; i++)
+    {
+      Stylesheet imported = stylesheet.getGlobalImport(i);
+
+      for (ElemTemplateElement child = imported.getFirstChildElem();
+              child != null; child = child.getNextSiblingElem())
+      {
+        if (Constants.ELEMNAME_EXTENSIONDECL == child.getXSLToken())
+        {
+          decl = (ElemExtensionDecl) child;
+
+          String prefix = decl.getPrefix();
+          String declNamespace = child.getNamespaceForPrefix(prefix);
+
+          if (namespace.equals(declNamespace))
+          {
+            return decl;
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+  
+  /**
+   * Execute the fallbacks when an extension is not available.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  private void executeFallbacks(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+    for (ElemTemplateElement child = m_firstChild; child != null;
+             child = child.m_nextSibling)
+    {
+      if (child.getXSLToken() == Constants.ELEMNAME_FALLBACK)
+      {
+        try
+        {
+          transformer.pushElemTemplateElement(child);
+          ((ElemFallback) child).executeFallback(transformer);
+        }
+        finally
+        {
+          transformer.popElemTemplateElement();
+        }
+      }
+    }
+
+  }
+  
+  /**
+   * Return true if this extension element has a <xsl:fallback> child element.
+   *
+   * @return true if this extension element has a <xsl:fallback> child element.
+   */
+  private boolean hasFallbackChildren()
+  {
+    for (ElemTemplateElement child = m_firstChild; child != null;
+             child = child.m_nextSibling)
+    {
+      if (child.getXSLToken() == Constants.ELEMNAME_FALLBACK)
+        return true;
+    }
+    
+    return false;
+  }
+
+
+  /**
+   * Execute an extension.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer)
+            throws TransformerException
+  {
+    if (transformer.getStylesheet().isSecureProcessing())
+      throw new TransformerException(
+        XSLMessages.createMessage(
+          XSLTErrorResources.ER_EXTENSION_ELEMENT_NOT_ALLOWED_IN_SECURE_PROCESSING,
+          new Object[] {getRawName()}));
+          
+    try
+    {
+      transformer.getResultTreeHandler().flushPending();
+
+      ExtensionsTable etable = transformer.getExtensionsTable();
+      ExtensionHandler nsh = etable.get(m_extns);
+
+      if (null == nsh)
+      {
+        if (hasFallbackChildren())
+        {
+          executeFallbacks(transformer);
+        }
+        else
+        {
+	  TransformerException te = new TransformerException(XSLMessages.createMessage(
+	  	XSLTErrorResources.ER_CALL_TO_EXT_FAILED, new Object[]{getNodeName()}));
+	  transformer.getErrorListener().fatalError(te);
+        }
+        
+        return;
+      }
+
+      try
+      {
+        nsh.processElement(this.getLocalName(), this, transformer,
+                           getStylesheet(), this);
+      }
+      catch (Exception e)
+      {
+
+	if (hasFallbackChildren())
+	  executeFallbacks(transformer);
+	else
+	{
+          if(e instanceof TransformerException)
+          {
+            TransformerException te = (TransformerException)e;
+            if(null == te.getLocator())
+              te.setLocator(this);
+            
+            transformer.getErrorListener().fatalError(te);            
+          }
+          else if (e instanceof RuntimeException)
+          {
+            transformer.getErrorListener().fatalError(new TransformerException(e));
+          }
+          else
+          {
+            transformer.getErrorListener().warning(new TransformerException(e));
+          }
+        }
+      }
+    }
+    catch(TransformerException e)
+    {
+      transformer.getErrorListener().fatalError(e);
+    }
+    catch(SAXException se) {
+      throw new TransformerException(se);
+    }
+  }
+
+  /**
+   * Return the value of the attribute interpreted as an Attribute
+   * Value Template (in other words, you can use curly expressions
+   * such as href="http://{website}".
+   *
+   * @param rawName Raw name of the attribute to get
+   * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @return the value of the attribute
+   *
+   * @throws TransformerException
+   */
+  public String getAttribute(
+          String rawName, org.w3c.dom.Node sourceNode, TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    AVT avt = getLiteralResultAttribute(rawName);
+
+    if ((null != avt) && avt.getRawName().equals(rawName))
+    {
+      XPathContext xctxt = transformer.getXPathContext();
+
+      return avt.evaluate(xctxt, 
+            xctxt.getDTMHandleFromNode(sourceNode), 
+            this);
+    }
+
+    return null;
+  }
+  
+  /**
+   * Accept a visitor and call the appropriate method 
+   * for this class.
+   * 
+   * @param visitor The visitor whose appropriate method will be called.
+   * @return true if the children of the object should be visited.
+   */
+  protected boolean accept(XSLTVisitor visitor)
+  {
+  	return visitor.visitExtensionElement(this);
+  }
+
+  
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemExtensionDecl.java b/src/main/java/org/apache/xalan/templates/ElemExtensionDecl.java
new file mode 100644
index 0000000..0e09bfd
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemExtensionDecl.java
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemExtensionDecl.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.extensions.ExtensionNamespaceSupport;
+import org.apache.xalan.extensions.ExtensionNamespacesManager;
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.StringVector;
+
+/**
+ * Implement the declaration of an extension element 
+ * @xsl.usage internal
+ */
+public class ElemExtensionDecl extends ElemTemplateElement
+{
+    static final long serialVersionUID = -4692738885172766789L;
+
+  /**
+   * Constructor ElemExtensionDecl
+   *
+   */
+  public ElemExtensionDecl()
+  {
+
+    // System.out.println("ElemExtensionDecl ctor");
+  }
+
+  /** Prefix string for this extension element.
+   *  @serial         */
+  private String m_prefix = null;
+
+  /**
+   * Set the prefix for this extension element  
+   *
+   *
+   * @param v Prefix to set for this extension element
+   */
+  public void setPrefix(String v)
+  {
+    m_prefix = v;
+  }
+
+  /**
+   * Get the prefix for this extension element
+   *
+   *
+   * @return Prefix for this extension element
+   */
+  public String getPrefix()
+  {
+    return m_prefix;
+  }
+
+  /** StringVector holding the names of functions defined in this extension.
+   *  @serial     */
+  private StringVector m_functions = new StringVector();
+
+  /**
+   * Set the names of functions defined in this extension  
+   *
+   *
+   * @param v StringVector holding the names of functions defined in this extension
+   */
+  public void setFunctions(StringVector v)
+  {
+    m_functions = v;
+  }
+
+  /**
+   * Get the names of functions defined in this extension
+   *
+   *
+   * @return StringVector holding the names of functions defined in this extension
+   */
+  public StringVector getFunctions()
+  {
+    return m_functions;
+  }
+
+  /**
+   * Get a function at a given index in this extension element 
+   *
+   *
+   * @param i Index of function to get
+   *
+   * @return Name of Function at given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public String getFunction(int i) throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_functions)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (String) m_functions.elementAt(i);
+  }
+
+  /**
+   * Get count of functions defined in this extension element
+   *
+   *
+   * @return count of functions defined in this extension element
+   */
+  public int getFunctionCount()
+  {
+    return (null != m_functions) ? m_functions.size() : 0;
+  }
+
+  /** StringVector of elements defined in this extension.
+   *  @serial         */
+  private StringVector m_elements = null;
+
+  /**
+   * Set StringVector of elements for this extension
+   *
+   *
+   * @param v StringVector of elements to set
+   */
+  public void setElements(StringVector v)
+  {
+    m_elements = v;
+  }
+
+  /**
+   * Get StringVector of elements defined for this extension  
+   *
+   *
+   * @return StringVector of elements defined for this extension
+   */
+  public StringVector getElements()
+  {
+    return m_elements;
+  }
+
+  /**
+   * Get the element at the given index
+   *
+   *
+   * @param i Index of element to get
+   *
+   * @return The element at the given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public String getElement(int i) throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_elements)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (String) m_elements.elementAt(i);
+  }
+
+  /**
+   * Return the count of elements defined for this extension element 
+   *
+   *
+   * @return the count of elements defined for this extension element
+   */
+  public int getElementCount()
+  {
+    return (null != m_elements) ? m_elements.size() : 0;
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_EXTENSIONDECL;
+  }
+  
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    String prefix = getPrefix();
+    String declNamespace = getNamespaceForPrefix(prefix);
+    String lang = null;
+    String srcURL = null;
+    String scriptSrc = null;
+    if (null == declNamespace)
+      throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_NAMESPACE_DECL, new Object[]{prefix})); 
+      //"Prefix " + prefix does not have a corresponding namespace declaration");
+    for (ElemTemplateElement child = getFirstChildElem(); child != null;
+          child = child.getNextSiblingElem())
+    {
+      if (Constants.ELEMNAME_EXTENSIONSCRIPT == child.getXSLToken())
+      {
+        ElemExtensionScript sdecl = (ElemExtensionScript) child;
+        lang = sdecl.getLang();
+        srcURL = sdecl.getSrc();
+        ElemTemplateElement childOfSDecl = sdecl.getFirstChildElem();
+        if (null != childOfSDecl)
+        {
+          if (Constants.ELEMNAME_TEXTLITERALRESULT
+                  == childOfSDecl.getXSLToken())
+          {
+            ElemTextLiteral tl = (ElemTextLiteral) childOfSDecl;
+            char[] chars = tl.getChars();
+            scriptSrc = new String(chars);
+            if (scriptSrc.trim().length() == 0)
+              scriptSrc = null;
+          }
+        }
+      }
+    }
+    if (null == lang)
+      lang = "javaclass";
+    if (lang.equals("javaclass") && (scriptSrc != null))
+        throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_ELEM_CONTENT_NOT_ALLOWED, new Object[]{scriptSrc})); 
+        //"Element content not allowed for lang=javaclass " + scriptSrc);
+
+    // Register the extension namespace if it has not already been registered.
+    ExtensionNamespaceSupport extNsSpt = null;
+    ExtensionNamespacesManager extNsMgr = sroot.getExtensionNamespacesManager();
+    if (extNsMgr.namespaceIndex(declNamespace,
+                                extNsMgr.getExtensions()) == -1)
+    {
+      if (lang.equals("javaclass"))
+      {
+        if (null == srcURL)
+        {
+           extNsSpt = extNsMgr.defineJavaNamespace(declNamespace);
+        }
+        else if (extNsMgr.namespaceIndex(srcURL,
+                                         extNsMgr.getExtensions()) == -1)
+        {
+          extNsSpt = extNsMgr.defineJavaNamespace(declNamespace, srcURL);
+        }
+      }
+      else  // not java
+      {
+        String handler = "org.apache.xalan.extensions.ExtensionHandlerGeneral";
+        Object [] args = {declNamespace, this.m_elements, this.m_functions,
+                          lang, srcURL, scriptSrc, getSystemId()};
+        extNsSpt = new ExtensionNamespaceSupport(declNamespace, handler, args);
+      }
+    }
+    if (extNsSpt != null)
+      extNsMgr.registerExtension(extNsSpt);
+  }
+
+  
+  /**
+   * This function will be called on top-level elements
+   * only, just before the transform begins.
+   *
+   * @param transformer The XSLT TransformerFactory.
+   *
+   * @throws TransformerException
+   */  
+  public void runtimeInit(TransformerImpl transformer) throws TransformerException
+  {
+/*    //System.out.println("ElemExtensionDecl.runtimeInit()");
+    String lang = null;
+    String srcURL = null;
+    String scriptSrc = null;
+    String prefix = getPrefix();
+    String declNamespace = getNamespaceForPrefix(prefix);
+
+    if (null == declNamespace)
+      throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_NAMESPACE_DECL, new Object[]{prefix})); 
+      //"Prefix " + prefix does not have a corresponding namespace declaration");
+
+    for (ElemTemplateElement child = getFirstChildElem(); child != null;
+            child = child.getNextSiblingElem())
+    {
+      if (Constants.ELEMNAME_EXTENSIONSCRIPT == child.getXSLToken())
+      {
+        ElemExtensionScript sdecl = (ElemExtensionScript) child;
+
+        lang = sdecl.getLang();
+        srcURL = sdecl.getSrc();
+
+        ElemTemplateElement childOfSDecl = sdecl.getFirstChildElem();
+
+        if (null != childOfSDecl)
+        {
+          if (Constants.ELEMNAME_TEXTLITERALRESULT
+                  == childOfSDecl.getXSLToken())
+          {
+            ElemTextLiteral tl = (ElemTextLiteral) childOfSDecl;
+            char[] chars = tl.getChars();
+
+            scriptSrc = new String(chars);
+
+            if (scriptSrc.trim().length() == 0)
+              scriptSrc = null;
+          }
+        }
+      }
+    }
+
+    if (null == lang)
+      lang = "javaclass";
+
+    if (lang.equals("javaclass") && (scriptSrc != null))
+      throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_ELEM_CONTENT_NOT_ALLOWED, new Object[]{scriptSrc})); 
+      //"Element content not allowed for lang=javaclass " + scriptSrc);
+    
+    // Instantiate a handler for this extension namespace.
+    ExtensionsTable etable = transformer.getExtensionsTable();    
+    ExtensionHandler nsh = etable.get(declNamespace);
+
+    // If we have no prior ExtensionHandler for this namespace, we need to
+    // create one.
+    // If the script element is for javaclass, this is our special compiled java.
+    // Element content is not supported for this so we throw an exception if
+    // it is provided.  Otherwise, we look up the srcURL to see if we already have
+    // an ExtensionHandler.
+    if (null == nsh)
+    {
+      if (lang.equals("javaclass"))
+      {
+        if (null == srcURL)
+        {
+          nsh = etable.makeJavaNamespace(declNamespace);
+        }
+        else
+        {
+          nsh = etable.get(srcURL);
+
+          if (null == nsh)
+          {
+            nsh = etable.makeJavaNamespace(srcURL);
+          }
+        }
+      }
+      else  // not java
+      {
+        nsh = new ExtensionHandlerGeneral(declNamespace, this.m_elements,
+                                          this.m_functions, lang, srcURL,
+                                          scriptSrc, getSystemId());
+
+        // System.out.println("Adding NS Handler: declNamespace = "+
+        //                   declNamespace+", lang = "+lang+", srcURL = "+
+        //                   srcURL+", scriptSrc="+scriptSrc);
+      }
+
+      etable.addExtensionNamespace(declNamespace, nsh);
+    }*/
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemExtensionScript.java b/src/main/java/org/apache/xalan/templates/ElemExtensionScript.java
new file mode 100644
index 0000000..0233768
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemExtensionScript.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemExtensionScript.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+/**
+ * Implement Script extension element
+ * @xsl.usage internal
+ */
+public class ElemExtensionScript extends ElemTemplateElement
+{
+    static final long serialVersionUID = -6995978265966057744L;
+
+  /**
+   * Constructor ElemExtensionScript
+   *
+   */
+  public ElemExtensionScript()
+  {
+
+    // System.out.println("ElemExtensionScript ctor");
+  }
+
+  /** Language used in extension.
+   *  @serial          */
+  private String m_lang = null;
+
+  /**
+   * Set language used by extension
+   *
+   *
+   * @param v Language used by extension
+   */
+  public void setLang(String v)
+  {
+    m_lang = v;
+  }
+
+  /**
+   * Get language used by extension
+   *
+   *
+   * @return Language used by extension
+   */
+  public String getLang()
+  {
+    return m_lang;
+  }
+
+  /** Extension handler.
+   *  @serial          */
+  private String m_src = null;
+
+  /**
+   * Set Extension handler name for this extension
+   *
+   *
+   * @param v Extension handler name to set
+   */
+  public void setSrc(String v)
+  {
+    m_src = v;
+  }
+
+  /**
+   * Get Extension handler name for this extension
+   *
+   *
+   * @return Extension handler name
+   */
+  public String getSrc()
+  {
+    return m_src;
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element 
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_EXTENSIONSCRIPT;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemFallback.java b/src/main/java/org/apache/xalan/templates/ElemFallback.java
new file mode 100644
index 0000000..914cdf5
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemFallback.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemFallback.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+
+/**
+ * Implement xsl:fallback.
+ * <pre>
+ * <!ELEMENT xsl:fallback %template;>
+ * <!ATTLIST xsl:fallback %space-att;>
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#fallback">fallback in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemFallback extends ElemTemplateElement
+{
+    static final long serialVersionUID = 1782962139867340703L;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_FALLBACK;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The Element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_FALLBACK_STRING;
+  }
+
+  /**
+   * This is the normal call when xsl:fallback is instantiated.
+   * In accordance with the XSLT 1.0 Recommendation, chapter 15,
+   * "Normally, instantiating an xsl:fallback element does nothing."
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+  }
+
+  /**
+   * Execute the fallback elements.  This must be explicitly called to
+   * instantiate the content of an xsl:fallback element.
+   * When an XSLT transformer performs fallback for an instruction
+   * element, if the instruction element has one or more xsl:fallback
+   * children, then the content of each of the xsl:fallback children
+   * must be instantiated in sequence; otherwise, an error must
+   * be signaled. The content of an xsl:fallback element is a template.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void executeFallback(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    int parentElemType = m_parentNode.getXSLToken();
+    if (Constants.ELEMNAME_EXTENSIONCALL == parentElemType 
+        || Constants.ELEMNAME_UNDEFINED == parentElemType)
+    {
+
+      transformer.executeChildTemplates(this, true);
+
+    }
+    else
+    {
+
+      // Should never happen
+      System.out.println(
+        "Error!  parent of xsl:fallback must be an extension or unknown element!");
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemForEach.java b/src/main/java/org/apache/xalan/templates/ElemForEach.java
new file mode 100644
index 0000000..72b3d7a
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemForEach.java
@@ -0,0 +1,494 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemForEach.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.NodeSorter;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.utils.IntStack;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+
+import java.io.ObjectInputStream;
+import java.io.IOException;
+
+/**
+ * Implement xsl:for-each.
+ * <pre>
+ * <!ELEMENT xsl:for-each
+ *  (#PCDATA
+ *   %instructions;
+ *   %result-elements;
+ *   | xsl:sort)
+ * >
+ *
+ * <!ATTLIST xsl:for-each
+ *   select %expr; #REQUIRED
+ *   %space-att;
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#for-each">for-each in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemForEach extends ElemTemplateElement implements ExpressionOwner
+{
+    static final long serialVersionUID = 6018140636363583690L;
+  /** Set true to request some basic status reports */
+  static final boolean DEBUG = false;
+  
+  /**
+   * This is set by an "xalan-doc-cache-off" pi, or the old "xalan:doc-cache-off" pi.
+   * The old form of the PI only works for XML parsers that are not namespace aware.
+   * It tells the engine that
+   * documents created in the location paths executed by this element
+   * will not be reparsed. It's set by StylesheetHandler during
+   * construction. Note that this feature applies _only_ to xsl:for-each
+   * elements in its current incarnation; a more general cache management
+   * solution is desperately needed.
+   */
+  public boolean m_doc_cache_off=false;
+  
+  /**
+   * Construct a element representing xsl:for-each.
+   */
+  public ElemForEach(){}
+
+  /**
+   * The "select" expression.
+   * @serial
+   */
+  protected Expression m_selectExpression = null;
+  
+  
+  /**
+   * Used to fix bug#16889
+   * Store XPath away for later processing.
+   */
+  protected XPath m_xpath = null;  
+
+  /**
+   * Set the "select" attribute.
+   *
+   * @param xpath The XPath expression for the "select" attribute.
+   */
+  public void setSelect(XPath xpath)
+  {
+    m_selectExpression = xpath.getExpression();
+    
+    // The following line is part of the codes added to fix bug#16889
+    // Store xpath which will be needed when firing Selected Event
+    m_xpath = xpath;    
+  }
+
+  /**
+   * Get the "select" attribute.
+   *
+   * @return The XPath expression for the "select" attribute.
+   */
+  public Expression getSelect()
+  {
+    return m_selectExpression;
+  }
+
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   *
+   * NEEDSDOC @param sroot
+   *
+   * @throws TransformerException
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+
+    super.compose(sroot);
+
+    int length = getSortElemCount();
+
+    for (int i = 0; i < length; i++)
+    {
+      getSortElem(i).compose(sroot);
+    }
+
+    java.util.Vector vnames = sroot.getComposeState().getVariableNames();
+
+    if (null != m_selectExpression)
+      m_selectExpression.fixupVariables(
+        vnames, sroot.getComposeState().getGlobalsSize());
+    else
+    {
+      m_selectExpression =
+        getStylesheetRoot().m_selectDefault.getExpression();
+    }
+  }
+  
+  /**
+   * This after the template's children have been composed.
+   */
+  public void endCompose(StylesheetRoot sroot) throws TransformerException
+  {
+    int length = getSortElemCount();
+
+    for (int i = 0; i < length; i++)
+    {
+      getSortElem(i).endCompose(sroot);
+    }
+    
+    super.endCompose(sroot);
+  }
+
+
+  //  /**
+  //   * This function is called after everything else has been
+  //   * recomposed, and allows the template to set remaining
+  //   * values that may be based on some other property that
+  //   * depends on recomposition.
+  //   *
+  //   * @throws TransformerException
+  //   */
+  //  public void compose() throws TransformerException
+  //  {
+  //
+  //    if (null == m_selectExpression)
+  //    {
+  //      m_selectExpression =
+  //        getStylesheetRoot().m_selectDefault.getExpression();
+  //    }
+  //  }
+
+  /**
+   * Vector containing the xsl:sort elements associated with this element.
+   *  @serial
+   */
+  protected Vector m_sortElems = null;
+
+  /**
+   * Get the count xsl:sort elements associated with this element.
+   * @return The number of xsl:sort elements.
+   */
+  public int getSortElemCount()
+  {
+    return (m_sortElems == null) ? 0 : m_sortElems.size();
+  }
+
+  /**
+   * Get a xsl:sort element associated with this element.
+   *
+   * @param i Index of xsl:sort element to get
+   *
+   * @return xsl:sort element at given index
+   */
+  public ElemSort getSortElem(int i)
+  {
+    return (ElemSort) m_sortElems.elementAt(i);
+  }
+
+  /**
+   * Set a xsl:sort element associated with this element.
+   *
+   * @param sortElem xsl:sort element to set
+   */
+  public void setSortElem(ElemSort sortElem)
+  {
+
+    if (null == m_sortElems)
+      m_sortElems = new Vector();
+
+    m_sortElems.addElement(sortElem);
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_FOREACH;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_FOREACH_STRING;
+  }
+
+  /**
+   * Execute the xsl:for-each transformation
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {
+
+    transformer.pushCurrentTemplateRuleIsNull(true);    
+    try
+    {
+      transformSelectedNodes(transformer);
+    }
+    finally
+    {
+      transformer.popCurrentTemplateRuleIsNull();
+    }
+  }
+
+  /**
+   * Get template element associated with this
+   *
+   *
+   * @return template element associated with this (itself)
+   */
+  protected ElemTemplateElement getTemplateMatch()
+  {
+    return this;
+  }
+
+  /**
+   * Sort given nodes
+   *
+   *
+   * @param xctxt The XPath runtime state for the sort.
+   * @param keys Vector of sort keyx
+   * @param sourceNodes Iterator of nodes to sort
+   *
+   * @return iterator of sorted nodes
+   *
+   * @throws TransformerException
+   */
+  public DTMIterator sortNodes(
+          XPathContext xctxt, Vector keys, DTMIterator sourceNodes)
+            throws TransformerException
+  {
+
+    NodeSorter sorter = new NodeSorter(xctxt);
+    sourceNodes.setShouldCacheNodes(true);
+    sourceNodes.runTo(-1);
+    xctxt.pushContextNodeList(sourceNodes);
+
+    try
+    {
+      sorter.sort(sourceNodes, keys, xctxt);
+      sourceNodes.setCurrentPos(0);
+    }
+    finally
+    {
+      xctxt.popContextNodeList();
+    }
+
+    return sourceNodes;
+  }
+
+  /**
+   * Perform a query if needed, and call transformNode for each child.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException Thrown in a variety of circumstances.
+   * @xsl.usage advanced
+   */
+  public void transformSelectedNodes(TransformerImpl transformer)
+          throws TransformerException
+  {
+
+    final XPathContext xctxt = transformer.getXPathContext();
+    final int sourceNode = xctxt.getCurrentNode();
+    DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt,
+            sourceNode);
+
+    try
+    {
+
+      final Vector keys = (m_sortElems == null)
+              ? null
+              : transformer.processSortKeys(this, sourceNode);
+
+      // Sort if we need to.
+      if (null != keys)
+        sourceNodes = sortNodes(xctxt, keys, sourceNodes);
+
+      xctxt.pushCurrentNode(DTM.NULL);
+
+      IntStack currentNodes = xctxt.getCurrentNodeStack();
+
+      xctxt.pushCurrentExpressionNode(DTM.NULL);
+
+      IntStack currentExpressionNodes = xctxt.getCurrentExpressionNodeStack();
+
+      xctxt.pushSAXLocatorNull();
+      xctxt.pushContextNodeList(sourceNodes);
+      transformer.pushElemTemplateElement(null);
+
+      // pushParams(transformer, xctxt);
+      // Should be able to get this from the iterator but there must be a bug.
+      DTM dtm = xctxt.getDTM(sourceNode);
+      int docID = sourceNode & DTMManager.IDENT_DTM_DEFAULT;
+      int child;
+
+      while (DTM.NULL != (child = sourceNodes.nextNode()))
+      {
+        currentNodes.setTop(child);
+        currentExpressionNodes.setTop(child);
+
+        if ((child & DTMManager.IDENT_DTM_DEFAULT) != docID)
+        {
+          dtm = xctxt.getDTM(child);
+          docID = child & DTMManager.IDENT_DTM_DEFAULT;
+        }
+
+        //final int exNodeType = dtm.getExpandedTypeID(child);
+        final int nodeType = dtm.getNodeType(child); 
+
+        // And execute the child templates.
+        // Loop through the children of the template, calling execute on 
+        // each of them.
+        for (ElemTemplateElement t = this.m_firstChild; t != null;
+             t = t.m_nextSibling)
+        {
+          xctxt.setSAXLocator(t);
+          transformer.setCurrentElement(t);
+          t.execute(transformer);
+        }
+
+        // KLUGE: Implement <?xalan:doc_cache_off?>
+	 	// ASSUMPTION: This will be set only when the XPath was indeed
+	 	// a call to the Document() function. Calling it in other
+	 	// situations is likely to fry Xalan.
+	 	//
+	 	// %REVIEW% We need a MUCH cleaner solution -- one that will
+	 	// handle cleaning up after document() and getDTM() in other
+		// contexts. The whole SourceTreeManager mechanism should probably
+	 	// be moved into DTMManager rather than being explicitly invoked in
+	 	// FuncDocument and here.
+	 	if(m_doc_cache_off)
+		{
+	 	  if(DEBUG)
+	 	    System.out.println("JJK***** CACHE RELEASE *****\n"+
+				       "\tdtm="+dtm.getDocumentBaseURI());
+	  	// NOTE: This will work because this is _NOT_ a shared DTM, and thus has
+	  	// only a single Document node. If it could ever be an RTF or other
+	 	// shared DTM, this would require substantial rework.
+	 	  xctxt.getSourceTreeManager().removeDocumentFromCache(dtm.getDocument());
+	 	  xctxt.release(dtm,false);
+	 	}
+      }
+    }
+    finally
+    {
+      xctxt.popSAXLocator();
+      xctxt.popContextNodeList();
+      transformer.popElemTemplateElement();
+      xctxt.popCurrentExpressionNode();
+      xctxt.popCurrentNode();
+      sourceNodes.detach();
+    }
+  }
+
+  /**
+   * Add a child to the child list.
+   * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
+   * <!ATTLIST xsl:apply-templates
+   *   select %expr; "node()"
+   *   mode %qname; #IMPLIED
+   * >
+   *
+   * @param newChild Child to add to child list
+   *
+   * @return Child just added to child list
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    int type = ((ElemTemplateElement) newChild).getXSLToken();
+
+    if (Constants.ELEMNAME_SORT == type)
+    {
+      setSortElem((ElemSort) newChild);
+
+      return newChild;
+    }
+    else
+      return super.appendChild(newChild);
+  }
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  public void callChildVisitors(XSLTVisitor visitor, boolean callAttributes)
+  {
+  	if(callAttributes && (null != m_selectExpression))
+  		m_selectExpression.callVisitors(this, visitor);
+  		
+    int length = getSortElemCount();
+
+    for (int i = 0; i < length; i++)
+    {
+      getSortElem(i).callVisitors(visitor);
+    }
+
+    super.callChildVisitors(visitor, callAttributes);
+  }
+
+  /**
+   * @see ExpressionOwner#getExpression()
+   */
+  public Expression getExpression()
+  {
+    return m_selectExpression;
+  }
+
+  /**
+   * @see ExpressionOwner#setExpression(Expression)
+   */
+  public void setExpression(Expression exp)
+  {
+  	exp.exprSetParent(this);
+  	m_selectExpression = exp;
+  }
+
+  /*
+   * to keep the binary compatibility, assign a default value for newly added
+   * globel varialbe m_xpath during deserialization of an object which was 
+   * serialized using an older version
+   */
+   private void readObject(ObjectInputStream os) throws 
+        IOException, ClassNotFoundException {
+           os.defaultReadObject();
+           m_xpath = null;
+   }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemIf.java b/src/main/java/org/apache/xalan/templates/ElemIf.java
new file mode 100644
index 0000000..27814d9
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemIf.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemIf.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Implement xsl:if.
+ * <pre>
+ * <!ELEMENT xsl:if %template;>
+ * <!ATTLIST xsl:if
+ *   test %expr; #REQUIRED
+ *   %space-att;
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl:if">XXX in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemIf extends ElemTemplateElement
+{
+    static final long serialVersionUID = 2158774632427453022L;
+
+  /**
+   * The xsl:if element must have a test attribute, which specifies an expression.
+   * @serial
+   */
+  private XPath m_test = null;
+
+  /**
+   * Set the "test" attribute.
+   * The xsl:if element must have a test attribute, which specifies an expression.
+   *
+   * @param v test attribute to set
+   */
+  public void setTest(XPath v)
+  {
+    m_test = v;
+  }
+
+  /**
+   * Get the "test" attribute.
+   * The xsl:if element must have a test attribute, which specifies an expression.
+   *
+   * @return the "test" attribute for this element.
+   */
+  public XPath getTest()
+  {
+    return m_test;
+  }
+
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   *
+   * @param sroot The root stylesheet.
+   *
+   * @throws TransformerException
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+
+    super.compose(sroot);
+
+    java.util.Vector vnames = sroot.getComposeState().getVariableNames();
+
+    if (null != m_test)
+      m_test.fixupVariables(vnames, sroot.getComposeState().getGlobalsSize());
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_IF;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return the element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_IF_STRING;
+  }
+
+  /**
+   * Conditionally execute a sub-template.
+   * The expression is evaluated and the resulting object is converted
+   * to a boolean as if by a call to the boolean function. If the result
+   * is true, then the content template is instantiated; otherwise, nothing
+   * is created.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {
+
+    XPathContext xctxt = transformer.getXPathContext();
+    int sourceNode = xctxt.getCurrentNode();
+
+      if (m_test.bool(xctxt, sourceNode, this)) {
+          transformer.executeChildTemplates(this, true);
+      }
+
+  }
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+  {
+  	if(callAttrs)
+  		m_test.getExpression().callVisitors(m_test, visitor);
+    super.callChildVisitors(visitor, callAttrs);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemLiteralResult.java b/src/main/java/org/apache/xalan/templates/ElemLiteralResult.java
new file mode 100644
index 0000000..3fc7db2
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemLiteralResult.java
@@ -0,0 +1,1478 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemLiteralResult.java 476350 2006-11-17 22:53:23Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xml.utils.StringVector;
+import org.apache.xpath.XPathContext;
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.TypeInfo;
+import org.w3c.dom.UserDataHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Implement a Literal Result Element.
+ * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemLiteralResult extends ElemUse
+{
+    static final long serialVersionUID = -8703409074421657260L;
+
+    /** The return value as Empty String. */
+    private static final String EMPTYSTRING = "";
+
+  /**
+   * Tells if this element represents a root element
+   * that is also the stylesheet element.
+   * TODO: This should be a derived class.
+   * @serial
+   */
+  private boolean isLiteralResultAsStylesheet = false;
+
+  /**
+   * Set whether this element represents a root element
+   * that is also the stylesheet element.
+   *
+   *
+   * @param b boolean flag indicating whether this element
+   * represents a root element that is also the stylesheet element.
+   */
+  public void setIsLiteralResultAsStylesheet(boolean b)
+  {
+    isLiteralResultAsStylesheet = b;
+  }
+
+  /**
+   * Return whether this element represents a root element
+   * that is also the stylesheet element.
+   *
+   *
+   * @return boolean flag indicating whether this element
+   * represents a root element that is also the stylesheet element.
+   */
+  public boolean getIsLiteralResultAsStylesheet()
+  {
+    return isLiteralResultAsStylesheet;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    java.util.Vector vnames = cstate.getVariableNames();
+    if (null != m_avts)
+    {
+      int nAttrs = m_avts.size();
+
+      for (int i = (nAttrs - 1); i >= 0; i--)
+      {
+        AVT avt = (AVT) m_avts.get(i);
+        avt.fixupVariables(vnames, cstate.getGlobalsSize());
+      } 
+    }   
+  }
+  
+  /**
+   * The created element node will have the attribute nodes
+   * that were present on the element node in the stylesheet tree,
+   * other than attributes with names in the XSLT namespace.
+   * @serial
+   */
+  private List m_avts = null;
+
+  /** List of attributes with the XSLT namespace.
+   *  @serial */
+  private List m_xslAttr = null;
+
+  /**
+   * Set a literal result attribute (AVTs only).
+   *
+   * @param avt literal result attribute to add (AVT only)
+   */
+  public void addLiteralResultAttribute(AVT avt)
+  {
+
+    if (null == m_avts)
+      m_avts = new ArrayList();
+
+    m_avts.add(avt);
+  }
+
+  /**
+   * Set a literal result attribute (used for xsl attributes).
+   *
+   * @param att literal result attribute to add
+   */
+  public void addLiteralResultAttribute(String att)
+  {
+
+    if (null == m_xslAttr)
+      m_xslAttr = new ArrayList();
+
+    m_xslAttr.add(att);
+  }
+  
+  /**
+   * Set the "xml:space" attribute.
+   * A text node is preserved if an ancestor element of the text node
+   * has an xml:space attribute with a value of preserve, and
+   * no closer ancestor element has xml:space with a value of default.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text in XSLT Specification</a>
+   *
+   * @param avt  Enumerated value, either Constants.ATTRVAL_PRESERVE 
+   * or Constants.ATTRVAL_STRIP.
+   */
+  public void setXmlSpace(AVT avt)
+  {
+    // This function is a bit-o-hack, I guess...
+    addLiteralResultAttribute(avt);
+    String val = avt.getSimpleString();
+    if(val.equals("default"))
+    {
+      super.setXmlSpace(Constants.ATTRVAL_STRIP);
+    }
+    else if(val.equals("preserve"))
+    {
+      super.setXmlSpace(Constants.ATTRVAL_PRESERVE);
+    }
+    // else maybe it's a real AVT, so we can't resolve it at this time.
+  }
+
+  /**
+   * Get a literal result attribute by name.
+   *
+   * @param namespaceURI Namespace URI of attribute node to get
+   * @param localName Local part of qualified name of attribute node to get
+   *
+   * @return literal result attribute (AVT)
+   */
+  public AVT getLiteralResultAttributeNS(String namespaceURI, String localName)
+  {
+
+    if (null != m_avts)
+    {
+      int nAttrs = m_avts.size();
+
+      for (int i = (nAttrs - 1); i >= 0; i--)
+      {
+        AVT avt = (AVT) m_avts.get(i);
+
+        if (avt.getName().equals(localName) && 
+                avt.getURI().equals(namespaceURI))
+        {
+          return avt;
+        }
+      }  // end for
+    }
+
+    return null;
+  }
+
+  /**
+   * Return the raw value of the attribute.
+   *
+   * @param namespaceURI Namespace URI of attribute node to get
+   * @param localName Local part of qualified name of attribute node to get
+   *
+   * @return The Attr value as a string, or the empty string if that attribute 
+   * does not have a specified or default value
+   */
+  public String getAttributeNS(String namespaceURI, String localName)
+  {
+
+    AVT avt = getLiteralResultAttributeNS(namespaceURI, localName);
+
+    if ((null != avt))
+    {
+      return avt.getSimpleString();
+    }
+
+    return EMPTYSTRING;
+  }
+
+  /**
+   * Get a literal result attribute by name. The name is namespaceURI:localname  
+   * if namespace is not null.
+   *
+   * @param name Name of literal result attribute to get
+   *
+   * @return literal result attribute (AVT)
+   */
+  public AVT getLiteralResultAttribute(String name)
+  {
+
+    if (null != m_avts)
+    {
+      int nAttrs = m_avts.size();
+      String namespace = null;
+      for (int i = (nAttrs - 1); i >= 0; i--)
+      {
+        AVT avt = (AVT) m_avts.get(i);
+        namespace = avt.getURI();
+        
+        if ((namespace != null && (!namespace.equals("")) && (namespace 
+                +":"+avt.getName()).equals(name))|| ((namespace == null || 
+                namespace.equals(""))&& avt.getRawName().equals(name)))
+        {
+          return avt;
+        }
+      }  // end for
+    }
+
+    return null;
+  }
+
+  /**
+   * Return the raw value of the attribute.
+   *
+   * @param namespaceURI:localName or localName if the namespaceURI is null of 
+   * the attribute to get
+   *
+   * @return The Attr value as a string, or the empty string if that attribute 
+   * does not have a specified or default value
+   */
+  public String getAttribute(String rawName)
+  {
+
+    AVT avt = getLiteralResultAttribute(rawName);
+
+    if ((null != avt))
+    {
+      return avt.getSimpleString();
+    }
+
+    return EMPTYSTRING;
+  }
+  
+  /**
+   * Get whether or not the passed URL is flagged by
+   * the "extension-element-prefixes" or "exclude-result-prefixes"
+   * properties.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @param prefix non-null reference to prefix that might be excluded.(not currently used)
+   * @param uri reference to namespace that prefix maps to
+   *
+   * @return true if the prefix should normally be excluded.
+   */
+  public boolean containsExcludeResultPrefix(String prefix, String uri)
+  {
+    if (uri == null ||
+                (null == m_excludeResultPrefixes &&
+                 null == m_ExtensionElementURIs)
+                )
+      return super.containsExcludeResultPrefix(prefix, uri);
+
+    if (prefix.length() == 0)
+      prefix = Constants.ATTRVAL_DEFAULT_PREFIX;
+
+    // This loop is ok here because this code only runs during
+    // stylesheet compile time.    
+        if(m_excludeResultPrefixes!=null)
+            for (int i =0; i< m_excludeResultPrefixes.size(); i++)
+            {
+                if (uri.equals(getNamespaceForPrefix(m_excludeResultPrefixes.elementAt(i))))
+                    return true;
+            }    
+        
+        // JJK Bugzilla 1133: Also check locally-scoped extensions
+    if(m_ExtensionElementURIs!=null && m_ExtensionElementURIs.contains(uri))
+       return true;
+
+        return super.containsExcludeResultPrefix(prefix, uri);
+  }
+
+  /**
+   * Augment resolvePrefixTables, resolving the namespace aliases once
+   * the superclass has resolved the tables.
+   *
+   * @throws TransformerException
+   */
+  public void resolvePrefixTables() throws TransformerException
+  {
+
+    super.resolvePrefixTables();
+
+    StylesheetRoot stylesheet = getStylesheetRoot();
+
+    if ((null != m_namespace) && (m_namespace.length() > 0))
+    {
+      NamespaceAlias nsa = stylesheet.getNamespaceAliasComposed(m_namespace);
+
+      if (null != nsa)
+      {
+        m_namespace = nsa.getResultNamespace();
+
+        // String resultPrefix = nsa.getResultPrefix();
+        String resultPrefix = nsa.getStylesheetPrefix();  // As per xsl WG, Mike Kay
+
+        if ((null != resultPrefix) && (resultPrefix.length() > 0))
+          m_rawName = resultPrefix + ":" + m_localName;
+        else
+          m_rawName = m_localName;
+      }
+    }
+
+    if (null != m_avts)
+    {
+      int n = m_avts.size();
+
+      for (int i = 0; i < n; i++)
+      {
+        AVT avt = (AVT) m_avts.get(i);
+
+        // Should this stuff be a method on AVT?
+        String ns = avt.getURI();
+
+        if ((null != ns) && (ns.length() > 0))
+        {
+          NamespaceAlias nsa =
+            stylesheet.getNamespaceAliasComposed(m_namespace); // %REVIEW% ns?
+
+          if (null != nsa)
+          {
+            String namespace = nsa.getResultNamespace();
+
+            // String resultPrefix = nsa.getResultPrefix();
+            String resultPrefix = nsa.getStylesheetPrefix();  // As per XSL WG
+            String rawName = avt.getName();
+
+            if ((null != resultPrefix) && (resultPrefix.length() > 0))
+              rawName = resultPrefix + ":" + rawName;
+
+            avt.setURI(namespace);
+            avt.setRawName(rawName);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Return whether we need to check namespace prefixes
+   * against the exclude result prefixes or extensions lists.
+   * Note that this will create a new prefix table if one
+   * has not been created already.
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  boolean needToCheckExclude()
+  {
+    if (null == m_excludeResultPrefixes && null == getPrefixTable()
+                && m_ExtensionElementURIs==null     // JJK Bugzilla 1133
+                )
+      return false;
+    else
+    {
+
+      // Create a new prefix table if one has not already been created.
+      if (null == getPrefixTable())
+        setPrefixTable(new java.util.ArrayList());
+
+      return true;
+    }
+  }
+
+  /**
+   * The namespace of the element to be created.
+   * @serial
+   */
+  private String m_namespace;
+
+  /**
+   * Set the namespace URI of the result element to be created.
+   * Note that after resolvePrefixTables has been called, this will
+   * return the aliased result namespace, not the original stylesheet
+   * namespace.
+   *
+   * @param ns The Namespace URI, or the empty string if the
+   *        element has no Namespace URI.
+   */
+  public void setNamespace(String ns)
+  {
+    if(null == ns) // defensive, shouldn't have to do this.
+      ns = "";
+    m_namespace = ns;
+  }
+
+  /**
+   * Get the original namespace of the Literal Result Element.
+   * 
+   * %REVIEW% Why isn't this overriding the getNamespaceURI method
+   * rather than introducing a new one?
+   *
+   * @return The Namespace URI, or the empty string if the
+   *        element has no Namespace URI.
+   */
+  public String getNamespace()
+  {
+    return m_namespace;
+  }
+
+  /**
+   * The local name of the element to be created.
+   * @serial
+   */
+  private String m_localName;
+
+  /**
+   * Set the local name of the LRE.
+   *
+   * @param localName The local name (without prefix) of the result element
+   *                  to be created.
+   */
+  public void setLocalName(String localName)
+  {
+    m_localName = localName;
+  }
+
+  /**
+   * Get the local name of the Literal Result Element.
+   * Note that after resolvePrefixTables has been called, this will
+   * return the aliased name prefix, not the original stylesheet
+   * namespace prefix.
+   *
+   * @return The local name (without prefix) of the result element
+   *                  to be created.
+   */
+  public String getLocalName()
+  {
+    return m_localName;
+  }
+
+  /**
+   * The raw name of the element to be created.
+   * @serial
+   */
+  private String m_rawName;
+
+  /**
+   * Set the raw name of the LRE.
+   *
+   * @param rawName The qualified name (with prefix), or the
+   *        empty string if qualified names are not available.
+   */
+  public void setRawName(String rawName)
+  {
+    m_rawName = rawName;
+  }
+
+  /**
+   * Get the raw name of the Literal Result Element.
+   *
+   * @return  The qualified name (with prefix), or the
+   *        empty string if qualified names are not available.
+   */
+  public String getRawName()
+  {
+    return m_rawName;
+  }
+    
+ /**
+   * Get the prefix part of the raw name of the Literal Result Element.
+   *
+   * @return The prefix, or the empty string if noprefix was provided.
+   */
+  public String getPrefix()
+  {
+        int len=m_rawName.length()-m_localName.length()-1;
+    return (len>0)
+            ? m_rawName.substring(0,len)
+            : "";
+  }
+
+
+  /**
+   * The "extension-element-prefixes" property, actually contains URIs.
+   * @serial
+   */
+  private StringVector m_ExtensionElementURIs;
+
+  /**
+   * Set the "extension-element-prefixes" property.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @param v Vector of URIs (not prefixes) to set as the "extension-element-prefixes" property
+   */
+  public void setExtensionElementPrefixes(StringVector v)
+  {
+    m_ExtensionElementURIs = v;
+  }
+
+  /**
+   * @see org.w3c.dom.Node
+   *
+   * @return NamedNodeMap
+   */
+  public NamedNodeMap getAttributes()
+  {
+        return new LiteralElementAttributes();
+  }
+
+  public class LiteralElementAttributes implements NamedNodeMap{
+          private int m_count = -1;
+          
+          /**
+           * Construct a NameNodeMap.
+           *
+           */
+          public LiteralElementAttributes(){         
+          }
+          
+          /**
+           * Return the number of Attributes on this Element
+           *
+           * @return The number of nodes in this map. The range of valid child 
+           * node indices is <code>0</code> to <code>length-1</code> inclusive
+           */
+          public int getLength()
+          {
+            if (m_count == -1)
+            {
+               if (null != m_avts) m_count = m_avts.size();
+               else m_count = 0;
+            }
+            return m_count;
+          }
+
+          /**
+           * Retrieves a node specified by name.
+           * @param name The <code>nodeName</code> of a node to retrieve.
+           * @return A <code>Node</code> (of any type) with the specified
+           *   <code>nodeName</code>, or <code>null</code> if it does not 
+           *   identify any node in this map.
+           */
+          public Node getNamedItem(String name)
+          {
+                if (getLength() == 0) return null;
+                String uri = null;
+                String localName = name; 
+                int index = name.indexOf(":"); 
+                if (-1 != index){
+                         uri = name.substring(0, index);
+                         localName = name.substring(index+1);
+                }
+                Node retNode = null;
+                Iterator eum = m_avts.iterator();
+                while (eum.hasNext()){
+                        AVT avt = (AVT) eum.next();
+                        if (localName.equals(avt.getName()))
+                        {
+                          String nsURI = avt.getURI(); 
+                          if ((uri == null && nsURI == null)
+                            || (uri != null && uri.equals(nsURI)))
+                          {
+                            retNode = new Attribute(avt, ElemLiteralResult.this);
+                            break;
+                          }
+                        }
+                }
+                return retNode;
+          }
+
+          /**
+           * Retrieves a node specified by local name and namespace URI.
+           * @param namespaceURI Namespace URI of attribute node to get
+           * @param localName Local part of qualified name of attribute node to 
+           * get
+           * @return A <code>Node</code> (of any type) with the specified
+           *   <code>nodeName</code>, or <code>null</code> if it does not 
+           *   identify any node in this map.
+           */
+          public Node getNamedItemNS(String namespaceURI, String localName)
+          {
+                  if (getLength() == 0) return null;
+                  Node retNode = null;
+                  Iterator eum = m_avts.iterator();
+                  while (eum.hasNext())
+                  {
+                    AVT avt = (AVT) eum.next();      
+                    if (localName.equals(avt.getName()))
+                    {
+                      String nsURI = avt.getURI(); 
+                      if ((namespaceURI == null && nsURI == null)
+                        || (namespaceURI != null && namespaceURI.equals(nsURI)))
+                      {
+                        retNode = new Attribute(avt, ElemLiteralResult.this);
+                        break;
+                      }
+                    }
+                  }
+                  return retNode;
+          }
+          
+          /**
+           * Returns the <code>index</code>th item in the map. If <code>index
+           * </code> is greater than or equal to the number of nodes in this 
+           * map, this returns <code>null</code>.
+           * @param i The index of the requested item.
+           * @return The node at the <code>index</code>th position in the map, 
+           *   or <code>null</code> if that is not a valid index.
+           */
+          public Node item(int i)
+          {
+                if (getLength() == 0 || i >= m_avts.size()) return null;
+                else return 
+                    new Attribute(((AVT)m_avts.get(i)), 
+                        ElemLiteralResult.this);
+          }
+          
+          /**
+           * @see org.w3c.dom.NamedNodeMap
+           *
+           * @param name of the node to remove
+           * 
+           * @return The node removed from this map if a node with such 
+           * a name exists. 
+           *
+           * @throws DOMException
+           */
+          public Node removeNamedItem(String name) throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+                  return null;
+          }
+          
+          /**
+           * @see org.w3c.dom.NamedNodeMap
+           *
+           * @param namespaceURI Namespace URI of the node to remove
+           * @param localName Local part of qualified name of the node to remove
+           * 
+           * @return The node removed from this map if a node with such a local
+           *  name and namespace URI exists
+           *
+           * @throws DOMException
+           */
+          public Node removeNamedItemNS(String namespaceURI, String localName) 
+                throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+                  return null;
+          } 
+          
+          /**
+           * Unimplemented. See org.w3c.dom.NamedNodeMap
+           *
+           * @param A node to store in this map
+           * 
+           * @return If the new Node replaces an existing node the replaced 
+           * Node is returned, otherwise null is returned
+           *
+           * @throws DOMException
+           */
+          public Node setNamedItem(Node arg) throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+                  return null;
+          }
+          
+          /**
+           * Unimplemented. See org.w3c.dom.NamedNodeMap
+           *
+           * @param A node to store in this map
+           * 
+           * @return If the new Node replaces an existing node the replaced 
+           * Node is returned, otherwise null is returned
+           *
+           * @throws DOMException
+           */
+          public Node setNamedItemNS(Node arg) throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+                  return null;
+          }                                                                         
+  }
+
+  public class Attribute implements Attr{
+          private AVT m_attribute;
+          private Element m_owner = null;
+          /**
+           * Construct a Attr.
+           *
+           */
+          public Attribute(AVT avt, Element elem){
+                m_attribute = avt;
+                m_owner = elem;
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @param newChild New node to append to the list of this node's 
+           * children
+           *
+           *
+           * @throws DOMException
+           */
+          public Node appendChild(Node newChild) throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+                  return null;
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @param deep Flag indicating whether to clone deep 
+           * (clone member variables)
+           *
+           * @return Returns a duplicate of this node
+           */
+          public Node cloneNode(boolean deep)
+          {
+                  return new Attribute(m_attribute, m_owner);
+          }
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return null
+           */
+          public NamedNodeMap getAttributes()
+          {
+            return null;
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return a NodeList containing no nodes. 
+           */
+          public NodeList getChildNodes()
+          {
+                  return new NodeList(){
+                          public int getLength(){
+                                  return 0;
+                          }
+                          public Node item(int index){
+                                  return null;
+                          }
+                  };
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return null
+           */
+          public Node getFirstChild()
+          {
+                  return null;
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return null
+           */
+          public Node getLastChild()
+          {
+                  return null;
+          }
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return the local part of the qualified name of this node
+           */
+          public String getLocalName()
+          {
+                  return m_attribute.getName();
+          }
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return The namespace URI of this node, or null if it is 
+           * unspecified
+           */
+          public String getNamespaceURI()
+          {
+                  String uri = m_attribute.getURI();
+                  return (uri.equals(""))?null:uri;
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return null
+           */
+          public Node getNextSibling()
+          {
+                return null;
+          }
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return The name of the attribute
+           */
+          public String getNodeName()
+          {
+                  String uri = m_attribute.getURI();
+                  String localName = getLocalName();
+                  return (uri.equals(""))?localName:uri+":"+localName;
+          }
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return The node is an Attr
+           */
+          public short getNodeType()
+          {
+                  return ATTRIBUTE_NODE;
+          }
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return The value of the attribute
+           *
+           * @throws DOMException
+           */
+          public String getNodeValue() throws DOMException
+          {
+                  return m_attribute.getSimpleString();
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return null
+           */
+          public Document getOwnerDocument()
+          {
+            return m_owner.getOwnerDocument();
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return the containing element node
+           */
+          public Node getParentNode()
+          {
+                  return m_owner;
+          }
+                    
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return The namespace prefix of this node, or null if it is 
+           * unspecified
+           */
+          public String getPrefix()
+          {
+                  String uri = m_attribute.getURI();
+                  String rawName = m_attribute.getRawName();
+                  return (uri.equals(""))? 
+                        null:rawName.substring(0, rawName.indexOf(":"));
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return null
+           */
+          public Node getPreviousSibling()
+          {
+                  return null;
+          }
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return false
+           */
+          public boolean hasAttributes()
+          {
+                  return false;
+          }
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return false
+           */
+          public boolean hasChildNodes()
+          {
+                  return false;
+          }                    
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @param newChild New child node to insert
+           * @param refChild Insert in front of this child
+           *
+           * @return null
+           *
+           * @throws DOMException
+           */
+          public Node insertBefore(Node newChild, Node refChild) 
+                throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR);
+                  return null;
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @return Returns <code>false</code>
+           * @since DOM Level 2
+           */
+          public boolean isSupported(String feature, String version)
+          {
+            return false;
+          }
+
+          /** @see org.w3c.dom.Node */
+          public void normalize(){}
+          
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @param oldChild Child to be removed
+           *
+           * @return null
+           *
+           * @throws DOMException
+           */
+          public Node removeChild(Node oldChild) throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+                  return null;
+          }         
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @param newChild Replace existing child with this one
+           * @param oldChild Existing child to be replaced
+           *
+           * @return null
+           *
+           * @throws DOMException
+           */
+          public Node replaceChild(Node newChild, Node oldChild) throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+                  return null;
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @param nodeValue Value to set this node to
+           *
+           * @throws DOMException
+           */
+          public void setNodeValue(String nodeValue) throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+          }
+
+          /**
+           * @see org.w3c.dom.Node
+           *
+           * @param prefix Prefix to set for this node
+           *
+           * @throws DOMException
+           */
+          public void setPrefix(String prefix) throws DOMException
+          {
+                  throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                      XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR);
+          }
+                                                                      
+          /**
+           *
+           * @return The name of this attribute
+           */          
+          public String getName(){
+                  return m_attribute.getName();                            
+          }
+
+          /**
+           *
+           * @return The value of this attribute returned as string
+           */          
+          public String getValue(){
+                  return m_attribute.getSimpleString();                            
+          }
+          
+          /**
+           *
+           * @return The Element node this attribute is attached to 
+           * or null if this attribute is not in use
+           */                    
+          public Element getOwnerElement(){
+                  return m_owner;
+          }
+          
+          /**
+           *
+           * @return true
+           */          
+          public boolean getSpecified(){
+                  return true;
+          }
+          
+          /**
+           * @see org.w3c.dom.Attr
+           *
+           * @param value Value to set this node to
+           *
+           * @throws DOMException
+           */
+          public void setValue(String value) throws DOMException
+          {
+            throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
+                XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
+          }
+
+ 	  public TypeInfo getSchemaTypeInfo() { return null; }
+    
+  	  public boolean isId( ) { return false; }
+
+  	  public Object setUserData(String key,
+                                    Object data,
+                                    UserDataHandler handler) {
+        	return getOwnerDocument().setUserData( key, data, handler);
+  	  }
+
+  	  public Object getUserData(String key) {
+        	return getOwnerDocument().getUserData( key);
+  	  } 
+
+  	  public Object getFeature(String feature, String version) {
+        	return isSupported(feature, version) ? this : null;
+   	  }
+          
+          public boolean isEqualNode(Node arg) {
+          	return arg == this;
+          }
+          
+          public String lookupNamespaceURI(String specifiedPrefix) {
+             	return null;
+          }
+          
+          public boolean isDefaultNamespace(String namespaceURI) {
+            	return false;
+          }
+
+	  public String lookupPrefix(String namespaceURI) {
+	    	return null;
+	  }
+	  
+  	  public boolean isSameNode(Node other) {
+        	// we do not use any wrapper so the answer is obvious
+        	return this == other;
+  	  }
+          
+  	  public void setTextContent(String textContent)
+        	throws DOMException {
+        	setNodeValue(textContent);
+  	  }
+
+  	  public String getTextContent() throws DOMException {
+            	return getNodeValue();  // overriden in some subclasses
+   	  }
+
+    	  public short compareDocumentPosition(Node other) throws DOMException {
+            	return 0;
+    	  }
+
+          public String getBaseURI() {
+            	return null;
+    	  }
+  }        
+  
+  /**
+   * Get an "extension-element-prefix" property.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @param i Index of URI ("extension-element-prefix" property) to get
+   *
+   * @return URI at given index ("extension-element-prefix" property)
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public String getExtensionElementPrefix(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_ExtensionElementURIs)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return m_ExtensionElementURIs.elementAt(i);
+  }
+
+  /**
+   * Get the number of "extension-element-prefixes" Strings.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @return the number of "extension-element-prefixes" Strings
+   */
+  public int getExtensionElementPrefixCount()
+  {
+    return (null != m_ExtensionElementURIs)
+           ? m_ExtensionElementURIs.size() : 0;
+  }
+
+  /**
+   * Find out if the given "extension-element-prefix" property is defined.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @param uri The URI to find
+   *
+   * @return True if the given URI is found
+   */
+  public boolean containsExtensionElementURI(String uri)
+  {
+
+    if (null == m_ExtensionElementURIs)
+      return false;
+
+    return m_ExtensionElementURIs.contains(uri);
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_LITERALRESULT;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+
+    // TODO: Need prefix.
+    return m_rawName;
+  }
+
+  /**
+   * The XSLT version as specified by this element.
+   * @serial
+   */
+  private String m_version;
+
+  /**
+   * Set the "version" property.
+   * @see <a href="http://www.w3.org/TR/xslt#forwards">forwards in XSLT Specification</a>
+   *
+   * @param v Version property value to set
+   */
+  public void setVersion(String v)
+  {
+    m_version = v;
+  }
+  
+  /**
+   * Get the "version" property.
+   * @see <a href="http://www.w3.org/TR/xslt#forwards">forwards in XSLT Specification</a>
+   *
+   * @return Version property value
+   */
+  public String getVersion()
+  {
+    return m_version;
+  }
+
+  /**
+   * The "exclude-result-prefixes" property.
+   * @serial
+   */
+  private StringVector m_excludeResultPrefixes;
+
+  /**
+   * Set the "exclude-result-prefixes" property.
+   * The designation of a namespace as an excluded namespace is
+   * effective within the subtree of the stylesheet rooted at
+   * the element bearing the exclude-result-prefixes or
+   * xsl:exclude-result-prefixes attribute; a subtree rooted
+   * at an xsl:stylesheet element does not include any stylesheets
+   * imported or included by children of that xsl:stylesheet element.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @param v vector of prefixes that are resolvable to strings.
+   */
+  public void setExcludeResultPrefixes(StringVector v)
+  {
+    m_excludeResultPrefixes = v;
+  }
+
+  /**
+   * Tell if the result namespace decl should be excluded.  Should be called before
+   * namespace aliasing (I think).
+   *
+   * @param prefix Prefix of namespace to check
+   * @param uri URI of namespace to check
+   *
+   * @return True if the given namespace should be excluded
+   *
+   * @throws TransformerException
+   */
+  private boolean excludeResultNSDecl(String prefix, String uri)
+          throws TransformerException
+  {
+
+    if (null != m_excludeResultPrefixes)
+    {
+      return containsExcludeResultPrefix(prefix, uri);
+    }
+
+    return false;
+  }
+  
+  /**
+   * Copy a Literal Result Element into the Result tree, copy the
+   * non-excluded namespace attributes, copy the attributes not
+   * of the XSLT namespace, and execute the children of the LRE.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+    public void execute(TransformerImpl transformer)
+        throws TransformerException
+    {
+        SerializationHandler rhandler = transformer.getSerializationHandler();
+
+        try
+        {
+
+            // JJK Bugzilla 3464, test namespace85 -- make sure LRE's
+            // namespace is asserted even if default, since xsl:element
+            // may have changed the context.
+            rhandler.startPrefixMapping(getPrefix(), getNamespace());
+
+            // Add namespace declarations.
+            executeNSDecls(transformer);
+            rhandler.startElement(getNamespace(), getLocalName(), getRawName());
+        }
+        catch (SAXException se)
+        {
+            throw new TransformerException(se);
+        }
+
+        /*
+         * If we make it to here we have done a successful startElement()
+         * we will do an endElement() call for balance, no matter what happens
+         * in the middle.  
+         */
+
+        // tException remembers if we had an exception "in the middle"
+        TransformerException tException = null;
+        try
+        {
+
+            // Process any possible attributes from xsl:use-attribute-sets first
+            super.execute(transformer);
+
+            //xsl:version, excludeResultPrefixes???
+            // Process the list of avts next
+            if (null != m_avts)
+            {
+                int nAttrs = m_avts.size();
+
+                for (int i = (nAttrs - 1); i >= 0; i--)
+                {
+                    AVT avt = (AVT) m_avts.get(i);
+                    XPathContext xctxt = transformer.getXPathContext();
+                    int sourceNode = xctxt.getCurrentNode();
+                    String stringedValue =
+                        avt.evaluate(xctxt, sourceNode, this);
+
+                    if (null != stringedValue)
+                    {
+
+                        // Important Note: I'm not going to check for excluded namespace 
+                        // prefixes here.  It seems like it's too expensive, and I'm not 
+                        // even sure this is right.  But I could be wrong, so this needs 
+                        // to be tested against other implementations.
+
+                        rhandler.addAttribute(
+                            avt.getURI(),
+                            avt.getName(),
+                            avt.getRawName(),
+                            "CDATA",
+                            stringedValue, false);
+                    }
+                } // end for
+            }
+
+            // Now process all the elements in this subtree
+            // TODO: Process m_extensionElementPrefixes && m_attributeSetsNames
+            transformer.executeChildTemplates(this, true);
+        }
+        catch (TransformerException te)
+        {
+            // thrown in finally to prevent original exception consumed by subsequent exceptions
+            tException = te;
+        }
+        catch (SAXException se)
+        {
+            tException = new TransformerException(se);
+        }
+
+        try
+        {
+            /* we need to do this endElement() to balance the
+             * successful startElement() call even if 
+             * there was an exception in the middle.
+             * Otherwise an exception in the middle could cause a system to hang.
+             */
+            rhandler.endElement(getNamespace(), getLocalName(), getRawName());
+        }
+        catch (SAXException se)
+        {
+            /* we did call endElement(). If thee was an exception
+             * in the middle throw that one, otherwise if there
+             * was an exception from endElement() throw that one.
+             */
+            if (tException != null)
+                throw tException;
+            else
+                throw new TransformerException(se);
+        }
+        
+        /* If an exception was thrown in the middle but not with startElement() or
+         * or endElement() then its time to let it percolate.
+         */ 
+        if (tException != null)
+            throw tException; 
+        
+        unexecuteNSDecls(transformer);
+
+        // JJK Bugzilla 3464, test namespace85 -- balance explicit start.
+        try
+        {
+            rhandler.endPrefixMapping(getPrefix());
+        }
+        catch (SAXException se)
+        {
+            throw new TransformerException(se);
+        }
+    }
+
+  /**
+   * Compiling templates requires that we be able to list the AVTs
+   * ADDED 9/5/2000 to support compilation experiment
+   *
+   * @return an Enumeration of the literal result attributes associated
+   * with this element.
+   */
+  public Iterator enumerateLiteralResultAttributes()
+  {
+    return (null == m_avts) ? null : m_avts.iterator();
+  }
+  
+    /**
+     * Accept a visitor and call the appropriate method 
+     * for this class.
+     * 
+     * @param visitor The visitor whose appropriate method will be called.
+     * @return true if the children of the object should be visited.
+     */
+    protected boolean accept(XSLTVisitor visitor)
+    {
+      return visitor.visitLiteralResultElement(this);
+    }
+
+    /**
+     * Call the children visitors.
+     * @param visitor The visitor whose appropriate method will be called.
+     */
+    protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+    {
+      if (callAttrs && null != m_avts)
+      {
+        int nAttrs = m_avts.size();
+
+        for (int i = (nAttrs - 1); i >= 0; i--)
+        {
+          AVT avt = (AVT) m_avts.get(i);
+          avt.callVisitors(visitor);
+        }
+      }
+      super.callChildVisitors(visitor, callAttrs);
+    }
+
+    /**
+     * Throw a DOMException
+     *
+     * @param msg key of the error that occured.
+     */
+    public void throwDOMException(short code, String msg)
+    {
+
+      String themsg = XSLMessages.createMessage(msg, null);
+
+      throw new DOMException(code, themsg);
+    }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemMessage.java b/src/main/java/org/apache/xalan/templates/ElemMessage.java
new file mode 100644
index 0000000..1cfdf17
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemMessage.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemMessage.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+
+/**
+ * Implement xsl:message.
+ * <pre>
+ * <!ELEMENT xsl:message %template;>
+ * <!ATTLIST xsl:message
+ *   %space-att;
+ *   terminate (yes|no) "no"
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#message">message in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemMessage extends ElemTemplateElement
+{
+    static final long serialVersionUID = 1530472462155060023L;
+
+  /**
+   * If the terminate attribute has the value yes, then the
+   * XSLT transformer should terminate processing after sending
+   * the message. The default value is no.
+   * @serial
+   */
+  private boolean m_terminate = Constants.ATTRVAL_NO;  // default value 
+
+  /**
+   * Set the "terminate" attribute.
+   * If the terminate attribute has the value yes, then the
+   * XSLT transformer should terminate processing after sending
+   * the message. The default value is no.
+   *
+   * @param v Value to set for "terminate" attribute. 
+   */
+  public void setTerminate(boolean v)
+  {
+    m_terminate = v;
+  }
+
+  /**
+   * Get the "terminate" attribute.
+   * If the terminate attribute has the value yes, then the
+   * XSLT transformer should terminate processing after sending
+   * the message. The default value is no.
+   *
+   * @return value of "terminate" attribute.
+   */
+  public boolean getTerminate()
+  {
+    return m_terminate;
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_MESSAGE;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return name of the element 
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_MESSAGE_STRING;
+  }
+
+  /**
+   * Send a message to diagnostics.
+   * The xsl:message instruction sends a message in a way that
+   * is dependent on the XSLT transformer. The content of the xsl:message
+   * instruction is a template. The xsl:message is instantiated by
+   * instantiating the content to create an XML fragment. This XML
+   * fragment is the content of the message.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    String data = transformer.transformToString(this);
+
+    transformer.getMsgMgr().message(this, data, m_terminate);
+    
+    if(m_terminate)
+      transformer.getErrorListener().fatalError(new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_STYLESHEET_DIRECTED_TERMINATION, null))); //"Stylesheet directed termination"));
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemNumber.java b/src/main/java/org/apache/xalan/templates/ElemNumber.java
new file mode 100644
index 0000000..1335902
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemNumber.java
@@ -0,0 +1,2157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemNumber.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.CountersTable;
+import org.apache.xalan.transformer.DecimalToRoman;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.NodeVector;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.StringBufferPool;
+import org.apache.xml.utils.res.XResourceBundle;
+import org.apache.xml.utils.res.CharArrayWrapper;
+import org.apache.xml.utils.res.IntArrayWrapper;
+import org.apache.xml.utils.res.LongArrayWrapper;
+import org.apache.xml.utils.res.StringArrayWrapper;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+import org.w3c.dom.Node;
+
+import org.xml.sax.SAXException;
+
+// import org.apache.xalan.dtm.*;
+
+/**
+ * Implement xsl:number.
+ * <pre>
+ * <!ELEMENT xsl:number EMPTY>
+ * <!ATTLIST xsl:number
+ *    level (single|multiple|any) "single"
+ *    count %pattern; #IMPLIED
+ *    from %pattern; #IMPLIED
+ *    value %expr; #IMPLIED
+ *    format %avt; '1'
+ *    lang %avt; #IMPLIED
+ *    letter-value %avt; #IMPLIED
+ *    grouping-separator %avt; #IMPLIED
+ *    grouping-size %avt; #IMPLIED
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#number">number in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemNumber extends ElemTemplateElement 
+{
+    static final long serialVersionUID = 8118472298274407610L;
+
+    /**
+     * Chars for converting integers into alpha counts.
+     * @see TransformerImpl#int2alphaCount
+     */
+    private CharArrayWrapper m_alphaCountTable = null;
+    
+    private class MyPrefixResolver implements PrefixResolver {
+        
+        DTM dtm;
+        int handle;
+        boolean handleNullPrefix;
+        
+		/**
+		 * Constructor for MyPrefixResolver.
+		 * @param xpathExpressionContext
+		 */
+		public MyPrefixResolver(Node xpathExpressionContext, DTM dtm, int handle, boolean handleNullPrefix) {
+            this.dtm = dtm;
+            this.handle = handle;
+            this.handleNullPrefix = handleNullPrefix;
+		}
+
+    	/**
+		 * @see PrefixResolver#getNamespaceForPrefix(String, Node)
+		 */
+		public String getNamespaceForPrefix(String prefix) {
+            return dtm.getNamespaceURI(handle);
+		}
+        
+        /**
+         * @see PrefixResolver#getNamespaceForPrefix(String, Node)
+         * this shouldn't get called.
+         */
+        public String getNamespaceForPrefix(String prefix, Node context) {
+            return getNamespaceForPrefix(prefix);
+        }
+
+		/**
+		 * @see PrefixResolver#getBaseIdentifier()
+		 */
+		public String getBaseIdentifier() {
+			return ElemNumber.this.getBaseIdentifier();
+		}
+
+		/**
+		 * @see PrefixResolver#handlesNullPrefixes()
+		 */
+		public boolean handlesNullPrefixes() {
+			return handleNullPrefix;
+		}
+
+}
+    
+  /**
+   * Only nodes are counted that match this pattern.
+   * @serial
+   */
+  private XPath m_countMatchPattern = null;
+
+  /**
+   * Set the "count" attribute.
+   * The count attribute is a pattern that specifies what nodes
+   * should be counted at those levels. If count attribute is not
+   * specified, then it defaults to the pattern that matches any
+   * node with the same node type as the current node and, if the
+   * current node has an expanded-name, with the same expanded-name
+   * as the current node.
+   *
+   * @param v Value to set for "count" attribute. 
+   */
+  public void setCount(XPath v)
+  {
+    m_countMatchPattern = v;
+  }
+
+  /**
+   * Get the "count" attribute.
+   * The count attribute is a pattern that specifies what nodes
+   * should be counted at those levels. If count attribute is not
+   * specified, then it defaults to the pattern that matches any
+   * node with the same node type as the current node and, if the
+   * current node has an expanded-name, with the same expanded-name
+   * as the current node.
+   *
+   * @return Value of "count" attribute.
+   */
+  public XPath getCount()
+  {
+    return m_countMatchPattern;
+  }
+
+  /**
+   * Specifies where to count from.
+   * For level="single" or level="multiple":
+   * Only ancestors that are searched are
+   * those that are descendants of the nearest ancestor that matches
+   * the from pattern.
+   * For level="any:
+   * Only nodes after the first node before the
+   * current node that match the from pattern are considered.
+   * @serial
+   */
+  private XPath m_fromMatchPattern = null;
+
+  /**
+   * Set the "from" attribute. Specifies where to count from.
+   * For level="single" or level="multiple":
+   * Only ancestors that are searched are
+   * those that are descendants of the nearest ancestor that matches
+   * the from pattern.
+   * For level="any:
+   * Only nodes after the first node before the
+   * current node that match the from pattern are considered.
+   *
+   * @param v Value to set for "from" attribute.
+   */
+  public void setFrom(XPath v)
+  {
+    m_fromMatchPattern = v;
+  }
+
+  /**
+   * Get the "from" attribute.
+   * For level="single" or level="multiple":
+   * Only ancestors that are searched are
+   * those that are descendants of the nearest ancestor that matches
+   * the from pattern.
+   * For level="any:
+   * Only nodes after the first node before the
+   * current node that match the from pattern are considered.
+   *
+   * @return Value of "from" attribute.
+   */
+  public XPath getFrom()
+  {
+    return m_fromMatchPattern;
+  }
+
+  /**
+   * When level="single", it goes up to the first node in the ancestor-or-self axis
+   * that matches the count pattern, and constructs a list of length one containing
+   * one plus the number of preceding siblings of that ancestor that match the count
+   * pattern. If there is no such ancestor, it constructs an empty list. If the from
+   * attribute is specified, then the only ancestors that are searched are those
+   * that are descendants of the nearest ancestor that matches the from pattern.
+   * Preceding siblings has the same meaning here as with the preceding-sibling axis.
+   *
+   * When level="multiple", it constructs a list of all ancestors of the current node
+   * in document order followed by the element itself; it then selects from the list
+   * those nodes that match the count pattern; it then maps each node in the list to
+   * one plus the number of preceding siblings of that node that match the count pattern.
+   * If the from attribute is specified, then the only ancestors that are searched are
+   * those that are descendants of the nearest ancestor that matches the from pattern.
+   * Preceding siblings has the same meaning here as with the preceding-sibling axis.
+   *
+   * When level="any", it constructs a list of length one containing the number of
+   * nodes that match the count pattern and belong to the set containing the current
+   * node and all nodes at any level of the document that are before the current node
+   * in document order, excluding any namespace and attribute nodes (in other words
+   * the union of the members of the preceding and ancestor-or-self axes). If the
+   * from attribute is specified, then only nodes after the first node before the
+   * current node that match the from pattern are considered.
+   * @serial
+   */
+  private int m_level = Constants.NUMBERLEVEL_SINGLE;
+
+  /**
+   * Set the "level" attribute.
+   * The level attribute specifies what levels of the source tree should
+   * be considered; it has the values single, multiple or any. The default
+   * is single.
+   *
+   * @param v Value to set for "level" attribute.
+   */
+  public void setLevel(int v)
+  {
+    m_level = v;
+  }
+
+  /**
+   * Get the "level" attribute.
+   * The level attribute specifies what levels of the source tree should
+   * be considered; it has the values single, multiple or any. The default
+   * is single.
+   *
+   * @return Value of "level" attribute.
+   */
+  public int getLevel()
+  {
+    return m_level;
+  }
+
+  /**
+   * The value attribute contains an expression. The expression is evaluated
+   * and the resulting object is converted to a number as if by a call to the
+   * number function.
+   * @serial
+   */
+  private XPath m_valueExpr = null;
+
+  /**
+   * Set the "value" attribute.
+   * The value attribute contains an expression. The expression is evaluated
+   * and the resulting object is converted to a number as if by a call to the
+   * number function.
+   *
+   * @param v Value to set for "value" attribute.
+   */
+  public void setValue(XPath v)
+  {
+    m_valueExpr = v;
+  }
+
+  /**
+   * Get the "value" attribute.
+   * The value attribute contains an expression. The expression is evaluated
+   * and the resulting object is converted to a number as if by a call to the
+   * number function.
+   *
+   * @return Value of "value" attribute.
+   */
+  public XPath getValue()
+  {
+    return m_valueExpr;
+  }
+
+  /**
+   * The "format" attribute is used to control conversion of a list of
+   * numbers into a string.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   * @serial
+   */
+  private AVT m_format_avt = null;
+
+  /**
+   * Set the "format" attribute.
+   * The "format" attribute is used to control conversion of a list of
+   * numbers into a string.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @param v Value to set for "format" attribute.
+   */
+  public void setFormat(AVT v)
+  {
+    m_format_avt = v;
+  }
+
+  /**
+   * Get the "format" attribute.
+   * The "format" attribute is used to control conversion of a list of
+   * numbers into a string.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @return Value of "format" attribute.
+   */
+  public AVT getFormat()
+  {
+    return m_format_avt;
+  }
+
+  /**
+   * When numbering with an alphabetic sequence, the lang attribute
+   * specifies which language's alphabet is to be used.
+   * @serial
+   */
+  private AVT m_lang_avt = null;
+
+  /**
+   * Set the "lang" attribute.
+   * When numbering with an alphabetic sequence, the lang attribute
+   * specifies which language's alphabet is to be used; it has the same
+   * range of values as xml:lang [XML]; if no lang value is specified,
+   * the language should be determined from the system environment.
+   * Implementers should document for which languages they support numbering.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @param v Value to set for "lang" attribute.
+   */
+  public void setLang(AVT v)
+  {
+    m_lang_avt = v;
+  }
+
+  /**
+   * Get the "lang" attribute.
+   * When numbering with an alphabetic sequence, the lang attribute
+   * specifies which language's alphabet is to be used; it has the same
+   * range of values as xml:lang [XML]; if no lang value is specified,
+   * the language should be determined from the system environment.
+   * Implementers should document for which languages they support numbering.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @return Value ofr "lang" attribute.
+   */
+  public AVT getLang()
+  {
+    return m_lang_avt;
+  }
+
+  /**
+   * The letter-value attribute disambiguates between numbering
+   * sequences that use letters.
+   * @serial
+   */
+  private AVT m_lettervalue_avt = null;
+
+  /**
+   * Set the "letter-value" attribute.
+   * The letter-value attribute disambiguates between numbering sequences
+   * that use letters.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @param v Value to set for "letter-value" attribute.
+   */
+  public void setLetterValue(AVT v)
+  {
+    m_lettervalue_avt = v;
+  }
+
+  /**
+   * Get the "letter-value" attribute.
+   * The letter-value attribute disambiguates between numbering sequences
+   * that use letters.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @return Value to set for "letter-value" attribute.
+   */
+  public AVT getLetterValue()
+  {
+    return m_lettervalue_avt;
+  }
+
+  /**
+   * The grouping-separator attribute gives the separator
+   * used as a grouping (e.g. thousands) separator in decimal
+   * numbering sequences.
+   * @serial
+   */
+  private AVT m_groupingSeparator_avt = null;
+
+  /**
+   * Set the "grouping-separator" attribute.
+   * The grouping-separator attribute gives the separator
+   * used as a grouping (e.g. thousands) separator in decimal
+   * numbering sequences.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @param v Value to set for "grouping-separator" attribute.
+   */
+  public void setGroupingSeparator(AVT v)
+  {
+    m_groupingSeparator_avt = v;
+  }
+
+  /**
+   * Get the "grouping-separator" attribute.
+   * The grouping-separator attribute gives the separator
+   * used as a grouping (e.g. thousands) separator in decimal
+   * numbering sequences.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @return Value of "grouping-separator" attribute.
+   */
+  public AVT getGroupingSeparator()
+  {
+    return m_groupingSeparator_avt;
+  }
+
+  /**
+   * The optional grouping-size specifies the size (normally 3) of the grouping.
+   * @serial
+   */
+  private AVT m_groupingSize_avt = null;
+
+  /**
+   * Set the "grouping-size" attribute.
+   * The optional grouping-size specifies the size (normally 3) of the grouping.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @param v Value to set for "grouping-size" attribute.
+   */
+  public void setGroupingSize(AVT v)
+  {
+    m_groupingSize_avt = v;
+  }
+
+  /**
+   * Get the "grouping-size" attribute.
+   * The optional grouping-size specifies the size (normally 3) of the grouping.
+   * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
+   *
+   * @return Value of "grouping-size" attribute.
+   */
+  public AVT getGroupingSize()
+  {
+    return m_groupingSize_avt;
+  }
+
+  /**
+   * Shouldn't this be in the transformer?  Big worries about threads...
+   */
+
+  // private XResourceBundle thisBundle;
+
+  /**
+   * Table to help in converting decimals to roman numerals.
+   * @see org.apache.xalan.transformer.DecimalToRoman
+   */
+  private final static DecimalToRoman m_romanConvertTable[] = {
+    new DecimalToRoman(1000, "M", 900, "CM"),
+    new DecimalToRoman(500, "D", 400, "CD"),
+    new DecimalToRoman(100L, "C", 90L, "XC"),
+    new DecimalToRoman(50L, "L", 40L, "XL"),
+    new DecimalToRoman(10L, "X", 9L, "IX"),
+    new DecimalToRoman(5L, "V", 4L, "IV"),
+    new DecimalToRoman(1L, "I", 1L, "I") };
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    java.util.Vector vnames = cstate.getVariableNames();
+    if(null != m_countMatchPattern)
+      m_countMatchPattern.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_format_avt)
+      m_format_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_fromMatchPattern)
+      m_fromMatchPattern.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_groupingSeparator_avt)
+      m_groupingSeparator_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_groupingSize_avt)
+      m_groupingSize_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_lang_avt)
+      m_lang_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_lettervalue_avt)
+      m_lettervalue_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_valueExpr)
+      m_valueExpr.fixupVariables(vnames, cstate.getGlobalsSize());
+  }
+
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_NUMBER;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_NUMBER_STRING;
+  }
+
+  /**
+   * Execute an xsl:number instruction. The xsl:number element is
+   * used to insert a formatted number into the result tree.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    int sourceNode = transformer.getXPathContext().getCurrentNode();
+    String countString = getCountString(transformer, sourceNode);
+
+    try
+    {
+      transformer.getResultTreeHandler().characters(countString.toCharArray(),
+                                                    0, countString.length());
+    }
+    catch(SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+  }
+
+  /**
+   * Add a child to the child list.
+   *
+   * @param newChild Child to add to child list
+   *
+   * @return Child just added to child list
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    error(XSLTErrorResources.ER_CANNOT_ADD,
+          new Object[]{ newChild.getNodeName(),
+                        this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    return null;
+  }
+
+  /**
+   * Given a 'from' pattern (ala xsl:number), a match pattern
+   * and a context, find the first ancestor that matches the
+   * pattern (including the context handed in).
+   *
+   * @param xctxt The XPath runtime state for this.
+   * @param fromMatchPattern The ancestor must match this pattern.
+   * @param countMatchPattern The ancestor must also match this pattern.
+   * @param context The node that "." expresses.
+   * @param namespaceContext The context in which namespaces in the
+   * queries are supposed to be expanded.
+   *
+   * @return the first ancestor that matches the given pattern
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  int findAncestor(
+          XPathContext xctxt, XPath fromMatchPattern, XPath countMatchPattern, 
+          int context, ElemNumber namespaceContext)
+            throws javax.xml.transform.TransformerException
+  {
+    DTM dtm = xctxt.getDTM(context);
+    while (DTM.NULL != context)
+    {
+      if (null != fromMatchPattern)
+      {
+        if (fromMatchPattern.getMatchScore(xctxt, context)
+                != XPath.MATCH_SCORE_NONE)
+        {
+
+          //context = null;
+          break;
+        }
+      }
+
+      if (null != countMatchPattern)
+      {
+        if (countMatchPattern.getMatchScore(xctxt, context)
+                != XPath.MATCH_SCORE_NONE)
+        {
+          break;
+        }
+      }
+
+      context = dtm.getParent(context);
+    }
+
+    return context;
+  }
+
+  /**
+   * Given a 'from' pattern (ala xsl:number), a match pattern
+   * and a context, find the first ancestor that matches the
+   * pattern (including the context handed in).
+   * @param xctxt The XPath runtime state for this.
+   * @param fromMatchPattern The ancestor must match this pattern.
+   * @param countMatchPattern The ancestor must also match this pattern.
+   * @param context The node that "." expresses.
+   * @param namespaceContext The context in which namespaces in the
+   * queries are supposed to be expanded.
+   *
+   * @return the first preceding, ancestor or self node that 
+   * matches the given pattern
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  private int findPrecedingOrAncestorOrSelf(
+          XPathContext xctxt, XPath fromMatchPattern, XPath countMatchPattern, 
+          int context, ElemNumber namespaceContext)
+            throws javax.xml.transform.TransformerException
+  {
+    DTM dtm = xctxt.getDTM(context);
+    while (DTM.NULL != context)
+    {
+      if (null != fromMatchPattern)
+      {
+        if (fromMatchPattern.getMatchScore(xctxt, context)
+                != XPath.MATCH_SCORE_NONE)
+        {
+          context = DTM.NULL;
+
+          break;
+        }
+      }
+
+      if (null != countMatchPattern)
+      {
+        if (countMatchPattern.getMatchScore(xctxt, context)
+                != XPath.MATCH_SCORE_NONE)
+        {
+          break;
+        }
+      }
+
+      int prevSibling = dtm.getPreviousSibling(context);
+
+      if (DTM.NULL == prevSibling)
+      {
+        context = dtm.getParent(context);
+      }
+      else
+      {
+
+        // Now go down the chain of children of this sibling 
+        context = dtm.getLastChild(prevSibling);
+
+        if (context == DTM.NULL)
+          context = prevSibling;
+      }
+    }
+
+    return context;
+  }
+
+  /**
+   * Get the count match pattern, or a default value.
+   *
+   * @param support The XPath runtime state for this.
+   * @param contextNode The node that "." expresses.
+   *
+   * @return the count match pattern, or a default value. 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  XPath getCountMatchPattern(XPathContext support, int contextNode)
+          throws javax.xml.transform.TransformerException
+  {
+
+    XPath countMatchPattern = m_countMatchPattern;
+    DTM dtm = support.getDTM(contextNode);
+    if (null == countMatchPattern)
+    {
+      switch (dtm.getNodeType(contextNode))
+      {
+      case DTM.ELEMENT_NODE :
+        MyPrefixResolver resolver;
+
+        if (dtm.getNamespaceURI(contextNode) == null) {
+             resolver =  new MyPrefixResolver(dtm.getNode(contextNode), dtm,contextNode, false);
+        } else {
+            resolver = new MyPrefixResolver(dtm.getNode(contextNode), dtm,contextNode, true);
+        }
+
+        countMatchPattern = new XPath(dtm.getNodeName(contextNode), this, resolver,
+                                      XPath.MATCH, support.getErrorListener());
+        break;
+
+      case DTM.ATTRIBUTE_NODE :
+
+        // countMatchPattern = m_stylesheet.createMatchPattern("@"+contextNode.getNodeName(), this);
+        countMatchPattern = new XPath("@" + dtm.getNodeName(contextNode), this,
+                                      this, XPath.MATCH, support.getErrorListener());
+        break;
+      case DTM.CDATA_SECTION_NODE :
+      case DTM.TEXT_NODE :
+
+        // countMatchPattern = m_stylesheet.createMatchPattern("text()", this);
+        countMatchPattern = new XPath("text()", this, this, XPath.MATCH, support.getErrorListener());
+        break;
+      case DTM.COMMENT_NODE :
+
+        // countMatchPattern = m_stylesheet.createMatchPattern("comment()", this);
+        countMatchPattern = new XPath("comment()", this, this, XPath.MATCH, support.getErrorListener());
+        break;
+      case DTM.DOCUMENT_NODE :
+
+        // countMatchPattern = m_stylesheet.createMatchPattern("/", this);
+        countMatchPattern = new XPath("/", this, this, XPath.MATCH, support.getErrorListener());
+        break;
+      case DTM.PROCESSING_INSTRUCTION_NODE :
+
+        // countMatchPattern = m_stylesheet.createMatchPattern("pi("+contextNode.getNodeName()+")", this);
+        countMatchPattern = new XPath("pi(" + dtm.getNodeName(contextNode)
+                                      + ")", this, this, XPath.MATCH, support.getErrorListener());
+        break;
+      default :
+        countMatchPattern = null;
+      }
+    }
+
+    return countMatchPattern;
+  }
+
+  /**
+   * Given an XML source node, get the count according to the
+   * parameters set up by the xsl:number attributes.
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param sourceNode The source node being counted.
+   *
+   * @return The count of nodes
+   *
+   * @throws TransformerException
+   */
+  String getCountString(TransformerImpl transformer, int sourceNode)
+          throws TransformerException
+  {
+
+    long[] list = null;
+    XPathContext xctxt = transformer.getXPathContext();
+    CountersTable ctable = transformer.getCountersTable();
+
+    if (null != m_valueExpr)
+    {
+      XObject countObj = m_valueExpr.execute(xctxt, sourceNode, this);
+      //According to Errata E24
+      double d_count = java.lang.Math.floor(countObj.num()+ 0.5);
+      if (Double.isNaN(d_count)) return "NaN";
+      else if (d_count < 0 && Double.isInfinite(d_count)) return "-Infinity";
+      else if (Double.isInfinite(d_count)) return "Infinity";
+      else if (d_count == 0) return "0";
+      else{
+              long count = (long)d_count;
+              list = new long[1];
+              list[0] = count;              
+      }
+    }
+    else
+    {
+      if (Constants.NUMBERLEVEL_ANY == m_level)
+      {
+        list = new long[1];
+        list[0] = ctable.countNode(xctxt, this, sourceNode);
+      }
+      else
+      {
+        NodeVector ancestors =
+          getMatchingAncestors(xctxt, sourceNode,
+                               Constants.NUMBERLEVEL_SINGLE == m_level);
+        int lastIndex = ancestors.size() - 1;
+
+        if (lastIndex >= 0)
+        {
+          list = new long[lastIndex + 1];
+
+          for (int i = lastIndex; i >= 0; i--)
+          {
+            int target = ancestors.elementAt(i);
+
+            list[lastIndex - i] = ctable.countNode(xctxt, this, target);
+          }
+        }
+      }
+    }
+
+    return (null != list)
+           ? formatNumberList(transformer, list, sourceNode) : "";
+  }
+
+  /**
+   * Get the previous node to be counted.
+   *
+   * @param xctxt The XPath runtime state for this.
+   * @param pos The current node
+   *
+   * @return the previous node to be counted.
+   *
+   * @throws TransformerException
+   */
+  public int getPreviousNode(XPathContext xctxt, int pos)
+          throws TransformerException
+  {
+
+    XPath countMatchPattern = getCountMatchPattern(xctxt, pos);
+    DTM dtm = xctxt.getDTM(pos);
+
+    if (Constants.NUMBERLEVEL_ANY == m_level)
+    {
+      XPath fromMatchPattern = m_fromMatchPattern;
+
+      // Do a backwards document-order walk 'till a node is found that matches 
+      // the 'from' pattern, or a node is found that matches the 'count' pattern, 
+      // or the top of the tree is found.
+      while (DTM.NULL != pos)
+      {
+
+        // Get the previous sibling, if there is no previous sibling, 
+        // then count the parent, but if there is a previous sibling, 
+        // dive down to the lowest right-hand (last) child of that sibling.
+        int next = dtm.getPreviousSibling(pos);
+
+        if (DTM.NULL == next)
+        {
+          next = dtm.getParent(pos);
+
+          if ((DTM.NULL != next) && ((((null != fromMatchPattern) && (fromMatchPattern.getMatchScore(
+                  xctxt, next) != XPath.MATCH_SCORE_NONE))) 
+              || (dtm.getNodeType(next) == DTM.DOCUMENT_NODE)))
+          {
+            pos = DTM.NULL;  // return null from function.
+
+            break;  // from while loop
+          }
+        }
+        else
+        {
+
+          // dive down to the lowest right child.
+          int child = next;
+
+          while (DTM.NULL != child)
+          {
+            child = dtm.getLastChild(next);
+
+            if (DTM.NULL != child)
+              next = child;
+          }
+        }
+
+        pos = next;
+
+        if ((DTM.NULL != pos)
+                && ((null == countMatchPattern)
+                    || (countMatchPattern.getMatchScore(xctxt, pos)
+                        != XPath.MATCH_SCORE_NONE)))
+        {
+          break;
+        }
+      }
+    }
+    else  // NUMBERLEVEL_MULTI or NUMBERLEVEL_SINGLE
+    {
+      while (DTM.NULL != pos)
+      {
+        pos = dtm.getPreviousSibling(pos);
+
+        if ((DTM.NULL != pos)
+                && ((null == countMatchPattern)
+                    || (countMatchPattern.getMatchScore(xctxt, pos)
+                        != XPath.MATCH_SCORE_NONE)))
+        {
+          break;
+        }
+      }
+    }
+
+    return pos;
+  }
+
+  /**
+   * Get the target node that will be counted..
+   *
+   * @param xctxt The XPath runtime state for this.
+   * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
+   *
+   * @return the target node that will be counted
+   *
+   * @throws TransformerException
+   */
+  public int getTargetNode(XPathContext xctxt, int sourceNode)
+          throws TransformerException
+  {
+
+    int target = DTM.NULL;
+    XPath countMatchPattern = getCountMatchPattern(xctxt, sourceNode);
+
+    if (Constants.NUMBERLEVEL_ANY == m_level)
+    {
+      target = findPrecedingOrAncestorOrSelf(xctxt, m_fromMatchPattern,
+                                             countMatchPattern, sourceNode,
+                                             this);
+    }
+    else
+    {
+      target = findAncestor(xctxt, m_fromMatchPattern, countMatchPattern,
+                            sourceNode, this);
+    }
+
+    return target;
+  }
+
+  /**
+   * Get the ancestors, up to the root, that match the
+   * pattern.
+   * 
+   * @param xctxt The XPath runtime state for this.
+   * @param node Count this node and it's ancestors.
+   * @param stopAtFirstFound Flag indicating to stop after the
+   * first node is found (difference between level = single
+   * or multiple)
+   * @return The number of ancestors that match the pattern.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  NodeVector getMatchingAncestors(
+          XPathContext xctxt, int node, boolean stopAtFirstFound)
+            throws javax.xml.transform.TransformerException
+  {
+
+    NodeSetDTM ancestors = new NodeSetDTM(xctxt.getDTMManager());
+    XPath countMatchPattern = getCountMatchPattern(xctxt, node);
+    DTM dtm = xctxt.getDTM(node);
+
+    while (DTM.NULL != node)
+    {
+      if ((null != m_fromMatchPattern)
+              && (m_fromMatchPattern.getMatchScore(xctxt, node)
+                  != XPath.MATCH_SCORE_NONE))
+      {
+
+        // The following if statement gives level="single" different 
+        // behavior from level="multiple", which seems incorrect according 
+        // to the XSLT spec.  For now we are leaving this in to replicate 
+        // the same behavior in XT, but, for all intents and purposes we 
+        // think this is a bug, or there is something about level="single" 
+        // that we still don't understand.
+        if (!stopAtFirstFound)
+          break;
+      }
+
+      if (null == countMatchPattern)
+        System.out.println(
+          "Programmers error! countMatchPattern should never be null!");
+
+      if (countMatchPattern.getMatchScore(xctxt, node)
+              != XPath.MATCH_SCORE_NONE)
+      {
+        ancestors.addElement(node);
+
+        if (stopAtFirstFound)
+          break;
+      }
+
+      node = dtm.getParent(node);
+    }
+
+    return ancestors;
+  }  // end getMatchingAncestors method
+
+  /**
+   * Get the locale we should be using.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param contextNode The node that "." expresses.
+   *
+   * @return The locale to use. May be specified by "lang" attribute,
+   * but if not, use default locale on the system. 
+   *
+   * @throws TransformerException
+   */
+  Locale getLocale(TransformerImpl transformer, int contextNode)
+          throws TransformerException
+  {
+
+    Locale locale = null;
+
+    if (null != m_lang_avt)
+    {
+      XPathContext xctxt = transformer.getXPathContext();
+      String langValue = m_lang_avt.evaluate(xctxt, contextNode, this);
+
+      if (null != langValue)
+      {
+
+        // Not really sure what to do about the country code, so I use the
+        // default from the system.
+        // TODO: fix xml:lang handling.
+        locale = new Locale(langValue.toUpperCase(), "");
+
+        //Locale.getDefault().getDisplayCountry());
+        if (null == locale)
+        {
+          transformer.getMsgMgr().warn(this, null, xctxt.getDTM(contextNode).getNode(contextNode),
+                                       XSLTErrorResources.WG_LOCALE_NOT_FOUND,
+                                       new Object[]{ langValue });  //"Warning: Could not find locale for xml:lang="+langValue);
+
+          locale = Locale.getDefault();
+        }
+      }
+    }
+    else
+    {
+      locale = Locale.getDefault();
+    }
+
+    return locale;
+  }
+
+  /**
+   * Get the number formatter to be used the format the numbers
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param contextNode The node that "." expresses.
+   *
+   * ($objectName$) @return The number formatter to be used
+   *
+   * @throws TransformerException
+   */
+  private DecimalFormat getNumberFormatter(
+          TransformerImpl transformer, int contextNode) throws TransformerException
+  {
+    // Patch from Steven Serocki
+    // Maybe we really want to do the clone in getLocale() and return  
+    // a clone of the default Locale??
+    Locale locale = (Locale)getLocale(transformer, contextNode).clone();
+
+    // Helper to format local specific numbers to strings.
+    DecimalFormat formatter = null;
+
+    //synchronized (locale)
+    //{
+    //     formatter = (DecimalFormat) NumberFormat.getNumberInstance(locale);
+    //}
+
+    String digitGroupSepValue =
+      (null != m_groupingSeparator_avt)
+      ? m_groupingSeparator_avt.evaluate(
+      transformer.getXPathContext(), contextNode, this) : null;
+      
+      
+    // Validate grouping separator if an AVT was used; otherwise this was 
+    // validated statically in XSLTAttributeDef.java.
+    if ((digitGroupSepValue != null) && (!m_groupingSeparator_avt.isSimple()) &&
+        (digitGroupSepValue.length() != 1))
+    {
+            transformer.getMsgMgr().warn(
+               this, XSLTErrorResources.WG_ILLEGAL_ATTRIBUTE_VALUE,
+               new Object[]{ Constants.ATTRNAME_NAME, m_groupingSeparator_avt.getName()});   
+    }                  
+      
+      
+    String nDigitsPerGroupValue =
+      (null != m_groupingSize_avt)
+      ? m_groupingSize_avt.evaluate(
+      transformer.getXPathContext(), contextNode, this) : null;
+
+    // TODO: Handle digit-group attributes
+    if ((null != digitGroupSepValue) && (null != nDigitsPerGroupValue) &&
+        // Ignore if separation value is empty string
+        (digitGroupSepValue.length() > 0))
+    {
+      try
+      {
+        formatter = (DecimalFormat) NumberFormat.getNumberInstance(locale);
+        formatter.setGroupingSize(
+          Integer.valueOf(nDigitsPerGroupValue).intValue());
+        
+        DecimalFormatSymbols symbols = formatter.getDecimalFormatSymbols();
+        symbols.setGroupingSeparator(digitGroupSepValue.charAt(0));
+        formatter.setDecimalFormatSymbols(symbols);
+        formatter.setGroupingUsed(true);
+      }
+      catch (NumberFormatException ex)
+      {
+        formatter.setGroupingUsed(false);
+      }
+    }
+
+    return formatter;
+  }
+
+  /**
+   * Format a vector of numbers into a formatted string.
+   * 
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param list Array of one or more long integer numbers.
+   * @param contextNode The node that "." expresses.
+   * @return String that represents list according to
+   * %conversion-atts; attributes.
+   * TODO: Optimize formatNumberList so that it caches the last count and
+   * reuses that info for the next count.
+   *
+   * @throws TransformerException
+   */
+  String formatNumberList(
+          TransformerImpl transformer, long[] list, int contextNode)
+            throws TransformerException
+  {
+
+    String numStr;
+    FastStringBuffer formattedNumber = StringBufferPool.get();
+
+    try
+    {
+      int nNumbers = list.length, numberWidth = 1;
+      char numberType = '1';
+      String formatToken, lastSepString = null, formatTokenString = null;
+
+      // If a seperator hasn't been specified, then use "."  
+      // as a default separator. 
+      // For instance: [2][1][5] with a format value of "1 "
+      // should format to "2.1.5 " (I think).
+      // Otherwise, use the seperator specified in the format string.
+      // For instance: [2][1][5] with a format value of "01-001. "
+      // should format to "02-001-005 ".
+      String lastSep = ".";
+      boolean isFirstToken = true;  // true if first token  
+      String formatValue =
+        (null != m_format_avt)
+        ? m_format_avt.evaluate(
+        transformer.getXPathContext(), contextNode, this) : null;
+
+      if (null == formatValue)
+        formatValue = "1";
+
+      NumberFormatStringTokenizer formatTokenizer =
+        new NumberFormatStringTokenizer(formatValue);
+
+      // int sepCount = 0;                  // keep track of seperators
+      // Loop through all the numbers in the list.
+      for (int i = 0; i < nNumbers; i++)
+      {
+
+        // Loop to the next digit, letter, or separator.
+        if (formatTokenizer.hasMoreTokens())
+        {
+          formatToken = formatTokenizer.nextToken();
+
+          // If the first character of this token is a character or digit, then 
+          // it is a number format directive.
+          if (Character.isLetterOrDigit(
+                  formatToken.charAt(formatToken.length() - 1)))
+          {
+            numberWidth = formatToken.length();
+            numberType = formatToken.charAt(numberWidth - 1);
+          }
+
+          // If there is a number format directive ahead, 
+          // then append the formatToken.
+          else if (formatTokenizer.isLetterOrDigitAhead())
+          {
+            formatTokenString = formatToken;
+
+            // Append the formatToken string...
+            // For instance [2][1][5] with a format value of "1--1. "
+            // should format to "2--1--5. " (I guess).
+            while (formatTokenizer.nextIsSep())
+            {
+              formatToken = formatTokenizer.nextToken();
+              formatTokenString += formatToken;
+            }
+
+            // Record this separator, so it can be used as the 
+            // next separator, if the next is the last.
+            // For instance: [2][1][5] with a format value of "1-1 "
+            // should format to "2-1-5 ".
+            if (!isFirstToken)
+              lastSep = formatTokenString;
+
+            // Since we know the next is a number or digit, we get it now.
+            formatToken = formatTokenizer.nextToken();
+            numberWidth = formatToken.length();
+            numberType = formatToken.charAt(numberWidth - 1);
+          }
+          else  // only separators left
+          {
+
+            // Set up the string for the trailing characters after 
+            // the last number is formatted (i.e. after the loop).
+            lastSepString = formatToken;
+
+            // And append any remaining characters to the lastSepString.
+            while (formatTokenizer.hasMoreTokens())
+            {
+              formatToken = formatTokenizer.nextToken();
+              lastSepString += formatToken;
+            }
+          }  // else
+        }  // end if(formatTokenizer.hasMoreTokens())
+
+        // if this is the first token and there was a prefix
+        // append the prefix else, append the separator
+        // For instance, [2][1][5] with a format value of "(1-1.) "
+        // should format to "(2-1-5.) " (I guess).
+        if (null != formatTokenString && isFirstToken)
+        {
+          formattedNumber.append(formatTokenString);
+        }
+        else if (null != lastSep &&!isFirstToken)
+          formattedNumber.append(lastSep);
+
+        getFormattedNumber(transformer, contextNode, numberType, numberWidth,
+                           list[i], formattedNumber);
+
+        isFirstToken = false;  // After the first pass, this should be false
+      }  // end for loop
+
+      // Check to see if we finished up the format string...
+      // Skip past all remaining letters or digits
+      while (formatTokenizer.isLetterOrDigitAhead())
+      {
+        formatTokenizer.nextToken();
+      }
+
+      if (lastSepString != null)
+        formattedNumber.append(lastSepString);
+
+      while (formatTokenizer.hasMoreTokens())
+      {
+        formatToken = formatTokenizer.nextToken();
+
+        formattedNumber.append(formatToken);
+      }
+
+      numStr = formattedNumber.toString();
+    }
+    finally
+    {
+      StringBufferPool.free(formattedNumber);
+    }
+
+    return numStr;
+  }  // end formatNumberList method
+
+  /*
+  * Get Formatted number
+  */
+
+  /**
+   * Format the given number and store it in the given buffer 
+   *
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param contextNode The node that "." expresses.
+   * @param numberType Type to format to
+   * @param numberWidth Maximum length of formatted number
+   * @param listElement Number to format
+   * @param formattedNumber Buffer to store formatted number
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  private void getFormattedNumber(
+          TransformerImpl transformer, int contextNode, 
+          char numberType, int numberWidth, long listElement, 
+          FastStringBuffer formattedNumber)
+            throws javax.xml.transform.TransformerException
+  {
+
+
+    String letterVal =
+      (m_lettervalue_avt != null)
+      ? m_lettervalue_avt.evaluate(
+      transformer.getXPathContext(), contextNode, this) : null;
+
+    /**
+     * Wrapper of Chars for converting integers into alpha counts.
+     */
+    CharArrayWrapper alphaCountTable = null;
+    
+    XResourceBundle thisBundle = null;
+     
+    switch (numberType)
+    {
+    case 'A' :
+        if (null == m_alphaCountTable){
+                thisBundle =
+                  (XResourceBundle) XResourceBundle.loadResourceBundle(
+                    org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, getLocale(transformer, contextNode));
+                m_alphaCountTable = (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET);                
+        }
+      int2alphaCount(listElement, m_alphaCountTable, formattedNumber);
+      break;
+    case 'a' :
+        if (null == m_alphaCountTable){
+                thisBundle =
+                  (XResourceBundle) XResourceBundle.loadResourceBundle(
+                    org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, getLocale(transformer, contextNode));
+                m_alphaCountTable = (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET);                
+        }
+      FastStringBuffer stringBuf = StringBufferPool.get();
+
+      try
+      {
+        int2alphaCount(listElement, m_alphaCountTable, stringBuf);
+        formattedNumber.append(
+          stringBuf.toString().toLowerCase(
+            getLocale(transformer, contextNode)));
+      }
+      finally
+      {
+        StringBufferPool.free(stringBuf);
+      }
+      break;
+    case 'I' :
+      formattedNumber.append(long2roman(listElement, true));
+      break;
+    case 'i' :
+      formattedNumber.append(
+        long2roman(listElement, true).toLowerCase(
+          getLocale(transformer, contextNode)));
+      break;
+    case 0x3042 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ja", "JP", "HA"));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        formattedNumber.append(
+          int2singlealphaCount(
+            listElement,
+            (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET)));
+
+      break;
+    }
+    case 0x3044 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ja", "JP", "HI"));
+
+      if ((letterVal != null)
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        formattedNumber.append(
+          int2singlealphaCount(
+            listElement,
+            (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET)));
+
+      break;
+    }
+    case 0x30A2 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ja", "JP", "A"));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        formattedNumber.append(
+          int2singlealphaCount(
+            listElement,
+            (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET)));
+
+      break;
+    }
+    case 0x30A4 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ja", "JP", "I"));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        formattedNumber.append(
+          int2singlealphaCount(
+            listElement,
+            (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET)));
+
+      break;
+    }
+    case 0x4E00 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("zh", "CN"));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+      {
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      }
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        int2alphaCount(listElement,
+                       (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
+                       formattedNumber);
+
+      break;
+    }
+    case 0x58F9 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("zh", "TW"));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        int2alphaCount(listElement,
+                       (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
+                       formattedNumber);
+
+      break;
+    }
+    case 0x0E51 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("th", ""));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        int2alphaCount(listElement,
+                       (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
+                       formattedNumber);
+
+      break;
+    }
+    case 0x05D0 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("he", ""));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        int2alphaCount(listElement,
+                       (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
+                       formattedNumber);
+
+      break;
+    }
+    case 0x10D0 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ka", ""));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        int2alphaCount(listElement,
+                       (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
+                       formattedNumber);
+
+      break;
+    }
+    case 0x03B1 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("el", ""));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        int2alphaCount(listElement,
+                       (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
+                       formattedNumber);
+
+      break;
+    }
+    case 0x0430 :
+    {
+
+      thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
+        org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("cy", ""));
+
+      if (letterVal != null
+              && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
+        formattedNumber.append(tradAlphaCount(listElement, thisBundle));
+      else  //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
+        int2alphaCount(listElement,
+                       (CharArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
+                       formattedNumber);
+
+      break;
+    }
+    default :  // "1"
+      DecimalFormat formatter = getNumberFormatter(transformer, contextNode);
+      String padString = formatter == null ? String.valueOf(0) : formatter.format(0);    
+      String numString = formatter == null ? String.valueOf(listElement) : formatter.format(listElement);
+      int nPadding = numberWidth - numString.length();
+
+      for (int k = 0; k < nPadding; k++)
+      {
+        formattedNumber.append(padString);
+      }
+
+      formattedNumber.append(numString);
+    }
+  }
+  
+  /**
+   * Get a string value for zero, which is not really defined by the 1.0 spec, 
+   * thought I think it might be cleared up by the erreta.
+   */
+   String getZeroString()
+   {
+     return ""+0;
+   }
+
+  /**
+   * Convert a long integer into alphabetic counting, in other words
+   * count using the sequence A B C ... Z.
+   * 
+   * @param val Value to convert -- must be greater than zero.
+   * @param table a table containing one character for each digit in the radix
+   * @return String representing alpha count of number.
+   * @see TransformerImpl#DecimalToRoman
+   *
+   * Note that the radix of the conversion is inferred from the size
+   * of the table.
+   */
+  protected String int2singlealphaCount(long val, CharArrayWrapper table)
+  {
+
+    int radix = table.getLength();
+
+    // TODO:  throw error on out of range input
+    if (val > radix)
+    {
+      return getZeroString();
+    }
+    else
+      return (new Character(table.getChar((int)val - 1))).toString();  // index into table is off one, starts at 0
+  }
+
+  /**
+   * Convert a long integer into alphabetic counting, in other words
+   * count using the sequence A B C ... Z AA AB AC.... etc.
+   * 
+   * @param val Value to convert -- must be greater than zero.
+   * @param table a table containing one character for each digit in the radix
+   * @param aTable Array of alpha characters representing numbers
+   * @param stringBuf Buffer where to save the string representing alpha count of number.
+   * 
+   * @see TransformerImpl#DecimalToRoman
+   *
+   * Note that the radix of the conversion is inferred from the size
+   * of the table.
+   */
+  protected void int2alphaCount(long val, CharArrayWrapper aTable,
+                                FastStringBuffer stringBuf)
+  {
+
+    int radix = aTable.getLength();
+    char[] table = new char[radix];
+
+    // start table at 1, add last char at index 0. Reason explained above and below.
+    int i;
+
+    for (i = 0; i < radix - 1; i++)
+    {
+      table[i + 1] = aTable.getChar(i);
+    }
+
+    table[0] = aTable.getChar(i);
+
+    // Create a buffer to hold the result
+    // TODO:  size of the table can be detereined by computing
+    // logs of the radix.  For now, we fake it.
+    char buf[] = new char[100];
+
+    //some languages go left to right(ie. english), right to left (ie. Hebrew),
+    //top to bottom (ie.Japanese), etc... Handle them differently
+    //String orientation = thisBundle.getString(org.apache.xml.utils.res.XResourceBundle.LANG_ORIENTATION);
+    // next character to set in the buffer
+    int charPos;
+
+    charPos = buf.length - 1;  // work backward through buf[]  
+
+    // index in table of the last character that we stored
+    int lookupIndex = 1;  // start off with anything other than zero to make correction work
+
+    //                                          Correction number
+    //
+    //  Correction can take on exactly two values:
+    //
+    //          0       if the next character is to be emitted is usual
+    //
+    //      radix - 1
+    //                  if the next char to be emitted should be one less than
+    //                  you would expect
+    //                  
+    // For example, consider radix 10, where 1="A" and 10="J"
+    //
+    // In this scheme, we count: A, B, C ...   H, I, J (not A0 and certainly
+    // not AJ), A1
+    //
+    // So, how do we keep from emitting AJ for 10?  After correctly emitting the
+    // J, lookupIndex is zero.  We now compute a correction number of 9 (radix-1).
+    // In the following line, we'll compute (val+correction) % radix, which is,
+    // (val+9)/10.  By this time, val is 1, so we compute (1+9) % 10, which
+    // is 10 % 10 or zero.  So, we'll prepare to emit "JJ", but then we'll
+    // later suppress the leading J as representing zero (in the mod system,
+    // it can represent either 10 or zero).  In summary, the correction value of
+    // "radix-1" acts like "-1" when run through the mod operator, but with the
+    // desireable characteristic that it never produces a negative number.
+    long correction = 0;
+
+    // TODO:  throw error on out of range input
+    do
+    {
+
+      // most of the correction calculation is explained above,  the reason for the
+      // term after the "|| " is that it correctly propagates carries across
+      // multiple columns.
+      correction =
+        ((lookupIndex == 0) || (correction != 0 && lookupIndex == radix - 1))
+        ? (radix - 1) : 0;
+
+      // index in "table" of the next char to emit
+      lookupIndex = (int)(val + correction) % radix;
+
+      // shift input by one "column"
+      val = (val / radix);
+
+      // if the next value we'd put out would be a leading zero, we're done.
+      if (lookupIndex == 0 && val == 0)
+        break;
+
+      // put out the next character of output
+      buf[charPos--] = table[lookupIndex];  // left to right or top to bottom   
+    }
+    while (val > 0);
+
+    stringBuf.append(buf, charPos + 1, (buf.length - charPos - 1));
+  }
+
+  /**
+   * Convert a long integer into traditional alphabetic counting, in other words
+   * count using the traditional numbering.
+   * 
+   * @param val Value to convert -- must be greater than zero.
+   * @param thisBundle Resource bundle to use
+   * 
+   * @return String representing alpha count of number.
+   * @see XSLProcessor#DecimalToRoman
+   *
+   * Note that the radix of the conversion is inferred from the size
+   * of the table.
+   */
+  protected String tradAlphaCount(long val, XResourceBundle thisBundle)
+  {
+
+    // if this number is larger than the largest number we can represent, error!
+    if (val > Long.MAX_VALUE)
+    {
+      this.error(XSLTErrorResources.ER_NUMBER_TOO_BIG);
+      return XSLTErrorResources.ERROR_STRING;
+    }
+    char[] table = null;
+
+    // index in table of the last character that we stored
+    int lookupIndex = 1;  // start off with anything other than zero to make correction work
+
+    // Create a buffer to hold the result
+    // TODO:  size of the table can be detereined by computing
+    // logs of the radix.  For now, we fake it.
+    char buf[] = new char[100];
+
+    //some languages go left to right(ie. english), right to left (ie. Hebrew),
+    //top to bottom (ie.Japanese), etc... Handle them differently
+    //String orientation = thisBundle.getString(org.apache.xml.utils.res.XResourceBundle.LANG_ORIENTATION);
+    // next character to set in the buffer
+    int charPos;
+
+    charPos = 0;  //start at 0
+
+    // array of number groups: ie.1000, 100, 10, 1
+    IntArrayWrapper groups = (IntArrayWrapper) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_NUMBERGROUPS);
+
+    // array of tables of hundreds, tens, digits...
+    StringArrayWrapper tables =
+      (StringArrayWrapper) (thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_NUM_TABLES));
+
+    //some languages have additive alphabetical notation,
+    //some multiplicative-additive, etc... Handle them differently.
+    String numbering = thisBundle.getString(org.apache.xml.utils.res.XResourceBundle.LANG_NUMBERING);
+
+    // do multiplicative part first
+    if (numbering.equals(org.apache.xml.utils.res.XResourceBundle.LANG_MULT_ADD))
+    {
+      String mult_order = thisBundle.getString(org.apache.xml.utils.res.XResourceBundle.MULT_ORDER);
+      LongArrayWrapper multiplier =
+        (LongArrayWrapper) (thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_MULTIPLIER));
+      CharArrayWrapper zeroChar = (CharArrayWrapper) thisBundle.getObject("zero");
+      int i = 0;
+
+      // skip to correct multiplier
+      while (i < multiplier.getLength() && val < multiplier.getLong(i))
+      {
+        i++;
+      }
+
+      do
+      {
+        if (i >= multiplier.getLength())
+          break;  //number is smaller than multipliers
+
+        // some languages (ie chinese) put a zero character (and only one) when
+        // the multiplier is multiplied by zero. (ie, 1001 is 1X1000 + 0X100 + 0X10 + 1)
+        // 0X100 is replaced by the zero character, we don't need one for 0X10
+        if (val < multiplier.getLong(i))
+        {
+          if (zeroChar.getLength() == 0)
+          {
+            i++;
+          }
+          else
+          {
+            if (buf[charPos - 1] != zeroChar.getChar(0))
+              buf[charPos++] = zeroChar.getChar(0);
+
+            i++;
+          }
+        }
+        else if (val >= multiplier.getLong(i))
+        {
+          long mult = val / multiplier.getLong(i);
+
+          val = val % multiplier.getLong(i);  // save this.
+
+          int k = 0;
+
+          while (k < groups.getLength())
+          {
+            lookupIndex = 1;  // initialize for each table
+
+            if (mult / groups.getInt(k) <= 0)  // look for right table
+              k++;
+            else
+            {
+
+              // get the table
+              CharArrayWrapper THEletters = (CharArrayWrapper) thisBundle.getObject(tables.getString(k));
+
+              table = new char[THEletters.getLength() + 1];
+
+              int j;
+
+              for (j = 0; j < THEletters.getLength(); j++)
+              {
+                table[j + 1] = THEletters.getChar(j);
+              }
+
+              table[0] = THEletters.getChar(j - 1);  // don't need this                                                                         
+
+              // index in "table" of the next char to emit
+              lookupIndex = (int)mult / groups.getInt(k);
+
+              //this should not happen
+              if (lookupIndex == 0 && mult == 0)
+                break;
+
+              char multiplierChar = ((CharArrayWrapper) (thisBundle.getObject(
+                org.apache.xml.utils.res.XResourceBundle.LANG_MULTIPLIER_CHAR))).getChar(i);
+
+              // put out the next character of output   
+              if (lookupIndex < table.length)
+              {
+                if (mult_order.equals(org.apache.xml.utils.res.XResourceBundle.MULT_PRECEDES))
+                {
+                  buf[charPos++] = multiplierChar;
+                  buf[charPos++] = table[lookupIndex];
+                }
+                else
+                {
+
+                  // don't put out 1 (ie 1X10 is just 10)
+                  if (lookupIndex == 1 && i == multiplier.getLength() - 1){}
+                  else
+                    buf[charPos++] = table[lookupIndex];
+
+                  buf[charPos++] = multiplierChar;
+                }
+
+                break;  // all done!
+              }
+              else
+                return XSLTErrorResources.ERROR_STRING;
+            }  //end else
+          }  // end while        
+
+          i++;
+        }  // end else if
+      }  // end do while
+      while (i < multiplier.getLength());
+    }
+
+    // Now do additive part...
+    int count = 0;
+    String tableName;
+
+    // do this for each table of hundreds, tens, digits...
+    while (count < groups.getLength())
+    {
+      if (val / groups.getInt(count) <= 0)  // look for correct table
+        count++;
+      else
+      {
+        CharArrayWrapper theletters = (CharArrayWrapper) thisBundle.getObject(tables.getString(count));
+
+        table = new char[theletters.getLength() + 1];
+
+        int j;
+
+        // need to start filling the table up at index 1
+        for (j = 0; j < theletters.getLength(); j++)
+        {
+          table[j + 1] = theletters.getChar(j);
+        }
+
+        table[0] = theletters.getChar(j - 1);  // don't need this
+
+        // index in "table" of the next char to emit
+        lookupIndex = (int)val / groups.getInt(count);
+
+        // shift input by one "column"
+        val = val % groups.getInt(count);
+
+        // this should not happen
+        if (lookupIndex == 0 && val == 0)
+          break;
+
+        if (lookupIndex < table.length)
+        {
+
+          // put out the next character of output       
+          buf[charPos++] = table[lookupIndex];  // left to right or top to bottom                                       
+        }
+        else
+          return XSLTErrorResources.ERROR_STRING;
+
+        count++;
+      }
+    }  // end while
+
+    // String s = new String(buf, 0, charPos);
+    return new String(buf, 0, charPos);
+  }
+
+  /**
+   * Convert a long integer into roman numerals.
+   * @param val Value to convert.
+   * @param prefixesAreOK true_ to enable prefix notation (e.g. 4 = "IV"),
+   * false_ to disable prefix notation (e.g. 4 = "IIII").
+   * @return Roman numeral string.
+   * @see DecimalToRoman
+   * @see m_romanConvertTable
+   */
+  protected String long2roman(long val, boolean prefixesAreOK)
+  {
+
+    if (val <= 0)
+    {
+      return getZeroString();
+    }
+
+    String roman = "";
+    int place = 0;
+
+    if (val <= 3999L)
+    {
+      do
+      {
+        while (val >= m_romanConvertTable[place].m_postValue)
+        {
+          roman += m_romanConvertTable[place].m_postLetter;
+          val -= m_romanConvertTable[place].m_postValue;
+        }
+
+        if (prefixesAreOK)
+        {
+          if (val >= m_romanConvertTable[place].m_preValue)
+          {
+            roman += m_romanConvertTable[place].m_preLetter;
+            val -= m_romanConvertTable[place].m_preValue;
+          }
+        }
+
+        place++;
+      }
+      while (val > 0);
+    }
+    else
+    {
+      roman = XSLTErrorResources.ERROR_STRING;
+    }
+
+    return roman;
+  }  // end long2roman
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  public void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+  {
+  	if(callAttrs)
+  	{
+	  	if(null != m_countMatchPattern)
+	  		m_countMatchPattern.getExpression().callVisitors(m_countMatchPattern, visitor);
+	  	if(null != m_fromMatchPattern)
+	  		m_fromMatchPattern.getExpression().callVisitors(m_fromMatchPattern, visitor);
+	  	if(null != m_valueExpr)
+	  		m_valueExpr.getExpression().callVisitors(m_valueExpr, visitor);
+	
+	  	if(null != m_format_avt)
+	  		m_format_avt.callVisitors(visitor);
+	  	if(null != m_groupingSeparator_avt)
+	  		m_groupingSeparator_avt.callVisitors(visitor);
+	  	if(null != m_groupingSize_avt)
+	  		m_groupingSize_avt.callVisitors(visitor);
+	  	if(null != m_lang_avt)
+	  		m_lang_avt.callVisitors(visitor);
+	  	if(null != m_lettervalue_avt)
+	  		m_lettervalue_avt.callVisitors(visitor);
+  	}
+
+    super.callChildVisitors(visitor, callAttrs);
+  }
+
+
+  /**
+   * This class returns tokens using non-alphanumberic
+   * characters as delimiters.
+   */
+  class NumberFormatStringTokenizer
+  {
+
+    /** Current position in the format string          */
+    private int currentPosition;
+
+    /** Index of last character in the format string      */
+    private int maxPosition;
+
+    /** Format string to be tokenized        */
+    private String str;
+
+    /**
+     * Construct a NumberFormatStringTokenizer.
+     *
+     * @param str Format string to be tokenized
+     */
+    public NumberFormatStringTokenizer(String str)
+    {
+      this.str = str;
+      maxPosition = str.length();
+    }
+
+    /**
+     * Reset tokenizer so that nextToken() starts from the beginning.
+     */
+    public void reset()
+    {
+      currentPosition = 0;
+    }
+
+    /**
+     * Returns the next token from this string tokenizer.
+     *
+     * @return     the next token from this string tokenizer.
+     * @throws  NoSuchElementException  if there are no more tokens in this
+     *               tokenizer's string.
+     */
+    public String nextToken()
+    {
+
+      if (currentPosition >= maxPosition)
+      {
+        throw new NoSuchElementException();
+      }
+
+      int start = currentPosition;
+
+      while ((currentPosition < maxPosition)
+             && Character.isLetterOrDigit(str.charAt(currentPosition)))
+      {
+        currentPosition++;
+      }
+
+      if ((start == currentPosition)
+              && (!Character.isLetterOrDigit(str.charAt(currentPosition))))
+      {
+        currentPosition++;
+      }
+
+      return str.substring(start, currentPosition);
+    }
+
+    /**
+     * Tells if there is a digit or a letter character ahead.
+     *
+     * @return     true if there is a number or character ahead.
+     */
+    public boolean isLetterOrDigitAhead()
+    {
+
+      int pos = currentPosition;
+
+      while (pos < maxPosition)
+      {
+        if (Character.isLetterOrDigit(str.charAt(pos)))
+          return true;
+
+        pos++;
+      }
+
+      return false;
+    }
+
+    /**
+     * Tells if there is a digit or a letter character ahead.
+     *
+     * @return     true if there is a number or character ahead.
+     */
+    public boolean nextIsSep()
+    {
+
+      if (Character.isLetterOrDigit(str.charAt(currentPosition)))
+        return false;
+      else
+        return true;
+    }
+
+    /**
+     * Tells if <code>nextToken</code> will throw an exception
+     * if it is called.
+     *
+     * @return true if <code>nextToken</code> can be called
+     * without throwing an exception.
+     */
+    public boolean hasMoreTokens()
+    {
+      return (currentPosition >= maxPosition) ? false : true;
+    }
+
+    /**
+     * Calculates the number of times that this tokenizer's
+     * <code>nextToken</code> method can be called before it generates an
+     * exception.
+     *
+     * @return  the number of tokens remaining in the string using the current
+     *          delimiter set.
+     * @see     java.util.StringTokenizer#nextToken()
+     */
+    public int countTokens()
+    {
+
+      int count = 0;
+      int currpos = currentPosition;
+
+      while (currpos < maxPosition)
+      {
+        int start = currpos;
+
+        while ((currpos < maxPosition)
+               && Character.isLetterOrDigit(str.charAt(currpos)))
+        {
+          currpos++;
+        }
+
+        if ((start == currpos)
+                && (Character.isLetterOrDigit(str.charAt(currpos)) == false))
+        {
+          currpos++;
+        }
+
+        count++;
+      }
+
+      return count;
+    }
+  }  // end NumberFormatStringTokenizer
+
+
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemOtherwise.java b/src/main/java/org/apache/xalan/templates/ElemOtherwise.java
new file mode 100644
index 0000000..c2a137a
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemOtherwise.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemOtherwise.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+
+/**
+ * Implement xsl:otherwise.
+ * <pre>
+ * <!ELEMENT xsl:otherwise %template;>
+ * <!ATTLIST xsl:otherwise %space-att;>
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl:choose">XXX in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemOtherwise extends ElemTemplateElement
+{
+    static final long serialVersionUID = 1863944560970181395L;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_OTHERWISE;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_OTHERWISE_STRING;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemPI.java b/src/main/java/org/apache/xalan/templates/ElemPI.java
new file mode 100644
index 0000000..449a6b7
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemPI.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemPI.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.XML11Char;
+import org.apache.xpath.XPathContext;
+
+/**
+ * Implement xsl:processing-instruction.
+ * <pre>
+ * <!ELEMENT xsl:processing-instruction %char-template;>
+ * <!ATTLIST xsl:processing-instruction
+ *   name %avt; #REQUIRED
+ *   %space-att;
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Processing-Instructions">section-Creating-Processing-Instructions in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemPI extends ElemTemplateElement
+{
+    static final long serialVersionUID = 5621976448020889825L;
+
+  /**
+   * The xsl:processing-instruction element has a required name
+   * attribute that specifies the name of the processing instruction node.
+   * The value of the name attribute is interpreted as an
+   * attribute value template.
+   * @serial
+   */
+  private AVT m_name_atv = null;
+
+  /**
+   * Set the "name" attribute.
+   * DJD
+   *
+   * @param v Value for the name attribute
+   */
+  public void setName(AVT v)
+  {
+    m_name_atv = v;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * DJD
+   *
+   * @return The value of the "name" attribute 
+   */
+  public AVT getName()
+  {
+    return m_name_atv;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    java.util.Vector vnames = sroot.getComposeState().getVariableNames();
+    if(null != m_name_atv)
+      m_name_atv.fixupVariables(vnames, sroot.getComposeState().getGlobalsSize());
+  }
+
+
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for the element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_PI;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_PI_STRING;
+  }
+
+  /**
+   * Create a processing instruction in the result tree.
+   * The content of the xsl:processing-instruction element is a
+   * template for the string-value of the processing instruction node.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Processing-Instructions">section-Creating-Processing-Instructions in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    XPathContext xctxt = transformer.getXPathContext();
+    int sourceNode = xctxt.getCurrentNode();
+    
+    String piName = m_name_atv == null ? null : m_name_atv.evaluate(xctxt, sourceNode, this);
+    
+    // Ignore processing instruction if name is null
+    if (piName == null) return;
+
+    if (piName.equalsIgnoreCase("xml"))
+    {
+     	transformer.getMsgMgr().warn(
+        this, XSLTErrorResources.WG_PROCESSINGINSTRUCTION_NAME_CANT_BE_XML,
+              new Object[]{ Constants.ATTRNAME_NAME, piName });
+		return;
+    }
+    
+    // Only check if an avt was used (ie. this wasn't checked at compose time.)
+    // Ignore processing instruction, if invalid
+    else if ((!m_name_atv.isSimple()) && (!XML11Char.isXML11ValidNCName(piName)))
+    {
+     	transformer.getMsgMgr().warn(
+        this, XSLTErrorResources.WG_PROCESSINGINSTRUCTION_NOTVALID_NCNAME,
+              new Object[]{ Constants.ATTRNAME_NAME, piName });
+		return;    	
+    }
+
+    // Note the content model is:
+    // <!ENTITY % instructions "
+    // %char-instructions;
+    // | xsl:processing-instruction
+    // | xsl:comment
+    // | xsl:element
+    // | xsl:attribute
+    // ">
+    String data = transformer.transformToString(this);
+
+    try
+    {
+      transformer.getResultTreeHandler().processingInstruction(piName, data);
+    }
+    catch(org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+  }
+
+  /**
+   * Add a child to the child list.
+   *
+   * @param newChild Child to add to child list
+   *
+   * @return The child just added to the child list
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    int type = ((ElemTemplateElement) newChild).getXSLToken();
+
+    switch (type)
+    {
+
+    // char-instructions 
+    case Constants.ELEMNAME_TEXTLITERALRESULT :
+    case Constants.ELEMNAME_APPLY_TEMPLATES :
+    case Constants.ELEMNAME_APPLY_IMPORTS :
+    case Constants.ELEMNAME_CALLTEMPLATE :
+    case Constants.ELEMNAME_FOREACH :
+    case Constants.ELEMNAME_VALUEOF :
+    case Constants.ELEMNAME_COPY_OF :
+    case Constants.ELEMNAME_NUMBER :
+    case Constants.ELEMNAME_CHOOSE :
+    case Constants.ELEMNAME_IF :
+    case Constants.ELEMNAME_TEXT :
+    case Constants.ELEMNAME_COPY :
+    case Constants.ELEMNAME_VARIABLE :
+    case Constants.ELEMNAME_MESSAGE :
+
+      // instructions 
+      // case Constants.ELEMNAME_PI:
+      // case Constants.ELEMNAME_COMMENT:
+      // case Constants.ELEMNAME_ELEMENT:
+      // case Constants.ELEMNAME_ATTRIBUTE:
+      break;
+    default :
+      error(XSLTErrorResources.ER_CANNOT_ADD,
+            new Object[]{ newChild.getNodeName(),
+                          this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    }
+
+    return super.appendChild(newChild);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemParam.java b/src/main/java/org/apache/xalan/templates/ElemParam.java
new file mode 100644
index 0000000..5e3feed
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemParam.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemParam.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Implement xsl:param.
+ * <pre>
+ * <!ELEMENT xsl:param %template;>
+ * <!ATTLIST xsl:param
+ *   name %qname; #REQUIRED
+ *   select %expr; #IMPLIED
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemParam extends ElemVariable
+{
+    static final long serialVersionUID = -1131781475589006431L;
+  int m_qnameID;
+
+  /**
+   * Constructor ElemParam
+   *
+   */
+  public ElemParam(){}
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID of the element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_PARAMVARIABLE;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_PARAMVARIABLE_STRING;
+  }
+
+  /**
+   * Copy constructor.
+   *
+   * @param param Element from an xsl:param
+   *
+   * @throws TransformerException
+   */
+  public ElemParam(ElemParam param) throws TransformerException
+  {
+    super(param);
+  }
+
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    m_qnameID = sroot.getComposeState().getQNameID(m_qname);
+    int parentToken = m_parentNode.getXSLToken();
+    if (parentToken == Constants.ELEMNAME_TEMPLATE
+        || parentToken == Constants.EXSLT_ELEMNAME_FUNCTION)
+      ((ElemTemplate)m_parentNode).m_inArgsSize++;
+  }
+  
+  /**
+   * Execute a variable declaration and push it onto the variable stack.
+   * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {
+    VariableStack vars = transformer.getXPathContext().getVarStack();
+    
+    if(!vars.isLocalSet(m_index))
+    {
+
+      int sourceNode = transformer.getXPathContext().getCurrentNode();
+      XObject var = getValue(transformer, sourceNode);
+  
+      // transformer.getXPathContext().getVarStack().pushVariable(m_qname, var);
+      transformer.getXPathContext().getVarStack().setLocalVariable(m_index, var);
+    }
+  }
+  
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemSort.java b/src/main/java/org/apache/xalan/templates/ElemSort.java
new file mode 100644
index 0000000..c8f6c14
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemSort.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemSort.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xpath.XPath;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+
+/**
+ * Implement xsl:sort.
+ * <pre>
+ * <!ELEMENT xsl:sort EMPTY>
+ * <!ATTLIST xsl:sort
+ *   select %expr; "."
+ *   lang %avt; #IMPLIED
+ *   data-type %avt; "text"
+ *   order %avt; "ascending"
+ *   case-order %avt; #IMPLIED
+ * >
+ * <!-- xsl:sort cannot occur after any other elements or
+ * any non-whitespace character -->
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#sorting">sorting in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemSort extends ElemTemplateElement
+{
+    static final long serialVersionUID = -4991510257335851938L;
+
+  /**
+   * xsl:sort has a select attribute whose value is an expression.
+   * @serial
+   */
+  private XPath m_selectExpression = null;
+
+  /**
+   * Set the "select" attribute.
+   * xsl:sort has a select attribute whose value is an expression.
+   * For each node to be processed, the expression is evaluated
+   * with that node as the current node and with the complete
+   * list of nodes being processed in unsorted order as the current
+   * node list. The resulting object is converted to a string as if
+   * by a call to the string function; this string is used as the
+   * sort key for that node. The default value of the select attribute
+   * is ., which will cause the string-value of the current node to
+   * be used as the sort key.
+   *
+   * @param v Value to set for the "select" attribute
+   */
+  public void setSelect(XPath v)
+  {
+
+    if (v.getPatternString().indexOf("{") < 0)
+      m_selectExpression = v;
+    else
+      error(XSLTErrorResources.ER_NO_CURLYBRACE, null);
+  }
+
+  /**
+   * Get the "select" attribute.
+   * xsl:sort has a select attribute whose value is an expression.
+   * For each node to be processed, the expression is evaluated
+   * with that node as the current node and with the complete
+   * list of nodes being processed in unsorted order as the current
+   * node list. The resulting object is converted to a string as if
+   * by a call to the string function; this string is used as the
+   * sort key for that node. The default value of the select attribute
+   * is ., which will cause the string-value of the current node to
+   * be used as the sort key.
+   *
+   * @return The value of the "select" attribute
+   */
+  public XPath getSelect()
+  {
+    return m_selectExpression;
+  }
+
+  /**
+   * lang specifies the language of the sort keys.
+   * @serial
+   */
+  private AVT m_lang_avt = null;
+
+  /**
+   * Set the "lang" attribute.
+   * lang specifies the language of the sort keys; it has the same
+   * range of values as xml:lang [XML]; if no lang value is
+   * specified, the language should be determined from the system environment.
+   *
+   * @param v The value to set for the "lang" attribute
+   */
+  public void setLang(AVT v)
+  {
+    m_lang_avt = v;
+  }
+
+  /**
+   * Get the "lang" attribute.
+   * lang specifies the language of the sort keys; it has the same
+   * range of values as xml:lang [XML]; if no lang value is
+   * specified, the language should be determined from the system environment.
+   *
+   * @return The value of the "lang" attribute
+   */
+  public AVT getLang()
+  {
+    return m_lang_avt;
+  }
+
+  /**
+   * data-type specifies the data type of the
+   * strings to be sorted.
+   * @serial
+   */
+  private AVT m_dataType_avt = null;
+
+  /**
+   * Set the "data-type" attribute.
+   * <code>data-type</code> specifies the data type of the
+   * strings; the following values are allowed:
+   * <ul>
+   * <li>
+   * <code>text</code> specifies that the sort keys should be
+   * sorted lexicographically in the culturally correct manner for the
+   * language specified by <code>lang</code>.
+   * </li>
+   * <li>
+   * <code>number</code> specifies that the sort keys should be
+   * converted to numbers and then sorted according to the numeric value;
+   * the sort key is converted to a number as if by a call to the
+   * <b><a href="http://www.w3.org/TR/xpath#function-number">number</a></b> function; the <code>lang</code>
+   * attribute is ignored.
+   * </li>
+   * <li>
+   * A <a href="http://www.w3.org/TR/REC-xml-names#NT-QName">QName</a> with a prefix
+   * is expanded into an <a href="http://www.w3.org/TR/xpath#dt-expanded-name">expanded-name</a> as described
+   * in <a href="#qname">[<b>2.4 Qualified Names</b>]</a>; the expanded-name identifies the data-type;
+   * the behavior in this case is not specified by this document.
+   * </li>
+   * </ul>
+   * <p>The default value is <code>text</code>.</p>
+   * <blockquote>
+   * <b>NOTE: </b>The XSL Working Group plans that future versions of XSLT will
+   * leverage XML Schemas to define further values for this
+   * attribute.</blockquote>
+   *
+   * @param v Value to set for the "data-type" attribute
+   */
+  public void setDataType(AVT v)
+  {
+    m_dataType_avt = v;
+  }
+
+  /**
+   * Get the "data-type" attribute.
+   * <code>data-type</code> specifies the data type of the
+   * strings; the following values are allowed:
+   * <ul>
+   * <li>
+   * <code>text</code> specifies that the sort keys should be
+   * sorted lexicographically in the culturally correct manner for the
+   * language specified by <code>lang</code>.
+   * </li>
+   * <li>
+   * <code>number</code> specifies that the sort keys should be
+   * converted to numbers and then sorted according to the numeric value;
+   * the sort key is converted to a number as if by a call to the
+   * <b><a href="http://www.w3.org/TR/xpath#function-number">number</a></b> function; the <code>lang</code>
+   * attribute is ignored.
+   * </li>
+   * <li>
+   * A <a href="http://www.w3.org/TR/REC-xml-names#NT-QName">QName</a> with a prefix
+   * is expanded into an <a href="http://www.w3.org/TR/xpath#dt-expanded-name">expanded-name</a> as described
+   * in <a href="#qname">[<b>2.4 Qualified Names</b>]</a>; the expanded-name identifies the data-type;
+   * the behavior in this case is not specified by this document.
+   * </li>
+   * </ul>
+   * <p>The default value is <code>text</code>.</p>
+   * <blockquote>
+   * <b>NOTE: </b>The XSL Working Group plans that future versions of XSLT will
+   * leverage XML Schemas to define further values for this
+   * attribute.</blockquote>
+   *
+   * @return The value of the "data-type" attribute
+   */
+  public AVT getDataType()
+  {
+    return m_dataType_avt;
+  }
+
+  /**
+   * order specifies whether the strings should be sorted in ascending
+   * or descending order.
+   * @serial
+   */
+  private AVT m_order_avt = null;
+
+  /**
+   * Set the "order" attribute.
+   * order specifies whether the strings should be sorted in ascending
+   * or descending order; ascending specifies ascending order; descending
+   * specifies descending order; the default is ascending.
+   *
+   * @param v The value to set for the "order" attribute
+   */
+  public void setOrder(AVT v)
+  {
+    m_order_avt = v;
+  }
+
+  /**
+   * Get the "order" attribute.
+   * order specifies whether the strings should be sorted in ascending
+   * or descending order; ascending specifies ascending order; descending
+   * specifies descending order; the default is ascending.
+   *
+   * @return The value of the "order" attribute
+   */
+  public AVT getOrder()
+  {
+    return m_order_avt;
+  }
+
+  /**
+   * case-order has the value upper-first or lower-first.
+   * The default value is language dependent.
+   * @serial
+   */
+  private AVT m_caseorder_avt = null;
+
+  /**
+   * Set the "case-order" attribute.
+   * case-order has the value upper-first or lower-first; this applies
+   * when data-type="text", and specifies that upper-case letters should
+   * sort before lower-case letters or vice-versa respectively.
+   * For example, if lang="en", then A a B b are sorted with
+   * case-order="upper-first" and a A b B are sorted with case-order="lower-first".
+   * The default value is language dependent.
+   *
+   * @param v The value to set for the "case-order" attribute
+   * 
+   * @serial
+   */
+  public void setCaseOrder(AVT v)
+  {
+    m_caseorder_avt = v;
+  }
+
+  /**
+   * Get the "case-order" attribute.
+   * case-order has the value upper-first or lower-first; this applies
+   * when data-type="text", and specifies that upper-case letters should
+   * sort before lower-case letters or vice-versa respectively.
+   * For example, if lang="en", then A a B b are sorted with
+   * case-order="upper-first" and a A b B are sorted with case-order="lower-first".
+   * The default value is language dependent.
+   *
+   * @return The value of the "case-order" attribute
+   */
+  public AVT getCaseOrder()
+  {
+    return m_caseorder_avt;
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID of the element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_SORT;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_SORT_STRING;
+  }
+
+  /**
+   * Add a child to the child list.
+   *
+   * @param newChild Child to add to the child list
+   *
+   * @return Child just added to the child list
+   *
+   * @throws DOMException
+   */
+  public Node appendChild(Node newChild) throws DOMException
+  {
+
+    error(XSLTErrorResources.ER_CANNOT_ADD,
+          new Object[]{ newChild.getNodeName(),
+                        this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    return null;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) 
+    throws javax.xml.transform.TransformerException
+  {
+    super.compose(sroot);
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    java.util.Vector vnames = cstate.getVariableNames();
+    if(null != m_caseorder_avt)
+      m_caseorder_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_dataType_avt)
+      m_dataType_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_lang_avt)
+      m_lang_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_order_avt)
+      m_order_avt.fixupVariables(vnames, cstate.getGlobalsSize());
+    if(null != m_selectExpression)
+      m_selectExpression.fixupVariables(vnames, cstate.getGlobalsSize());
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemTemplate.java b/src/main/java/org/apache/xalan/templates/ElemTemplate.java
new file mode 100644
index 0000000..cc17381
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemTemplate.java
@@ -0,0 +1,413 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemTemplate.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+
+/**
+ * Implement xsl:template.
+ * <pre>
+ * <!ELEMENT xsl:template
+ *  (#PCDATA
+ *   %instructions;
+ *   %result-elements;
+ *   | xsl:param)
+ * >
+ *
+ * <!ATTLIST xsl:template
+ *   match %pattern; #IMPLIED
+ *   name %qname; #IMPLIED
+ *   priority %priority; #IMPLIED
+ *   mode %qname; #IMPLIED
+ *   %space-att;
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemTemplate extends ElemTemplateElement
+{
+    static final long serialVersionUID = -5283056789965384058L;
+  /** The public identifier for the current document event.
+   *  @serial          */
+  private String m_publicId;
+
+  /** The system identifier for the current document event.
+   *  @serial          */
+  private String m_systemId;
+
+  /**
+   * Return the public identifier for the current document event.
+   * <p>This will be the public identifier
+   * @return A string containing the public identifier, or
+   *         null if none is available.
+   * @see #getSystemId
+   */
+  public String getPublicId()
+  {
+    return m_publicId;
+  }
+
+  /**
+   * Return the system identifier for the current document event.
+   *
+   * <p>If the system identifier is a URL, the parser must resolve it
+   * fully before passing it to the application.</p>
+   *
+   * @return A string containing the system identifier, or null
+   *         if none is available.
+   * @see #getPublicId
+   */
+  public String getSystemId()
+  {
+    return m_systemId;
+  }
+
+  /**
+   * Set the location information for this element.
+   *
+   * @param locator SourceLocator holding location information 
+   */
+  public void setLocaterInfo(SourceLocator locator)
+  {
+
+    m_publicId = locator.getPublicId();
+    m_systemId = locator.getSystemId();
+
+    super.setLocaterInfo(locator);
+  }
+
+  /**
+   * The owning stylesheet.
+   * (Should this only be put on the template element, to
+   * conserve space?)
+   * @serial
+   */
+  private Stylesheet m_stylesheet;
+
+  /**
+   * Get the stylesheet composed (resolves includes and
+   * imports and has methods on it that return "composed" properties.
+   * 
+   * @return The stylesheet composed.
+   */
+  public StylesheetComposed getStylesheetComposed()
+  {
+    return m_stylesheet.getStylesheetComposed();
+  }
+
+  /**
+   * Get the owning stylesheet.
+   *
+   * @return The owning stylesheet.
+   */
+  public Stylesheet getStylesheet()
+  {
+    return m_stylesheet;
+  }
+
+  /**
+   * Set the owning stylesheet.
+   *
+   * @param sheet The owning stylesheet for this element
+   */
+  public void setStylesheet(Stylesheet sheet)
+  {
+    m_stylesheet = sheet;
+  }
+
+  /**
+   * Get the root stylesheet.
+   *
+   * @return The root stylesheet for this element
+   */
+  public StylesheetRoot getStylesheetRoot()
+  {
+    return m_stylesheet.getStylesheetRoot();
+  }
+
+  /**
+   * The match attribute is a Pattern that identifies the source
+   * node or nodes to which the rule applies.
+   * @serial
+   */
+  private XPath m_matchPattern = null;
+
+  /**
+   * Set the "match" attribute.
+   * The match attribute is a Pattern that identifies the source
+   * node or nodes to which the rule applies. The match attribute
+   * is required unless the xsl:template element has a name
+   * attribute (see [6 Named Templates]). It is an error for the
+   * value of the match attribute to contain a VariableReference.
+   * @see <a href="http://www.w3.org/TR/xslt#patterns">patterns in XSLT Specification</a>
+   *
+   * @param v Value to set for the "match" attribute
+   */
+  public void setMatch(XPath v)
+  {
+    m_matchPattern = v;
+  }
+
+  /**
+   * Get the "match" attribute.
+   * The match attribute is a Pattern that identifies the source
+   * node or nodes to which the rule applies. The match attribute
+   * is required unless the xsl:template element has a name
+   * attribute (see [6 Named Templates]). It is an error for the
+   * value of the match attribute to contain a VariableReference.
+   * @see <a href="http://www.w3.org/TR/xslt#patterns">patterns in XSLT Specification</a>
+   *
+   * @return Value of the "match" attribute 
+   */
+  public XPath getMatch()
+  {
+    return m_matchPattern;
+  }
+
+  /**
+   * An xsl:template element with a name attribute specifies a named template.
+   * @serial
+   */
+  private QName m_name = null;
+
+  /**
+   * Set the "name" attribute.
+   * An xsl:template element with a name attribute specifies a named template.
+   * If an xsl:template element has a name attribute, it may, but need not,
+   * also have a match attribute.
+   * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
+   *
+   * @param v Value to set the "name" attribute
+   */
+  public void setName(QName v)
+  {
+    m_name = v;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * An xsl:template element with a name attribute specifies a named template.
+   * If an xsl:template element has a name attribute, it may, but need not,
+   * also have a match attribute.
+   * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
+   *
+   * @return Value of the "name" attribute
+   */
+  public QName getName()
+  {
+    return m_name;
+  }
+
+  /**
+   * Modes allow an element to be processed multiple times,
+   * each time producing a different result.
+   * @serial
+   */
+  private QName m_mode;
+
+  /**
+   * Set the "mode" attribute.
+   * Modes allow an element to be processed multiple times,
+   * each time producing a different result.  If xsl:template
+   * does not have a match attribute, it must not have a mode attribute.
+   * @see <a href="http://www.w3.org/TR/xslt#modes">modes in XSLT Specification</a>
+   *
+   * @param v Value to set the "mode" attribute
+   */
+  public void setMode(QName v)
+  {
+    m_mode = v;
+  }
+
+  /**
+   * Get the "mode" attribute.
+   * Modes allow an element to be processed multiple times,
+   * each time producing a different result.  If xsl:template
+   * does not have a match attribute, it must not have a mode attribute.
+   * @see <a href="http://www.w3.org/TR/xslt#modes">modes in XSLT Specification</a>
+   *
+   * @return Value of the "mode" attribute
+   */
+  public QName getMode()
+  {
+    return m_mode;
+  }
+
+  /**
+   * The priority of a template rule is specified by the priority
+   * attribute on the template rule.
+   * @serial
+   */
+  private double m_priority = XPath.MATCH_SCORE_NONE;
+
+  /**
+   * Set the "priority" attribute.
+   * The priority of a template rule is specified by the priority
+   * attribute on the template rule. The value of this must be a
+   * real number (positive or negative), matching the production
+   * Number with an optional leading minus sign (-).
+   * @see <a href="http://www.w3.org/TR/xslt#conflict">conflict in XSLT Specification</a>
+   *
+   * @param v The value to set for the "priority" attribute
+   */
+  public void setPriority(double v)
+  {
+    m_priority = v;
+  }
+
+  /**
+   * Get the "priority" attribute.
+   * The priority of a template rule is specified by the priority
+   * attribute on the template rule. The value of this must be a
+   * real number (positive or negative), matching the production
+   * Number with an optional leading minus sign (-).
+   * @see <a href="http://www.w3.org/TR/xslt#conflict">conflict in XSLT Specification</a>
+   *
+   * @return The value of the "priority" attribute
+   */
+  public double getPriority()
+  {
+    return m_priority;
+  }
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for the element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_TEMPLATE;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_TEMPLATE_STRING;
+  }
+  
+  /**
+   * The stack frame size for this template, which is equal to the maximum number 
+   * of params and variables that can be declared in the template at one time.
+   */
+  public int m_frameSize;
+  
+  /**
+   * The size of the portion of the stack frame that can hold parameter 
+   * arguments.
+   */
+  int m_inArgsSize;
+  
+  /**
+   * List of namespace/local-name pairs, DTM style, that are unique 
+   * qname identifiers for the arguments.  The position of a given qname 
+   * in the list is the argument ID, and thus the position in the stack
+   * frame.
+   */
+  private int[] m_argsQNameIDs;
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.compose(sroot);
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    java.util.Vector vnames = cstate.getVariableNames();
+    if(null != m_matchPattern)
+      m_matchPattern.fixupVariables(vnames, sroot.getComposeState().getGlobalsSize());
+      
+    cstate.resetStackFrameSize();
+    m_inArgsSize = 0;
+  }
+  
+  /**
+   * This after the template's children have been composed.
+   */
+  public void endCompose(StylesheetRoot sroot) throws TransformerException
+  {
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    super.endCompose(sroot);
+    m_frameSize = cstate.getFrameSize();
+    
+    cstate.resetStackFrameSize();
+  }
+
+  /**
+   * Copy the template contents into the result tree.
+   * The content of the xsl:template element is the template
+   * that is instantiated when the template rule is applied.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+    XPathContext xctxt = transformer.getXPathContext();
+    
+    xctxt.pushRTFContext();
+
+      // %REVIEW% commenting out of the code below.
+//    if (null != sourceNode)
+//    {
+      transformer.executeChildTemplates(this, true);
+//    }
+//    else  // if(null == sourceNode)
+//    {
+//      transformer.getMsgMgr().error(this,
+//        this, sourceNode,
+//        XSLTErrorResources.ER_NULL_SOURCENODE_HANDLEAPPLYTEMPLATES);
+//
+//      //"sourceNode is null in handleApplyTemplatesInstruction!");
+//    }
+
+    xctxt.popRTFContext();
+    }
+
+  /**
+   * This function is called during recomposition to
+   * control how this element is composed.
+   * @param root The root stylesheet for this transformation.
+   */
+  public void recompose(StylesheetRoot root)
+  {
+    root.recomposeTemplates(this);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemTemplateElement.java b/src/main/java/org/apache/xalan/templates/ElemTemplateElement.java
new file mode 100644
index 0000000..8a4cedf
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemTemplateElement.java
@@ -0,0 +1,1660 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemTemplateElement.java 475981 2006-11-16 23:35:53Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.UnImplNode;
+import org.apache.xpath.ExpressionNode;
+import org.apache.xpath.WhitespaceStrippingElementMatcher;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.xml.sax.helpers.NamespaceSupport;
+
+/**
+ * An instance of this class represents an element inside
+ * an xsl:template class.  It has a single "execute" method
+ * which is expected to perform the given action on the
+ * result tree.
+ * This class acts like a Element node, and implements the
+ * Element interface, but is not a full implementation
+ * of that interface... it only implements enough for
+ * basic traversal of the tree.
+ *
+ * @see Stylesheet
+ * @xsl.usage advanced
+ */
+public class ElemTemplateElement extends UnImplNode
+        implements PrefixResolver, Serializable, ExpressionNode, 
+                   WhitespaceStrippingElementMatcher, XSLTVisitable
+{
+    static final long serialVersionUID = 4440018597841834447L;
+
+  /**
+   * Construct a template element instance.
+   *
+   */
+  public ElemTemplateElement(){}
+
+  /**
+   * Tell if this template is a compiled template.
+   *
+   * @return Boolean flag indicating whether this is a compiled template   
+   */
+  public boolean isCompiledTemplate()
+  {
+    return false;
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_UNDEFINED;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return An invalid node name
+   */
+  public String getNodeName()
+  {
+    return "Unknown XSLT Element";
+  }
+  
+  /**
+   * For now, just return the result of getNodeName(), which 
+   * the local name.
+   *
+   * @return The result of getNodeName().
+   */
+  public String getLocalName()
+  {
+
+    return getNodeName();
+  }
+
+
+  /**
+   * This function will be called on top-level elements
+   * only, just before the transform begins.
+   *
+   * @param transformer The XSLT TransformerFactory.
+   *
+   * @throws TransformerException
+   */
+  public void runtimeInit(TransformerImpl transformer) throws TransformerException{}
+
+  /**
+   * Execute the element's primary function.  Subclasses of this
+   * function may recursivly execute down the element tree.
+   *
+   * @param transformer The XSLT TransformerFactory.
+   * 
+   * @throws TransformerException if any checked exception occurs.
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException{}
+
+  /**
+   * Get the owning "composed" stylesheet.  This looks up the
+   * inheritance chain until it calls getStylesheetComposed
+   * on a Stylesheet object, which will Get the owning
+   * aggregated stylesheet, or that stylesheet if it is aggregated.
+   *
+   * @return the owning "composed" stylesheet.
+   */
+  public StylesheetComposed getStylesheetComposed()
+  {
+    return m_parentNode.getStylesheetComposed();
+  }
+
+  /**
+   * Get the owning stylesheet.  This looks up the
+   * inheritance chain until it calls getStylesheet
+   * on a Stylesheet object, which will return itself.
+   *
+   * @return the owning stylesheet
+   */
+  public Stylesheet getStylesheet()
+  {
+    return (null==m_parentNode) ? null : m_parentNode.getStylesheet();
+  }
+
+  /**
+   * Get the owning root stylesheet.  This looks up the
+   * inheritance chain until it calls StylesheetRoot
+   * on a Stylesheet object, which will return a reference
+   * to the root stylesheet.
+   *
+   * @return the owning root stylesheet
+   */
+  public StylesheetRoot getStylesheetRoot()
+  {
+    return m_parentNode.getStylesheetRoot();
+  }
+
+  /**
+   * This function is called during recomposition to
+   * control how this element is composed.
+   */
+  public void recompose(StylesheetRoot root) throws TransformerException
+  {
+  }
+
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    resolvePrefixTables();
+    ElemTemplateElement t = getFirstChildElem();
+    m_hasTextLitOnly = ((t != null) 
+              && (t.getXSLToken() == Constants.ELEMNAME_TEXTLITERALRESULT) 
+              && (t.getNextSiblingElem() == null));
+              
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    cstate.pushStackMark();
+  }
+  
+  /**
+   * This after the template's children have been composed.
+   */
+  public void endCompose(StylesheetRoot sroot) throws TransformerException
+  {
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    cstate.popStackMark();
+  }
+
+  /**
+   * Throw a template element runtime error.  (Note: should we throw a TransformerException instead?)
+   *
+   * @param msg key of the error that occured.
+   * @param args Arguments to be used in the message
+   */
+  public void error(String msg, Object[] args)
+  {
+
+    String themsg = XSLMessages.createMessage(msg, args);
+
+    throw new RuntimeException(XSLMessages.createMessage(
+                                    XSLTErrorResources.ER_ELEMTEMPLATEELEM_ERR,
+                                    new Object[]{ themsg }));
+  }
+  
+  /*
+   * Throw an error.
+   *
+   * @param msg Message key for the error
+   *
+   */
+  public void error(String msg)
+  {
+    error(msg, null);
+  }
+  
+
+  // Implemented DOM Element methods.
+  /**
+   * Add a child to the child list.
+   * NOTE: This presumes the child did not previously have a parent.
+   * Making that assumption makes this a less expensive operation -- but
+   * requires that if you *do* want to reparent a node, you use removeChild()
+   * first to remove it from its previous context. Failing to do so will
+   * damage the tree.
+   *
+   * @param newChild Child to be added to child list
+   *
+   * @return Child just added to the child list
+   * @throws DOMException
+   */
+  public Node appendChild(Node newChild) throws DOMException
+  {
+
+    if (null == newChild)
+    {
+      error(XSLTErrorResources.ER_NULL_CHILD, null);  //"Trying to add a null child!");
+    }
+
+    ElemTemplateElement elem = (ElemTemplateElement) newChild;
+
+    if (null == m_firstChild)
+    {
+      m_firstChild = elem;
+    }
+    else
+    {
+      ElemTemplateElement last = (ElemTemplateElement) getLastChild();
+
+      last.m_nextSibling = elem;
+    }
+
+    elem.m_parentNode = this;
+
+    return newChild;
+  }
+
+  /**
+   * Add a child to the child list.
+   * NOTE: This presumes the child did not previously have a parent.
+   * Making that assumption makes this a less expensive operation -- but
+   * requires that if you *do* want to reparent a node, you use removeChild()
+   * first to remove it from its previous context. Failing to do so will
+   * damage the tree.
+   *
+   * @param elem Child to be added to child list
+   *
+   * @return Child just added to the child list
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement elem)
+  {
+
+    if (null == elem)
+    {
+      error(XSLTErrorResources.ER_NULL_CHILD, null);  //"Trying to add a null child!");
+    }
+
+    if (null == m_firstChild)
+    {
+      m_firstChild = elem;
+    }
+    else
+    {
+      ElemTemplateElement last = getLastChildElem();
+
+      last.m_nextSibling = elem;
+    }
+
+    elem.setParentElem(this);
+
+    return elem;
+  }
+
+
+  /**
+   * Tell if there are child nodes.
+   *
+   * @return True if there are child nodes
+   */
+  public boolean hasChildNodes()
+  {
+    return (null != m_firstChild);
+  }
+
+  /**
+   * Get the type of the node.
+   *
+   * @return Constant for this node type
+   */
+  public short getNodeType()
+  {
+    return org.w3c.dom.Node.ELEMENT_NODE;
+  }
+
+  /**
+   * Return the nodelist (same reference).
+   *
+   * @return The nodelist containing the child nodes (this)
+   */
+  public NodeList getChildNodes()
+  {
+    return this;
+  }
+
+  /**
+   * Remove a child.
+   * ADDED 9/8/200 to support compilation.
+   * TODO: ***** Alternative is "removeMe() from my parent if any"
+   * ... which is less well checked, but more convenient in some cases.
+   * Given that we assume only experts are calling this class, it might
+   * be preferable. It's less DOMish, though.
+   * 
+   * @param childETE The child to remove. This operation is a no-op
+   * if oldChild is not a child of this node.
+   *
+   * @return the removed child, or null if the specified
+   * node was not a child of this element.
+   */
+  public ElemTemplateElement removeChild(ElemTemplateElement childETE)
+  {
+
+    if (childETE == null || childETE.m_parentNode != this)
+      return null;
+
+    // Pointers to the child
+    if (childETE == m_firstChild)
+      m_firstChild = childETE.m_nextSibling;
+    else
+    {
+      ElemTemplateElement prev = childETE.getPreviousSiblingElem();
+
+      prev.m_nextSibling = childETE.m_nextSibling;
+    }
+
+    // Pointers from the child
+    childETE.m_parentNode = null;
+    childETE.m_nextSibling = null;
+
+    return childETE;
+  }
+
+  /**
+   * Replace the old child with a new child.
+   *
+   * @param newChild New child to replace with
+   * @param oldChild Old child to be replaced
+   *
+   * @return The new child
+   *
+   * @throws DOMException
+   */
+  public Node replaceChild(Node newChild, Node oldChild) throws DOMException
+  {
+
+    if (oldChild == null || oldChild.getParentNode() != this)
+      return null;
+
+    ElemTemplateElement newChildElem = ((ElemTemplateElement) newChild);
+    ElemTemplateElement oldChildElem = ((ElemTemplateElement) oldChild);
+
+    // Fix up previous sibling.
+    ElemTemplateElement prev =
+      (ElemTemplateElement) oldChildElem.getPreviousSibling();
+
+    if (null != prev)
+      prev.m_nextSibling = newChildElem;
+
+    // Fix up parent (this)
+    if (m_firstChild == oldChildElem)
+      m_firstChild = newChildElem;
+
+    newChildElem.m_parentNode = this;
+    oldChildElem.m_parentNode = null;
+    newChildElem.m_nextSibling = oldChildElem.m_nextSibling;
+    oldChildElem.m_nextSibling = null;
+
+    // newChildElem.m_stylesheet = oldChildElem.m_stylesheet;
+    // oldChildElem.m_stylesheet = null;
+    return newChildElem;
+  }
+  
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param newChild New child node to insert
+   * @param refChild Insert in front of this child
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Node insertBefore(Node newChild, Node refChild) throws DOMException
+  {
+  	if(null == refChild)
+  	{
+  		appendChild(newChild);
+  		return newChild;
+  	}
+  	
+  	if(newChild == refChild)
+  	{
+  		// hmm...
+  		return newChild;
+  	}
+
+    Node node = m_firstChild; 
+    Node prev = null;  
+    boolean foundit = false;
+    
+    while (null != node)
+    {
+    	// If the newChild is already in the tree, it is first removed.
+    	if(newChild == node)
+    	{
+    		if(null != prev)
+    			((ElemTemplateElement)prev).m_nextSibling = 
+    				(ElemTemplateElement)node.getNextSibling();
+    		else
+    			m_firstChild = (ElemTemplateElement)node.getNextSibling();
+    		node = node.getNextSibling();
+    		continue; // prev remains the same.
+    	}
+    	if(refChild == node)
+    	{
+    		if(null != prev)
+    		{
+    			((ElemTemplateElement)prev).m_nextSibling = (ElemTemplateElement)newChild;
+    		}
+    		else
+    		{
+    			m_firstChild = (ElemTemplateElement)newChild;
+    		}
+    		((ElemTemplateElement)newChild).m_nextSibling = (ElemTemplateElement)refChild;
+    		((ElemTemplateElement)newChild).setParentElem(this);
+    		prev = newChild;
+    		node = node.getNextSibling();
+    		foundit = true;
+    		continue;
+    	}
+    	prev = node;
+    	node = node.getNextSibling();
+    }
+    
+    if(!foundit)
+    	throw new DOMException(DOMException.NOT_FOUND_ERR, 
+    		"refChild was not found in insertBefore method!");
+    else
+    	return newChild;
+  }
+
+
+  /**
+   * Replace the old child with a new child.
+   *
+   * @param newChildElem New child to replace with
+   * @param oldChildElem Old child to be replaced
+   *
+   * @return The new child
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement replaceChild(ElemTemplateElement newChildElem, 
+                                          ElemTemplateElement oldChildElem)
+  {
+
+    if (oldChildElem == null || oldChildElem.getParentElem() != this)
+      return null;
+
+    // Fix up previous sibling.
+    ElemTemplateElement prev =
+      oldChildElem.getPreviousSiblingElem();
+
+    if (null != prev)
+      prev.m_nextSibling = newChildElem;
+
+    // Fix up parent (this)
+    if (m_firstChild == oldChildElem)
+      m_firstChild = newChildElem;
+
+    newChildElem.m_parentNode = this;
+    oldChildElem.m_parentNode = null;
+    newChildElem.m_nextSibling = oldChildElem.m_nextSibling;
+    oldChildElem.m_nextSibling = null;
+
+    // newChildElem.m_stylesheet = oldChildElem.m_stylesheet;
+    // oldChildElem.m_stylesheet = null;
+    return newChildElem;
+  }
+
+  /**
+   * NodeList method: Count the immediate children of this node
+   *
+   * @return The count of children of this node
+   */
+  public int getLength()
+  {
+
+    // It is assumed that the getChildNodes call synchronized
+    // the children. Therefore, we can access the first child
+    // reference directly.
+    int count = 0;
+
+    for (ElemTemplateElement node = m_firstChild; node != null;
+            node = node.m_nextSibling)
+    {
+      count++;
+    }
+
+    return count;
+  }  // getLength():int
+
+  /**
+   * NodeList method: Return the Nth immediate child of this node, or
+   * null if the index is out of bounds.
+   *
+   * @param index Index of child to find
+   * @return org.w3c.dom.Node: the child node at given index
+   */
+  public Node item(int index)
+  {
+
+    // It is assumed that the getChildNodes call synchronized
+    // the children. Therefore, we can access the first child
+    // reference directly.
+    ElemTemplateElement node = m_firstChild;
+
+    for (int i = 0; i < index && node != null; i++)
+    {
+      node = node.m_nextSibling;
+    }
+
+    return node;
+  }  // item(int):Node
+
+  /**
+   * Get the stylesheet owner.
+   *
+   * @return The stylesheet owner
+   */
+  public Document getOwnerDocument()
+  {
+    return getStylesheet();
+  }
+  
+  /**
+   * Get the owning xsl:template element.
+   *
+   * @return The owning xsl:template element, this element if it is a xsl:template, or null if not found.
+   */
+  public ElemTemplate getOwnerXSLTemplate()
+  {
+  	ElemTemplateElement el = this;
+  	int type = el.getXSLToken();
+  	while((null != el) && (type != Constants.ELEMNAME_TEMPLATE))
+  	{
+    	el = el.getParentElem();
+    	if(null != el)
+  			type = el.getXSLToken();
+  	}
+  	return (ElemTemplate)el;
+  }
+
+
+  /**
+   * Return the element name.
+   *
+   * @return The element name
+   */
+  public String getTagName()
+  {
+    return getNodeName();
+  }
+  
+  /**
+   * Tell if this element only has one text child, for optimization purposes.
+   * @return true of this element only has one text literal child.
+   */
+  public boolean hasTextLitOnly()
+  {
+    return m_hasTextLitOnly;
+  }
+
+  /**
+   * Return the base identifier.
+   *
+   * @return The base identifier 
+   */
+  public String getBaseIdentifier()
+  {
+
+    // Should this always be absolute?
+    return this.getSystemId();
+  }
+
+  /** line number where the current document event ends.
+   *  @serial         */
+  private int m_lineNumber;
+
+  /** line number where the current document event ends.
+   *  @serial         */
+  private int m_endLineNumber;
+
+  /**
+   * Return the line number where the current document event ends.
+   * Note that this is the line position of the first character
+   * after the text associated with the document event.
+   * @return The line number, or -1 if none is available.
+   * @see #getColumnNumber
+   */
+  public int getEndLineNumber()
+  {
+	return m_endLineNumber;
+  }
+
+  /**
+   * Return the line number where the current document event ends.
+   * Note that this is the line position of the first character
+   * after the text associated with the document event.
+   * @return The line number, or -1 if none is available.
+   * @see #getColumnNumber
+   */
+  public int getLineNumber()
+  {
+    return m_lineNumber;
+  }
+
+  /** the column number where the current document event ends.
+   *  @serial        */
+  private int m_columnNumber;
+
+  /** the column number where the current document event ends.
+   *  @serial        */
+  private int m_endColumnNumber;
+
+  /**
+   * Return the column number where the current document event ends.
+   * Note that this is the column number of the first
+   * character after the text associated with the document
+   * event.  The first column in a line is position 1.
+   * @return The column number, or -1 if none is available.
+   * @see #getLineNumber
+   */
+  public int getEndColumnNumber()
+  {
+	return m_endColumnNumber;
+  }
+
+  /**
+   * Return the column number where the current document event ends.
+   * Note that this is the column number of the first
+   * character after the text associated with the document
+   * event.  The first column in a line is position 1.
+   * @return The column number, or -1 if none is available.
+   * @see #getLineNumber
+   */
+  public int getColumnNumber()
+  {
+    return m_columnNumber;
+  }
+
+  /**
+   * Return the public identifier for the current document event.
+   * <p>This will be the public identifier
+   * @return A string containing the public identifier, or
+   *         null if none is available.
+   * @see #getSystemId
+   */
+  public String getPublicId()
+  {
+    return (null != m_parentNode) ? m_parentNode.getPublicId() : null;
+  }
+
+  /**
+   * Return the system identifier for the current document event.
+   *
+   * <p>If the system identifier is a URL, the parser must resolve it
+   * fully before passing it to the application.</p>
+   *
+   * @return A string containing the system identifier, or null
+   *         if none is available.
+   * @see #getPublicId
+   */
+  public String getSystemId()
+  {
+    Stylesheet sheet=getStylesheet();
+    return (sheet==null) ? null : sheet.getHref();
+  }
+
+  /**
+   * Set the location information for this element.
+   *
+   * @param locator Source Locator with location information for this element
+   */
+  public void setLocaterInfo(SourceLocator locator)
+  {
+    m_lineNumber = locator.getLineNumber();
+    m_columnNumber = locator.getColumnNumber();
+  }
+  
+  /**
+   * Set the end location information for this element.
+   *
+   * @param locator Source Locator with location information for this element
+   */
+  public void setEndLocaterInfo(SourceLocator locator)
+  {
+	m_endLineNumber = locator.getLineNumber();
+	m_endColumnNumber = locator.getColumnNumber();
+  } 
+
+  /**
+   * Tell if this element has the default space handling
+   * turned off or on according to the xml:space attribute.
+   * @serial
+   */
+  private boolean m_defaultSpace = true;
+
+  /**
+   * Tell if this element only has one text child, for optimization purposes.
+   * @serial
+   */
+  private boolean m_hasTextLitOnly = false;
+
+  /**
+   * Tell if this element only has one text child, for optimization purposes.
+   * @serial
+   */
+  protected boolean m_hasVariableDecl = false;
+  
+  public boolean hasVariableDecl()
+  {
+    return m_hasVariableDecl;
+  }
+
+  /**
+   * Set the "xml:space" attribute.
+   * A text node is preserved if an ancestor element of the text node
+   * has an xml:space attribute with a value of preserve, and
+   * no closer ancestor element has xml:space with a value of default.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text in XSLT Specification</a>
+   *
+   * @param v  Enumerated value, either Constants.ATTRVAL_PRESERVE 
+   * or Constants.ATTRVAL_STRIP.
+   */
+  public void setXmlSpace(int v)
+  {
+    m_defaultSpace = ((Constants.ATTRVAL_STRIP == v) ? true : false);
+  }
+
+  /**
+   * Get the "xml:space" attribute.
+   * A text node is preserved if an ancestor element of the text node
+   * has an xml:space attribute with a value of preserve, and
+   * no closer ancestor element has xml:space with a value of default.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text in XSLT Specification</a>
+   *
+   * @return The value of the xml:space attribute
+   */
+  public boolean getXmlSpace()
+  {
+    return m_defaultSpace;
+  }
+
+  /**
+   * The list of namespace declarations for this element only.
+   * @serial
+   */
+  private List m_declaredPrefixes;
+
+  /**
+   * Return a table that contains all prefixes available
+   * within this element context.
+   *
+   * @return Vector containing the prefixes available within this
+   * element context 
+   */
+  public List getDeclaredPrefixes()
+  {
+    return m_declaredPrefixes;
+  }
+
+  /**
+   * From the SAX2 helper class, set the namespace table for
+   * this element.  Take care to call resolveInheritedNamespaceDecls.
+   * after all namespace declarations have been added.
+   *
+   * @param nsSupport non-null reference to NamespaceSupport from 
+   * the ContentHandler.
+   *
+   * @throws TransformerException
+   */
+  public void setPrefixes(NamespaceSupport nsSupport) throws TransformerException
+  {
+    setPrefixes(nsSupport, false);
+  }
+
+  /**
+   * Copy the namespace declarations from the NamespaceSupport object.  
+   * Take care to call resolveInheritedNamespaceDecls.
+   * after all namespace declarations have been added.
+   *
+   * @param nsSupport non-null reference to NamespaceSupport from 
+   * the ContentHandler.
+   * @param excludeXSLDecl true if XSLT namespaces should be ignored.
+   *
+   * @throws TransformerException
+   */
+  public void setPrefixes(NamespaceSupport nsSupport, boolean excludeXSLDecl)
+          throws TransformerException
+  {
+
+    Enumeration decls = nsSupport.getDeclaredPrefixes();
+
+    while (decls.hasMoreElements())
+    {
+      String prefix = (String) decls.nextElement();
+
+      if (null == m_declaredPrefixes)
+        m_declaredPrefixes = new ArrayList();
+
+      String uri = nsSupport.getURI(prefix);
+
+      if (excludeXSLDecl && uri.equals(Constants.S_XSLNAMESPACEURL))
+        continue;
+
+      // System.out.println("setPrefixes - "+prefix+", "+uri);
+      XMLNSDecl decl = new XMLNSDecl(prefix, uri, false);
+
+      m_declaredPrefixes.add(decl);
+    }
+  }
+
+  /**
+   * Fullfill the PrefixResolver interface.  Calling this for this class 
+   * will throw an error.
+   *
+   * @param prefix The prefix to look up, which may be an empty string ("") 
+   *               for the default Namespace.
+   * @param context The node context from which to look up the URI.
+   *
+   * @return null if the error listener does not choose to throw an exception.
+   */
+  public String getNamespaceForPrefix(String prefix, org.w3c.dom.Node context)
+  {
+    this.error(XSLTErrorResources.ER_CANT_RESOLVE_NSPREFIX, null);
+
+    return null;
+  }
+
+  /**
+   * Given a namespace, get the corrisponding prefix.
+   * 9/15/00: This had been iteratively examining the m_declaredPrefixes
+   * field for this node and its parents. That makes life difficult for
+   * the compilation experiment, which doesn't have a static vector of
+   * local declarations. Replaced a recursive solution, which permits
+   * easier subclassing/overriding.
+   *
+   * @param prefix non-null reference to prefix string, which should map 
+   *               to a namespace URL.
+   *
+   * @return The namespace URL that the prefix maps to, or null if no 
+   *         mapping can be found.
+   */
+  public String getNamespaceForPrefix(String prefix)
+  {
+//    if (null != prefix && prefix.equals("xmlns"))
+//    {
+//      return Constants.S_XMLNAMESPACEURI;
+//    }
+
+    List nsDecls = m_declaredPrefixes;
+
+    if (null != nsDecls)
+    {
+      int n = nsDecls.size();
+      if(prefix.equals(Constants.ATTRVAL_DEFAULT_PREFIX))
+      {
+        prefix = "";
+      }
+
+      for (int i = 0; i < n; i++)
+      {
+        XMLNSDecl decl = (XMLNSDecl) nsDecls.get(i);
+
+        if (prefix.equals(decl.getPrefix()))
+          return decl.getURI();
+      }
+    }
+
+    // Not found; ask our ancestors
+    if (null != m_parentNode)
+      return m_parentNode.getNamespaceForPrefix(prefix);
+
+    // JJK: No ancestors; try implicit
+    // %REVIEW% Are there literals somewhere that we should use instead?
+    // %REVIEW% Is this really the best place to patch?
+    if("xml".equals(prefix))
+      return "http://www.w3.org/XML/1998/namespace";
+
+    // No parent, so no definition
+    return null;
+  }
+
+  /**
+   * The table of {@link XMLNSDecl}s for this element
+   * and all parent elements, screened for excluded prefixes.
+   * @serial
+   */
+  private List m_prefixTable;
+
+  /**
+   * Return a table that contains all prefixes available
+   * within this element context.
+   *
+   * @return reference to vector of {@link XMLNSDecl}s, which may be null.
+   */
+  List getPrefixTable()
+  {
+    return m_prefixTable;
+  }
+  
+  void setPrefixTable(List list) {
+      m_prefixTable = list;
+  }
+  
+  /**
+   * Get whether or not the passed URL is contained flagged by
+   * the "extension-element-prefixes" property.  This method is overridden 
+   * by {@link ElemLiteralResult#containsExcludeResultPrefix}.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @param prefix non-null reference to prefix that might be excluded.
+   *
+   * @return true if the prefix should normally be excluded.
+   */
+  public boolean containsExcludeResultPrefix(String prefix, String uri)
+  {
+    ElemTemplateElement parent = this.getParentElem();
+    if(null != parent)
+      return parent.containsExcludeResultPrefix(prefix, uri);
+      
+    return false;
+  }
+
+  /**
+   * Tell if the result namespace decl should be excluded.  Should be called before
+   * namespace aliasing (I think).
+   *
+   * @param prefix non-null reference to prefix.
+   * @param uri reference to namespace that prefix maps to, which is protected 
+   *            for null, but should really never be passed as null.
+   *
+   * @return true if the given namespace should be excluded.
+   *
+   * @throws TransformerException
+   */
+  private boolean excludeResultNSDecl(String prefix, String uri)
+          throws TransformerException
+  {
+
+    if (uri != null)
+    {
+      if (uri.equals(Constants.S_XSLNAMESPACEURL)
+              || getStylesheet().containsExtensionElementURI(uri))
+        return true;
+
+      if (containsExcludeResultPrefix(prefix, uri))
+        return true;
+    }
+
+    return false;
+  }
+  
+  /**
+   * Combine the parent's namespaces with this namespace
+   * for fast processing, taking care to reference the
+   * parent's namespace if this namespace adds nothing new.
+   * (Recursive method, walking the elements depth-first,
+   * processing parents before children).
+   * Note that this method builds m_prefixTable with aliased 
+   * namespaces, *not* the original namespaces.
+   *
+   * @throws TransformerException
+   */
+  public void resolvePrefixTables() throws TransformerException
+  {
+    // Always start with a fresh prefix table!
+    setPrefixTable(null);
+
+    // If we have declared declarations, then we look for 
+    // a parent that has namespace decls, and add them 
+    // to this element's decls.  Otherwise we just point 
+    // to the parent that has decls.
+    if (null != this.m_declaredPrefixes)
+    {
+      StylesheetRoot stylesheet = this.getStylesheetRoot();
+      
+      // Add this element's declared prefixes to the 
+      // prefix table.
+      int n = m_declaredPrefixes.size();
+
+      for (int i = 0; i < n; i++)
+      {
+        XMLNSDecl decl = (XMLNSDecl) m_declaredPrefixes.get(i);
+        String prefix = decl.getPrefix();
+        String uri = decl.getURI();
+        if(null == uri)
+          uri = "";
+        boolean shouldExclude = excludeResultNSDecl(prefix, uri);
+
+        // Create a new prefix table if one has not already been created.
+        if (null == m_prefixTable)
+            setPrefixTable(new ArrayList());
+
+        NamespaceAlias nsAlias = stylesheet.getNamespaceAliasComposed(uri);
+        if(null != nsAlias)
+        {
+          // Should I leave the non-aliased element in the table as 
+          // an excluded element?
+          
+          // The exclusion should apply to the non-aliased prefix, so 
+          // we don't calculate it here.  -sb
+          // Use stylesheet prefix, as per xsl WG
+          decl = new XMLNSDecl(nsAlias.getStylesheetPrefix(), 
+                              nsAlias.getResultNamespace(), shouldExclude);
+        }
+        else
+          decl = new XMLNSDecl(prefix, uri, shouldExclude);
+
+        m_prefixTable.add(decl);
+        
+      }
+    }
+
+    ElemTemplateElement parent = this.getParentNodeElem();
+
+    if (null != parent)
+    {
+
+      // The prefix table of the parent should never be null!
+      List prefixes = parent.m_prefixTable;
+
+      if (null == m_prefixTable && !needToCheckExclude())
+      {
+
+        // Nothing to combine, so just use parent's table!
+        setPrefixTable(parent.m_prefixTable);
+      }
+      else
+      {
+
+        // Add the prefixes from the parent's prefix table.
+        int n = prefixes.size();
+        
+        for (int i = 0; i < n; i++)
+        {
+          XMLNSDecl decl = (XMLNSDecl) prefixes.get(i);
+          boolean shouldExclude = excludeResultNSDecl(decl.getPrefix(),
+                                                      decl.getURI());
+
+          if (shouldExclude != decl.getIsExcluded())
+          {
+            decl = new XMLNSDecl(decl.getPrefix(), decl.getURI(),
+                                 shouldExclude);
+          }
+          
+          //m_prefixTable.addElement(decl);
+          addOrReplaceDecls(decl);
+        }
+      }
+    }
+    else if (null == m_prefixTable)
+    {
+
+      // Must be stylesheet element without any result prefixes!
+      setPrefixTable(new ArrayList());
+    }
+  }
+  
+  /**
+   * Add or replace this namespace declaration in list
+   * of namespaces in scope for this element.
+   *
+   * @param newDecl namespace declaration to add to list
+   */
+  void addOrReplaceDecls(XMLNSDecl newDecl)
+  {
+      int n = m_prefixTable.size();
+
+        for (int i = n - 1; i >= 0; i--)
+        {
+          XMLNSDecl decl = (XMLNSDecl) m_prefixTable.get(i);
+
+          if (decl.getPrefix().equals(newDecl.getPrefix()))
+          {
+            return;
+          }
+        }
+      m_prefixTable.add(newDecl);    
+    
+  }
+  
+  /**
+   * Return whether we need to check namespace prefixes 
+   * against and exclude result prefixes list.
+   */
+  boolean needToCheckExclude()
+  {
+    return false;    
+  } 
+
+  /**
+   * Send startPrefixMapping events to the result tree handler
+   * for all declared prefix mappings in the stylesheet.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  void executeNSDecls(TransformerImpl transformer) throws TransformerException
+  {
+       executeNSDecls(transformer, null);
+  }
+
+  /**
+   * Send startPrefixMapping events to the result tree handler
+   * for all declared prefix mappings in the stylesheet.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param ignorePrefix string prefix to not startPrefixMapping
+   *
+   * @throws TransformerException
+   */
+  void executeNSDecls(TransformerImpl transformer, String ignorePrefix) throws TransformerException
+  {  
+    try
+    {
+      if (null != m_prefixTable)
+      {
+        SerializationHandler rhandler = transformer.getResultTreeHandler();
+        int n = m_prefixTable.size();
+
+        for (int i = n - 1; i >= 0; i--)
+        {
+          XMLNSDecl decl = (XMLNSDecl) m_prefixTable.get(i);
+
+          if (!decl.getIsExcluded() && !(null != ignorePrefix && decl.getPrefix().equals(ignorePrefix)))
+          {
+            rhandler.startPrefixMapping(decl.getPrefix(), decl.getURI(), true);
+          }
+        }
+      }
+    }
+    catch(org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+  }
+
+  /**
+   * Send endPrefixMapping events to the result tree handler
+   * for all declared prefix mappings in the stylesheet.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  void unexecuteNSDecls(TransformerImpl transformer) throws TransformerException
+  {
+       unexecuteNSDecls(transformer, null);
+  }
+
+  /**
+   * Send endPrefixMapping events to the result tree handler
+   * for all declared prefix mappings in the stylesheet.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param ignorePrefix string prefix to not endPrefixMapping
+   * 
+   * @throws TransformerException
+   */
+  void unexecuteNSDecls(TransformerImpl transformer, String ignorePrefix) throws TransformerException
+  {
+ 
+    try
+    {
+      if (null != m_prefixTable)
+      {
+        SerializationHandler rhandler = transformer.getResultTreeHandler();
+        int n = m_prefixTable.size();
+
+        for (int i = 0; i < n; i++)
+        {
+          XMLNSDecl decl = (XMLNSDecl) m_prefixTable.get(i);
+
+          if (!decl.getIsExcluded() && !(null != ignorePrefix && decl.getPrefix().equals(ignorePrefix)))
+          {
+            rhandler.endPrefixMapping(decl.getPrefix());
+          }
+        }
+      }
+    }
+    catch(org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+  }
+  
+  /** The *relative* document order number of this element.
+   *  @serial */
+  protected int m_docOrderNumber = -1;
+  
+  /**
+   * Set the UID (document order index).
+   *
+   * @param i Index of this child.
+   */
+  public void setUid(int i)
+  {
+    m_docOrderNumber = i;
+  }
+
+  /**
+   * Get the UID (document order index).
+   *
+   * @return Index of this child
+   */
+  public int getUid()
+  {
+    return m_docOrderNumber;
+  }
+
+
+  /**
+   * Parent node.
+   * @serial
+   */
+  protected ElemTemplateElement m_parentNode;
+
+  /**
+   * Get the parent as a Node.
+   *
+   * @return This node's parent node
+   */
+  public Node getParentNode()
+  {
+    return m_parentNode;
+  }
+
+  /**
+   * Get the parent as an ElemTemplateElement.
+   *
+   * @return This node's parent as an ElemTemplateElement
+   */
+  public ElemTemplateElement getParentElem()
+  {
+    return m_parentNode;
+  }
+
+  /**
+   * Set the parent as an ElemTemplateElement.
+   *
+   * @param p This node's parent as an ElemTemplateElement
+   */
+  public void setParentElem(ElemTemplateElement p)
+  {
+    m_parentNode = p;
+  }
+
+  /**
+   * Next sibling.
+   * @serial
+   */
+  ElemTemplateElement m_nextSibling;
+
+  /**
+   * Get the next sibling (as a Node) or return null.
+   *
+   * @return this node's next sibling or null
+   */
+  public Node getNextSibling()
+  {
+    return m_nextSibling;
+  }
+
+  /**
+   * Get the previous sibling (as a Node) or return null.
+   * Note that this may be expensive if the parent has many kids;
+   * we accept that price in exchange for avoiding the prev pointer
+   * TODO: If we were sure parents and sibs are always ElemTemplateElements,
+   * we could hit the fields directly rather than thru accessors.
+   *
+   * @return This node's previous sibling or null
+   */
+  public Node getPreviousSibling()
+  {
+
+    Node walker = getParentNode(), prev = null;
+
+    if (walker != null)
+      for (walker = walker.getFirstChild(); walker != null;
+              prev = walker, walker = walker.getNextSibling())
+      {
+        if (walker == this)
+          return prev;
+      }
+
+    return null;
+  }
+
+  /**
+   * Get the previous sibling (as a Node) or return null.
+   * Note that this may be expensive if the parent has many kids;
+   * we accept that price in exchange for avoiding the prev pointer
+   * TODO: If we were sure parents and sibs are always ElemTemplateElements,
+   * we could hit the fields directly rather than thru accessors.
+   *
+   * @return This node's previous sibling or null
+   */
+  public ElemTemplateElement getPreviousSiblingElem()
+  {
+
+    ElemTemplateElement walker = getParentNodeElem();
+    ElemTemplateElement prev = null;
+
+    if (walker != null)
+      for (walker = walker.getFirstChildElem(); walker != null;
+              prev = walker, walker = walker.getNextSiblingElem())
+      {
+        if (walker == this)
+          return prev;
+      }
+
+    return null;
+  }
+
+
+  /**
+   * Get the next sibling (as a ElemTemplateElement) or return null.
+   *
+   * @return This node's next sibling (as a ElemTemplateElement) or null 
+   */
+  public ElemTemplateElement getNextSiblingElem()
+  {
+    return m_nextSibling;
+  }
+  
+  /**
+   * Get the parent element.
+   *
+   * @return This node's next parent (as a ElemTemplateElement) or null 
+   */
+  public ElemTemplateElement getParentNodeElem()
+  {
+    return m_parentNode;
+  }
+
+
+  /**
+   * First child.
+   * @serial
+   */
+  ElemTemplateElement m_firstChild;
+
+  /**
+   * Get the first child as a Node.
+   *
+   * @return This node's first child or null
+   */
+  public Node getFirstChild()
+  {
+    return m_firstChild;
+  }
+
+  /**
+   * Get the first child as a ElemTemplateElement.
+   *
+   * @return This node's first child (as a ElemTemplateElement) or null
+   */
+  public ElemTemplateElement getFirstChildElem()
+  {
+    return m_firstChild;
+  }
+
+  /**
+   * Get the last child.
+   *
+   * @return This node's last child
+   */
+  public Node getLastChild()
+  {
+
+    ElemTemplateElement lastChild = null;
+
+    for (ElemTemplateElement node = m_firstChild; node != null;
+            node = node.m_nextSibling)
+    {
+      lastChild = node;
+    }
+
+    return lastChild;
+  }
+
+  /**
+   * Get the last child.
+   *
+   * @return This node's last child
+   */
+  public ElemTemplateElement getLastChildElem()
+  {
+
+    ElemTemplateElement lastChild = null;
+
+    for (ElemTemplateElement node = m_firstChild; node != null;
+            node = node.m_nextSibling)
+    {
+      lastChild = node;
+    }
+
+    return lastChild;
+  }
+
+
+  /** DOM backpointer that this element originated from.          */
+  transient private org.w3c.dom.Node m_DOMBackPointer;
+
+  /**
+   * If this stylesheet was created from a DOM, get the
+   * DOM backpointer that this element originated from.
+   * For tooling use.
+   *
+   * @return DOM backpointer that this element originated from or null.
+   */
+  public org.w3c.dom.Node getDOMBackPointer()
+  {
+    return m_DOMBackPointer;
+  }
+
+  /**
+   * If this stylesheet was created from a DOM, set the
+   * DOM backpointer that this element originated from.
+   * For tooling use.
+   *
+   * @param n DOM backpointer that this element originated from.
+   */
+  public void setDOMBackPointer(org.w3c.dom.Node n)
+  {
+    m_DOMBackPointer = n;
+  }
+
+  /**
+   * Compares this object with the specified object for precedence order.
+   * The order is determined by the getImportCountComposed() of the containing
+   * composed stylesheet and the getUid() of this element.
+   * Returns a negative integer, zero, or a positive integer as this
+   * object is less than, equal to, or greater than the specified object.
+   * 
+   * @param o The object to be compared to this object
+   * @return  a negative integer, zero, or a positive integer as this object is
+   *          less than, equal to, or greater than the specified object.
+   * @throws ClassCastException if the specified object's
+   *         type prevents it from being compared to this Object.
+   */
+  public int compareTo(Object o) throws ClassCastException {
+    
+    ElemTemplateElement ro = (ElemTemplateElement) o;
+    int roPrecedence = ro.getStylesheetComposed().getImportCountComposed();
+    int myPrecedence = this.getStylesheetComposed().getImportCountComposed();
+
+    if (myPrecedence < roPrecedence)
+      return -1;
+    else if (myPrecedence > roPrecedence)
+      return 1;
+    else
+      return this.getUid() - ro.getUid();
+  }
+  
+  /**
+   * Get information about whether or not an element should strip whitespace.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @param support The XPath runtime state.
+   * @param targetElement Element to check
+   *
+   * @return true if the whitespace should be stripped.
+   *
+   * @throws TransformerException
+   */
+  public boolean shouldStripWhiteSpace(
+          org.apache.xpath.XPathContext support, 
+          org.w3c.dom.Element targetElement) throws TransformerException
+  {
+    StylesheetRoot sroot = this.getStylesheetRoot();
+    return (null != sroot) ? sroot.shouldStripWhiteSpace(support, targetElement) :false;
+  }
+  
+  /**
+   * Get information about whether or not whitespace can be stripped.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @return true if the whitespace can be stripped.
+   */
+  public boolean canStripWhiteSpace()
+  {
+    StylesheetRoot sroot = this.getStylesheetRoot();
+    return (null != sroot) ? sroot.canStripWhiteSpace() : false;
+  }
+  
+  /**
+   * Tell if this element can accept variable declarations.
+   * @return true if the element can accept and process variable declarations.
+   */
+  public boolean canAcceptVariables()
+  {
+  	return true;
+  }
+  
+  //=============== ExpressionNode methods ================
+  
+  /** 
+   * Set the parent of this node.
+   * @param n Must be a ElemTemplateElement.
+   */
+  public void exprSetParent(ExpressionNode n)
+  {
+  	// This obviously requires that only a ElemTemplateElement can 
+  	// parent a node of this type.
+  	setParentElem((ElemTemplateElement)n);
+  }
+  
+  /**
+   * Get the ExpressionNode parent of this node.
+   */
+  public ExpressionNode exprGetParent()
+  {
+  	return getParentElem();
+  }
+
+  /** 
+   * This method tells the node to add its argument to the node's
+   * list of children. 
+   * @param n Must be a ElemTemplateElement. 
+   */
+  public void exprAddChild(ExpressionNode n, int i)
+  {
+  	appendChild((ElemTemplateElement)n);
+  }
+
+  /** This method returns a child node.  The children are numbered
+     from zero, left to right. */
+  public ExpressionNode exprGetChild(int i)
+  {
+  	return (ExpressionNode)item(i);
+  }
+
+  /** Return the number of children the node has. */
+  public int exprGetNumChildren()
+  {
+  	return getLength();
+  }
+  
+  /**
+   * Accept a visitor and call the appropriate method 
+   * for this class.
+   * 
+   * @param visitor The visitor whose appropriate method will be called.
+   * @return true if the children of the object should be visited.
+   */
+  protected boolean accept(XSLTVisitor visitor)
+  {
+  	return visitor.visitInstruction(this);
+  }
+
+  /**
+   * @see XSLTVisitable#callVisitors(XSLTVisitor)
+   */
+  public void callVisitors(XSLTVisitor visitor)
+  {
+  	if(accept(visitor))
+  	{
+		callChildVisitors(visitor);
+  	}
+  }
+
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor, boolean callAttributes)
+  {
+    for (ElemTemplateElement node = m_firstChild;
+      node != null;
+      node = node.m_nextSibling)
+      {
+      node.callVisitors(visitor);
+    }
+  }
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor)
+  {
+  	callChildVisitors(visitor, true);
+  }
+
+
+	/**
+	 * @see PrefixResolver#handlesNullPrefixes()
+	 */
+	public boolean handlesNullPrefixes() {
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemText.java b/src/main/java/org/apache/xalan/templates/ElemText.java
new file mode 100644
index 0000000..7dc22da
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemText.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemText.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xalan.res.XSLTErrorResources;
+
+/**
+ * Implement xsl:template.
+ * This primarily acts as a marker on the element
+ * stack to signal that whitespace should be preserved.
+ * <pre>
+ * <!ELEMENT xsl:text (#PCDATA)>
+ * <!ATTLIST xsl:text
+ *   disable-output-escaping (yes|no) "no"
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemText extends ElemTemplateElement
+{
+    static final long serialVersionUID = 1383140876182316711L;
+
+  /**
+   * Tells if this element should disable escaping.
+   * @serial
+   */
+  private boolean m_disableOutputEscaping = false;
+
+  /**
+   * Set the "disable-output-escaping" attribute.
+   * Normally, the xml output method escapes & and < (and
+   * possibly other characters) when outputting text nodes.
+   * This ensures that the output is well-formed XML. However,
+   * it is sometimes convenient to be able to produce output
+   * that is almost, but not quite well-formed XML; for
+   * example, the output may include ill-formed sections
+   * which are intended to be transformed into well-formed
+   * XML by a subsequent non-XML aware process. For this reason,
+   * XSLT provides a mechanism for disabling output escaping.
+   * An xsl:value-of or xsl:text element may have a
+   * disable-output-escaping attribute; the allowed values
+   * are yes or no; the default is no; if the value is yes,
+   * then a text node generated by instantiating the xsl:value-of
+   * or xsl:text element should be output without any escaping.
+   * @see <a href="http://www.w3.org/TR/xslt#disable-output-escaping">disable-output-escaping in XSLT Specification</a>
+   *
+   * @param v Boolean flag indicating whether this element should disable escaping
+   */
+  public void setDisableOutputEscaping(boolean v)
+  {
+    m_disableOutputEscaping = v;
+  }
+
+  /**
+   * Get the "disable-output-escaping" attribute.
+   * Normally, the xml output method escapes & and < (and
+   * possibly other characters) when outputting text nodes.
+   * This ensures that the output is well-formed XML. However,
+   * it is sometimes convenient to be able to produce output
+   * that is almost, but not quite well-formed XML; for
+   * example, the output may include ill-formed sections
+   * which are intended to be transformed into well-formed
+   * XML by a subsequent non-XML aware process. For this reason,
+   * XSLT provides a mechanism for disabling output escaping.
+   * An xsl:value-of or xsl:text element may have a
+   * disable-output-escaping attribute; the allowed values
+   * are yes or no; the default is no; if the value is yes,
+   * then a text node generated by instantiating the xsl:value-of
+   * or xsl:text element should be output without any escaping.
+   * @see <a href="http://www.w3.org/TR/xslt#disable-output-escaping">disable-output-escaping in XSLT Specification</a>
+   *
+   * @return Boolean flag indicating whether this element should disable escaping
+   */
+  public boolean getDisableOutputEscaping()
+  {
+    return m_disableOutputEscaping;
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_TEXT;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_TEXT_STRING;
+  }
+
+  /**
+   * Add a child to the child list.
+   *
+   * @param newChild Child to add to children list
+   *
+   * @return Child added to children list
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    int type = ((ElemTemplateElement) newChild).getXSLToken();
+
+    switch (type)
+    {
+    case Constants.ELEMNAME_TEXTLITERALRESULT :
+      break;
+    default :
+      error(XSLTErrorResources.ER_CANNOT_ADD,
+            new Object[]{ newChild.getNodeName(),
+                          this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    }
+
+    return super.appendChild(newChild);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemTextLiteral.java b/src/main/java/org/apache/xalan/templates/ElemTextLiteral.java
new file mode 100644
index 0000000..1d6f5e1
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemTextLiteral.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemTextLiteral.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.serializer.SerializationHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Implement a text literal.
+ * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemTextLiteral extends ElemTemplateElement
+{
+    static final long serialVersionUID = -7872620006767660088L;
+
+  /**
+   * Tell if space should be preserved.
+   * @serial
+   */
+  private boolean m_preserveSpace;
+
+  /**
+   * Set whether or not space should be preserved.
+   *
+   * @param v Boolean flag indicating whether 
+   * or not space should be preserved
+   */
+  public void setPreserveSpace(boolean v)
+  {
+    m_preserveSpace = v;
+  }
+
+  /**
+   * Get whether or not space should be preserved.
+   *
+   * @return Boolean flag indicating whether 
+   * or not space should be preserved 
+   */
+  public boolean getPreserveSpace()
+  {
+    return m_preserveSpace;
+  }
+
+  /**
+   * The character array.
+   * @serial
+   */
+  private char m_ch[];
+  
+  /**
+   * The character array as a string.
+   * @serial
+   */
+  private String m_str;
+
+  /**
+   * Set the characters that will be output to the result tree..
+   *
+   * @param v Array of characters that will be output to the result tree 
+   */
+  public void setChars(char[] v)
+  {
+    m_ch = v;
+  }
+
+  /**
+   * Get the characters that will be output to the result tree..
+   *
+   * @return Array of characters that will be output to the result tree
+   */
+  public char[] getChars()
+  {
+    return m_ch;
+  }
+  
+  /**
+   * Get the value of the node as a string.
+   *
+   * @return null
+   */
+  public synchronized String getNodeValue()
+  {
+
+    if(null == m_str)
+    {
+      m_str = new String(m_ch);
+    }
+
+    return m_str;
+  }
+
+
+  /**
+   * Tells if this element should disable escaping.
+   * @serial
+   */
+  private boolean m_disableOutputEscaping = false;
+
+  /**
+   * Set the "disable-output-escaping" attribute.
+   * Normally, the xml output method escapes & and < (and
+   * possibly other characters) when outputting text nodes.
+   * This ensures that the output is well-formed XML. However,
+   * it is sometimes convenient to be able to produce output
+   * that is almost, but not quite well-formed XML; for
+   * example, the output may include ill-formed sections
+   * which are intended to be transformed into well-formed
+   * XML by a subsequent non-XML aware process. For this reason,
+   * XSLT provides a mechanism for disabling output escaping.
+   * An xsl:value-of or xsl:text element may have a
+   * disable-output-escaping attribute; the allowed values
+   * are yes or no; the default is no; if the value is yes,
+   * then a text node generated by instantiating the xsl:value-of
+   * or xsl:text element should be output without any escaping.
+   * @see <a href="http://www.w3.org/TR/xslt#disable-output-escaping">disable-output-escaping in XSLT Specification</a>
+   *
+   * @param v Boolean value for "disable-output-escaping" attribute.
+   */
+  public void setDisableOutputEscaping(boolean v)
+  {
+    m_disableOutputEscaping = v;
+  }
+
+  /**
+   * Get the "disable-output-escaping" attribute.
+   * Normally, the xml output method escapes & and < (and
+   * possibly other characters) when outputting text nodes.
+   * This ensures that the output is well-formed XML. However,
+   * it is sometimes convenient to be able to produce output
+   * that is almost, but not quite well-formed XML; for
+   * example, the output may include ill-formed sections
+   * which are intended to be transformed into well-formed
+   * XML by a subsequent non-XML aware process. For this reason,
+   * XSLT provides a mechanism for disabling output escaping.
+   * An xsl:value-of or xsl:text element may have a
+   * disable-output-escaping attribute; the allowed values
+   * are yes or no; the default is no; if the value is yes,
+   * then a text node generated by instantiating the xsl:value-of
+   * or xsl:text element should be output without any escaping.
+   * @see <a href="http://www.w3.org/TR/xslt#disable-output-escaping">disable-output-escaping in XSLT Specification</a>
+   *
+   * @return Boolean value of "disable-output-escaping" attribute.
+   */
+  public boolean getDisableOutputEscaping()
+  {
+    return m_disableOutputEscaping;
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_TEXTLITERALRESULT;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The element's name
+   */
+  public String getNodeName()
+  {
+    return "#Text";
+  }
+
+  /**
+   * Copy the text literal to the result tree.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+    try
+    {
+      SerializationHandler rth = transformer.getResultTreeHandler();
+
+        if (m_disableOutputEscaping)
+      {
+        rth.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
+      }
+
+      rth.characters(m_ch, 0, m_ch.length);
+
+      if (m_disableOutputEscaping)
+      {
+        rth.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
+      }
+    }
+    catch(SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemUnknown.java b/src/main/java/org/apache/xalan/templates/ElemUnknown.java
new file mode 100644
index 0000000..618ba3f
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemUnknown.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemUnknown.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+
+
+/**
+ * Implement an unknown element
+ * @xsl.usage advanced
+ */
+public class ElemUnknown extends ElemLiteralResult
+{
+    static final long serialVersionUID = -4573981712648730168L;
+
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   *@return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_UNDEFINED;
+  }
+  
+  /**
+   * Execute the fallbacks when an extension is not available.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  private void executeFallbacks(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+    for (ElemTemplateElement child = m_firstChild; child != null;
+             child = child.m_nextSibling)
+    {
+      if (child.getXSLToken() == Constants.ELEMNAME_FALLBACK)
+      {
+        try
+        {
+          transformer.pushElemTemplateElement(child);
+          ((ElemFallback) child).executeFallback(transformer);
+        }
+        finally
+        {
+          transformer.popElemTemplateElement();
+        }
+      }
+    }
+
+  }
+  
+  /**
+   * Return true if this extension element has a <xsl:fallback> child element.
+   *
+   * @return true if this extension element has a <xsl:fallback> child element.
+   */
+  private boolean hasFallbackChildren()
+  {
+    for (ElemTemplateElement child = m_firstChild; child != null;
+             child = child.m_nextSibling)
+    {
+      if (child.getXSLToken() == Constants.ELEMNAME_FALLBACK)
+        return true;
+    }
+    
+    return false;
+  }
+
+
+  /**
+   * Execute an unknown element.
+   * Execute fallback if fallback child exists or do nothing
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer)
+            throws TransformerException
+  {
+
+
+	try {
+
+		if (hasFallbackChildren()) {
+			executeFallbacks(transformer);
+		} else {
+			// do nothing
+		}
+		
+	} catch (TransformerException e) {
+		transformer.getErrorListener().fatalError(e);
+	}
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemUse.java b/src/main/java/org/apache/xalan/templates/ElemUse.java
new file mode 100644
index 0000000..85d3a57
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemUse.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemUse.java 476466 2006-11-18 08:22:31Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.QName;
+
+/**
+ * Implement xsl:use.
+ * This acts as a superclass for ElemCopy, ElemAttributeSet,
+ * ElemElement, and ElemLiteralResult, on order to implement
+ * shared behavior the use-attribute-sets attribute.
+ * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemUse extends ElemTemplateElement
+{
+    static final long serialVersionUID = 5830057200289299736L;
+
+  /**
+   * The value of the "use-attribute-sets" attribute.
+   * @serial
+   */
+  private QName m_attributeSetsNames[] = null;
+
+  /**
+   * Set the "use-attribute-sets" attribute.
+   * Attribute sets are used by specifying a use-attribute-sets
+   * attribute on xsl:element, xsl:copy (see [7.5 Copying]) or
+   * xsl:attribute-set elements. The value of the use-attribute-sets
+   * attribute is a whitespace-separated list of names of attribute
+   * sets. Each name is specified as a QName, which is expanded as
+   * described in [2.4 Qualified Names].
+   *
+   * @param v The value to set for the "use-attribute-sets" attribute. 
+   */
+  public void setUseAttributeSets(Vector v)
+  {
+
+    int n = v.size();
+
+    m_attributeSetsNames = new QName[n];
+
+    for (int i = 0; i < n; i++)
+    {
+      m_attributeSetsNames[i] = (QName) v.elementAt(i);
+    }
+  }
+
+  /**
+   * Set the "use-attribute-sets" attribute.
+   * Attribute sets are used by specifying a use-attribute-sets
+   * attribute on xsl:element, xsl:copy (see [7.5 Copying]) or
+   * xsl:attribute-set elements. The value of the use-attribute-sets
+   * attribute is a whitespace-separated list of names of attribute
+   * sets. Each name is specified as a QName, which is expanded as
+   * described in [2.4 Qualified Names].
+   *
+   * @param v The value to set for the "use-attribute-sets" attribute. 
+   */
+  public void setUseAttributeSets(QName[] v)
+  {
+    m_attributeSetsNames = v;
+  }
+
+  /**
+   * Get the "use-attribute-sets" attribute.
+   * Attribute sets are used by specifying a use-attribute-sets
+   * attribute on xsl:element, xsl:copy (see [7.5 Copying]) or
+   * xsl:attribute-set elements, or a xsl:use-attribute-sets attribute on
+   * Literal Result Elements.
+   * The value of the use-attribute-sets
+   * attribute is a whitespace-separated list of names of attribute
+   * sets. Each name is specified as a QName, which is expanded as
+   * described in [2.4 Qualified Names].
+   *
+   * @return The value of the "use-attribute-sets" attribute. 
+   */
+  public QName[] getUseAttributeSets()
+  {
+    return m_attributeSetsNames;
+  }
+  
+  /**
+   * Add the attributes from the named attribute sets to the attribute list.
+   * TODO: Error handling for: "It is an error if there are two attribute sets
+   * with the same expanded-name and with equal import precedence and that both
+   * contain the same attribute unless there is a definition of the attribute
+   * set with higher import precedence that also contains the attribute."
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param stylesheet The owning root stylesheet
+   *
+   * @throws TransformerException
+   */
+  public void applyAttrSets(
+          TransformerImpl transformer, StylesheetRoot stylesheet)
+            throws TransformerException
+  {
+    applyAttrSets(transformer, stylesheet, m_attributeSetsNames);
+  }
+
+  /**
+   * Add the attributes from the named attribute sets to the attribute list.
+   * TODO: Error handling for: "It is an error if there are two attribute sets
+   * with the same expanded-name and with equal import precedence and that both
+   * contain the same attribute unless there is a definition of the attribute
+   * set with higher import precedence that also contains the attribute."
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param stylesheet The owning root stylesheet
+   * @param attributeSetsNames List of attribute sets names to apply
+   *
+   * @throws TransformerException
+   */
+  private void applyAttrSets(
+          TransformerImpl transformer, StylesheetRoot stylesheet, QName attributeSetsNames[])
+            throws TransformerException
+  {
+
+    if (null != attributeSetsNames)
+    {
+      int nNames = attributeSetsNames.length;
+
+      for (int i = 0; i < nNames; i++)
+      {
+        QName qname = attributeSetsNames[i];
+        java.util.List attrSets = stylesheet.getAttributeSetComposed(qname);
+
+        if (null != attrSets)
+        {
+          int nSets = attrSets.size();
+
+          // Highest priority attribute set will be at the top,
+          // so process it last.
+          for (int k = nSets-1; k >= 0 ; k--)
+          {
+            ElemAttributeSet attrSet =
+              (ElemAttributeSet) attrSets.get(k);
+
+            attrSet.execute(transformer);
+          }
+        } 
+        else 
+        {
+          throw new TransformerException(
+              XSLMessages.createMessage(XSLTErrorResources.ER_NO_ATTRIB_SET, 
+                  new Object[] {qname}),this); 
+        }
+      }
+    }
+  }
+
+  /**
+   * Copy attributes specified by use-attribute-sets to the result tree.
+   * Specifying a use-attribute-sets attribute is equivalent to adding
+   * xsl:attribute elements for each of the attributes in each of the
+   * named attribute sets to the beginning of the content of the element
+   * with the use-attribute-sets attribute, in the same order in which
+   * the names of the attribute sets are specified in the use-attribute-sets
+   * attribute. It is an error if use of use-attribute-sets attributes
+   * on xsl:attribute-set elements causes an attribute set to directly
+   * or indirectly use itself.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(
+          TransformerImpl transformer)
+            throws TransformerException
+  {
+
+    if (null != m_attributeSetsNames)
+    {
+      applyAttrSets(transformer, getStylesheetRoot(),
+                    m_attributeSetsNames);
+    }
+ 
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemValueOf.java b/src/main/java/org/apache/xalan/templates/ElemValueOf.java
new file mode 100644
index 0000000..a8d38c7
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemValueOf.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemValueOf.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.xml.sax.SAXException;
+
+/**
+ * Implement xsl:value-of.
+ * <pre>
+ * <!ELEMENT xsl:value-of EMPTY>
+ * <!ATTLIST xsl:value-of
+ *   select %expr; #REQUIRED
+ *   disable-output-escaping (yes|no) "no"
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#value-of">value-of in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemValueOf extends ElemTemplateElement
+{
+    static final long serialVersionUID = 3490728458007586786L;
+
+  /**
+   * The select expression to be executed.
+   * @serial
+   */
+  private XPath m_selectExpression = null;
+
+  /**
+   * True if the pattern is a simple ".".
+   * @serial
+   */
+  private boolean m_isDot = false;
+
+  /**
+   * Set the "select" attribute.
+   * The required select attribute is an expression; this expression
+   * is evaluated and the resulting object is converted to a
+   * string as if by a call to the string function.
+   *
+   * @param v The value to set for the "select" attribute.
+   */
+  public void setSelect(XPath v)
+  {
+
+    if (null != v)
+    {
+      String s = v.getPatternString();
+
+      m_isDot = (null != s) && s.equals(".");
+    }
+
+    m_selectExpression = v;
+  }
+
+  /**
+   * Get the "select" attribute.
+   * The required select attribute is an expression; this expression
+   * is evaluated and the resulting object is converted to a
+   * string as if by a call to the string function.
+   *
+   * @return The value of the "select" attribute.
+   */
+  public XPath getSelect()
+  {
+    return m_selectExpression;
+  }
+
+  /**
+   * Tells if this element should disable escaping.
+   * @serial
+   */
+  private boolean m_disableOutputEscaping = false;
+
+  /**
+   * Set the "disable-output-escaping" attribute.
+   * Normally, the xml output method escapes & and < (and
+   * possibly other characters) when outputting text nodes.
+   * This ensures that the output is well-formed XML. However,
+   * it is sometimes convenient to be able to produce output
+   * that is almost, but not quite well-formed XML; for
+   * example, the output may include ill-formed sections
+   * which are intended to be transformed into well-formed
+   * XML by a subsequent non-XML aware process. For this reason,
+   * XSLT provides a mechanism for disabling output escaping.
+   * An xsl:value-of or xsl:text element may have a
+   * disable-output-escaping attribute; the allowed values
+   * are yes or no; the default is no; if the value is yes,
+   * then a text node generated by instantiating the xsl:value-of
+   * or xsl:text element should be output without any escaping.
+   * @see <a href="http://www.w3.org/TR/xslt#disable-output-escaping">disable-output-escaping in XSLT Specification</a>
+   *
+   * @param v The value to set for the "disable-output-escaping" attribute.
+   */
+  public void setDisableOutputEscaping(boolean v)
+  {
+    m_disableOutputEscaping = v;
+  }
+
+  /**
+   * Get the "disable-output-escaping" attribute.
+   * Normally, the xml output method escapes & and < (and
+   * possibly other characters) when outputting text nodes.
+   * This ensures that the output is well-formed XML. However,
+   * it is sometimes convenient to be able to produce output
+   * that is almost, but not quite well-formed XML; for
+   * example, the output may include ill-formed sections
+   * which are intended to be transformed into well-formed
+   * XML by a subsequent non-XML aware process. For this reason,
+   * XSLT provides a mechanism for disabling output escaping.
+   * An xsl:value-of or xsl:text element may have a
+   * disable-output-escaping attribute; the allowed values
+   * are yes or no; the default is no; if the value is yes,
+   * then a text node generated by instantiating the xsl:value-of
+   * or xsl:text element should be output without any escaping.
+   * @see <a href="http://www.w3.org/TR/xslt#disable-output-escaping">disable-output-escaping in XSLT Specification</a>
+   *
+   * @return The value of the "disable-output-escaping" attribute.
+   */
+  public boolean getDisableOutputEscaping()
+  {
+    return m_disableOutputEscaping;
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_VALUEOF;
+  }
+
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   *
+   * NEEDSDOC @param sroot
+   *
+   * @throws TransformerException
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+
+    super.compose(sroot);
+
+    java.util.Vector vnames = sroot.getComposeState().getVariableNames();
+
+    if (null != m_selectExpression)
+      m_selectExpression.fixupVariables(
+        vnames, sroot.getComposeState().getGlobalsSize());
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The node name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_VALUEOF_STRING;
+  }
+
+  /**
+   * Execute the string expression and copy the text to the
+   * result tree.
+   * The required select attribute is an expression; this expression
+   * is evaluated and the resulting object is converted to a string
+   * as if by a call to the string function. The string specifies
+   * the string-value of the created text node. If the string is
+   * empty, no text node will be created. The created text node will
+   * be merged with any adjacent text nodes.
+   * @see <a href="http://www.w3.org/TR/xslt#value-of">value-of in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {
+
+    XPathContext xctxt = transformer.getXPathContext();
+    SerializationHandler rth = transformer.getResultTreeHandler();
+
+    try
+    {
+      // Optimize for "."
+        xctxt.pushNamespaceContext(this);
+
+        int current = xctxt.getCurrentNode();
+
+        xctxt.pushCurrentNodeAndExpression(current, current);
+
+        if (m_disableOutputEscaping)
+          rth.processingInstruction(
+            javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
+
+        try
+        {
+          Expression expr = m_selectExpression.getExpression();
+
+            expr.executeCharsToContentHandler(xctxt, rth);
+        }
+        finally
+        {
+          if (m_disableOutputEscaping)
+            rth.processingInstruction(
+              javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
+
+          xctxt.popNamespaceContext();
+          xctxt.popCurrentNodeAndExpression();
+        }
+    }
+    catch (SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+    catch (RuntimeException re) {
+    	TransformerException te = new TransformerException(re);
+    	te.setLocator(this);
+    	throw te;
+    }
+  }
+
+  /**
+   * Add a child to the child list.
+   *
+   * @param newChild Child to add to children list
+   *
+   * @return Child just added to children list
+   *
+   * @throws DOMException
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
+  {
+
+    error(XSLTErrorResources.ER_CANNOT_ADD,
+          new Object[]{ newChild.getNodeName(),
+                        this.getNodeName() });  //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
+
+    //" to " + this.m_elemName);
+    return null;
+  }
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+  {
+  	if(callAttrs)
+  		m_selectExpression.getExpression().callVisitors(m_selectExpression, visitor);
+    super.callChildVisitors(visitor, callAttrs);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemVariable.java b/src/main/java/org/apache/xalan/templates/ElemVariable.java
new file mode 100644
index 0000000..099463a
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemVariable.java
@@ -0,0 +1,528 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemVariable.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XRTreeFrag;
+import org.apache.xpath.objects.XRTreeFragSelectWrapper;
+import org.apache.xpath.objects.XString;
+import org.apache.xalan.res.XSLTErrorResources;
+
+/**
+ * Implement xsl:variable.
+ * <pre>
+ * <!ELEMENT xsl:variable %template;>
+ * <!ATTLIST xsl:variable
+ *   name %qname; #REQUIRED
+ *   select %expr; #IMPLIED
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemVariable extends ElemTemplateElement
+{
+    static final long serialVersionUID = 9111131075322790061L;
+
+  /**
+   * Constructor ElemVariable
+   *
+   */
+  public ElemVariable(){}
+
+  /**
+   * This is the index into the stack frame.
+   */
+  protected int m_index;
+  
+  /**
+   * The stack frame size for this variable if it is a global variable 
+   * that declares an RTF, which is equal to the maximum number 
+   * of variables that can be declared in the variable at one time.
+   */
+  int m_frameSize = -1;
+
+  
+  /**
+   * Sets the relative position of this variable within the stack frame (if local)
+   * or the global area (if global).  Note that this should be called only for
+   * global variables since the local position is computed in the compose() method.
+   */
+  public void setIndex(int index)
+  {
+    m_index = index;
+  }
+
+  /**
+   * If this element is not at the top-level, get the relative position of the
+   * variable into the stack frame.  If this variable is at the top-level, get
+   * the relative position within the global area.
+   */
+  public int getIndex()
+  {
+    return m_index;
+  }
+
+  /**
+   * The value of the "select" attribute.
+   * @serial
+   */
+  private XPath m_selectPattern;
+
+  /**
+   * Set the "select" attribute.
+   * If the variable-binding element has a select attribute,
+   * then the value of the attribute must be an expression and
+   * the value of the variable is the object that results from
+   * evaluating the expression. In this case, the content
+   * of the variable must be empty.
+   *
+   * @param v Value to set for the "select" attribute.
+   */
+  public void setSelect(XPath v)
+  {
+    m_selectPattern = v;
+  }
+
+  /**
+   * Get the "select" attribute.
+   * If the variable-binding element has a select attribute,
+   * then the value of the attribute must be an expression and
+   * the value of the variable is the object that results from
+   * evaluating the expression. In this case, the content
+   * of the variable must be empty.
+   *
+   * @return Value of the "select" attribute.
+   */
+  public XPath getSelect()
+  {
+    return m_selectPattern;
+  }
+
+  /**
+   * The value of the "name" attribute.
+   * @serial
+   */
+  protected QName m_qname;
+
+  /**
+   * Set the "name" attribute.
+   * Both xsl:variable and xsl:param have a required name
+   * attribute, which specifies the name of the variable. The
+   * value of the name attribute is a QName, which is expanded
+   * as described in [2.4 Qualified Names].
+   * @see <a href="http://www.w3.org/TR/xslt#qname">qname in XSLT Specification</a>
+   *
+   * @param v Value to set for the "name" attribute.
+   */
+  public void setName(QName v)
+  {
+    m_qname = v;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * Both xsl:variable and xsl:param have a required name
+   * attribute, which specifies the name of the variable. The
+   * value of the name attribute is a QName, which is expanded
+   * as described in [2.4 Qualified Names].
+   * @see <a href="http://www.w3.org/TR/xslt#qname">qname in XSLT Specification</a>
+   *
+   * @return Value of the "name" attribute.
+   */
+  public QName getName()
+  {
+    return m_qname;
+  }
+
+  /**
+   * Tells if this is a top-level variable or param, or not.
+   * @serial
+   */
+  private boolean m_isTopLevel = false;
+
+  /**
+   * Set if this is a top-level variable or param, or not.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @param v Boolean indicating whether this is a top-level variable
+   * or param, or not.
+   */
+  public void setIsTopLevel(boolean v)
+  {
+    m_isTopLevel = v;
+  }
+
+  /**
+   * Get if this is a top-level variable or param, or not.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @return Boolean indicating whether this is a top-level variable
+   * or param, or not.
+   */
+  public boolean getIsTopLevel()
+  {
+    return m_isTopLevel;
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_VARIABLE;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The node name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_VARIABLE_STRING;
+  }
+
+  /**
+   * Copy constructor.
+   *
+   * @param param An element created from an xsl:variable
+   *
+   * @throws TransformerException
+   */
+  public ElemVariable(ElemVariable param) throws TransformerException
+  {
+
+    m_selectPattern = param.m_selectPattern;
+    m_qname = param.m_qname;
+    m_isTopLevel = param.m_isTopLevel;
+
+    // m_value = param.m_value;
+    // m_varContext = param.m_varContext;
+  }
+
+  /**
+   * Execute a variable declaration and push it onto the variable stack.
+   * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {
+
+    int sourceNode = transformer.getXPathContext().getCurrentNode();
+  
+    XObject var = getValue(transformer, sourceNode);
+
+    // transformer.getXPathContext().getVarStack().pushVariable(m_qname, var);
+    transformer.getXPathContext().getVarStack().setLocalVariable(m_index, var);
+  }
+
+  /**
+   * Get the XObject representation of the variable.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
+   *
+   * @return the XObject representation of the variable.
+   *
+   * @throws TransformerException
+   */
+  public XObject getValue(TransformerImpl transformer, int sourceNode)
+          throws TransformerException
+  {
+
+    XObject var;
+    XPathContext xctxt = transformer.getXPathContext();
+
+    xctxt.pushCurrentNode(sourceNode);
+ 
+    try
+    {
+      if (null != m_selectPattern)
+      {
+        var = m_selectPattern.execute(xctxt, sourceNode, this);
+
+        var.allowDetachToRelease(false);
+      }
+      else if (null == getFirstChildElem())
+      {
+        var = XString.EMPTYSTRING;
+      }
+      else
+      {
+
+        // Use result tree fragment.
+        // Global variables may be deferred (see XUnresolvedVariable) and hence
+        // need to be assigned to a different set of DTMs than local variables
+        // so they aren't popped off the stack on return from a template.
+        int df;
+
+		// Bugzilla 7118: A variable set via an RTF may create local
+		// variables during that computation. To keep them from overwriting
+		// variables at this level, push a new variable stack.
+		////// PROBLEM: This is provoking a variable-used-before-set
+		////// problem in parameters. Needs more study.
+		try
+		{
+			//////////xctxt.getVarStack().link(0);
+			if(m_parentNode instanceof Stylesheet) // Global variable
+				df = transformer.transformToGlobalRTF(this);
+			else
+				df = transformer.transformToRTF(this);
+    	}
+		finally{ 
+			//////////////xctxt.getVarStack().unlink(); 
+			}
+
+        var = new XRTreeFrag(df, xctxt, this);
+      }
+    }
+    finally
+    {      
+      xctxt.popCurrentNode();
+    }
+
+    return var;
+  }
+  
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    // See if we can reduce an RTF to a select with a string expression.
+    if(null == m_selectPattern  
+       && sroot.getOptimizer())
+    {
+      XPath newSelect = rewriteChildToExpression(this);
+      if(null != newSelect)
+        m_selectPattern = newSelect;
+    }
+    
+    StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    
+    // This should be done before addVariableName, so we don't have visibility 
+    // to the variable now being defined.
+    java.util.Vector vnames = cstate.getVariableNames();
+    if(null != m_selectPattern)
+      m_selectPattern.fixupVariables(vnames, cstate.getGlobalsSize());
+      
+    // Only add the variable if this is not a global.  If it is a global, 
+    // it was already added by stylesheet root.
+    if(!(m_parentNode instanceof Stylesheet) && m_qname != null)
+    {
+      m_index = cstate.addVariableName(m_qname) - cstate.getGlobalsSize();
+    }
+    else if (m_parentNode instanceof Stylesheet)
+    {
+    	// If this is a global, then we need to treat it as if it's a xsl:template, 
+    	// and count the number of variables it contains.  So we set the count to 
+    	// zero here.
+		cstate.resetStackFrameSize();
+    }
+    
+    // This has to be done after the addVariableName, so that the variable 
+    // pushed won't be immediately popped again in endCompose.
+    super.compose(sroot);
+  }
+  
+  /**
+   * This after the template's children have been composed.  We have to get 
+   * the count of how many variables have been declared, so we can do a link 
+   * and unlink.
+   */
+  public void endCompose(StylesheetRoot sroot) throws TransformerException
+  {
+    super.endCompose(sroot);
+    if(m_parentNode instanceof Stylesheet)
+    {
+    	StylesheetRoot.ComposeState cstate = sroot.getComposeState();
+    	m_frameSize = cstate.getFrameSize();
+    	cstate.resetStackFrameSize();
+    }
+  }
+
+  
+  
+//  /**
+//   * This after the template's children have been composed.
+//   */
+//  public void endCompose() throws TransformerException
+//  {
+//    super.endCompose();
+//  }
+
+
+  /**
+   * If the children of a variable is a single xsl:value-of or text literal, 
+   * it is cheaper to evaluate this as an expression, so try and adapt the 
+   * child an an expression.
+   *
+   * @param varElem Should be a ElemParam, ElemVariable, or ElemWithParam.
+   *
+   * @return An XPath if rewrite is possible, else null.
+   *
+   * @throws TransformerException
+   */
+  static XPath rewriteChildToExpression(ElemTemplateElement varElem)
+          throws TransformerException
+  {
+
+    ElemTemplateElement t = varElem.getFirstChildElem();
+
+    // Down the line this can be done with multiple string objects using 
+    // the concat function.
+    if (null != t && null == t.getNextSiblingElem())
+    {
+      int etype = t.getXSLToken();
+
+      if (Constants.ELEMNAME_VALUEOF == etype)
+      {
+        ElemValueOf valueof = (ElemValueOf) t;
+
+        // %TBD% I'm worried about extended attributes here.
+        if (valueof.getDisableOutputEscaping() == false
+                && valueof.getDOMBackPointer() == null)
+        {
+          varElem.m_firstChild = null;
+
+          return new XPath(new XRTreeFragSelectWrapper(valueof.getSelect().getExpression()));
+        }
+      }
+      else if (Constants.ELEMNAME_TEXTLITERALRESULT == etype)
+      {
+        ElemTextLiteral lit = (ElemTextLiteral) t;
+
+        if (lit.getDisableOutputEscaping() == false
+                && lit.getDOMBackPointer() == null)
+        {
+          String str = lit.getNodeValue();
+          XString xstr = new XString(str);
+
+          varElem.m_firstChild = null;
+
+          return new XPath(new XRTreeFragSelectWrapper(xstr));
+        }
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * This function is called during recomposition to
+   * control how this element is composed.
+   * @param root The root stylesheet for this transformation.
+   */
+  public void recompose(StylesheetRoot root)
+  {
+    root.recomposeVariables(this);
+  }
+  
+  /**
+   * Set the parent as an ElemTemplateElement.
+   *
+   * @param p This node's parent as an ElemTemplateElement
+   */
+  public void setParentElem(ElemTemplateElement p)
+  {
+    super.setParentElem(p);
+    p.m_hasVariableDecl = true;
+  }
+  
+  /**
+   * Accept a visitor and call the appropriate method 
+   * for this class.
+   * 
+   * @param visitor The visitor whose appropriate method will be called.
+   * @return true if the children of the object should be visited.
+   */
+  protected boolean accept(XSLTVisitor visitor)
+  {
+  	return visitor.visitVariableOrParamDecl(this);
+  }
+
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+  {
+  	if(null != m_selectPattern)
+  		m_selectPattern.getExpression().callVisitors(m_selectPattern, visitor);
+    super.callChildVisitors(visitor, callAttrs);
+  }
+  
+  /**
+   * Tell if this is a psuedo variable reference, declared by Xalan instead 
+   * of by the user.
+   */
+  public boolean isPsuedoVar()
+  {
+  	java.lang.String ns = m_qname.getNamespaceURI();
+  	if((null != ns) && ns.equals(RedundentExprEliminator.PSUEDOVARNAMESPACE))
+  	{
+  		if(m_qname.getLocalName().startsWith("#"))
+  			return true;
+  	}
+  	return false;
+  }
+  
+  /**
+   * Add a child to the child list. If the select attribute
+   * is present, an error will be raised.
+   *
+   * @param elem New element to append to this element's children list
+   *
+   * @return null if the select attribute was present, otherwise the 
+   * child just added to the child list 
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement elem)
+  {
+    // cannot have content and select
+    if (m_selectPattern != null)
+    {
+      error(XSLTErrorResources.ER_CANT_HAVE_CONTENT_AND_SELECT, 
+          new Object[]{"xsl:" + this.getNodeName()});
+      return null;
+    }
+    return super.appendChild(elem);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemVariablePsuedo.java b/src/main/java/org/apache/xalan/templates/ElemVariablePsuedo.java
new file mode 100644
index 0000000..9200e8c
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemVariablePsuedo.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemVariablePsuedo.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.XPath;
+
+public class ElemVariablePsuedo extends ElemVariable
+{
+    static final long serialVersionUID = 692295692732588486L;
+  XUnresolvedVariableSimple m_lazyVar;
+	
+  /**
+   * Set the "select" attribute.
+   * If the variable-binding element has a select attribute,
+   * then the value of the attribute must be an expression and
+   * the value of the variable is the object that results from
+   * evaluating the expression. In this case, the content
+   * of the variable must be empty.
+   *
+   * @param v Value to set for the "select" attribute.
+   */
+  public void setSelect(XPath v)
+  {
+    super.setSelect(v);
+    m_lazyVar = new XUnresolvedVariableSimple(this);
+  }
+  
+  /**
+   * Execute a variable declaration and push it onto the variable stack.
+   * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   *
+   * @throws TransformerException
+   */
+  public void execute(TransformerImpl transformer) throws TransformerException
+  {
+
+    // if (TransformerImpl.S_DEBUG)
+    //  transformer.getTraceManager().fireTraceEvent(this);
+
+    // transformer.getXPathContext().getVarStack().pushVariable(m_qname, var);
+    transformer.getXPathContext().getVarStack().setLocalVariable(m_index, m_lazyVar);
+  }
+
+}
+
diff --git a/src/main/java/org/apache/xalan/templates/ElemWhen.java b/src/main/java/org/apache/xalan/templates/ElemWhen.java
new file mode 100644
index 0000000..b129161
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemWhen.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemWhen.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xpath.XPath;
+
+/**
+ * Implement xsl:when.
+ * <pre>
+ * <!ELEMENT xsl:when %template;>
+ * <!ATTLIST xsl:when
+ *   test %expr; #REQUIRED
+ *   %space-att;
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl:choose">XXX in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemWhen extends ElemTemplateElement
+{
+    static final long serialVersionUID = 5984065730262071360L;
+
+  /**
+   * Each xsl:when element has a single attribute, test,
+   * which specifies an expression.
+   * @serial
+   */
+  private XPath m_test;
+
+  /**
+   * Set the "test" attribute.
+   * Each xsl:when element has a single attribute, test,
+   * which specifies an expression.
+   *
+   * @param v Value to set for the "test" attribute.
+   */
+  public void setTest(XPath v)
+  {
+    m_test = v;
+  }
+
+  /**
+   * Get the "test" attribute.
+   * Each xsl:when element has a single attribute, test,
+   * which specifies an expression.
+   *
+   * @return Value of the "test" attribute.
+   */
+  public XPath getTest()
+  {
+    return m_test;
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_WHEN;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) 
+    throws javax.xml.transform.TransformerException
+  {
+    super.compose(sroot);
+    java.util.Vector vnames = sroot.getComposeState().getVariableNames();
+    if(null != m_test)
+      m_test.fixupVariables(vnames, sroot.getComposeState().getGlobalsSize());
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The node name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_WHEN_STRING;
+  }
+
+  /**
+   * Constructor ElemWhen
+   *
+   */
+  public ElemWhen(){}
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+  {
+  	if(callAttrs)
+  		m_test.getExpression().callVisitors(m_test, visitor);
+    super.callChildVisitors(visitor, callAttrs);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/ElemWithParam.java b/src/main/java/org/apache/xalan/templates/ElemWithParam.java
new file mode 100644
index 0000000..b59212c
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/ElemWithParam.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemWithParam.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XRTreeFrag;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Implement xsl:with-param.  xsl:with-param is allowed within
+ * both xsl:call-template and xsl:apply-templates.
+ * <pre>
+ * <!ELEMENT xsl:with-param %template;>
+ * <!ATTLIST xsl:with-param
+ *   name %qname; #REQUIRED
+ *   select %expr; #IMPLIED
+ * >
+ * </pre>
+ * @see <a href="http://www.w3.org/TR/xslt#element-with-param">element-with-param in XSLT Specification</a>
+ * @xsl.usage advanced
+ */
+public class ElemWithParam extends ElemTemplateElement
+{
+    static final long serialVersionUID = -1070355175864326257L;
+  /**
+   * This is the index to the stack frame being called, <emph>not</emph> the 
+   * stack frame that contains this element.
+   */
+  int m_index;
+
+  /**
+   * The "select" attribute, which specifies the value of the
+   * argument, if element content is not specified.
+   * @serial
+   */
+  private XPath m_selectPattern = null;
+
+  /**
+   * Set the "select" attribute.
+   * The "select" attribute specifies the value of the
+   * argument, if element content is not specified.
+   *
+   * @param v Value to set for the "select" attribute. 
+   */
+  public void setSelect(XPath v)
+  {
+    m_selectPattern = v;
+  }
+
+  /**
+   * Get the "select" attribute.
+   * The "select" attribute specifies the value of the
+   * argument, if element content is not specified.
+   *
+   * @return Value of the "select" attribute. 
+   */
+  public XPath getSelect()
+  {
+    return m_selectPattern;
+  }
+
+  /**
+   * The required name attribute specifies the name of the
+   * parameter (the variable the value of whose binding is
+   * to be replaced). The value of the name attribute is a QName,
+   * which is expanded as described in [2.4 Qualified Names].
+   * @serial
+   */
+  private QName m_qname = null;
+  
+  int m_qnameID;
+
+  /**
+   * Set the "name" attribute.
+   * DJD
+   *
+   * @param v Value to set for the "name" attribute.
+   */
+  public void setName(QName v)
+  {
+    m_qname = v;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * DJD
+   *
+   * @return Value of the "name" attribute.
+   */
+  public QName getName()
+  {
+    return m_qname;
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_WITHPARAM;
+  }
+
+
+  /**
+   * Return the node name.
+   *
+   * @return the node name.
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_WITHPARAM_STRING;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+    // See if we can reduce an RTF to a select with a string expression.
+    if(null == m_selectPattern  
+       && sroot.getOptimizer())
+    {
+      XPath newSelect = ElemVariable.rewriteChildToExpression(this);
+      if(null != newSelect)
+        m_selectPattern = newSelect;
+    }
+    m_qnameID = sroot.getComposeState().getQNameID(m_qname);
+    super.compose(sroot);
+    
+    java.util.Vector vnames = sroot.getComposeState().getVariableNames();
+    if(null != m_selectPattern)
+      m_selectPattern.fixupVariables(vnames, sroot.getComposeState().getGlobalsSize());
+      
+    // m_index must be resolved by ElemApplyTemplates and ElemCallTemplate!
+  }
+  
+  /**
+   * Set the parent as an ElemTemplateElement.
+   *
+   * @param p This node's parent as an ElemTemplateElement
+   */
+  public void setParentElem(ElemTemplateElement p)
+  {
+    super.setParentElem(p);
+    p.m_hasVariableDecl = true;
+  }
+  
+  /**
+   * Get the XObject representation of the variable.
+   *
+   * @param transformer non-null reference to the the current transform-time state.
+   * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
+   *
+   * @return the XObject representation of the variable.
+   *
+   * @throws TransformerException
+   */
+  public XObject getValue(TransformerImpl transformer, int sourceNode)
+          throws TransformerException
+  {
+
+    XObject var;
+    XPathContext xctxt = transformer.getXPathContext();
+
+    xctxt.pushCurrentNode(sourceNode);
+
+    try
+    {
+      if (null != m_selectPattern)
+      {
+        var = m_selectPattern.execute(xctxt, sourceNode, this);
+
+        var.allowDetachToRelease(false);
+      }
+      else if (null == getFirstChildElem())
+      {
+        var = XString.EMPTYSTRING;
+      }
+      else
+      {
+
+        // Use result tree fragment
+        int df = transformer.transformToRTF(this);
+
+        var = new XRTreeFrag(df, xctxt, this);
+      }
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+    }
+
+    return var;
+  }
+  
+  /**
+   * Call the children visitors.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+  {
+  	if(callAttrs && (null != m_selectPattern))
+  		m_selectPattern.getExpression().callVisitors(m_selectPattern, visitor);
+    super.callChildVisitors(visitor, callAttrs);
+  }
+  
+  /**
+   * Add a child to the child list. If the select attribute
+   * is present, an error will be raised.
+   *
+   * @param elem New element to append to this element's children list
+   *
+   * @return null if the select attribute was present, otherwise the 
+   * child just added to the child list 
+   */
+  public ElemTemplateElement appendChild(ElemTemplateElement elem)
+  {
+    // cannot have content and select
+    if (m_selectPattern != null)
+    {
+      error(XSLTErrorResources.ER_CANT_HAVE_CONTENT_AND_SELECT, 
+          new Object[]{"xsl:" + this.getNodeName()});
+      return null;
+    }
+    return super.appendChild(elem);
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/FuncDocument.java b/src/main/java/org/apache/xalan/templates/FuncDocument.java
new file mode 100644
index 0000000..ba2d38d
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/FuncDocument.java
@@ -0,0 +1,463 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncDocument.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.Expression;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.SourceTreeManager;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.functions.Function2Args;
+import org.apache.xpath.functions.WrongNumberArgsException;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Doc() function.
+ *
+ * When the document function has exactly one argument and the argument
+ * is a node-set, then the result is the union, for each node in the
+ * argument node-set, of the result of calling the document function with
+ * the first argument being the string-value of the node, and the second
+ * argument being a node-set with the node as its only member. When the
+ * document function has two arguments and the first argument is a node-set,
+ * then the result is the union, for each node in the argument node-set,
+ * of the result of calling the document function with the first argument
+ * being the string-value of the node, and with the second argument being
+ * the second argument passed to the document function.
+ * @xsl.usage advanced
+ */
+public class FuncDocument extends Function2Args
+{
+    static final long serialVersionUID = 2483304325971281424L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    int context = xctxt.getCurrentNode();
+    DTM dtm = xctxt.getDTM(context);
+    
+    int docContext = dtm.getDocumentRoot(context);
+    XObject arg = (XObject) this.getArg0().execute(xctxt);
+
+    String base = "";
+    Expression arg1Expr = this.getArg1();
+
+    if (null != arg1Expr)
+    {
+
+      // The URI reference may be relative. The base URI (see [3.2 Base URI]) 
+      // of the node in the second argument node-set that is first in document 
+      // order is used as the base URI for resolving the 
+      // relative URI into an absolute URI. 
+      XObject arg2 = arg1Expr.execute(xctxt);
+
+      if (XObject.CLASS_NODESET == arg2.getType())
+      {
+        int baseNode = arg2.iter().nextNode();
+
+        if (baseNode == DTM.NULL)
+        {
+            // See http://www.w3.org/1999/11/REC-xslt-19991116-errata#E14.
+            // If the second argument is an empty nodeset, this is an error.
+            // The processor can recover by returning an empty nodeset.
+          	warn(xctxt, XSLTErrorResources.WG_EMPTY_SECOND_ARG, null);
+          	XNodeSet nodes = new XNodeSet(xctxt.getDTMManager());
+   	        return nodes;
+        } else{
+	        DTM baseDTM = xctxt.getDTM(baseNode);
+    	    base = baseDTM.getDocumentBaseURI();
+        }
+        // %REVIEW% This doesn't seem to be a problem with the conformance
+        // suite, but maybe it's just not doing a good test?
+//        int baseDoc = baseDTM.getDocument();
+//
+//        if (baseDoc == DTM.NULL /* || baseDoc instanceof Stylesheet  -->What to do?? */)
+//        {
+//
+//          // base = ((Stylesheet)baseDoc).getBaseIdentifier();
+//          base = xctxt.getNamespaceContext().getBaseIdentifier();
+//        }
+//        else
+//          base = xctxt.getSourceTreeManager().findURIFromDoc(baseDoc);
+      }
+      else
+      {
+        //Can not convert other type to a node-set!;
+        arg2.iter();
+      }
+    }
+    else
+    {
+
+      // If the second argument is omitted, then it defaults to 
+      // the node in the stylesheet that contains the expression that 
+      // includes the call to the document function. Note that a 
+      // zero-length URI reference is a reference to the document 
+      // relative to which the URI reference is being resolved; thus 
+      // document("") refers to the root node of the stylesheet; 
+      // the tree representation of the stylesheet is exactly 
+      // the same as if the XML document containing the stylesheet 
+      // was the initial source document.
+      assertion(null != xctxt.getNamespaceContext(), "Namespace context can not be null!");
+      base = xctxt.getNamespaceContext().getBaseIdentifier();
+    }
+
+    XNodeSet nodes = new XNodeSet(xctxt.getDTMManager());
+    NodeSetDTM mnl = nodes.mutableNodeset();
+    DTMIterator iterator = (XObject.CLASS_NODESET == arg.getType())
+                            ? arg.iter() : null;
+    int pos = DTM.NULL;
+
+    while ((null == iterator) || (DTM.NULL != (pos = iterator.nextNode())))
+    {
+      XMLString ref = (null != iterator)
+                   ? xctxt.getDTM(pos).getStringValue(pos) : arg.xstr();
+      
+      // The first and only argument was a nodeset, the base in that
+      // case is the base URI of the node from the first argument nodeset. 
+      // Remember, when the document function has exactly one argument and
+      // the argument is a node-set, then the result is the union, for each
+      // node in the argument node-set, of the result of calling the document
+      // function with the first argument being the string-value of the node,
+      // and the second argument being a node-set with the node as its only 
+      // member.
+      if (null == arg1Expr && DTM.NULL != pos)
+      {
+        DTM baseDTM = xctxt.getDTM(pos);
+        base = baseDTM.getDocumentBaseURI();
+      }
+
+      if (null == ref)
+        continue;
+
+      if (DTM.NULL == docContext)
+      {
+        error(xctxt, XSLTErrorResources.ER_NO_CONTEXT_OWNERDOC, null);  //"context does not have an owner document!");
+      }
+
+      // From http://www.ics.uci.edu/pub/ietf/uri/rfc1630.txt
+      // A partial form can be distinguished from an absolute form in that the
+      // latter must have a colon and that colon must occur before any slash
+      // characters. Systems not requiring partial forms should not use any
+      // unencoded slashes in their naming schemes.  If they do, absolute URIs
+      // will still work, but confusion may result.
+      int indexOfColon = ref.indexOf(':');
+      int indexOfSlash = ref.indexOf('/');
+
+      if ((indexOfColon != -1) && (indexOfSlash != -1)
+              && (indexOfColon < indexOfSlash))
+      {
+
+        // The url (or filename, for that matter) is absolute.
+        base = null;
+      }
+
+      int newDoc = getDoc(xctxt, context, ref.toString(), base);
+
+      // nodes.mutableNodeset().addNode(newDoc);  
+      if (DTM.NULL != newDoc)
+      {
+        // TODO: mnl.addNodeInDocOrder(newDoc, true, xctxt); ??
+        if (!mnl.contains(newDoc))
+        {
+          mnl.addElement(newDoc);
+        }
+      }
+
+      if (null == iterator || newDoc == DTM.NULL)
+        break;
+    }
+
+    return nodes;
+  }
+
+  /**
+   * Get the document from the given URI and base
+   *
+   * @param xctxt The XPath runtime state.
+   * @param context The current context node
+   * @param uri Relative(?) URI of the document
+   * @param base Base to resolve relative URI from.
+   *
+   * @return The document Node pointing to the document at the given URI
+   * or null
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  int getDoc(XPathContext xctxt, int context, String uri, String base)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // System.out.println("base: "+base+", uri: "+uri);
+    SourceTreeManager treeMgr = xctxt.getSourceTreeManager();
+    Source source;
+   
+    int newDoc;
+    try
+    {
+      source = treeMgr.resolveURI(base, uri, xctxt.getSAXLocator());
+      newDoc = treeMgr.getNode(source);
+    }
+    catch (IOException ioe)
+    {
+      throw new TransformerException(ioe.getMessage(), 
+        (SourceLocator)xctxt.getSAXLocator(), ioe);
+    }
+    catch(TransformerException te)
+    {
+      throw new TransformerException(te);
+    }
+
+    if (DTM.NULL != newDoc)
+      return newDoc;
+
+    // If the uri length is zero, get the uri of the stylesheet.
+    if (uri.length() == 0)
+    {
+      // Hmmm... this seems pretty bogus to me... -sb
+      uri = xctxt.getNamespaceContext().getBaseIdentifier();
+      try
+      {
+        source = treeMgr.resolveURI(base, uri, xctxt.getSAXLocator());
+      }
+      catch (IOException ioe)
+      {
+        throw new TransformerException(ioe.getMessage(), 
+          (SourceLocator)xctxt.getSAXLocator(), ioe);
+      }
+    }
+
+    String diagnosticsString = null;
+
+    try
+    {
+      if ((null != uri) && (uri.length() > 0))
+      {
+        newDoc = treeMgr.getSourceTree(source, xctxt.getSAXLocator(), xctxt);
+
+        // System.out.println("newDoc: "+((Document)newDoc).getDocumentElement().getNodeName());
+      }
+      else
+        warn(xctxt, XSLTErrorResources.WG_CANNOT_MAKE_URL_FROM,
+             new Object[]{ ((base == null) ? "" : base) + uri });  //"Can not make URL from: "+((base == null) ? "" : base )+uri);
+    }
+    catch (Throwable throwable)
+    {
+
+      // throwable.printStackTrace();
+      newDoc = DTM.NULL;
+
+      // path.warn(XSLTErrorResources.WG_ENCODING_NOT_SUPPORTED_USING_JAVA, new Object[]{((base == null) ? "" : base )+uri}); //"Can not load requested doc: "+((base == null) ? "" : base )+uri);
+      while (throwable
+             instanceof org.apache.xml.utils.WrappedRuntimeException)
+      {
+        throwable =
+          ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
+      }
+
+      if ((throwable instanceof NullPointerException)
+              || (throwable instanceof ClassCastException))
+      {
+        throw new org.apache.xml.utils.WrappedRuntimeException(
+          (Exception) throwable);
+      }
+
+      StringWriter sw = new StringWriter();
+      PrintWriter diagnosticsWriter = new PrintWriter(sw);
+
+      if (throwable instanceof TransformerException)
+      {
+        TransformerException spe = (TransformerException) throwable;
+
+        {
+          Throwable e = spe;
+
+          while (null != e)
+          {
+            if (null != e.getMessage())
+            {
+              diagnosticsWriter.println(" (" + e.getClass().getName() + "): "
+                                        + e.getMessage());
+            }
+
+            if (e instanceof TransformerException)
+            {
+              TransformerException spe2 = (TransformerException) e;
+
+              SourceLocator locator = spe2.getLocator();
+              if ((null != locator) && (null != locator.getSystemId()))
+                diagnosticsWriter.println("   ID: " + locator.getSystemId()
+                                          + " Line #" + locator.getLineNumber()
+                                          + " Column #"
+                                          + locator.getColumnNumber());
+
+              e = spe2.getException();
+
+              if (e instanceof org.apache.xml.utils.WrappedRuntimeException)
+                e = ((org.apache.xml.utils.WrappedRuntimeException) e).getException();
+            }
+            else
+              e = null;
+          }
+        }
+      }
+      else
+      {
+        diagnosticsWriter.println(" (" + throwable.getClass().getName()
+                                  + "): " + throwable.getMessage());
+      }
+
+      diagnosticsString = throwable.getMessage(); //sw.toString();
+    }
+
+    if (DTM.NULL == newDoc)
+    {
+
+      // System.out.println("what?: "+base+", uri: "+uri);
+      if (null != diagnosticsString)
+      {
+        warn(xctxt, XSLTErrorResources.WG_CANNOT_LOAD_REQUESTED_DOC,
+             new Object[]{ diagnosticsString });  //"Can not load requested doc: "+((base == null) ? "" : base )+uri);
+      }
+      else
+        warn(xctxt, XSLTErrorResources.WG_CANNOT_LOAD_REQUESTED_DOC,
+             new Object[]{
+               uri == null
+               ? ((base == null) ? "" : base) + uri : uri.toString() });  //"Can not load requested doc: "+((base == null) ? "" : base )+uri);
+    }
+    else
+    {
+      // %REVIEW%
+      // TBD: What to do about XLocator?
+      // xctxt.getSourceTreeManager().associateXLocatorToNode(newDoc, url, null);
+    }
+
+    return newDoc;
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param xctxt The XPath runtime state.
+   * @param msg The error message key
+   * @param args Arguments to be used in the error message
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void error(XPathContext xctxt, String msg, Object args[])
+          throws javax.xml.transform.TransformerException
+  {
+
+    String formattedMsg = XSLMessages.createMessage(msg, args);
+    ErrorListener errHandler = xctxt.getErrorListener();
+    TransformerException spe = new TransformerException(formattedMsg,
+                              (SourceLocator)xctxt.getSAXLocator());
+
+    if (null != errHandler)
+      errHandler.error(spe);
+    else
+      System.out.println(formattedMsg);
+  }
+
+  /**
+   * Warn the user of a problem.
+   *
+   * @param xctxt The XPath runtime state.
+   * @param msg Warning message key
+   * @param args Arguments to be used in the warning message
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void warn(XPathContext xctxt, String msg, Object args[])
+          throws javax.xml.transform.TransformerException
+  {
+
+    String formattedMsg = XSLMessages.createWarning(msg, args);
+    ErrorListener errHandler = xctxt.getErrorListener();
+    TransformerException spe = new TransformerException(formattedMsg,
+                              (SourceLocator)xctxt.getSAXLocator());
+
+    if (null != errHandler)
+      errHandler.warning(spe);
+    else
+      System.out.println(formattedMsg);
+  }
+
+ /**
+   * Overide the superclass method to allow one or two arguments.
+   *
+   *
+   * @param argNum Number of arguments passed in to this function
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if ((argNum < 1) || (argNum > 2))
+      reportWrongNumberArgs();
+  }
+  
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createMessage(XSLTErrorResources.ER_ONE_OR_TWO, null)); //"1 or 2");
+  }
+  
+  /**
+   * Tell if the expression is a nodeset expression.
+   * @return true if the expression can be represented as a nodeset.
+   */
+  public boolean isNodesetExpr()
+  {
+    return true;
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/FuncFormatNumb.java b/src/main/java/org/apache/xalan/templates/FuncFormatNumb.java
new file mode 100644
index 0000000..bf1a877
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/FuncFormatNumb.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncFormatNumb.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.SAXSourceLocator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.functions.Function3Args;
+import org.apache.xpath.functions.WrongNumberArgsException;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the FormatNumber() function.
+ * @xsl.usage advanced
+ */
+public class FuncFormatNumb extends Function3Args
+{
+    static final long serialVersionUID = -8869935264870858636L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    // A bit of an ugly hack to get our context.
+    ElemTemplateElement templElem =
+      (ElemTemplateElement) xctxt.getNamespaceContext();
+    StylesheetRoot ss = templElem.getStylesheetRoot();
+    java.text.DecimalFormat formatter = null;
+    java.text.DecimalFormatSymbols dfs = null;
+    double num = getArg0().execute(xctxt).num();
+    String patternStr = getArg1().execute(xctxt).str();
+
+    // TODO: what should be the behavior here??
+    if (patternStr.indexOf(0x00A4) > 0)
+      ss.error(XSLTErrorResources.ER_CURRENCY_SIGN_ILLEGAL);  // currency sign not allowed
+
+    // this third argument is not a locale name. It is the name of a
+    // decimal-format declared in the stylesheet!(xsl:decimal-format
+    try
+    {
+      Expression arg2Expr = getArg2();
+
+      if (null != arg2Expr)
+      {
+        String dfName = arg2Expr.execute(xctxt).str();
+        QName qname = new QName(dfName, xctxt.getNamespaceContext());
+
+        dfs = ss.getDecimalFormatComposed(qname);
+
+        if (null == dfs)
+        {
+          warn(xctxt, XSLTErrorResources.WG_NO_DECIMALFORMAT_DECLARATION,
+               new Object[]{ dfName });  //"not found!!!
+
+          //formatter = new java.text.DecimalFormat(patternStr);
+        }
+        else
+        {
+
+          //formatter = new java.text.DecimalFormat(patternStr, dfs);
+          formatter = new java.text.DecimalFormat();
+
+          formatter.setDecimalFormatSymbols(dfs);
+          formatter.applyLocalizedPattern(patternStr);
+        }
+      }
+
+      //else
+      if (null == formatter)
+      {
+
+        // look for a possible default decimal-format
+        dfs = ss.getDecimalFormatComposed(new QName(""));
+
+        if (dfs != null)
+        {
+          formatter = new java.text.DecimalFormat();
+
+          formatter.setDecimalFormatSymbols(dfs);
+          formatter.applyLocalizedPattern(patternStr);
+        }
+        else
+        {
+          dfs = new java.text.DecimalFormatSymbols(java.util.Locale.US);
+
+          dfs.setInfinity(Constants.ATTRVAL_INFINITY);
+          dfs.setNaN(Constants.ATTRVAL_NAN);
+
+          formatter = new java.text.DecimalFormat();
+
+          formatter.setDecimalFormatSymbols(dfs);
+
+          if (null != patternStr)
+            formatter.applyLocalizedPattern(patternStr);
+        }
+      }
+
+      return new XString(formatter.format(num));
+    }
+    catch (Exception iae)
+    {
+      templElem.error(XSLTErrorResources.ER_MALFORMED_FORMAT_STRING,
+                      new Object[]{ patternStr });
+
+      return XString.EMPTYSTRING;
+
+      //throw new XSLProcessorException(iae);
+    }
+  }
+
+  /**
+   * Warn the user of a problem.
+   *
+   * @param xctxt The XPath runtime state.
+   * @param msg Warning message key
+   * @param args Arguments to be used in warning message
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void warn(XPathContext xctxt, String msg, Object args[])
+          throws javax.xml.transform.TransformerException
+  {
+
+    String formattedMsg = XSLMessages.createWarning(msg, args);
+    ErrorListener errHandler = xctxt.getErrorListener();
+
+    errHandler.warning(new TransformerException(formattedMsg,
+                                             (SAXSourceLocator)xctxt.getSAXLocator()));
+  }
+
+  /**
+   * Overide the superclass method to allow one or two arguments. 
+   *
+   *
+   * @param argNum Number of arguments passed in
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if ((argNum > 3) || (argNum < 2))
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createMessage(XSLTErrorResources.ER_TWO_OR_THREE, null)); //"2 or 3");
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/FuncKey.java b/src/main/java/org/apache/xalan/templates/FuncKey.java
new file mode 100644
index 0000000..0f98fc2
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/FuncKey.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncKey.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Hashtable;
+
+import org.apache.xalan.transformer.KeyManager;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.UnionPathIterator;
+import org.apache.xpath.functions.Function2Args;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Key() function.
+ * @xsl.usage advanced
+ */
+public class FuncKey extends Function2Args
+{
+    static final long serialVersionUID = 9089293100115347340L;
+
+  /** Dummy value to be used in usedrefs hashtable           */
+  static private Boolean ISTRUE = new Boolean(true);
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    // TransformerImpl transformer = (TransformerImpl)xctxt;
+    TransformerImpl transformer = (TransformerImpl) xctxt.getOwnerObject();
+    XNodeSet nodes = null;
+    int context = xctxt.getCurrentNode();
+    DTM dtm = xctxt.getDTM(context);
+    int docContext = dtm.getDocumentRoot(context);
+
+    if (DTM.NULL == docContext)
+    {
+
+      // path.error(context, XPATHErrorResources.ER_CONTEXT_HAS_NO_OWNERDOC); //"context does not have an owner document!");
+    }
+
+    String xkeyname = getArg0().execute(xctxt).str();
+    QName keyname = new QName(xkeyname, xctxt.getNamespaceContext());
+    XObject arg = getArg1().execute(xctxt);
+    boolean argIsNodeSetDTM = (XObject.CLASS_NODESET == arg.getType());
+    KeyManager kmgr = transformer.getKeyManager();
+    
+    // Don't bother with nodeset logic if the thing is only one node.
+    if(argIsNodeSetDTM)
+    {
+    	XNodeSet ns = (XNodeSet)arg;
+    	ns.setShouldCacheNodes(true);
+    	int len = ns.getLength();
+    	if(len <= 1)
+    		argIsNodeSetDTM = false;
+    }
+
+    if (argIsNodeSetDTM)
+    {
+      Hashtable usedrefs = null;
+      DTMIterator ni = arg.iter();
+      int pos;
+      UnionPathIterator upi = new UnionPathIterator();
+      upi.exprSetParent(this);
+
+      while (DTM.NULL != (pos = ni.nextNode()))
+      {
+        dtm = xctxt.getDTM(pos);
+        XMLString ref = dtm.getStringValue(pos);
+
+        if (null == ref)
+          continue;
+
+        if (null == usedrefs)
+          usedrefs = new Hashtable();
+
+        if (usedrefs.get(ref) != null)
+        {
+          continue;  // We already have 'em.
+        }
+        else
+        {
+
+          // ISTRUE being used as a dummy value.
+          usedrefs.put(ref, ISTRUE);
+        }
+
+        XNodeSet nl =
+          kmgr.getNodeSetDTMByKey(xctxt, docContext, keyname, ref,
+                               xctxt.getNamespaceContext());
+                               
+        nl.setRoot(xctxt.getCurrentNode(), xctxt);
+
+//        try
+//        {
+          upi.addIterator(nl);
+//        }
+//        catch(CloneNotSupportedException cnse)
+//        {
+//          // will never happen.
+//        }
+        //mnodeset.addNodesInDocOrder(nl, xctxt); needed??
+      }
+
+      int current = xctxt.getCurrentNode();
+      upi.setRoot(current, xctxt);
+
+      nodes = new XNodeSet(upi);
+    }
+    else
+    {
+      XMLString ref = arg.xstr();
+      nodes = kmgr.getNodeSetDTMByKey(xctxt, docContext, keyname,
+                                                ref,
+                                                xctxt.getNamespaceContext());
+      nodes.setRoot(xctxt.getCurrentNode(), xctxt);
+    }
+
+    return nodes;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/KeyDeclaration.java b/src/main/java/org/apache/xalan/templates/KeyDeclaration.java
new file mode 100644
index 0000000..5caacbe
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/KeyDeclaration.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: KeyDeclaration.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+
+/**
+ * Holds the attribute declarations for the xsl:keys element.
+ * A stylesheet declares a set of keys for each document using
+ * the xsl:key element. When this set of keys contains a member
+ * with node x, name y and value z, we say that node x has a key
+ * with name y and value z.
+ * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a>
+ * @xsl.usage internal
+ */
+public class KeyDeclaration extends ElemTemplateElement
+{
+    static final long serialVersionUID = 7724030248631137918L;
+
+  /**
+   * Constructs a new element representing the xsl:key.  The parameters
+   * are needed to prioritize this key element as part of the recomposing
+   * process.  For this element, they are not automatically created
+   * because the element is never added on to the stylesheet parent.
+   */
+  public KeyDeclaration(Stylesheet parentNode, int docOrderNumber)
+  {
+    m_parentNode = parentNode;
+    setUid(docOrderNumber);
+  }
+
+  /**
+   * The "name" property.
+   * @serial
+   */
+  private QName m_name;
+
+  /**
+   * Set the "name" attribute.
+   * The name attribute specifies the name of the key. The value
+   * of the name attribute is a QName, which is expanded as
+   * described in [2.4 Qualified Names].
+   *
+   * @param name Value to set for the "name" attribute.
+   */
+  public void setName(QName name)
+  {
+    m_name = name;
+  }
+
+  /**
+   * Get the "name" attribute.
+   * The name attribute specifies the name of the key. The value
+   * of the name attribute is a QName, which is expanded as
+   * described in [2.4 Qualified Names].
+   *
+   * @return Value of the "name" attribute.
+   */
+  public QName getName()
+  {
+    return m_name;
+  }
+  
+  /**
+   * Return the node name.
+   *
+   * @return the element's name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_KEY_STRING;
+  }
+
+
+  /**
+   * The "match" attribute.
+   * @serial
+   */
+  private XPath m_matchPattern = null;
+
+  /**
+   * Set the "match" attribute.
+   * The match attribute is a Pattern; an xsl:key element gives
+   * information about the keys of any node that matches the
+   * pattern specified in the match attribute.
+   * @see <a href="http://www.w3.org/TR/xslt#patterns">patterns in XSLT Specification</a>
+   *
+   * @param v Value to set for the "match" attribute.
+   */
+  public void setMatch(XPath v)
+  {
+    m_matchPattern = v;
+  }
+
+  /**
+   * Get the "match" attribute.
+   * The match attribute is a Pattern; an xsl:key element gives
+   * information about the keys of any node that matches the
+   * pattern specified in the match attribute.
+   * @see <a href="http://www.w3.org/TR/xslt#patterns">patterns in XSLT Specification</a>
+   *
+   * @return Value of the "match" attribute.
+   */
+  public XPath getMatch()
+  {
+    return m_matchPattern;
+  }
+
+  /**
+   * The "use" attribute.
+   * @serial
+   */
+  private XPath m_use;
+
+  /**
+   * Set the "use" attribute.
+   * The use attribute is an expression specifying the values
+   * of the key; the expression is evaluated once for each node
+   * that matches the pattern.
+   *
+   * @param v Value to set for the "use" attribute.
+   */
+  public void setUse(XPath v)
+  {
+    m_use = v;
+  }
+
+  /**
+   * Get the "use" attribute.
+   * The use attribute is an expression specifying the values
+   * of the key; the expression is evaluated once for each node
+   * that matches the pattern.
+   *
+   * @return Value of the "use" attribute.
+   */
+  public XPath getUse()
+  {
+    return m_use;
+  }
+  
+  /**
+   * Get an int constant identifying the type of element.
+   * @see org.apache.xalan.templates.Constants
+   *
+   * @return The token ID for this element
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_KEY;
+  }
+  
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) 
+    throws javax.xml.transform.TransformerException
+  {
+    super.compose(sroot);
+    java.util.Vector vnames = sroot.getComposeState().getVariableNames();
+    if(null != m_matchPattern)
+      m_matchPattern.fixupVariables(vnames, sroot.getComposeState().getGlobalsSize());
+    if(null != m_use)
+      m_use.fixupVariables(vnames, sroot.getComposeState().getGlobalsSize());
+  }
+
+  /**
+   * This function is called during recomposition to
+   * control how this element is composed.
+   * @param root The root stylesheet for this transformation.
+   */
+  public void recompose(StylesheetRoot root)
+  {
+    root.recomposeKeys(this);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/NamespaceAlias.java b/src/main/java/org/apache/xalan/templates/NamespaceAlias.java
new file mode 100644
index 0000000..76aee1a
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/NamespaceAlias.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NamespaceAlias.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+/**
+ * Object to hold an xsl:namespace element.
+ * A stylesheet can use the xsl:namespace-alias element to declare
+ * that one namespace URI is an alias for another namespace URI.
+ * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+ */
+public class NamespaceAlias extends ElemTemplateElement
+{
+    static final long serialVersionUID = 456173966637810718L;
+  
+  /**
+   * Constructor NamespaceAlias
+   * 
+   * @param docOrderNumber The document order number
+   *
+   */
+  public NamespaceAlias(int docOrderNumber)
+  {
+    super();
+    m_docOrderNumber = docOrderNumber;
+  }
+
+  /**
+   * The "stylesheet-prefix" attribute.
+   * @serial
+   */
+  private String m_StylesheetPrefix;
+
+  /**
+   * Set the "stylesheet-prefix" attribute.
+   *
+   * @param v non-null prefix value.
+   */
+  public void setStylesheetPrefix(String v)
+  {
+    m_StylesheetPrefix = v;
+  }
+
+  /**
+   * Get the "stylesheet-prefix" attribute.
+   *
+   * @return non-null prefix value.
+   */
+  public String getStylesheetPrefix()
+  {
+    return m_StylesheetPrefix;
+  }
+  
+  /**
+   * The namespace in the stylesheet space.
+   * @serial
+   */
+  private String m_StylesheetNamespace;
+
+  /**
+   * Set the value for the stylesheet namespace.
+   *
+   * @param v non-null prefix value.
+   */
+  public void setStylesheetNamespace(String v)
+  {
+    m_StylesheetNamespace = v;
+  }
+
+  /**
+   * Get the value for the stylesheet namespace.
+   *
+   * @return non-null prefix value.
+   */
+  public String getStylesheetNamespace()
+  {
+    return m_StylesheetNamespace;
+  }
+
+  /**
+   * The "result-prefix" attribute.
+   * @serial
+   */
+  private String m_ResultPrefix;
+
+  /**
+   * Set the "result-prefix" attribute.
+   *
+   * @param v non-null prefix value.
+   */
+  public void setResultPrefix(String v)
+  {
+    m_ResultPrefix = v;
+  }
+
+  /**
+   * Get the "result-prefix" attribute.
+   *
+   * @return non-null prefix value.
+   */
+  public String getResultPrefix()
+  {
+    return m_ResultPrefix;
+  }
+  
+  /**
+   * The result namespace.
+   * @serial
+   */
+  private String m_ResultNamespace;
+
+  /**
+   * Set the result namespace.
+   *
+   * @param v non-null namespace value
+   */
+  public void setResultNamespace(String v)
+  {
+    m_ResultNamespace = v;
+  }
+
+  /**
+   * Get the result namespace value.
+   *
+   * @return non-null namespace value.
+   */
+  public String getResultNamespace()
+  {
+    return m_ResultNamespace;
+  }
+
+  /**
+   * This function is called to recompose() all of the namespace alias properties elements.
+   * 
+   * @param root The owning root stylesheet
+   */
+  public void recompose(StylesheetRoot root)
+  {
+    root.recomposeNamespaceAliases(this);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/OutputProperties.java b/src/main/java/org/apache/xalan/templates/OutputProperties.java
new file mode 100644
index 0000000..e4bc447
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/OutputProperties.java
@@ -0,0 +1,689 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: OutputProperties.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Vector;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.serializer.OutputPropertiesFactory;
+import org.apache.xml.serializer.OutputPropertyUtils;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.QName;
+
+/**
+ * This class provides information from xsl:output elements. It is mainly
+ * a wrapper for {@link java.util.Properties}, but can not extend that class
+ * because it must be part of the {@link org.apache.xalan.templates.ElemTemplateElement}
+ * heararchy.
+ * <p>An OutputProperties list can contain another OutputProperties list as
+ * its "defaults"; this second property list is searched if the property key
+ * is not found in the original property list.</p>
+ * @see <a href="http://www.w3.org/TR/xslt#dtd">XSLT DTD</a>
+ * @see <a href="http://www.w3.org/TR/xslt#output">xsl:output in XSLT Specification</a>
+ *
+ */
+public class OutputProperties extends ElemTemplateElement
+        implements Cloneable
+{
+    static final long serialVersionUID = -6975274363881785488L;
+  /**
+   * Creates an empty OutputProperties with no default values.
+   */
+  public OutputProperties()
+  {
+    this(org.apache.xml.serializer.Method.XML);
+  }
+
+  /**
+   * Creates an empty OutputProperties with the specified defaults.
+   *
+   * @param   defaults   the defaults.
+   */
+  public OutputProperties(Properties defaults)
+  {
+    m_properties = new Properties(defaults);
+  }
+
+  /**
+   * Creates an empty OutputProperties with the defaults specified by
+   * a property file.  The method argument is used to construct a string of
+   * the form output_[method].properties (for instance, output_html.properties).
+   * The output_xml.properties file is always used as the base.
+   * <p>At the moment, anything other than 'text', 'xml', and 'html', will
+   * use the output_xml.properties file.</p>
+   *
+   * @param   method non-null reference to method name.
+   */
+  public OutputProperties(String method)
+  {
+    m_properties = new Properties(
+        OutputPropertiesFactory.getDefaultMethodProperties(method));
+  }
+
+  /**
+   * Clone this OutputProperties, including a clone of the wrapped Properties
+   * reference.
+   *
+   * @return A new OutputProperties reference, mutation of which should not
+   *         effect this object.
+   */
+  public Object clone()
+  {
+
+    try
+    {
+      OutputProperties cloned = (OutputProperties) super.clone();
+
+      cloned.m_properties = (Properties) cloned.m_properties.clone();
+
+      return cloned;
+    }
+    catch (CloneNotSupportedException e)
+    {
+      return null;
+    }
+  }
+
+  /**
+   * Set an output property.
+   *
+   * @param key the key to be placed into the property list.
+   * @param value the value corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setProperty(QName key, String value)
+  {
+    setProperty(key.toNamespacedString(), value);
+  }
+
+  /**
+   * Set an output property.
+   *
+   * @param key the key to be placed into the property list.
+   * @param value the value corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setProperty(String key, String value)
+  {
+    if(key.equals(OutputKeys.METHOD))
+    {
+      setMethodDefaults(value);
+    }
+    
+    if (key.startsWith(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL))
+      key = OutputPropertiesFactory.S_BUILTIN_EXTENSIONS_UNIVERSAL
+         + key.substring(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL_LEN);
+    
+    m_properties.put(key, value);
+  }
+
+  /**
+   * Searches for the property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list with the specified key value.
+   */
+  public String getProperty(QName key)
+  {
+    return m_properties.getProperty(key.toNamespacedString());
+  }
+
+  /**
+   * Searches for the property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list with the specified key value.
+   */
+  public String getProperty(String key) 
+  {
+    if (key.startsWith(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL))
+      key = OutputPropertiesFactory.S_BUILTIN_EXTENSIONS_UNIVERSAL 
+        + key.substring(OutputPropertiesFactory.S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL_LEN);
+    return m_properties.getProperty(key);
+  }
+
+  /**
+   * Set an output property.
+   *
+   * @param key the key to be placed into the property list.
+   * @param value the value corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setBooleanProperty(QName key, boolean value)
+  {
+    m_properties.put(key.toNamespacedString(), value ? "yes" : "no");
+  }
+
+  /**
+   * Set an output property.
+   *
+   * @param key the key to be placed into the property list.
+   * @param value the value corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setBooleanProperty(String key, boolean value)
+  {
+    m_properties.put(key, value ? "yes" : "no");
+  }
+
+  /**
+   * Searches for the boolean property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>false</code> if the property is not found, or if the value is other
+   * than "yes".
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list as a boolean value, or false
+   * if null or not "yes".
+   */
+  public boolean getBooleanProperty(QName key)
+  {
+    return getBooleanProperty(key.toNamespacedString());
+  }
+
+  /**
+   * Searches for the boolean property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>false</code> if the property is not found, or if the value is other
+   * than "yes".
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list as a boolean value, or false
+   * if null or not "yes".
+   */
+  public boolean getBooleanProperty(String key)
+  {
+    return OutputPropertyUtils.getBooleanProperty(key, m_properties);
+  }
+
+  /**
+   * Set an output property.
+   *
+   * @param key the key to be placed into the property list.
+   * @param value the value corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setIntProperty(QName key, int value)
+  {
+    setIntProperty(key.toNamespacedString(), value);
+  }
+
+  /**
+   * Set an output property.
+   *
+   * @param key the key to be placed into the property list.
+   * @param value the value corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setIntProperty(String key, int value)
+  {
+    m_properties.put(key, Integer.toString(value));
+  }
+
+  /**
+   * Searches for the int property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>false</code> if the property is not found, or if the value is other
+   * than "yes".
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list as a int value, or false
+   * if null or not a number.
+   */
+  public int getIntProperty(QName key)
+  {
+    return getIntProperty(key.toNamespacedString());
+  }
+
+  /**
+   * Searches for the int property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>false</code> if the property is not found, or if the value is other
+   * than "yes".
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list as a int value, or false
+   * if null or not a number.
+   */
+  public int getIntProperty(String key)
+  {
+    return OutputPropertyUtils.getIntProperty(key, m_properties);
+  }
+
+
+  /**
+   * Set an output property with a QName value.  The QName will be turned
+   * into a string with the namespace in curly brackets.
+   *
+   * @param key the key to be placed into the property list.
+   * @param value the value corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setQNameProperty(QName key, QName value)
+  {
+    setQNameProperty(key.toNamespacedString(), value);
+  }
+  
+  /**
+   * Reset the default properties based on the method.
+   *
+   * @param method the method value.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setMethodDefaults(String method)
+  {
+        String defaultMethod = m_properties.getProperty(OutputKeys.METHOD);
+ 
+        if((null == defaultMethod) || !defaultMethod.equals(method)
+         // bjm - add the next condition as a hack
+         // but it is because both output_xml.properties and
+         // output_unknown.properties have the same method=xml
+         // for their default. Otherwise we end up with
+         // a ToUnknownStream wraping a ToXMLStream even
+         // when the users says method="xml"
+         //
+         || defaultMethod.equals("xml")
+         )
+        {
+            Properties savedProps = m_properties;
+            Properties newDefaults = 
+                OutputPropertiesFactory.getDefaultMethodProperties(method);
+            m_properties = new Properties(newDefaults);
+            copyFrom(savedProps, false);
+        }
+  }
+  
+
+  /**
+   * Set an output property with a QName value.  The QName will be turned
+   * into a string with the namespace in curly brackets.
+   *
+   * @param key the key to be placed into the property list.
+   * @param value the value corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setQNameProperty(String key, QName value)
+  {
+    setProperty(key, value.toNamespacedString());
+  }
+
+  /**
+   * Searches for the qname property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list as a QName value, or false
+   * if null or not "yes".
+   */
+  public QName getQNameProperty(QName key)
+  {
+    return getQNameProperty(key.toNamespacedString());
+  }
+
+  /**
+   * Searches for the qname property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list as a QName value, or false
+   * if null or not "yes".
+   */
+  public QName getQNameProperty(String key)
+  {
+    return getQNameProperty(key, m_properties);
+  }
+
+  /**
+   * Searches for the qname property with the specified key in the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @param props the list of properties to search in.
+   * @return  the value in this property list as a QName value, or false
+   * if null or not "yes".
+   */
+  public static QName getQNameProperty(String key, Properties props)
+  {
+
+    String s = props.getProperty(key);
+
+    if (null != s)
+      return QName.getQNameFromString(s);
+    else
+      return null;
+  }
+
+  /**
+   * Set an output property with a QName list value.  The QNames will be turned
+   * into strings with the namespace in curly brackets.
+   *
+   * @param key the key to be placed into the property list.
+   * @param v non-null list of QNames corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setQNameProperties(QName key, Vector v)
+  {
+    setQNameProperties(key.toNamespacedString(), v);
+  }
+
+  /**
+   * Set an output property with a QName list value.  The QNames will be turned
+   * into strings with the namespace in curly brackets.
+   *
+   * @param key the key to be placed into the property list.
+   * @param v non-null list of QNames corresponding to <tt>key</tt>.
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setQNameProperties(String key, Vector v)
+  {
+
+    int s = v.size();
+
+    // Just an initial guess at reasonable tuning parameters
+    FastStringBuffer fsb = new FastStringBuffer(9,9);
+
+    for (int i = 0; i < s; i++)
+    {
+      QName qname = (QName) v.elementAt(i);
+
+      fsb.append(qname.toNamespacedString());
+      // Don't append space after last value
+      if (i < s-1) 
+        fsb.append(' ');
+    }
+
+    m_properties.put(key, fsb.toString());
+  }
+
+  /**
+   * Searches for the list of qname properties with the specified key in
+   * the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list as a vector of QNames, or false
+   * if null or not "yes".
+   */
+  public Vector getQNameProperties(QName key)
+  {
+    return getQNameProperties(key.toNamespacedString());
+  }
+
+  /**
+   * Searches for the list of qname properties with the specified key in
+   * the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @return  the value in this property list as a vector of QNames, or false
+   * if null or not "yes".
+   */
+  public Vector getQNameProperties(String key)
+  {
+    return getQNameProperties(key, m_properties);
+  }
+
+  /**
+   * Searches for the list of qname properties with the specified key in
+   * the property list.
+   * If the key is not found in this property list, the default property list,
+   * and its defaults, recursively, are then checked. The method returns
+   * <code>null</code> if the property is not found.
+   *
+   * @param   key   the property key.
+   * @param props the list of properties to search in.
+   * @return  the value in this property list as a vector of QNames, or false
+   * if null or not "yes".
+   */
+  public static Vector getQNameProperties(String key, Properties props)
+  {
+
+    String s = props.getProperty(key);
+
+    if (null != s)
+    {
+      Vector v = new Vector();
+      int l = s.length();
+      boolean inCurly = false;
+      FastStringBuffer buf = new FastStringBuffer();
+
+      // parse through string, breaking on whitespaces.  I do this instead 
+      // of a tokenizer so I can track whitespace inside of curly brackets, 
+      // which theoretically shouldn't happen if they contain legal URLs.
+      for (int i = 0; i < l; i++)
+      {
+        char c = s.charAt(i);
+
+        if (Character.isWhitespace(c))
+        {
+          if (!inCurly)
+          {
+            if (buf.length() > 0)
+            {
+              QName qname = QName.getQNameFromString(buf.toString());
+              v.addElement(qname);
+              buf.reset();
+            }
+            continue;
+          }
+        }
+        else if ('{' == c)
+          inCurly = true;
+        else if ('}' == c)
+          inCurly = false;
+
+        buf.append(c);
+      }
+
+      if (buf.length() > 0)
+      {
+        QName qname = QName.getQNameFromString(buf.toString());
+        v.addElement(qname);
+        buf.reset();
+      }
+
+      return v;
+    }
+    else
+      return null;
+  }
+
+  /**
+   * This function is called to recompose all of the output format extended elements.
+   *
+   * @param root non-null reference to the stylesheet root object.
+   */
+  public void recompose(StylesheetRoot root)
+    throws TransformerException
+  {
+    root.recomposeOutput(this);
+  }
+
+  /**
+   * This function is called after everything else has been
+   * recomposed, and allows the template to set remaining
+   * values that may be based on some other property that
+   * depends on recomposition.
+   */
+  public void compose(StylesheetRoot sroot) throws TransformerException
+  {
+
+    super.compose(sroot);
+
+  }
+
+  /**
+   * Get the Properties object that this class wraps.
+   *
+   * @return non-null reference to Properties object.
+   */
+  public Properties getProperties()
+  {
+    return m_properties;
+  }
+  
+  /**
+   * Copy the keys and values from the source to this object.  This will
+   * not copy the default values.  This is meant to be used by going from
+   * a higher precedence object to a lower precedence object, so that if a
+   * key already exists, this method will not reset it.
+   *
+   * @param src non-null reference to the source properties.
+   */
+  public void copyFrom(Properties src)
+  {
+    copyFrom(src, true);
+  }
+
+  /**
+   * Copy the keys and values from the source to this object.  This will
+   * not copy the default values.  This is meant to be used by going from
+   * a higher precedence object to a lower precedence object, so that if a
+   * key already exists, this method will not reset it.
+   *
+   * @param src non-null reference to the source properties.
+   * @param shouldResetDefaults true if the defaults should be reset based on 
+   *                            the method property.
+   */
+  public void copyFrom(Properties src, boolean shouldResetDefaults)
+  {
+
+    Enumeration keys = src.keys();
+
+    while (keys.hasMoreElements())
+    {
+      String key = (String) keys.nextElement();
+    
+      if (!isLegalPropertyKey(key))
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{key})); //"output property not recognized: "
+      
+      Object oldValue = m_properties.get(key);
+      if (null == oldValue)
+      {
+        String val = (String) src.get(key);
+        
+        if(shouldResetDefaults && key.equals(OutputKeys.METHOD))
+        {
+          setMethodDefaults(val);
+        }
+
+        m_properties.put(key, val);
+      }
+      else if (key.equals(OutputKeys.CDATA_SECTION_ELEMENTS))
+      {
+        m_properties.put(key, (String) oldValue + " " + (String) src.get(key));
+      }
+    }
+  }
+
+  /**
+   * Copy the keys and values from the source to this object.  This will
+   * not copy the default values.  This is meant to be used by going from
+   * a higher precedence object to a lower precedence object, so that if a
+   * key already exists, this method will not reset it.
+   *
+   * @param opsrc non-null reference to an OutputProperties.
+   */
+  public void copyFrom(OutputProperties opsrc)
+    throws TransformerException
+  {
+   // Bugzilla 6157: recover from xsl:output statements
+    // checkDuplicates(opsrc);
+    copyFrom(opsrc.getProperties());
+  }
+
+  /**
+   * Report if the key given as an argument is a legal xsl:output key.
+   *
+   * @param key non-null reference to key name.
+   *
+   * @return true if key is legal.
+   */
+  public static boolean isLegalPropertyKey(String key)
+  {
+
+    return (key.equals(OutputKeys.CDATA_SECTION_ELEMENTS)
+            || key.equals(OutputKeys.DOCTYPE_PUBLIC)
+            || key.equals(OutputKeys.DOCTYPE_SYSTEM)
+            || key.equals(OutputKeys.ENCODING)
+            || key.equals(OutputKeys.INDENT)
+            || key.equals(OutputKeys.MEDIA_TYPE)
+            || key.equals(OutputKeys.METHOD)
+            || key.equals(OutputKeys.OMIT_XML_DECLARATION)
+            || key.equals(OutputKeys.STANDALONE)
+            || key.equals(OutputKeys.VERSION)
+            || (key.length() > 0) 
+                  && (key.charAt(0) == '{') 
+                  && (key.lastIndexOf('{') == 0)
+                  && (key.indexOf('}') > 0)
+                  && (key.lastIndexOf('}') == key.indexOf('}')));
+  }
+
+  /** The output properties.
+   *  @serial */
+  private Properties m_properties = null;
+
+    /**
+     * Creates an empty OutputProperties with the defaults specified by
+     * a property file.  The method argument is used to construct a string of
+     * the form output_[method].properties (for instance, output_html.properties).
+     * The output_xml.properties file is always used as the base.
+     * <p>At the moment, anything other than 'text', 'xml', and 'html', will
+     * use the output_xml.properties file.</p>
+     *
+     * @param   method non-null reference to method name.
+     *
+     * @return Properties object that holds the defaults for the given method.
+     * 
+     * @deprecated Use org.apache.xml.serializer.OuputPropertiesFactory.
+     * getDefaultMethodProperties directly.
+     */
+    static public Properties getDefaultMethodProperties(String method)
+    {
+        return org.apache.xml.serializer.OutputPropertiesFactory.getDefaultMethodProperties(method);
+    }
+}
diff --git a/src/main/java/org/apache/xalan/templates/RedundentExprEliminator.java b/src/main/java/org/apache/xalan/templates/RedundentExprEliminator.java
new file mode 100644
index 0000000..a2a0c03
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/RedundentExprEliminator.java
@@ -0,0 +1,1432 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: RedundentExprEliminator.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Vector;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.WrappedRuntimeException;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionNode;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPath;
+import org.apache.xpath.axes.AxesWalker;
+import org.apache.xpath.axes.FilterExprIteratorSimple;
+import org.apache.xpath.axes.FilterExprWalker;
+import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.SelfIteratorNoPredicate;
+import org.apache.xpath.axes.WalkerFactory;
+import org.apache.xpath.axes.WalkingIterator;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.operations.VariableSafeAbsRef;
+
+/**
+ * This class eleminates redundent XPaths from a given subtree, 
+ * and also collects all absolute paths within the subtree.  First 
+ * it must be called as a visitor to the subtree, and then 
+ * eleminateRedundent must be called.
+ */
+public class RedundentExprEliminator extends XSLTVisitor
+{
+  Vector m_paths;
+  Vector m_absPaths;
+  boolean m_isSameContext;
+  AbsPathChecker m_absPathChecker = new AbsPathChecker();
+  
+  private static int m_uniquePseudoVarID = 1;
+  static final String PSUEDOVARNAMESPACE = Constants.S_VENDORURL+"/xalan/psuedovar";
+ 
+  public static final boolean DEBUG = false;
+  public static final boolean DIAGNOSE_NUM_PATHS_REDUCED = false;
+  public static final boolean DIAGNOSE_MULTISTEPLIST = false;
+
+  /**
+   * So we can reuse it over and over again.
+   */
+  VarNameCollector m_varNameCollector = new VarNameCollector();
+
+  /**
+   * Construct a RedundentExprEliminator.
+   */
+  public RedundentExprEliminator()
+  {
+    m_isSameContext = true;
+    m_absPaths = new Vector();
+    m_paths = null;
+  }
+  
+  
+  /**
+   * Method to be called after the all expressions within an  
+   * node context have been visited.  It eliminates redundent 
+   * expressions by creating a variable in the psuedoVarRecipient 
+   * for each redundent expression, and then rewriting the redundent 
+   * expression to be a variable reference.
+   * 
+   * @param psuedoVarRecipient The recipient of the psuedo vars.  The 
+   * variables will be inserted as first children of the element, before 
+   * any existing variables.
+   */
+  public void eleminateRedundentLocals(ElemTemplateElement psuedoVarRecipient)
+  {
+    eleminateRedundent(psuedoVarRecipient, m_paths);
+  }
+  
+  /**
+   * Method to be called after the all global expressions within a stylesheet 
+   * have been collected.  It eliminates redundent 
+   * expressions by creating a variable in the psuedoVarRecipient 
+   * for each redundent expression, and then rewriting the redundent 
+   * expression to be a variable reference.
+   * 
+   */
+  public void eleminateRedundentGlobals(StylesheetRoot stylesheet)
+  {
+    eleminateRedundent(stylesheet, m_absPaths);
+  }
+
+  
+  /**
+   * Method to be called after the all expressions within an  
+   * node context have been visited.  It eliminates redundent 
+   * expressions by creating a variable in the psuedoVarRecipient 
+   * for each redundent expression, and then rewriting the redundent 
+   * expression to be a variable reference.
+   * 
+   * @param psuedoVarRecipient The owner of the subtree from where the 
+   *                           paths were collected.
+   * @param paths A vector of paths that hold ExpressionOwner objects, 
+   *              which must yield LocationPathIterators.
+   */
+  protected void eleminateRedundent(ElemTemplateElement psuedoVarRecipient, Vector paths)
+  {
+    int n = paths.size();
+    int numPathsEliminated = 0;
+    int numUniquePathsEliminated = 0;
+    for (int i = 0; i < n; i++)
+    {
+      ExpressionOwner owner = (ExpressionOwner) paths.elementAt(i);
+      if (null != owner)
+      {
+        int found = findAndEliminateRedundant(i + 1, i, owner, psuedoVarRecipient, paths);
+        if (found > 0)
+                  numUniquePathsEliminated++;
+        numPathsEliminated += found;
+      }
+    }
+    
+    eleminateSharedPartialPaths(psuedoVarRecipient, paths);
+    
+    if(DIAGNOSE_NUM_PATHS_REDUCED)
+		diagnoseNumPaths(paths, numPathsEliminated, numUniquePathsEliminated);
+  }
+  
+  /**
+   * Eliminate the shared partial paths in the expression list.
+   * 
+   * @param psuedoVarRecipient The recipient of the psuedo vars.
+   * 
+   * @param paths A vector of paths that hold ExpressionOwner objects, 
+   *              which must yield LocationPathIterators.
+   */
+  protected void eleminateSharedPartialPaths(ElemTemplateElement psuedoVarRecipient, Vector paths)
+  {
+  	MultistepExprHolder list = createMultistepExprList(paths);
+  	if(null != list)
+  	{
+  		if(DIAGNOSE_MULTISTEPLIST)
+        	list.diagnose();
+        	
+        boolean isGlobal = (paths == m_absPaths);
+        	
+        // Iterate over the list, starting with the most number of paths, 
+        // trying to find the longest matches first.
+        int longestStepsCount = list.m_stepCount;
+    	for (int i = longestStepsCount-1; i >= 1; i--)
+    	{
+    		MultistepExprHolder next = list;
+        	while(null != next)
+        	{
+        		if(next.m_stepCount < i)
+        			break;
+				list = matchAndEliminatePartialPaths(next, list, isGlobal, i, psuedoVarRecipient);
+				next = next.m_next;
+        	}
+    	}
+  	}
+  }
+
+  /**
+   * For a given path, see if there are any partitial matches in the list, 
+   * and, if there are, replace those partial paths with psuedo variable refs,
+   * and create the psuedo variable decl.
+   * 
+   * @return The head of the list, which may have changed.
+   */
+  protected MultistepExprHolder matchAndEliminatePartialPaths(MultistepExprHolder testee, 
+                                               MultistepExprHolder head,
+                                               boolean isGlobal,
+                                               int lengthToTest,
+                                               ElemTemplateElement varScope)
+  {  	
+  	if(null == testee.m_exprOwner)
+  		return head;
+  		
+    // Start with the longest possible match, and move down.
+    WalkingIterator iter1 = (WalkingIterator) testee.m_exprOwner.getExpression();
+    if(partialIsVariable(testee, lengthToTest))
+    	return head;
+    MultistepExprHolder matchedPaths = null;
+    MultistepExprHolder matchedPathsTail = null;
+    MultistepExprHolder meh = head;
+    while( null != meh)
+    {
+      if ((meh != testee) && (null != meh.m_exprOwner))
+      {
+	      WalkingIterator iter2 = (WalkingIterator) meh.m_exprOwner.getExpression();
+	      if (stepsEqual(iter1, iter2, lengthToTest))
+	      {
+	        if (null == matchedPaths)
+	        {
+	          try
+	          {
+	          	matchedPaths = (MultistepExprHolder)testee.clone();
+	          	testee.m_exprOwner = null; // So it won't be processed again.
+	          }
+	          catch(CloneNotSupportedException cnse){}
+	          matchedPathsTail = matchedPaths;
+	          matchedPathsTail.m_next = null;
+	        }
+	       
+	        try
+	        {
+	          matchedPathsTail.m_next = (MultistepExprHolder)meh.clone();
+	          meh.m_exprOwner = null; // So it won't be processed again.
+	        }
+	        catch(CloneNotSupportedException cnse){}
+	        matchedPathsTail = matchedPathsTail.m_next;
+	        matchedPathsTail.m_next = null;
+	      }
+      }
+      meh = meh.m_next;
+    }
+    	
+	int matchCount = 0;
+	if(null != matchedPaths)
+	{
+		ElemTemplateElement root = isGlobal ? varScope : findCommonAncestor(matchedPaths);
+		WalkingIterator sharedIter = (WalkingIterator)matchedPaths.m_exprOwner.getExpression();
+		WalkingIterator newIter = createIteratorFromSteps(sharedIter, lengthToTest);
+		ElemVariable var = createPseudoVarDecl(root, newIter, isGlobal);
+		if(DIAGNOSE_MULTISTEPLIST)
+			System.err.println("Created var: "+var.getName()+(isGlobal ? "(Global)" : ""));
+		while(null != matchedPaths)
+		{
+			ExpressionOwner owner = matchedPaths.m_exprOwner;
+			WalkingIterator iter = (WalkingIterator)owner.getExpression();
+			
+			if(DIAGNOSE_MULTISTEPLIST)
+				diagnoseLineNumber(iter);
+			
+			LocPathIterator newIter2 = 
+			    changePartToRef(var.getName(), iter, lengthToTest, isGlobal);
+			owner.setExpression(newIter2);
+			
+			matchedPaths = matchedPaths.m_next;
+		}
+	}
+	
+	if(DIAGNOSE_MULTISTEPLIST)
+		diagnoseMultistepList(matchCount, lengthToTest, isGlobal);
+    return head;
+  }
+  
+  /**
+   * Check if results of partial reduction will just be a variable, in 
+   * which case, skip it.
+   */
+  boolean partialIsVariable(MultistepExprHolder testee, int lengthToTest)
+  {
+  	if(1 == lengthToTest)
+  	{
+  		WalkingIterator wi = (WalkingIterator)testee.m_exprOwner.getExpression();
+  		if(wi.getFirstWalker() instanceof FilterExprWalker)
+  			return true;
+  	}
+  	return false;
+  }
+
+  /**
+   * Tell what line number belongs to a given expression.
+   */
+  protected void diagnoseLineNumber(Expression expr)
+  {
+    ElemTemplateElement e = getElemFromExpression(expr);
+    System.err.println("   " + e.getSystemId() + " Line " + e.getLineNumber());
+  }
+      
+  /**
+   * Given a linked list of expressions, find the common ancestor that is 
+   * suitable for holding a psuedo variable for shared access.
+   */
+  protected ElemTemplateElement findCommonAncestor(MultistepExprHolder head)
+  {
+  	// Not sure this algorithm is the best, but will do for the moment.
+  	int numExprs = head.getLength();
+  	// The following could be made cheaper by pre-allocating large arrays, 
+  	// but then we would have to assume a max number of reductions, 
+  	// which I am not inclined to do right now.
+    ElemTemplateElement[] elems = new ElemTemplateElement[numExprs];
+    int[] ancestorCounts = new int[numExprs];
+    
+    // Loop through, getting the parent elements, and counting the 
+    // ancestors.
+  	MultistepExprHolder next = head;
+  	int shortestAncestorCount = 10000;
+  	for(int i = 0; i < numExprs; i++)
+  	{
+  		ElemTemplateElement elem = 
+  			getElemFromExpression(next.m_exprOwner.getExpression());
+  		elems[i] = elem;
+  		int numAncestors = countAncestors(elem);
+  		ancestorCounts[i] = numAncestors;
+  		if(numAncestors < shortestAncestorCount)
+  		{
+  			shortestAncestorCount = numAncestors;
+  		}
+  		next = next.m_next;
+  	}
+  	
+  	// Now loop through and "correct" the elements that have more ancestors.
+  	for(int i = 0; i < numExprs; i++)
+  	{
+  		if(ancestorCounts[i] > shortestAncestorCount)
+  		{
+  			int numStepCorrection = ancestorCounts[i] - shortestAncestorCount;
+  			for(int j = 0; j < numStepCorrection; j++)
+  			{
+  				elems[i] = elems[i].getParentElem();
+  			}
+  		}
+  	}
+  	
+  	// Now everyone has an equal number of ancestors.  Walk up from here 
+  	// equally until all are equal.
+  	ElemTemplateElement first = null;
+  	while(shortestAncestorCount-- >= 0)
+  	{
+  		boolean areEqual = true;
+  		first = elems[0];
+  		for(int i = 1; i < numExprs; i++)
+  		{
+  			if(first != elems[i])
+  			{
+  				areEqual = false;
+  				break;
+  			}
+  		}
+  		// This second check is to make sure we have a common ancestor that is not the same 
+  		// as the expression owner... i.e. the var decl has to go above the expression owner.
+  		if(areEqual && isNotSameAsOwner(head, first) && first.canAcceptVariables())
+  		{
+  			if(DIAGNOSE_MULTISTEPLIST)
+  			{
+  				System.err.print(first.getClass().getName());
+  				System.err.println(" at   " + first.getSystemId() + " Line " + first.getLineNumber());
+  			}
+  			return first;
+  		}
+   			
+  		for(int i = 0; i < numExprs; i++)
+  		{
+  			elems[i] = elems[i].getParentElem();
+  		}
+  	}
+  	
+  	assertion(false, "Could not find common ancestor!!!");
+  	return null;
+  }
+  
+  /**
+   * Find out if the given ElemTemplateElement is not the same as one of 
+   * the ElemTemplateElement owners of the expressions.
+   * 
+   * @param head Head of linked list of expression owners.
+   * @param ete The ElemTemplateElement that is a candidate for a psuedo 
+   * variable parent.
+   * @return true if the given ElemTemplateElement is not the same as one of 
+   * the ElemTemplateElement owners of the expressions.  This is to make sure 
+   * we find an ElemTemplateElement that is in a viable position to hold 
+   * psuedo variables that are visible to the references.
+   */
+  protected boolean isNotSameAsOwner(MultistepExprHolder head, ElemTemplateElement ete)
+  {
+  	MultistepExprHolder next = head;
+  	while(null != next)
+  	{
+  		ElemTemplateElement elemOwner = getElemFromExpression(next.m_exprOwner.getExpression());
+  		if(elemOwner == ete)
+  			return false;
+  		next = next.m_next;
+  	}
+  	return true;
+  }
+  
+  /**
+   * Count the number of ancestors that a ElemTemplateElement has.
+   * 
+   * @param elem An representation of an element in an XSLT stylesheet.
+   * @return The number of ancestors of elem (including the element itself).
+   */
+  protected int countAncestors(ElemTemplateElement elem)
+  {
+  	int count = 0;
+  	while(null != elem)
+  	{
+  		count++;
+  		elem = elem.getParentElem();
+  	}
+  	return count;
+  }
+
+  /**
+   * Print out diagnostics about partial multistep evaluation.
+   */
+  protected void diagnoseMultistepList(
+      int matchCount,
+      int lengthToTest,
+      boolean isGlobal)
+  {
+      if (matchCount > 0)
+        {
+        System.err.print(
+          "Found multistep matches: " + matchCount + ", " + lengthToTest + " length");
+        if (isGlobal)
+              System.err.println(" (global)");
+        else
+              System.err.println();
+      }
+  }
+  
+  /**
+   * Change a given number of steps to a single variable reference.
+   * 
+   * @param uniquePseudoVarName The name of the variable reference.
+   * @param wi The walking iterator that is to be changed.
+   * @param numSteps The number of steps to be changed.
+   * @param isGlobal true if this will be a global reference.
+   */
+  protected LocPathIterator changePartToRef(final QName uniquePseudoVarName, WalkingIterator wi, 
+                                 final int numSteps, final boolean isGlobal)
+  {
+  	Variable var = new Variable();
+  	var.setQName(uniquePseudoVarName);
+  	var.setIsGlobal(isGlobal);
+  	if(isGlobal)
+  	{	ElemTemplateElement elem = getElemFromExpression(wi);
+  		StylesheetRoot root = elem.getStylesheetRoot();
+  		Vector vars = root.getVariablesAndParamsComposed();
+  		var.setIndex(vars.size()-1);
+  	}
+  	
+  	// Walk to the first walker after the one's we are replacing.
+  	AxesWalker walker = wi.getFirstWalker();
+  	for(int i = 0; i < numSteps; i++)
+  	{
+  		assertion(null != walker, "Walker should not be null!");
+  		walker = walker.getNextWalker();
+  	}
+  	
+  	if(null != walker)
+  	{
+  	
+  	  FilterExprWalker few = new FilterExprWalker(wi);
+  	  few.setInnerExpression(var);
+  	  few.exprSetParent(wi);
+  	  few.setNextWalker(walker);
+  	  walker.setPrevWalker(few);
+  	  wi.setFirstWalker(few);
+  	  return wi;
+  	}
+  	else
+  	{
+  	  FilterExprIteratorSimple feis = new FilterExprIteratorSimple(var);
+  	  feis.exprSetParent(wi.exprGetParent());
+  	  return feis;
+  	}
+  }
+  
+  /**
+   * Create a new WalkingIterator from the steps in another WalkingIterator.
+   * 
+   * @param wi The iterator from where the steps will be taken.
+   * @param numSteps The number of steps from the first to copy into the new 
+   *                 iterator.
+   * @return The new iterator.
+   */
+  protected WalkingIterator createIteratorFromSteps(final WalkingIterator wi, int numSteps)
+  {
+  	WalkingIterator newIter = new WalkingIterator(wi.getPrefixResolver());
+  	try
+  	{
+  		AxesWalker walker = (AxesWalker)wi.getFirstWalker().clone();
+  		newIter.setFirstWalker(walker);
+  		walker.setLocPathIterator(newIter);
+  		for(int i = 1; i < numSteps; i++)
+  		{
+  			AxesWalker next = (AxesWalker)walker.getNextWalker().clone();
+  			walker.setNextWalker(next);
+  			next.setLocPathIterator(newIter);
+  			walker = next;
+  		}
+  		walker.setNextWalker(null);
+  	}
+  	catch(CloneNotSupportedException cnse)
+  	{
+  		throw new WrappedRuntimeException(cnse);
+  	}
+  	return newIter;
+  }
+    
+  /**
+   * Compare a given number of steps between two iterators, to see if they are equal.
+   * 
+   * @param iter1 The first iterator to compare.
+   * @param iter2 The second iterator to compare.
+   * @param numSteps The number of steps to compare.
+   * @return true If the given number of steps are equal.
+   * 
+   */
+  protected boolean stepsEqual(WalkingIterator iter1, WalkingIterator iter2, 
+                                         int numSteps)
+  {
+  	AxesWalker aw1 = iter1.getFirstWalker();
+  	AxesWalker aw2 = iter2.getFirstWalker();
+  	
+  	for(int i = 0; (i < numSteps); i++)
+  	{
+  		if((null == aw1) || (null == aw2))
+  		 	return false;
+  		 	
+  		if(!aw1.deepEquals(aw2))
+  			return false;
+  		
+  		aw1 = aw1.getNextWalker();
+  		aw2 = aw2.getNextWalker();
+  	}
+  	
+  	assertion((null != aw1) || (null != aw2), "Total match is incorrect!");
+  	
+  	return true;
+  }
+  
+  /**
+   * For the reduction of location path parts, create a list of all 
+   * the multistep paths with more than one step, sorted by the 
+   * number of steps, with the most steps occuring earlier in the list.
+   * If the list is only one member, don't bother returning it.
+   * 
+   * @param paths Vector of ExpressionOwner objects, which may contain null entries. 
+   *              The ExpressionOwner objects must own LocPathIterator objects.
+   * @return null if no multipart paths are found or the list is only of length 1, 
+   * otherwise the first MultistepExprHolder in a linked list of these objects.
+   */
+  protected MultistepExprHolder createMultistepExprList(Vector paths)
+  {
+  	MultistepExprHolder first = null;
+  	int n = paths.size();
+  	for(int i = 0; i < n; i++)
+  	{
+  		ExpressionOwner eo = (ExpressionOwner)paths.elementAt(i);
+  		if(null == eo)
+  			continue;
+  			
+  		// Assuming location path iterators should be OK.
+  		LocPathIterator lpi = (LocPathIterator)eo.getExpression();
+  		int numPaths = countSteps(lpi);
+  		if(numPaths > 1)
+  		{
+  			if(null == first)
+  				first = new MultistepExprHolder(eo, numPaths, null);
+  			else
+  				first = first.addInSortedOrder(eo, numPaths);
+  		}
+  	}
+  	
+  	if((null == first) || (first.getLength() <= 1))
+  		return null;
+  	else
+  		return first;
+  }
+  
+  /**
+   * Look through the vector from start point, looking for redundant occurances.
+   * When one or more are found, create a psuedo variable declaration, insert 
+   * it into the stylesheet, and replace the occurance with a reference to 
+   * the psuedo variable.  When a redundent variable is found, it's slot in 
+   * the vector will be replaced by null.
+   * 
+   * @param start The position to start looking in the vector.
+   * @param firstOccuranceIndex The position of firstOccuranceOwner.
+   * @param firstOccuranceOwner The owner of the expression we are looking for.
+   * @param psuedoVarRecipient Where to put the psuedo variables.
+   * 
+   * @return The number of expression occurances that were modified.
+   */
+  protected int findAndEliminateRedundant(int start, int firstOccuranceIndex,
+                         ExpressionOwner firstOccuranceOwner, 
+                         ElemTemplateElement psuedoVarRecipient,
+                         Vector paths) 
+                 throws org.w3c.dom.DOMException 
+  {
+	MultistepExprHolder head = null;
+	MultistepExprHolder tail = null;
+	int numPathsFound = 0;
+	int n = paths.size();
+	
+	Expression expr1 = firstOccuranceOwner.getExpression();
+	if(DEBUG)
+		assertIsLocPathIterator(expr1, firstOccuranceOwner);
+	boolean isGlobal = (paths == m_absPaths);
+	LocPathIterator lpi = (LocPathIterator)expr1;
+	int stepCount = countSteps(lpi);
+	for(int j = start; j < n; j++)
+	{
+		ExpressionOwner owner2 = (ExpressionOwner)paths.elementAt(j);
+		if(null != owner2)
+		{
+			Expression expr2 = owner2.getExpression();
+			boolean isEqual = expr2.deepEquals(lpi);
+			if(isEqual)
+			{  		
+				LocPathIterator lpi2  = (LocPathIterator)expr2;				
+				if(null == head)
+				{
+					head = new MultistepExprHolder(firstOccuranceOwner, stepCount, null);
+					tail = head;
+					numPathsFound++;
+				}
+				tail.m_next = new MultistepExprHolder(owner2, stepCount, null);
+				tail = tail.m_next;
+	
+				// Null out the occurance, so we don't have to test it again.
+				paths.setElementAt(null, j);
+				
+				// foundFirst = true;
+				numPathsFound++;
+			}
+		}
+	}
+	
+	// Change all globals in xsl:templates, etc, to global vars no matter what.
+	if((0 == numPathsFound) && isGlobal)
+	{
+      head = new MultistepExprHolder(firstOccuranceOwner, stepCount, null);
+      numPathsFound++;
+	}
+	
+	if(null != head)
+	{
+		ElemTemplateElement root = isGlobal ? psuedoVarRecipient : findCommonAncestor(head);
+		LocPathIterator sharedIter = (LocPathIterator)head.m_exprOwner.getExpression();
+		ElemVariable var = createPseudoVarDecl(root, sharedIter, isGlobal);
+		if(DIAGNOSE_MULTISTEPLIST)
+			System.err.println("Created var: "+var.getName()+(isGlobal ? "(Global)" : ""));
+		QName uniquePseudoVarName = var.getName();
+		while(null != head)
+		{
+			ExpressionOwner owner = head.m_exprOwner;	
+			if(DIAGNOSE_MULTISTEPLIST)
+				diagnoseLineNumber(owner.getExpression());
+			changeToVarRef(uniquePseudoVarName, owner, paths, root);
+			head = head.m_next;
+		}
+		// Replace the first occurance with the variable's XPath, so  
+		// that further reduction may take place if needed.
+		paths.setElementAt(var.getSelect(), firstOccuranceIndex);
+	}
+	
+	return numPathsFound;
+  } 
+  
+  /**
+   * To be removed.
+   */
+  protected int oldFindAndEliminateRedundant(int start, int firstOccuranceIndex,
+                         ExpressionOwner firstOccuranceOwner, 
+                         ElemTemplateElement psuedoVarRecipient,
+                         Vector paths) 
+                 throws org.w3c.dom.DOMException 
+  {
+	QName uniquePseudoVarName = null;
+	boolean foundFirst = false;
+	int numPathsFound = 0;
+	int n = paths.size();
+	Expression expr1 = firstOccuranceOwner.getExpression();
+	if(DEBUG)
+		assertIsLocPathIterator(expr1, firstOccuranceOwner);
+	boolean isGlobal = (paths == m_absPaths);
+	LocPathIterator lpi = (LocPathIterator)expr1;
+	for(int j = start; j < n; j++)
+	{
+		ExpressionOwner owner2 = (ExpressionOwner)paths.elementAt(j);
+		if(null != owner2)
+		{
+			Expression expr2 = owner2.getExpression();
+			boolean isEqual = expr2.deepEquals(lpi);
+			if(isEqual)
+			{  		
+				LocPathIterator lpi2  = (LocPathIterator)expr2;				
+				if(!foundFirst)
+				{
+					foundFirst = true;
+					// Insert variable decl into psuedoVarRecipient
+					// We want to insert this into the first legitimate 
+					// position for a variable.
+				    ElemVariable var = createPseudoVarDecl(psuedoVarRecipient, lpi, isGlobal);
+				    if(null == var)
+				    	return 0;
+				    uniquePseudoVarName = var.getName();
+	
+					changeToVarRef(uniquePseudoVarName, firstOccuranceOwner, 
+					               paths, psuedoVarRecipient);
+					               
+					// Replace the first occurance with the variable's XPath, so  
+					// that further reduction may take place if needed.
+					paths.setElementAt(var.getSelect(), firstOccuranceIndex);
+					numPathsFound++;
+				}
+	
+				changeToVarRef(uniquePseudoVarName, owner2, paths, psuedoVarRecipient);
+	
+				// Null out the occurance, so we don't have to test it again.
+				paths.setElementAt(null, j);
+				
+				// foundFirst = true;
+				numPathsFound++;
+			}
+		}
+	}
+	
+	// Change all globals in xsl:templates, etc, to global vars no matter what.
+	if((0 == numPathsFound) && (paths == m_absPaths))
+	{
+      ElemVariable var = createPseudoVarDecl(psuedoVarRecipient, lpi, true);
+      if(null == var)
+        return 0;
+	  uniquePseudoVarName = var.getName();
+      changeToVarRef(uniquePseudoVarName, firstOccuranceOwner, paths, psuedoVarRecipient);
+      paths.setElementAt(var.getSelect(), firstOccuranceIndex);
+      numPathsFound++;
+	}
+	return numPathsFound;
+  }
+  
+  /**
+   * Count the steps in a given location path.
+   * 
+   * @param lpi The location path iterator that owns the steps.
+   * @return The number of steps in the given location path.
+   */
+  protected int countSteps(LocPathIterator lpi)
+  {
+  	if(lpi instanceof WalkingIterator)
+  	{
+  		WalkingIterator wi = (WalkingIterator)lpi;
+  		AxesWalker aw = wi.getFirstWalker();
+  		int count = 0;
+  		while(null != aw)
+  		{
+  			count++;
+  			aw = aw.getNextWalker();
+  		}
+  		return count;
+  	}
+  	else
+  		return 1;
+  }
+  
+  /**
+   * Change the expression owned by the owner argument to a variable reference 
+   * of the given name.
+   * 
+   * Warning: For global vars, this function relies on the variable declaration 
+   * to which it refers having been added just prior to this function being called,
+   * so that the reference index can be determined from the size of the global variables 
+   * list minus one.
+   * 
+   * @param varName The name of the variable which will be referenced.
+   * @param owner The owner of the expression which will be replaced by a variable ref.
+   * @param paths The paths list that the iterator came from, mainly to determine
+   *              if this is a local or global reduction.
+   * @param psuedoVarRecipient The element within whose scope the variable is 
+   *                           being inserted, possibly a StylesheetRoot.
+   */
+  protected void changeToVarRef(QName varName, ExpressionOwner owner, 
+                                Vector paths, ElemTemplateElement psuedoVarRecipient) 
+  {
+	Variable varRef = (paths == m_absPaths) ? new VariableSafeAbsRef() : new Variable();
+	varRef.setQName(varName);
+	if(paths == m_absPaths)
+	{
+		StylesheetRoot root = (StylesheetRoot)psuedoVarRecipient;
+		Vector globalVars = root.getVariablesAndParamsComposed();
+		// Assume this operation is occuring just after the decl has 
+		// been added.
+		varRef.setIndex(globalVars.size()-1);
+		varRef.setIsGlobal(true);
+	}
+	owner.setExpression(varRef);
+  }
+   
+  private synchronized static int getPseudoVarID(){
+      return m_uniquePseudoVarID++; 
+  }
+  
+  /**
+   * Create a psuedo variable reference that will represent the 
+   * shared redundent XPath, and add it to the stylesheet.
+   * 
+   * @param psuedoVarRecipient The broadest scope of where the variable 
+   * should be inserted, usually an xsl:template or xsl:for-each.
+   * @param lpi The LocationPathIterator that the variable should represent.
+   * @param isGlobal true if the paths are global.
+   * @return The new psuedo var element.
+   */
+  protected ElemVariable createPseudoVarDecl(
+      ElemTemplateElement psuedoVarRecipient,
+      LocPathIterator lpi, boolean isGlobal)
+      throws org.w3c.dom.DOMException
+  {
+    QName uniquePseudoVarName = new QName (PSUEDOVARNAMESPACE, "#"+getPseudoVarID());
+  		
+  	if(isGlobal)
+  	{
+  	  return createGlobalPseudoVarDecl(uniquePseudoVarName, 
+  	                                  (StylesheetRoot)psuedoVarRecipient, lpi);
+  	}
+  	else						
+      return createLocalPseudoVarDecl(uniquePseudoVarName, psuedoVarRecipient, lpi);
+  }
+  
+  /**
+   * Create a psuedo variable reference that will represent the 
+   * shared redundent XPath, for a local reduction.
+   * 
+   * @param uniquePseudoVarName The name of the new variable.
+   * @param stylesheetRoot The broadest scope of where the variable 
+   *        should be inserted, which must be a StylesheetRoot element in this case.
+   * @param lpi The LocationPathIterator that the variable should represent.
+   * @return null if the decl was not created, otherwise the new Pseudo var  
+   *              element.
+   */
+  protected ElemVariable createGlobalPseudoVarDecl(QName uniquePseudoVarName,
+                                           StylesheetRoot stylesheetRoot, 
+                                           LocPathIterator lpi) 
+        throws org.w3c.dom.DOMException 
+  {
+  	ElemVariable psuedoVar = new ElemVariable();
+  	psuedoVar.setIsTopLevel(true);
+	XPath xpath = new XPath(lpi);
+	psuedoVar.setSelect(xpath);
+	psuedoVar.setName(uniquePseudoVarName);
+	
+	Vector globalVars = stylesheetRoot.getVariablesAndParamsComposed();
+	psuedoVar.setIndex(globalVars.size());
+	globalVars.addElement(psuedoVar);
+	return psuedoVar;
+  }
+
+  
+  
+
+  /**
+   * Create a psuedo variable reference that will represent the 
+   * shared redundent XPath, for a local reduction.
+   * 
+   * @param uniquePseudoVarName The name of the new variable.
+   * @param psuedoVarRecipient The broadest scope of where the variable 
+   * should be inserted, usually an xsl:template or xsl:for-each.
+   * @param lpi The LocationPathIterator that the variable should represent.
+   * @return null if the decl was not created, otherwise the new Pseudo var  
+   *              element.
+   */
+  protected ElemVariable createLocalPseudoVarDecl(QName uniquePseudoVarName,
+                                           ElemTemplateElement psuedoVarRecipient, 
+                                           LocPathIterator lpi) 
+        throws org.w3c.dom.DOMException 
+  {
+		ElemVariable psuedoVar = new ElemVariablePsuedo();
+		
+		XPath xpath = new XPath(lpi);
+		psuedoVar.setSelect(xpath);
+		psuedoVar.setName(uniquePseudoVarName);
+
+		ElemVariable var = addVarDeclToElem(psuedoVarRecipient, lpi, psuedoVar);
+		
+		lpi.exprSetParent(var);
+		
+		return var;
+  }
+
+  /**
+   * Add the given variable to the psuedoVarRecipient.
+   */
+  protected ElemVariable addVarDeclToElem(
+    ElemTemplateElement psuedoVarRecipient,
+    LocPathIterator lpi,
+    ElemVariable psuedoVar)
+    throws org.w3c.dom.DOMException
+  {
+    // Create psuedo variable element
+    ElemTemplateElement ete = psuedoVarRecipient.getFirstChildElem();
+
+    lpi.callVisitors(null, m_varNameCollector);
+
+    // If the location path contains variables, we have to insert the 
+    // psuedo variable after the reference. (Otherwise, we want to 
+    // insert it as close as possible to the top, so we'll be sure 
+    // it is in scope for any other vars.
+    if (m_varNameCollector.getVarCount() > 0)
+    {
+      ElemTemplateElement baseElem = getElemFromExpression(lpi);
+      ElemVariable varElem = getPrevVariableElem(baseElem);
+      while (null != varElem)
+      {
+        if (m_varNameCollector.doesOccur(varElem.getName()))
+          {
+          psuedoVarRecipient = varElem.getParentElem();
+          ete = varElem.getNextSiblingElem();
+          break;
+        }
+        varElem = getPrevVariableElem(varElem);
+      }
+    }
+
+    if ((null != ete) && (Constants.ELEMNAME_PARAMVARIABLE == ete.getXSLToken()))
+    {
+      // Can't stick something in front of a param, so abandon! (see variable13.xsl)
+      if(isParam(lpi))
+        return null;
+
+      while (null != ete)
+      {
+        ete = ete.getNextSiblingElem();
+        if ((null != ete) && Constants.ELEMNAME_PARAMVARIABLE != ete.getXSLToken())
+            break;
+      }
+    }
+    psuedoVarRecipient.insertBefore(psuedoVar, ete);
+    m_varNameCollector.reset();
+    return psuedoVar;
+  }
+    
+  /**
+   * Tell if the expr param is contained within an xsl:param.
+   */
+  protected boolean isParam(ExpressionNode expr)
+  {
+  	while(null != expr)
+  	{
+  		if(expr instanceof ElemTemplateElement)
+  			break;
+  		expr = expr.exprGetParent();
+  	}
+  	if(null != expr)
+  	{
+  		ElemTemplateElement ete = (ElemTemplateElement)expr;
+  		while(null != ete)
+  		{
+  			int type = ete.getXSLToken();
+  			switch(type)
+  			{
+  				case Constants.ELEMNAME_PARAMVARIABLE:
+  					return true;
+  				case Constants.ELEMNAME_TEMPLATE:
+  				case Constants.ELEMNAME_STYLESHEET:
+  					return false;
+  			}
+  			ete = ete.getParentElem();
+  		}
+  	}
+  	return false;
+  	
+  }
+  
+  /**
+   * Find the previous occurance of a xsl:variable.  Stop 
+   * the search when a xsl:for-each, xsl:template, or xsl:stylesheet is 
+   * encountered.
+   * 
+   * @param elem Should be non-null template element.
+   * @return The first previous occurance of an xsl:variable or xsl:param, 
+   * or null if none is found.
+   */
+  protected ElemVariable getPrevVariableElem(ElemTemplateElement elem)
+  {
+  	// This could be somewhat optimized.  since getPreviousSiblingElem is a  
+  	// fairly expensive operation.
+  	while(null != (elem = getPrevElementWithinContext(elem)))
+  	{
+  		int type = elem.getXSLToken();
+  			
+  		if((Constants.ELEMNAME_VARIABLE == type) ||
+  		   (Constants.ELEMNAME_PARAMVARIABLE == type))
+  		{
+  			return (ElemVariable)elem;
+  		}
+  	}
+  	return null;
+  }
+  
+  /**
+   * Get the previous sibling or parent of the given template, stopping at 
+   * xsl:for-each, xsl:template, or xsl:stylesheet.
+   * 
+   * @param elem Should be non-null template element.
+   * @return previous sibling or parent, or null if previous is xsl:for-each, 
+   * xsl:template, or xsl:stylesheet.
+   */
+  protected ElemTemplateElement getPrevElementWithinContext(ElemTemplateElement elem)
+  {
+  	ElemTemplateElement prev = elem.getPreviousSiblingElem();
+  	if(null == prev)
+  		prev = elem.getParentElem();
+  	if(null != prev)
+  	{
+  	  int type = prev.getXSLToken();
+  	  if((Constants.ELEMNAME_FOREACH == type) || 
+  	     (Constants.ELEMNAME_TEMPLATE == type) ||
+  	     (Constants.ELEMNAME_STYLESHEET == type))
+  	  {
+  	  	prev = null;
+  	  }
+  	}
+  	return prev;
+  }
+  
+  /**
+   * From an XPath expression component, get the ElemTemplateElement 
+   * owner.
+   * 
+   * @param expr Should be static expression with proper parentage.
+   * @return Valid ElemTemplateElement, or throw a runtime exception 
+   * if it is not found.
+   */
+  protected ElemTemplateElement getElemFromExpression(Expression expr)
+  {
+  	ExpressionNode parent = expr.exprGetParent();
+  	while(null != parent)
+  	{
+  		if(parent instanceof ElemTemplateElement)
+  			return (ElemTemplateElement)parent;
+  		parent = parent.exprGetParent();
+  	}
+  	throw new RuntimeException(XSLMessages.createMessage(XSLTErrorResources.ER_ASSERT_NO_TEMPLATE_PARENT, null));
+  	// "Programmer's error! expr has no ElemTemplateElement parent!");
+  }
+      
+  /**
+   * Tell if the given LocPathIterator is relative to an absolute path, i.e. 
+   * in not dependent on the context.
+   * 
+   * @return true if the LocPathIterator is not dependent on the context node.
+   */
+  public boolean isAbsolute(LocPathIterator path)
+  {
+  	int analysis = path.getAnalysisBits();
+    boolean isAbs = (WalkerFactory.isSet(analysis, WalkerFactory.BIT_ROOT) || 
+           WalkerFactory.isSet(analysis, WalkerFactory.BIT_ANY_DESCENDANT_FROM_ROOT));
+    if(isAbs)
+    {
+    	isAbs = m_absPathChecker.checkAbsolute(path);
+    }
+    return isAbs;
+  }
+
+
+  /**
+   * Visit a LocationPath.
+   * @param owner The owner of the expression, to which the expression can 
+   *              be reset if rewriting takes place.
+   * @param path The LocationPath object.
+   * @return true if the sub expressions should be traversed.
+   */
+  public boolean visitLocationPath(ExpressionOwner owner, LocPathIterator path)
+  {
+  	// Don't optimize "." or single step variable paths.
+  	// Both of these cases could use some further optimization by themselves.
+  	if(path instanceof SelfIteratorNoPredicate)
+  	{
+  		return true;
+  	}
+  	else if(path instanceof WalkingIterator)
+  	{
+  		WalkingIterator wi = (WalkingIterator)path;
+  		AxesWalker aw = wi.getFirstWalker();
+  		if((aw instanceof FilterExprWalker) && (null == aw.getNextWalker()))
+  		{
+  			FilterExprWalker few = (FilterExprWalker)aw;
+  			Expression exp = few.getInnerExpression();
+  			if(exp instanceof Variable)
+  				return true;
+  		}
+  	}
+
+    if (isAbsolute(path) && (null != m_absPaths))
+    {
+      if(DEBUG)
+        validateNewAddition(m_absPaths, owner, path);
+      m_absPaths.addElement(owner);
+    }
+    else if (m_isSameContext && (null != m_paths))
+    {
+      if(DEBUG)
+        validateNewAddition(m_paths, owner, path);
+      m_paths.addElement(owner);
+    }
+
+    return true;
+  }
+
+  /**
+   * Visit a predicate within a location path.  Note that there isn't a 
+   * proper unique component for predicates, and that the expression will 
+   * be called also for whatever type Expression is.
+   * 
+   * @param owner The owner of the expression, to which the expression can 
+   *              be reset if rewriting takes place.
+   * @param pred The predicate object.
+   * @return true if the sub expressions should be traversed.
+   */
+  public boolean visitPredicate(ExpressionOwner owner, Expression pred)
+  {
+    boolean savedIsSame = m_isSameContext;
+    m_isSameContext = false;
+
+    // Any further down, just collect the absolute paths.
+    pred.callVisitors(owner, this);
+
+    m_isSameContext = savedIsSame;
+
+    // We've already gone down the subtree, so don't go have the caller 
+    // go any further.
+    return false;
+  }
+  
+  /**
+   * Visit an XSLT top-level instruction.
+   * 
+   * @param elem The xsl instruction element object.
+   * @return true if the sub expressions should be traversed.
+   */
+   public boolean visitTopLevelInstruction(ElemTemplateElement elem)
+   {
+     int type = elem.getXSLToken();
+     switch(type)
+     {
+       case Constants.ELEMNAME_TEMPLATE :
+         return visitInstruction(elem);
+       default:
+         return true;
+     }
+   }
+
+
+  /**
+   * Visit an XSLT instruction.  Any element that isn't called by one 
+   * of the other visit methods, will be called by this method.
+   * 
+   * @param elem The xsl instruction element object.
+   * @return true if the sub expressions should be traversed.
+   */
+  public boolean visitInstruction(ElemTemplateElement elem)
+  {
+    int type = elem.getXSLToken();
+    switch (type)
+    {
+      case Constants.ELEMNAME_CALLTEMPLATE :
+      case Constants.ELEMNAME_TEMPLATE :
+      case Constants.ELEMNAME_FOREACH :
+        {
+          
+          // Just get the select value.
+          if(type == Constants.ELEMNAME_FOREACH)
+          {
+            ElemForEach efe = (ElemForEach) elem;
+   		    
+  		    Expression select = efe.getSelect();
+  		    select.callVisitors(efe, this);
+          }
+         
+  		  Vector savedPaths = m_paths;
+  		  m_paths = new Vector();
+  		    
+  		  // Visit children.  Call the superclass callChildVisitors, because 
+  		  // we don't want to visit the xsl:for-each select attribute, or, for 
+  		  // that matter, the xsl:template's match attribute.
+  		  elem.callChildVisitors(this, false);  		
+  		  eleminateRedundentLocals(elem);
+  		    
+  		  m_paths = savedPaths;
+ 
+          // select.callVisitors(efe, this);
+          return false;
+        }
+      case Constants.ELEMNAME_NUMBER :
+      case Constants.ELEMNAME_SORT :
+        // Just collect absolute paths until and unless we can fully
+        // analyze these cases.
+        boolean savedIsSame = m_isSameContext;
+        m_isSameContext = false;
+        elem.callChildVisitors(this);
+        m_isSameContext = savedIsSame;
+        return false;
+        
+      default :
+        return true;
+    }
+  }
+  
+  // ==== DIAGNOSTIC AND DEBUG FUNCTIONS ====
+  
+  /**
+   * Print out to std err the number of paths reduced.
+   */
+  protected void diagnoseNumPaths(Vector paths, int numPathsEliminated,  
+                                  int numUniquePathsEliminated) 
+  {
+		if (numPathsEliminated > 0)
+		{ 
+		  if(paths == m_paths)
+		  {
+		    System.err.println("Eliminated " + numPathsEliminated + " total paths!");
+		    System.err.println(
+		      "Consolodated " + numUniquePathsEliminated + " redundent paths!");
+		  }
+		  else
+		  {
+		    System.err.println("Eliminated " + numPathsEliminated + " total global paths!");
+		    System.err.println(
+		      "Consolodated " + numUniquePathsEliminated + " redundent global paths!");
+		  }
+		}  
+  }
+
+
+  /**
+   * Assert that the expression is a LocPathIterator, and, if 
+   * not, try to give some diagnostic info.
+   */
+  private final void assertIsLocPathIterator(Expression expr1, ExpressionOwner eo) 
+    throws RuntimeException 
+  {
+		if(!(expr1 instanceof LocPathIterator))
+		{
+			String errMsg;
+			if(expr1 instanceof Variable)
+			{
+				errMsg = "Programmer's assertion: expr1 not an iterator: "+
+				          ((Variable)expr1).getQName();
+			}
+			else
+			{
+				errMsg = "Programmer's assertion: expr1 not an iterator: "+
+				          expr1.getClass().getName();
+			}
+			throw new RuntimeException(errMsg + ", "+
+				          eo.getClass().getName()+" "+
+				          expr1.exprGetParent());
+		}
+  }
+
+
+  /**
+   * Validate some assumptions about the new LocPathIterator and it's 
+   * owner and the state of the list.
+   */
+  private static void validateNewAddition(Vector paths, ExpressionOwner owner, 
+                                          LocPathIterator path) 
+		throws RuntimeException 
+  {
+  	assertion(owner.getExpression() == path, "owner.getExpression() != path!!!");
+	int n = paths.size();
+	// There should never be any duplicates in the list!
+	for(int i = 0; i < n; i++)
+	{
+		ExpressionOwner ew = (ExpressionOwner)paths.elementAt(i);
+		assertion(ew != owner, "duplicate owner on the list!!!");
+		assertion(ew.getExpression() != path, "duplicate expression on the list!!!");
+	}
+  }
+  
+  /**
+   * Simple assertion.
+   */
+  protected static void assertion(boolean b, String msg)
+  {
+  	if(!b)
+  	{
+  		throw new RuntimeException(XSLMessages.createMessage(XSLTErrorResources.ER_ASSERT_REDUNDENT_EXPR_ELIMINATOR, new Object[]{msg}));
+  		// "Programmer's assertion in RundundentExprEliminator: "+msg);
+  	}
+  }
+  
+  /**
+   * Since we want to sort multistep expressions by length, use 
+   * a linked list with elements of type MultistepExprHolder.
+   */
+  class MultistepExprHolder implements Cloneable
+  {
+	ExpressionOwner m_exprOwner; // Will change to null once we have processed this item.
+	final int m_stepCount;
+	MultistepExprHolder m_next;
+	
+	/**
+	 * Clone this object.
+	 */
+	public Object clone()
+		throws CloneNotSupportedException
+	{
+		return super.clone();
+	}
+	
+	/**
+	 * Create a MultistepExprHolder.
+	 * 
+	 * @param exprOwner the owner of the expression we are holding.
+	 *                  It must hold a LocationPathIterator.
+	 * @param stepCount The number of steps in the location path.
+	 */
+  	MultistepExprHolder(ExpressionOwner exprOwner, int stepCount, MultistepExprHolder next)
+  	{
+  		m_exprOwner = exprOwner;
+  		assertion(null != m_exprOwner, "exprOwner can not be null!");
+  		m_stepCount = stepCount;
+  		m_next = next;
+  	}
+	
+	/**
+	 * Add a new MultistepExprHolder in sorted order in the list.
+	 * 
+	 * @param exprOwner the owner of the expression we are holding.
+	 *                  It must hold a LocationPathIterator.
+	 * @param stepCount The number of steps in the location path.
+	 * @return The new head of the linked list.
+	 */
+	MultistepExprHolder addInSortedOrder(ExpressionOwner exprOwner, int stepCount)
+	{
+		MultistepExprHolder first = this;
+		MultistepExprHolder next = this;
+		MultistepExprHolder prev = null;
+		while(null != next)
+		{
+			if(stepCount >= next.m_stepCount)
+			{
+				MultistepExprHolder newholder = new MultistepExprHolder(exprOwner, stepCount, next);
+				if(null == prev)
+					first = newholder;
+				else
+					prev.m_next = newholder;
+					
+				return first;
+			}
+			prev = next;
+			next = next.m_next;
+		}
+		
+		prev.m_next = new MultistepExprHolder(exprOwner, stepCount, null);
+		return first;
+	}
+	
+	/**
+	 * Remove the given element from the list.  'this' should 
+	 * be the head of the list.  If the item to be removed is not 
+	 * found, an assertion will be made.
+	 * 
+	 * @param itemToRemove The item to remove from the list.
+	 * @return The head of the list, which may have changed if itemToRemove 
+	 * is the same as this element.  Null if the item to remove is the 
+	 * only item in the list.
+	 */
+	MultistepExprHolder unlink(MultistepExprHolder itemToRemove)
+	{
+		MultistepExprHolder first = this;
+		MultistepExprHolder next = this;
+		MultistepExprHolder prev = null;
+		while(null != next)
+		{
+			if(next == itemToRemove)
+			{
+				if(null == prev)
+					first = next.m_next;
+				else
+					prev.m_next = next.m_next;
+				
+				next.m_next = null;
+					
+				return first;
+			}
+			prev = next;
+			next = next.m_next;
+		}
+		
+		assertion(false, "unlink failed!!!");
+		return null;
+	}
+		
+	/**
+	 * Get the number of linked list items.
+	 */
+	int getLength()
+	{
+		int count = 0;
+		MultistepExprHolder next = this;
+		while(null != next)
+		{
+			count++;
+			next = next.m_next;
+		}
+		return count;
+	}
+	
+    /**
+     * Print diagnostics out for the multistep list.
+     */
+    protected void diagnose()
+    {
+      System.err.print("Found multistep iterators: " + this.getLength() + "  ");
+      MultistepExprHolder next = this;
+      while (null != next)
+      {
+        System.err.print("" + next.m_stepCount);
+        next = next.m_next;
+        if (null != next)
+              System.err.print(", ");
+      }
+      System.err.println();
+    }
+	
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/Stylesheet.java b/src/main/java/org/apache/xalan/templates/Stylesheet.java
new file mode 100644
index 0000000..11bbe9e
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/Stylesheet.java
@@ -0,0 +1,1516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Stylesheet.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.Vector;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.StringVector;
+import org.apache.xml.utils.SystemIDResolver;
+
+/**
+ * Represents a stylesheet element.
+ * <p>All properties in this class have a fixed form of bean-style property
+ * accessors for all properties that represent XSL attributes or elements.
+ * These properties have setter method names accessed generically by the
+ * processor, and so these names must be fixed according to the system
+ * defined in the <a href="XSLTAttributeDef#getSetterMethodName">getSetterMethodName</a>
+ * function.</p>
+ * <p><pre>
+ * <!ENTITY % top-level "
+ *  (xsl:import*,
+ *   (xsl:include
+ *   | xsl:strip-space
+ *   | xsl:preserve-space
+ *   | xsl:output
+ *   | xsl:key
+ *   | xsl:decimal-format
+ *   | xsl:attribute-set
+ *   | xsl:variable
+ *   | xsl:param
+ *   | xsl:template
+ *   | xsl:namespace-alias
+ *   %non-xsl-top-level;)*)
+ * ">
+ *
+ * <!ENTITY % top-level-atts '
+ *   extension-element-prefixes CDATA #IMPLIED
+ *   exclude-result-prefixes CDATA #IMPLIED
+ *   id ID #IMPLIED
+ *   version NMTOKEN #REQUIRED
+ *   xmlns:xsl CDATA #FIXED "http://www.w3.org/1999/XSL/Transform"
+ *   %space-att;
+ * '>
+ *
+ * <!ELEMENT xsl:stylesheet %top-level;>
+ * <!ATTLIST xsl:stylesheet %top-level-atts;>
+ *
+ * <!ELEMENT xsl:transform %top-level;>
+ * <!ATTLIST xsl:transform %top-level-atts;>
+ *
+ * </p></pre>
+ * @see <a href="http://www.w3.org/TR/xslt#section-Stylesheet-Structure">section-Stylesheet-Structure in XSLT Specification</a>
+ */
+public class Stylesheet extends ElemTemplateElement
+        implements java.io.Serializable /* , Document */
+{
+    static final long serialVersionUID = 2085337282743043776L;
+
+  /**
+   * Constructor for a Stylesheet.
+   * @param parent  The including or importing stylesheet.
+   */
+  public Stylesheet(Stylesheet parent)
+  {
+
+    if (null != parent)
+    {
+      m_stylesheetParent = parent;
+      m_stylesheetRoot = parent.getStylesheetRoot();
+    }
+  }
+
+  /**
+   * Get the owning stylesheet.  This looks up the
+   * inheritance chain until it calls getStylesheet
+   * on a Stylesheet object, which will return itself.
+   *
+   * @return The owning stylesheet, itself.
+   */
+  public Stylesheet getStylesheet()
+  {
+    return this;
+  }
+
+  /**
+   * Tell if this can be cast to a StylesheetComposed, meaning, you
+   * can ask questions from getXXXComposed functions.
+   *
+   * @return False if this is not a StylesheetComposed
+   */
+  public boolean isAggregatedType()
+  {
+    return false;
+  }
+
+  /**
+   * Tell if this is the root of the stylesheet tree.
+   *
+   * @return False is this is not the root of the stylesheet tree.
+   */
+  public boolean isRoot()
+  {
+    return false;
+  }
+
+  /**
+   * Extension to be used when serializing to disk.
+   */
+  public static final String STYLESHEET_EXT = ".lxc";
+
+  /**
+   * Read the stylesheet from a serialization stream.
+   *
+   * @param stream Input stream to read from
+   *
+   * @throws IOException
+   * @throws TransformerException
+   */
+  private void readObject(ObjectInputStream stream)
+          throws IOException, TransformerException
+  {
+
+    // System.out.println("Reading Stylesheet");
+    try
+    {
+      stream.defaultReadObject();
+    }
+    catch (ClassNotFoundException cnfe)
+    {
+      throw new TransformerException(cnfe);
+    }
+
+    // System.out.println("Done reading Stylesheet");
+  }
+
+  /**
+   * Write out the given output stream 
+   *
+   *
+   * @param stream The output stream to write out
+   *
+   * @throws IOException
+   */
+  private void writeObject(ObjectOutputStream stream) throws IOException
+  {
+
+    // System.out.println("Writing Stylesheet");
+    stream.defaultWriteObject();
+
+    // System.out.println("Done writing Stylesheet");
+  }
+
+  //============== XSLT Properties =================
+
+  /**
+   * The "xmlns:xsl" property.
+   * @serial
+   */
+  private String m_XmlnsXsl;
+
+  /**
+   * Set the "xmlns:xsl" property.
+   * @see <a href="http://www.w3.org/TR/xslt#xslt-namespace">xslt-namespace in XSLT Specification</a>
+   *
+   * @param v The value to be set for the "xmlns:xsl" property.
+   */
+  public void setXmlnsXsl(String v)
+  {
+    m_XmlnsXsl = v;
+  }
+
+  /**
+   * Get the "xmlns:xsl" property.
+   * @see <a href="http://www.w3.org/TR/xslt#xslt-namespace">xslt-namespace in XSLT Specification</a>
+   *
+   * @return The value of the "xmlns:xsl" property.
+   */
+  public String getXmlnsXsl()
+  {
+    return m_XmlnsXsl;
+  }
+
+  /**
+   * The "extension-element-prefixes" property, actually contains URIs.
+   * @serial
+   */
+  private StringVector m_ExtensionElementURIs;
+
+  /**
+   * Set the "extension-element-prefixes" property.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @param v The value to be set for the "extension-element-prefixes" 
+   * property: a vector of extension element URIs.
+   */
+  public void setExtensionElementPrefixes(StringVector v)
+  {
+    m_ExtensionElementURIs = v;
+  }
+
+  /**
+   * Get and "extension-element-prefix" property.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @param i Index of extension element URI in list 
+   *
+   * @return The extension element URI at the given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public String getExtensionElementPrefix(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_ExtensionElementURIs)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return m_ExtensionElementURIs.elementAt(i);
+  }
+
+  /**
+   * Get the number of "extension-element-prefixes" Strings.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @return Number of URIs in the list
+   */
+  public int getExtensionElementPrefixCount()
+  {
+    return (null != m_ExtensionElementURIs)
+           ? m_ExtensionElementURIs.size() : 0;
+  }
+
+  /**
+   * Find out if this contains a given "extension-element-prefix" property.
+   * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
+   *
+   * @param uri URI of extension element to look for
+   *
+   * @return True if the given URI was found in the list 
+   */
+  public boolean containsExtensionElementURI(String uri)
+  {
+
+    if (null == m_ExtensionElementURIs)
+      return false;
+
+    return m_ExtensionElementURIs.contains(uri);
+  }
+
+  /**
+   * The "exclude-result-prefixes" property.
+   * @serial
+   */
+  private StringVector m_ExcludeResultPrefixs;
+
+  /**
+   * Set the "exclude-result-prefixes" property.
+   * The designation of a namespace as an excluded namespace is
+   * effective within the subtree of the stylesheet rooted at
+   * the element bearing the exclude-result-prefixes or
+   * xsl:exclude-result-prefixes attribute; a subtree rooted
+   * at an xsl:stylesheet element does not include any stylesheets
+   * imported or included by children of that xsl:stylesheet element.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @param v A StringVector of prefixes to exclude 
+   */
+  public void setExcludeResultPrefixes(StringVector v)
+  {
+    m_ExcludeResultPrefixs = v;
+  }
+
+  /**
+   * Get an "exclude-result-prefix" property.
+   * The designation of a namespace as an excluded namespace is
+   * effective within the subtree of the stylesheet rooted at
+   * the element bearing the exclude-result-prefixes or
+   * xsl:exclude-result-prefixes attribute; a subtree rooted
+   * at an xsl:stylesheet element does not include any stylesheets
+   * imported or included by children of that xsl:stylesheet element.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @param i Index of prefix to get in list 
+   *
+   * @return Prefix to be excluded at the given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public String getExcludeResultPrefix(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_ExcludeResultPrefixs)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return m_ExcludeResultPrefixs.elementAt(i);
+  }
+
+  /**
+   * Get the number of "exclude-result-prefixes" Strings.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @return The number of prefix strings to be excluded. 
+   */
+  public int getExcludeResultPrefixCount()
+  {
+    return (null != m_ExcludeResultPrefixs)
+           ? m_ExcludeResultPrefixs.size() : 0;
+  }
+
+  /**
+   * Get whether or not the passed prefix is contained flagged by
+   * the "exclude-result-prefixes" property.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @param prefix non-null reference to prefix that might be excluded.
+   * @param uri reference to namespace that prefix maps to
+   *
+   * @return true if the prefix should normally be excluded.>
+   */
+  public boolean containsExcludeResultPrefix(String prefix, String uri) 
+  {
+
+    if (null == m_ExcludeResultPrefixs || uri == null )
+      return false;
+    
+    // This loop is ok here because this code only runs during
+    // stylesheet compile time.
+    for (int i =0; i< m_ExcludeResultPrefixs.size(); i++)
+    {
+      if (uri.equals(getNamespaceForPrefix(m_ExcludeResultPrefixs.elementAt(i))))
+        return true;
+    }
+    
+    return false;
+
+  /*  if (prefix.length() == 0)
+      prefix = Constants.ATTRVAL_DEFAULT_PREFIX;
+
+    return m_ExcludeResultPrefixs.contains(prefix); */
+  }
+
+  /**
+   * The "id" property.
+   * @serial
+   */
+  private String m_Id;
+
+  /**
+   * Set the "id" property.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Embedding-Stylesheets">section-Embedding-Stylesheets in XSLT Specification</a>
+   *
+   * @param v Value for the "id" property.
+   */
+  public void setId(String v)
+  {
+    m_Id = v;
+  }
+
+  /**
+   * Get the "id" property.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Embedding-Stylesheets">section-Embedding-Stylesheets in XSLT Specification</a>
+   *
+   * @return The value of the "id" property.
+   */
+  public String getId()
+  {
+    return m_Id;
+  }
+
+  /**
+   * The "version" property.
+   * @serial
+   */
+  private String m_Version;
+  
+  /**
+   * Whether or not the stylesheet is in "Forward Compatibility Mode" 
+   * @serial
+   */
+  private boolean m_isCompatibleMode = false;
+
+  /**
+   * Set the "version" property.
+   * @see <a href="http://www.w3.org/TR/xslt#forwards">forwards in XSLT Specification</a>
+   *
+   * @param v Value for the "version" property.
+   */
+  public void setVersion(String v)
+  {
+    m_Version = v;
+    m_isCompatibleMode = (Double.valueOf(v).doubleValue() > Constants.XSLTVERSUPPORTED);
+  }
+
+  /**
+   * Get whether or not the stylesheet is in "Forward Compatibility Mode"
+   * 
+   * @return true if in forward compatible mode, false otherwise
+   */
+  public boolean getCompatibleMode()
+  {
+  	return m_isCompatibleMode;
+  }
+
+  /**
+   * Get the "version" property.
+   * @see <a href="http://www.w3.org/TR/xslt#forwards">forwards in XSLT Specification</a>
+   *
+   * @return The value of the "version" property.
+   */
+  public String getVersion()
+  {
+    return m_Version;
+  }
+
+  /**
+   * The "xsl:import" list.
+   * @serial
+   */
+  private Vector m_imports;
+
+  /**
+   * Add a stylesheet to the "import" list.
+   * @see <a href="http://www.w3.org/TR/xslt#import">import in XSLT Specification</a>
+   *
+   * @param v Stylesheet to add to the import list
+   */
+  public void setImport(StylesheetComposed v)
+  {
+
+    if (null == m_imports)
+      m_imports = new Vector();
+
+    // I'm going to insert the elements in backwards order,
+    // so I can walk them 0 to n.
+    m_imports.addElement(v);
+  }
+
+  /**
+   * Get a stylesheet from the "import" list.
+   * @see <a href="http://www.w3.org/TR/xslt#import">import in XSLT Specification</a>
+   *
+   * @param i Index of the stylesheet to get
+   *
+   * @return The stylesheet at the given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public StylesheetComposed getImport(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_imports)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (StylesheetComposed) m_imports.elementAt(i);
+  }
+
+  /**
+   * Get the number of imported stylesheets.
+   * @see <a href="http://www.w3.org/TR/xslt#import">import in XSLT Specification</a>
+   *
+   * @return the number of imported stylesheets.
+   */
+  public int getImportCount()
+  {
+    return (null != m_imports) ? m_imports.size() : 0;
+  }
+
+  /**
+   * The "xsl:include" properties.
+   * @serial
+   */
+  private Vector m_includes;
+
+  /**
+   * Add a stylesheet to the "include" list.
+   * @see <a href="http://www.w3.org/TR/xslt#include">include in XSLT Specification</a>
+   *
+   * @param v Stylesheet to add to the "include" list  
+   */
+  public void setInclude(Stylesheet v)
+  {
+
+    if (null == m_includes)
+      m_includes = new Vector();
+
+    m_includes.addElement(v);
+  }
+
+  /**
+   * Get the stylesheet at the given in index in "include" list
+   * @see <a href="http://www.w3.org/TR/xslt#include">include in XSLT Specification</a>
+   *
+   * @param i Index of stylesheet to get
+   *
+   * @return Stylesheet at the given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public Stylesheet getInclude(int i) throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_includes)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (Stylesheet) m_includes.elementAt(i);
+  }
+
+  /**
+   * Get the number of included stylesheets.
+   * @see <a href="http://www.w3.org/TR/xslt#import">import in XSLT Specification</a>
+   *
+   * @return the number of included stylesheets.
+   */
+  public int getIncludeCount()
+  {
+    return (null != m_includes) ? m_includes.size() : 0;
+  }
+
+  /**
+   * Table of tables of element decimal-format.
+   * @see DecimalFormatProperties
+   * @serial
+   */
+  Stack m_DecimalFormatDeclarations;
+
+  /**
+   * Process the xsl:decimal-format element.
+   *
+   * @param edf Decimal-format element to push into stack  
+   */
+  public void setDecimalFormat(DecimalFormatProperties edf)
+  {
+
+    if (null == m_DecimalFormatDeclarations)
+      m_DecimalFormatDeclarations = new Stack();
+
+    // Elements are pushed in by order of importance
+    // so that when recomposed, they get overiden properly.
+    m_DecimalFormatDeclarations.push(edf);
+  }
+
+  /**
+   * Get an "xsl:decimal-format" property.
+   * 
+   * @see DecimalFormatProperties
+   * @see <a href="http://www.w3.org/TR/xslt#format-number">format-number in XSLT Specification</a>
+   *
+   * @param name The qualified name of the decimal format property.
+   * @return null if not found, otherwise a DecimalFormatProperties
+   * object, from which you can get a DecimalFormatSymbols object.
+   */
+  public DecimalFormatProperties getDecimalFormat(QName name)
+  {
+
+    if (null == m_DecimalFormatDeclarations)
+      return null;
+
+    int n = getDecimalFormatCount();
+
+    for (int i = (n - 1); i >= 0; i++)
+    {
+      DecimalFormatProperties dfp = getDecimalFormat(i);
+
+      if (dfp.getName().equals(name))
+        return dfp;
+    }
+
+    return null;
+  }
+
+  /**
+   * Get an "xsl:decimal-format" property.
+   * @see <a href="http://www.w3.org/TR/xslt#format-number">format-number in XSLT Specification</a>
+   * @see DecimalFormatProperties
+   *
+   * @param i Index of decimal-format property in stack
+   *
+   * @return The decimal-format property at the given index 
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public DecimalFormatProperties getDecimalFormat(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_DecimalFormatDeclarations)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (DecimalFormatProperties) m_DecimalFormatDeclarations.elementAt(i);
+  }
+
+  /**
+   * Get the number of xsl:decimal-format declarations.
+   * @see DecimalFormatProperties
+   *
+   * @return the number of xsl:decimal-format declarations.
+   */
+  public int getDecimalFormatCount()
+  {
+    return (null != m_DecimalFormatDeclarations)
+           ? m_DecimalFormatDeclarations.size() : 0;
+  }
+
+  /**
+   * The "xsl:strip-space" properties,
+   * A lookup table of all space stripping elements.
+   * @serial
+   */
+  private Vector m_whitespaceStrippingElements;
+
+  /**
+   * Set the "xsl:strip-space" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @param wsi WhiteSpaceInfo element to add to list 
+   */
+  public void setStripSpaces(WhiteSpaceInfo wsi)
+  {
+
+    if (null == m_whitespaceStrippingElements)
+    {
+      m_whitespaceStrippingElements = new Vector();
+    }
+
+    m_whitespaceStrippingElements.addElement(wsi);
+  }
+
+  /**
+   * Get an "xsl:strip-space" property.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @param i Index of WhiteSpaceInfo to get
+   *
+   * @return WhiteSpaceInfo at given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public WhiteSpaceInfo getStripSpace(int i) throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_whitespaceStrippingElements)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (WhiteSpaceInfo) m_whitespaceStrippingElements.elementAt(i);
+  }
+
+  /**
+   * Get the number of "xsl:strip-space" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @return the number of "xsl:strip-space" properties.
+   */
+  public int getStripSpaceCount()
+  {
+    return (null != m_whitespaceStrippingElements)
+           ? m_whitespaceStrippingElements.size() : 0;
+  }
+
+  /**
+   * The "xsl:preserve-space" property,
+   * A lookup table of all space preserving elements.
+   * @serial
+   */
+  private Vector m_whitespacePreservingElements;
+
+  /**
+   * Set the "xsl:preserve-space" property.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @param wsi WhiteSpaceInfo element to add to list
+   */
+  public void setPreserveSpaces(WhiteSpaceInfo wsi)
+  {
+
+    if (null == m_whitespacePreservingElements)
+    {
+      m_whitespacePreservingElements = new Vector();
+    }
+
+    m_whitespacePreservingElements.addElement(wsi);
+  }
+
+  /**
+   * Get a "xsl:preserve-space" property.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @param i Index of WhiteSpaceInfo to get
+   *
+   * @return WhiteSpaceInfo at the given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public WhiteSpaceInfo getPreserveSpace(int i) throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_whitespacePreservingElements)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (WhiteSpaceInfo) m_whitespacePreservingElements.elementAt(i);
+  }
+
+  /**
+   * Get the number of "xsl:preserve-space" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @return the number of "xsl:preserve-space" properties.
+   */
+  public int getPreserveSpaceCount()
+  {
+    return (null != m_whitespacePreservingElements)
+           ? m_whitespacePreservingElements.size() : 0;
+  }
+
+  /**
+   * The "xsl:output" properties.  This is a vector of OutputProperties objects.
+   * @serial
+   */
+  private Vector m_output;
+
+  /**
+   * Set the "xsl:output" property.
+   * @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT Specification</a>
+   *
+   * @param v non-null reference to the OutputProperties object to be 
+   *          added to the collection.
+   */
+  public void setOutput(OutputProperties v)
+  {
+    if (null == m_output)
+    {
+      m_output = new Vector();
+    }
+
+    m_output.addElement(v);
+  }
+
+  /**
+   * Get an "xsl:output" property.
+   * @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT Specification</a>
+   *
+   * @param i Index of OutputFormatExtended to get
+   *
+   * @return non-null reference to an OutputProperties object.
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public OutputProperties getOutput(int i) throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_output)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (OutputProperties) m_output.elementAt(i);
+  }
+
+  /**
+   * Get the number of "xsl:output" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT Specification</a>
+   *
+   * @return The number of OutputProperties objects contained in this stylesheet.
+   */
+  public int getOutputCount()
+  {
+    return (null != m_output)
+           ? m_output.size() : 0;
+  }
+
+  /**
+   * The "xsl:key" property.
+   * @serial
+   */
+  private Vector m_keyDeclarations;
+
+  /**
+   * Set the "xsl:key" property.
+   * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a>
+   *
+   * @param v KeyDeclaration element to add to the list of key declarations 
+   */
+  public void setKey(KeyDeclaration v)
+  {
+
+    if (null == m_keyDeclarations)
+      m_keyDeclarations = new Vector();
+
+    m_keyDeclarations.addElement(v);
+  }
+
+  /**
+   * Get an "xsl:key" property.
+   * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a>
+   *
+   * @param i Index of KeyDeclaration element to get
+   *
+   * @return KeyDeclaration element at given index in list 
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public KeyDeclaration getKey(int i) throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_keyDeclarations)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (KeyDeclaration) m_keyDeclarations.elementAt(i);
+  }
+
+  /**
+   * Get the number of "xsl:key" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a>
+   *
+   * @return the number of "xsl:key" properties.
+   */
+  public int getKeyCount()
+  {
+    return (null != m_keyDeclarations) ? m_keyDeclarations.size() : 0;
+  }
+
+  /**
+   * The "xsl:attribute-set" property.
+   * @serial
+   */
+  private Vector m_attributeSets;
+
+  /**
+   * Set the "xsl:attribute-set" property.
+   * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
+   *
+   * @param attrSet ElemAttributeSet to add to the list of attribute sets
+   */
+  public void setAttributeSet(ElemAttributeSet attrSet)
+  {
+
+    if (null == m_attributeSets)
+    {
+      m_attributeSets = new Vector();
+    }
+
+    m_attributeSets.addElement(attrSet);
+  }
+
+  /**
+   * Get an "xsl:attribute-set" property.
+   * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
+   *
+   * @param i Index of ElemAttributeSet to get in list
+   *
+   * @return ElemAttributeSet at the given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public ElemAttributeSet getAttributeSet(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_attributeSets)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (ElemAttributeSet) m_attributeSets.elementAt(i);
+  }
+
+  /**
+   * Get the number of "xsl:attribute-set" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
+   *
+   * @return the number of "xsl:attribute-set" properties.
+   */
+  public int getAttributeSetCount()
+  {
+    return (null != m_attributeSets) ? m_attributeSets.size() : 0;
+  }
+
+  /**
+   * The "xsl:variable" and "xsl:param" properties.
+   * @serial
+   */
+  private Vector m_topLevelVariables;
+
+  /**
+   * Set the "xsl:variable" property.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @param v ElemVariable object to add to list of top level variables
+   */
+  public void setVariable(ElemVariable v)
+  {
+
+    if (null == m_topLevelVariables)
+      m_topLevelVariables = new Vector();
+
+    m_topLevelVariables.addElement(v);
+  }
+  
+  /**
+   * Get an "xsl:variable" or "xsl:param" property.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @param qname non-null reference to the qualified name of the variable.
+   *
+   * @return The ElemVariable with the given name in the list or null
+   */
+  public ElemVariable getVariableOrParam(QName qname)
+  {
+
+    if (null != m_topLevelVariables)
+    {
+      int n = getVariableOrParamCount();
+
+      for (int i = 0; i < n; i++)
+      {
+        ElemVariable var = (ElemVariable) getVariableOrParam(i);
+
+        if (var.getName().equals(qname))
+          return var;
+      }
+    }
+
+    return null;
+  }
+
+
+  /**
+   * Get an "xsl:variable" property.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @param qname Qualified name of the xsl:variable to get 
+   *
+   * @return reference to the variable named by qname, or null if not found.
+   */
+  public ElemVariable getVariable(QName qname)
+  {
+
+    if (null != m_topLevelVariables)
+    {
+      int n = getVariableOrParamCount();
+
+      for (int i = 0; i < n; i++)
+      {
+        ElemVariable var = getVariableOrParam(i);
+        if((var.getXSLToken() == Constants.ELEMNAME_VARIABLE) &&
+           (var.getName().equals(qname)))
+          return var;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Get an "xsl:variable" property.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @param i Index of variable to get in the list
+   *
+   * @return ElemVariable at the given index in the list 
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public ElemVariable getVariableOrParam(int i) throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_topLevelVariables)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (ElemVariable) m_topLevelVariables.elementAt(i);
+  }
+
+  /**
+   * Get the number of "xsl:variable" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @return the number of "xsl:variable" properties.
+   */
+  public int getVariableOrParamCount()
+  {
+    return (null != m_topLevelVariables) ? m_topLevelVariables.size() : 0;
+  }
+
+  /**
+   * Set an "xsl:param" property.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @param v A non-null ElemParam reference.
+   */
+  public void setParam(ElemParam v)
+  {
+    setVariable(v);
+  }
+
+  /**
+   * Get an "xsl:param" property.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @param qname non-null reference to qualified name of the parameter.
+   *
+   * @return ElemParam with the given name in the list or null
+   */
+  public ElemParam getParam(QName qname)
+  {
+
+    if (null != m_topLevelVariables)
+    {
+      int n = getVariableOrParamCount();
+
+      for (int i = 0; i < n; i++)
+      {
+        ElemVariable var = getVariableOrParam(i);
+        if((var.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE) &&
+           (var.getName().equals(qname)))
+          return (ElemParam)var;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * The "xsl:template" properties.
+   * @serial
+   */
+  private Vector m_templates;
+
+  /**
+   * Set an "xsl:template" property.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
+   *
+   * @param v ElemTemplate to add to list of templates
+   */
+  public void setTemplate(ElemTemplate v)
+  {
+
+    if (null == m_templates)
+      m_templates = new Vector();
+
+    m_templates.addElement(v);
+    v.setStylesheet(this);
+  }
+
+  /**
+   * Get an "xsl:template" property.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
+   *
+   * @param i Index of ElemTemplate in the list to get
+   *
+   * @return ElemTemplate at the given index in the list
+   *
+   * @throws TransformerException
+   */
+  public ElemTemplate getTemplate(int i) throws TransformerException
+  {
+
+    if (null == m_templates)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (ElemTemplate) m_templates.elementAt(i);
+  }
+
+  /**
+   * Get the number of "xsl:template" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
+   *
+   * @return the number of "xsl:template" properties.
+   */
+  public int getTemplateCount()
+  {
+    return (null != m_templates) ? m_templates.size() : 0;
+  }
+
+  /**
+   * The "xsl:namespace-alias" properties.
+   * @serial
+   */
+  private Vector m_prefix_aliases;
+
+  /**
+   * Set the "xsl:namespace-alias" property.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @param na NamespaceAlias elemeent to add to the list
+   */
+  public void setNamespaceAlias(NamespaceAlias na)
+  {
+
+    if (m_prefix_aliases == null)
+      m_prefix_aliases = new Vector();
+
+    m_prefix_aliases.addElement(na);
+  }
+
+  /**
+   * Get an "xsl:namespace-alias" property.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @param i Index of NamespaceAlias element to get from the list 
+   *
+   * @return NamespaceAlias element at the given index in the list
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public NamespaceAlias getNamespaceAlias(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    if (null == m_prefix_aliases)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (NamespaceAlias) m_prefix_aliases.elementAt(i);
+  }
+
+  /**
+   * Get the number of "xsl:namespace-alias" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @return the number of "xsl:namespace-alias" properties.
+   */
+  public int getNamespaceAliasCount()
+  {
+    return (null != m_prefix_aliases) ? m_prefix_aliases.size() : 0;
+  }
+
+  /**
+   * The "non-xsl-top-level" properties.
+   * @serial
+   */
+  private Hashtable m_NonXslTopLevel;
+
+  /**
+   * Set found a non-xslt element.
+   * @see <a href="http://www.w3.org/TR/xslt#stylesheet-element">stylesheet-element in XSLT Specification</a>
+   *
+   * @param name Qualified name of the element
+   * @param obj The element object
+   */
+  public void setNonXslTopLevel(QName name, Object obj)
+  {
+
+    if (null == m_NonXslTopLevel)
+      m_NonXslTopLevel = new Hashtable();
+
+    m_NonXslTopLevel.put(name, obj);
+  }
+
+  /**
+   * Get a non-xslt element.
+   * @see <a href="http://www.w3.org/TR/xslt#stylesheet-element">stylesheet-element in XSLT Specification</a>
+   *
+   * @param name Qualified name of the element to get
+   *
+   * @return The object associate with the given name 
+   */
+  public Object getNonXslTopLevel(QName name)
+  {
+    return (null != m_NonXslTopLevel) ? m_NonXslTopLevel.get(name) : null;
+  }
+
+  // =========== End top-level XSLT properties ===========
+
+  /**
+   * The base URL of the XSL document.
+   * @serial
+   */
+  private String m_href = null;
+
+  /** The doctype-public element.
+   *  @serial          */
+  private String m_publicId;
+
+  /** The doctype-system element.
+   *  @serial          */
+  private String m_systemId;
+
+  /**
+   * Get the base identifier with which this stylesheet is associated.
+   *
+   * @return the base identifier with which this stylesheet is associated.
+   */
+  public String getHref()
+  {
+    return m_href;
+  }
+
+  /**
+   * Set the base identifier with which this stylesheet is associated.
+   *
+   * @param baseIdent the base identifier with which this stylesheet is associated.
+   */
+  public void setHref(String baseIdent)
+  {
+    m_href = baseIdent;
+  }
+
+  /**
+   * Set the location information for this element.
+   *
+   * @param locator SourceLocator object with location information  
+   */
+  public void setLocaterInfo(SourceLocator locator)
+  {
+
+    if (null != locator)
+    {
+      m_publicId = locator.getPublicId();
+      m_systemId = locator.getSystemId();
+
+      if (null != m_systemId)
+      {
+        try
+        {
+          m_href = SystemIDResolver.getAbsoluteURI(m_systemId, null);
+        }
+        catch (TransformerException se)
+        {
+
+          // Ignore this for right now
+        }
+      }
+
+      super.setLocaterInfo(locator);
+    }
+  }
+
+  /**
+   * The root of the stylesheet, where all the tables common
+   * to all stylesheets are kept.
+   * @serial
+   */
+  private StylesheetRoot m_stylesheetRoot;
+
+  /**
+   * Get the root of the stylesheet, where all the tables common
+   * to all stylesheets are kept.
+   *
+   * @return the root of the stylesheet
+   */
+  public StylesheetRoot getStylesheetRoot()
+  {
+    return m_stylesheetRoot;
+  }
+
+  /**
+   * Set the root of the stylesheet, where all the tables common
+   * to all stylesheets are kept.
+   *
+   * @param v the root of the stylesheet
+   */
+  public void setStylesheetRoot(StylesheetRoot v)
+  {
+    m_stylesheetRoot = v;
+  }
+
+  /**
+   * The parent of the stylesheet.  This will be null if this
+   * is the root stylesheet.
+   * @serial
+   */
+  private Stylesheet m_stylesheetParent;
+
+  /**
+   * Get the parent of the stylesheet.  This will be null if this
+   * is the root stylesheet.
+   *
+   * @return the parent of the stylesheet.
+   */
+  public Stylesheet getStylesheetParent()
+  {
+    return m_stylesheetParent;
+  }
+
+  /**
+   * Set the parent of the stylesheet.  This should be null if this
+   * is the root stylesheet.
+   *
+   * @param v the parent of the stylesheet.
+   */
+  public void setStylesheetParent(Stylesheet v)
+  {
+    m_stylesheetParent = v;
+  }
+
+  /**
+   * Get the owning aggregated stylesheet, or this
+   * stylesheet if it is aggregated.
+   *
+   * @return the owning aggregated stylesheet or itself
+   */
+  public StylesheetComposed getStylesheetComposed()
+  {
+
+    Stylesheet sheet = this;
+
+    while (!sheet.isAggregatedType())
+    {
+      sheet = sheet.getStylesheetParent();
+    }
+
+    return (StylesheetComposed) sheet;
+  }
+
+  /**
+   * Get the type of the node.  We'll pretend we're a Document.
+   *
+   * @return the type of the node: document node.
+   */
+  public short getNodeType()
+  {
+    return DTM.DOCUMENT_NODE;
+  }
+
+  /**
+   * Get an integer representation of the element type.
+   *
+   * @return An integer representation of the element, defined in the
+   *     Constants class.
+   * @see org.apache.xalan.templates.Constants
+   */
+  public int getXSLToken()
+  {
+    return Constants.ELEMNAME_STYLESHEET;
+  }
+
+  /**
+   * Return the node name.
+   *
+   * @return The node name
+   */
+  public String getNodeName()
+  {
+    return Constants.ELEMNAME_STYLESHEET_STRING;
+  }
+
+  /**
+   * Replace an "xsl:template" property.
+   * This is a hook for CompilingStylesheetHandler, to allow
+   * us to access a template, compile it, instantiate it,
+   * and replace the original with the compiled instance.
+   * ADDED 9/5/2000 to support compilation experiment
+   *
+   * @param v Compiled template to replace with
+   * @param i Index of template to be replaced
+   *
+   * @throws TransformerException
+   */
+  public void replaceTemplate(ElemTemplate v, int i) throws TransformerException
+  {
+
+    if (null == m_templates)
+      throw new ArrayIndexOutOfBoundsException();
+
+    replaceChild(v, (ElemTemplateElement)m_templates.elementAt(i));
+    m_templates.setElementAt(v, i);
+    v.setStylesheet(this);
+  }
+  
+    /**
+     * Call the children visitors.
+     * @param visitor The visitor whose appropriate method will be called.
+     */
+    protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
+    {
+      int s = getImportCount();
+      for (int j = 0; j < s; j++)
+      {
+      	getImport(j).callVisitors(visitor);
+      }
+   
+      s = getIncludeCount();
+      for (int j = 0; j < s; j++)
+      {
+      	getInclude(j).callVisitors(visitor);
+      }
+
+      s = getOutputCount();
+      for (int j = 0; j < s; j++)
+      {
+        visitor.visitTopLevelInstruction(getOutput(j));
+      }
+
+      // Next, add in the attribute-set elements
+
+      s = getAttributeSetCount();
+      for (int j = 0; j < s; j++)
+      {
+      	ElemAttributeSet attrSet = getAttributeSet(j);
+        if (visitor.visitTopLevelInstruction(attrSet))
+        {
+          attrSet.callChildVisitors(visitor);
+        }
+      }
+      // Now the decimal-formats
+
+      s = getDecimalFormatCount();
+      for (int j = 0; j < s; j++)
+      {
+        visitor.visitTopLevelInstruction(getDecimalFormat(j));
+      }
+
+      // Now the keys
+
+      s = getKeyCount();
+      for (int j = 0; j < s; j++)
+      {
+        visitor.visitTopLevelInstruction(getKey(j));
+      }
+
+      // And the namespace aliases
+
+      s = getNamespaceAliasCount();
+      for (int j = 0; j < s; j++)
+      {
+        visitor.visitTopLevelInstruction(getNamespaceAlias(j));
+      }
+
+      // Next comes the templates
+
+      s = getTemplateCount();
+      for (int j = 0; j < s; j++)
+      {
+        try
+        {
+          ElemTemplate template = getTemplate(j);
+          if (visitor.visitTopLevelInstruction(template))
+          {
+            template.callChildVisitors(visitor);
+          }
+        }
+        catch (TransformerException te)
+        {
+          throw new org.apache.xml.utils.WrappedRuntimeException(te);
+        }
+      }
+
+      // Then, the variables
+
+      s = getVariableOrParamCount();
+      for (int j = 0; j < s; j++)
+      {
+      	ElemVariable var = getVariableOrParam(j);
+        if (visitor.visitTopLevelVariableOrParamDecl(var))
+        {
+          var.callChildVisitors(visitor);
+        }
+      }
+
+      // And lastly the whitespace preserving and stripping elements
+
+      s = getStripSpaceCount();
+      for (int j = 0; j < s; j++)
+      {
+        visitor.visitTopLevelInstruction(getStripSpace(j));
+      }
+
+      s = getPreserveSpaceCount();
+      for (int j = 0; j < s; j++)
+      {
+        visitor.visitTopLevelInstruction(getPreserveSpace(j));
+      }
+      
+      if(null != m_NonXslTopLevel)
+      {
+      	java.util.Enumeration elements = m_NonXslTopLevel.elements();
+      	while(elements.hasMoreElements())
+      	{
+      	  ElemTemplateElement elem = (ElemTemplateElement)elements.nextElement();
+          if (visitor.visitTopLevelInstruction(elem))
+          {
+            elem.callChildVisitors(visitor);
+          }
+      		
+      	}
+      }
+    }
+        
+          
+  /**
+   * Accept a visitor and call the appropriate method 
+   * for this class.
+   * 
+   * @param visitor The visitor whose appropriate method will be called.
+   * @return true if the children of the object should be visited.
+   */
+  protected boolean accept(XSLTVisitor visitor)
+  {
+  	return visitor.visitStylesheet(this);
+  }
+
+  
+}
diff --git a/src/main/java/org/apache/xalan/templates/StylesheetComposed.java b/src/main/java/org/apache/xalan/templates/StylesheetComposed.java
new file mode 100644
index 0000000..595410e
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/StylesheetComposed.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StylesheetComposed.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+/**
+ * Represents a stylesheet that has methods that resolve includes and
+ * imports.  It has methods on it that
+ * return "composed" properties, which mean that:
+ * <ol>
+ * <li>Properties that are aggregates, like OutputProperties, will
+ * be composed of properties declared in this stylsheet and all
+ * included stylesheets.</li>
+ * <li>Properties that aren't found, will be searched for first in
+ * the includes, and, if none are located, will be searched for in
+ * the imports.</li>
+ * <li>Properties in that are not atomic on a stylesheet will
+ * have the form getXXXComposed. Some properties, like version and id,
+ * are not inherited, and so won't have getXXXComposed methods.</li>
+ * </ol>
+ * <p>In some cases getXXXComposed methods may calculate the composed
+ * values dynamically, while in other cases they may store the composed
+ * values.</p>
+ */
+public class StylesheetComposed extends Stylesheet
+{
+    static final long serialVersionUID = -3444072247410233923L;
+
+  /**
+   * Uses an XSL stylesheet document.
+   * @param parent  The including or importing stylesheet.
+   */
+  public StylesheetComposed(Stylesheet parent)
+  {
+    super(parent);
+  }
+
+  /**
+   * Tell if this can be cast to a StylesheetComposed, meaning, you
+   * can ask questions from getXXXComposed functions.
+   *
+   * @return True since this is a StylesheetComposed 
+   */
+  public boolean isAggregatedType()
+  {
+    return true;
+  }
+
+  /**
+   * Adds all recomposable values for this precedence level into the recomposableElements Vector
+   * that was passed in as the first parameter.  All elements added to the
+   * recomposableElements vector should extend ElemTemplateElement.
+   * @param recomposableElements a Vector of ElemTemplateElement objects that we will add all of
+   *        our recomposable objects to.
+   */
+  public void recompose(Vector recomposableElements) throws TransformerException
+  {
+
+    //recomposeImports();         // Calculate the number of this import.
+    //recomposeIncludes(this);    // Build the global include list for this stylesheet.
+
+    // Now add in all of the recomposable elements at this precedence level
+
+    int n = getIncludeCountComposed();
+
+    for (int i = -1; i < n; i++)
+    {
+      Stylesheet included = getIncludeComposed(i);
+
+      // Add in the output elements
+
+      int s = included.getOutputCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getOutput(j));
+      }
+
+      // Next, add in the attribute-set elements
+
+      s = included.getAttributeSetCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getAttributeSet(j));
+      }
+
+      // Now the decimal-formats
+
+      s = included.getDecimalFormatCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getDecimalFormat(j));
+      }
+
+      // Now the keys
+
+      s = included.getKeyCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getKey(j));
+      }
+
+      // And the namespace aliases
+
+      s = included.getNamespaceAliasCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getNamespaceAlias(j));
+      }
+
+      // Next comes the templates
+
+      s = included.getTemplateCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getTemplate(j));
+      }
+
+      // Then, the variables
+
+      s = included.getVariableOrParamCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getVariableOrParam(j));
+      }
+
+      // And lastly the whitespace preserving and stripping elements
+
+      s = included.getStripSpaceCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getStripSpace(j));
+      }
+
+      s = included.getPreserveSpaceCount();
+      for (int j = 0; j < s; j++)
+      {
+        recomposableElements.addElement(included.getPreserveSpace(j));
+      }
+    }
+  }
+
+  /** Order in import chain.
+   *  @serial         */
+  private int m_importNumber = -1;
+
+  /** The precedence of this stylesheet in the global import list.
+   *  The lowest precedence stylesheet is 0.  A higher
+   *  number has a higher precedence.
+   *  @serial
+   */
+  private int m_importCountComposed;
+  
+  /* The count of imports composed for this stylesheet */
+  private int m_endImportCountComposed;
+
+  /**
+   * Recalculate the precedence of this stylesheet in the global
+   * import list.  The lowest precedence stylesheet is 0.  A higher
+   * number has a higher precedence.
+   */
+  void recomposeImports()
+  {
+
+    m_importNumber = getStylesheetRoot().getImportNumber(this);
+
+    StylesheetRoot root = getStylesheetRoot();
+    int globalImportCount = root.getGlobalImportCount();
+
+    m_importCountComposed = (globalImportCount - m_importNumber) - 1;
+    
+    // Now get the count of composed imports from this stylesheet's imports
+    int count = getImportCount();
+    if ( count > 0)
+    {
+      m_endImportCountComposed += count;
+      while (count > 0)
+        m_endImportCountComposed += this.getImport(--count).getEndImportCountComposed();
+    }
+    
+    // Now get the count of composed imports from this stylesheet's
+    // composed includes.
+    count = getIncludeCountComposed();
+    while (count>0)
+    {
+      int imports = getIncludeComposed(--count).getImportCount();
+      m_endImportCountComposed += imports;
+      while (imports > 0)
+        m_endImportCountComposed +=getIncludeComposed(count).getImport(--imports).getEndImportCountComposed();
+     
+    }                                                            
+  }
+
+  /**
+   * Get a stylesheet from the "import" list.
+   * @see <a href="http://www.w3.org/TR/xslt#import">import in XSLT Specification</a>
+   *
+   * @param i Index of stylesheet in import list 
+   *
+   * @return The stylesheet at the given index
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public StylesheetComposed getImportComposed(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    StylesheetRoot root = getStylesheetRoot();
+
+    // Get the stylesheet that is offset past this stylesheet.
+    // Thus, if the index of this stylesheet is 3, an argument 
+    // to getImportComposed of 0 will return the 4th stylesheet 
+    // in the global import list.
+    return root.getGlobalImport(1 + m_importNumber + i);
+  }
+
+  /**
+   * Get the precedence of this stylesheet in the global import list.
+   * The lowest precedence is 0.  A higher number has a higher precedence.
+   * @see <a href="http://www.w3.org/TR/xslt#import">import in XSLT Specification</a>
+   *
+   * @return the precedence of this stylesheet in the global import list.
+   */
+  public int getImportCountComposed()
+  {
+    return m_importCountComposed;
+  }
+  
+  /**
+   * Get the number of import in this stylesheet's composed list.
+   *
+   * @return the number of imports in this stylesheet's composed list.
+   */
+  public int getEndImportCountComposed()
+  {
+    return m_endImportCountComposed;
+  }
+  
+
+  /**
+   * The combined list of includes.
+   * @serial
+   */
+  private transient Vector m_includesComposed;
+
+  /**
+   * Recompose the value of the composed include list.  Builds a composite
+   * list of all stylesheets included by this stylesheet to any depth.
+   *
+   * @param including Stylesheet to recompose
+   */
+  void recomposeIncludes(Stylesheet including)
+  {
+
+    int n = including.getIncludeCount();
+
+    if (n > 0)
+    {
+      if (null == m_includesComposed)
+        m_includesComposed = new Vector();
+
+      for (int i = 0; i < n; i++)
+      {
+        Stylesheet included = including.getInclude(i);
+        m_includesComposed.addElement(included);
+        recomposeIncludes(included);
+      }
+    }
+  }
+
+  /**
+   * Get an "xsl:include" property.
+   * @see <a href="http://www.w3.org/TR/xslt#include">include in XSLT Specification</a>
+   *
+   * @param i Index of stylesheet in "include" list 
+   *
+   * @return The stylesheet at the given index in the "include" list 
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public Stylesheet getIncludeComposed(int i)
+          throws ArrayIndexOutOfBoundsException
+  {
+
+    if (-1 == i)
+      return this;
+
+    if (null == m_includesComposed)
+      throw new ArrayIndexOutOfBoundsException();
+
+    return (Stylesheet) m_includesComposed.elementAt(i);
+  }
+
+  /**
+   * Get the number of included stylesheets.
+   * @see <a href="http://www.w3.org/TR/xslt#import">import in XSLT Specification</a>
+   *
+   * @return the number of included stylesheets.
+   */
+  public int getIncludeCountComposed()
+  {
+    return (null != m_includesComposed) ? m_includesComposed.size() : 0;
+  }
+
+  /**
+   * For compilation support, we need the option of overwriting
+   * (rather than appending to) previous composition.
+   * We could phase out the old API in favor of this one, but I'm
+   * holding off until we've made up our minds about compilation.
+   * ADDED 9/5/2000 to support compilation experiment.
+   * NOTE: GLP 29-Nov-00 I've left this method in so that CompilingStylesheetHandler will compile.  However,
+   *                     I'm not sure why it's needed or what it does and I've commented out the body.
+   *
+   * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
+   * @param flushFirst Flag indicating the option of overwriting
+   * (rather than appending to) previous composition.
+   *
+   * @throws TransformerException
+   */
+  public void recomposeTemplates(boolean flushFirst) throws TransformerException
+  {
+/***************************************  KEEP METHOD IN FOR COMPILATION
+    if (flushFirst)
+      m_templateList = new TemplateList(this);
+
+    recomposeTemplates();
+*****************************************/
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/StylesheetRoot.java b/src/main/java/org/apache/xalan/templates/StylesheetRoot.java
new file mode 100644
index 0000000..2cc8935
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/StylesheetRoot.java
@@ -0,0 +1,1405 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StylesheetRoot.java 476466 2006-11-18 08:22:31Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.extensions.ExtensionNamespacesManager;
+import org.apache.xalan.processor.XSLTSchema;
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.ref.ExpandedNameTable;
+import org.apache.xml.utils.IntStack;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+
+/**
+ * This class represents the root object of the stylesheet tree.
+ * @xsl.usage general
+ */
+public class StylesheetRoot extends StylesheetComposed
+        implements java.io.Serializable, Templates
+{
+    static final long serialVersionUID = 3875353123529147855L;
+    
+    /**
+     * The flag for the setting of the optimize feature;
+     */    
+    private boolean m_optimizer = true;
+
+    /**
+     * The flag for the setting of the incremental feature;
+     */    
+    private boolean m_incremental = false;
+
+    /**
+     * The flag for the setting of the source_location feature;
+     */  
+    private boolean m_source_location = false;
+
+    /**
+     * State of the secure processing feature.
+     */
+    private boolean m_isSecureProcessing = false;
+
+  /**
+   * Uses an XSL stylesheet document.
+   * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL.
+   */
+  public StylesheetRoot(ErrorListener errorListener) throws TransformerConfigurationException
+  {
+
+    super(null);
+
+    setStylesheetRoot(this);
+
+    try
+    {
+      m_selectDefault = new XPath("node()", this, this, XPath.SELECT, errorListener);
+
+      initDefaultRule(errorListener);
+    }
+    catch (TransformerException se)
+    {
+      throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_INIT_DEFAULT_TEMPLATES, null), se); //"Can't init default templates!", se);
+    }
+  }
+
+  /**
+   * The schema used when creating this StylesheetRoot
+   * @serial
+   */
+  private HashMap m_availElems;
+  
+  /**
+   * Creates a StylesheetRoot and retains a pointer to the schema used to create this
+   * StylesheetRoot.  The schema may be needed later for an element-available() function call.
+   * 
+   * @param schema The schema used to create this stylesheet
+   * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL.
+   */
+  public StylesheetRoot(XSLTSchema schema, ErrorListener listener) throws TransformerConfigurationException
+  {
+
+    this(listener);
+    m_availElems = schema.getElemsAvailable();
+  }
+
+  /**
+   * Tell if this is the root of the stylesheet tree.
+   *
+   * @return True since this is the root of the stylesheet tree.
+   */
+  public boolean isRoot()
+  {
+    return true;
+  }
+
+  /**
+   * Set the state of the secure processing feature.
+   */
+  public void setSecureProcessing(boolean flag)
+  {
+    m_isSecureProcessing = flag;
+  }
+  
+  /**
+   * Return the state of the secure processing feature.
+   */
+  public boolean isSecureProcessing()
+  {
+    return m_isSecureProcessing;
+  }
+
+  /**
+   * Get the hashtable of available elements.
+   *
+   * @return table of available elements, keyed by qualified names, and with 
+   * values of the same qualified names.
+   */
+  public HashMap getAvailableElements()
+  {
+    return m_availElems;
+  }
+  
+  private transient ExtensionNamespacesManager m_extNsMgr = null;
+  
+  /**
+   * Only instantiate an ExtensionNamespacesManager if one is called for
+   * (i.e., if the stylesheet contains  extension functions and/or elements).
+   */
+  public ExtensionNamespacesManager getExtensionNamespacesManager()
+  {
+     if (m_extNsMgr == null)
+       m_extNsMgr = new ExtensionNamespacesManager();
+     return m_extNsMgr;
+  }
+  
+  /**
+   * Get the vector of extension namespaces. Used to provide
+   * the extensions table access to a list of extension
+   * namespaces encountered during composition of a stylesheet.
+   */
+  public Vector getExtensions()
+  {
+    return m_extNsMgr != null ? m_extNsMgr.getExtensions() : null;
+  }  
+
+/*
+  public void runtimeInit(TransformerImpl transformer) throws TransformerException
+  {
+    System.out.println("StylesheetRoot.runtimeInit()");
+      
+  //    try{throw new Exception("StylesheetRoot.runtimeInit()");} catch(Exception e){e.printStackTrace();}
+
+    }
+*/  
+
+  //============== Templates Interface ================
+
+  /**
+   * Create a new transformation context for this Templates object.
+   *
+   * @return A Transformer instance, never null.
+   */
+  public Transformer newTransformer()
+  {
+    return new TransformerImpl(this);
+  }
+  
+
+  public Properties getDefaultOutputProps()
+  {
+    return m_outputProperties.getProperties();
+  }
+  
+  /**
+   * Get the static properties for xsl:output.  The object returned will
+   * be a clone of the internal values, and thus it can be mutated
+   * without mutating the Templates object, and then handed in to
+   * the process method.
+   *
+   * <p>For XSLT, Attribute Value Templates attribute values will
+   * be returned unexpanded (since there is no context at this point).</p>
+   *
+   * @return A Properties object, not null.
+   */
+  public Properties getOutputProperties()
+  {    
+    return (Properties)getDefaultOutputProps().clone();
+  }
+
+  //============== End Templates Interface ================
+
+  /**
+   * Recompose the values of all "composed" properties, meaning
+   * properties that need to be combined or calculated from
+   * the combination of imported and included stylesheets.  This
+   * method determines the proper import precedence of all imported
+   * stylesheets.  It then iterates through all of the elements and 
+   * properties in the proper order and triggers the individual recompose
+   * methods.
+   *
+   * @throws TransformerException
+   */
+  public void recompose() throws TransformerException
+  {
+    // Now we make a Vector that is going to hold all of the recomposable elements
+
+      Vector recomposableElements = new Vector();
+
+    // First, we build the global import tree.
+
+    if (null == m_globalImportList)
+    {
+
+      Vector importList = new Vector();
+
+      addImports(this, true, importList);            
+
+      // Now we create an array and reverse the order of the importList vector.
+      // We built the importList vector backwards so that we could use addElement
+      // to append to the end of the vector instead of constantly pushing new
+      // stylesheets onto the front of the vector and having to shift the rest
+      // of the vector each time.
+
+      m_globalImportList = new StylesheetComposed[importList.size()];
+
+      for (int i =  0, j= importList.size() -1; i < importList.size(); i++)
+      {  
+        m_globalImportList[j] = (StylesheetComposed) importList.elementAt(i);
+        // Build the global include list for this stylesheet.
+        // This needs to be done ahead of the recomposeImports
+        // because we need the info from the composed includes. 
+        m_globalImportList[j].recomposeIncludes(m_globalImportList[j]);
+        // Calculate the number of this import.    
+        m_globalImportList[j--].recomposeImports();        
+      }
+    }    
+    // Next, we walk the import tree and add all of the recomposable elements to the vector.
+    int n = getGlobalImportCount();
+
+    for (int i = 0; i < n; i++)
+    {
+      StylesheetComposed imported = getGlobalImport(i);
+      imported.recompose(recomposableElements);
+    }
+
+    // We sort the elements into ascending order.
+
+    QuickSort2(recomposableElements, 0, recomposableElements.size() - 1);
+
+    // We set up the global variables that will hold the recomposed information.
+
+
+    m_outputProperties = new OutputProperties(org.apache.xml.serializer.Method.UNKNOWN);
+//  m_outputProperties = new OutputProperties(Method.XML);
+    
+    m_attrSets = new HashMap();
+    m_decimalFormatSymbols = new Hashtable();
+    m_keyDecls = new Vector();
+    m_namespaceAliasComposed = new Hashtable();
+    m_templateList = new TemplateList();
+    m_variables = new Vector();
+
+    // Now we sequence through the sorted elements, 
+    // calling the recompose() function on each one.  This will call back into the
+    // appropriate routine here to actually do the recomposition.
+    // Note that we're going backwards, encountering the highest precedence items first.
+    for (int i = recomposableElements.size() - 1; i >= 0; i--)
+      ((ElemTemplateElement) recomposableElements.elementAt(i)).recompose(this);
+    
+/*
+ * Backing out REE again, as it seems to cause some new failures
+ * which need to be investigated. -is
+ */      
+    // This has to be done before the initialization of the compose state, because 
+    // eleminateRedundentGlobals will add variables to the m_variables vector, which 
+    // it then copied in the ComposeState constructor.
+    
+//    if(true && org.apache.xalan.processor.TransformerFactoryImpl.m_optimize)
+//    {
+//          RedundentExprEliminator ree = new RedundentExprEliminator();
+//          callVisitors(ree);
+//          ree.eleminateRedundentGlobals(this);
+//    }
+          
+    initComposeState();
+
+    // Need final composition of TemplateList.  This adds the wild cards onto the chains.
+    m_templateList.compose(this);
+    
+    // Need to clear check for properties at the same import level.
+    m_outputProperties.compose(this);
+    m_outputProperties.endCompose(this);
+    
+    // Now call the compose() method on every element to give it a chance to adjust
+    // based on composed values.
+    
+    n = getGlobalImportCount();
+
+    for (int i = 0; i < n; i++)
+    {
+      StylesheetComposed imported = this.getGlobalImport(i);
+      int includedCount = imported.getIncludeCountComposed();
+      for (int j = -1; j < includedCount; j++)
+      {
+        Stylesheet included = imported.getIncludeComposed(j);
+        composeTemplates(included);
+      }
+    }
+    // Attempt to register any remaining unregistered extension namespaces.
+    if (m_extNsMgr != null)
+      m_extNsMgr.registerUnregisteredNamespaces();
+
+    clearComposeState();
+  }
+
+  /**
+   * Call the compose function for each ElemTemplateElement.
+   *
+   * @param templ non-null reference to template element that will have 
+   * the composed method called on it, and will have it's children's composed 
+   * methods called.
+   */
+  void composeTemplates(ElemTemplateElement templ) throws TransformerException
+  {
+
+    templ.compose(this);
+
+    for (ElemTemplateElement child = templ.getFirstChildElem();
+            child != null; child = child.getNextSiblingElem())
+    {
+      composeTemplates(child);
+    }
+    
+    templ.endCompose(this);
+  }
+
+  /**
+   * The combined list of imports.  The stylesheet with the highest
+   * import precedence will be at element 0.  The one with the lowest
+   * import precedence will be at element length - 1.
+   * @serial
+   */
+  private StylesheetComposed[] m_globalImportList;
+
+  /**
+   * Add the imports in the given sheet to the working importList vector.
+   * The will be added from highest import precedence to
+   * least import precedence.  This is a post-order traversal of the
+   * import tree as described in <a href="http://www.w3.org/TR/xslt.html#import">the
+   * XSLT Recommendation</a>.
+   * <p>For example, suppose</p>
+   * <p>stylesheet A imports stylesheets B and C in that order;</p>
+   * <p>stylesheet B imports stylesheet D;</p>
+   * <p>stylesheet C imports stylesheet E.</p>
+   * <p>Then the order of import precedence (highest first) is
+   * A, C, E, B, D.</p>
+   *
+   * @param stylesheet Stylesheet to examine for imports.
+   * @param addToList  <code>true</code> if this template should be added to the import list
+   * @param importList The working import list.  Templates are added here in the reverse
+   *        order of priority.  When we're all done, we'll reverse this to the correct
+   *        priority in an array.
+   */
+  protected void addImports(Stylesheet stylesheet, boolean addToList, Vector importList)
+  {
+
+    // Get the direct imports of this sheet.
+
+    int n = stylesheet.getImportCount();
+
+    if (n > 0)
+    {
+      for (int i = 0; i < n; i++)
+      {
+        Stylesheet imported = stylesheet.getImport(i);
+
+        addImports(imported, true, importList);
+      }
+    }
+
+    n = stylesheet.getIncludeCount();
+
+    if (n > 0)
+    {
+      for (int i = 0; i < n; i++)
+      {
+        Stylesheet included = stylesheet.getInclude(i);
+
+        addImports(included, false, importList);
+      }
+    }
+
+    if (addToList)
+      importList.addElement(stylesheet);
+
+  }
+
+  /**
+   * Get a stylesheet from the global import list. 
+   * TODO: JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH COUNT.
+   * 
+   * @param i Index of stylesheet to get from global import list 
+   *
+   * @return The stylesheet at the given index 
+   */
+  public StylesheetComposed getGlobalImport(int i)
+  {
+    return m_globalImportList[i];
+  }
+
+  /**
+   * Get the total number of imports in the global import list.
+   * 
+   * @return The total number of imported stylesheets, including
+   * the root stylesheet, thus the number will always be 1 or
+   * greater.
+   * TODO: JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH DESCRIPTION.
+   */
+  public int getGlobalImportCount()
+  {
+          return (m_globalImportList!=null)
+                        ? m_globalImportList.length 
+                          : 1;
+  }
+
+  /**
+   * Given a stylesheet, return the number of the stylesheet
+   * in the global import list.
+   * @param sheet The stylesheet which will be located in the
+   * global import list.
+   * @return The index into the global import list of the given stylesheet,
+   * or -1 if it is not found (which should never happen).
+   */
+  public int getImportNumber(StylesheetComposed sheet)
+  {
+
+    if (this == sheet)
+      return 0;
+
+    int n = getGlobalImportCount();
+
+    for (int i = 0; i < n; i++)
+    {
+      if (sheet == getGlobalImport(i))
+        return i;
+    }
+
+    return -1;
+  }
+
+  /**
+   * This will be set up with the default values, and then the values
+   * will be set as stylesheets are encountered.
+   * @serial
+   */
+  private OutputProperties m_outputProperties;
+
+  /**
+   * Recompose the output format object from the included elements.
+   *
+   * @param oprops non-null reference to xsl:output properties representation.
+   */
+  void recomposeOutput(OutputProperties oprops)
+    throws TransformerException
+  {
+    
+    m_outputProperties.copyFrom(oprops);
+  }
+
+  /**
+   * Get the combined "xsl:output" property with the properties
+   * combined from the included stylesheets.  If a xsl:output
+   * is not declared in this stylesheet or an included stylesheet,
+   * look in the imports.
+   * Please note that this returns a reference to the OutputProperties
+   * object, not a cloned object, like getOutputProperties does.
+   * @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT Specification</a>
+   *
+   * @return non-null reference to composed output properties object.
+   */
+  public OutputProperties getOutputComposed()
+  {
+
+    // System.out.println("getOutputComposed.getIndent: "+m_outputProperties.getIndent());
+    // System.out.println("getOutputComposed.getIndenting: "+m_outputProperties.getIndenting());
+    return m_outputProperties;
+  }
+
+  /** Flag indicating whether an output method has been set by the user.
+   *  @serial           */
+  private boolean m_outputMethodSet = false;
+
+  /**
+   * Find out if an output method has been set by the user.
+   *
+   * @return Value indicating whether an output method has been set by the user
+   * @xsl.usage internal
+   */
+  public boolean isOutputMethodSet()
+  {
+    return m_outputMethodSet;
+  }
+
+  /**
+   * Composed set of all included and imported attribute set properties.
+   * Each entry is a vector of ElemAttributeSet objects.
+   * @serial
+   */
+  private HashMap m_attrSets;
+
+  /**
+   * Recompose the attribute-set declarations.
+   *
+   * @param attrSet An attribute-set to add to the hashtable of attribute sets.
+   */
+  void recomposeAttributeSets(ElemAttributeSet attrSet)
+  {
+    ArrayList attrSetList = (ArrayList) m_attrSets.get(attrSet.getName());
+
+    if (null == attrSetList)
+    {
+      attrSetList = new ArrayList();
+
+      m_attrSets.put(attrSet.getName(), attrSetList);
+    }
+
+    attrSetList.add(attrSet);
+  }
+
+  /**
+   * Get a list "xsl:attribute-set" properties that match the qname.
+   * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
+   *
+   * @param name Qualified name of attribute set properties to get
+   *
+   * @return A vector of attribute sets matching the given name
+   *
+   * @throws ArrayIndexOutOfBoundsException
+   */
+  public ArrayList getAttributeSetComposed(QName name)
+          throws ArrayIndexOutOfBoundsException
+  {
+    return (ArrayList) m_attrSets.get(name);
+  }
+
+  /**
+   * Table of DecimalFormatSymbols, keyed by QName.
+   * @serial
+   */
+  private Hashtable m_decimalFormatSymbols;
+
+  /**
+   * Recompose the decimal-format declarations.
+   *
+   * @param dfp A DecimalFormatProperties to add to the hashtable of decimal formats.
+   */
+  void recomposeDecimalFormats(DecimalFormatProperties dfp)
+  {
+    DecimalFormatSymbols oldDfs =
+                  (DecimalFormatSymbols) m_decimalFormatSymbols.get(dfp.getName());
+    if (null == oldDfs)
+    {
+      m_decimalFormatSymbols.put(dfp.getName(), dfp.getDecimalFormatSymbols());
+    }
+    else if (!dfp.getDecimalFormatSymbols().equals(oldDfs))
+    {
+      String themsg;
+      if (dfp.getName().equals(new QName("")))
+      {
+        // "Only one default xsl:decimal-format declaration is allowed."
+        themsg = XSLMessages.createWarning(
+                          XSLTErrorResources.WG_ONE_DEFAULT_XSLDECIMALFORMAT_ALLOWED,
+                          new Object[0]);
+      }
+      else
+      {
+        // "xsl:decimal-format names must be unique. Name {0} has been duplicated."
+        themsg = XSLMessages.createWarning(
+                          XSLTErrorResources.WG_XSLDECIMALFORMAT_NAMES_MUST_BE_UNIQUE,
+                          new Object[] {dfp.getName()});
+      }
+
+      error(themsg);   // Should we throw TransformerException instead?
+    }
+
+  }
+
+  /**
+   * Given a valid element decimal-format name, return the
+   * decimalFormatSymbols with that name.
+   * <p>It is an error to declare either the default decimal-format or
+   * a decimal-format with a given name more than once (even with
+   * different import precedence), unless it is declared every
+   * time with the same value for all attributes (taking into
+   * account any default values).</p>
+   * <p>Which means, as far as I can tell, the decimal-format
+   * properties are not additive.</p>
+   *
+   * @param name Qualified name of the decimal format to find 
+   * @return DecimalFormatSymbols object matching the given name or
+   * null if name is not found.
+   */
+  public DecimalFormatSymbols getDecimalFormatComposed(QName name)
+  {
+    return (DecimalFormatSymbols) m_decimalFormatSymbols.get(name);
+  }
+
+  /**
+   * A list of all key declarations visible from this stylesheet and all
+   * lesser stylesheets.
+   * @serial
+   */
+  private Vector m_keyDecls;
+
+  /**
+   * Recompose the key declarations.
+   *
+   * @param keyDecl A KeyDeclaration to be added to the vector of key declarations.
+   */
+  void recomposeKeys(KeyDeclaration keyDecl)
+  {
+    m_keyDecls.addElement(keyDecl);
+  }
+
+  /**
+   * Get the composed "xsl:key" properties.
+   * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a>
+   *
+   * @return A vector of the composed "xsl:key" properties.
+   */
+  public Vector getKeysComposed()
+  {
+    return m_keyDecls;
+  }
+
+  /**
+   * Composed set of all namespace aliases.
+   * @serial
+   */
+  private Hashtable m_namespaceAliasComposed;
+
+  /**
+   * Recompose the namespace-alias declarations.
+   *
+   * @param nsAlias A NamespaceAlias object to add to the hashtable of namespace aliases.
+   */
+  void recomposeNamespaceAliases(NamespaceAlias nsAlias)
+  {
+    m_namespaceAliasComposed.put(nsAlias.getStylesheetNamespace(),
+                                 nsAlias);
+  }
+
+  /**
+   * Get the "xsl:namespace-alias" property.
+   * Return the NamespaceAlias for a given namespace uri.
+   * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
+   *
+   * @param uri non-null reference to namespace that is to be aliased.
+   *
+   * @return NamespaceAlias that matches uri, or null if no match.
+   */
+  public NamespaceAlias getNamespaceAliasComposed(String uri)
+  {
+    return (NamespaceAlias) ((null == m_namespaceAliasComposed) 
+                    ? null : m_namespaceAliasComposed.get(uri));
+  }
+
+  /**
+   * The "xsl:template" properties.
+   * @serial
+   */
+  private TemplateList m_templateList;
+
+  /**
+   * Recompose the template declarations.
+   *
+   * @param template An ElemTemplate object to add to the template list.
+   */
+  void recomposeTemplates(ElemTemplate template)
+  {
+    m_templateList.setTemplate(template);
+  }
+
+  /**
+   * Accessor method to retrieve the <code>TemplateList</code> associated with
+   * this StylesheetRoot.
+   * 
+   * @return The composed <code>TemplateList</code>.
+   */
+  public final TemplateList getTemplateListComposed()
+  {
+    return m_templateList;
+  }
+
+  /**
+   * Mutator method to set the <code>TemplateList</code> associated with this
+   * StylesheetRoot.  This method should only be used by the compiler.  Normally,
+   * the template list is built during the recompose process and should not be
+   * altered by the user.
+   * @param templateList The new <code>TemplateList</code> for this StylesheetRoot.
+   */
+  public final void setTemplateListComposed(TemplateList templateList)
+  {
+    m_templateList = templateList;
+  }
+
+  /**
+   * Get an "xsl:template" property by node match. This looks in the imports as
+   * well as this stylesheet.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
+   *
+   * @param xctxt non-null reference to XPath runtime execution context.
+   * @param targetNode non-null reference of node that the template must match.
+   * @param mode qualified name of the node, or null.
+   * @param quietConflictWarnings true if conflict warnings should not be reported.
+   *
+   * @return reference to ElemTemplate that is the best match for targetNode, or 
+   *         null if no match could be made.
+   *
+   * @throws TransformerException
+   */
+  public ElemTemplate getTemplateComposed(XPathContext xctxt,
+                                          int targetNode,
+                                          QName mode,
+                                          boolean quietConflictWarnings,
+                                          DTM dtm)
+            throws TransformerException
+  {
+    return m_templateList.getTemplate(xctxt, targetNode, mode, 
+                                      quietConflictWarnings,
+                                      dtm);
+  }
+  
+  /**
+   * Get an "xsl:template" property by node match. This looks in the imports as
+   * well as this stylesheet.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
+   *
+   * @param xctxt non-null reference to XPath runtime execution context.
+   * @param targetNode non-null reference of node that the template must match.
+   * @param mode qualified name of the node, or null.
+   * @param maxImportLevel The maximum importCountComposed that we should consider or -1
+   *        if we should consider all import levels.  This is used by apply-imports to
+   *        access templates that have been overridden.
+   * @param endImportLevel The count of composed imports
+   * @param quietConflictWarnings true if conflict warnings should not be reported.
+   *
+   * @return reference to ElemTemplate that is the best match for targetNode, or 
+   *         null if no match could be made.
+   *
+   * @throws TransformerException
+   */
+  public ElemTemplate getTemplateComposed(XPathContext xctxt,
+                                          int targetNode,
+                                          QName mode,
+                                          int maxImportLevel, int endImportLevel,
+                                          boolean quietConflictWarnings,
+                                          DTM dtm)
+            throws TransformerException
+  {
+    return m_templateList.getTemplate(xctxt, targetNode, mode, 
+                                      maxImportLevel, endImportLevel,
+                                      quietConflictWarnings,
+                                      dtm);
+  }
+
+  /**
+   * Get an "xsl:template" property. This looks in the imports as
+   * well as this stylesheet.
+   * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
+   *
+   * @param qname non-null reference to qualified name of template.
+   *
+   * @return reference to named template, or null if not found.
+   */
+  public ElemTemplate getTemplateComposed(QName qname)
+  {
+    return m_templateList.getTemplate(qname);
+  }
+  
+  /**
+   * Composed set of all variables and params.
+   * @serial
+   */
+  private Vector m_variables;
+
+  /**
+   * Recompose the top level variable and parameter declarations.
+   *
+   * @param elemVar A top level variable or parameter to be added to the Vector.
+   */
+  void recomposeVariables(ElemVariable elemVar)
+  {
+    // Don't overide higher priority variable        
+    if (getVariableOrParamComposed(elemVar.getName()) == null)
+    {
+      elemVar.setIsTopLevel(true);        // Mark as a top-level variable or param
+      elemVar.setIndex(m_variables.size());
+      m_variables.addElement(elemVar);
+    }
+  }
+
+  /**
+   * Get an "xsl:variable" property.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @param qname Qualified name of variable or param
+   *
+   * @return The ElemVariable with the given qualified name
+   */
+  public ElemVariable getVariableOrParamComposed(QName qname)
+  {
+    if (null != m_variables)
+    {
+      int n = m_variables.size();
+
+      for (int i = 0; i < n; i++)
+      {
+        ElemVariable var = (ElemVariable)m_variables.elementAt(i);
+        if(var.getName().equals(qname))
+          return var;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Get all global "xsl:variable" properties in scope for this stylesheet.
+   * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
+   *
+   * @return Vector of all variables and params in scope
+   */
+  public Vector getVariablesAndParamsComposed()
+  {
+    return m_variables;
+  }
+
+  /**
+   * A list of properties that specify how to do space
+   * stripping. This uses the same exact mechanism as Templates.
+   * @serial
+   */
+  private TemplateList m_whiteSpaceInfoList;
+
+  /**
+   * Recompose the strip-space and preserve-space declarations.
+   *
+   * @param wsi A WhiteSpaceInfo element to add to the list of WhiteSpaceInfo elements.
+   */
+  void recomposeWhiteSpaceInfo(WhiteSpaceInfo wsi)
+  {
+    if (null == m_whiteSpaceInfoList)
+      m_whiteSpaceInfoList = new TemplateList();
+
+    m_whiteSpaceInfoList.setTemplate(wsi);
+  }
+
+  /**
+   * Check to see if the caller should bother with check for
+   * whitespace nodes.
+   *
+   * @return Whether the caller should bother with check for
+   * whitespace nodes.
+   */
+  public boolean shouldCheckWhitespace()
+  {
+    return null != m_whiteSpaceInfoList;
+  }
+
+  /**
+   * Get information about whether or not an element should strip whitespace.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @param support The XPath runtime state.
+   * @param targetElement Element to check
+   *
+   * @return WhiteSpaceInfo for the given element
+   *
+   * @throws TransformerException
+   */
+  public WhiteSpaceInfo getWhiteSpaceInfo(
+          XPathContext support, int targetElement, DTM dtm) throws TransformerException
+  {
+
+    if (null != m_whiteSpaceInfoList)
+      return (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support,
+              targetElement, null, false, dtm);
+    else
+      return null;
+  }
+  
+  /**
+   * Get information about whether or not an element should strip whitespace.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @param support The XPath runtime state.
+   * @param targetElement Element to check
+   *
+   * @return true if the whitespace should be stripped.
+   *
+   * @throws TransformerException
+   */
+  public boolean shouldStripWhiteSpace(
+          XPathContext support, int targetElement) throws TransformerException
+  {
+    if (null != m_whiteSpaceInfoList)
+    {
+      while(DTM.NULL != targetElement)
+      {
+        DTM dtm = support.getDTM(targetElement);
+        WhiteSpaceInfo info = (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support,
+                targetElement, null, false, dtm);
+        if(null != info)
+          return info.getShouldStripSpace();
+        
+        int parent = dtm.getParent(targetElement);
+        if(DTM.NULL != parent && DTM.ELEMENT_NODE == dtm.getNodeType(parent))
+          targetElement = parent;
+        else
+          targetElement = DTM.NULL;
+      }
+    }
+    return false;
+  }
+  
+  /**
+   * Get information about whether or not whitespace can be stripped.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @return true if the whitespace can be stripped.
+   */
+  public boolean canStripWhiteSpace()
+  {
+    return (null != m_whiteSpaceInfoList);
+  }
+  
+
+
+  /**
+   * The default template to use for text nodes if we don't find
+   * anything else.  This is initialized in initDefaultRule().
+   * @serial
+   * @xsl.usage advanced
+   */
+  private ElemTemplate m_defaultTextRule;
+
+  /**
+   * Get the default template for text.
+   *
+   * @return the default template for text.
+   * @xsl.usage advanced
+   */
+  public final ElemTemplate getDefaultTextRule()
+  {
+    return m_defaultTextRule;
+  }
+
+  /**
+   * The default template to use if we don't find anything
+   * else.  This is initialized in initDefaultRule().
+   * @serial
+   * @xsl.usage advanced
+   */
+  private ElemTemplate m_defaultRule;
+
+  /**
+   * Get the default template for elements.
+   *
+   * @return the default template for elements.
+   * @xsl.usage advanced
+   */
+  public final ElemTemplate getDefaultRule()
+  {
+    return m_defaultRule;
+  }
+
+  /**
+   * The default template to use for the root if we don't find
+   * anything else.  This is initialized in initDefaultRule().
+   * We kind of need this because the defaultRule isn't good
+   * enough because it doesn't supply a document context.
+   * For now, I default the root document element to "HTML".
+   * Don't know if this is really a good idea or not.
+   * I suspect it is not.
+   * @serial
+   * @xsl.usage advanced
+   */
+  private ElemTemplate m_defaultRootRule;
+
+  /**
+   * Get the default template for a root node.
+   *
+   * @return The default template for a root node.
+   * @xsl.usage advanced
+   */
+  public final ElemTemplate getDefaultRootRule()
+  {
+    return m_defaultRootRule;
+  }
+  
+  /**
+   * The start rule to kick off the transformation.
+   * @serial
+   * @xsl.usage advanced
+   */
+  private ElemTemplate m_startRule;
+
+  /**
+   * Get the default template for a root node.
+   *
+   * @return The default template for a root node.
+   * @xsl.usage advanced
+   */
+  public final ElemTemplate getStartRule()
+  {
+    return m_startRule;
+  }
+
+
+  /**
+   * Used for default selection.
+   * @serial
+   */
+  XPath m_selectDefault;
+
+  /**
+   * Create the default rule if needed.
+   *
+   * @throws TransformerException
+   */
+  private void initDefaultRule(ErrorListener errorListener) throws TransformerException
+  {
+
+    // Then manufacture a default
+    m_defaultRule = new ElemTemplate();
+
+    m_defaultRule.setStylesheet(this);
+
+    XPath defMatch = new XPath("*", this, this, XPath.MATCH, errorListener);
+
+    m_defaultRule.setMatch(defMatch);
+
+    ElemApplyTemplates childrenElement = new ElemApplyTemplates();
+
+    childrenElement.setIsDefaultTemplate(true);
+    childrenElement.setSelect(m_selectDefault);
+    m_defaultRule.appendChild(childrenElement);
+    
+    m_startRule = m_defaultRule;
+
+    // -----------------------------
+    m_defaultTextRule = new ElemTemplate();
+
+    m_defaultTextRule.setStylesheet(this);
+
+    defMatch = new XPath("text() | @*", this, this, XPath.MATCH, errorListener);
+
+    m_defaultTextRule.setMatch(defMatch);
+
+    ElemValueOf elemValueOf = new ElemValueOf();
+
+    m_defaultTextRule.appendChild(elemValueOf);
+
+    XPath selectPattern = new XPath(".", this, this, XPath.SELECT, errorListener);
+
+    elemValueOf.setSelect(selectPattern);
+
+    //--------------------------------
+    m_defaultRootRule = new ElemTemplate();
+
+    m_defaultRootRule.setStylesheet(this);
+
+    defMatch = new XPath("/", this, this, XPath.MATCH, errorListener);
+
+    m_defaultRootRule.setMatch(defMatch);
+
+    childrenElement = new ElemApplyTemplates();
+
+    childrenElement.setIsDefaultTemplate(true);
+    m_defaultRootRule.appendChild(childrenElement);
+    childrenElement.setSelect(m_selectDefault);
+  }
+
+  /**
+   * This is a generic version of C.A.R Hoare's Quick Sort
+   * algorithm.  This will handle arrays that are already
+   * sorted, and arrays with duplicate keys.  It was lifted from
+   * the NodeSorter class but should probably be eliminated and replaced
+   * with a call to Collections.sort when we migrate to Java2.<BR>
+   *
+   * If you think of a one dimensional array as going from
+   * the lowest index on the left to the highest index on the right
+   * then the parameters to this function are lowest index or
+   * left and highest index or right.  The first time you call
+   * this function it will be with the parameters 0, a.length - 1.
+   *
+   * @param v       a vector of ElemTemplateElement elements 
+   * @param lo0     left boundary of partition
+   * @param hi0     right boundary of partition
+   *
+   */
+
+  private void QuickSort2(Vector v, int lo0, int hi0)
+    {
+      int lo = lo0;
+      int hi = hi0;
+
+      if ( hi0 > lo0)
+      {
+        // Arbitrarily establishing partition element as the midpoint of
+        // the array.
+        ElemTemplateElement midNode = (ElemTemplateElement) v.elementAt( ( lo0 + hi0 ) / 2 );
+
+        // loop through the array until indices cross
+        while( lo <= hi )
+        {
+          // find the first element that is greater than or equal to
+          // the partition element starting from the left Index.
+          while( (lo < hi0) && (((ElemTemplateElement) v.elementAt(lo)).compareTo(midNode) < 0) )
+          {
+            ++lo;
+          } // end while
+
+          // find an element that is smaller than or equal to
+          // the partition element starting from the right Index.
+          while( (hi > lo0) && (((ElemTemplateElement) v.elementAt(hi)).compareTo(midNode) > 0) )          {
+            --hi;
+          }
+
+          // if the indexes have not crossed, swap
+          if( lo <= hi )
+          {
+            ElemTemplateElement node = (ElemTemplateElement) v.elementAt(lo);
+            v.setElementAt(v.elementAt(hi), lo);
+            v.setElementAt(node, hi);
+
+            ++lo;
+            --hi;
+          }
+        }
+
+        // If the right index has not reached the left side of array
+        // must now sort the left partition.
+        if( lo0 < hi )
+        {
+          QuickSort2( v, lo0, hi );
+        }
+
+        // If the left index has not reached the right side of array
+        // must now sort the right partition.
+        if( lo < hi0 )
+        {
+          QuickSort2( v, lo, hi0 );
+        }
+      }
+    } // end QuickSort2  */
+    
+    private transient ComposeState m_composeState;
+    
+    /**
+     * Initialize a new ComposeState.
+     */
+    void initComposeState()
+    {
+      m_composeState = new ComposeState();
+    }
+
+    /**
+     * Return class to track state global state during the compose() operation.
+     * @return ComposeState reference, or null if endCompose has been called.
+     */
+    ComposeState getComposeState()
+    {
+      return m_composeState;
+    }
+    
+    /**
+     * Clear the compose state.
+     */
+    private void clearComposeState()
+    {
+      m_composeState = null;
+    }
+
+    private String m_extensionHandlerClass = 
+        "org.apache.xalan.extensions.ExtensionHandlerExsltFunction";
+    
+    /**
+     * This internal method allows the setting of the java class
+     * to handle the extension function (if other than the default one).
+     * 
+     * @xsl.usage internal
+     */
+    public String setExtensionHandlerClass(String handlerClassName) {
+        String oldvalue = m_extensionHandlerClass;
+        m_extensionHandlerClass = handlerClassName;
+        return oldvalue;
+    } 
+    /**
+     * 
+     * @xsl.usage internal
+     */
+    public String getExtensionHandlerClass() {
+        return m_extensionHandlerClass;
+    }
+        
+    /**
+     * Class to track state global state during the compose() operation.
+     */
+    class ComposeState
+    {
+      ComposeState()
+      {
+        int size = m_variables.size();
+        for (int i = 0; i < size; i++) 
+        {
+          ElemVariable ev = (ElemVariable)m_variables.elementAt(i);
+          m_variableNames.addElement(ev.getName());
+        }
+        
+      }
+      
+      private ExpandedNameTable m_ent = new ExpandedNameTable();
+      
+      /**
+       * Given a qualified name, return an integer ID that can be 
+       * quickly compared.
+       *
+       * @param qname a qualified name object, must not be null.
+       *
+       * @return the expanded-name id of the qualified name.
+       */
+      public int getQNameID(QName qname)
+      {
+        
+        return m_ent.getExpandedTypeID(qname.getNamespace(), 
+                                       qname.getLocalName(),
+                                       // The type doesn't matter for our 
+                                       // purposes. 
+                                       org.apache.xml.dtm.DTM.ELEMENT_NODE);
+      }
+      
+      /**
+       * A Vector of the current params and QNames within the current template.
+       * Set by ElemTemplate and used by ProcessorVariable.
+       */
+      private java.util.Vector m_variableNames = new java.util.Vector();
+            
+      /**
+       * Add the name of a qualified name within the template.  The position in 
+       * the vector is its ID.
+       * @param qname A qualified name of a param or variable, should be non-null.
+       * @return the index where the variable was added.
+       */
+      int addVariableName(final org.apache.xml.utils.QName qname)
+      {
+        int pos = m_variableNames.size();
+        m_variableNames.addElement(qname);
+        int frameSize = m_variableNames.size() - getGlobalsSize();
+        if(frameSize > m_maxStackFrameSize)
+          m_maxStackFrameSize++;
+        return pos;
+      }
+      
+      void resetStackFrameSize()
+      {
+        m_maxStackFrameSize = 0;
+      }
+      
+      int getFrameSize()
+      {
+        return m_maxStackFrameSize;
+      }
+      
+      /**
+       * Get the current size of the stack frame.  Use this to record the position 
+       * in a template element at startElement, so that it can be popped 
+       * at endElement.
+       */
+      int getCurrentStackFrameSize()
+      {
+        return m_variableNames.size();
+      }
+      
+      /**
+       * Set the current size of the stack frame.
+       */
+      void setCurrentStackFrameSize(int sz)
+      {
+        m_variableNames.setSize(sz);
+      }
+      
+      int getGlobalsSize()
+      {
+        return m_variables.size();
+      }
+      
+      IntStack m_marks = new IntStack();
+      
+      void pushStackMark()
+      {
+        m_marks.push(getCurrentStackFrameSize());
+      }
+      
+      void popStackMark()
+      {
+        int mark = m_marks.pop();
+        setCurrentStackFrameSize(mark);
+      }
+      
+      /**
+       * Get the Vector of the current params and QNames to be collected 
+       * within the current template.
+       * @return A reference to the vector of variable names.  The reference 
+       * returned is owned by this class, and so should not really be mutated, or 
+       * stored anywhere.
+       */
+      java.util.Vector getVariableNames()
+      {
+        return m_variableNames;
+      }
+      
+      private int m_maxStackFrameSize;
+
+    }
+    
+    /**
+     * @return Optimization flag
+     */
+    public boolean getOptimizer() {
+        return m_optimizer;
+    }
+
+    /**
+     * @param b Optimization flag
+     */
+    public void setOptimizer(boolean b) {
+        m_optimizer = b;
+    }
+
+    /**
+     * @return Incremental flag
+     */
+    public boolean getIncremental() {
+        return m_incremental;
+    }
+
+    /**
+     * @return source location flag
+     */
+    public boolean getSource_location() {
+        return m_source_location;
+    }
+
+    /**
+     * @param b Incremental flag
+     */
+    public void setIncremental(boolean b) {
+        m_incremental = b;
+    }
+
+    /**
+     * @param b Source location flag
+     */
+    public void setSource_location(boolean b) {
+        m_source_location = b;
+    }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/TemplateList.java b/src/main/java/org/apache/xalan/templates/TemplateList.java
new file mode 100644
index 0000000..90c3169
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/TemplateList.java
@@ -0,0 +1,923 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TemplateList.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.compiler.PsuedoNames;
+import org.apache.xpath.patterns.NodeTest;
+import org.apache.xpath.patterns.StepPattern;
+import org.apache.xpath.patterns.UnionPattern;
+
+/**
+ * Encapsulates a template list, and helps locate individual templates.
+ * @xsl.usage advanced
+ */
+public class TemplateList implements java.io.Serializable
+{
+    static final long serialVersionUID = 5803675288911728791L;
+
+  /**
+   * Construct a TemplateList object. Needs to be public so it can
+   * be invoked from the CompilingStylesheetHandler.
+   */
+  public TemplateList()
+  {
+    super();
+  }
+
+  /**
+   * Add a template to the table of named templates and/or the table of templates
+   * with match patterns.  This routine should
+   * be called in decreasing order of precedence but it checks nonetheless.
+   *
+   * @param template
+   */
+  public void setTemplate(ElemTemplate template)
+  {
+    XPath matchXPath = template.getMatch();
+    
+    if (null == template.getName() && null == matchXPath)
+    {  
+      template.error(XSLTErrorResources.ER_NEED_NAME_OR_MATCH_ATTRIB,
+          new Object[]{ "xsl:template" });
+    }
+    
+    if (null != template.getName())
+    {
+      ElemTemplate existingTemplate = (ElemTemplate) m_namedTemplates.get(template.getName());
+      if (null == existingTemplate)
+      {
+        m_namedTemplates.put(template.getName(), template);
+      }
+      else
+      {
+        int existingPrecedence =
+                        existingTemplate.getStylesheetComposed().getImportCountComposed();
+        int newPrecedence = template.getStylesheetComposed().getImportCountComposed();
+        if (newPrecedence > existingPrecedence)
+        {
+          // This should never happen
+          m_namedTemplates.put(template.getName(), template);
+        }
+        else if (newPrecedence == existingPrecedence)
+          template.error(XSLTErrorResources.ER_DUPLICATE_NAMED_TEMPLATE,
+                       new Object[]{ template.getName() });
+      }
+    }
+
+    
+
+    if (null != matchXPath)
+    {
+      Expression matchExpr = matchXPath.getExpression();
+
+      if (matchExpr instanceof StepPattern)
+      {
+        insertPatternInTable((StepPattern) matchExpr, template);
+      }
+      else if (matchExpr instanceof UnionPattern)
+      {
+        UnionPattern upat = (UnionPattern) matchExpr;
+        StepPattern[] pats = upat.getPatterns();
+        int n = pats.length;
+
+        for (int i = 0; i < n; i++)
+        {
+          insertPatternInTable(pats[i], template);
+        }
+      }
+      else
+      {
+
+        // TODO: assert error
+      }
+    }
+  }
+
+  /** Flag to indicate whether in DEBUG mode          */
+  final static boolean DEBUG = false;
+
+  /**
+   * Dump all patterns and elements that match those patterns
+   *
+   */
+  void dumpAssociationTables()
+  {
+
+    Enumeration associations = m_patternTable.elements();
+
+    while (associations.hasMoreElements())
+    {
+      TemplateSubPatternAssociation head =
+        (TemplateSubPatternAssociation) associations.nextElement();
+
+      while (null != head)
+      {
+        System.out.print("(" + head.getTargetString() + ", "
+                         + head.getPattern() + ")");
+
+        head = head.getNext();
+      }
+
+      System.out.println("\n.....");
+    }
+
+    TemplateSubPatternAssociation head = m_wildCardPatterns;
+
+    System.out.print("wild card list: ");
+
+    while (null != head)
+    {
+      System.out.print("(" + head.getTargetString() + ", "
+                       + head.getPattern() + ")");
+
+      head = head.getNext();
+    }
+
+    System.out.println("\n.....");
+  }
+
+  /**
+   * After all templates have been added, this function
+   * should be called.
+   */
+  public void compose(StylesheetRoot sroot)
+  {
+
+    if (DEBUG)
+    {
+      System.out.println("Before wildcard insert...");
+      dumpAssociationTables();
+    }
+
+    if (null != m_wildCardPatterns)
+    {
+      Enumeration associations = m_patternTable.elements();
+
+      while (associations.hasMoreElements())
+      {
+        TemplateSubPatternAssociation head =
+          (TemplateSubPatternAssociation) associations.nextElement();
+        TemplateSubPatternAssociation wild = m_wildCardPatterns;
+
+        while (null != wild)
+        {
+          try
+          {
+            head = insertAssociationIntoList(
+              head, (TemplateSubPatternAssociation) wild.clone(), true);
+          }
+          catch (CloneNotSupportedException cnse){}
+
+          wild = wild.getNext();
+        }
+      }
+    }
+
+    if (DEBUG)
+    {
+      System.out.println("After wildcard insert...");
+      dumpAssociationTables();
+    }
+  }
+
+  /**
+   * Insert the given TemplateSubPatternAssociation into the the linked
+   * list.  Sort by import precedence, then priority, then by document order.
+   *
+   * @param head The first TemplateSubPatternAssociation in the linked list.
+   * @param item The item that we want to insert into the proper place.
+   * @param isWildCardInsert <code>true</code> if we are inserting a wild card 
+   *             template onto this list.
+   * @return the new head of the list.
+   */
+  private TemplateSubPatternAssociation
+              insertAssociationIntoList(TemplateSubPatternAssociation head,
+                                         TemplateSubPatternAssociation item,
+                                         boolean isWildCardInsert)
+  {
+
+    // Sort first by import level (higher level is at front),
+    // then by priority (highest priority is at front),
+    // then by document order (later in document is at front).
+
+    double priority = getPriorityOrScore(item);
+    double workPriority;
+    int importLevel = item.getImportLevel();
+    int docOrder = item.getDocOrderPos();
+    TemplateSubPatternAssociation insertPoint = head;
+    TemplateSubPatternAssociation next;
+    boolean insertBefore;         // true means insert before insertPoint; otherwise after
+                                  // This can only be true if insertPoint is pointing to
+                                  // the first or last template.
+
+    // Spin down so that insertPoint points to:
+    // (a) the template immediately _before_ the first template on the chain with
+    // a precedence that is either (i) less than ours or (ii) the same as ours but
+    // the template document position is less than ours
+    // -or-
+    // (b) the last template on the chain if no such template described in (a) exists.
+    // If we are pointing to the first template or the last template (that is, case b),
+    // we need to determine whether to insert before or after the template.  Otherwise,
+    // we always insert after the insertPoint.
+
+    while (true)
+    {
+      next = insertPoint.getNext();
+      if (null == next)
+        break;
+      else
+      {
+        workPriority = getPriorityOrScore(next);
+        if (importLevel > next.getImportLevel())
+          break;
+        else if (importLevel < next.getImportLevel())
+          insertPoint = next;
+        else if (priority > workPriority)               // import precedence is equal
+          break;
+        else if (priority < workPriority)
+          insertPoint = next;
+        else if (docOrder >= next.getDocOrderPos())     // priorities, import are equal
+          break;
+        else
+          insertPoint = next;
+      }
+    }
+
+    if ( (null == next) || (insertPoint == head) )      // insert point is first or last
+    {
+      workPriority = getPriorityOrScore(insertPoint);
+      if (importLevel > insertPoint.getImportLevel())
+        insertBefore = true;
+      else if (importLevel < insertPoint.getImportLevel())
+        insertBefore = false;
+      else if (priority > workPriority)
+        insertBefore = true;
+      else if (priority < workPriority)
+        insertBefore = false;
+      else if (docOrder >= insertPoint.getDocOrderPos())
+        insertBefore = true;
+      else
+        insertBefore = false;
+    }
+    else
+      insertBefore = false;
+
+    // System.out.println("appending: "+target+" to "+matchPat.getPattern());
+    
+    if (isWildCardInsert)
+    {
+      if (insertBefore)
+      {
+        item.setNext(insertPoint);
+
+        String key = insertPoint.getTargetString();
+
+        item.setTargetString(key);
+        putHead(key, item);
+        return item;
+      }
+      else
+      {
+        item.setNext(next);
+        insertPoint.setNext(item);
+        return head;
+      }
+    }
+    else
+    {
+      if (insertBefore)
+      {
+        item.setNext(insertPoint);
+
+        if (insertPoint.isWild() || item.isWild())
+          m_wildCardPatterns = item;
+        else
+          putHead(item.getTargetString(), item);
+        return item;
+      }
+      else
+      {
+        item.setNext(next);
+        insertPoint.setNext(item);
+        return head;
+      }
+    }
+  }
+
+  /**
+   * Add a template to the template list.
+   *
+   * @param pattern
+   * @param template
+   */
+  private void insertPatternInTable(StepPattern pattern, ElemTemplate template)
+  {
+
+    String target = pattern.getTargetString();
+
+    if (null != target)
+    {
+      String pstring = template.getMatch().getPatternString();
+      TemplateSubPatternAssociation association =
+        new TemplateSubPatternAssociation(template, pattern, pstring);
+
+      // See if there's already one there
+      boolean isWildCard = association.isWild();
+      TemplateSubPatternAssociation head = isWildCard
+                                           ? m_wildCardPatterns
+                                           : getHead(target);
+
+      if (null == head)
+      {
+        if (isWildCard)
+          m_wildCardPatterns = association;
+        else
+          putHead(target, association);
+      }
+      else
+      {
+        insertAssociationIntoList(head, association, false);
+      }
+    }
+  }
+
+  /**
+   * Given a match pattern and template association, return the 
+   * score of that match.  This score or priority can always be 
+   * statically calculated.
+   *
+   * @param matchPat The match pattern to template association.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST}, 
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE}, 
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD}, 
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}, or 
+   *         the value defined by the priority attribute of the template.
+   *
+   */
+  private double getPriorityOrScore(TemplateSubPatternAssociation matchPat)
+  {
+
+    double priority = matchPat.getTemplate().getPriority();
+
+    if (priority == XPath.MATCH_SCORE_NONE)
+    {
+      Expression ex = matchPat.getStepPattern();
+
+      if (ex instanceof NodeTest)
+      {
+        return ((NodeTest) ex).getDefaultScore();
+      }
+    }
+
+    return priority;
+  }
+
+  /**
+   * Locate a named template.
+   *
+   * @param qname  Qualified name of the template.
+   *
+   * @return Template argument with the requested name, or null if not found.
+   */
+  public ElemTemplate getTemplate(QName qname)
+  {
+    return (ElemTemplate) m_namedTemplates.get(qname);
+  }
+
+  /**
+   * Get the head of the most likely list of associations to check, based on 
+   * the name and type of the targetNode argument.
+   *
+   * @param xctxt The XPath runtime context.
+   * @param targetNode The target node that will be checked for a match.
+   * @param dtm The dtm owner for the target node.
+   *
+   * @return The head of a linked list that contains all possible match pattern to 
+   * template associations.
+   */
+  public TemplateSubPatternAssociation getHead(XPathContext xctxt, 
+                                               int targetNode, DTM dtm)
+  {
+    short targetNodeType = dtm.getNodeType(targetNode);
+    TemplateSubPatternAssociation head;
+
+    switch (targetNodeType)
+    {
+    case DTM.ELEMENT_NODE :
+    case DTM.ATTRIBUTE_NODE :
+      head = (TemplateSubPatternAssociation) m_patternTable.get(
+        dtm.getLocalName(targetNode));
+      break;
+    case DTM.TEXT_NODE :
+    case DTM.CDATA_SECTION_NODE :
+      head = m_textPatterns;
+      break;
+    case DTM.ENTITY_REFERENCE_NODE :
+    case DTM.ENTITY_NODE :
+      head = (TemplateSubPatternAssociation) m_patternTable.get(
+        dtm.getNodeName(targetNode)); // %REVIEW% I think this is right
+      break;
+    case DTM.PROCESSING_INSTRUCTION_NODE :
+      head = (TemplateSubPatternAssociation) m_patternTable.get(
+        dtm.getLocalName(targetNode));
+      break;
+    case DTM.COMMENT_NODE :
+      head = m_commentPatterns;
+      break;
+    case DTM.DOCUMENT_NODE :
+    case DTM.DOCUMENT_FRAGMENT_NODE :
+      head = m_docPatterns;
+      break;
+    case DTM.NOTATION_NODE :
+    default :
+      head = (TemplateSubPatternAssociation) m_patternTable.get(
+        dtm.getNodeName(targetNode)); // %REVIEW% I think this is right
+    }
+
+    return (null == head) ? m_wildCardPatterns : head;
+  }
+  
+  /**
+   * Given a target element, find the template that best
+   * matches in the given XSL document, according
+   * to the rules specified in the xsl draft.  This variation of getTemplate 
+   * assumes the current node and current expression node have already been 
+   * pushed. 
+   *
+   * @param xctxt
+   * @param targetNode
+   * @param mode A string indicating the display mode.
+   * @param maxImportLevel The maximum importCountComposed that we should consider or -1
+   *        if we should consider all import levels.  This is used by apply-imports to
+   *        access templates that have been overridden.
+   * @param quietConflictWarnings
+   * @return Rule that best matches targetElem.
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   */
+  public ElemTemplate getTemplateFast(XPathContext xctxt,
+                                int targetNode,
+                                int expTypeID,
+                                QName mode,
+                                int maxImportLevel,
+                                boolean quietConflictWarnings,
+                                DTM dtm)
+            throws TransformerException
+  {
+    
+    TemplateSubPatternAssociation head;
+
+    switch (dtm.getNodeType(targetNode))
+    {
+    case DTM.ELEMENT_NODE :
+    case DTM.ATTRIBUTE_NODE :
+      head = (TemplateSubPatternAssociation) m_patternTable.get(
+        dtm.getLocalNameFromExpandedNameID(expTypeID));
+      break;
+    case DTM.TEXT_NODE :
+    case DTM.CDATA_SECTION_NODE :
+      head = m_textPatterns;
+      break;
+    case DTM.ENTITY_REFERENCE_NODE :
+    case DTM.ENTITY_NODE :
+      head = (TemplateSubPatternAssociation) m_patternTable.get(
+        dtm.getNodeName(targetNode)); // %REVIEW% I think this is right
+      break;
+    case DTM.PROCESSING_INSTRUCTION_NODE :
+      head = (TemplateSubPatternAssociation) m_patternTable.get(
+        dtm.getLocalName(targetNode));
+      break;
+    case DTM.COMMENT_NODE :
+      head = m_commentPatterns;
+      break;
+    case DTM.DOCUMENT_NODE :
+    case DTM.DOCUMENT_FRAGMENT_NODE :
+      head = m_docPatterns;
+      break;
+    case DTM.NOTATION_NODE :
+    default :
+      head = (TemplateSubPatternAssociation) m_patternTable.get(
+        dtm.getNodeName(targetNode)); // %REVIEW% I think this is right
+    }
+
+    if(null == head)
+    {
+      head = m_wildCardPatterns;
+      if(null == head)
+        return null;
+    }                                              
+
+    // XSLT functions, such as xsl:key, need to be able to get to 
+    // current ElemTemplateElement via a cast to the prefix resolver.
+    // Setting this fixes bug idkey03.
+    xctxt.pushNamespaceContextNull();
+    try
+    {
+      do
+      {
+        if ( (maxImportLevel > -1) && (head.getImportLevel() > maxImportLevel) )
+        {
+          continue;
+        }
+        ElemTemplate template = head.getTemplate();        
+        xctxt.setNamespaceContext(template);
+        
+        if ((head.m_stepPattern.execute(xctxt, targetNode, dtm, expTypeID) != NodeTest.SCORE_NONE)
+                && head.matchMode(mode))
+        {
+          if (quietConflictWarnings)
+            checkConflicts(head, xctxt, targetNode, mode);
+
+          return template;
+        }
+      }
+      while (null != (head = head.getNext()));
+    }
+    finally
+    {
+      xctxt.popNamespaceContext();
+    }
+
+    return null;
+  }  // end findTemplate
+
+  /**
+   * Given a target element, find the template that best
+   * matches in the given XSL document, according
+   * to the rules specified in the xsl draft.
+   *
+   * @param xctxt
+   * @param targetNode
+   * @param mode A string indicating the display mode.
+   * @param quietConflictWarnings
+   * @return Rule that best matches targetElem.
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   */
+  public ElemTemplate getTemplate(XPathContext xctxt,
+                                int targetNode,
+                                QName mode,
+                                boolean quietConflictWarnings,
+                                DTM dtm)
+            throws TransformerException
+  {
+
+    TemplateSubPatternAssociation head = getHead(xctxt, targetNode, dtm);
+
+    if (null != head)
+    {
+      // XSLT functions, such as xsl:key, need to be able to get to 
+      // current ElemTemplateElement via a cast to the prefix resolver.
+      // Setting this fixes bug idkey03.
+      xctxt.pushNamespaceContextNull();
+      xctxt.pushCurrentNodeAndExpression(targetNode, targetNode);
+      try
+      {
+        do
+        {
+          ElemTemplate template = head.getTemplate();        
+          xctxt.setNamespaceContext(template);
+          
+          if ((head.m_stepPattern.execute(xctxt, targetNode) != NodeTest.SCORE_NONE)
+                  && head.matchMode(mode))
+          {
+            if (quietConflictWarnings)
+              checkConflicts(head, xctxt, targetNode, mode);
+
+            return template;
+          }
+        }
+        while (null != (head = head.getNext()));
+      }
+      finally
+      {
+        xctxt.popCurrentNodeAndExpression();
+        xctxt.popNamespaceContext();
+      }
+    }
+
+    return null;
+  }  // end findTemplate
+  
+  /**
+   * Given a target element, find the template that best
+   * matches in the given XSL document, according
+   * to the rules specified in the xsl draft.
+   *
+   * @param xctxt
+   * @param targetNode
+   * @param mode A string indicating the display mode.
+   * @param maxImportLevel The maximum importCountComposed that we should consider or -1
+   *        if we should consider all import levels.  This is used by apply-imports to
+   *        access templates that have been overridden.
+   * @param endImportLevel The count of composed imports
+   * @param quietConflictWarnings
+   * @return Rule that best matches targetElem.
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   */
+  public ElemTemplate getTemplate(XPathContext xctxt,
+                                int targetNode,
+                                QName mode,
+                                int maxImportLevel, int endImportLevel,
+                                boolean quietConflictWarnings,
+                                DTM dtm)
+            throws TransformerException
+  {
+
+    TemplateSubPatternAssociation head = getHead(xctxt, targetNode, dtm);
+
+    if (null != head)
+    {
+      // XSLT functions, such as xsl:key, need to be able to get to 
+      // current ElemTemplateElement via a cast to the prefix resolver.
+      // Setting this fixes bug idkey03.
+      xctxt.pushNamespaceContextNull();
+      xctxt.pushCurrentNodeAndExpression(targetNode, targetNode);
+      try
+      {
+        do
+        {
+          if ( (maxImportLevel > -1) && (head.getImportLevel() > maxImportLevel))
+          {
+            continue;
+          }
+          if (head.getImportLevel()<= maxImportLevel - endImportLevel)
+            return null;
+          ElemTemplate template = head.getTemplate();        
+          xctxt.setNamespaceContext(template);
+          
+          if ((head.m_stepPattern.execute(xctxt, targetNode) != NodeTest.SCORE_NONE)
+                  && head.matchMode(mode))
+          {
+            if (quietConflictWarnings)
+              checkConflicts(head, xctxt, targetNode, mode);
+
+            return template;
+          }
+        }
+        while (null != (head = head.getNext()));
+      }
+      finally
+      {
+        xctxt.popCurrentNodeAndExpression();
+        xctxt.popNamespaceContext();
+      }
+    }
+
+    return null;
+  }  // end findTemplate
+
+  /**
+   * Get a TemplateWalker for use by a compiler.  See the documentation for
+   * the TreeWalker inner class for further details.
+   */
+  public TemplateWalker getWalker()
+  {
+    return new TemplateWalker();
+  }
+
+  /**
+   * Check for match conflicts, and warn the stylesheet author.
+   *
+   * @param head Template pattern
+   * @param xctxt Current XPath context
+   * @param targetNode Node matching the pattern
+   * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
+   */
+  private void checkConflicts(TemplateSubPatternAssociation head,
+                              XPathContext xctxt, int targetNode, QName mode)
+  {
+
+    // TODO: Check for conflicts.
+  }
+
+  /**
+   * Add object to vector if not already there.
+   *
+   * @param obj
+   * @param v
+   */
+  private void addObjectIfNotFound(Object obj, Vector v)
+  {
+
+    int n = v.size();
+    boolean addIt = true;
+
+    for (int i = 0; i < n; i++)
+    {
+      if (v.elementAt(i) == obj)
+      {
+        addIt = false;
+
+        break;
+      }
+    }
+
+    if (addIt)
+    {
+      v.addElement(obj);
+    }
+  }
+
+  /**
+   * Keyed on string macro names, and holding values
+   * that are macro elements in the XSL DOM tree.
+   * Initialized in initMacroLookupTable, and used in
+   * findNamedTemplate.
+   * @serial
+   */
+  private Hashtable m_namedTemplates = new Hashtable(89);
+
+  /**
+   * This table is keyed on the target elements
+   * of patterns, and contains linked lists of
+   * the actual patterns that match the target element
+   * to some degree of specifity.
+   * @serial
+   */
+  private Hashtable m_patternTable = new Hashtable(89);
+
+  /** Wildcard patterns.
+   *  @serial          */
+  private TemplateSubPatternAssociation m_wildCardPatterns = null;
+
+  /** Text Patterns.
+   *  @serial          */
+  private TemplateSubPatternAssociation m_textPatterns = null;
+
+  /** Root document Patterns.
+   *  @serial          */
+  private TemplateSubPatternAssociation m_docPatterns = null;
+
+  /** Comment Patterns.
+   *  @serial          */
+  private TemplateSubPatternAssociation m_commentPatterns = null;
+
+  /**
+   * Get table of named Templates.
+   * These are keyed on template names, and holding values
+   * that are template elements.
+   *
+   * @return A Hashtable dictionary that contains {@link java.lang.String}s 
+   * as the keys, and {@link org.apache.xalan.templates.ElemTemplate}s as the 
+   * values. 
+   */
+  private Hashtable getNamedTemplates()
+  {
+    return m_namedTemplates;
+  }
+
+  /**
+   * Set table of named Templates.
+   * These are keyed on string macro names, and holding values
+   * that are template elements in the XSL DOM tree.
+   *
+   * @param v Hashtable dictionary that contains {@link java.lang.String}s 
+   * as the keys, and {@link org.apache.xalan.templates.ElemTemplate}s as the 
+   * values.
+   */
+  private void setNamedTemplates(Hashtable v)
+  {
+    m_namedTemplates = v;
+  }
+
+  /**
+   * Get the head of the assocation list that is keyed by target.
+   *
+   * @param key The name of a node. 
+   *
+   * @return The head of a linked list that contains all possible match pattern to 
+   * template associations for the given key.
+   */
+  private TemplateSubPatternAssociation getHead(String key)
+  {
+    return (TemplateSubPatternAssociation) m_patternTable.get(key);
+  }
+
+  /**
+   * Get the head of the assocation list that is keyed by target.
+   *
+   * @param key
+   * @param assoc
+   */
+  private void putHead(String key, TemplateSubPatternAssociation assoc)
+  {
+
+    if (key.equals(PsuedoNames.PSEUDONAME_TEXT))
+      m_textPatterns = assoc;
+    else if (key.equals(PsuedoNames.PSEUDONAME_ROOT))
+      m_docPatterns = assoc;
+    else if (key.equals(PsuedoNames.PSEUDONAME_COMMENT))
+      m_commentPatterns = assoc;
+
+    m_patternTable.put(key, assoc);
+  }
+
+  /**
+   * An inner class used by a compiler to iterate over all of the ElemTemplates
+   * stored in this TemplateList.  The compiler can replace returned templates
+   * with their compiled equivalent.
+   */
+  public class TemplateWalker
+  {
+    private Enumeration hashIterator;
+    private boolean inPatterns;
+    private TemplateSubPatternAssociation curPattern;
+
+    private Hashtable m_compilerCache = new Hashtable();
+
+    private TemplateWalker()
+    {
+      hashIterator = m_patternTable.elements();
+      inPatterns = true;
+      curPattern = null;
+    }
+
+    public ElemTemplate next()
+    {
+
+      ElemTemplate retValue = null;
+      ElemTemplate ct;
+
+      while (true)
+      {
+        if (inPatterns)
+        {
+          if (null != curPattern)
+            curPattern = curPattern.getNext();
+
+          if (null != curPattern)
+            retValue = curPattern.getTemplate();
+          else
+          {
+            if (hashIterator.hasMoreElements())
+            {
+              curPattern = (TemplateSubPatternAssociation) hashIterator.nextElement();
+              retValue =  curPattern.getTemplate();
+            }
+            else
+            {
+              inPatterns = false;
+              hashIterator = m_namedTemplates.elements();
+            }
+          }
+        }
+
+        if (!inPatterns)
+        {
+          if (hashIterator.hasMoreElements())
+            retValue = (ElemTemplate) hashIterator.nextElement();
+          else
+            return null;
+        }
+
+        ct = (ElemTemplate) m_compilerCache.get(new Integer(retValue.getUid()));
+        if (null == ct)
+        {
+          m_compilerCache.put(new Integer(retValue.getUid()), retValue);
+          return retValue;
+        }
+      }
+    }
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/TemplateSubPatternAssociation.java b/src/main/java/org/apache/xalan/templates/TemplateSubPatternAssociation.java
new file mode 100644
index 0000000..40477aa
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/TemplateSubPatternAssociation.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TemplateSubPatternAssociation.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.io.Serializable;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.patterns.StepPattern;
+
+/**
+ * A class to contain a match pattern and it's corresponding template.
+ * This class also defines a node in a match pattern linked list.
+ */
+class TemplateSubPatternAssociation implements Serializable, Cloneable
+{
+    static final long serialVersionUID = -8902606755229903350L;
+
+  /** Step pattern           */
+  StepPattern m_stepPattern;
+
+  /** Template pattern          */
+  private String m_pattern;
+
+  /** The template element         */
+  private ElemTemplate m_template;
+
+  /** Next pattern         */
+  private TemplateSubPatternAssociation m_next = null;
+
+  /** Flag indicating whether this is wild card pattern          */
+  private boolean m_wild;
+
+  /** Target string for this match pattern           */
+  private String m_targetString;
+
+  /**
+   * Construct a match pattern from a pattern and template.
+   * @param template The node that contains the template for this pattern.
+   * @param pattern An executable XSLT StepPattern.
+   * @param pat For now a Nodelist that contains old-style element patterns.
+   */
+  TemplateSubPatternAssociation(ElemTemplate template, StepPattern pattern, String pat)
+  {
+
+    m_pattern = pat;
+    m_template = template;
+    m_stepPattern = pattern;
+    m_targetString = m_stepPattern.getTargetString();
+    m_wild = m_targetString.equals("*");
+  }
+
+  /**
+   * Clone this object.
+   *
+   * @return The cloned object.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+
+    TemplateSubPatternAssociation tspa =
+      (TemplateSubPatternAssociation) super.clone();
+
+    tspa.m_next = null;
+
+    return tspa;
+  }
+
+  /**
+   * Get the target string of the pattern.  For instance, if the pattern is
+   * "foo/baz/boo[@daba]", this string will be "boo".
+   *
+   * @return The "target" string.
+   */
+  public final String getTargetString()
+  {
+    return m_targetString;
+  }
+
+  /**
+   * Set Target String for this template pattern  
+   *
+   *
+   * @param key Target string to set
+   */
+  public void setTargetString(String key)
+  {
+    m_targetString = key;
+  }
+
+  /**
+   * Tell if two modes match according to the rules of XSLT.
+   *
+   * @param m1 mode to match
+   *
+   * @return True if the given mode matches this template's mode
+   */
+  boolean matchMode(QName m1)
+  {
+    return matchModes(m1, m_template.getMode());
+  }
+
+  /**
+   * Tell if two modes match according to the rules of XSLT.
+   *
+   * @param m1 First mode to match
+   * @param m2 Second mode to match
+   *
+   * @return True if the two given modes match
+   */
+  private boolean matchModes(QName m1, QName m2)
+  {
+    return (((null == m1) && (null == m2))
+            || ((null != m1) && (null != m2) && m1.equals(m2)));
+  }
+
+  /**
+   * Return the mode associated with the template.
+   *
+   *
+   * @param xctxt XPath context to use with this template
+   * @param targetNode Target node
+   * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
+   * @return The mode associated with the template.
+   *
+   * @throws TransformerException
+   */
+  public boolean matches(XPathContext xctxt, int targetNode, QName mode)
+          throws TransformerException
+  {
+
+    double score = m_stepPattern.getMatchScore(xctxt, targetNode);
+
+    return (XPath.MATCH_SCORE_NONE != score)
+           && matchModes(mode, m_template.getMode());
+  }
+
+  /**
+   * Tell if the pattern for this association is a wildcard.
+   *
+   * @return true if this pattern is considered to be a wild match.
+   */
+  public final boolean isWild()
+  {
+    return m_wild;
+  }
+
+  /**
+   * Get associated XSLT StepPattern.
+   *
+   * @return An executable StepPattern object, never null.
+   *
+   */
+  public final StepPattern getStepPattern()
+  {
+    return m_stepPattern;
+  }
+
+  /**
+   * Get the pattern string for diagnostic purposes.
+   *
+   * @return The pattern string for diagnostic purposes.
+   *
+   */
+  public final String getPattern()
+  {
+    return m_pattern;
+  }
+
+  /**
+   * Return the position of the template in document
+   * order in the stylesheet.
+   *
+   * @return The position of the template in the overall template order.
+   */
+  public int getDocOrderPos()
+  {
+    return m_template.getUid();
+  }
+
+  /**
+   * Return the import level associated with the stylesheet into which  
+   * this template is composed.
+   *
+   * @return The import level of this template.
+   */
+  public final int getImportLevel()
+  {
+    return m_template.getStylesheetComposed().getImportCountComposed();
+  }
+
+  /**
+   * Get the assocated xsl:template.
+   *
+   * @return An ElemTemplate, never null.
+   *
+   */
+  public final ElemTemplate getTemplate()
+  {
+    return m_template;
+  }
+
+  /**
+   * Get the next association.
+   *
+   * @return A valid TemplateSubPatternAssociation, or null.
+   */
+  public final TemplateSubPatternAssociation getNext()
+  {
+    return m_next;
+  }
+
+  /**
+   * Set the next element on this association
+   * list, which should be equal or less in priority to
+   * this association, and, if equal priority, should occur
+   * before this template in document order.
+   *
+   * @param mp The next association to score if this one fails.
+   *
+   */
+  public void setNext(TemplateSubPatternAssociation mp)
+  {
+    m_next = mp;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/VarNameCollector.java b/src/main/java/org/apache/xalan/templates/VarNameCollector.java
new file mode 100644
index 0000000..c9842fc
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/VarNameCollector.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: VarNameCollector.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import java.util.Vector;
+
+import org.apache.xml.utils.QName;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.operations.Variable;
+
+/**
+ * This class visits variable refs in an XPath and collects their QNames.
+ */
+public class VarNameCollector extends XPathVisitor
+{
+	Vector m_refs = new Vector();
+	
+	/**
+	 * Reset the list for a fresh visitation and collection.
+	 */
+	public void reset()
+	{
+		m_refs.removeAllElements(); //.clear();
+	}
+	
+	/**
+	 * Get the number of variable references that were collected.
+	 * @return the size of the list.
+	 */
+	public int getVarCount()
+	{
+		return m_refs.size();
+	}
+	
+	/**
+	 * Tell if the given qualified name occurs in 
+	 * the list of qualified names collected.
+	 * 
+	 * @param refName Must be a valid qualified name.
+	 * @return true if the list contains the qualified name.
+	 */
+	boolean doesOccur(QName refName)
+	{
+		return m_refs.contains(refName);
+	}
+
+	/**
+	 * Visit a variable reference.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param var The variable reference object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitVariableRef(ExpressionOwner owner, Variable var)
+	{
+		m_refs.addElement(var.getQName());
+		return true;
+	}
+
+}
+
diff --git a/src/main/java/org/apache/xalan/templates/WhiteSpaceInfo.java b/src/main/java/org/apache/xalan/templates/WhiteSpaceInfo.java
new file mode 100644
index 0000000..5bf45b7
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/WhiteSpaceInfo.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WhiteSpaceInfo.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xpath.XPath;
+
+/**
+ * This is used as a special "fake" template that can be
+ * handled by the TemplateList to do pattern matching
+ * on nodes.
+ */
+public class WhiteSpaceInfo extends ElemTemplate
+{
+    static final long serialVersionUID = 6389208261999943836L;
+
+  /** Flag indicating whether whitespaces should be stripped.
+   *  @serial        */
+  private boolean m_shouldStripSpace;
+
+  /**
+   * Return true if this element specifies that the node that
+   * matches the match pattern should be stripped, otherwise
+   * the space should be preserved.
+   *
+   * @return value of m_shouldStripSpace flag
+   */
+  public boolean getShouldStripSpace()
+  {
+    return m_shouldStripSpace;
+  }
+  
+  /**
+   * Constructor WhiteSpaceInfo
+   * @param thisSheet The current stylesheet
+   */
+  public WhiteSpaceInfo(Stylesheet thisSheet)
+  {
+  	setStylesheet(thisSheet);
+  }
+
+
+  /**
+   * Constructor WhiteSpaceInfo
+   *
+   *
+   * @param matchPattern Match pattern
+   * @param shouldStripSpace Flag indicating whether or not
+   * to strip whitespaces
+   * @param thisSheet The current stylesheet
+   */
+  public WhiteSpaceInfo(XPath matchPattern, boolean shouldStripSpace, Stylesheet thisSheet)
+  {
+
+    m_shouldStripSpace = shouldStripSpace;
+
+    setMatch(matchPattern);
+
+    setStylesheet(thisSheet);
+  }
+
+  /**
+   * This function is called to recompose() all of the WhiteSpaceInfo elements.
+   */
+  public void recompose(StylesheetRoot root)
+  {
+    root.recomposeWhiteSpaceInfo(this);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/XMLNSDecl.java b/src/main/java/org/apache/xalan/templates/XMLNSDecl.java
new file mode 100644
index 0000000..4bc1523
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/XMLNSDecl.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLNSDecl.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+/**
+ * Represents an xmlns declaration
+ */
+public class XMLNSDecl
+        implements java.io.Serializable // 20001009 jkess
+{
+    static final long serialVersionUID = 6710237366877605097L;
+
+  /**
+   * Constructor XMLNSDecl
+   *
+   * @param prefix non-null reference to prefix, using "" for default namespace.
+   * @param uri non-null reference to namespace URI.
+   * @param isExcluded true if this namespace declaration should normally be excluded.
+   */
+  public XMLNSDecl(String prefix, String uri, boolean isExcluded)
+  {
+
+    m_prefix = prefix;
+    m_uri = uri;
+    m_isExcluded = isExcluded;
+  }
+
+  /** non-null reference to prefix, using "" for default namespace.
+   *  @serial */
+  private String m_prefix;
+
+  /**
+   * Return the prefix.
+   * @return The prefix that is associated with this URI, or null
+   * if the XMLNSDecl is declaring the default namespace.
+   */
+  public String getPrefix()
+  {
+    return m_prefix;
+  }
+
+  /** non-null reference to namespace URI.
+   *  @serial  */
+  private String m_uri;
+
+  /**
+   * Return the URI.
+   * @return The URI that is associated with this declaration.
+   */
+  public String getURI()
+  {
+    return m_uri;
+  }
+
+  /** true if this namespace declaration should normally be excluded.
+   *  @serial  */
+  private boolean m_isExcluded;
+
+  /**
+   * Tell if this declaration should be excluded from the
+   * result namespace.
+   *
+   * @return true if this namespace declaration should normally be excluded.
+   */
+  public boolean getIsExcluded()
+  {
+    return m_isExcluded;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/templates/XSLTVisitable.java b/src/main/java/org/apache/xalan/templates/XSLTVisitable.java
new file mode 100644
index 0000000..a35a6e1
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/XSLTVisitable.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLTVisitable.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+/**
+ * A class that implements this interface will call a XSLTVisitor 
+ * for itself and members within it's heararchy.  If the XSLTVistor's 
+ * method returns false, the sub-member heararchy will not be 
+ * traversed.
+ */
+public interface XSLTVisitable
+{
+	/**
+	 * This will traverse the heararchy, calling the visitor for 
+	 * each member.  If the called visitor method returns 
+	 * false, the subtree should not be called.
+	 * 
+	 * @param visitor The visitor whose appropriate method will be called.
+	 */
+	public void callVisitors(XSLTVisitor visitor);
+}
+
diff --git a/src/main/java/org/apache/xalan/templates/XSLTVisitor.java b/src/main/java/org/apache/xalan/templates/XSLTVisitor.java
new file mode 100644
index 0000000..cb68f1f
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/XSLTVisitor.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLTVisitor.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xpath.XPathVisitor;
+
+/**
+ * A derivation from this class can be passed to a class that implements 
+ * the XSLTVisitable interface, to have the appropriate method called 
+ * for each component of an XSLT stylesheet.  Aside from possible other uses,
+ * the main intention is to provide a reasonable means to perform expression 
+ * rewriting.
+ */
+public class XSLTVisitor extends XPathVisitor
+{
+	/**
+	 * Visit an XSLT instruction.  Any element that isn't called by one 
+	 * of the other visit methods, will be called by this method.
+	 * 
+	 * @param elem The xsl instruction element object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitInstruction(ElemTemplateElement elem)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit an XSLT stylesheet instruction.
+	 * 
+	 * @param elem The xsl instruction element object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitStylesheet(ElemTemplateElement elem)
+	{
+		return true;
+	}
+
+	
+	/**
+	 * Visit an XSLT top-level instruction.
+	 * 
+	 * @param elem The xsl instruction element object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitTopLevelInstruction(ElemTemplateElement elem)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit an XSLT top-level instruction.
+	 * 
+	 * @param elem The xsl instruction element object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitTopLevelVariableOrParamDecl(ElemTemplateElement elem)
+	{
+		return true;
+	}
+
+	
+	/**
+	 * Visit an XSLT variable or parameter declaration.
+	 * 
+	 * @param elem The xsl instruction element object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitVariableOrParamDecl(ElemVariable elem)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit a LiteralResultElement.
+	 * 
+	 * @param elem The literal result object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitLiteralResultElement(ElemLiteralResult elem)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit an Attribute Value Template (at the top level).
+	 * 
+	 * @param elem The attribute value template object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitAVT(AVT elem)
+	{
+		return true;
+	}
+
+
+	/**
+	 * Visit an extension element.
+	 * @param elem The extension object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitExtensionElement(ElemExtensionCall elem)
+	{
+		return true;
+	}
+
+}
+
diff --git a/src/main/java/org/apache/xalan/templates/XUnresolvedVariable.java b/src/main/java/org/apache/xalan/templates/XUnresolvedVariable.java
new file mode 100644
index 0000000..e821c80
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/XUnresolvedVariable.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XUnresolvedVariable.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * An instance of this class holds unto a variable until 
+ * it is executed.  It is used at this time for global 
+ * variables which must (we think) forward reference.
+ */
+public class XUnresolvedVariable extends XObject
+{  
+    static final long serialVersionUID = -256779804767950188L;
+  /** The node context for execution. */
+  transient private int m_context;
+  
+  /** The transformer context for execution. */
+  transient private TransformerImpl m_transformer;
+  
+  /** An index to the point in the variable stack where we should
+   * begin variable searches for evaluation of expressions.
+   * This is -1 if m_isTopLevel is false. 
+   **/
+  transient private int m_varStackPos = -1;
+
+  /** An index into the variable stack where the variable context 
+   * ends, i.e. at the point we should terminate the search. 
+   **/
+  transient private int m_varStackContext;
+  
+  /** true if this variable or parameter is a global.
+   *  @serial */
+  private boolean m_isGlobal;
+  
+  /** true if this variable or parameter is not currently being evaluated. */
+  transient private boolean m_doneEval = true;
+  
+  /**
+   * Create an XUnresolvedVariable, that may be executed at a later time.
+   * This is primarily used so that forward referencing works with 
+   * global variables.  An XUnresolvedVariable is initially pushed 
+   * into the global variable stack, and then replaced with the real 
+   * thing when it is accessed.
+   *
+   * @param obj Must be a non-null reference to an ElemVariable.
+   * @param sourceNode The node context for execution.
+   * @param transformer The transformer execution context.
+   * @param varStackPos An index to the point in the variable stack where we should
+   * begin variable searches for evaluation of expressions.
+   * @param varStackContext An index into the variable stack where the variable context 
+   * ends, i.e. at the point we should terminate the search.
+   * @param isGlobal true if this is a global variable.
+   */
+  public XUnresolvedVariable(ElemVariable obj, int sourceNode, 
+                             TransformerImpl transformer,
+                             int varStackPos, int varStackContext,
+                             boolean isGlobal)
+  {
+    super(obj);
+    m_context = sourceNode;
+    m_transformer = transformer;
+    
+    // For globals, this value will have to be updated once we 
+    // have determined how many global variables have been pushed.
+    m_varStackPos = varStackPos;
+    
+    // For globals, this should zero.
+    m_varStackContext = varStackContext;
+    
+    m_isGlobal = isGlobal;
+  }
+    
+  /**
+   * For support of literal objects in xpaths.
+   *
+   * @param xctxt The XPath execution context.
+   *
+   * @return This object.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    if (!m_doneEval) 
+    {
+      this.m_transformer.getMsgMgr().error      
+        (xctxt.getSAXLocator(), XSLTErrorResources.ER_REFERENCING_ITSELF, 
+          new Object[]{((ElemVariable)this.object()).getName().getLocalName()}); 
+    }
+    VariableStack vars = xctxt.getVarStack();
+    
+    // These three statements need to be combined into one operation.
+    int currentFrame = vars.getStackFrame();
+    //// vars.setStackFrame(m_varStackPos);
+   
+
+    ElemVariable velem = (ElemVariable)m_obj;
+    try
+    {
+      m_doneEval = false;
+      if(-1 != velem.m_frameSize)
+      	vars.link(velem.m_frameSize);
+      XObject var = velem.getValue(m_transformer, m_context);
+      m_doneEval = true;
+      return var;
+    }
+    finally
+    {
+      // These two statements need to be combined into one operation.
+      // vars.setStackFrame(currentFrame);
+      
+      if(-1 != velem.m_frameSize)
+	  	vars.unlink(currentFrame);
+    }
+  }
+  
+  /**
+   * Set an index to the point in the variable stack where we should
+   * begin variable searches for evaluation of expressions.
+   * This is -1 if m_isTopLevel is false. 
+   * 
+   * @param top A valid value that specifies where in the variable 
+   * stack the search should begin.
+   */
+  public void setVarStackPos(int top)
+  {
+    m_varStackPos = top;
+  }
+
+  /**
+   * Set an index into the variable stack where the variable context 
+   * ends, i.e. at the point we should terminate the search.
+   * 
+   * @param bottom The point at which the search should terminate, normally 
+   * zero for global variables.
+   */
+  public void setVarStackContext(int bottom)
+  {
+    m_varStackContext = bottom;
+  }
+  
+  /**
+   * Tell what kind of class this is.
+   *
+   * @return CLASS_UNRESOLVEDVARIABLE
+   */
+  public int getType()
+  {
+    return CLASS_UNRESOLVEDVARIABLE;
+  }
+  
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return An informational string.
+   */
+  public String getTypeString()
+  {
+    return "XUnresolvedVariable (" + object().getClass().getName() + ")";
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xalan/templates/XUnresolvedVariableSimple.java b/src/main/java/org/apache/xalan/templates/XUnresolvedVariableSimple.java
new file mode 100644
index 0000000..92e8654
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/XUnresolvedVariableSimple.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XUnresolvedVariableSimple.java 468643 2006-10-28 06:56:03Z minchau $
+ */
+package org.apache.xalan.templates;
+
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+
+/**
+ * This is the same as XUnresolvedVariable, but it assumes that the 
+ * context is already set up.  For use with psuedo variables.
+ * Also, it holds an Expression object, instead of an ElemVariable.
+ * It must only hold static context, since a single copy will be 
+ * held in the template.
+ */
+public class XUnresolvedVariableSimple extends XObject
+{
+    static final long serialVersionUID = -1224413807443958985L;
+  public XUnresolvedVariableSimple(ElemVariable obj)
+  {
+    super(obj);
+  }
+    
+	
+  /**
+   * For support of literal objects in xpaths.
+   *
+   * @param xctxt The XPath execution context.
+   *
+   * @return This object.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+  	Expression expr = ((ElemVariable)m_obj).getSelect().getExpression();
+    XObject xobj = expr.execute(xctxt);
+    xobj.allowDetachToRelease(false);
+    return xobj;
+  }
+  
+  /**
+   * Tell what kind of class this is.
+   *
+   * @return CLASS_UNRESOLVEDVARIABLE
+   */
+  public int getType()
+  {
+    return CLASS_UNRESOLVEDVARIABLE;
+  }
+  
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return An informational string.
+   */
+  public String getTypeString()
+  {
+    return "XUnresolvedVariableSimple (" + object().getClass().getName() + ")";
+  }
+
+
+}
+
diff --git a/src/main/java/org/apache/xalan/templates/package.html b/src/main/java/org/apache/xalan/templates/package.html
new file mode 100644
index 0000000..240f6a5
--- /dev/null
+++ b/src/main/java/org/apache/xalan/templates/package.html
@@ -0,0 +1,44 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468643 2006-10-28 06:56:03Z minchau $ -->
+<html>
+  <title>Xalan Templates Package.</title>
+  <body>
+    <p>Implements the {@link javax.xml.transform.Templates} interface, 
+    and defines a set of classes that represent an XSLT stylesheet.</p>
+    
+    <p>{@link org.apache.xalan.templates.StylesheetRoot} implements the {@link javax.xml.transform.Templates} interface, and 
+    extends {@link org.apache.xalan.templates.StylesheetComposed}, 
+    which is a {@link org.apache.xalan.templates.Stylesheet} composed of itself 
+    and its included Stylesheet objects. A StylesheetRoot also contains a 
+    global list of all imported StylesheetComposed objects. The
+    role of these objects is to hold immutable stylesheet data, not to perform 
+    procedural tasks associated with the
+    construction of the data (the org.apache.xalan.processor package) or with the transformation (the org.apache.xalan.transformer
+    package).</p>
+    <p>{@link org.apache.xalan.templates.ElemTemplateElement} is the superclass of 
+    all XSLT instruction elements, including Stylesheet. The <i>x</i>Func classes
+    extend members of the org.apache.xpath package and implement XSLT functions. Unlike 
+    the Stylesheet "container" objects, the
+    instruction element and function classes do contain procedural execute() 
+    methods that are called during the transformation.</p>
+    <p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xalan/transformer/ClonerToResultTree.java b/src/main/java/org/apache/xalan/transformer/ClonerToResultTree.java
new file mode 100644
index 0000000..cc7ea6c
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/ClonerToResultTree.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ClonerToResultTree.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.serialize.SerializerUtils;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xml.utils.XMLString;
+
+/**
+ * Class used to clone a node, possibly including its children to 
+ * a result tree.
+ * @xsl.usage internal
+ */
+public class ClonerToResultTree
+{
+
+//  /**
+//   * Clone an element with or without children.
+//   * TODO: Fix or figure out node clone failure!
+//   * the error condition is severe enough to halt processing.
+//   *
+//   * @param node The node to clone
+//   * @param shouldCloneAttributes Flag indicating whether to 
+//   * clone children attributes
+//   * 
+//   * @throws TransformerException
+//   */
+//  public void cloneToResultTree(int node, boolean shouldCloneAttributes)
+//    throws TransformerException
+//  {
+//
+//    try
+//    {
+//      XPathContext xctxt = m_transformer.getXPathContext();
+//      DTM dtm = xctxt.getDTM(node);
+//
+//      int type = dtm.getNodeType(node);
+//      switch (type)
+//      {
+//      case DTM.TEXT_NODE :
+//        dtm.dispatchCharactersEvents(node, m_rth, false);
+//        break;
+//      case DTM.DOCUMENT_FRAGMENT_NODE :
+//      case DTM.DOCUMENT_NODE :
+//
+//        // Can't clone a document, but refrain from throwing an error
+//        // so that copy-of will work
+//        break;
+//      case DTM.ELEMENT_NODE :
+//        {
+//          Attributes atts;
+//
+//          if (shouldCloneAttributes)
+//          {
+//            m_rth.addAttributes(node);
+//            m_rth.processNSDecls(node, type, dtm);
+//          }
+//
+//          String ns = dtm.getNamespaceURI(node);
+//          String localName = dtm.getLocalName(node);
+//
+//          m_rth.startElement(ns, localName, dtm.getNodeNameX(node), null);
+//        }
+//        break;
+//      case DTM.CDATA_SECTION_NODE :
+//        m_rth.startCDATA();          
+//        dtm.dispatchCharactersEvents(node, m_rth, false);
+//        m_rth.endCDATA();
+//        break;
+//      case DTM.ATTRIBUTE_NODE :
+//        m_rth.addAttribute(node);
+//        break;
+//      case DTM.COMMENT_NODE :
+//        XMLString xstr = dtm.getStringValue (node);
+//        xstr.dispatchAsComment(m_rth);
+//        break;
+//      case DTM.ENTITY_REFERENCE_NODE :
+//        m_rth.entityReference(dtm.getNodeNameX(node));
+//        break;
+//      case DTM.PROCESSING_INSTRUCTION_NODE :
+//        {
+//          // %REVIEW% Is the node name the same as the "target"?
+//          m_rth.processingInstruction(dtm.getNodeNameX(node), 
+//                                      dtm.getNodeValue(node));
+//        }
+//        break;
+//      default :
+//        //"Can not create item in result tree: "+node.getNodeName());
+//        m_transformer.getMsgMgr().error(null, 
+//                         XSLTErrorResources.ER_CANT_CREATE_ITEM,
+//                         new Object[]{ dtm.getNodeName(node) });  
+//      }
+//    }
+//    catch(org.xml.sax.SAXException se)
+//    {
+//      throw new TransformerException(se);
+//    }
+//  }  // end cloneToResultTree function
+  
+  /**
+   * Clone an element with or without children.
+   * TODO: Fix or figure out node clone failure!
+   * the error condition is severe enough to halt processing.
+   *
+   * @param node The node to clone
+   * @param shouldCloneAttributes Flag indicating whether to 
+   * clone children attributes
+   * 
+   * @throws TransformerException
+   */
+  public static void cloneToResultTree(int node, int nodeType, DTM dtm, 
+                                             SerializationHandler rth,
+                                             boolean shouldCloneAttributes)
+    throws TransformerException
+  {
+
+    try
+    {
+      switch (nodeType)
+      {
+      case DTM.TEXT_NODE :
+        dtm.dispatchCharactersEvents(node, rth, false);
+        break;
+      case DTM.DOCUMENT_FRAGMENT_NODE :
+      case DTM.DOCUMENT_NODE :
+        // Can't clone a document, but refrain from throwing an error
+        // so that copy-of will work
+        break;
+      case DTM.ELEMENT_NODE :
+        {
+          // Note: SAX apparently expects "no namespace" to be
+          // represented as "" rather than null.
+          String ns = dtm.getNamespaceURI(node);
+          if (ns==null) ns="";
+          String localName = dtm.getLocalName(node);
+      //  rth.startElement(ns, localName, dtm.getNodeNameX(node), null);
+      //  don't call a real SAX startElement (as commented out above),
+      //  call a SAX-like startElement, to be able to add attributes after this call
+          rth.startElement(ns, localName, dtm.getNodeNameX(node));
+          
+	  // If outputting attrs as separate events, they must
+	  // _follow_ the startElement event. (Think of the
+	  // xsl:attribute directive.)
+          if (shouldCloneAttributes)
+          {
+            SerializerUtils.addAttributes(rth, node);
+            SerializerUtils.processNSDecls(rth, node, nodeType, dtm);
+          }
+        }
+        break;
+      case DTM.CDATA_SECTION_NODE :
+        rth.startCDATA();          
+        dtm.dispatchCharactersEvents(node, rth, false);
+        rth.endCDATA();
+        break;
+      case DTM.ATTRIBUTE_NODE :
+        SerializerUtils.addAttribute(rth, node);
+        break;
+			case DTM.NAMESPACE_NODE:
+				// %REVIEW% Normally, these should have been handled with element.
+				// It's possible that someone may write a stylesheet that tries to
+				// clone them explicitly. If so, we need the equivalent of
+				// rth.addAttribute().
+  			    SerializerUtils.processNSDecls(rth,node,DTM.NAMESPACE_NODE,dtm);
+				break;
+      case DTM.COMMENT_NODE :
+        XMLString xstr = dtm.getStringValue (node);
+        xstr.dispatchAsComment(rth);
+        break;
+      case DTM.ENTITY_REFERENCE_NODE :
+        rth.entityReference(dtm.getNodeNameX(node));
+        break;
+      case DTM.PROCESSING_INSTRUCTION_NODE :
+        {
+          // %REVIEW% Is the node name the same as the "target"?
+          rth.processingInstruction(dtm.getNodeNameX(node), 
+                                      dtm.getNodeValue(node));
+        }
+        break;
+      default :
+        //"Can not create item in result tree: "+node.getNodeName());
+        throw new  TransformerException(
+                         "Can't clone node: "+dtm.getNodeName(node));
+      }
+    }
+    catch(org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+  }  // end cloneToResultTree function
+}
diff --git a/src/main/java/org/apache/xalan/transformer/Counter.java b/src/main/java/org/apache/xalan/transformer/Counter.java
new file mode 100644
index 0000000..a8aabd8
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/Counter.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Counter.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.templates.ElemNumber;
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.XPathContext;
+
+/**
+ * A class that does incremental counting for support of xsl:number.
+ * This class stores a cache of counted nodes (m_countNodes).
+ * It tries to cache the counted nodes in document order...
+ * the node count is based on its position in the cache list
+ * @xsl.usage internal
+ */
+public class Counter
+{
+
+  /**
+   * Set the maximum ammount the m_countNodes list can
+   * grow to.
+   */
+  static final int MAXCOUNTNODES = 500;
+
+  /**
+   * The start count from where m_countNodes counts
+   * from.  In other words, the count of a given node
+   * in the m_countNodes vector is node position +
+   * m_countNodesStartCount.
+   */
+  int m_countNodesStartCount = 0;
+
+  /**
+   * A vector of all nodes counted so far.
+   */
+  NodeSetDTM m_countNodes;
+
+  /**
+   * The node from where the counting starts.  This is needed to
+   * find a counter if the node being counted is not immediatly
+   * found in the m_countNodes vector.
+   */
+  int m_fromNode = DTM.NULL;
+
+  /**
+   * The owning xsl:number element.
+   */
+  ElemNumber m_numberElem;
+
+  /**
+   * Value to store result of last getCount call, for benifit
+   * of returning val from CountersTable.getCounterByCounted,
+   * who calls getCount.
+   */
+  int m_countResult;
+
+  /**
+   * Construct a counter object.
+   *
+   * @param numberElem The owning xsl:number element. 
+   * @param countNodes A vector of all nodes counted so far.
+   *
+   * @throws TransformerException
+   */
+  Counter(ElemNumber numberElem, NodeSetDTM countNodes) throws TransformerException
+  {
+    m_countNodes = countNodes;
+    m_numberElem = numberElem;
+  }
+
+  /**
+   * Construct a counter object.
+   *
+   * @param numberElem The owning xsl:number element. 
+   *
+   * @throws TransformerException
+   *
+  Counter(ElemNumber numberElem) throws TransformerException
+  {
+    m_numberElem = numberElem;
+  }*/
+
+  /**
+   * Try and find a node that was previously counted. If found,
+   * return a positive integer that corresponds to the count.
+   *
+   * @param support The XPath context to use
+   * @param node The node to be counted.
+   * 
+   * @return The count of the node, or -1 if not found.
+   */
+  int getPreviouslyCounted(XPathContext support, int node)
+  {
+
+    int n = m_countNodes.size();
+
+    m_countResult = 0;
+
+    for (int i = n - 1; i >= 0; i--)
+    {
+      int countedNode = m_countNodes.elementAt(i);
+
+      if (node == countedNode)
+      {
+
+        // Since the list is in backwards order, the count is 
+        // how many are in the rest of the list.
+        m_countResult = i + 1 + m_countNodesStartCount;
+
+        break;
+      }
+      
+      DTM dtm = support.getDTM(countedNode);
+
+      // Try to see if the given node falls after the counted node...
+      // if it does, don't keep searching backwards.
+      if (dtm.isNodeAfter(countedNode, node))
+        break;
+    }
+
+    return m_countResult;
+  }
+
+  /**
+   * Get the last node in the list.
+   *
+   * @return the last node in the list.
+   */
+  int getLast()
+  {
+
+    int size = m_countNodes.size();
+
+    return (size > 0) ? m_countNodes.elementAt(size - 1) : DTM.NULL;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/transformer/CountersTable.java b/src/main/java/org/apache/xalan/transformer/CountersTable.java
new file mode 100644
index 0000000..258fdb1
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/CountersTable.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: CountersTable.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.templates.ElemNumber;
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.XPathContext;
+
+/**
+ * This is a table of counters, keyed by ElemNumber objects, each
+ * of which has a list of Counter objects.  This really isn't a true
+ * table, it is more like a list of lists (there must be a technical
+ * term for that...).
+ * @xsl.usage internal
+ */
+public class CountersTable extends Hashtable
+{
+    static final long serialVersionUID = 2159100770924179875L;
+
+  /**
+   * Construct a CountersTable.
+   */
+  public CountersTable(){}
+
+  /**
+   * Get the list of counters that corresponds to
+   * the given ElemNumber object.
+   *
+   * @param numberElem the given xsl:number element.
+   *
+   * @return the list of counters that corresponds to
+   * the given ElemNumber object.
+   */
+  Vector getCounters(ElemNumber numberElem)
+  {
+
+    Vector counters = (Vector) this.get(numberElem);
+
+    return (null == counters) ? putElemNumber(numberElem) : counters;
+  }
+
+  /**
+   * Put a counter into the table and create an empty
+   * vector as it's value.
+   *
+   * @param numberElem the given xsl:number element.
+   *
+   * @return an empty vector to be used to store counts
+   * for this number element.
+   */
+  Vector putElemNumber(ElemNumber numberElem)
+  {
+
+    Vector counters = new Vector();
+
+    this.put(numberElem, counters);
+
+    return counters;
+  }
+
+  /**
+   * Place to collect new counters.
+   */
+  transient private NodeSetDTM m_newFound;
+
+  /**
+   * Add a list of counted nodes that were built in backwards document
+   * order, or a list of counted nodes that are in forwards document
+   * order.
+   *
+   * @param flist Vector of nodes built in forwards document order
+   * @param blist Vector of nodes built in backwards document order
+   */
+  void appendBtoFList(NodeSetDTM flist, NodeSetDTM blist)
+  {
+
+    int n = blist.size();
+
+    for (int i = (n - 1); i >= 0; i--)
+    {
+      flist.addElement(blist.item(i));
+    }
+  }
+
+  // For diagnostics
+
+  /** Number of counters created so far          */
+  transient int m_countersMade = 0;
+
+  /**
+   * Count forward until the given node is found, or until
+   * we have looked to the given amount.
+   *
+   * @param support The XPath context to use  
+   * @param numberElem The given xsl:number element.
+   * @param node The node to count.
+   * 
+   * @return The node count, or 0 if not found.
+   *
+   * @throws TransformerException
+   */
+  public int countNode(XPathContext support, ElemNumber numberElem, int node)
+          throws TransformerException
+  {
+
+    int count = 0;
+    Vector counters = getCounters(numberElem);
+    int nCounters = counters.size();
+
+    // XPath countMatchPattern = numberElem.getCountMatchPattern(support, node);
+    // XPath fromMatchPattern = numberElem.m_fromMatchPattern;
+    int target = numberElem.getTargetNode(support, node);
+
+    if (DTM.NULL != target)
+    {
+      for (int i = 0; i < nCounters; i++)
+      {
+        Counter counter = (Counter) counters.elementAt(i);
+
+        count = counter.getPreviouslyCounted(support, target);
+
+        if (count > 0)
+          return count;
+      }
+
+      // In the loop below, we collect the nodes in backwards doc order, so 
+      // we don't have to do inserts, but then we store the nodes in forwards 
+      // document order, so we don't have to insert nodes into that list, 
+      // so that's what the appendBtoFList stuff is all about.  In cases 
+      // of forward counting by one, this will mean a single node copy from 
+      // the backwards list (m_newFound) to the forwards list (counter.m_countNodes).
+      count = 0;
+      if (m_newFound == null)
+        m_newFound = new NodeSetDTM(support.getDTMManager());
+
+      for (; DTM.NULL != target;
+              target = numberElem.getPreviousNode(support, target))
+      {
+
+        // First time in, we should not have to check for previous counts, 
+        // since the original target node was already checked in the 
+        // block above.
+        if (0 != count)
+        {
+          for (int i = 0; i < nCounters; i++)
+          {
+            Counter counter = (Counter) counters.elementAt(i);
+            int cacheLen = counter.m_countNodes.size();
+
+            if ((cacheLen > 0)
+                    && (counter.m_countNodes.elementAt(cacheLen
+                                                      - 1) == target))
+            {
+              count += (cacheLen + counter.m_countNodesStartCount);
+
+              if (cacheLen > 0)
+                appendBtoFList(counter.m_countNodes, m_newFound);
+
+              m_newFound.removeAllElements();
+
+              return count;
+            }
+          }
+        }
+
+        m_newFound.addElement(target);
+
+        count++;
+      }
+
+      // If we got to this point, then we didn't find a counter, so make 
+      // one and add it to the list.
+      Counter counter = new Counter(numberElem, new NodeSetDTM(support.getDTMManager()));
+
+      m_countersMade++;  // for diagnostics
+
+      appendBtoFList(counter.m_countNodes, m_newFound);
+      m_newFound.removeAllElements();
+      counters.addElement(counter);
+    }
+
+    return count;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/transformer/DecimalToRoman.java b/src/main/java/org/apache/xalan/transformer/DecimalToRoman.java
new file mode 100644
index 0000000..3a600d0
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/DecimalToRoman.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DecimalToRoman.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+/**
+ * Structure to help in converting integers to roman numerals
+ * @xsl.usage internal
+ */
+public class DecimalToRoman
+{
+
+  /**
+   * Constructor DecimalToRoman
+   *
+   *
+   * @param postValue Minimum value for a given range of 
+   * roman numbers
+   * @param postLetter Correspoding letter (roman) to postValue
+   * @param preValue Value of last prefixed number within 
+   * that same range (i.e. IV if postval is 5 (V))
+   * @param preLetter Correspoding letter(roman) to preValue
+   */
+  public DecimalToRoman(long postValue, String postLetter, long preValue,
+                        String preLetter)
+  {
+
+    this.m_postValue = postValue;
+    this.m_postLetter = postLetter;
+    this.m_preValue = preValue;
+    this.m_preLetter = preLetter;
+  }
+
+  /** Minimum value for a given range of roman numbers          */
+  public long m_postValue;
+
+  /** Correspoding letter (roman) to m_postValue          */
+  public String m_postLetter;
+
+  /** Value of last prefixed number within that same range  */
+  public long m_preValue;
+
+  /** Correspoding letter (roman) to m_preValue          */
+  public String m_preLetter;
+}
diff --git a/src/main/java/org/apache/xalan/transformer/KeyIterator.java b/src/main/java/org/apache/xalan/transformer/KeyIterator.java
new file mode 100644
index 0000000..588f0eb
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/KeyIterator.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: KeyIterator.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.KeyDeclaration;
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+import org.apache.xpath.axes.OneStepIteratorForward;
+
+/**
+ * This class implements an optimized iterator for 
+ * "key()" patterns, matching each node to the 
+ * match attribute in one or more xsl:key declarations.
+ * @xsl.usage internal
+ */
+public class KeyIterator extends OneStepIteratorForward
+{
+    static final long serialVersionUID = -1349109910100249661L;
+
+  /** Key name.
+   *  @serial           */
+  private QName m_name;
+
+  /**
+   * Get the key name from a key declaration this iterator will process
+   *
+   *
+   * @return Key name
+   */
+  public QName getName()
+  {
+    return m_name;
+  }
+
+  /** Vector of Key declarations in the stylesheet.
+   *  @serial          */
+  private Vector m_keyDeclarations;
+
+  /**
+   * Get the key declarations from the stylesheet 
+   *
+   *
+   * @return Vector containing the key declarations from the stylesheet
+   */
+  public Vector getKeyDeclarations()
+  {
+    return m_keyDeclarations;
+  }
+
+  /**
+    * Create a KeyIterator object.
+    *
+    * @throws javax.xml.transform.TransformerException
+    */
+  KeyIterator(QName name, Vector keyDeclarations)
+  {
+    super(Axis.ALL);
+    m_keyDeclarations = keyDeclarations;
+    // m_prefixResolver = nscontext;
+    m_name = name;
+  }
+
+  /**
+   *  Test whether a specified node is visible in the logical view of a
+   * TreeWalker or NodeIterator. This function will be called by the
+   * implementation of TreeWalker and NodeIterator; it is not intended to
+   * be called directly from user code.
+   * 
+   * @param testNode  The node to check to see if it passes the filter or not.
+   *
+   * @return  a constant to determine whether the node is accepted,
+   *   rejected, or skipped, as defined  above .
+   */
+  public short acceptNode(int testNode)
+  {
+    boolean foundKey = false;
+    KeyIterator ki = (KeyIterator) m_lpi;
+    org.apache.xpath.XPathContext xctxt = ki.getXPathContext();
+    Vector keys = ki.getKeyDeclarations();
+
+    QName name = ki.getName();
+    try
+    {
+      // System.out.println("lookupKey: "+lookupKey);
+      int nDeclarations = keys.size();
+
+      // Walk through each of the declarations made with xsl:key
+      for (int i = 0; i < nDeclarations; i++)
+      {
+        KeyDeclaration kd = (KeyDeclaration) keys.elementAt(i);
+
+        // Only continue if the name on this key declaration
+        // matches the name on the iterator for this walker. 
+        if (!kd.getName().equals(name))
+          continue;
+
+        foundKey = true;
+        // xctxt.setNamespaceContext(ki.getPrefixResolver());
+
+        // See if our node matches the given key declaration according to 
+        // the match attribute on xsl:key.
+        XPath matchExpr = kd.getMatch();
+        double score = matchExpr.getMatchScore(xctxt, testNode);
+
+        if (score == kd.getMatch().MATCH_SCORE_NONE)
+          continue;
+
+        return DTMIterator.FILTER_ACCEPT;
+
+      } // end for(int i = 0; i < nDeclarations; i++)
+    }
+    catch (TransformerException se)
+    {
+
+      // TODO: What to do?
+    }
+
+    if (!foundKey)
+      throw new RuntimeException(
+        XSLMessages.createMessage(
+          XSLTErrorResources.ER_NO_XSLKEY_DECLARATION,
+          new Object[] { name.getLocalName()}));
+          
+    return DTMIterator.FILTER_REJECT;
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/transformer/KeyManager.java b/src/main/java/org/apache/xalan/transformer/KeyManager.java
new file mode 100644
index 0000000..884783c
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/KeyManager.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: KeyManager.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.util.Vector;
+
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNodeSet;
+
+/**
+ * This class manages the key tables.
+ */
+public class KeyManager
+{
+
+  /**
+   * Table of tables of element keys.
+   * @see org.apache.xalan.transformer.KeyTable
+   */
+  private transient Vector m_key_tables = null;
+
+  /**
+   * Given a valid element key, return the corresponding node list.
+   *
+   * @param xctxt The XPath runtime state
+   * @param doc The document node
+   * @param name The key element name
+   * @param ref The key value we're looking for 
+   * @param nscontext The prefix resolver for the execution context
+   *
+   * @return A nodelist of nodes mathing the given key
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XNodeSet getNodeSetDTMByKey(
+          XPathContext xctxt, int doc, QName name, XMLString ref, PrefixResolver nscontext)
+            throws javax.xml.transform.TransformerException
+  {
+
+    XNodeSet nl = null;
+    ElemTemplateElement template = (ElemTemplateElement) nscontext;  // yuck -sb
+
+    if ((null != template)
+            && null != template.getStylesheetRoot().getKeysComposed())
+    {
+      boolean foundDoc = false;
+
+      if (null == m_key_tables)
+      {
+        m_key_tables = new Vector(4);
+      }
+      else
+      {
+        int nKeyTables = m_key_tables.size();
+
+        for (int i = 0; i < nKeyTables; i++)
+        {
+          KeyTable kt = (KeyTable) m_key_tables.elementAt(i);
+
+          if (kt.getKeyTableName().equals(name) && doc == kt.getDocKey())
+          {
+            nl = kt.getNodeSetDTMByKey(name, ref);
+
+            if (nl != null)
+            {
+              foundDoc = true;
+
+              break;
+            }
+          }
+        }
+      }
+
+      if ((null == nl) &&!foundDoc /* && m_needToBuildKeysTable */)
+      {
+        KeyTable kt =
+          new KeyTable(doc, nscontext, name,
+                       template.getStylesheetRoot().getKeysComposed(),
+                       xctxt);
+
+        m_key_tables.addElement(kt);
+
+        if (doc == kt.getDocKey())
+        {
+          foundDoc = true;
+          nl = kt.getNodeSetDTMByKey(name, ref);
+        }
+      }
+    }
+
+    return nl;
+  }
+}
diff --git a/src/main/java/org/apache/xalan/transformer/KeyTable.java b/src/main/java/org/apache/xalan/transformer/KeyTable.java
new file mode 100644
index 0000000..fcfa366
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/KeyTable.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: KeyTable.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.templates.KeyDeclaration;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.WrappedRuntimeException;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Table of element keys, keyed by document node.  An instance of this
+ * class is keyed by a Document node that should be matched with the
+ * root of the current context.
+ * @xsl.usage advanced
+ */
+public class KeyTable
+{
+  /**
+   * The document key.  This table should only be used with contexts
+   * whose Document roots match this key.
+   */
+  private int m_docKey;
+
+  /**
+   * Vector of KeyDeclaration instances holding the key declarations.
+   */
+  private Vector m_keyDeclarations;
+
+  /**
+   * Hold a cache of key() function result for each ref.
+   * Key is XMLString, the ref value
+   * Value is XNodeSet, the key() function result for the given ref value.
+   */
+  private Hashtable m_refsTable = null;
+
+  /**
+   * Get the document root matching this key.  
+   *
+   * @return the document root matching this key
+   */
+  public int getDocKey()
+  {
+    return m_docKey;
+  }
+
+  /** 
+   * The main iterator that will walk through the source  
+   * tree for this key.
+   */
+  private XNodeSet m_keyNodes;
+  
+  KeyIterator getKeyIterator()
+  {
+  	return (KeyIterator)(m_keyNodes.getContainedIter());
+  }
+
+  /**
+   * Build a keys table.
+   * @param doc The owner document key.
+   * @param nscontext The stylesheet's namespace context.
+   * @param name The key name
+   * @param keyDeclarations The stylesheet's xsl:key declarations.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public KeyTable(
+          int doc, PrefixResolver nscontext, QName name, Vector keyDeclarations, XPathContext xctxt)
+            throws javax.xml.transform.TransformerException
+  {
+    m_docKey = doc;
+    m_keyDeclarations = keyDeclarations;
+    KeyIterator ki = new KeyIterator(name, keyDeclarations);
+
+    m_keyNodes = new XNodeSet(ki);
+    m_keyNodes.allowDetachToRelease(false);
+    m_keyNodes.setRoot(doc, xctxt);
+  }
+
+  /**
+   * Given a valid element key, return the corresponding node list.
+   * 
+   * @param name The name of the key, which must match the 'name' attribute on xsl:key.
+   * @param ref The value that must match the value found by the 'match' attribute on xsl:key.
+   * @return a set of nodes referenced by the key named <CODE>name</CODE> and the reference <CODE>ref</CODE>. If no node is referenced by this key, an empty node set is returned.
+   */
+  public XNodeSet getNodeSetDTMByKey(QName name, XMLString ref)
+
+  {
+    XNodeSet refNodes = (XNodeSet) getRefsTable().get(ref);
+    // clone wiht reset the node set
+   try
+    {
+      if (refNodes != null)
+      {
+         refNodes = (XNodeSet) refNodes.cloneWithReset();
+       }
+    }
+    catch (CloneNotSupportedException e)
+    {
+      refNodes = null;
+    }
+
+    if (refNodes == null) {
+     //  create an empty XNodeSet
+      KeyIterator ki = (KeyIterator) (m_keyNodes).getContainedIter();
+      XPathContext xctxt = ki.getXPathContext();
+      refNodes = new XNodeSet(xctxt.getDTMManager()) {
+        public void setRoot(int nodeHandle, Object environment) {
+          // Root cannot be set on non-iterated node sets. Ignore it.
+        }
+      };
+      refNodes.reset();
+    }
+
+    return refNodes;
+  }
+
+  /**
+   * Get Key Name for this KeyTable  
+   *
+   * @return Key name
+   */
+  public QName getKeyTableName()
+  {
+    return getKeyIterator().getName();
+  }
+
+  /**
+   * @return key declarations for the key associated to this KeyTable
+   */
+  private Vector getKeyDeclarations() {
+    int nDeclarations = m_keyDeclarations.size();
+    Vector keyDecls = new Vector(nDeclarations);
+
+    // Walk through each of the declarations made with xsl:key
+    for (int i = 0; i < nDeclarations; i++)
+    {
+      KeyDeclaration kd = (KeyDeclaration) m_keyDeclarations.elementAt(i);
+
+      // Add the declaration if the name on this key declaration
+      // matches the name on the iterator for this walker.
+      if (kd.getName().equals(getKeyTableName())) {
+        keyDecls.add(kd);
+      }
+    }
+
+    return keyDecls;
+  }
+
+  /**
+   * @return lazy initialized refs table associating evaluation of key function
+   *         with a XNodeSet
+   */
+  private Hashtable getRefsTable()
+  {
+    if (m_refsTable == null) {
+      // initial capacity set to a prime number to improve hash performance
+      m_refsTable = new Hashtable(89);
+
+      KeyIterator ki = (KeyIterator) (m_keyNodes).getContainedIter();
+      XPathContext xctxt = ki.getXPathContext();
+
+      Vector keyDecls = getKeyDeclarations();
+      int nKeyDecls = keyDecls.size();
+
+      int currentNode;
+      m_keyNodes.reset();
+      while (DTM.NULL != (currentNode = m_keyNodes.nextNode()))
+      {
+        try
+        {
+          for (int keyDeclIdx = 0; keyDeclIdx < nKeyDecls; keyDeclIdx++) {
+            KeyDeclaration keyDeclaration =
+                (KeyDeclaration) keyDecls.elementAt(keyDeclIdx);
+            XObject xuse =
+                keyDeclaration.getUse().execute(xctxt,
+                                                currentNode,
+                                                ki.getPrefixResolver());
+
+            if (xuse.getType() != xuse.CLASS_NODESET) {
+              XMLString exprResult = xuse.xstr();
+              addValueInRefsTable(xctxt, exprResult, currentNode);
+            } else {
+              DTMIterator i = ((XNodeSet)xuse).iterRaw();
+              int currentNodeInUseClause;
+
+              while (DTM.NULL != (currentNodeInUseClause = i.nextNode())) {
+                DTM dtm = xctxt.getDTM(currentNodeInUseClause);
+                XMLString exprResult =
+                    dtm.getStringValue(currentNodeInUseClause);
+                addValueInRefsTable(xctxt, exprResult, currentNode);
+              }
+            }
+          }
+        } catch (TransformerException te) {
+          throw new WrappedRuntimeException(te);
+        }
+      }
+    }
+    return m_refsTable;
+  }
+
+  /**
+   * Add an association between a ref and a node in the m_refsTable.
+   * Requires that m_refsTable != null
+   * @param xctxt XPath context
+   * @param ref the value of the use clause of the current key for the given node
+   * @param node the node to reference
+   */
+  private void addValueInRefsTable(XPathContext xctxt, XMLString ref, int node) {
+    
+    XNodeSet nodes = (XNodeSet) m_refsTable.get(ref);
+    if (nodes == null)
+    {
+      nodes = new XNodeSet(node, xctxt.getDTMManager());
+      nodes.nextNode();
+      m_refsTable.put(ref, nodes);
+    }
+    else
+    {
+      // Nodes are passed to this method in document order.  Since we need to
+      // suppress duplicates, we only need to check against the last entry
+      // in each nodeset.  We use nodes.nextNode after each entry so we can
+      // easily compare node against the current node.
+      if (nodes.getCurrentNode() != node) {
+          nodes.mutableNodeset().addNode(node);
+          nodes.nextNode();
+      }    
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/transformer/MsgMgr.java b/src/main/java/org/apache/xalan/transformer/MsgMgr.java
new file mode 100644
index 0000000..7f42c29
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/MsgMgr.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: MsgMgr.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+
+import org.w3c.dom.Node;
+
+/**
+ * This class will manage error messages, warning messages, and other types of
+ * message events.
+ */
+public class MsgMgr
+{
+
+  /**
+   * Create a message manager object.
+   *
+   * @param transformer non transformer instance
+   */
+  public MsgMgr(TransformerImpl transformer)
+  {
+    m_transformer = transformer;
+  }
+
+  /** Transformer instance          */
+  private TransformerImpl m_transformer;
+
+  /**
+   * Warn the user of a problem.
+   * This is public for access by extensions.
+   *
+   * @param msg The message text to issue
+   * @param terminate Flag indicating whether to terminate this process
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   */
+  public void message(SourceLocator srcLctr, String msg, boolean terminate) throws TransformerException
+  {
+
+    ErrorListener errHandler = m_transformer.getErrorListener();
+
+    if (null != errHandler)
+    {
+      errHandler.warning(new TransformerException(msg, srcLctr));
+    }
+    else
+    {
+      if (terminate)
+        throw new TransformerException(msg, srcLctr);
+      else
+        System.out.println(msg);
+    }
+  }
+
+  /**
+   * Warn the user of a problem.
+   *
+   * @param msg Message text to issue
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void warn(SourceLocator srcLctr, String msg) throws TransformerException
+  {
+    warn(srcLctr, null, null, msg, null);
+  }
+
+  /**
+   * Warn the user of a problem.
+   *
+   * @param msg Message text to issue
+   * @param args Arguments to pass to the message
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void warn(SourceLocator srcLctr, String msg, Object[] args) throws TransformerException
+  {
+    warn(srcLctr, null, null, msg, args);
+  }
+
+  /**
+   * Warn the user of a problem.
+   *
+   * 
+   * @param styleNode Stylesheet node
+   * @param sourceNode Source tree node
+   * @param msg Message text to issue
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void warn(SourceLocator srcLctr, Node styleNode, Node sourceNode, String msg)
+          throws TransformerException
+  {
+    warn(srcLctr, styleNode, sourceNode, msg, null);
+  }
+
+  /**
+   * Warn the user of a problem.
+   *
+   * @param styleNode Stylesheet node
+   * @param sourceNode Source tree node
+   * @param msg Message text to issue
+   * @param args Arguments to pass to the message
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void warn(SourceLocator srcLctr, Node styleNode, Node sourceNode, String msg, Object args[])
+          throws TransformerException
+  {
+
+    String formattedMsg = XSLMessages.createWarning(msg, args);
+    ErrorListener errHandler = m_transformer.getErrorListener();
+
+    if (null != errHandler)
+      errHandler.warning(new TransformerException(formattedMsg, srcLctr));
+    else
+      System.out.println(formattedMsg);
+  }
+
+  /* This method is not properly i18nized. We need to use the following method
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg Message text to issue
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   *
+  public void error(SourceLocator srcLctr, String msg) throws TransformerException
+  {
+
+    // Locator locator = m_stylesheetLocatorStack.isEmpty()
+    //                  ? null :
+    //                    ((Locator)m_stylesheetLocatorStack.peek());
+    // Locator locator = null;
+    ErrorListener errHandler = m_transformer.getErrorListener();
+
+    if (null != errHandler)
+      errHandler.fatalError(new TransformerException(msg, srcLctr));
+    else
+      throw new TransformerException(msg, srcLctr);
+  }
+
+ * @xsl.usage internal
+ */
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg Message text to issue
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void error(SourceLocator srcLctr, String msg) throws TransformerException
+  {
+    error(srcLctr, null, null, msg, null);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg Message text to issue
+   * @param args Arguments to be passed to the message 
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void error(SourceLocator srcLctr, String msg, Object[] args) throws TransformerException
+  {
+    error(srcLctr, null, null, msg, args);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg Message text to issue
+   * @param e Exception to throw
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void error(SourceLocator srcLctr, String msg, Exception e) throws TransformerException
+  {
+    error(srcLctr, msg, null, e);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg Message text to issue
+   * @param args Arguments to use in message
+   * @param e Exception to throw
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void error(SourceLocator srcLctr, String msg, Object args[], Exception e) throws TransformerException
+  {
+
+    //msg  = (null == msg) ? XSLTErrorResources.ER_PROCESSOR_ERROR : msg;
+    String formattedMsg = XSLMessages.createMessage(msg, args);
+
+    // Locator locator = m_stylesheetLocatorStack.isEmpty()
+    //                   ? null :
+    //                    ((Locator)m_stylesheetLocatorStack.peek());
+    // Locator locator = null;
+    ErrorListener errHandler = m_transformer.getErrorListener();
+
+    if (null != errHandler)
+      errHandler.fatalError(new TransformerException(formattedMsg, srcLctr));
+    else
+      throw new TransformerException(formattedMsg, srcLctr);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param styleNode Stylesheet node
+   * @param sourceNode Source tree node
+   * @param msg Message text to issue
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void error(SourceLocator srcLctr, Node styleNode, Node sourceNode, String msg)
+          throws TransformerException
+  {
+    error(srcLctr, styleNode, sourceNode, msg, null);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param styleNode Stylesheet node
+   * @param sourceNode Source tree node
+   * @param msg Message text to issue
+   * @param args Arguments to use in message
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws TransformerException
+   * @xsl.usage internal
+   */
+  public void error(SourceLocator srcLctr, Node styleNode, Node sourceNode, String msg, Object args[])
+          throws TransformerException
+  {
+
+    String formattedMsg = XSLMessages.createMessage(msg, args);
+
+    // Locator locator = m_stylesheetLocatorStack.isEmpty()
+    //                   ? null :
+    //                    ((Locator)m_stylesheetLocatorStack.peek());
+    // Locator locator = null;
+    ErrorListener errHandler = m_transformer.getErrorListener();
+
+    if (null != errHandler)
+      errHandler.fatalError(new TransformerException(formattedMsg, srcLctr));
+    else
+      throw new TransformerException(formattedMsg, srcLctr);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/transformer/NodeSortKey.java b/src/main/java/org/apache/xalan/transformer/NodeSortKey.java
new file mode 100644
index 0000000..91a1fd5
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/NodeSortKey.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeSortKey.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.text.Collator;
+import java.util.Locale;
+
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xpath.XPath;
+
+/**
+ * Data structure for use by the NodeSorter class.
+ * @xsl.usage internal
+ */
+class NodeSortKey
+{
+
+  /** Select pattern for this sort key          */
+  XPath m_selectPat;
+
+  /** Flag indicating whether to treat thee result as a number     */
+  boolean m_treatAsNumbers;
+
+  /** Flag indicating whether to sort in descending order      */
+  boolean m_descending;
+
+  /** Flag indicating by case          */
+  boolean m_caseOrderUpper;
+
+  /** Collator instance          */
+  Collator m_col;
+
+  /** Locale we're in          */
+  Locale m_locale;
+
+  /** Prefix resolver to use          */
+  org.apache.xml.utils.PrefixResolver m_namespaceContext;
+
+  /** Transformer instance          */
+  TransformerImpl m_processor;  // needed for error reporting.
+
+  /**
+   * Constructor NodeSortKey
+   *
+   *
+   * @param transformer non null transformer instance
+   * @param selectPat Select pattern for this key 
+   * @param treatAsNumbers Flag indicating whether the result will be a number
+   * @param descending Flag indicating whether to sort in descending order
+   * @param langValue Lang value to use to get locale
+   * @param caseOrderUpper Flag indicating whether case is relevant
+   * @param namespaceContext Prefix resolver
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  NodeSortKey(
+          TransformerImpl transformer, XPath selectPat, boolean treatAsNumbers, 
+          boolean descending, String langValue, boolean caseOrderUpper, 
+          org.apache.xml.utils.PrefixResolver namespaceContext)
+            throws javax.xml.transform.TransformerException
+  {
+
+    m_processor = transformer;
+    m_namespaceContext = namespaceContext;
+    m_selectPat = selectPat;
+    m_treatAsNumbers = treatAsNumbers;
+    m_descending = descending;
+    m_caseOrderUpper = caseOrderUpper;
+
+    if (null != langValue && m_treatAsNumbers == false)
+    {
+      // See http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2851
+      // The constructor of Locale is defined as 
+      //   public Locale(String language, String country)
+      // with
+      //   language - lowercase two-letter ISO-639 code
+      //   country - uppercase two-letter ISO-3166 code
+      // a) language must be provided as a lower-case ISO-code 
+      //    instead of an upper-case code
+      // b) country must be provided as an ISO-code 
+      //    instead of a full localized country name (e.g. "France")
+      m_locale = new Locale(langValue.toLowerCase(), 
+                  Locale.getDefault().getCountry());
+                  
+      // (old, before bug report 2851).
+      //  m_locale = new Locale(langValue.toUpperCase(),
+      //                        Locale.getDefault().getDisplayCountry());                    
+
+      if (null == m_locale)
+      {
+
+        // m_processor.warn("Could not find locale for <sort xml:lang="+langValue);
+        m_locale = Locale.getDefault();
+      }
+    }
+    else
+    {
+      m_locale = Locale.getDefault();
+    }
+
+    m_col = Collator.getInstance(m_locale);
+
+    if (null == m_col)
+    {
+      m_processor.getMsgMgr().warn(null, XSLTErrorResources.WG_CANNOT_FIND_COLLATOR,
+                                   new Object[]{ langValue });  //"Could not find Collator for <sort xml:lang="+langValue);
+
+      m_col = Collator.getInstance();
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/transformer/NodeSorter.java b/src/main/java/org/apache/xalan/transformer/NodeSorter.java
new file mode 100644
index 0000000..d7977b2
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/NodeSorter.java
@@ -0,0 +1,557 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeSorter.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.text.CollationKey;
+import java.util.Vector;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * This class can sort vectors of DOM nodes according to a select pattern.
+ * @xsl.usage internal
+ */
+public class NodeSorter
+{
+
+  /** Current XPath context           */
+  XPathContext m_execContext;
+
+  /** Vector of NodeSortKeys          */
+  Vector m_keys;  // vector of NodeSortKeys
+
+//  /**
+//   * TODO: Adjust this for locale.
+//   */
+//  NumberFormat m_formatter = NumberFormat.getNumberInstance();
+
+  /**
+   * Construct a NodeSorter, passing in the XSL TransformerFactory
+   * so it can know how to get the node data according to
+   * the proper whitespace rules.
+   *
+   * @param p Xpath context to use
+   */
+  public NodeSorter(XPathContext p)
+  {
+    m_execContext = p;
+  }
+
+  /**
+   * Given a vector of nodes, sort each node according to
+   * the criteria in the keys.
+   * @param v an vector of Nodes.
+   * @param keys a vector of NodeSortKeys.
+   * @param support XPath context to use
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void sort(DTMIterator v, Vector keys, XPathContext support)
+          throws javax.xml.transform.TransformerException
+  {
+
+    m_keys = keys;
+
+    // QuickSort2(v, 0, v.size() - 1 );
+    int n = v.getLength();
+
+    // %OPT% Change mergesort to just take a DTMIterator?
+    // We would also have to adapt DTMIterator to have the function 
+    // of NodeCompareElem.
+    
+    // Create a vector of node compare elements
+    // based on the input vector of nodes
+    Vector nodes = new Vector();
+
+    for (int i = 0; i < n; i++)
+    {
+      NodeCompareElem elem = new NodeCompareElem(v.item(i));
+
+      nodes.addElement(elem);
+    }
+
+    Vector scratchVector = new Vector();
+
+    mergesort(nodes, scratchVector, 0, n - 1, support);
+
+    // return sorted vector of nodes
+    for (int i = 0; i < n; i++)
+    {
+      v.setItem(((NodeCompareElem) nodes.elementAt(i)).m_node, i);
+    }
+    v.setCurrentPos(0);
+
+    // old code...
+    //NodeVector scratchVector = new NodeVector(n);
+    //mergesort(v, scratchVector, 0, n - 1, support);
+  }
+
+  /**
+   * Return the results of a compare of two nodes.
+   * TODO: Optimize compare -- cache the getStringExpr results, key by m_selectPat + hash of node.
+   *
+   * @param n1 First node to use in compare
+   * @param n2 Second node to use in compare
+   * @param kIndex Index of NodeSortKey to use for sort
+   * @param support XPath context to use
+   *
+   * @return The results of the compare of the two nodes.
+   *
+   * @throws TransformerException
+   */
+  int compare(
+          NodeCompareElem n1, NodeCompareElem n2, int kIndex, XPathContext support)
+            throws TransformerException
+  {
+
+    int result = 0;
+    NodeSortKey k = (NodeSortKey) m_keys.elementAt(kIndex);
+
+    if (k.m_treatAsNumbers)
+    {
+      double n1Num, n2Num;
+
+      if (kIndex == 0)
+      {
+        n1Num = ((Double) n1.m_key1Value).doubleValue();
+        n2Num = ((Double) n2.m_key1Value).doubleValue();
+      }
+      else if (kIndex == 1)
+      {
+        n1Num = ((Double) n1.m_key2Value).doubleValue();
+        n2Num = ((Double) n2.m_key2Value).doubleValue();
+      }
+
+      /* Leave this in case we decide to use an array later
+      if (kIndex < maxkey)
+      {
+      double n1Num = (double)n1.m_keyValue[kIndex];
+      double n2Num = (double)n2.m_keyValue[kIndex];
+      }*/
+      else
+      {
+
+        // Get values dynamically
+        XObject r1 = k.m_selectPat.execute(m_execContext, n1.m_node,
+                                           k.m_namespaceContext);
+        XObject r2 = k.m_selectPat.execute(m_execContext, n2.m_node,
+                                           k.m_namespaceContext);
+        n1Num = r1.num();
+
+        // Can't use NaN for compare. They are never equal. Use zero instead.
+        // That way we can keep elements in document order. 
+        //n1Num = Double.isNaN(d) ? 0.0 : d;
+        n2Num = r2.num();
+        //n2Num = Double.isNaN(d) ? 0.0 : d;
+      }
+
+      if ((n1Num == n2Num) && ((kIndex + 1) < m_keys.size()))
+      {
+        result = compare(n1, n2, kIndex + 1, support);
+      }
+      else
+      {
+        double diff;
+        if (Double.isNaN(n1Num))
+        {
+          if (Double.isNaN(n2Num))
+            diff = 0.0;
+          else
+            diff = -1;
+        }
+        else if (Double.isNaN(n2Num))
+           diff = 1;
+        else
+          diff = n1Num - n2Num;
+
+        // process order parameter 
+        result = (int) ((diff < 0.0)
+                        ? (k.m_descending ? 1 : -1)
+                        : (diff > 0.0) ? (k.m_descending ? -1 : 1) : 0);
+      }
+    }  // end treat as numbers 
+    else
+    {
+      CollationKey n1String, n2String;
+
+      if (kIndex == 0)
+      {
+        n1String = (CollationKey) n1.m_key1Value;
+        n2String = (CollationKey) n2.m_key1Value;
+      }
+      else if (kIndex == 1)
+      {
+        n1String = (CollationKey) n1.m_key2Value;
+        n2String = (CollationKey) n2.m_key2Value;
+      }
+
+      /* Leave this in case we decide to use an array later
+      if (kIndex < maxkey)
+      {
+        String n1String = (String)n1.m_keyValue[kIndex];
+        String n2String = (String)n2.m_keyValue[kIndex];
+      }*/
+      else
+      {
+
+        // Get values dynamically
+        XObject r1 = k.m_selectPat.execute(m_execContext, n1.m_node,
+                                           k.m_namespaceContext);
+        XObject r2 = k.m_selectPat.execute(m_execContext, n2.m_node,
+                                           k.m_namespaceContext);
+
+        n1String = k.m_col.getCollationKey(r1.str());
+        n2String = k.m_col.getCollationKey(r2.str());
+      }
+
+      // Use collation keys for faster compare, but note that whitespaces 
+      // etc... are treated differently from if we were comparing Strings.
+      result = n1String.compareTo(n2String);
+
+      //Process caseOrder parameter
+      if (k.m_caseOrderUpper)
+      {
+        String tempN1 = n1String.getSourceString().toLowerCase();
+        String tempN2 = n2String.getSourceString().toLowerCase();
+
+        if (tempN1.equals(tempN2))
+        {
+
+          //java defaults to upper case is greater.
+          result = result == 0 ? 0 : -result;
+        }
+      }
+
+      //Process order parameter
+      if (k.m_descending)
+      {
+        result = -result;
+      }
+    }  //end else
+
+    if (0 == result)
+    {
+      if ((kIndex + 1) < m_keys.size())
+      {
+        result = compare(n1, n2, kIndex + 1, support);
+      }
+    }
+
+    if (0 == result)
+    {
+
+      // I shouldn't have to do this except that there seems to 
+      // be a glitch in the mergesort
+      // if(r1.getType() == r1.CLASS_NODESET)
+      // {
+      DTM dtm = support.getDTM(n1.m_node); // %OPT%
+      result = dtm.isNodeAfter(n1.m_node, n2.m_node) ? -1 : 1;
+
+      // }
+    }
+
+    return result;
+  }
+
+  /**
+   * This implements a standard Mergesort, as described in
+   * Robert Sedgewick's Algorithms book.  This is a better
+   * sort for our purpose than the Quicksort because it
+   * maintains the original document order of the input if
+   * the order isn't changed by the sort.
+   *
+   * @param a First vector of nodes to compare
+   * @param b Second vector of  nodes to compare 
+   * @param l Left boundary of  partition
+   * @param r Right boundary of  partition
+   * @param support XPath context to use
+   *
+   * @throws TransformerException
+   */
+  void mergesort(Vector a, Vector b, int l, int r, XPathContext support)
+          throws TransformerException
+  {
+
+    if ((r - l) > 0)
+    {
+      int m = (r + l) / 2;
+
+      mergesort(a, b, l, m, support);
+      mergesort(a, b, m + 1, r, support);
+
+      int i, j, k;
+
+      for (i = m; i >= l; i--)
+      {
+
+        // b[i] = a[i];
+        // Use insert if we need to increment vector size.
+        if (i >= b.size())
+          b.insertElementAt(a.elementAt(i), i);
+        else
+          b.setElementAt(a.elementAt(i), i);
+      }
+
+      i = l;
+
+      for (j = (m + 1); j <= r; j++)
+      {
+
+        // b[r+m+1-j] = a[j];
+        if (r + m + 1 - j >= b.size())
+          b.insertElementAt(a.elementAt(j), r + m + 1 - j);
+        else
+          b.setElementAt(a.elementAt(j), r + m + 1 - j);
+      }
+
+      j = r;
+
+      int compVal;
+
+      for (k = l; k <= r; k++)
+      {
+
+        // if(b[i] < b[j])
+        if (i == j)
+          compVal = -1;
+        else
+          compVal = compare((NodeCompareElem) b.elementAt(i),
+                            (NodeCompareElem) b.elementAt(j), 0, support);
+
+        if (compVal < 0)
+        {
+
+          // a[k]=b[i];
+          a.setElementAt(b.elementAt(i), k);
+
+          i++;
+        }
+        else if (compVal > 0)
+        {
+
+          // a[k]=b[j]; 
+          a.setElementAt(b.elementAt(j), k);
+
+          j--;
+        }
+      }
+    }
+  }
+
+  /**
+   * This is a generic version of C.A.R Hoare's Quick Sort
+   * algorithm.  This will handle arrays that are already
+   * sorted, and arrays with duplicate keys.<BR>
+   *
+   * If you think of a one dimensional array as going from
+   * the lowest index on the left to the highest index on the right
+   * then the parameters to this function are lowest index or
+   * left and highest index or right.  The first time you call
+   * this function it will be with the parameters 0, a.length - 1.
+   *
+   * @param v       a vector of integers 
+   * @param lo0     left boundary of array partition
+   * @param hi0     right boundary of array partition
+   *
+   */
+
+  /*  private void QuickSort2(Vector v, int lo0, int hi0, XPathContext support)
+      throws javax.xml.transform.TransformerException,
+             java.net.MalformedURLException,
+             java.io.FileNotFoundException,
+             java.io.IOException
+    {
+      int lo = lo0;
+      int hi = hi0;
+
+      if ( hi0 > lo0)
+      {
+        // Arbitrarily establishing partition element as the midpoint of
+        // the array.
+        Node midNode = (Node)v.elementAt( ( lo0 + hi0 ) / 2 );
+
+        // loop through the array until indices cross
+        while( lo <= hi )
+        {
+          // find the first element that is greater than or equal to
+          // the partition element starting from the left Index.
+          while( (lo < hi0) && (compare((Node)v.elementAt(lo), midNode, 0, support) < 0) )
+          {
+            ++lo;
+          } // end while
+
+          // find an element that is smaller than or equal to
+          // the partition element starting from the right Index.
+          while( (hi > lo0) && (compare((Node)v.elementAt(hi), midNode, 0, support) > 0) )
+          {
+            --hi;
+          }
+
+          // if the indexes have not crossed, swap
+          if( lo <= hi )
+          {
+            swap(v, lo, hi);
+            ++lo;
+            --hi;
+          }
+        }
+
+        // If the right index has not reached the left side of array
+        // must now sort the left partition.
+        if( lo0 < hi )
+        {
+          QuickSort2( v, lo0, hi, support );
+        }
+
+        // If the left index has not reached the right side of array
+        // must now sort the right partition.
+        if( lo < hi0 )
+        {
+          QuickSort2( v, lo, hi0, support );
+        }
+      }
+    } // end QuickSort2  */
+
+//  /**
+//   * Simple function to swap two elements in
+//   * a vector.
+//   * 
+//   * @param v Vector of nodes to swap
+//   * @param i Index of first node to swap
+//   * @param i Index of second node to swap
+//   */
+//  private void swap(Vector v, int i, int j)
+//  {
+//
+//    int node = (Node) v.elementAt(i);
+//
+//    v.setElementAt(v.elementAt(j), i);
+//    v.setElementAt(node, j);
+//  }
+
+  /**
+   * This class holds the value(s) from executing the given
+   * node against the sort key(s). 
+   * @xsl.usage internal
+   */
+  class NodeCompareElem
+  {
+
+    /** Current node          */
+    int m_node;
+
+    /** This maxkey value was chosen arbitrarily. We are assuming that the    
+    // maxkey + 1 keys will only hit fairly rarely and therefore, we
+    // will get the node values for those keys dynamically.
+    */
+    int maxkey = 2;
+
+    // Keep this in case we decide to use an array. Right now
+    // using two variables is cheaper.
+    //Object[] m_KeyValue = new Object[2];
+
+    /** Value from first sort key           */
+    Object m_key1Value;
+
+    /** Value from second sort key            */
+    Object m_key2Value;
+
+    /**
+     * Constructor NodeCompareElem
+     *
+     *
+     * @param node Current node
+     *
+     * @throws javax.xml.transform.TransformerException
+     */
+    NodeCompareElem(int node) throws javax.xml.transform.TransformerException
+    {
+      m_node = node;
+
+      if (!m_keys.isEmpty())
+      {
+        NodeSortKey k1 = (NodeSortKey) m_keys.elementAt(0);
+        XObject r = k1.m_selectPat.execute(m_execContext, node,
+                                           k1.m_namespaceContext);
+
+        double d;
+
+        if (k1.m_treatAsNumbers)
+        {
+          d = r.num();
+
+          // Can't use NaN for compare. They are never equal. Use zero instead.  
+          m_key1Value = new Double(d);
+        }
+        else
+        {
+          m_key1Value = k1.m_col.getCollationKey(r.str());
+        }
+
+        if (r.getType() == XObject.CLASS_NODESET)
+        {
+          // %REVIEW%
+          DTMIterator ni = ((XNodeSet)r).iterRaw();
+          int current = ni.getCurrentNode();
+          if(DTM.NULL == current)
+            current = ni.nextNode();
+
+          // if (ni instanceof ContextNodeList) // %REVIEW%
+          // tryNextKey = (DTM.NULL != current);
+
+          // else abdicate... should never happen, but... -sb
+        }
+
+        if (m_keys.size() > 1)
+        {
+          NodeSortKey k2 = (NodeSortKey) m_keys.elementAt(1);
+
+          XObject r2 = k2.m_selectPat.execute(m_execContext, node,
+                                              k2.m_namespaceContext);
+
+          if (k2.m_treatAsNumbers) {
+            d = r2.num();
+            m_key2Value = new Double(d);
+          } else {
+            m_key2Value = k2.m_col.getCollationKey(r2.str());
+          }
+        }
+
+        /* Leave this in case we decide to use an array later
+        while (kIndex <= m_keys.size() && kIndex < maxkey)
+        {
+          NodeSortKey k = (NodeSortKey)m_keys.elementAt(kIndex);
+          XObject r = k.m_selectPat.execute(m_execContext, node, k.m_namespaceContext);
+          if(k.m_treatAsNumbers)
+            m_KeyValue[kIndex] = r.num();
+          else
+            m_KeyValue[kIndex] = r.str();
+        } */
+      }  // end if not empty    
+    }
+  }  // end NodeCompareElem class
+}
diff --git a/src/main/java/org/apache/xalan/transformer/SerializerSwitcher.java b/src/main/java/org/apache/xalan/transformer/SerializerSwitcher.java
new file mode 100644
index 0000000..73ec2d4
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/SerializerSwitcher.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerSwitcher.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Properties;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.serializer.Serializer;
+import org.apache.xml.serializer.SerializerFactory;
+import org.apache.xml.serializer.Method;
+import org.apache.xalan.templates.OutputProperties;
+
+import org.xml.sax.ContentHandler;
+
+/**
+ * This is a helper class that decides if Xalan needs to switch
+ * serializers, based on the first output element.
+ */
+public class SerializerSwitcher
+{
+
+  /**
+   * Switch to HTML serializer if element is HTML
+   *
+   *
+   * @param transformer Non-null transformer instance
+   * @param ns Namespace URI of the element
+   * @param localName Local part of name of element
+   *
+   * @throws TransformerException
+   */
+  public static void switchSerializerIfHTML(
+          TransformerImpl transformer, String ns, String localName)
+            throws TransformerException
+  {
+
+    if (null == transformer)
+      return;
+
+    if (((null == ns) || (ns.length() == 0))
+            && localName.equalsIgnoreCase("html"))
+    {
+      // System.out.println("transformer.getOutputPropertyNoDefault(OutputKeys.METHOD): "+
+      //              transformer.getOutputPropertyNoDefault(OutputKeys.METHOD));     
+      // Access at level of hashtable to see if the method has been set.
+      if (null != transformer.getOutputPropertyNoDefault(OutputKeys.METHOD))
+        return;
+
+      // Getting the output properties this way won't cause a clone of 
+      // the properties.
+      Properties prevProperties = transformer.getOutputFormat().getProperties();
+      
+      // We have to make sure we get an output properties with the proper 
+      // defaults for the HTML method.  The easiest way to do this is to 
+      // have the OutputProperties class do it.
+      OutputProperties htmlOutputProperties = new OutputProperties(Method.HTML);
+
+      htmlOutputProperties.copyFrom(prevProperties, true);
+      Properties htmlProperties = htmlOutputProperties.getProperties();
+
+      try
+      {
+//        Serializer oldSerializer = transformer.getSerializer();
+        Serializer oldSerializer = null;
+
+        if (null != oldSerializer)
+        {
+          Serializer serializer =
+            SerializerFactory.getSerializer(htmlProperties);
+
+          Writer writer = oldSerializer.getWriter();
+
+          if (null != writer)
+            serializer.setWriter(writer);
+          else
+          {
+            OutputStream os = oldSerializer.getOutputStream();
+
+            if (null != os)
+              serializer.setOutputStream(os);
+          }
+
+//          transformer.setSerializer(serializer);
+
+          ContentHandler ch = serializer.asContentHandler();
+
+          transformer.setContentHandler(ch);
+        }
+      }
+      catch (java.io.IOException e)
+      {
+        throw new TransformerException(e);
+      }
+    }
+  }
+  
+  /**
+   * Get the value of a property, without using the default properties.  This 
+   * can be used to test if a property has been explicitly set by the stylesheet 
+   * or user.
+   *
+   * @param name The property name, which is a fully-qualified URI.
+   *
+   * @return The value of the property, or null if not found.
+   *
+   * @throws IllegalArgumentException If the property is not supported, 
+   * and is not namespaced.
+   */
+  private static String getOutputPropertyNoDefault(String qnameString, Properties props)
+    throws IllegalArgumentException
+  {    
+    String value = (String)props.get(qnameString);
+    
+    return value;
+  }
+  
+  /**
+   * Switch to HTML serializer if element is HTML
+   *
+   *
+   * @param ns Namespace URI of the element
+   * @param localName Local part of name of element
+   *
+   * @throws TransformerException
+   * @return new contentHandler.
+   */
+  public static Serializer switchSerializerIfHTML(
+          String ns, String localName, Properties props, Serializer oldSerializer)
+            throws TransformerException
+  {
+    Serializer newSerializer = oldSerializer;
+
+    if (((null == ns) || (ns.length() == 0))
+            && localName.equalsIgnoreCase("html"))
+    {
+      // System.out.println("transformer.getOutputPropertyNoDefault(OutputKeys.METHOD): "+
+      //              transformer.getOutputPropertyNoDefault(OutputKeys.METHOD));     
+      // Access at level of hashtable to see if the method has been set.
+      if (null != getOutputPropertyNoDefault(OutputKeys.METHOD, props))
+        return newSerializer;
+
+      // Getting the output properties this way won't cause a clone of 
+      // the properties.
+      Properties prevProperties = props;
+      
+      // We have to make sure we get an output properties with the proper 
+      // defaults for the HTML method.  The easiest way to do this is to 
+      // have the OutputProperties class do it.
+      OutputProperties htmlOutputProperties = new OutputProperties(Method.HTML);
+
+      htmlOutputProperties.copyFrom(prevProperties, true);
+      Properties htmlProperties = htmlOutputProperties.getProperties();
+
+//      try
+      {
+        if (null != oldSerializer)
+        {
+          Serializer serializer =
+            SerializerFactory.getSerializer(htmlProperties);
+
+          Writer writer = oldSerializer.getWriter();
+
+          if (null != writer)
+            serializer.setWriter(writer);
+          else
+          {
+            OutputStream os = serializer.getOutputStream();
+
+            if (null != os)
+              serializer.setOutputStream(os);
+          }
+          newSerializer = serializer;
+        }
+      }
+//      catch (java.io.IOException e)
+//      {
+//        throw new TransformerException(e);
+//      }
+    }
+    return newSerializer;
+  }
+  
+}
diff --git a/src/main/java/org/apache/xalan/transformer/TrAXFilter.java b/src/main/java/org/apache/xalan/transformer/TrAXFilter.java
new file mode 100644
index 0000000..c32a081
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/TrAXFilter.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TrAXFilter.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.io.IOException;
+
+import javax.xml.XMLConstants;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerConfigurationException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+
+public class TrAXFilter extends XMLFilterImpl
+{
+  private Templates m_templates;
+  private TransformerImpl m_transformer;
+    
+  /**
+   * Construct an empty XML filter, with no parent.
+   *
+   * <p>This filter will have no parent: you must assign a parent
+   * before you start a parse or do any configuration with
+   * setFeature or setProperty.</p>
+   *
+   * @see org.xml.sax.XMLReader#setFeature
+   * @see org.xml.sax.XMLReader#setProperty
+   */
+  public TrAXFilter (Templates templates)
+    throws TransformerConfigurationException
+  {
+    m_templates = templates;
+    m_transformer = (TransformerImpl)templates.newTransformer();
+  }
+  
+  /**
+   * Return the Transformer object used for this XML filter.
+   */
+  public TransformerImpl getTransformer()
+  {
+    return m_transformer;
+  }
+  
+  /** Set the parent reader.
+   *
+   * <p>This is the {@link org.xml.sax.XMLReader XMLReader} from which 
+   * this filter will obtain its events and to which it will pass its 
+   * configuration requests.  The parent may itself be another filter.</p>
+   *
+   * <p>If there is no parent reader set, any attempt to parse
+   * or to set or get a feature or property will fail.</p>
+   *
+   * @param parent The parent XML reader.
+   * @throws java.lang.NullPointerException If the parent is null.
+   */
+  public void setParent (XMLReader parent)
+  { 
+    super.setParent(parent);
+    
+    if(null != parent.getContentHandler())
+      this.setContentHandler(parent.getContentHandler());
+
+    // Not really sure if we should do this here, but 
+    // it seems safer in case someone calls parse() on 
+    // the parent.
+    setupParse ();
+  }
+  
+  /**
+   * Parse a document.
+   *
+   * @param input The input source for the document entity.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @throws java.io.IOException An IO exception from the parser,
+   *            possibly from a byte stream or character stream
+   *            supplied by the application.
+   * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource)
+   */
+  public void parse (InputSource input)
+    throws org.xml.sax.SAXException, IOException
+  {
+    if(null == getParent())
+    {
+      XMLReader reader=null;
+
+      // Use JAXP1.1 ( if possible )
+      try {
+          javax.xml.parsers.SAXParserFactory factory=
+              javax.xml.parsers.SAXParserFactory.newInstance();
+          factory.setNamespaceAware( true );
+          
+          if (m_transformer.getStylesheet().isSecureProcessing()) {
+              try {
+                  factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+              }
+              catch (org.xml.sax.SAXException se) {}
+          }
+          
+          javax.xml.parsers.SAXParser jaxpParser=
+              factory.newSAXParser();
+          reader=jaxpParser.getXMLReader();
+          
+      } catch( javax.xml.parsers.ParserConfigurationException ex ) {
+          throw new org.xml.sax.SAXException( ex );
+      } catch( javax.xml.parsers.FactoryConfigurationError ex1 ) {
+          throw new org.xml.sax.SAXException( ex1.toString() );
+      } catch( NoSuchMethodError ex2 ) {
+      }
+      catch (AbstractMethodError ame){}
+
+      XMLReader parent;
+      if( reader==null )
+          parent= XMLReaderFactory.createXMLReader();
+      else
+          parent=reader;
+      try
+      {
+        parent.setFeature("http://xml.org/sax/features/namespace-prefixes",
+                          true);
+      }
+      catch (org.xml.sax.SAXException se){}
+      // setParent calls setupParse...
+      setParent(parent);
+    }
+    else
+    {
+      // Make sure everything is set up.
+      setupParse ();
+    }
+    if(null == m_transformer.getContentHandler())
+    {
+      throw new org.xml.sax.SAXException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_CALL_PARSE, null)); //"parse can not be called if the ContentHandler has not been set!");
+    }
+
+    getParent().parse(input);
+    Exception e = m_transformer.getExceptionThrown();
+    if(null != e)
+    {
+      if(e instanceof org.xml.sax.SAXException)
+        throw (org.xml.sax.SAXException)e;
+      else
+        throw new org.xml.sax.SAXException(e);
+    }
+  }
+  
+  /**
+   * Parse a document.
+   *
+   * @param systemId The system identifier as a fully-qualified URI.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @throws java.io.IOException An IO exception from the parser,
+   *            possibly from a byte stream or character stream
+   *            supplied by the application.
+   * @see org.xml.sax.XMLReader#parse(java.lang.String)
+   */
+  public void parse (String systemId)
+    throws org.xml.sax.SAXException, IOException
+  {
+    parse(new InputSource(systemId));
+  }
+
+
+  /**
+   * Set up before a parse.
+   *
+   * <p>Before every parse, check whether the parent is
+   * non-null, and re-register the filter for all of the 
+   * events.</p>
+   */
+  private void setupParse ()
+  {
+    XMLReader p = getParent();
+    if (p == null) {
+      throw new NullPointerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_PARENT_FOR_FILTER, null)); //"No parent for filter");
+    }
+    
+    ContentHandler ch = m_transformer.getInputContentHandler();
+//    if(ch instanceof SourceTreeHandler)
+//      ((SourceTreeHandler)ch).setUseMultiThreading(true);
+    p.setContentHandler(ch);
+    p.setEntityResolver(this);
+    p.setDTDHandler(this);
+    p.setErrorHandler(this);
+  }
+
+  /**
+   * Set the content event handler.
+   *
+   * @param handler The new content handler.
+   * @throws java.lang.NullPointerException If the handler
+   *            is null.
+   * @see org.xml.sax.XMLReader#setContentHandler
+   */
+  public void setContentHandler (ContentHandler handler)
+  {
+    m_transformer.setContentHandler(handler);
+    // super.setContentHandler(m_transformer.getResultTreeHandler());
+  }
+  
+  public void setErrorListener (ErrorListener handler)
+  {
+    m_transformer.setErrorListener(handler);
+  }
+
+}
diff --git a/src/main/java/org/apache/xalan/transformer/TransformState.java b/src/main/java/org/apache/xalan/transformer/TransformState.java
new file mode 100644
index 0000000..3f04554
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/TransformState.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TransformState.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import javax.xml.transform.Transformer;
+
+import org.apache.xalan.templates.ElemTemplate;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xml.serializer.TransformStateSetter;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * This interface is meant to be used by a consumer of
+ * SAX2 events produced by Xalan, and enables the consumer
+ * to get information about the state of the transform.  It
+ * is primarily intended as a tooling interface.  A content
+ * handler can get a reference to a TransformState by implementing
+ * the TransformerClient interface.  Xalan will check for
+ * that interface before it calls startDocument, and, if it
+ * is implemented, pass in a TransformState reference to the
+ * setTransformState method.
+ *
+ * <p>Note that the current stylesheet and root stylesheet can
+ * be retrieved from the ElemTemplateElement obtained from
+ * either getCurrentElement() or getCurrentTemplate().</p>
+ * 
+ * This interface contains only getter methods, any setters are in the interface
+ * TransformStateSetter which this interface extends.
+ * 
+ * @see org.apache.xml.serializer.TransformStateSetter
+ */
+public interface TransformState extends TransformStateSetter
+{
+
+  /**
+   * Retrieves the stylesheet element that produced
+   * the SAX event.
+   *
+   * <p>Please note that the ElemTemplateElement returned may
+   * be in a default template, and thus may not be
+   * defined in the stylesheet.</p>
+   *
+   * @return the stylesheet element that produced the SAX event.
+   */
+  ElemTemplateElement getCurrentElement();
+
+  /**
+   * This method retrieves the current context node
+   * in the source tree.
+   *
+   * @return the current context node in the source tree.
+   */
+  Node getCurrentNode();
+
+  /**
+   * This method retrieves the xsl:template
+   * that is in effect, which may be a matched template
+   * or a named template.
+   *
+   * <p>Please note that the ElemTemplate returned may
+   * be a default template, and thus may not have a template
+   * defined in the stylesheet.</p>
+   *
+   * @return the xsl:template that is in effect
+   */
+  ElemTemplate getCurrentTemplate();
+
+  /**
+   * This method retrieves the xsl:template
+   * that was matched.  Note that this may not be
+   * the same thing as the current template (which
+   * may be from getCurrentElement()), since a named
+   * template may be in effect.
+   *
+   * <p>Please note that the ElemTemplate returned may
+   * be a default template, and thus may not have a template
+   * defined in the stylesheet.</p>
+   *
+   * @return the xsl:template that was matched.
+   */
+  ElemTemplate getMatchedTemplate();
+
+  /**
+   * Retrieves the node in the source tree that matched
+   * the template obtained via getMatchedTemplate().
+   *
+   * @return the node in the source tree that matched
+   * the template obtained via getMatchedTemplate().
+   */
+  Node getMatchedNode();
+
+  /**
+   * Get the current context node list.
+   *
+   * @return the current context node list.
+   */
+  NodeIterator getContextNodeList();
+
+  /**
+   * Get the TrAX Transformer object in effect.
+   *
+   * @return the TrAX Transformer object in effect.
+   */
+  Transformer getTransformer();
+  
+
+    
+}
diff --git a/src/main/java/org/apache/xalan/transformer/TransformerClient.java b/src/main/java/org/apache/xalan/transformer/TransformerClient.java
new file mode 100644
index 0000000..b35b036
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/TransformerClient.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TransformerClient.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+/**
+ * A content handler can get a reference
+ * to a TransformState by implementing
+ * the TransformerClient interface.  Xalan will check for
+ * that interface before it calls startDocument, and, if it
+ * is implemented, pass in a TransformState reference to the
+ * setTransformState method.
+ */
+public interface TransformerClient
+{
+
+  /**
+   * Pass in a reference to a TransformState object, which
+   * can be used during SAX ContentHandler events to obtain
+   * information about he state of the transformation. This
+   * method will be called  before each startDocument event.
+   *
+   * @param ts A reference to a TransformState object
+   */
+  void setTransformState(TransformState ts);
+}
diff --git a/src/main/java/org/apache/xalan/transformer/TransformerHandlerImpl.java b/src/main/java/org/apache/xalan/transformer/TransformerHandlerImpl.java
new file mode 100644
index 0000000..3f4c948
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/TransformerHandlerImpl.java
@@ -0,0 +1,1080 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TransformerHandlerImpl.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.io.IOException;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.dtm.ref.IncrementalSAXSource_Filter;
+import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
+import org.apache.xpath.XPathContext;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+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.SAXParseException;
+import org.xml.sax.ext.DeclHandler;
+import org.xml.sax.ext.LexicalHandler;
+import org.apache.xml.serializer.SerializationHandler;
+
+
+/**
+ * A TransformerHandler
+ * listens for SAX ContentHandler parse events and transforms
+ * them to a Result.
+ */
+public class TransformerHandlerImpl
+        implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler,
+                   LexicalHandler, TransformerHandler, DeclHandler
+{
+    /**
+     * The flag for the setting of the optimize feature;
+     */    
+    private final boolean m_optimizer;
+
+    /**
+     * The flag for the setting of the incremental feature;
+     */    
+    private final boolean m_incremental;
+
+    /**
+     * The flag for the setting of the source_location feature;
+     */  
+    private final boolean m_source_location;
+  
+  private boolean m_insideParse = false;
+
+  ////////////////////////////////////////////////////////////////////
+  // Constructors.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Construct a TransformerHandlerImpl.
+   *
+   * @param transformer Non-null reference to the Xalan transformer impl.
+   * @param doFragment True if the result should be a document fragement.
+   * @param baseSystemID  The system ID to use as the base for relative URLs.
+   */
+  public TransformerHandlerImpl(TransformerImpl transformer,
+                                boolean doFragment, String baseSystemID)
+  {
+
+    super();
+
+    m_transformer = transformer;
+    m_baseSystemID = baseSystemID;
+
+    XPathContext xctxt = transformer.getXPathContext();
+    DTM dtm = xctxt.getDTM(null, true, transformer, true, true);
+    
+    m_dtm = dtm;
+    dtm.setDocumentBaseURI(baseSystemID);
+
+    m_contentHandler = dtm.getContentHandler();
+    m_dtdHandler = dtm.getDTDHandler();
+    m_entityResolver = dtm.getEntityResolver();
+    m_errorHandler = dtm.getErrorHandler();
+    m_lexicalHandler = dtm.getLexicalHandler();
+    m_incremental = transformer.getIncremental();
+    m_optimizer = transformer.getOptimize();
+    m_source_location = transformer.getSource_location();
+  }
+  
+  /** 
+   * Do what needs to be done to shut down the CoRoutine management.
+   */
+  protected void clearCoRoutine()
+  {
+    clearCoRoutine(null);
+  }
+  
+  /** 
+   * Do what needs to be done to shut down the CoRoutine management.
+   */
+  protected void clearCoRoutine(SAXException ex)
+  {
+    if(null != ex)
+      m_transformer.setExceptionThrown(ex);
+    
+    if(m_dtm instanceof SAX2DTM)
+    {
+      if(DEBUG)
+        System.err.println("In clearCoRoutine...");
+      try
+      {
+        SAX2DTM sax2dtm = ((SAX2DTM)m_dtm);          
+        if(null != m_contentHandler 
+           && m_contentHandler instanceof IncrementalSAXSource_Filter)
+        {
+          IncrementalSAXSource_Filter sp =
+            (IncrementalSAXSource_Filter)m_contentHandler;
+          // This should now be all that's needed.
+          sp.deliverMoreNodes(false);
+        }
+        
+        sax2dtm.clearCoRoutine(true);
+        m_contentHandler = null;
+        m_dtdHandler = null;
+        m_entityResolver = null;
+        m_errorHandler = null;
+        m_lexicalHandler = null;
+      }
+      catch(Throwable throwable)
+      {
+        throwable.printStackTrace();
+      }
+      
+      if(DEBUG)
+        System.err.println("...exiting clearCoRoutine");
+    }
+  }
+  
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of javax.xml.transform.sax.TransformerHandler.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Enables the user of the TransformerHandler to set the
+   * to set the Result for the transformation.
+   *
+   * @param result A Result instance, should not be null.
+   *
+   * @throws IllegalArgumentException if result is invalid for some reason.
+   */
+  public void setResult(Result result) throws IllegalArgumentException
+  {
+
+    if (null == result)
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_RESULT_NULL, null)); //"result should not be null");
+
+    try
+    {
+//      ContentHandler handler =
+//        m_transformer.createResultContentHandler(result);
+//      m_transformer.setContentHandler(handler);
+        SerializationHandler xoh = 
+            m_transformer.createSerializationHandler(result);
+        m_transformer.setSerializationHandler(xoh);
+    }
+    catch (javax.xml.transform.TransformerException te)
+    {
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_RESULT_COULD_NOT_BE_SET, null)); //"result could not be set");
+    }
+
+    m_result = result;
+  }
+
+  /**
+   * Set the base ID (URI or system ID) from where relative
+   * URLs will be resolved.
+   * @param systemID Base URI for the source tree.
+   */
+  public void setSystemId(String systemID)
+  {
+    m_baseSystemID = systemID;
+    m_dtm.setDocumentBaseURI(systemID);
+  }
+
+  /**
+   * Get the base ID (URI or system ID) from where relative
+   * URLs will be resolved.
+   * @return The systemID that was set with {@link #setSystemId}.
+   */
+  public String getSystemId()
+  {
+    return m_baseSystemID;
+  }
+
+  /**
+   * Get the Transformer associated with this handler, which
+   * is needed in order to set parameters and output properties.
+   *
+   * @return The Transformer associated with this handler
+   */
+  public Transformer getTransformer()
+  {
+    return m_transformer;
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of org.xml.sax.EntityResolver.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Filter an external entity resolution.
+   *
+   * @param publicId The entity's public identifier, or null.
+   * @param systemId The entity's system identifier.
+   * @return A new InputSource or null for the default.
+   *
+   * @throws IOException
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @throws java.io.IOException The client may throw an
+   *            I/O-related exception while obtaining the
+   *            new InputSource.
+   * @see org.xml.sax.EntityResolver#resolveEntity
+   */
+  public InputSource resolveEntity(String publicId, String systemId)
+          throws SAXException, IOException
+  {
+
+    if (m_entityResolver != null)
+    {
+      return m_entityResolver.resolveEntity(publicId, systemId);
+    }
+    else
+    {
+      return null;
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of org.xml.sax.DTDHandler.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Filter a notation declaration event.
+   *
+   * @param name The notation name.
+   * @param publicId The notation's public identifier, or null.
+   * @param systemId The notation's system identifier, or null.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.DTDHandler#notationDecl
+   */
+  public void notationDecl(String name, String publicId, String systemId)
+          throws SAXException
+  {
+
+    if (m_dtdHandler != null)
+    {
+      m_dtdHandler.notationDecl(name, publicId, systemId);
+    }
+  }
+
+  /**
+   * Filter an unparsed entity declaration event.
+   *
+   * @param name The entity name.
+   * @param publicId The entity's public identifier, or null.
+   * @param systemId The entity's system identifier, or null.
+   * @param notationName The name of the associated notation.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void unparsedEntityDecl(
+          String name, String publicId, String systemId, String notationName)
+            throws SAXException
+  {
+
+    if (m_dtdHandler != null)
+    {
+      m_dtdHandler.unparsedEntityDecl(name, publicId, systemId, notationName);
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of org.xml.sax.ContentHandler.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Filter a new document locator event.
+   *
+   * @param locator The document locator.
+   * @see org.xml.sax.ContentHandler#setDocumentLocator
+   */
+  public void setDocumentLocator(Locator locator)
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#setDocumentLocator: "
+                         + locator.getSystemId());
+
+    this.m_locator = locator;
+    
+    if(null == m_baseSystemID)
+    {
+      setSystemId(locator.getSystemId());
+    }
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.setDocumentLocator(locator);
+    }
+  }
+
+  /**
+   * Filter a start document event.
+   *
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#startDocument
+   */
+  public void startDocument() throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#startDocument");
+      
+    m_insideParse = true;
+
+   // Thread listener = new Thread(m_transformer);
+
+    if (m_contentHandler != null)
+    {
+      //m_transformer.setTransformThread(listener);
+      if(m_incremental)
+      {
+        m_transformer.setSourceTreeDocForThread(m_dtm.getDocument());
+            
+        int cpriority = Thread.currentThread().getPriority();
+    
+        // runTransformThread is equivalent with the 2.0.1 code,
+        // except that the Thread may come from a pool.
+        m_transformer.runTransformThread( cpriority );
+      }
+
+      // This is now done _last_, because IncrementalSAXSource_Filter
+      // will immediately go into a "wait until events are requested"
+      // pause. I believe that will close our timing window.
+      // %REVIEW%
+      m_contentHandler.startDocument();
+   }
+        
+   //listener.setDaemon(false);
+   //listener.start();
+
+  }
+
+  /**
+   * Filter an end document event.
+   *
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#endDocument
+   */
+  public void endDocument() throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#endDocument");
+
+    m_insideParse = false;
+    
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.endDocument();
+    }
+    
+    if(m_incremental)
+    {
+      m_transformer.waitTransformThread();
+    }
+    else
+    {
+      m_transformer.setSourceTreeDocForThread(m_dtm.getDocument());
+      m_transformer.run();
+    }
+   /* Thread transformThread = m_transformer.getTransformThread();
+
+    if (null != transformThread)
+    {
+      try
+      {
+
+        // This should wait until the transformThread is considered not alive.
+        transformThread.join();
+
+        if (!m_transformer.hasTransformThreadErrorCatcher())
+        {
+          Exception e = m_transformer.getExceptionThrown();
+
+          if (null != e)
+            throw new org.xml.sax.SAXException(e);
+        }
+
+        m_transformer.setTransformThread(null);
+      }
+      catch (InterruptedException ie){}
+    }*/
+  }
+
+  /**
+   * Filter a start Namespace prefix mapping event.
+   *
+   * @param prefix The Namespace prefix.
+   * @param uri The Namespace URI.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#startPrefixMapping
+   */
+  public void startPrefixMapping(String prefix, String uri)
+          throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#startPrefixMapping: "
+                         + prefix + ", " + uri);
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.startPrefixMapping(prefix, uri);
+    }
+  }
+
+  /**
+   * Filter an end Namespace prefix mapping event.
+   *
+   * @param prefix The Namespace prefix.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#endPrefixMapping
+   */
+  public void endPrefixMapping(String prefix) throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#endPrefixMapping: "
+                         + prefix);
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.endPrefixMapping(prefix);
+    }
+  }
+
+  /**
+   * Filter a start element event.
+   *
+   * @param uri The element's Namespace URI, or the empty string.
+   * @param localName The element's local name, or the empty string.
+   * @param qName The element's qualified (prefixed) name, or the empty
+   *        string.
+   * @param atts The element's attributes.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#startElement
+   */
+  public void startElement(
+          String uri, String localName, String qName, Attributes atts)
+            throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#startElement: " + qName);
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.startElement(uri, localName, qName, atts);
+    }
+  }
+
+  /**
+   * Filter an end element event.
+   *
+   * @param uri The element's Namespace URI, or the empty string.
+   * @param localName The element's local name, or the empty string.
+   * @param qName The element's qualified (prefixed) name, or the empty
+   *        string.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#endElement
+   */
+  public void endElement(String uri, String localName, String qName)
+          throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#endElement: " + qName);
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.endElement(uri, localName, qName);
+    }
+  }
+
+  /**
+   * Filter a character data event.
+   *
+   * @param ch An array of characters.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#characters
+   */
+  public void characters(char ch[], int start, int length) throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#characters: " + start + ", "
+                         + length);
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.characters(ch, start, length);
+    }
+  }
+
+  /**
+   * Filter an ignorable whitespace event.
+   *
+   * @param ch An array of characters.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#ignorableWhitespace
+   */
+  public void ignorableWhitespace(char ch[], int start, int length)
+          throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#ignorableWhitespace: "
+                         + start + ", " + length);
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.ignorableWhitespace(ch, start, length);
+    }
+  }
+
+  /**
+   * Filter a processing instruction event.
+   *
+   * @param target The processing instruction target.
+   * @param data The text following the target.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   */
+  public void processingInstruction(String target, String data)
+          throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#processingInstruction: "
+                         + target + ", " + data);
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.processingInstruction(target, data);
+    }
+  }
+
+  /**
+   * Filter a skipped entity event.
+   *
+   * @param name The name of the skipped entity.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ContentHandler#skippedEntity
+   */
+  public void skippedEntity(String name) throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#skippedEntity: " + name);
+
+    if (m_contentHandler != null)
+    {
+      m_contentHandler.skippedEntity(name);
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of org.xml.sax.ErrorHandler.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Filter a warning event.
+   *
+   * @param e The nwarning as an exception.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ErrorHandler#warning
+   */
+  public void warning(SAXParseException e) throws SAXException
+  {
+    // This is not great, but we really would rather have the error 
+    // handler be the error listener if it is a error handler.  Coroutine's fatalError 
+    // can't really be configured, so I think this is the best thing right now 
+    // for error reporting.  Possibly another JAXP 1.1 hole.  -sb
+    javax.xml.transform.ErrorListener errorListener = m_transformer.getErrorListener();
+    if(errorListener instanceof ErrorHandler)
+    {
+      ((ErrorHandler)errorListener).warning(e);
+    }
+    else
+    {
+      try
+      {
+        errorListener.warning(new javax.xml.transform.TransformerException(e));
+      }
+      catch(javax.xml.transform.TransformerException te)
+      {
+        throw e;
+      }
+    }
+  }
+
+  /**
+   * Filter an error event.
+   *
+   * @param e The error as an exception.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ErrorHandler#error
+   */
+  public void error(SAXParseException e) throws SAXException
+  {
+    // %REVIEW% I don't think this should be called.  -sb
+    // clearCoRoutine(e);
+
+    // This is not great, but we really would rather have the error 
+    // handler be the error listener if it is a error handler.  Coroutine's fatalError 
+    // can't really be configured, so I think this is the best thing right now 
+    // for error reporting.  Possibly another JAXP 1.1 hole.  -sb
+    javax.xml.transform.ErrorListener errorListener = m_transformer.getErrorListener();
+    if(errorListener instanceof ErrorHandler)
+    {
+      ((ErrorHandler)errorListener).error(e);
+      if(null != m_errorHandler)
+        m_errorHandler.error(e); // may not be called.
+    }
+    else
+    {
+      try
+      {
+        errorListener.error(new javax.xml.transform.TransformerException(e));
+        if(null != m_errorHandler)
+          m_errorHandler.error(e); // may not be called.
+      }
+      catch(javax.xml.transform.TransformerException te)
+      {
+        throw e;
+      }
+    }
+  }
+
+  /**
+   * Filter a fatal error event.
+   *
+   * @param e The error as an exception.
+   * @throws SAXException The client may throw
+   *            an exception during processing.
+   * @see org.xml.sax.ErrorHandler#fatalError
+   */
+  public void fatalError(SAXParseException e) throws SAXException
+  {
+    if(null != m_errorHandler)
+    {
+      try
+      {
+        m_errorHandler.fatalError(e);
+      }
+      catch(SAXParseException se)
+      {
+        // ignore
+      }
+      // clearCoRoutine(e);
+    }
+
+    // This is not great, but we really would rather have the error 
+    // handler be the error listener if it is a error handler.  Coroutine's fatalError 
+    // can't really be configured, so I think this is the best thing right now 
+    // for error reporting.  Possibly another JAXP 1.1 hole.  -sb
+    javax.xml.transform.ErrorListener errorListener = m_transformer.getErrorListener();
+    
+    if(errorListener instanceof ErrorHandler)
+    {
+      ((ErrorHandler)errorListener).fatalError(e);
+      if(null != m_errorHandler)
+        m_errorHandler.fatalError(e); // may not be called.
+    }
+    else
+    {
+      try
+      {
+        errorListener.fatalError(new javax.xml.transform.TransformerException(e));
+        if(null != m_errorHandler)
+          m_errorHandler.fatalError(e); // may not be called.
+      }
+      catch(javax.xml.transform.TransformerException te)
+      {
+        throw e;
+      }
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of org.xml.sax.ext.LexicalHandler.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Report the start of DTD declarations, if any.
+   *
+   * <p>Any declarations are assumed to be in the internal subset
+   * unless otherwise indicated by a {@link #startEntity startEntity}
+   * event.</p>
+   *
+   * <p>Note that the start/endDTD events will appear within
+   * the start/endDocument events from ContentHandler and
+   * before the first startElement event.</p>
+   *
+   * @param name The document type name.
+   * @param publicId The declared public identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @param systemId The declared system identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @throws SAXException The application may raise an
+   *            exception.
+   * @see #endDTD
+   * @see #startEntity
+   */
+  public void startDTD(String name, String publicId, String systemId)
+          throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#startDTD: " + name + ", "
+                         + publicId + ", " + systemId);
+
+    if (null != m_lexicalHandler)
+    {
+      m_lexicalHandler.startDTD(name, publicId, systemId);
+    }
+  }
+
+  /**
+   * Report the end of DTD declarations.
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #startDTD
+   */
+  public void endDTD() throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#endDTD");
+
+    if (null != m_lexicalHandler)
+    {
+      m_lexicalHandler.endDTD();
+    }
+  }
+
+  /**
+   * Report the beginning of an entity in content.
+   *
+   * <p><strong>NOTE:</entity> entity references in attribute
+   * values -- and the start and end of the document entity --
+   * are never reported.</p>
+   *
+   * <p>The start and end of the external DTD subset are reported
+   * using the pseudo-name "[dtd]".  All other events must be
+   * properly nested within start/end entity events.</p>
+   *
+   * <p>Note that skipped entities will be reported through the
+   * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
+   * event, which is part of the ContentHandler interface.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @throws SAXException The application may raise an exception.
+   * @see #endEntity
+   * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
+   * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
+   */
+  public void startEntity(String name) throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#startEntity: " + name);
+
+    if (null != m_lexicalHandler)
+    {
+      m_lexicalHandler.startEntity(name);
+    }
+  }
+
+  /**
+   * Report the end of an entity.
+   *
+   * @param name The name of the entity that is ending.
+   * @throws SAXException The application may raise an exception.
+   * @see #startEntity
+   */
+  public void endEntity(String name) throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#endEntity: " + name);
+
+    if (null != m_lexicalHandler)
+    {
+      m_lexicalHandler.endEntity(name);
+    }
+  }
+
+  /**
+   * Report the start of a CDATA section.
+   *
+   * <p>The contents of the CDATA section will be reported through
+   * the regular {@link org.xml.sax.ContentHandler#characters
+   * characters} event.</p>
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #endCDATA
+   */
+  public void startCDATA() throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#startCDATA");
+
+    if (null != m_lexicalHandler)
+    {
+      m_lexicalHandler.startCDATA();
+    }
+  }
+
+  /**
+   * Report the end of a CDATA section.
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #startCDATA
+   */
+  public void endCDATA() throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#endCDATA");
+
+    if (null != m_lexicalHandler)
+    {
+      m_lexicalHandler.endCDATA();
+    }
+  }
+
+  /**
+   * Report an XML comment anywhere in the document.
+   *
+   * <p>This callback will be used for comments inside or outside the
+   * document element, including comments in the external DTD
+   * subset (if read).</p>
+   *
+   * @param ch An array holding the characters in the comment.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   * @throws SAXException The application may raise an exception.
+   */
+  public void comment(char ch[], int start, int length) throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#comment: " + start + ", "
+                         + length);
+
+    if (null != m_lexicalHandler)
+    {
+      m_lexicalHandler.comment(ch, start, length);
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of org.xml.sax.ext.DeclHandler.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Report an element type declaration.
+   *
+   * <p>The content model will consist of the string "EMPTY", the
+   * string "ANY", or a parenthesised group, optionally followed
+   * by an occurrence indicator.  The model will be normalized so
+   * that all whitespace is removed,and will include the enclosing
+   * parentheses.</p>
+   *
+   * @param name The element type name.
+   * @param model The content model as a normalized string.
+   * @throws SAXException The application may raise an exception.
+   */
+  public void elementDecl(String name, String model) throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#elementDecl: " + name + ", "
+                         + model);
+
+    if (null != m_declHandler)
+    {
+      m_declHandler.elementDecl(name, model);
+    }
+  }
+
+  /**
+   * Report an attribute type declaration.
+   *
+   * <p>Only the effective (first) declaration for an attribute will
+   * be reported.  The type will be one of the strings "CDATA",
+   * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
+   * "ENTITIES", or "NOTATION", or a parenthesized token group with
+   * the separator "|" and all whitespace removed.</p>
+   *
+   * @param eName The name of the associated element.
+   * @param aName The name of the attribute.
+   * @param type A string representing the attribute type.
+   * @param valueDefault A string representing the attribute default
+   *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
+   *        none of these applies.
+   * @param value A string representing the attribute's default value,
+   *        or null if there is none.
+   * @throws SAXException The application may raise an exception.
+   */
+  public void attributeDecl(
+          String eName, String aName, String type, String valueDefault, String value)
+            throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#attributeDecl: " + eName
+                         + ", " + aName + ", etc...");
+
+    if (null != m_declHandler)
+    {
+      m_declHandler.attributeDecl(eName, aName, type, valueDefault, value);
+    }
+  }
+
+  /**
+   * Report an internal entity declaration.
+   *
+   * <p>Only the effective (first) declaration for each entity
+   * will be reported.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @param value The replacement text of the entity.
+   * @throws SAXException The application may raise an exception.
+   * @see #externalEntityDecl
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void internalEntityDecl(String name, String value)
+          throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#internalEntityDecl: " + name
+                         + ", " + value);
+
+    if (null != m_declHandler)
+    {
+      m_declHandler.internalEntityDecl(name, value);
+    }
+  }
+
+  /**
+   * Report a parsed external entity declaration.
+   *
+   * <p>Only the effective (first) declaration for each entity
+   * will be reported.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @param publicId The declared public identifier of the entity, or
+   *        null if none was declared.
+   * @param systemId The declared system identifier of the entity.
+   * @throws SAXException The application may raise an exception.
+   * @see #internalEntityDecl
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void externalEntityDecl(
+          String name, String publicId, String systemId) throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("TransformerHandlerImpl#externalEntityDecl: " + name
+                         + ", " + publicId + ", " + systemId);
+
+    if (null != m_declHandler)
+    {
+      m_declHandler.externalEntityDecl(name, publicId, systemId);
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Internal state.
+  ////////////////////////////////////////////////////////////////////
+
+  /** Set to true for diagnostics output.         */
+  private static boolean DEBUG = false;
+
+  /**
+   * The transformer this will use to transform a
+   * source tree into a result tree.
+   */
+  private TransformerImpl m_transformer;
+
+  /** The system ID to use as a base for relative URLs. */
+  private String m_baseSystemID;
+
+  /** The result for the transformation. */
+  private Result m_result = null;
+
+  /** The locator for this TransformerHandler. */
+  private Locator m_locator = null;
+
+  /** The entity resolver to aggregate to. */
+  private EntityResolver m_entityResolver = null;
+
+  /** The DTD handler to aggregate to. */
+  private DTDHandler m_dtdHandler = null;
+
+  /** The content handler to aggregate to. */
+  private ContentHandler m_contentHandler = null;
+
+  /** The error handler to aggregate to. */
+  private ErrorHandler m_errorHandler = null;
+
+  /** The lexical handler to aggregate to. */
+  private LexicalHandler m_lexicalHandler = null;
+
+  /** The decl handler to aggregate to. */
+  private DeclHandler m_declHandler = null;
+  
+  /** The Document Table Instance we are transforming. */
+  DTM m_dtm;
+}
diff --git a/src/main/java/org/apache/xalan/transformer/TransformerIdentityImpl.java b/src/main/java/org/apache/xalan/transformer/TransformerIdentityImpl.java
new file mode 100644
index 0000000..c7b2534
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/TransformerIdentityImpl.java
@@ -0,0 +1,1481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TransformerIdentityImpl.java 575747 2007-09-14 16:28:37Z kcormier $
+ */
+package org.apache.xalan.transformer;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xalan.templates.OutputProperties;
+import org.apache.xml.serializer.Serializer;
+import org.apache.xml.serializer.SerializerFactory;
+import org.apache.xml.serializer.Method;
+import org.apache.xml.utils.DOMBuilder;
+import org.apache.xml.utils.XMLReaderManager;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.DeclHandler;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * This class implements an identity transformer for
+ * {@link javax.xml.transform.sax.SAXTransformerFactory#newTransformerHandler()}
+ * and {@link javax.xml.transform.TransformerFactory#newTransformer()}.  It
+ * simply feeds SAX events directly to a serializer ContentHandler, if the
+ * result is a stream.  If the result is a DOM, it will send the events to
+ * {@link org.apache.xml.utils.DOMBuilder}.  If the result is another
+ * content handler, it will simply pass the events on.
+ */
+public class TransformerIdentityImpl extends Transformer
+        implements TransformerHandler, DeclHandler
+{
+
+  /**
+   * Constructor TransformerIdentityImpl creates an identity transform.
+   *
+   */
+  public TransformerIdentityImpl(boolean isSecureProcessing)
+  {
+    m_outputFormat = new OutputProperties(Method.XML);
+    m_isSecureProcessing = isSecureProcessing;
+  }
+
+  /**
+   * Constructor TransformerIdentityImpl creates an identity transform.
+   *
+   */
+  public TransformerIdentityImpl()
+  {
+    this(false);
+  }
+
+  /**
+   * Enables the user of the TransformerHandler to set the
+   * to set the Result for the transformation.
+   *
+   * @param result A Result instance, should not be null.
+   *
+   * @throws IllegalArgumentException if result is invalid for some reason.
+   */
+  public void setResult(Result result) throws IllegalArgumentException
+  {
+    if(null == result)
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_RESULT_NULL, null)); //"Result should not be null");        
+    m_result = result;
+  }
+
+  /**
+   * Set the base ID (URI or system ID) from where relative
+   * URLs will be resolved.
+   * @param systemID Base URI for the source tree.
+   */
+  public void setSystemId(String systemID)
+  {
+    m_systemID = systemID;
+  }
+
+  /**
+   * Get the base ID (URI or system ID) from where relative
+   * URLs will be resolved.
+   * @return The systemID that was set with {@link #setSystemId}.
+   */
+  public String getSystemId()
+  {
+    return m_systemID;
+  }
+
+  /**
+   * Get the Transformer associated with this handler, which
+   * is needed in order to set parameters and output properties.
+   *
+   * @return non-null reference to the transformer.
+   */
+  public Transformer getTransformer()
+  {
+    return this;
+  }
+
+  /**
+   * Reset the status of the transformer.
+   */
+  public void reset()
+  {
+    m_flushedStartDoc = false;
+    m_foundFirstElement = false;
+    m_outputStream = null;
+    clearParameters();
+    m_result = null;
+    m_resultContentHandler = null;
+    m_resultDeclHandler = null;
+    m_resultDTDHandler = null;
+    m_resultLexicalHandler = null;
+    m_serializer = null;
+    m_systemID = null;
+    m_URIResolver = null;
+    m_outputFormat = new OutputProperties(Method.XML);
+  }
+
+  /**
+   * Create a result ContentHandler from a Result object, based
+   * on the current OutputProperties.
+   *
+   * @param outputTarget Where the transform result should go,
+   * should not be null.
+   *
+   * @return A valid ContentHandler that will create the
+   * result tree when it is fed SAX events.
+   *
+   * @throws TransformerException
+   */
+  private void createResultContentHandler(Result outputTarget)
+          throws TransformerException
+  {
+
+    if (outputTarget instanceof SAXResult)
+    {
+      SAXResult saxResult = (SAXResult) outputTarget;
+
+      m_resultContentHandler = saxResult.getHandler();
+      m_resultLexicalHandler = saxResult.getLexicalHandler();
+
+      if (m_resultContentHandler instanceof Serializer)
+      {
+
+        // Dubious but needed, I think.
+        m_serializer = (Serializer) m_resultContentHandler;
+      }
+    }
+    else if (outputTarget instanceof DOMResult)
+    {
+      DOMResult domResult = (DOMResult) outputTarget;
+      Node outputNode = domResult.getNode();
+      Node nextSibling = domResult.getNextSibling();
+      Document doc;
+      short type;
+
+      if (null != outputNode)
+      {
+        type = outputNode.getNodeType();
+        doc = (Node.DOCUMENT_NODE == type)
+              ? (Document) outputNode : outputNode.getOwnerDocument();
+      }
+      else
+      {
+        try
+        {
+          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+
+          dbf.setNamespaceAware(true);
+
+          if (m_isSecureProcessing)
+          {
+            try
+            {
+              dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            }
+            catch (ParserConfigurationException pce) {}
+          }
+
+          DocumentBuilder db = dbf.newDocumentBuilder();
+
+          doc = db.newDocument();
+        }
+        catch (ParserConfigurationException pce)
+        {
+          throw new TransformerException(pce);
+        }
+
+        outputNode = doc;
+        type = outputNode.getNodeType();
+
+        ((DOMResult) outputTarget).setNode(outputNode);
+      }
+
+      DOMBuilder domBuilder =
+        (Node.DOCUMENT_FRAGMENT_NODE == type)
+        ? new DOMBuilder(doc, (DocumentFragment) outputNode)
+        : new DOMBuilder(doc, outputNode);
+      
+      if (nextSibling != null)
+        domBuilder.setNextSibling(nextSibling);
+      
+      m_resultContentHandler = domBuilder;
+      m_resultLexicalHandler = domBuilder;
+    }
+    else if (outputTarget instanceof StreamResult)
+    {
+      StreamResult sresult = (StreamResult) outputTarget;
+
+      try
+      {
+        Serializer serializer =
+          SerializerFactory.getSerializer(m_outputFormat.getProperties());
+
+        m_serializer = serializer;
+
+        if (null != sresult.getWriter())
+          serializer.setWriter(sresult.getWriter());
+        else if (null != sresult.getOutputStream())
+          serializer.setOutputStream(sresult.getOutputStream());
+        else if (null != sresult.getSystemId())
+        {
+          String fileURL = sresult.getSystemId();
+
+          if (fileURL.startsWith("file:///")) {
+            if (fileURL.substring(8).indexOf(":") >0) {
+              fileURL = fileURL.substring(8);
+            } else  {
+              fileURL = fileURL.substring(7);
+            }
+          } else if (fileURL.startsWith("file:/")) {
+            if (fileURL.substring(6).indexOf(":") >0) {
+              fileURL = fileURL.substring(6);
+            } else {
+              fileURL = fileURL.substring(5);
+            }
+          }
+
+          m_outputStream = new java.io.FileOutputStream(fileURL);
+          serializer.setOutputStream(m_outputStream);
+        }
+        else
+          throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!");
+
+        m_resultContentHandler = serializer.asContentHandler();
+      }
+      catch (IOException ioe)
+      {
+        throw new TransformerException(ioe);
+      }
+    }
+    else
+    {
+      throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE, new Object[]{outputTarget.getClass().getName()})); //"Can't transform to a Result of type "
+                                    // + outputTarget.getClass().getName()
+                                    // + "!");
+    }
+
+    if (m_resultContentHandler instanceof DTDHandler)
+      m_resultDTDHandler = (DTDHandler) m_resultContentHandler;
+    
+    if (m_resultContentHandler instanceof DeclHandler)
+      m_resultDeclHandler = (DeclHandler) m_resultContentHandler;
+
+    if (m_resultContentHandler instanceof LexicalHandler)
+      m_resultLexicalHandler = (LexicalHandler) m_resultContentHandler;
+  }
+
+  /**
+   * Process the source tree to the output result.
+   * @param source  The input for the source tree.
+   *
+   * @param outputTarget The output target.
+   *
+   * @throws TransformerException If an unrecoverable error occurs
+   * during the course of the transformation.
+   */
+  public void transform(Source source, Result outputTarget)
+          throws TransformerException
+  {
+
+    createResultContentHandler(outputTarget);
+    
+    /*
+     * According to JAXP1.2, new SAXSource()/StreamSource()
+     * should create an empty input tree, with a default root node. 
+     * new DOMSource()creates an empty document using DocumentBuilder.
+     * newDocument(); Use DocumentBuilder.newDocument() for all 3 situations,
+     * since there is no clear spec. how to create an empty tree when
+     * both SAXSource() and StreamSource() are used.
+     */
+    if ((source instanceof StreamSource && source.getSystemId()==null &&
+       ((StreamSource)source).getInputStream()==null &&
+       ((StreamSource)source).getReader()==null)||
+       (source instanceof SAXSource &&
+       ((SAXSource)source).getInputSource()==null &&
+       ((SAXSource)source).getXMLReader()==null )||
+       (source instanceof DOMSource && ((DOMSource)source).getNode()==null)){
+      try {
+        DocumentBuilderFactory builderF = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = builderF.newDocumentBuilder();
+        String systemID = source.getSystemId();
+        source = new DOMSource(builder.newDocument());
+
+        // Copy system ID from original, empty Source to new Source
+        if (systemID != null) {
+          source.setSystemId(systemID);
+        }
+      } catch (ParserConfigurationException e){
+        throw new TransformerException(e.getMessage());
+      }           
+    }
+    
+    try
+    {
+      if (source instanceof DOMSource)
+      {
+        DOMSource dsource = (DOMSource) source;
+  
+        m_systemID = dsource.getSystemId();
+  
+        Node dNode = dsource.getNode();
+  
+        if (null != dNode)
+        {
+          try
+          {
+            if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
+              this.startDocument();
+            try
+            {
+              if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
+              {
+                String data = dNode.getNodeValue();
+                char[] chars = data.toCharArray();
+                characters(chars, 0, chars.length);
+              }
+              else
+              { 
+                org.apache.xml.serializer.TreeWalker walker;
+                walker = new org.apache.xml.serializer.TreeWalker(this, m_systemID);
+                walker.traverse(dNode);
+              }
+            }
+            finally
+            {
+              if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
+                this.endDocument();
+            }
+          }
+          catch (SAXException se)
+          {
+            throw new TransformerException(se);
+          }
+  
+          return;
+        }
+        else
+        {
+          String messageStr = XSLMessages.createMessage(
+            XSLTErrorResources.ER_ILLEGAL_DOMSOURCE_INPUT, null);
+  
+          throw new IllegalArgumentException(messageStr);
+        }
+      }
+  
+      InputSource xmlSource = SAXSource.sourceToInputSource(source);
+  
+      if (null == xmlSource)
+      {
+        throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_SOURCE_TYPE, new Object[]{source.getClass().getName()})); //"Can't transform a Source of type "
+                                       //+ source.getClass().getName() + "!");
+      }
+  
+      if (null != xmlSource.getSystemId())
+        m_systemID = xmlSource.getSystemId();
+  
+      XMLReader reader = null;
+      boolean managedReader = false;
+  
+      try
+      {
+        if (source instanceof SAXSource) {
+          reader = ((SAXSource) source).getXMLReader();
+        }
+          
+        if (null == reader) {
+          try {
+            reader = XMLReaderManager.getInstance().getXMLReader();
+            managedReader = true;
+          } catch (SAXException se) {
+            throw new TransformerException(se);
+          }
+        } else {
+          try {
+            reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
+                              true);
+          } catch (org.xml.sax.SAXException se) {
+            // We don't care.
+          }
+        }
+
+        // Get the input content handler, which will handle the 
+        // parse events and create the source tree. 
+        ContentHandler inputHandler = this;
+  
+        reader.setContentHandler(inputHandler);
+  
+        if (inputHandler instanceof org.xml.sax.DTDHandler)
+          reader.setDTDHandler((org.xml.sax.DTDHandler) inputHandler);
+  
+        try
+        {
+          if (inputHandler instanceof org.xml.sax.ext.LexicalHandler)
+            reader.setProperty("http://xml.org/sax/properties/lexical-handler",
+                               inputHandler);
+  
+          if (inputHandler instanceof org.xml.sax.ext.DeclHandler)
+            reader.setProperty(
+              "http://xml.org/sax/properties/declaration-handler",
+              inputHandler);
+        }
+        catch (org.xml.sax.SAXException se){}
+  
+        try
+        {
+          if (inputHandler instanceof org.xml.sax.ext.LexicalHandler)
+            reader.setProperty("http://xml.org/sax/handlers/LexicalHandler",
+                               inputHandler);
+  
+          if (inputHandler instanceof org.xml.sax.ext.DeclHandler)
+            reader.setProperty("http://xml.org/sax/handlers/DeclHandler",
+                               inputHandler);
+        }
+        catch (org.xml.sax.SAXNotRecognizedException snre){}
+  
+        reader.parse(xmlSource);
+      }
+      catch (org.apache.xml.utils.WrappedRuntimeException wre)
+      {
+        Throwable throwable = wre.getException();
+  
+        while (throwable
+               instanceof org.apache.xml.utils.WrappedRuntimeException)
+        {
+          throwable =
+            ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
+        }
+  
+        throw new TransformerException(wre.getException());
+      }
+      catch (org.xml.sax.SAXException se)
+      {
+        throw new TransformerException(se);
+      }
+      catch (IOException ioe)
+      {
+        throw new TransformerException(ioe);
+      } finally {
+        if (managedReader) {
+          XMLReaderManager.getInstance().releaseXMLReader(reader);
+        }
+      }
+    }
+    finally
+    {
+      if(null != m_outputStream)
+      {
+        try
+        {
+          m_outputStream.close();
+        }
+        catch(IOException ioe){}
+        m_outputStream = null;
+      }
+    }
+  }
+
+  /**
+   * Add a parameter for the transformation.
+   *
+   * <p>Pass a qualified name as a two-part string, the namespace URI
+   * enclosed in curly braces ({}), followed by the local name. If the
+   * name has a null URL, the String only contain the local name. An
+   * application can safely check for a non-null URI by testing to see if the first
+   * character of the name is a '{' character.</p>
+   * <p>For example, if a URI and local name were obtained from an element
+   * defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
+   * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
+   * no prefix is used.</p>
+   *
+   * @param name The name of the parameter, which may begin with a namespace URI
+   * in curly braces ({}).
+   * @param value The value object.  This can be any valid Java object. It is
+   * up to the processor to provide the proper object coersion or to simply
+   * pass the object on for use in an extension.
+   */
+  public void setParameter(String name, Object value)
+  {
+    if (value == null) {
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name}));
+    }
+    
+    if (null == m_params)
+    {
+      m_params = new Hashtable();
+    }
+
+    m_params.put(name, value);
+  }
+
+  /**
+   * Get a parameter that was explicitly set with setParameter
+   * or setParameters.
+   *
+   * <p>This method does not return a default parameter value, which
+   * cannot be determined until the node context is evaluated during
+   * the transformation process.
+   *
+   *
+   * @param name Name of the parameter.
+   * @return A parameter that has been set with setParameter.
+   */
+  public Object getParameter(String name)
+  {
+
+    if (null == m_params)
+      return null;
+
+    return m_params.get(name);
+  }
+
+  /**
+   * Clear all parameters set with setParameter.
+   */
+  public void clearParameters()
+  {
+
+    if (null == m_params)
+      return;
+
+    m_params.clear();
+  }
+
+  /**
+   * Set an object that will be used to resolve URIs used in
+   * document().
+   *
+   * <p>If the resolver argument is null, the URIResolver value will
+   * be cleared, and the default behavior will be used.</p>
+   *
+   * @param resolver An object that implements the URIResolver interface,
+   * or null.
+   */
+  public void setURIResolver(URIResolver resolver)
+  {
+    m_URIResolver = resolver;
+  }
+
+  /**
+   * Get an object that will be used to resolve URIs used in
+   * document(), etc.
+   *
+   * @return An object that implements the URIResolver interface,
+   * or null.
+   */
+  public URIResolver getURIResolver()
+  {
+    return m_URIResolver;
+  }
+
+  /**
+   * Set the output properties for the transformation.  These
+   * properties will override properties set in the Templates
+   * with xsl:output.
+   *
+   * <p>If argument to this function is null, any properties
+   * previously set are removed, and the value will revert to the value
+   * defined in the templates object.</p>
+   *
+   * <p>Pass a qualified property key name as a two-part string, the namespace URI
+   * enclosed in curly braces ({}), followed by the local name. If the
+   * name has a null URL, the String only contain the local name. An
+   * application can safely check for a non-null URI by testing to see if the first
+   * character of the name is a '{' character.</p>
+   * <p>For example, if a URI and local name were obtained from an element
+   * defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
+   * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
+   * no prefix is used.</p>
+   *
+   * @param oformat A set of output properties that will be
+   * used to override any of the same properties in affect
+   * for the transformation.
+   *
+   * @see javax.xml.transform.OutputKeys
+   * @see java.util.Properties
+   *
+   * @throws IllegalArgumentException if any of the argument keys are not
+   * recognized and are not namespace qualified.
+   */
+  public void setOutputProperties(Properties oformat)
+          throws IllegalArgumentException
+  {
+
+    if (null != oformat)
+    {
+
+      // See if an *explicit* method was set.
+      String method = (String) oformat.get(OutputKeys.METHOD);
+
+      if (null != method)
+        m_outputFormat = new OutputProperties(method);
+      else
+        m_outputFormat = new OutputProperties();
+
+      m_outputFormat.copyFrom(oformat);
+    }
+    else {
+      // if oformat is null JAXP says that any props previously set are removed
+      // and we are to revert back to those in the templates object (i.e. Stylesheet).
+      m_outputFormat = null;
+    }
+  }
+
+  /**
+   * Get a copy of the output properties for the transformation.
+   *
+   * <p>The properties returned should contain properties set by the user,
+   * and properties set by the stylesheet, and these properties
+   * are "defaulted" by default properties specified by <a href="http://www.w3.org/TR/xslt#output">section 16 of the
+   * XSL Transformations (XSLT) W3C Recommendation</a>.  The properties that
+   * were specifically set by the user or the stylesheet should be in the base
+   * Properties list, while the XSLT default properties that were not
+   * specifically set should be the default Properties list.  Thus,
+   * getOutputProperties().getProperty(String key) will obtain any
+   * property in that was set by {@link #setOutputProperty},
+   * {@link #setOutputProperties}, in the stylesheet, <em>or</em> the default
+   * properties, while
+   * getOutputProperties().get(String key) will only retrieve properties
+   * that were explicitly set by {@link #setOutputProperty},
+   * {@link #setOutputProperties}, or in the stylesheet.</p>
+   *
+   * <p>Note that mutation of the Properties object returned will not
+   * effect the properties that the transformation contains.</p>
+   *
+   * <p>If any of the argument keys are not recognized and are not
+   * namespace qualified, the property will be ignored.  In other words the
+   * behaviour is not orthogonal with setOutputProperties.</p>
+   *
+   * @return A copy of the set of output properties in effect
+   * for the next transformation.
+   *
+   * @see javax.xml.transform.OutputKeys
+   * @see java.util.Properties
+   */
+  public Properties getOutputProperties()
+  {
+    return (Properties) m_outputFormat.getProperties().clone();
+  }
+
+  /**
+   * Set an output property that will be in effect for the
+   * transformation.
+   *
+   * <p>Pass a qualified property name as a two-part string, the namespace URI
+   * enclosed in curly braces ({}), followed by the local name. If the
+   * name has a null URL, the String only contain the local name. An
+   * application can safely check for a non-null URI by testing to see if the first
+   * character of the name is a '{' character.</p>
+   * <p>For example, if a URI and local name were obtained from an element
+   * defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
+   * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
+   * no prefix is used.</p>
+   *
+   * <p>The Properties object that was passed to {@link #setOutputProperties} won't
+   * be effected by calling this method.</p>
+   *
+   * @param name A non-null String that specifies an output
+   * property name, which may be namespace qualified.
+   * @param value The non-null string value of the output property.
+   *
+   * @throws IllegalArgumentException If the property is not supported, and is
+   * not qualified with a namespace.
+   *
+   * @see javax.xml.transform.OutputKeys
+   */
+  public void setOutputProperty(String name, String value)
+          throws IllegalArgumentException
+  {
+
+    if (!OutputProperties.isLegalPropertyKey(name))
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
+                                         //+ name);
+
+    m_outputFormat.setProperty(name, value);
+  }
+
+  /**
+   * Get an output property that is in effect for the
+   * transformation.  The property specified may be a property
+   * that was set with setOutputProperty, or it may be a
+   * property specified in the stylesheet.
+   *
+   * @param name A non-null String that specifies an output
+   * property name, which may be namespace qualified.
+   *
+   * @return The string value of the output property, or null
+   * if no property was found.
+   *
+   * @throws IllegalArgumentException If the property is not supported.
+   *
+   * @see javax.xml.transform.OutputKeys
+   */
+  public String getOutputProperty(String name) throws IllegalArgumentException
+  {
+
+    String value = null;
+    OutputProperties props = m_outputFormat;
+
+    value = props.getProperty(name);
+
+    if (null == value)
+    {
+      if (!OutputProperties.isLegalPropertyKey(name))
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
+                                          // + name);
+    }
+
+    return value;
+  }
+
+  /**
+   * Set the error event listener in effect for the transformation.
+   *
+   * @param listener The new error listener.
+   * @throws IllegalArgumentException if listener is null.
+   */
+  public void setErrorListener(ErrorListener listener)
+          throws IllegalArgumentException
+  {
+      if (listener == null)
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null));
+      else
+        m_errorListener = listener;
+  }
+
+  /**
+   * Get the error event handler in effect for the transformation.
+   *
+   * @return The current error handler, which should never be null.
+   */
+  public ErrorListener getErrorListener()
+  {
+    return m_errorListener;
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Default implementation of DTDHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Receive notification of a notation declaration.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass if they wish to keep track of the notations
+   * declared in a document.</p>
+   *
+   * @param name The notation name.
+   * @param publicId The notation public identifier, or null if not
+   *                 available.
+   * @param systemId The notation system identifier.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.DTDHandler#notationDecl
+   *
+   * @throws SAXException
+   */
+  public void notationDecl(String name, String publicId, String systemId)
+          throws SAXException
+  {
+    if (null != m_resultDTDHandler)
+      m_resultDTDHandler.notationDecl(name, publicId, systemId);
+  }
+
+  /**
+   * Receive notification of an unparsed entity declaration.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to keep track of the unparsed entities
+   * declared in a document.</p>
+   *
+   * @param name The entity name.
+   * @param publicId The entity public identifier, or null if not
+   *                 available.
+   * @param systemId The entity system identifier.
+   * @param notationName The name of the associated notation.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   *
+   * @throws SAXException
+   */
+  public void unparsedEntityDecl(
+          String name, String publicId, String systemId, String notationName)
+            throws SAXException
+  {
+
+    if (null != m_resultDTDHandler)
+      m_resultDTDHandler.unparsedEntityDecl(name, publicId, systemId,
+                                            notationName);
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Default implementation of ContentHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Receive a Locator object for document events.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass if they wish to store the locator for use
+   * with other document events.</p>
+   *
+   * @param locator A locator for all SAX document events.
+   * @see org.xml.sax.ContentHandler#setDocumentLocator
+   * @see org.xml.sax.Locator
+   */
+  public void setDocumentLocator(Locator locator)
+  {
+    try
+    {
+      if (null == m_resultContentHandler)
+        createResultContentHandler(m_result);
+    }
+    catch (TransformerException te)
+    {
+      throw new org.apache.xml.utils.WrappedRuntimeException(te);
+    }
+
+    m_resultContentHandler.setDocumentLocator(locator);
+  }
+
+  /**
+   * Receive notification of the beginning of the document.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the beginning
+   * of a document (such as allocating the root node of a tree or
+   * creating an output file).</p>
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startDocument
+   *
+   * @throws SAXException
+   */
+  public void startDocument() throws SAXException
+  {
+
+    try
+    {
+      if (null == m_resultContentHandler)
+        createResultContentHandler(m_result);
+    }
+    catch (TransformerException te)
+    {
+      throw new SAXException(te.getMessage(), te);
+    }
+
+    // Reset for multiple transforms with this transformer.
+    m_flushedStartDoc = false;
+    m_foundFirstElement = false;
+  }
+  
+  boolean m_flushedStartDoc = false;
+  
+  protected final void flushStartDoc()
+     throws SAXException
+  {
+    if(!m_flushedStartDoc)
+    {
+      if (m_resultContentHandler == null)
+      {
+        try
+        {
+          createResultContentHandler(m_result);
+        }
+        catch(TransformerException te)
+        {
+            throw new SAXException(te);
+        }
+      }
+      m_resultContentHandler.startDocument();
+      m_flushedStartDoc = true;
+    }
+  }
+
+  /**
+   * Receive notification of the end of the document.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the end
+   * of a document (such as finalising a tree or closing an output
+   * file).</p>
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endDocument
+   *
+   * @throws SAXException
+   */
+  public void endDocument() throws SAXException
+  {
+    flushStartDoc();
+    m_resultContentHandler.endDocument();
+  }
+
+  /**
+   * Receive notification of the start of a Namespace mapping.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the start of
+   * each Namespace prefix scope (such as storing the prefix mapping).</p>
+   *
+   * @param prefix The Namespace prefix being declared.
+   * @param uri The Namespace URI mapped to the prefix.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startPrefixMapping
+   *
+   * @throws SAXException
+   */
+  public void startPrefixMapping(String prefix, String uri)
+          throws SAXException
+  {
+    flushStartDoc();
+    m_resultContentHandler.startPrefixMapping(prefix, uri);
+  }
+
+  /**
+   * Receive notification of the end of a Namespace mapping.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the end of
+   * each prefix mapping.</p>
+   *
+   * @param prefix The Namespace prefix being declared.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endPrefixMapping
+   *
+   * @throws SAXException
+   */
+  public void endPrefixMapping(String prefix) throws SAXException
+  {
+    flushStartDoc();
+    m_resultContentHandler.endPrefixMapping(prefix);
+  }
+
+  /**
+   * Receive notification of the start of an element.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the start of
+   * each element (such as allocating a new tree node or writing
+   * output to a file).</p>
+   *
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param qName The qualified name (with prefix), or the
+   *        empty string if qualified names are not available.
+   * @param attributes The specified or defaulted attributes.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startElement
+   *
+   * @throws SAXException
+   */
+  public void startElement(
+          String uri, String localName, String qName, Attributes attributes)
+            throws SAXException
+  {
+
+    if (!m_foundFirstElement && null != m_serializer)
+    {
+      m_foundFirstElement = true;
+
+      Serializer newSerializer;
+
+      try
+      {
+        newSerializer = SerializerSwitcher.switchSerializerIfHTML(uri,
+                localName, m_outputFormat.getProperties(), m_serializer);
+      }
+      catch (TransformerException te)
+      {
+        throw new SAXException(te);
+      }
+
+      if (newSerializer != m_serializer)
+      {
+        try
+        {
+          m_resultContentHandler = newSerializer.asContentHandler();
+        }
+        catch (IOException ioe)  // why?
+        {
+          throw new SAXException(ioe);
+        }
+
+        if (m_resultContentHandler instanceof DTDHandler)
+          m_resultDTDHandler = (DTDHandler) m_resultContentHandler;
+
+        if (m_resultContentHandler instanceof LexicalHandler)
+          m_resultLexicalHandler = (LexicalHandler) m_resultContentHandler;
+
+        m_serializer = newSerializer;
+      }
+    }
+    flushStartDoc();
+    m_resultContentHandler.startElement(uri, localName, qName, attributes);
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the end of
+   * each element (such as finalising a tree node or writing
+   * output to a file).</p>
+   *
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param qName The qualified name (with prefix), or the
+   *        empty string if qualified names are not available.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endElement
+   *
+   * @throws SAXException
+   */
+  public void endElement(String uri, String localName, String qName)
+          throws SAXException
+  {
+    m_resultContentHandler.endElement(uri, localName, qName);
+  }
+
+  /**
+   * Receive notification of character data inside an element.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method to take specific actions for each chunk of character data
+   * (such as adding the data to a node or buffer, or printing it to
+   * a file).</p>
+   *
+   * @param ch The characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#characters
+   *
+   * @throws SAXException
+   */
+  public void characters(char ch[], int start, int length) throws SAXException
+  {
+    flushStartDoc();
+    m_resultContentHandler.characters(ch, start, length);
+  }
+
+  /**
+   * Receive notification of ignorable whitespace in element content.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method to take specific actions for each chunk of ignorable
+   * whitespace (such as adding data to a node or buffer, or printing
+   * it to a file).</p>
+   *
+   * @param ch The whitespace characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#ignorableWhitespace
+   *
+   * @throws SAXException
+   */
+  public void ignorableWhitespace(char ch[], int start, int length)
+          throws SAXException
+  {
+    m_resultContentHandler.ignorableWhitespace(ch, start, length);
+  }
+
+  /**
+   * Receive notification of a processing instruction.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions for each
+   * processing instruction, such as setting status variables or
+   * invoking other methods.</p>
+   *
+   * @param target The processing instruction target.
+   * @param data The processing instruction data, or null if
+   *             none is supplied.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   *
+   * @throws SAXException
+   */
+  public void processingInstruction(String target, String data)
+          throws SAXException
+  {
+    flushStartDoc();
+    m_resultContentHandler.processingInstruction(target, data);
+  }
+
+  /**
+   * Receive notification of a skipped entity.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions for each
+   * processing instruction, such as setting status variables or
+   * invoking other methods.</p>
+   *
+   * @param name The name of the skipped entity.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   *
+   * @throws SAXException
+   */
+  public void skippedEntity(String name) throws SAXException
+  {
+    flushStartDoc();
+    m_resultContentHandler.skippedEntity(name);
+  }
+
+  /**
+   * Report the start of DTD declarations, if any.
+   *
+   * <p>Any declarations are assumed to be in the internal subset
+   * unless otherwise indicated by a {@link #startEntity startEntity}
+   * event.</p>
+   *
+   * <p>Note that the start/endDTD events will appear within
+   * the start/endDocument events from ContentHandler and
+   * before the first startElement event.</p>
+   *
+   * @param name The document type name.
+   * @param publicId The declared public identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @param systemId The declared system identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @throws SAXException The application may raise an
+   *            exception.
+   * @see #endDTD
+   * @see #startEntity
+   */
+  public void startDTD(String name, String publicId, String systemId)
+          throws SAXException
+  {
+    flushStartDoc();
+    if (null != m_resultLexicalHandler)
+      m_resultLexicalHandler.startDTD(name, publicId, systemId);
+  }
+
+  /**
+   * Report the end of DTD declarations.
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #startDTD
+   */
+  public void endDTD() throws SAXException
+  {
+    if (null != m_resultLexicalHandler)
+      m_resultLexicalHandler.endDTD();
+  }
+
+  /**
+   * Report the beginning of an entity in content.
+   *
+   * <p><strong>NOTE:</entity> entity references in attribute
+   * values -- and the start and end of the document entity --
+   * are never reported.</p>
+   *
+   * <p>The start and end of the external DTD subset are reported
+   * using the pseudo-name "[dtd]".  All other events must be
+   * properly nested within start/end entity events.</p>
+   *
+   * <p>Note that skipped entities will be reported through the
+   * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
+   * event, which is part of the ContentHandler interface.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @throws SAXException The application may raise an exception.
+   * @see #endEntity
+   * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
+   * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
+   */
+  public void startEntity(String name) throws SAXException
+  {
+    if (null != m_resultLexicalHandler)
+      m_resultLexicalHandler.startEntity(name);
+  }
+
+  /**
+   * Report the end of an entity.
+   *
+   * @param name The name of the entity that is ending.
+   * @throws SAXException The application may raise an exception.
+   * @see #startEntity
+   */
+  public void endEntity(String name) throws SAXException
+  {
+    if (null != m_resultLexicalHandler)
+      m_resultLexicalHandler.endEntity(name);
+  }
+
+  /**
+   * Report the start of a CDATA section.
+   *
+   * <p>The contents of the CDATA section will be reported through
+   * the regular {@link org.xml.sax.ContentHandler#characters
+   * characters} event.</p>
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #endCDATA
+   */
+  public void startCDATA() throws SAXException
+  {
+    if (null != m_resultLexicalHandler)
+      m_resultLexicalHandler.startCDATA();
+  }
+
+  /**
+   * Report the end of a CDATA section.
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #startCDATA
+   */
+  public void endCDATA() throws SAXException
+  {
+    if (null != m_resultLexicalHandler)
+      m_resultLexicalHandler.endCDATA();
+  }
+
+  /**
+   * Report an XML comment anywhere in the document.
+   *
+   * <p>This callback will be used for comments inside or outside the
+   * document element, including comments in the external DTD
+   * subset (if read).</p>
+   *
+   * @param ch An array holding the characters in the comment.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   * @throws SAXException The application may raise an exception.
+   */
+  public void comment(char ch[], int start, int length) throws SAXException
+  {
+    flushStartDoc();
+    if (null != m_resultLexicalHandler)
+      m_resultLexicalHandler.comment(ch, start, length);
+  }
+  
+  // Implement DeclHandler
+  
+  /**
+     * Report an element type declaration.
+     *
+     * <p>The content model will consist of the string "EMPTY", the
+     * string "ANY", or a parenthesised group, optionally followed
+     * by an occurrence indicator.  The model will be normalized so
+     * that all whitespace is removed,and will include the enclosing
+     * parentheses.</p>
+     *
+     * @param name The element type name.
+     * @param model The content model as a normalized string.
+     * @exception SAXException The application may raise an exception.
+     */
+    public void elementDecl (String name, String model)
+        throws SAXException
+    {
+                        if (null != m_resultDeclHandler)
+                                m_resultDeclHandler.elementDecl(name, model);
+    }
+
+
+    /**
+     * Report an attribute type declaration.
+     *
+     * <p>Only the effective (first) declaration for an attribute will
+     * be reported.  The type will be one of the strings "CDATA",
+     * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
+     * "ENTITIES", or "NOTATION", or a parenthesized token group with 
+     * the separator "|" and all whitespace removed.</p>
+     *
+     * @param eName The name of the associated element.
+     * @param aName The name of the attribute.
+     * @param type A string representing the attribute type.
+     * @param valueDefault A string representing the attribute default
+     *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
+     *        none of these applies.
+     * @param value A string representing the attribute's default value,
+     *        or null if there is none.
+     * @exception SAXException The application may raise an exception.
+     */
+    public void attributeDecl (String eName,
+                                        String aName,
+                                        String type,
+                                        String valueDefault,
+                                        String value)
+        throws SAXException
+    {
+      if (null != m_resultDeclHandler)
+                                m_resultDeclHandler.attributeDecl(eName, aName, type, valueDefault, value);
+    }
+
+
+    /**
+     * Report an internal entity declaration.
+     *
+     * <p>Only the effective (first) declaration for each entity
+     * will be reported.</p>
+     *
+     * @param name The name of the entity.  If it is a parameter
+     *        entity, the name will begin with '%'.
+     * @param value The replacement text of the entity.
+     * @exception SAXException The application may raise an exception.
+     * @see #externalEntityDecl
+     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+     */
+    public void internalEntityDecl (String name, String value)
+        throws SAXException
+    {
+      if (null != m_resultDeclHandler)
+                                m_resultDeclHandler.internalEntityDecl(name, value); 
+    }
+
+
+    /**
+     * Report a parsed external entity declaration.
+     *
+     * <p>Only the effective (first) declaration for each entity
+     * will be reported.</p>
+     *
+     * @param name The name of the entity.  If it is a parameter
+     *        entity, the name will begin with '%'.
+     * @param publicId The declared public identifier of the entity, or
+     *        null if none was declared.
+     * @param systemId The declared system identifier of the entity.
+     * @exception SAXException The application may raise an exception.
+     * @see #internalEntityDecl
+     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+     */
+    public void externalEntityDecl (String name, String publicId,
+                                             String systemId)
+        throws SAXException
+    {
+      if (null != m_resultDeclHandler)
+                                m_resultDeclHandler.externalEntityDecl(name, publicId, systemId);
+    }
+  
+  /**
+   * This is null unless we own the stream.
+   */
+  private java.io.FileOutputStream m_outputStream = null;
+
+  /** The content handler where result events will be sent. */
+  private ContentHandler m_resultContentHandler;
+
+  /** The lexical handler where result events will be sent. */
+  private LexicalHandler m_resultLexicalHandler;
+
+  /** The DTD handler where result events will be sent. */
+  private DTDHandler m_resultDTDHandler;
+  
+  /** The Decl handler where result events will be sent. */
+  private DeclHandler m_resultDeclHandler;
+
+  /** The Serializer, which may or may not be null. */
+  private Serializer m_serializer;
+
+  /** The Result object. */
+  private Result m_result;
+
+  /**
+   * The system ID, which is unused, but must be returned to fullfill the
+   *  TransformerHandler interface.
+   */
+  private String m_systemID;
+
+  /**
+   * The parameters, which is unused, but must be returned to fullfill the
+   *  Transformer interface.
+   */
+  private Hashtable m_params;
+
+  /** The error listener for TrAX errors and warnings. */
+  private ErrorListener m_errorListener =
+    new org.apache.xml.utils.DefaultErrorHandler(false);
+
+  /**
+   * The URIResolver, which is unused, but must be returned to fullfill the
+   *  TransformerHandler interface.
+   */
+  URIResolver m_URIResolver;
+
+  /** The output properties. */
+  private OutputProperties m_outputFormat;
+
+  /** Flag to set if we've found the first element, so we can tell if we have 
+   *  to check to see if we should create an HTML serializer.      */
+  boolean m_foundFirstElement;
+  
+  /**
+   * State of the secure processing feature.
+   */
+  private boolean m_isSecureProcessing = false;
+}
diff --git a/src/main/java/org/apache/xalan/transformer/TransformerImpl.java b/src/main/java/org/apache/xalan/transformer/TransformerImpl.java
new file mode 100644
index 0000000..cf550b9
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/TransformerImpl.java
@@ -0,0 +1,3271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TransformerImpl.java 475979 2006-11-16 23:32:48Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.xalan.extensions.ExtensionsTable;
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+import org.apache.xml.serializer.Method;
+import org.apache.xml.serializer.Serializer;
+import org.apache.xml.serializer.SerializerFactory;
+import org.apache.xalan.templates.AVT;
+import org.apache.xalan.templates.Constants;
+import org.apache.xalan.templates.ElemAttributeSet;
+import org.apache.xalan.templates.ElemForEach;
+import org.apache.xalan.templates.ElemSort;
+import org.apache.xalan.templates.ElemTemplate;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.ElemTextLiteral;
+import org.apache.xalan.templates.ElemVariable;
+import org.apache.xalan.templates.OutputProperties;
+import org.apache.xalan.templates.Stylesheet;
+import org.apache.xalan.templates.StylesheetComposed;
+import org.apache.xalan.templates.StylesheetRoot;
+import org.apache.xalan.templates.XUnresolvedVariable;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.dtm.DTMWSFilter;
+import org.apache.xml.serializer.ToSAXHandler;
+import org.apache.xml.serializer.ToTextStream;
+import org.apache.xml.serializer.ToXMLSAXHandler;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xml.utils.BoolStack;
+import org.apache.xml.utils.DOMBuilder;
+import org.apache.xml.utils.NodeVector;
+import org.apache.xml.utils.ObjectPool;
+import org.apache.xml.utils.ObjectStack;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.SAXSourceLocator;
+import org.apache.xml.utils.ThreadControllerWrapper;
+import org.apache.xpath.Arg;
+import org.apache.xpath.ExtensionsProvider;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.functions.FuncExtFunction;
+import org.apache.xpath.objects.XObject;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * This class implements the
+ * {@link javax.xml.transform.Transformer} interface, and is the core
+ * representation of the transformation execution.</p>
+ * @xsl.usage advanced
+ */
+public class TransformerImpl extends Transformer
+        implements Runnable, DTMWSFilter, ExtensionsProvider, org.apache.xml.serializer.SerializerTrace
+{
+
+  // Synch object to gaurd against setting values from the TrAX interface 
+  // or reentry while the transform is going on.
+
+  /** NEEDSDOC Field m_reentryGuard          */
+  private Boolean m_reentryGuard = new Boolean(true);
+
+  /**
+   * This is null unless we own the stream.
+   */
+  private java.io.FileOutputStream m_outputStream = null;
+
+  /** The thread that the transformer is running on. */
+  private Thread m_transformThread;
+
+  /** The base URL of the source tree. */
+  private String m_urlOfSource = null;
+
+  /** The Result object at the start of the transform, if any. */
+  private Result m_outputTarget = null;
+
+  /**
+   * The output format object set by the user.  May be null.
+   */
+  private OutputProperties m_outputFormat;
+
+
+  /**
+   * The content handler for the source input tree.
+   */
+  ContentHandler m_inputContentHandler;
+
+  /**
+   * The content handler for the result tree.
+   */
+  private ContentHandler m_outputContentHandler = null;
+
+  //  /*
+  //   * Use member variable to store param variables as they're
+  //   * being created, use member variable so we don't
+  //   * have to create a new vector every time.
+  //   */
+  //  private Vector m_newVars = new Vector();
+
+  /**
+   * A pool of ResultTreeHandlers, for serialization of a subtree to text.
+   *  Please note that each of these also holds onto a Text Serializer.  
+   */
+  private ObjectPool m_textResultHandlerObjectPool =
+    new ObjectPool(ToTextStream.class);
+
+  /**
+   * Related to m_textResultHandlerObjectPool, this is a pool of
+   * StringWriters, which are passed to the Text Serializers.
+   * (I'm not sure if this is really needed any more.  -sb)      
+   */
+  private ObjectPool m_stringWriterObjectPool =
+    new ObjectPool(StringWriter.class);
+
+  /**
+   * A static text format object, which can be used over and
+   * over to create the text serializers.    
+   */
+  private OutputProperties m_textformat = new OutputProperties(Method.TEXT);
+
+  // Commenteded out in response to problem reported by 
+  // Nicola Brown <Nicola.Brown@jacobsrimell.com>
+  //  /**
+  //   * Flag to let us know if an exception should be reported inside the 
+  //   * postExceptionFromThread method.  This is needed if the transform is 
+  //   * being generated from SAX events, and thus there is no central place 
+  //   * to report the exception from.  (An exception is usually picked up in 
+  //   * the main thread from the transform thread in {@link #transform(Source source)} 
+  //   * from {@link #getExceptionThrown()}. )
+  //   */
+  //  private boolean m_reportInPostExceptionFromThread = false;
+
+  /**
+   * A node vector used as a stack to track the current
+   * ElemTemplateElement.  Needed for the
+   * org.apache.xalan.transformer.TransformState interface,
+   * so a tool can discover the calling template. Note the use of an array 
+   * for this limits the recursion depth to 4K.
+   */
+  ObjectStack m_currentTemplateElements 
+      = new ObjectStack(XPathContext.RECURSIONLIMIT);
+  
+  /** The top of the currentTemplateElements stack. */
+  //int m_currentTemplateElementsTop = 0;
+
+  /**
+   * A node vector used as a stack to track the current
+   * ElemTemplate that was matched.
+   * Needed for the
+   * org.apache.xalan.transformer.TransformState interface,
+   * so a tool can discover the matched template
+   */
+  Stack m_currentMatchTemplates = new Stack();
+
+  /**
+   * A node vector used as a stack to track the current
+   * node that was matched.
+   * Needed for the
+   * org.apache.xalan.transformer.TransformState interface,
+   * so a tool can discover the matched
+   * node. 
+   */
+  NodeVector m_currentMatchedNodes = new NodeVector();
+
+  /**
+   * The root of a linked set of stylesheets.
+   */
+  private StylesheetRoot m_stylesheetRoot = null;
+
+  /**
+   * If this is set to true, do not warn about pattern
+   * match conflicts.
+   */
+  private boolean m_quietConflictWarnings = true;
+
+  /**
+   * The liason to the XML parser, so the XSL processor
+   * can handle included files, and the like, and do the
+   * initial parse of the XSL document.
+   */
+  private XPathContext m_xcontext;
+
+  /**
+   * Output handler to bottleneck SAX events.
+   */
+  private SerializationHandler m_serializationHandler;  
+
+  /** The key manager, which manages xsl:keys. */
+  private KeyManager m_keyManager = new KeyManager();
+
+  /**
+   * Stack for the purposes of flagging infinite recursion with
+   * attribute sets.
+   */
+  Stack m_attrSetStack = null;
+
+  /**
+   * The table of counters for xsl:number support.
+   */
+  CountersTable m_countersTable = null;
+
+  /**
+   * Is > 0 when we're processing a for-each.
+   */
+  BoolStack m_currentTemplateRuleIsNull = new BoolStack();
+
+  /**
+   * Keeps track of the result delivered by any EXSLT <code>func:result</code>
+   * instruction that has been executed for the currently active EXSLT
+   * <code>func:function</code>
+   */
+  ObjectStack m_currentFuncResult = new ObjectStack();
+  
+  /**
+   * The message manager, which manages error messages, warning
+   * messages, and other types of message events.   
+   */
+  private MsgMgr m_msgMgr;
+
+  /**
+   * The flag for the setting of the optimize feature;
+   * This flag should have the same value as the FEATURE_OPTIMIZE feature
+   * which is set by the TransformerFactory.setAttribut() method before a
+   * Transformer is created
+   */    
+  private boolean m_optimizer = true;
+
+  /**
+   * The flag for the setting of the incremental feature;
+   * This flag should have the same value as the FEATURE_INCREMENTAL feature
+   * which is set by the TransformerFactory.setAttribut() method before a
+   * Transformer is created
+   */    
+  private boolean m_incremental = false;
+
+  /**
+   * The flag for the setting of the source_location feature;
+   * This flag should have the same value as the FEATURE_SOURCE_LOCATION feature
+   * which is set by the TransformerFactory.setAttribut() method before a
+   * Transformer is created
+   */  
+  private boolean m_source_location = false;
+    
+  /**
+   * The SAX error handler, where errors and warnings are sent.
+   */
+  private ErrorListener m_errorHandler =
+    new org.apache.xml.utils.DefaultErrorHandler(false);
+
+  /**
+   * If the transform thread throws an exception, the exception needs to
+   * be stashed away so that the main thread can pass it on to the
+   * client. 
+   */
+  private Exception m_exceptionThrown = null;
+
+  /**
+   * This is needed for support of setSourceTreeDocForThread(Node doc),
+   * which must be called in order for the transform thread's run
+   * method to obtain the root of the source tree to be transformed.     
+   */
+  private int m_doc;
+
+  /** Flag to to tell if the tranformer needs to be reset. */
+  private boolean m_hasBeenReset = false;
+
+  /** NEEDSDOC Field m_shouldReset          */
+  private boolean m_shouldReset = true;
+
+  /**
+   * A stack of current template modes.
+   */
+  private Stack m_modes = new Stack();
+
+  //==========================================================
+  // SECTION: Constructor
+  //==========================================================
+
+  /**
+   * Construct a TransformerImpl.
+   *
+   * @param stylesheet The root of the stylesheet tree.
+   */
+  public TransformerImpl(StylesheetRoot stylesheet)
+   // throws javax.xml.transform.TransformerException    
+  {
+    m_optimizer = stylesheet.getOptimizer();
+    m_incremental = stylesheet.getIncremental();
+    m_source_location = stylesheet.getSource_location();  	
+    setStylesheet(stylesheet);
+    XPathContext xPath = new XPathContext(this);
+    xPath.setIncremental(m_incremental);
+    xPath.getDTMManager().setIncremental(m_incremental);
+    xPath.setSource_location(m_source_location);
+    xPath.getDTMManager().setSource_location(m_source_location);
+    
+    if (stylesheet.isSecureProcessing())
+      xPath.setSecureProcessing(true);
+    
+    setXPathContext(xPath);
+    getXPathContext().setNamespaceContext(stylesheet);
+  }
+  
+  // ================ ExtensionsTable ===================
+
+  /**
+   * The table of ExtensionHandlers.
+   */
+  private ExtensionsTable m_extensionsTable = null;
+
+  /**
+   * Get the extensions table object. 
+   *
+   * @return The extensions table.
+   */
+  public ExtensionsTable getExtensionsTable()
+  {
+    return m_extensionsTable;
+  }
+
+  /**
+   * If the stylesheet contains extensions, set the extensions table object.
+   *
+   *
+   * @param sroot The stylesheet.
+   * @throws javax.xml.transform.TransformerException
+   */
+  void setExtensionsTable(StylesheetRoot sroot)
+       throws javax.xml.transform.TransformerException
+  {
+    try
+    {
+      if (sroot.getExtensions() != null)
+        m_extensionsTable = new ExtensionsTable(sroot);
+    }
+    catch (javax.xml.transform.TransformerException te)
+    {te.printStackTrace();}
+  }
+  
+  //== Implementation of the XPath ExtensionsProvider interface.
+  
+  public boolean functionAvailable(String ns, String funcName)
+          throws javax.xml.transform.TransformerException
+  {
+    return getExtensionsTable().functionAvailable(ns, funcName);
+  }
+  
+  public boolean elementAvailable(String ns, String elemName)
+          throws javax.xml.transform.TransformerException
+  {
+    return getExtensionsTable().elementAvailable(ns, elemName);   
+  }
+   
+  public Object extFunction(String ns, String funcName, 
+                            Vector argVec, Object methodKey)
+            throws javax.xml.transform.TransformerException
+  {//System.out.println("TransImpl.extFunction() " + ns + " " + funcName +" " + getExtensionsTable());
+    return getExtensionsTable().extFunction(ns, funcName, 
+                                        argVec, methodKey,
+                                        getXPathContext().getExpressionContext());   
+  }
+
+  public Object extFunction(FuncExtFunction extFunction, Vector argVec)
+            throws javax.xml.transform.TransformerException
+  {
+    return getExtensionsTable().extFunction(extFunction, argVec,
+                                            getXPathContext().getExpressionContext());   
+  }
+  
+  //=========================
+
+  /**
+   * Reset the state.  This needs to be called after a process() call
+   * is invoked, if the processor is to be used again.
+   */
+  public void reset()
+  {
+
+    if (!m_hasBeenReset && m_shouldReset)
+    {
+      m_hasBeenReset = true;
+
+      if (this.m_outputStream != null)
+      {
+        try
+        {
+          m_outputStream.close();
+        }
+        catch (java.io.IOException ioe){}
+      }
+
+      m_outputStream = null;
+
+      // I need to look more carefully at which of these really
+      // needs to be reset.
+      m_countersTable = null;
+
+      m_xcontext.reset();
+      
+      m_xcontext.getVarStack().reset();
+      resetUserParameters();
+      
+
+      m_currentTemplateElements.removeAllElements();     
+      m_currentMatchTemplates.removeAllElements();
+      m_currentMatchedNodes.removeAllElements();
+      
+      m_serializationHandler = null;      
+      m_outputTarget = null;
+      m_keyManager = new KeyManager();
+      m_attrSetStack = null;
+      m_countersTable = null;
+      m_currentTemplateRuleIsNull = new BoolStack();
+      // m_xmlSource = null; // android-removed
+      m_doc = DTM.NULL;
+      // m_isTransformDone = false; // android-removed
+      m_transformThread = null;
+
+      // m_inputContentHandler = null;
+      // For now, reset the document cache each time.
+      m_xcontext.getSourceTreeManager().reset();
+    }
+
+    //    m_reportInPostExceptionFromThread = false;
+  }
+
+  // ========= Transformer Interface Implementation ==========
+
+  /**
+   * Get the thread that the transform process is on.
+   *
+   * @return The thread that the transform process is on, or null.
+   * @xsl.usage internal
+   */
+  public Thread getTransformThread()
+  {
+    return m_transformThread;
+  }
+
+  /**
+   * Get the thread that the transform process is on.
+   *
+   * @param t The transform thread, may be null.
+   * @xsl.usage internal
+   */
+  public void setTransformThread(Thread t)
+  {
+    m_transformThread = t;
+  }
+
+  /** NEEDSDOC Field m_hasTransformThreadErrorCatcher          */
+  private boolean m_hasTransformThreadErrorCatcher = false;
+
+  /**
+   * Return true if the transform was initiated from the transform method,
+   * otherwise it was probably done from a pure parse events.
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public boolean hasTransformThreadErrorCatcher()
+  {
+    return m_hasTransformThreadErrorCatcher;
+  }
+        
+        /**
+   * Process the source tree to SAX parse events.
+   * @param source  The input for the source tree.
+   *
+   * @throws TransformerException
+   */
+  public void transform(Source source) throws TransformerException
+  {
+                transform(source, true); 
+        }
+
+  /**
+   * Process the source tree to SAX parse events.
+   * @param source  The input for the source tree.
+   * @param shouldRelease  Flag indicating whether to release DTMManager.
+   *
+   * @throws TransformerException
+   */
+  public void transform(Source source, boolean shouldRelease) throws TransformerException
+  {
+
+    try
+    {
+        
+      // Patch for bugzilla #13863.  If we don't reset the namespaceContext
+      // then we will get a NullPointerException if transformer is reused 
+      // (for stylesheets that use xsl:key).  Not sure if this should go 
+      // here or in reset(). -is  
+      if(getXPathContext().getNamespaceContext() == null){
+         getXPathContext().setNamespaceContext(getStylesheet());
+      }
+      String base = source.getSystemId();
+      
+      // If no systemID of the source, use the base of the stylesheet.
+      if(null == base)
+      {
+        base = m_stylesheetRoot.getBaseIdentifier();
+      }
+
+      // As a last resort, use the current user dir.
+      if(null == base)
+      {
+        String currentDir = "";
+        try {
+          currentDir = System.getProperty("user.dir");
+        }
+        catch (SecurityException se) {}// user.dir not accessible from applet
+              
+        if (currentDir.startsWith(java.io.File.separator))
+          base = "file://" + currentDir;
+        else
+          base = "file:///" + currentDir;
+        
+        base = base + java.io.File.separatorChar
+               + source.getClass().getName();
+      }
+      setBaseURLOfSource(base);
+      DTMManager mgr = m_xcontext.getDTMManager();
+      /*
+       * According to JAXP1.2, new SAXSource()/StreamSource()
+       * should create an empty input tree, with a default root node. 
+       * new DOMSource()creates an empty document using DocumentBuilder.
+       * newDocument(); Use DocumentBuilder.newDocument() for all 3 situations,
+       * since there is no clear spec. how to create an empty tree when
+       * both SAXSource() and StreamSource() are used.
+       */
+      if ((source instanceof StreamSource && source.getSystemId()==null &&
+         ((StreamSource)source).getInputStream()==null &&
+         ((StreamSource)source).getReader()==null)||
+         (source instanceof SAXSource &&
+         ((SAXSource)source).getInputSource()==null &&
+         ((SAXSource)source).getXMLReader()==null )||
+         (source instanceof DOMSource && ((DOMSource)source).getNode()==null)){
+        try {
+          DocumentBuilderFactory builderF = 
+                   DocumentBuilderFactory.newInstance();
+          DocumentBuilder builder = builderF.newDocumentBuilder();
+          String systemID = source.getSystemId();
+          source = new DOMSource(builder.newDocument());
+
+          // Copy system ID from original, empty Source to new Source
+          if (systemID != null) {
+            source.setSystemId(systemID);
+          }
+        } catch (ParserConfigurationException e) {
+          fatalError(e);
+        }           
+      }
+      DTM dtm = mgr.getDTM(source, false, this, true, true);
+      dtm.setDocumentBaseURI(base);
+      
+      boolean hardDelete = true;  // %REVIEW% I have to think about this. -sb
+
+      try
+      {
+      	// NOTE: This will work because this is _NOT_ a shared DTM, and thus has
+      	// only a single Document node. If it could ever be an RTF or other
+      	// shared DTM, look at dtm.getDocumentRoot(nodeHandle).
+        this.transformNode(dtm.getDocument());
+      }
+      finally
+      {
+        if (shouldRelease)
+          mgr.release(dtm, hardDelete);
+      }
+
+      // Kick off the parse.  When the ContentHandler gets 
+      // the startDocument event, it will call transformNode( node ).
+      // reader.parse( xmlSource );
+      // This has to be done to catch exceptions thrown from 
+      // the transform thread spawned by the STree handler.
+      Exception e = getExceptionThrown();
+
+      if (null != e)
+      {
+        if (e instanceof javax.xml.transform.TransformerException)
+        {
+          throw (javax.xml.transform.TransformerException) e;
+        }
+        else if (e instanceof org.apache.xml.utils.WrappedRuntimeException)
+        {
+          fatalError(
+              ((org.apache.xml.utils.WrappedRuntimeException) e).getException());
+        }
+        else
+        {
+          throw new javax.xml.transform.TransformerException(e);
+        }
+      }
+      else if (null != m_serializationHandler)
+      {
+        m_serializationHandler.endDocument();
+      }
+    }
+    catch (org.apache.xml.utils.WrappedRuntimeException wre)
+    {
+      Throwable throwable = wre.getException();
+
+      while (throwable
+             instanceof org.apache.xml.utils.WrappedRuntimeException)
+      {
+        throwable =
+          ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
+      }
+
+      fatalError(throwable);
+    }
+
+    // Patch attributed to David Eisenberg <david@catcode.com>
+    catch (org.xml.sax.SAXParseException spe)
+    {
+      fatalError(spe);
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      m_errorHandler.fatalError(new TransformerException(se));
+    }
+    finally
+    {
+      m_hasTransformThreadErrorCatcher = false;
+
+      // This looks to be redundent to the one done in TransformNode.
+      reset();
+    }
+  }
+  
+  private void fatalError(Throwable throwable) throws TransformerException
+  {
+    if (throwable instanceof org.xml.sax.SAXParseException)
+      m_errorHandler.fatalError(new TransformerException(throwable.getMessage(),new SAXSourceLocator((org.xml.sax.SAXParseException)throwable)));
+    else
+      m_errorHandler.fatalError(new TransformerException(throwable));
+    
+  }
+
+  /**
+   * Get the base URL of the source.
+   *
+   *
+   * NEEDSDOC @param base
+   * @return The base URL of the source tree, or null.
+   */
+  public void setBaseURLOfSource(String base)
+  {
+    m_urlOfSource = base;
+  }
+
+  /**
+   * Get an output property that is in effect for the
+   * transformation.  The property specified may be a property
+   * that was set with setOutputProperty, or it may be a
+   * property specified in the stylesheet.
+   *
+   * NEEDSDOC @param qnameString
+   *
+   * @return The string value of the output property, or null
+   * if no property was found.
+   *
+   * @throws IllegalArgumentException If the property is not supported.
+   *
+   * @see javax.xml.transform.OutputKeys
+   */
+  public String getOutputProperty(String qnameString)
+          throws IllegalArgumentException
+  {
+
+    String value = null;
+    OutputProperties props = getOutputFormat();
+
+    value = props.getProperty(qnameString);
+
+    if (null == value)
+    {
+      if (!OutputProperties.isLegalPropertyKey(qnameString))
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: "
+                                           //+ qnameString);
+    }
+
+    return value;
+  }
+
+  /**
+   * Get the value of a property, without using the default properties.  This
+   * can be used to test if a property has been explicitly set by the stylesheet
+   * or user.
+   *
+   * NEEDSDOC @param qnameString
+   *
+   * @return The value of the property, or null if not found.
+   *
+   * @throws IllegalArgumentException If the property is not supported,
+   * and is not namespaced.
+   */
+  public String getOutputPropertyNoDefault(String qnameString)
+          throws IllegalArgumentException
+  {
+
+    String value = null;
+    OutputProperties props = getOutputFormat();
+
+    value = (String) props.getProperties().get(qnameString);
+
+    if (null == value)
+    {
+      if (!OutputProperties.isLegalPropertyKey(qnameString))
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: "
+                                          // + qnameString);
+    }
+
+    return value;
+  }
+
+  /**
+   * This method is used to set or override the value
+   * of the effective xsl:output attribute values
+   * specified in the stylesheet.
+   * <p>
+   * The recognized standard output properties are:
+   * <ul>
+   * <li>cdata-section-elements
+   * <li>doctype-system
+   * <li>doctype-public
+   * <li>indent
+   * <li>media-type
+   * <li>method
+   * <li>omit-xml-declaration
+   * <li>standalone
+   * <li>version
+   * </ul>
+   * <p>
+   * For example:
+   * <pre>
+   *   tran.setOutputProperty("standalone", "yes");
+   * </pre>
+   * <p>
+   * In the case of the cdata-section-elements property,
+   * the value should be a whitespace separated list of
+   * element names.  The element name is the local name
+   * of the element, if it is in no namespace, or, the URI
+   * in braces followed immediately by the local name
+   * if the element is in that namespace. For example: 
+   * <pre>
+   * tran.setOutputProperty(
+   *   "cdata-section-elements", 
+   *   "elem1 {http://example.uri}elem2 elem3");
+   * </pre>
+   * <p>
+   * The recognized Xalan extension elements are: 
+   * <ul>
+   * <li>content-handler
+   * <li>entities
+   * <li>indent-amount
+   * <li>line-separator
+   * <li>omit-meta-tag
+   * <li>use-url-escaping
+   * </ul>
+   * <p>
+   * These must be in the extension namespace of
+   * "http://xml.apache.org/xalan".  This is accomplished
+   * by putting the namespace URI in braces before the 
+   * property name, for example:
+   * <pre>
+   *   tran.setOutputProperty(
+   *     "{http://xml.apache.org/xalan}line-separator" ,
+   *     "\n");
+   * </pre> 
+   *
+   * @param name The property name.
+   * @param value The requested value for the property.
+   * @throws IllegalArgumentException if the property name is not legal.
+   */
+  public void setOutputProperty(String name, String value)
+          throws IllegalArgumentException
+  {
+
+    synchronized (m_reentryGuard)
+    {
+
+      // Get the output format that was set by the user, otherwise get the 
+      // output format from the stylesheet.
+      if (null == m_outputFormat)
+      {
+        m_outputFormat =
+          (OutputProperties) getStylesheet().getOutputComposed().clone();
+      }
+
+      if (!OutputProperties.isLegalPropertyKey(name))
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
+                                           //+ name);
+
+      m_outputFormat.setProperty(name, value);
+    }
+  }
+
+  /**
+   * Set the output properties for the transformation.  These
+   * properties will override properties set in the templates
+   * with xsl:output.
+   *
+   * <p>If argument to this function is null, any properties
+   * previously set will be removed.</p>
+   *
+   * @param oformat A set of output properties that will be
+   * used to override any of the same properties in effect
+   * for the transformation.
+   *
+   * @see javax.xml.transform.OutputKeys
+   * @see java.util.Properties
+   *
+   * @throws IllegalArgumentException if any of the argument keys are not
+   * recognized and are not namespace qualified.   
+   */
+  public void setOutputProperties(Properties oformat)
+  		throws IllegalArgumentException
+  {
+
+    synchronized (m_reentryGuard)
+    {
+      if (null != oformat)
+      {
+
+        // See if an *explicit* method was set.
+        String method = (String) oformat.get(OutputKeys.METHOD);
+
+        if (null != method)
+          m_outputFormat = new OutputProperties(method);
+        else if(m_outputFormat==null)
+          m_outputFormat = new OutputProperties();
+
+        m_outputFormat.copyFrom(oformat);
+        // copyFrom does not set properties that have been already set, so 
+        // this must be called after, which is a bit in the reverse from 
+        // what one might think.
+        m_outputFormat.copyFrom(m_stylesheetRoot.getOutputProperties());
+      }
+      else {
+        // if oformat is null JAXP says that any props previously set are removed
+        // and we are to revert back to those in the templates object (i.e. Stylesheet).
+        m_outputFormat = null;
+      }
+    }
+  }
+
+  /**
+   * Get a copy of the output properties for the transformation.  These
+   * properties will override properties set in the templates
+   * with xsl:output.
+   *
+   * <p>Note that mutation of the Properties object returned will not
+   * effect the properties that the transformation contains.</p>
+   *
+   * @return  A copy of the set of output properties in effect
+   * for the next transformation.
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public Properties getOutputProperties()
+  {
+    return (Properties) getOutputFormat().getProperties().clone();
+  }
+
+    /**
+     * Create a result ContentHandler from a Result object, based
+     * on the current OutputProperties.
+     *
+     * @param outputTarget Where the transform result should go,
+     * should not be null.
+     *
+     * @return A valid ContentHandler that will create the
+     * result tree when it is fed SAX events.
+     *
+     * @throws TransformerException
+     */
+    public SerializationHandler createSerializationHandler(Result outputTarget)
+            throws TransformerException
+    {
+       SerializationHandler xoh =
+        createSerializationHandler(outputTarget, getOutputFormat());
+       return xoh;
+    }
+
+    /**
+     * Create a ContentHandler from a Result object and an OutputProperties.
+     *
+     * @param outputTarget Where the transform result should go,
+     * should not be null.
+     * @param format The OutputProperties object that will contain
+     * instructions on how to serialize the output.
+     *
+     * @return A valid ContentHandler that will create the
+     * result tree when it is fed SAX events.
+     *
+     * @throws TransformerException
+     */
+    public SerializationHandler createSerializationHandler(
+            Result outputTarget, OutputProperties format)
+              throws TransformerException
+    {
+
+      SerializationHandler xoh;
+
+      // If the Result object contains a Node, then create
+      // a ContentHandler that will add nodes to the input node.
+      org.w3c.dom.Node outputNode = null;
+
+      if (outputTarget instanceof DOMResult)
+      {
+        outputNode = ((DOMResult) outputTarget).getNode();
+        org.w3c.dom.Node nextSibling = ((DOMResult)outputTarget).getNextSibling();
+
+        org.w3c.dom.Document doc;
+        short type;
+
+        if (null != outputNode)
+        {
+          type = outputNode.getNodeType();
+          doc = (org.w3c.dom.Node.DOCUMENT_NODE == type)
+                ? (org.w3c.dom.Document) outputNode
+                : outputNode.getOwnerDocument();
+        }
+        else
+        {
+          boolean isSecureProcessing = m_stylesheetRoot.isSecureProcessing();
+          doc = org.apache.xml.utils.DOMHelper.createDocument(isSecureProcessing);
+          outputNode = doc;
+          type = outputNode.getNodeType();
+
+          ((DOMResult) outputTarget).setNode(outputNode);
+        }
+
+        DOMBuilder handler =
+          (org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE == type)
+          ? new DOMBuilder(doc, (org.w3c.dom.DocumentFragment) outputNode)
+          : new DOMBuilder(doc, outputNode);
+        
+        if (nextSibling != null)
+          handler.setNextSibling(nextSibling);
+        
+          String encoding = format.getProperty(OutputKeys.ENCODING);          
+          xoh = new ToXMLSAXHandler(handler, (LexicalHandler)handler, encoding);
+      }
+      else if (outputTarget instanceof SAXResult)
+      {
+        ContentHandler handler = ((SAXResult) outputTarget).getHandler();
+        
+        if (null == handler)
+           throw new IllegalArgumentException(
+             "handler can not be null for a SAXResult"); 
+             
+        LexicalHandler lexHandler;
+        if (handler instanceof LexicalHandler)     
+            lexHandler = (LexicalHandler)  handler;
+        else
+            lexHandler = null;
+            
+        String encoding = format.getProperty(OutputKeys.ENCODING); 
+        String method = format.getProperty(OutputKeys.METHOD);
+
+        ToXMLSAXHandler toXMLSAXHandler = new ToXMLSAXHandler(handler, lexHandler, encoding);
+        toXMLSAXHandler.setShouldOutputNSAttr(false);
+        xoh = toXMLSAXHandler;   
+
+
+        String publicID = format.getProperty(OutputKeys.DOCTYPE_PUBLIC); 
+        String systemID = format.getProperty(OutputKeys.DOCTYPE_SYSTEM); 
+        if (systemID != null)
+            xoh.setDoctypeSystem(systemID);
+        if (publicID != null)
+            xoh.setDoctypePublic(publicID);
+        
+        if (handler instanceof TransformerClient) {
+            XalanTransformState state = new XalanTransformState();
+            ((TransformerClient)handler).setTransformState(state);
+            ((ToSAXHandler)xoh).setTransformState(state);
+        }
+
+ 
+      }
+
+      // Otherwise, create a ContentHandler that will serialize the
+      // result tree to either a stream or a writer.
+      else if (outputTarget instanceof StreamResult)
+      {
+        StreamResult sresult = (StreamResult) outputTarget;
+
+        try
+        {
+          SerializationHandler serializer =
+            (SerializationHandler) SerializerFactory.getSerializer(format.getProperties());
+
+          if (null != sresult.getWriter())
+            serializer.setWriter(sresult.getWriter());
+          else if (null != sresult.getOutputStream())
+            serializer.setOutputStream(sresult.getOutputStream());
+          else if (null != sresult.getSystemId())
+          {
+            String fileURL = sresult.getSystemId();
+
+            if (fileURL.startsWith("file:///"))
+            {
+              if (fileURL.substring(8).indexOf(":") >0)
+                fileURL = fileURL.substring(8);
+              else
+                fileURL = fileURL.substring(7);
+            }
+            else if (fileURL.startsWith("file:/"))
+            {
+                if (fileURL.substring(6).indexOf(":") >0)
+                    fileURL = fileURL.substring(6);
+                  else
+                    fileURL = fileURL.substring(5);            	
+            }
+
+            m_outputStream = new java.io.FileOutputStream(fileURL);
+
+            serializer.setOutputStream(m_outputStream);
+            
+            xoh = serializer;
+          }
+          else
+            throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!");
+
+          // handler = serializer.asContentHandler();
+
+        //  this.setSerializer(serializer);
+
+          xoh = serializer;  
+        }
+//        catch (UnsupportedEncodingException uee)
+//        {
+//          throw new TransformerException(uee);
+//        }
+        catch (IOException ioe)
+        {
+          throw new TransformerException(ioe);
+        }
+      }
+      else
+      {
+        throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE, new Object[]{outputTarget.getClass().getName()})); //"Can't transform to a Result of type "
+                                       //+ outputTarget.getClass().getName()
+                                       //+ "!");
+      }
+      
+      // before we forget, lets make the created handler hold a reference
+      // to the current TransformImpl object
+      xoh.setTransformer(this);
+
+      SourceLocator srcLocator = getStylesheet();
+      xoh.setSourceLocator(srcLocator);
+      
+      
+      return xoh;
+
+   
+    }
+        
+        /**
+   * Process the source tree to the output result.
+   * @param xmlSource  The input for the source tree.
+   * @param outputTarget The output source target.
+   *
+   * @throws TransformerException
+   */
+  public void transform(Source xmlSource, Result outputTarget)
+          throws TransformerException
+  {
+                transform(xmlSource, outputTarget, true);
+        }
+
+  /**
+   * Process the source tree to the output result.
+   * @param xmlSource  The input for the source tree.
+   * @param outputTarget The output source target.
+   * @param shouldRelease  Flag indicating whether to release DTMManager. 
+   *
+   * @throws TransformerException
+   */
+  public void transform(Source xmlSource, Result outputTarget, boolean shouldRelease)
+          throws TransformerException
+  {
+
+    synchronized (m_reentryGuard)
+    {
+      SerializationHandler xoh = createSerializationHandler(outputTarget);
+      this.setSerializationHandler(xoh);        
+
+      m_outputTarget = outputTarget;
+
+      transform(xmlSource, shouldRelease);
+    }
+  }
+
+  /**
+   * Process the source node to the output result, if the
+   * processor supports the "http://xml.org/trax/features/dom/input"
+   * feature.
+   * %REVIEW% Do we need a Node version of this?
+   * @param node  The input source node, which can be any valid DTM node.
+   * @param outputTarget The output source target.
+   *
+   * @throws TransformerException
+   */
+  public void transformNode(int node, Result outputTarget)
+          throws TransformerException
+  {
+    
+
+    SerializationHandler xoh = createSerializationHandler(outputTarget);
+    this.setSerializationHandler(xoh);
+
+    m_outputTarget = outputTarget;
+
+    transformNode(node);
+  }
+
+  /**
+   * Process the source node to the output result, if the
+   * processor supports the "http://xml.org/trax/features/dom/input"
+   * feature.
+   * %REVIEW% Do we need a Node version of this?
+   * @param node  The input source node, which can be any valid DTM node.
+   *
+   * @throws TransformerException
+   */
+  public void transformNode(int node) throws TransformerException
+  {
+    //dml
+    setExtensionsTable(getStylesheet());
+    // Make sure we're not writing to the same output content handler.
+    synchronized (m_serializationHandler)
+    {
+      m_hasBeenReset = false;
+      
+      XPathContext xctxt = getXPathContext();
+      DTM dtm = xctxt.getDTM(node);
+
+      try
+      {
+        pushGlobalVars(node);
+
+        // ==========
+        // Give the top-level templates a chance to pass information into 
+        // the context (this is mainly for setting up tables for extensions).
+        StylesheetRoot stylesheet = this.getStylesheet();
+        int n = stylesheet.getGlobalImportCount();
+
+        for (int i = 0; i < n; i++)
+        {
+          StylesheetComposed imported = stylesheet.getGlobalImport(i);
+          int includedCount = imported.getIncludeCountComposed();
+
+          for (int j = -1; j < includedCount; j++)
+          {
+            Stylesheet included = imported.getIncludeComposed(j);
+
+            included.runtimeInit(this);
+
+            for (ElemTemplateElement child = included.getFirstChildElem();
+                    child != null; child = child.getNextSiblingElem())
+            {
+              child.runtimeInit(this);
+            }
+          }
+        }
+        // ===========        
+        // System.out.println("Calling applyTemplateToNode - "+Thread.currentThread().getName());
+        DTMIterator dtmIter = new org.apache.xpath.axes.SelfIteratorNoPredicate();
+        dtmIter.setRoot(node, xctxt);
+        xctxt.pushContextNodeList(dtmIter);
+        try
+        {
+          this.applyTemplateToNode(null, null, node);
+        }
+        finally
+        {
+          xctxt.popContextNodeList();
+        }
+        // m_stylesheetRoot.getStartRule().execute(this);
+
+        // System.out.println("Done with applyTemplateToNode - "+Thread.currentThread().getName());
+        if (null != m_serializationHandler)
+        {
+          m_serializationHandler.endDocument();
+        }
+      }
+      catch (Exception se)
+      {
+
+        // System.out.println(Thread.currentThread().getName()+" threw an exception! "
+        //                   +se.getMessage());
+        // If an exception was thrown, we need to make sure that any waiting 
+        // handlers can terminate, which I guess is best done by sending 
+        // an endDocument.
+        
+        // SAXSourceLocator
+        while(se instanceof org.apache.xml.utils.WrappedRuntimeException)
+        {
+          Exception e = ((org.apache.xml.utils.WrappedRuntimeException)se).getException();
+          if(null != e)
+            se = e;
+        }
+        
+        if (null != m_serializationHandler)
+        {
+          try
+          {
+            if(se instanceof org.xml.sax.SAXParseException)
+              m_serializationHandler.fatalError((org.xml.sax.SAXParseException)se);
+            else if(se instanceof TransformerException)
+            {
+              TransformerException te = ((TransformerException)se);
+              SAXSourceLocator sl = new SAXSourceLocator( te.getLocator() );
+              m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(te.getMessage(), sl, te)); 
+            }
+            else
+            {
+              m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(se.getMessage(), new SAXSourceLocator(), se)); 
+            }             
+          }
+          catch (Exception e){}
+        }        
+        
+        if(se instanceof TransformerException)
+        {
+          m_errorHandler.fatalError((TransformerException)se);
+        }
+        else if(se instanceof org.xml.sax.SAXParseException)
+        {
+          m_errorHandler.fatalError(new TransformerException(se.getMessage(), 
+                      new SAXSourceLocator((org.xml.sax.SAXParseException)se), 
+                      se));
+        }
+        else
+        {
+          m_errorHandler.fatalError(new TransformerException(se));
+        }
+        
+      }
+      finally
+      {
+        this.reset();
+      }
+    }
+  }
+
+  /**
+   * Get a SAX2 ContentHandler for the input.
+   *
+   * @return A valid ContentHandler, which should never be null, as
+   * long as getFeature("http://xml.org/trax/features/sax/input")
+   * returns true.
+   */
+  public ContentHandler getInputContentHandler()
+  {
+    return getInputContentHandler(false);
+  }
+
+  /**
+   * Get a SAX2 ContentHandler for the input.
+   *
+   * @param doDocFrag true if a DocumentFragment should be created as
+   * the root, rather than a Document.
+   *
+   * @return A valid ContentHandler, which should never be null, as
+   * long as getFeature("http://xml.org/trax/features/sax/input")
+   * returns true.
+   */
+  public ContentHandler getInputContentHandler(boolean doDocFrag)
+  {
+
+    if (null == m_inputContentHandler)
+    {
+
+      //      if(null == m_urlOfSource && null != m_stylesheetRoot)
+      //        m_urlOfSource = m_stylesheetRoot.getBaseIdentifier();
+      m_inputContentHandler = new TransformerHandlerImpl(this, doDocFrag,
+              m_urlOfSource);
+    }
+
+    return m_inputContentHandler;
+  }
+
+  /**
+   * Set the output properties for the transformation.  These
+   * properties will override properties set in the templates
+   * with xsl:output.
+   *
+   * @param oformat A valid OutputProperties object (which will
+   * not be mutated), or null.
+   */
+  public void setOutputFormat(OutputProperties oformat)
+  {
+    m_outputFormat = oformat;
+  }
+
+  /**
+   * Get the output properties used for the transformation.
+   *
+   * @return the output format that was set by the user,
+   * otherwise the output format from the stylesheet.
+   */
+  public OutputProperties getOutputFormat()
+  {
+
+    // Get the output format that was set by the user, otherwise get the 
+    // output format from the stylesheet.
+    OutputProperties format = (null == m_outputFormat)
+                              ? getStylesheet().getOutputComposed()
+                              : m_outputFormat;
+
+    return format;
+  }
+
+  /**
+   * Set a parameter for the templates.
+   * 
+   * @param name The name of the parameter.
+   * @param namespace The namespace of the parameter.
+   * @param value The value object.  This can be any valid Java object
+   * -- it's up to the processor to provide the proper
+   * coersion to the object, or simply pass it on for use
+   * in extensions.
+   */
+  public void setParameter(String name, String namespace, Object value)
+  {
+
+    VariableStack varstack = getXPathContext().getVarStack();
+    QName qname = new QName(namespace, name);
+    XObject xobject = XObject.create(value, getXPathContext());
+    
+    StylesheetRoot sroot = m_stylesheetRoot;
+    Vector vars = sroot.getVariablesAndParamsComposed();
+    int i = vars.size();
+    while (--i >= 0)
+    {
+      ElemVariable variable = (ElemVariable)vars.elementAt(i);
+      if(variable.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE && 
+         variable.getName().equals(qname))
+      {
+          varstack.setGlobalVariable(i, xobject);
+      }
+    }
+  }
+
+  /** NEEDSDOC Field m_userParams          */
+  Vector m_userParams;
+
+  /**
+   * Set a parameter for the transformation.
+   *
+   * @param name The name of the parameter,
+   *             which may have a namespace URI.
+   * @param value The value object.  This can be any valid Java object
+   * -- it's up to the processor to provide the proper
+   * coersion to the object, or simply pass it on for use
+   * in extensions.
+   */
+  public void setParameter(String name, Object value)
+  {
+    
+    if (value == null) {
+      throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name}));
+    }    
+
+    StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
+
+    try
+    {
+
+      // The first string might be the namespace, or it might be 
+      // the local name, if the namespace is null.
+      String s1 = tokenizer.nextToken();
+      String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+
+      if (null == m_userParams)
+        m_userParams = new Vector();
+
+      if (null == s2)
+      {
+        replaceOrPushUserParam(new QName(s1), XObject.create(value, getXPathContext()));
+        setParameter(s1, null, value);
+      }
+      else
+      {
+        replaceOrPushUserParam(new QName(s1, s2), XObject.create(value, getXPathContext()));
+        setParameter(s2, s1, value);
+      }
+    }
+    catch (java.util.NoSuchElementException nsee)
+    {
+
+      // Should throw some sort of an error.
+    }
+  }
+
+  /**
+   * NEEDSDOC Method replaceOrPushUserParam 
+   *
+   *
+   * NEEDSDOC @param qname
+   * NEEDSDOC @param xval
+   */
+  private void replaceOrPushUserParam(QName qname, XObject xval)
+  {
+
+    int n = m_userParams.size();
+
+    for (int i = n - 1; i >= 0; i--)
+    {
+      Arg arg = (Arg) m_userParams.elementAt(i);
+
+      if (arg.getQName().equals(qname))
+      {
+        m_userParams.setElementAt(new Arg(qname, xval, true), i);
+
+        return;
+      }
+    }
+
+    m_userParams.addElement(new Arg(qname, xval, true));
+  }
+
+  /**
+   * Get a parameter that was explicitly set with setParameter
+   * or setParameters.
+   *
+   *
+   * NEEDSDOC @param name
+   * @return A parameter that has been set with setParameter
+   * or setParameters,
+   * *not* all the xsl:params on the stylesheet (which require
+   * a transformation Source to be evaluated).
+   */
+  public Object getParameter(String name)
+  {
+
+    try
+    {
+
+      // VariableStack varstack = getXPathContext().getVarStack();
+      // The first string might be the namespace, or it might be 
+      // the local name, if the namespace is null.
+      QName qname = QName.getQNameFromString(name);
+
+      if (null == m_userParams)
+        return null;
+
+      int n = m_userParams.size();
+
+      for (int i = n - 1; i >= 0; i--)
+      {
+        Arg arg = (Arg) m_userParams.elementAt(i);
+
+        if (arg.getQName().equals(qname))
+        {
+          return arg.getVal().object();
+        }
+      }
+
+      return null;
+    }
+    catch (java.util.NoSuchElementException nsee)
+    {
+
+      // Should throw some sort of an error.
+      return null;
+    }
+  }
+  
+  /**
+   * Reset parameters that the user specified for the transformation.
+   * Called during transformer.reset() after we have cleared the 
+   * variable stack. We need to make sure that user params are
+   * reset so that the transformer object can be reused. 
+   */
+  private void resetUserParameters()
+  {
+
+    try
+    {
+      
+      if (null == m_userParams)
+        return;
+
+      int n = m_userParams.size();
+      for (int i = n - 1; i >= 0; i--)
+      {
+        Arg arg = (Arg) m_userParams.elementAt(i);
+        QName name = arg.getQName();
+        // The first string might be the namespace, or it might be 
+        // the local name, if the namespace is null.
+        String s1 = name.getNamespace();
+        String s2 = name.getLocalPart();
+
+        setParameter(s2, s1, arg.getVal().object());
+        
+      }
+      
+    }
+    catch (java.util.NoSuchElementException nsee)
+    {
+      // Should throw some sort of an error.
+      
+    }
+  }
+
+  /**
+   * Set a bag of parameters for the transformation. Note that
+   * these will not be additive, they will replace the existing
+   * set of parameters.
+   *
+   * NEEDSDOC @param params
+   */
+  public void setParameters(Properties params)
+  {
+
+    clearParameters();
+
+    Enumeration names = params.propertyNames();
+
+    while (names.hasMoreElements())
+    {
+      String name = params.getProperty((String) names.nextElement());
+      StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
+
+      try
+      {
+
+        // The first string might be the namespace, or it might be 
+        // the local name, if the namespace is null.
+        String s1 = tokenizer.nextToken();
+        String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+
+        if (null == s2)
+          setParameter(s1, null, params.getProperty(name));
+        else
+          setParameter(s2, s1, params.getProperty(name));
+      }
+      catch (java.util.NoSuchElementException nsee)
+      {
+
+        // Should throw some sort of an error.
+      }
+    }
+  }
+
+  /**
+   * Reset the parameters to a null list.
+   */
+  public void clearParameters()
+  {
+
+    synchronized (m_reentryGuard)
+    {
+      VariableStack varstack = new VariableStack();
+
+      m_xcontext.setVarStack(varstack);
+
+      m_userParams = null;
+    }
+  }
+
+
+  /**
+   * Internal -- push the global variables from the Stylesheet onto
+   * the context's runtime variable stack.
+   * <p>If we encounter a variable
+   * that is already defined in the variable stack, we ignore it.  This
+   * is because the second variable definition will be at a lower import
+   * precedence.  Presumably, global"variables at the same import precedence
+   * with the same name will have been caught during the recompose process.
+   * <p>However, if we encounter a parameter that is already defined in the
+   * variable stack, we need to see if this is a parameter whose value was
+   * supplied by a setParameter call.  If so, we need to "receive" the one
+   * already in the stack, ignoring this one.  If it is just an earlier
+   * xsl:param or xsl:variable definition, we ignore it using the same
+   * reasoning as explained above for the variable.
+   *
+   * @param contextNode The root of the source tree, can't be null.
+   *
+   * @throws TransformerException
+   */
+  protected void pushGlobalVars(int contextNode) throws TransformerException
+  {
+
+    XPathContext xctxt = m_xcontext;
+    VariableStack vs = xctxt.getVarStack();
+    StylesheetRoot sr = getStylesheet();
+    Vector vars = sr.getVariablesAndParamsComposed();
+    
+    int i = vars.size();
+    vs.link(i);
+
+    while (--i >= 0)
+    {
+      ElemVariable v = (ElemVariable) vars.elementAt(i);
+
+      // XObject xobj = v.getValue(this, contextNode);
+      XObject xobj = new XUnresolvedVariable(v, contextNode, this,
+                                     vs.getStackFrame(), 0, true);
+      
+      if(null == vs.elementAt(i))                               
+        vs.setGlobalVariable(i, xobj);
+    }
+
+  }
+
+  /**
+   * Set an object that will be used to resolve URIs used in
+   * document(), etc.
+   * @param resolver An object that implements the URIResolver interface,
+   * or null.
+   */
+  public void setURIResolver(URIResolver resolver)
+  {
+
+    synchronized (m_reentryGuard)
+    {
+      m_xcontext.getSourceTreeManager().setURIResolver(resolver);
+    }
+  }
+
+  /**
+   * Get an object that will be used to resolve URIs used in
+   * document(), etc.
+   *
+   * @return An object that implements the URIResolver interface,
+   * or null.
+   */
+  public URIResolver getURIResolver()
+  {
+    return m_xcontext.getSourceTreeManager().getURIResolver();
+  }
+
+  // ======== End Transformer Implementation ========  
+
+  /**
+   * Set the content event handler.
+   *
+   * NEEDSDOC @param handler
+   * @throws java.lang.NullPointerException If the handler
+   *            is null.
+   * @see org.xml.sax.XMLReader#setContentHandler
+   */
+  public void setContentHandler(ContentHandler handler)
+  {
+
+    if (handler == null)
+    {
+      throw new NullPointerException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_CONTENT_HANDLER, null)); //"Null content handler");
+    }
+    else
+    {
+      m_outputContentHandler = handler;
+
+      if (null == m_serializationHandler)
+      {
+        ToXMLSAXHandler h = new ToXMLSAXHandler();
+        h.setContentHandler(handler);
+        h.setTransformer(this);
+        
+        m_serializationHandler = h;
+      }
+      else
+        m_serializationHandler.setContentHandler(handler);
+    }
+  }
+
+  /**
+   * Get the content event handler.
+   *
+   * @return The current content handler, or null if none was set.
+   * @see org.xml.sax.XMLReader#getContentHandler
+   */
+  public ContentHandler getContentHandler()
+  {
+    return m_outputContentHandler;
+  }
+
+  /**
+   * Given a stylesheet element, create a result tree fragment from it's
+   * contents. The fragment will be built within the shared RTF DTM system
+   * used as a variable stack.
+   * @param templateParent The template element that holds the fragment.
+   * @return the NodeHandle for the root node of the resulting RTF.
+   *
+   * @throws TransformerException
+   * @xsl.usage advanced
+   */
+  public int transformToRTF(ElemTemplateElement templateParent)
+          throws TransformerException
+  {
+    // Retrieve a DTM to contain the RTF. At this writing, this may be a
+    // multi-document DTM (SAX2RTFDTM).
+    DTM dtmFrag = m_xcontext.getRTFDTM();
+    return transformToRTF(templateParent,dtmFrag);
+  }
+  
+  /**
+   * Given a stylesheet element, create a result tree fragment from it's
+   * contents. The fragment will also use the shared DTM system, but will
+   * obtain its space from the global variable pool rather than the dynamic
+   * variable stack. This allows late binding of XUnresolvedVariables without
+   * the risk that their content will be discarded when the variable stack
+   * is popped.
+   * 
+   * @param templateParent The template element that holds the fragment.
+   * @return the NodeHandle for the root node of the resulting RTF.
+   *
+   * @throws TransformerException
+   * @xsl.usage advanced
+   */
+  public int transformToGlobalRTF(ElemTemplateElement templateParent)
+          throws TransformerException
+  {
+    // Retrieve a DTM to contain the RTF. At this writing, this may be a
+    // multi-document DTM (SAX2RTFDTM).
+    DTM dtmFrag = m_xcontext.getGlobalRTFDTM();
+    return transformToRTF(templateParent,dtmFrag);
+  }
+  
+  /**
+   * Given a stylesheet element, create a result tree fragment from it's
+   * contents.
+   * @param templateParent The template element that holds the fragment.
+   * @param dtmFrag The DTM to write the RTF into
+   * @return the NodeHandle for the root node of the resulting RTF.
+   *
+   * @throws TransformerException
+   * @xsl.usage advanced
+   */
+  private int transformToRTF(ElemTemplateElement templateParent,DTM dtmFrag)
+          throws TransformerException
+  {
+
+    XPathContext xctxt = m_xcontext;
+    
+    ContentHandler rtfHandler = dtmFrag.getContentHandler();
+
+    // Obtain the ResultTreeFrag's root node.
+    // NOTE: In SAX2RTFDTM, this value isn't available until after
+    // the startDocument has been issued, so assignment has been moved
+    // down a bit in the code.
+    int resultFragment; // not yet reliably = dtmFrag.getDocument();
+
+    // Save the current result tree handler.
+    SerializationHandler savedRTreeHandler = this.m_serializationHandler;
+ 
+
+    // And make a new handler for the RTF.
+    ToSAXHandler h = new ToXMLSAXHandler();
+    h.setContentHandler(rtfHandler);
+    h.setTransformer(this);
+    
+    // Replace the old handler (which was already saved)
+    m_serializationHandler = h;
+ 
+    // use local variable for the current handler
+    SerializationHandler rth = m_serializationHandler;
+
+    try
+    {
+      rth.startDocument();
+      
+      // startDocument is "bottlenecked" in RTH. We need it acted upon immediately,
+      // to set the DTM's state as in-progress, so that if the xsl:variable's body causes
+      // further RTF activity we can keep that from bashing this DTM.
+      rth.flushPending(); 
+ 
+      try
+      {
+
+        // Do the transformation of the child elements.
+        executeChildTemplates(templateParent, true);
+
+        // Make sure everything is flushed!
+        rth.flushPending();
+        
+        // Get the document ID. May not exist until the RTH has not only
+        // received, but flushed, the startDocument, and may be invalid
+        // again after the document has been closed (still debating that)
+        // ... so waiting until just before the end seems simplest/safest. 
+	resultFragment = dtmFrag.getDocument();      
+      }
+      finally
+      {
+        rth.endDocument();
+      }
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+    finally
+    {
+
+      // Restore the previous result tree handler.
+      this.m_serializationHandler = savedRTreeHandler;
+    }
+
+    return resultFragment;
+  }
+
+  /**
+   * Take the contents of a template element, process it, and
+   * convert it to a string.
+   *
+   * @param elem The parent element whose children will be output
+   * as a string.
+   *
+   * @return The stringized result of executing the elements children.
+   *
+   * @throws TransformerException
+   * @xsl.usage advanced
+   */
+  public String transformToString(ElemTemplateElement elem)
+          throws TransformerException
+  {
+    ElemTemplateElement firstChild = elem.getFirstChildElem();
+    if(null == firstChild)
+      return "";
+    if(elem.hasTextLitOnly() && m_optimizer)
+    {
+      return ((ElemTextLiteral)firstChild).getNodeValue();
+    }
+
+    // Save the current result tree handler.
+    SerializationHandler savedRTreeHandler = this.m_serializationHandler;
+
+    // Create a Serializer object that will handle the SAX events 
+    // and build the ResultTreeFrag nodes.
+    StringWriter sw = (StringWriter) m_stringWriterObjectPool.getInstance();
+
+    m_serializationHandler =
+        (ToTextStream) m_textResultHandlerObjectPool.getInstance();
+
+      if (null == m_serializationHandler)
+      {
+        // if we didn't get one from the pool, go make a new one
+
+        
+        Serializer serializer = org.apache.xml.serializer.SerializerFactory.getSerializer(
+            m_textformat.getProperties());
+        m_serializationHandler = (SerializationHandler) serializer;
+      } 
+
+        m_serializationHandler.setTransformer(this);
+        m_serializationHandler.setWriter(sw);
+ 
+ 
+    String result;
+
+    try
+    {
+        /* Don't call startDocument, the SerializationHandler  will
+         * generate its own internal startDocument call anyways
+         */
+      // this.m_serializationHandler.startDocument();
+
+      // Do the transformation of the child elements.
+      executeChildTemplates(elem, true);
+        this.m_serializationHandler.endDocument();
+
+      result = sw.toString();
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+    finally
+    {
+      sw.getBuffer().setLength(0);
+
+      try
+      {
+        sw.close();
+      }
+      catch (Exception ioe){}
+
+      m_stringWriterObjectPool.freeInstance(sw);
+      m_serializationHandler.reset();
+      m_textResultHandlerObjectPool.freeInstance(m_serializationHandler);
+
+      // Restore the previous result tree handler.
+      m_serializationHandler = savedRTreeHandler;
+    }
+
+    return result;
+  }
+
+  /**
+   * Given an element and mode, find the corresponding
+   * template and process the contents.
+   *
+   * @param xslInstruction The calling element.
+   * @param template The template to use if xsl:for-each, current template for apply-imports, or null.
+   * @param child The source context node.
+   * @throws TransformerException
+   * @return true if applied a template, false if not.
+   * @xsl.usage advanced
+   */
+  public boolean applyTemplateToNode(ElemTemplateElement xslInstruction,  // xsl:apply-templates or xsl:for-each
+                                     ElemTemplate template, int child)
+                                             throws TransformerException
+  {
+
+    DTM dtm = m_xcontext.getDTM(child);
+    short nodeType = dtm.getNodeType(child);
+    boolean isDefaultTextRule = false;
+    boolean isApplyImports = false;
+    
+    isApplyImports = ((xslInstruction == null)
+                                ? false
+                                : xslInstruction.getXSLToken()
+                                  == Constants.ELEMNAME_APPLY_IMPORTS);        
+
+    if (null == template || isApplyImports)
+    {
+      int maxImportLevel, endImportLevel=0;
+
+      if (isApplyImports)
+      {
+        maxImportLevel =
+          template.getStylesheetComposed().getImportCountComposed() - 1;
+        endImportLevel =
+          template.getStylesheetComposed().getEndImportCountComposed();
+      }
+      else
+      {
+        maxImportLevel = -1;
+      }
+
+      // If we're trying an xsl:apply-imports at the top level (ie there are no
+      // imported stylesheets), we need to indicate that there is no matching template.
+      // The above logic will calculate a maxImportLevel of -1 which indicates
+      // that we should find any template.  This is because a value of -1 for
+      // maxImportLevel has a special meaning.  But we don't want that.
+      // We want to match -no- templates. See bugzilla bug 1170.
+      if (isApplyImports && (maxImportLevel == -1))
+      {
+        template = null;
+      }
+      else
+      {
+
+        // Find the XSL template that is the best match for the 
+        // element.        
+        XPathContext xctxt = m_xcontext;
+
+        try
+        {
+          xctxt.pushNamespaceContext(xslInstruction);
+
+          QName mode = this.getMode();
+          
+          if (isApplyImports)
+            template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode,
+                  maxImportLevel, endImportLevel, m_quietConflictWarnings, dtm);
+          else
+            template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode,
+                  m_quietConflictWarnings, dtm);
+          
+        }
+        finally
+        {
+          xctxt.popNamespaceContext();
+        }
+      }
+
+      // If that didn't locate a node, fall back to a default template rule.
+      // See http://www.w3.org/TR/xslt#built-in-rule.
+      if (null == template)
+      {
+        switch (nodeType)
+        {
+        case DTM.DOCUMENT_FRAGMENT_NODE :
+        case DTM.ELEMENT_NODE :
+          template = m_stylesheetRoot.getDefaultRule();
+          break;
+        case DTM.CDATA_SECTION_NODE :
+        case DTM.TEXT_NODE :
+        case DTM.ATTRIBUTE_NODE :
+          template = m_stylesheetRoot.getDefaultTextRule();
+          isDefaultTextRule = true;
+          break;
+        case DTM.DOCUMENT_NODE :
+          template = m_stylesheetRoot.getDefaultRootRule();
+          break;
+        default :
+
+          // No default rules for processing instructions and the like.
+          return false;
+        }
+      }
+    }
+
+    // If we are processing the default text rule, then just clone 
+    // the value directly to the result tree.
+    try
+    {
+      pushElemTemplateElement(template);
+      m_xcontext.pushCurrentNode(child);
+      pushPairCurrentMatched(template, child);
+      
+      // Fix copy copy29 test.
+      if (!isApplyImports) {
+          DTMIterator cnl = new org.apache.xpath.NodeSetDTM(child, m_xcontext.getDTMManager());
+          m_xcontext.pushContextNodeList(cnl);
+      }
+
+      if (isDefaultTextRule)
+      {
+        switch (nodeType)
+        {
+        case DTM.CDATA_SECTION_NODE :
+        case DTM.TEXT_NODE :
+          ClonerToResultTree.cloneToResultTree(child, nodeType, 
+                                        dtm, getResultTreeHandler(), false);
+          break;
+        case DTM.ATTRIBUTE_NODE :
+          dtm.dispatchCharactersEvents(child, getResultTreeHandler(), false);
+          break;
+        }
+      }
+      else
+      {
+
+        // And execute the child templates.
+        // 9/11/00: If template has been compiled, hand off to it
+        // since much (most? all?) of the processing has been inlined.
+        // (It would be nice if there was a single entry point that
+        // worked for both... but the interpretive system works by
+        // having the Tranformer execute the children, while the
+        // compiled obviously has to run its own code. It's
+        // also unclear that "execute" is really the right name for
+        // that entry point.)
+        m_xcontext.setSAXLocator(template);
+        // m_xcontext.getVarStack().link();
+        m_xcontext.getVarStack().link(template.m_frameSize);
+        executeChildTemplates(template, true);
+      }
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+    finally
+    {
+      if (!isDefaultTextRule)
+        m_xcontext.getVarStack().unlink();
+      m_xcontext.popCurrentNode();
+      if (!isApplyImports) {
+          m_xcontext.popContextNodeList();
+      }
+      popCurrentMatched();
+      
+      popElemTemplateElement();
+    }
+
+    return true;
+  }
+  
+  
+  /**
+   * Execute each of the children of a template element.  This method
+   * is only for extension use.
+   *
+   * @param elem The ElemTemplateElement that contains the children
+   * that should execute.
+   * NEEDSDOC @param context
+   * @param mode The current mode.
+   * @param handler The ContentHandler to where the result events
+   * should be fed.
+   *
+   * @throws TransformerException
+   * @xsl.usage advanced
+   */
+  public void executeChildTemplates(
+          ElemTemplateElement elem, org.w3c.dom.Node context, QName mode, ContentHandler handler)
+            throws TransformerException
+  {
+
+    XPathContext xctxt = m_xcontext;
+
+    try
+    {
+      if(null != mode)
+        pushMode(mode);
+      xctxt.pushCurrentNode(xctxt.getDTMHandleFromNode(context));
+      executeChildTemplates(elem, handler);
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+      
+      // I'm not sure where or why this was here.  It is clearly in 
+      // error though, without a corresponding pushMode().
+      if (null != mode)
+        popMode();
+    }
+  }
+
+  /**
+   * Execute each of the children of a template element.
+   *
+   * @param elem The ElemTemplateElement that contains the children
+   * that should execute.
+   * @param shouldAddAttrs true if xsl:attributes should be executed.
+   *
+   * @throws TransformerException
+   * @xsl.usage advanced
+   */
+  public void executeChildTemplates(
+          ElemTemplateElement elem, boolean shouldAddAttrs)
+            throws TransformerException
+  {
+
+    // Does this element have any children?
+    ElemTemplateElement t = elem.getFirstChildElem();
+
+    if (null == t)
+      return;      
+    
+    if(elem.hasTextLitOnly() && m_optimizer)
+    {      
+      char[] chars = ((ElemTextLiteral)t).getChars();
+      try
+      {
+        // Have to push stuff on for tooling...
+        this.pushElemTemplateElement(t);
+        m_serializationHandler.characters(chars, 0, chars.length);
+      }
+      catch(SAXException se)
+      {
+        throw new TransformerException(se);
+      }
+      finally
+      {
+        this.popElemTemplateElement();
+      }
+      return;
+    }
+
+//    // Check for infinite loops if we have to.
+//    boolean check = (m_stackGuard.m_recursionLimit > -1);
+//
+//    if (check)
+//      getStackGuard().push(elem, xctxt.getCurrentNode());
+
+    XPathContext xctxt = m_xcontext;
+    xctxt.pushSAXLocatorNull();
+    int currentTemplateElementsTop = m_currentTemplateElements.size();
+    m_currentTemplateElements.push(null);
+
+    try
+    {
+      // Loop through the children of the template, calling execute on 
+      // each of them.
+      for (; t != null; t = t.getNextSiblingElem())
+      {
+        if (!shouldAddAttrs
+                && t.getXSLToken() == Constants.ELEMNAME_ATTRIBUTE)
+          continue;
+
+        xctxt.setSAXLocator(t);
+        m_currentTemplateElements.setElementAt(t,currentTemplateElementsTop);
+        t.execute(this);
+      }
+    }
+    catch(RuntimeException re)
+    {
+    	TransformerException te = new TransformerException(re);
+    	te.setLocator(t);
+    	throw te;
+    }
+    finally
+    {
+      m_currentTemplateElements.pop();
+      xctxt.popSAXLocator();
+    }
+
+    // Check for infinite loops if we have to
+//    if (check)
+//      getStackGuard().pop();
+  }
+    /**
+      * Execute each of the children of a template element.
+      *
+      * @param elem The ElemTemplateElement that contains the children
+      * that should execute.
+      * @param handler The ContentHandler to where the result events
+      * should be fed.
+      *
+      * @throws TransformerException
+      * @xsl.usage advanced
+      */
+     public void executeChildTemplates(
+             ElemTemplateElement elem, ContentHandler handler)
+               throws TransformerException
+     {
+
+       SerializationHandler xoh = this.getSerializationHandler();
+
+       // These may well not be the same!  In this case when calling
+       // the Redirect extension, it has already set the ContentHandler
+       // in the Transformer.
+       SerializationHandler savedHandler = xoh;
+
+       try
+       {
+         xoh.flushPending();
+
+         // %REVIEW% Make sure current node is being pushed.
+         LexicalHandler lex = null;
+         if (handler instanceof LexicalHandler) {
+            lex = (LexicalHandler) handler;
+         }
+         m_serializationHandler = new ToXMLSAXHandler(handler, lex, savedHandler.getEncoding());
+         m_serializationHandler.setTransformer(this);
+         executeChildTemplates(elem, true);
+       }
+       catch (TransformerException e)
+       {
+         throw e;
+       }
+       catch (SAXException se) {
+       	 throw new TransformerException(se);
+       }
+       finally
+       {
+         m_serializationHandler = savedHandler;
+    }
+  }
+
+  /**
+   * Get the keys for the xsl:sort elements.
+   * Note: Should this go into ElemForEach?
+   *
+   * @param foreach Valid ElemForEach element, not null.
+   * @param sourceNodeContext The current node context in the source tree,
+   * needed to evaluate the Attribute Value Templates.
+   *
+   * @return A Vector of NodeSortKeys, or null.
+   *
+   * @throws TransformerException
+   * @xsl.usage advanced
+   */
+  public Vector processSortKeys(ElemForEach foreach, int sourceNodeContext)
+          throws TransformerException
+  {
+
+    Vector keys = null;
+    XPathContext xctxt = m_xcontext;
+    int nElems = foreach.getSortElemCount();
+
+    if (nElems > 0)
+      keys = new Vector();
+
+    // March backwards, collecting the sort keys.
+    for (int i = 0; i < nElems; i++)
+    {
+      ElemSort sort = foreach.getSortElem(i);
+      
+      String langString =
+        (null != sort.getLang())
+        ? sort.getLang().evaluate(xctxt, sourceNodeContext, foreach) : null;
+      String dataTypeString = sort.getDataType().evaluate(xctxt,
+                                sourceNodeContext, foreach);
+
+      if (dataTypeString.indexOf(":") >= 0)
+        System.out.println(
+          "TODO: Need to write the hooks for QNAME sort data type");
+      else if (!(dataTypeString.equalsIgnoreCase(Constants.ATTRVAL_DATATYPE_TEXT))
+               &&!(dataTypeString.equalsIgnoreCase(
+                 Constants.ATTRVAL_DATATYPE_NUMBER)))
+        foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
+                      new Object[]{ Constants.ATTRNAME_DATATYPE,
+                                    dataTypeString });
+
+      boolean treatAsNumbers =
+        ((null != dataTypeString) && dataTypeString.equals(
+        Constants.ATTRVAL_DATATYPE_NUMBER)) ? true : false;
+      String orderString = sort.getOrder().evaluate(xctxt, sourceNodeContext,
+                             foreach);
+
+      if (!(orderString.equalsIgnoreCase(Constants.ATTRVAL_ORDER_ASCENDING))
+              &&!(orderString.equalsIgnoreCase(
+                Constants.ATTRVAL_ORDER_DESCENDING)))
+        foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
+                      new Object[]{ Constants.ATTRNAME_ORDER,
+                                    orderString });
+
+      boolean descending =
+        ((null != orderString) && orderString.equals(
+        Constants.ATTRVAL_ORDER_DESCENDING)) ? true : false;
+      AVT caseOrder = sort.getCaseOrder();
+      boolean caseOrderUpper;
+
+      if (null != caseOrder)
+      {
+        String caseOrderString = caseOrder.evaluate(xctxt, sourceNodeContext,
+                                                    foreach);
+
+        if (!(caseOrderString.equalsIgnoreCase(Constants.ATTRVAL_CASEORDER_UPPER))
+                &&!(caseOrderString.equalsIgnoreCase(
+                  Constants.ATTRVAL_CASEORDER_LOWER)))
+          foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
+                        new Object[]{ Constants.ATTRNAME_CASEORDER,
+                                      caseOrderString });
+
+        caseOrderUpper =
+          ((null != caseOrderString) && caseOrderString.equals(
+          Constants.ATTRVAL_CASEORDER_UPPER)) ? true : false;
+      }
+      else
+      {
+        caseOrderUpper = false;
+      }
+
+      keys.addElement(new NodeSortKey(this, sort.getSelect(), treatAsNumbers,
+                                      descending, langString, caseOrderUpper,
+                                      foreach));
+     }
+
+    return keys;
+  }
+
+  //==========================================================
+  // SECTION: TransformState implementation
+  //==========================================================
+  
+  /**
+   * Get the count of how many elements are 
+   * active.
+   * @return The number of active elements on 
+   * the currentTemplateElements stack.
+   */
+  public int getCurrentTemplateElementsCount()
+  {
+  	return m_currentTemplateElements.size();
+  }
+  
+  
+  /**
+   * Get the count of how many elements are 
+   * active.
+   * @return The number of active elements on 
+   * the currentTemplateElements stack.
+   */
+  public ObjectStack getCurrentTemplateElements()
+  {
+  	return m_currentTemplateElements;
+  }
+
+  /**
+   * Push the current template element.
+   *
+   * @param elem The current ElemTemplateElement (may be null, and then
+   * set via setCurrentElement).
+   */
+  public void pushElemTemplateElement(ElemTemplateElement elem)
+  {
+    m_currentTemplateElements.push(elem);
+  }
+
+  /**
+   * Pop the current template element.
+   */
+  public void popElemTemplateElement()
+  {
+    m_currentTemplateElements.pop();
+  }
+
+  /**
+   * Set the top of the current template elements
+   * stack.
+   *
+   * @param e The current ElemTemplateElement about to
+   * be executed.
+   */
+  public void setCurrentElement(ElemTemplateElement e)
+  {
+    m_currentTemplateElements.setTop(e);
+  }
+
+  /**
+   * Retrieves the current ElemTemplateElement that is
+   * being executed.
+   *
+   * @return The current ElemTemplateElement that is executing,
+   * should not normally be null.
+   */
+  public ElemTemplateElement getCurrentElement()
+  {
+    return (m_currentTemplateElements.size() > 0) ? 
+        (ElemTemplateElement) m_currentTemplateElements.peek() : null;
+  }
+
+  /**
+   * This method retrieves the current context node
+   * in the source tree.
+   *
+   * @return The current context node (should never be null?).
+   */
+  public int getCurrentNode()
+  {
+    return m_xcontext.getCurrentNode();
+  }
+  
+  /**
+   * This method retrieves the xsl:template
+   * that is in effect, which may be a matched template
+   * or a named template.
+   *
+   * <p>Please note that the ElemTemplate returned may
+   * be a default template, and thus may not have a template
+   * defined in the stylesheet.</p>
+   *
+   * @return The current xsl:template, should not be null.
+   */
+  public ElemTemplate getCurrentTemplate()
+  {
+
+    ElemTemplateElement elem = getCurrentElement();
+
+    while ((null != elem)
+           && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE))
+    {
+      elem = elem.getParentElem();
+    }
+
+    return (ElemTemplate) elem;
+  }
+
+  /**
+   * Push both the current xsl:template or xsl:for-each onto the
+   * stack, along with the child node that was matched.
+   * (Note: should this only be used for xsl:templates?? -sb)
+   *
+   * @param template xsl:template or xsl:for-each.
+   * @param child The child that was matched.
+   */
+  public void pushPairCurrentMatched(ElemTemplateElement template, int child)
+  {
+    m_currentMatchTemplates.push(template);
+    m_currentMatchedNodes.push(child);
+  }
+
+  /**
+   * Pop the elements that were pushed via pushPairCurrentMatched.
+   */
+  public void popCurrentMatched()
+  {
+    m_currentMatchTemplates.pop();
+    m_currentMatchedNodes.pop();
+  }
+
+  /**
+   * This method retrieves the xsl:template
+   * that was matched.  Note that this may not be
+   * the same thing as the current template (which
+   * may be from getCurrentElement()), since a named
+   * template may be in effect.
+   *
+   * @return The pushed template that was pushed via pushPairCurrentMatched.
+   */
+  public ElemTemplate getMatchedTemplate()
+  {
+    return (ElemTemplate) m_currentMatchTemplates.peek();
+  }
+
+  /**
+   * Retrieves the node in the source tree that matched
+   * the template obtained via getMatchedTemplate().
+   *
+   * @return The matched node that corresponds to the
+   * match attribute of the current xsl:template.
+   */
+  public int getMatchedNode()
+  {
+    return m_currentMatchedNodes.peepTail();
+  }
+
+  /**
+   * Get the current context node list.
+   *
+   * @return A reset clone of the context node list.
+   */
+  public DTMIterator getContextNodeList()
+  {
+
+    try
+    {
+      DTMIterator cnl = m_xcontext.getContextNodeList();
+
+      return (cnl == null) ? null : (DTMIterator) cnl.cloneWithReset();
+    }
+    catch (CloneNotSupportedException cnse)
+    {
+
+      // should never happen.
+      return null;
+    }
+  }
+
+  /**
+   * Get the TrAX Transformer object in effect.
+   *
+   * @return This object.
+   */
+  public Transformer getTransformer()
+  {
+    return this;
+  }
+
+  //==========================================================
+  // SECTION: Accessor Functions
+  //==========================================================
+
+  /**
+   * Set the stylesheet for this processor.  If this is set, then the
+   * process calls that take only the input .xml will use
+   * this instead of looking for a stylesheet PI.  Also,
+   * setting the stylesheet is needed if you are going
+   * to use the processor as a SAX ContentHandler.
+   *
+   * @param stylesheetRoot A non-null StylesheetRoot object,
+   * or null if you wish to clear the stylesheet reference.
+   */
+  public void setStylesheet(StylesheetRoot stylesheetRoot)
+  {
+    m_stylesheetRoot = stylesheetRoot;
+  }
+
+  /**
+   * Get the current stylesheet for this processor.
+   *
+   * @return The stylesheet that is associated with this
+   * transformer.
+   */
+  public final StylesheetRoot getStylesheet()
+  {
+    return m_stylesheetRoot;
+  }
+
+  /**
+   * Get quietConflictWarnings property. If the quietConflictWarnings
+   * property is set to true, warnings about pattern conflicts won't be
+   * printed to the diagnostics stream.
+   *
+   * @return True if this transformer should not report
+   * template match conflicts.
+   */
+  public boolean getQuietConflictWarnings()
+  {
+    return m_quietConflictWarnings;
+  }
+
+  /**
+   * Set the execution context for XPath.
+   *
+   * @param xcontext A non-null reference to the XPathContext
+   * associated with this transformer.
+   * @xsl.usage internal
+   */
+  public void setXPathContext(XPathContext xcontext)
+  {
+    m_xcontext = xcontext;
+  }
+
+  /**
+   * Get the XPath context associated with this transformer.
+   *
+   * @return The XPathContext reference, never null.
+   */
+  public final XPathContext getXPathContext()
+  {
+    return m_xcontext;
+  }
+
+  /**
+   * Get the SerializationHandler object.
+   *
+   * @return The current SerializationHandler, which may not
+   * be the main result tree manager.
+   */
+  public SerializationHandler getResultTreeHandler()
+  {
+    return m_serializationHandler;
+  }
+
+  /**
+   * Get the SerializationHandler object.
+   *
+   * @return The current SerializationHandler, which may not
+   * be the main result tree manager.
+   */
+  public SerializationHandler getSerializationHandler()
+  {
+    return m_serializationHandler;
+  }
+  
+  /**
+   * Get the KeyManager object.
+   *
+   * @return A reference to the KeyManager object, which should
+   * never be null.
+   */
+  public KeyManager getKeyManager()
+  {
+    return m_keyManager;
+  }
+
+  /**
+   * Check to see if this is a recursive attribute definition.
+   *
+   * @param attrSet A non-null ElemAttributeSet reference.
+   *
+   * @return true if the attribute set is recursive.
+   */
+  public boolean isRecursiveAttrSet(ElemAttributeSet attrSet)
+  {
+
+    if (null == m_attrSetStack)
+    {
+      m_attrSetStack = new Stack();
+    }
+
+    if (!m_attrSetStack.empty())
+    {
+      int loc = m_attrSetStack.search(attrSet);
+
+      if (loc > -1)
+      {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Push an executing attribute set, so we can check for
+   * recursive attribute definitions.
+   *
+   * @param attrSet A non-null ElemAttributeSet reference.
+   */
+  public void pushElemAttributeSet(ElemAttributeSet attrSet)
+  {
+    m_attrSetStack.push(attrSet);
+  }
+
+  /**
+   * Pop the current executing attribute set.
+   */
+  public void popElemAttributeSet()
+  {
+    m_attrSetStack.pop();
+  }
+
+  /**
+   * Get the table of counters, for optimized xsl:number support.
+   *
+   * @return The CountersTable, never null.
+   */
+  public CountersTable getCountersTable()
+  {
+
+    if (null == m_countersTable)
+      m_countersTable = new CountersTable();
+
+    return m_countersTable;
+  }
+
+  /**
+   * Tell if the current template rule is null, i.e. if we are
+   * directly within an apply-templates.  Used for xsl:apply-imports.
+   *
+   * @return True if the current template rule is null.
+   */
+  public boolean currentTemplateRuleIsNull()
+  {
+    return ((!m_currentTemplateRuleIsNull.isEmpty())
+            && (m_currentTemplateRuleIsNull.peek() == true));
+  }
+
+  /**
+   * Push true if the current template rule is null, false
+   * otherwise.
+   *
+   * @param b True if the we are executing an xsl:for-each
+   * (or xsl:call-template?).
+   */
+  public void pushCurrentTemplateRuleIsNull(boolean b)
+  {
+    m_currentTemplateRuleIsNull.push(b);
+  }
+
+  /**
+   * Push true if the current template rule is null, false
+   * otherwise.
+   */
+  public void popCurrentTemplateRuleIsNull()
+  {
+    m_currentTemplateRuleIsNull.pop();
+  }
+
+  /**
+   * Push a funcion result for the currently active EXSLT
+   * <code>func:function</code>.
+   * 
+   * @param val the result of executing an EXSLT
+   * <code>func:result</code> instruction for the current
+   * <code>func:function</code>.
+   */
+  public void pushCurrentFuncResult(Object val) {
+    m_currentFuncResult.push(val);
+  }
+
+  /**
+   * Pops the result of the currently active EXSLT <code>func:function</code>.
+   * 
+   * @return the value of the <code>func:function</code>
+   */
+  public Object popCurrentFuncResult() {
+    return m_currentFuncResult.pop();
+  }
+
+  /**
+   * Determines whether an EXSLT <code>func:result</code> instruction has been
+   * executed for the currently active EXSLT <code>func:function</code>.
+   * 
+   * @return <code>true</code> if and only if a <code>func:result</code>
+   * instruction has been executed
+   */
+  public boolean currentFuncResultSeen() {
+    return !m_currentFuncResult.empty()
+               && m_currentFuncResult.peek() != null;
+  }
+
+  /**
+   * Return the message manager.
+   *
+   * @return The message manager, never null.
+   */
+  public MsgMgr getMsgMgr()
+  {
+
+    if (null == m_msgMgr)
+      m_msgMgr = new MsgMgr(this);
+
+    return m_msgMgr;
+  }
+
+  /**
+   * Set the error event listener.
+   *
+   * @param listener The new error listener.
+   * @throws IllegalArgumentException if
+   */
+  public void setErrorListener(ErrorListener listener)
+          throws IllegalArgumentException
+  {
+
+    synchronized (m_reentryGuard)
+    {
+      if (listener == null)
+        throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler");
+
+      m_errorHandler = listener;
+    }
+  }
+
+  /**
+   * Get the current error event handler.
+   *
+   * @return The current error handler, which should never be null.
+   */
+  public ErrorListener getErrorListener()
+  {
+    return m_errorHandler;
+  }
+
+  /**
+   * Look up the value of a feature.
+   *
+   * <p>The feature name is any fully-qualified URI.  It is
+   * possible for an TransformerFactory to recognize a feature name but
+   * to be unable to return its value; this is especially true
+   * in the case of an adapter for a SAX1 Parser, which has
+   * no way of knowing whether the underlying parser is
+   * validating, for example.</p>
+   *
+   * <h3>Open issues:</h3>
+   * <dl>
+   *    <dt><h4>Should getFeature be changed to hasFeature?</h4></dt>
+   *    <dd>Keith Visco writes: Should getFeature be changed to hasFeature?
+   *        It returns a boolean which indicated whether the "state"
+   *        of feature is "true or false". I assume this means whether
+   *        or not a feature is supported? I know SAX is using "getFeature",
+   *        but to me "hasFeature" is cleaner.</dd>
+   * </dl>
+   *
+   * @param name The feature name, which is a fully-qualified
+   *        URI.
+   * @return The current state of the feature (true or false).
+   * @throws org.xml.sax.SAXNotRecognizedException When the
+   *            TransformerFactory does not recognize the feature name.
+   * @throws org.xml.sax.SAXNotSupportedException When the
+   *            TransformerFactory recognizes the feature name but
+   *            cannot determine its value at this time.
+   *
+   * @throws SAXNotRecognizedException
+   * @throws SAXNotSupportedException
+   */
+  public boolean getFeature(String name)
+          throws SAXNotRecognizedException, SAXNotSupportedException
+  {
+
+    if ("http://xml.org/trax/features/sax/input".equals(name))
+      return true;
+    else if ("http://xml.org/trax/features/dom/input".equals(name))
+      return true;
+
+    throw new SAXNotRecognizedException(name);
+  }
+
+  // %TODO% Doc
+
+  /**
+   * NEEDSDOC Method getMode 
+   *
+   *
+   * NEEDSDOC (getMode) @return
+   */
+  public QName getMode()
+  {
+    return m_modes.isEmpty() ? null : (QName) m_modes.peek();
+  }
+
+  // %TODO% Doc
+
+  /**
+   * NEEDSDOC Method pushMode 
+   *
+   *
+   * NEEDSDOC @param mode
+   */
+  public void pushMode(QName mode)
+  {
+    m_modes.push(mode);
+  }
+
+  // %TODO% Doc
+
+  /**
+   * NEEDSDOC Method popMode 
+   *
+   */
+  public void popMode()
+  {
+    m_modes.pop();
+  }
+
+  /**
+   * Called by SourceTreeHandler to start the transformation
+   *  in a separate thread
+   *
+   * NEEDSDOC @param priority
+   */
+  public void runTransformThread(int priority)
+  {
+
+    // used in SourceTreeHandler
+    Thread t = ThreadControllerWrapper.runThread(this, priority);
+    this.setTransformThread(t);
+  }
+
+  /**
+   * Called by this.transform() if isParserEventsOnMain()==false.
+   *  Similar with runTransformThread(), but no priority is set
+   *  and setTransformThread is not set.
+   */
+  public void runTransformThread()
+  {
+    ThreadControllerWrapper.runThread(this, -1);
+  }
+  
+  /**
+   * Called by CoRoutineSAXParser. Launches the CoroutineSAXParser
+   * in a thread, and prepares it to invoke the parser from that thread
+   * upon request. 
+   *  
+   */
+  public static void runTransformThread(Runnable runnable)
+  {
+    ThreadControllerWrapper.runThread(runnable, -1);
+  }
+
+  /**
+   * Used by SourceTreeHandler to wait until the transform
+   *   completes
+   *
+   * @throws SAXException
+   */
+  public void waitTransformThread() throws SAXException
+  {
+
+    // This is called to make sure the task is done.
+    // It is possible that the thread has been reused -
+    // but for a different transformation. ( what if we 
+    // recycle the transformer ? Not a problem since this is
+    // still in use. )
+    Thread transformThread = this.getTransformThread();
+
+    if (null != transformThread)
+    {
+      try
+      {
+        ThreadControllerWrapper.waitThread(transformThread, this);
+
+        if (!this.hasTransformThreadErrorCatcher())
+        {
+          Exception e = this.getExceptionThrown();
+
+          if (null != e)
+          {
+            e.printStackTrace();
+            throw new org.xml.sax.SAXException(e);
+          }
+        }
+
+        this.setTransformThread(null);
+      }
+      catch (InterruptedException ie){}
+    }
+  }
+
+  /**
+   * Get the exception thrown by the secondary thread (normally
+   * the transform thread).
+   *
+   * @return The thrown exception, or null if no exception was
+   * thrown.
+   */
+  public Exception getExceptionThrown()
+  {
+    return m_exceptionThrown;
+  }
+
+  /**
+   * Set the exception thrown by the secondary thread (normally
+   * the transform thread).
+   *
+   * @param e The thrown exception, or null if no exception was
+   * thrown.
+   */
+  public void setExceptionThrown(Exception e)
+  {
+    m_exceptionThrown = e;
+  }
+
+  /**
+   * This is just a way to set the document for run().
+   *
+   * @param doc A non-null reference to the root of the
+   * tree to be transformed.
+   */
+  public void setSourceTreeDocForThread(int doc)
+  {
+    m_doc = doc;
+  }
+
+  /**
+   * From a secondary thread, post the exception, so that
+   * it can be picked up from the main thread.
+   *
+   * @param e The exception that was thrown.
+   */
+  void postExceptionFromThread(Exception e)
+  {
+
+    // Commented out in response to problem reported by Nicola Brown <Nicola.Brown@jacobsrimell.com>
+    //    if(m_reportInPostExceptionFromThread)
+    //    {
+    //      // Consider re-throwing the exception if this flag is set.
+    //      e.printStackTrace();
+    //    }
+    // %REVIEW Need DTM equivelent?    
+    //    if (m_inputContentHandler instanceof SourceTreeHandler)
+    //    {
+    //      SourceTreeHandler sth = (SourceTreeHandler) m_inputContentHandler;
+    //
+    //      sth.setExceptionThrown(e);
+    //    }
+ //   ContentHandler ch = getContentHandler();
+
+    //    if(ch instanceof SourceTreeHandler)
+    //    {
+    //      SourceTreeHandler sth = (SourceTreeHandler) ch;
+    //      ((TransformerImpl)(sth.getTransformer())).postExceptionFromThread(e);
+    //    }
+    // m_isTransformDone = true; // android-removed
+    m_exceptionThrown = e;
+    ;  // should have already been reported via the error handler?
+
+    synchronized (this)
+    {
+
+      // See message from me on 3/27/2001 to Patrick Moore.
+      //      String msg = e.getMessage();
+      // System.out.println(e.getMessage());
+      // Is this really needed?  -sb
+      notifyAll();
+
+      //      if (null == msg)
+      //      {
+      //
+      //        // m_throwNewError = false;
+      //        e.printStackTrace();
+      //      }
+      // throw new org.apache.xml.utils.WrappedRuntimeException(e);
+    }
+  }
+
+  /**
+   * Run the transform thread.
+   */
+  public void run()
+  {
+
+    m_hasBeenReset = false;
+
+    try
+    {
+
+      // int n = ((SourceTreeHandler)getInputContentHandler()).getDTMRoot();
+      // transformNode(n);
+      try
+      {
+        // m_isTransformDone = false; // android-removed
+        
+        // Should no longer be needed...
+//          if(m_inputContentHandler instanceof TransformerHandlerImpl)
+//          {
+//            TransformerHandlerImpl thi = (TransformerHandlerImpl)m_inputContentHandler;
+//            thi.waitForInitialEvents();
+//          }
+
+        transformNode(m_doc);
+        
+      }
+      catch (Exception e)
+      {
+        // e.printStackTrace();
+
+        // Strange that the other catch won't catch this...
+        if (null != m_transformThread)
+          postExceptionFromThread(e);   // Assume we're on the main thread
+        else 
+          throw new RuntimeException(e.getMessage());
+      }
+      finally
+      {
+        // m_isTransformDone = true; // android-removed
+
+        if (m_inputContentHandler instanceof TransformerHandlerImpl)
+        {
+          ((TransformerHandlerImpl) m_inputContentHandler).clearCoRoutine();
+        }
+
+        //        synchronized (this)
+        //        {
+        //          notifyAll();
+        //        }
+      }
+    }
+    catch (Exception e)
+    {
+
+      // e.printStackTrace();
+      if (null != m_transformThread)
+        postExceptionFromThread(e);
+      else 
+        throw new RuntimeException(e.getMessage());         // Assume we're on the main thread.
+    }
+  }
+
+  // Fragment re-execution interfaces for a tool.
+
+  /**
+   * Test whether whitespace-only text nodes are visible in the logical
+   * view of <code>DTM</code>. Normally, this function
+   * will be called by the implementation of <code>DTM</code>;
+   * it is not normally called directly from
+   * user code.
+   *
+   * @param elementHandle int Handle of the element.
+   * @return one of NOTSTRIP, STRIP, or INHERIT.
+   */
+  public short getShouldStripSpace(int elementHandle, DTM dtm)
+  {
+
+    try
+    {
+      org.apache.xalan.templates.WhiteSpaceInfo info =
+        m_stylesheetRoot.getWhiteSpaceInfo(m_xcontext, elementHandle, dtm);
+
+      if (null == info)
+      {
+        return DTMWSFilter.INHERIT;
+      }
+      else
+      {
+
+        // System.out.println("getShouldStripSpace: "+info.getShouldStripSpace());
+        return info.getShouldStripSpace()
+               ? DTMWSFilter.STRIP : DTMWSFilter.NOTSTRIP;
+      }
+    }
+    catch (TransformerException se)
+    {
+      return DTMWSFilter.INHERIT;
+    }
+  }
+  /**
+   * Initializer method.
+   *
+   * @param transformer non-null transformer instance
+   * @param realHandler Content Handler instance
+   */
+   public void init(ToXMLSAXHandler h,Transformer transformer, ContentHandler realHandler)
+   {
+      h.setTransformer(transformer);
+      h.setContentHandler(realHandler);
+   }
+      
+   public void setSerializationHandler(SerializationHandler xoh)
+   {
+      m_serializationHandler = xoh;
+   }
+   
+   
+     
+	/**
+	 * Fire off characters, cdate events.
+	 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, char[], int, int)
+	 */
+	public void fireGenerateEvent(
+		int eventType,
+		char[] ch,
+		int start,
+		int length) {
+	}
+
+	/**
+	 * Fire off startElement, endElement events.
+	 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, Attributes)
+	 */
+	public void fireGenerateEvent(
+		int eventType,
+		String name,
+		Attributes atts) {
+	}
+
+	/**
+	 * Fire off processingInstruction events.
+	 */
+	public void fireGenerateEvent(int eventType, String name, String data) {
+	}
+
+	/**
+	 * Fire off comment and entity ref events.
+	 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String)
+	 */
+	public void fireGenerateEvent(int eventType, String data) {
+	}
+
+	/**
+	 * Fire off startDocument, endDocument events.
+	 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int)
+	 */
+	public void fireGenerateEvent(int eventType) {
+	}
+
+    /**
+     * @see org.apache.xml.serializer.SerializerTrace#hasTraceListeners()
+     */
+    public boolean hasTraceListeners() {
+        return false;
+    }
+
+    /**
+     * @return Incremental flag
+     */
+    public boolean getIncremental() {
+        return m_incremental;
+    }
+
+    /**
+     * @return Optimization flag
+     */
+    public boolean getOptimize() {
+        return m_optimizer;
+    }
+
+    /**
+     * @return Source location flag
+     */
+    public boolean getSource_location() {
+        return m_source_location;
+    }
+
+}  // end TransformerImpl class
+
diff --git a/src/main/java/org/apache/xalan/transformer/TreeWalker2Result.java b/src/main/java/org/apache/xalan/transformer/TreeWalker2Result.java
new file mode 100644
index 0000000..3e78cbc
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/TreeWalker2Result.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TreeWalker2Result.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+package org.apache.xalan.transformer;
+
+import org.apache.xalan.serialize.SerializerUtils;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.ref.DTMTreeWalker;
+import org.apache.xml.serializer.SerializationHandler;
+import org.apache.xpath.XPathContext;
+
+/**
+ * Handle a walk of a tree, but screen out attributes for
+ * the result tree.
+ * @xsl.usage internal
+ */
+public class TreeWalker2Result extends DTMTreeWalker
+{
+
+  /** The transformer instance          */
+  TransformerImpl m_transformer;
+
+  /** The result tree handler          */
+  SerializationHandler m_handler;
+
+  /** Node where to start the tree walk           */
+  int m_startNode;
+
+  /**
+   * Constructor.
+   *
+   * @param transformer Non-null transformer instance
+   * @param handler The Result tree handler to use
+   */
+  public TreeWalker2Result(TransformerImpl transformer,
+                           SerializationHandler handler)
+  {
+
+    super(handler, null);
+
+    m_transformer = transformer;
+    m_handler = handler;
+  }
+
+  /**
+   * Perform a pre-order traversal non-recursive style.
+   *
+   * @param pos Start node for traversal
+   *
+   * @throws TransformerException
+   */
+  public void traverse(int pos) throws org.xml.sax.SAXException
+  {
+    m_dtm = m_transformer.getXPathContext().getDTM(pos);
+    m_startNode = pos;
+
+    super.traverse(pos);
+  }
+        
+        /**
+   * End processing of given node 
+   *
+   *
+   * @param node Node we just finished processing
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  protected void endNode(int node) throws org.xml.sax.SAXException
+  {
+    super.endNode(node);
+    if(DTM.ELEMENT_NODE == m_dtm.getNodeType(node))
+    {
+      m_transformer.getXPathContext().popCurrentNode();
+    }
+  }
+
+  /**
+   * Start traversal of the tree at the given node
+   *
+   *
+   * @param node Starting node for traversal
+   *
+   * @throws TransformerException
+   */
+  protected void startNode(int node) throws org.xml.sax.SAXException
+  {
+
+    XPathContext xcntxt = m_transformer.getXPathContext();
+    try
+    {
+      
+      if (DTM.ELEMENT_NODE == m_dtm.getNodeType(node))
+      {
+        xcntxt.pushCurrentNode(node);                   
+                                        
+        if(m_startNode != node)
+        {
+          super.startNode(node);
+        }
+        else
+        {
+          String elemName = m_dtm.getNodeName(node);
+          String localName = m_dtm.getLocalName(node);
+          String namespace = m_dtm.getNamespaceURI(node);
+                                        
+          //xcntxt.pushCurrentNode(node);       
+          // SAX-like call to allow adding attributes afterwards
+          m_handler.startElement(namespace, localName, elemName);
+          boolean hasNSDecls = false;
+          DTM dtm = m_dtm;
+          for (int ns = dtm.getFirstNamespaceNode(node, true); 
+               DTM.NULL != ns; ns = dtm.getNextNamespaceNode(node, ns, true))
+          {
+            SerializerUtils.ensureNamespaceDeclDeclared(m_handler,dtm, ns);
+          }
+                                                
+                                                
+          for (int attr = dtm.getFirstAttribute(node); 
+               DTM.NULL != attr; attr = dtm.getNextAttribute(attr))
+          {
+            SerializerUtils.addAttribute(m_handler, attr);
+          }
+        }
+                                
+      }
+      else
+      {
+        xcntxt.pushCurrentNode(node);
+        super.startNode(node);
+        xcntxt.popCurrentNode();
+      }
+    }
+    catch(javax.xml.transform.TransformerException te)
+    {
+      throw new org.xml.sax.SAXException(te);
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xalan/transformer/XalanProperties.java b/src/main/java/org/apache/xalan/transformer/XalanProperties.java
new file mode 100644
index 0000000..28df780
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/XalanProperties.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XalanProperties.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+
+package org.apache.xalan.transformer;
+
+/**
+ * <code>XalanProperties</code> defines the features understood by
+ * Xalan.
+ *
+ * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
+ * @since May 23, 2001
+ */
+public class XalanProperties
+{
+  public final static String SOURCE_LOCATION
+    = "http://xml.apache.org/xalan/properties/source-location";
+}
diff --git a/src/main/java/org/apache/xalan/transformer/XalanTransformState.java b/src/main/java/org/apache/xalan/transformer/XalanTransformState.java
new file mode 100644
index 0000000..cae10a2
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/XalanTransformState.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XalanTransformState.java 468645 2006-10-28 06:57:24Z minchau $
+ */
+
+package org.apache.xalan.transformer;
+
+import javax.xml.transform.Transformer;
+
+import org.apache.xalan.templates.ElemTemplate;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * Before the serializer merge, the TransformState interface was
+ * implemented by ResultTreeHandler.
+ */
+public class XalanTransformState
+    implements TransformState {
+        
+    Node m_node = null;
+    ElemTemplateElement m_currentElement = null;
+    ElemTemplate m_currentTemplate = null;
+    ElemTemplate m_matchedTemplate = null;
+    int m_currentNodeHandle = DTM.NULL;
+    Node m_currentNode = null;
+    int m_matchedNode = DTM.NULL;
+    DTMIterator m_contextNodeList = null;
+    boolean m_elemPending = false;    
+    TransformerImpl m_transformer = null;
+
+    /**
+     * @see org.apache.xml.serializer.TransformStateSetter#setCurrentNode(Node)
+     */
+    public void setCurrentNode(Node n) {
+        m_node = n;
+    }
+
+    /**
+     * @see org.apache.xml.serializer.TransformStateSetter#resetState(Transformer)
+     */
+    public void resetState(Transformer transformer) {
+        if ((transformer != null) && (transformer instanceof TransformerImpl)) {
+           m_transformer = (TransformerImpl)transformer;
+           m_currentElement = m_transformer.getCurrentElement();
+           m_currentTemplate = m_transformer.getCurrentTemplate();
+           m_matchedTemplate = m_transformer.getMatchedTemplate();
+           int currentNodeHandle = m_transformer.getCurrentNode();
+           DTM dtm = m_transformer.getXPathContext().getDTM(currentNodeHandle);
+           m_currentNode = dtm.getNode(currentNodeHandle);
+           m_matchedNode = m_transformer.getMatchedNode();
+           m_contextNodeList = m_transformer.getContextNodeList();    
+        }       
+    }
+
+    /**
+     * @see org.apache.xalan.transformer.TransformState#getCurrentElement()
+     */
+    public ElemTemplateElement getCurrentElement() {
+      if (m_elemPending)
+         return m_currentElement;
+      else
+         return m_transformer.getCurrentElement();
+    }
+
+    /**
+     * @see org.apache.xalan.transformer.TransformState#getCurrentNode()
+     */
+    public Node getCurrentNode() {
+      if (m_currentNode != null) {
+         return m_currentNode;
+      } else {
+         DTM dtm = m_transformer.getXPathContext().getDTM(m_transformer.getCurrentNode());
+         return dtm.getNode(m_transformer.getCurrentNode());
+      }
+    }
+    
+    /**
+     * @see org.apache.xalan.transformer.TransformState#getCurrentTemplate()
+     */
+    public ElemTemplate getCurrentTemplate() {
+       if (m_elemPending)
+         return m_currentTemplate;
+       else
+         return m_transformer.getCurrentTemplate();
+    }
+
+    /**
+     * @see org.apache.xalan.transformer.TransformState#getMatchedTemplate()
+     */
+    public ElemTemplate getMatchedTemplate() {
+      if (m_elemPending)
+         return m_matchedTemplate;
+      else
+         return m_transformer.getMatchedTemplate();
+    }
+
+    /**
+     * @see org.apache.xalan.transformer.TransformState#getMatchedNode()
+     */
+    public Node getMatchedNode() {
+ 
+       if (m_elemPending) {
+         DTM dtm = m_transformer.getXPathContext().getDTM(m_matchedNode);
+         return dtm.getNode(m_matchedNode);
+       } else {
+         DTM dtm = m_transformer.getXPathContext().getDTM(m_transformer.getMatchedNode());
+         return dtm.getNode(m_transformer.getMatchedNode());
+       }
+    }
+
+    /**
+     * @see org.apache.xalan.transformer.TransformState#getContextNodeList()
+     */
+    public NodeIterator getContextNodeList() {
+      if (m_elemPending) {
+          return new org.apache.xml.dtm.ref.DTMNodeIterator(m_contextNodeList);
+      } else {
+          return new org.apache.xml.dtm.ref.DTMNodeIterator(m_transformer.getContextNodeList());
+      }
+    }
+    /**
+     * @see org.apache.xalan.transformer.TransformState#getTransformer()
+     */
+    public Transformer getTransformer() {
+        return m_transformer;
+    }
+
+}
diff --git a/src/main/java/org/apache/xalan/transformer/package.html b/src/main/java/org/apache/xalan/transformer/package.html
new file mode 100644
index 0000000..763b1c1
--- /dev/null
+++ b/src/main/java/org/apache/xalan/transformer/package.html
@@ -0,0 +1,39 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468645 2006-10-28 06:57:24Z minchau $ -->
+<html>
+  <title>Xalan Transformer Package.</title>
+  <body>
+    <p>In charge of run-time transformations and the production of result trees.</p>
+    
+    <p>{@link org.apache.xalan.transformer.TransformerImpl} implements the 
+    {@link javax.xml.transform.Transformer} interface, and is the core representation 
+    of the transformation execution.</p>
+    <p>For each node in the XML source, TransformerImpl uses the StylesheetRoot and underlying XSLT schema to determine which
+    template to apply: one of the templates in the StylesheetRoot, a default template rule as specified in the XSLT spec, or
+    none.</p>
+    <p>The Transformer works with {@link org.apache.xml.serializer.SerializationHandler} to 
+    forward the SAX-like events produced by the
+    transformation to the appropriate output ContentHandler.</p>
+    <p>To the degree possible, the parsing of the XML source and application of the Templates object to that source are performed
+    concurrently in separate threads. When necessary, the Transformer waits for the parse events that must be in place before a
+    given template may be applied.</p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xalan/xslt/EnvironmentCheck.java b/src/main/java/org/apache/xalan/xslt/EnvironmentCheck.java
new file mode 100644
index 0000000..7ba3899
--- /dev/null
+++ b/src/main/java/org/apache/xalan/xslt/EnvironmentCheck.java
@@ -0,0 +1,1296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: EnvironmentCheck.java 468646 2006-10-28 06:57:58Z minchau $
+ */
+package org.apache.xalan.xslt;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Utility class to report simple information about the environment.
+ * Simplistic reporting about certain classes found in your JVM may 
+ * help answer some FAQs for simple problems.
+ *
+ * <p>Usage-command line:  
+ * <code>
+ * java org.apache.xalan.xslt.EnvironmentCheck [-out outFile]
+ * </code></p>
+ * 
+ * <p>Usage-from program:  
+ * <code>
+ * boolean environmentOK = 
+ * (new EnvironmentCheck()).checkEnvironment(yourPrintWriter);
+ * </code></p>
+ *
+ * <p>Usage-from stylesheet:  
+ * <code><pre>
+ *    &lt;?xml version="1.0"?&gt;
+ *    &lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+ *        xmlns:xalan="http://xml.apache.org/xalan"
+ *        exclude-result-prefixes="xalan"&gt;
+ *    &lt;xsl:output indent="yes"/&gt;
+ *    &lt;xsl:template match="/"&gt;
+ *      &lt;xsl:copy-of select="xalan:checkEnvironment()"/&gt;
+ *    &lt;/xsl:template&gt;
+ *    &lt;/xsl:stylesheet&gt;
+ * </pre></code></p>
+ *  
+ * <p>Xalan users reporting problems are encouraged to use this class 
+ * to see if there are potential problems with their actual 
+ * Java environment <b>before</b> reporting a bug.  Note that you 
+ * should both check from the JVM/JRE's command line as well as 
+ * temporarily calling checkEnvironment() directly from your code, 
+ * since the classpath may differ (especially for servlets, etc).</p>
+ *
+ * <p>Also see http://xml.apache.org/xalan-j/faq.html</p>
+ *
+ * <p>Note: This class is pretty simplistic: 
+ * results are not necessarily definitive nor will it find all 
+ * problems related to environment setup.  Also, you should avoid 
+ * calling this in deployed production code, both because it is 
+ * quite slow and because it forces classes to get loaded.</p>
+ *
+ * <p>Note: This class explicitly has very limited compile-time 
+ * dependencies to enable easy compilation and usage even when 
+ * Xalan, DOM/SAX/JAXP, etc. are not present.</p>
+ * 
+ * <p>Note: for an improved version of this utility, please see 
+ * the xml-commons' project Which utility which does the same kind 
+ * of thing but in a much simpler manner.</p>
+ *
+ * @author Shane_Curcuru@us.ibm.com
+ * @version $Id: EnvironmentCheck.java 468646 2006-10-28 06:57:58Z minchau $
+ */
+public class EnvironmentCheck
+{
+
+  /**
+   * Command line runnability: checks for [-out outFilename] arg.
+   * <p>Command line entrypoint; Sets output and calls 
+   * {@link #checkEnvironment(PrintWriter)}.</p>
+   * @param args command line args
+   */
+  public static void main(String[] args)
+  {
+    // Default to System.out, autoflushing
+    PrintWriter sendOutputTo = new PrintWriter(System.out, true);
+
+    // Read our simplistic input args, if supplied
+    for (int i = 0; i < args.length; i++)
+    {
+      if ("-out".equalsIgnoreCase(args[i]))
+      {
+        i++;
+
+        if (i < args.length)
+        {
+          try
+          {
+            sendOutputTo = new PrintWriter(new FileWriter(args[i], true));
+          }
+          catch (Exception e)
+          {
+            System.err.println("# WARNING: -out " + args[i] + " threw "
+                               + e.toString());
+          }
+        }
+        else
+        {
+          System.err.println(
+            "# WARNING: -out argument should have a filename, output sent to console");
+        }
+      }
+    }
+
+    EnvironmentCheck app = new EnvironmentCheck();
+    app.checkEnvironment(sendOutputTo);
+  }
+
+  /**
+   * Programmatic entrypoint: Report on basic Java environment 
+   * and CLASSPATH settings that affect Xalan.
+   *
+   * <p>Note that this class is not advanced enough to tell you 
+   * everything about the environment that affects Xalan, and 
+   * sometimes reports errors that will not actually affect 
+   * Xalan's behavior.  Currently, it very simplistically 
+   * checks the JVM's environment for some basic properties and 
+   * logs them out; it will report a problem if it finds a setting 
+   * or .jar file that is <i>likely</i> to cause problems.</p>
+   *
+   * <p>Advanced users can peruse the code herein to help them 
+   * investigate potential environment problems found; other users 
+   * may simply send the output from this tool along with any bugs 
+   * they submit to help us in the debugging process.</p>
+   *
+   * @param pw PrintWriter to send output to; can be sent to a 
+   * file that will look similar to a Properties file; defaults 
+   * to System.out if null
+   * @return true if your environment appears to have no major 
+   * problems; false if potential environment problems found
+   * @see #getEnvironmentHash()
+   */
+  public boolean checkEnvironment(PrintWriter pw)
+  {
+
+    // Use user-specified output writer if non-null
+    if (null != pw)
+      outWriter = pw;
+
+    // Setup a hash to store various environment information in
+    Hashtable hash = getEnvironmentHash();
+
+    // Check for ERROR keys in the hashtable, and print report
+    boolean environmentHasErrors = writeEnvironmentReport(hash);
+
+    if (environmentHasErrors)
+    {
+      // Note: many logMsg calls have # at the start to 
+      //  fake a property-file like output
+      logMsg("# WARNING: Potential problems found in your environment!");
+      logMsg("#    Check any 'ERROR' items above against the Xalan FAQs");
+      logMsg("#    to correct potential problems with your classes/jars");
+      logMsg("#    http://xml.apache.org/xalan-j/faq.html");
+      if (null != outWriter)
+        outWriter.flush();
+      return false;
+    }
+    else
+    {
+      logMsg("# YAHOO! Your environment seems to be OK.");
+      if (null != outWriter)
+        outWriter.flush();
+      return true;
+    }
+  }
+
+  /**
+   * Fill a hash with basic environment settings that affect Xalan.
+   *
+   * <p>Worker method called from various places.</p>
+   * <p>Various system and CLASSPATH, etc. properties are put into 
+   * the hash as keys with a brief description of the current state 
+   * of that item as the value.  Any serious problems will be put in 
+   * with a key that is prefixed with {@link #ERROR 'ERROR.'} so it
+   * stands out in any resulting report; also a key with just that 
+   * constant will be set as well for any error.</p>
+   * <p>Note that some legitimate cases are flaged as potential 
+   * errors - namely when a developer recompiles xalan.jar on their 
+   * own - and even a non-error state doesn't guaruntee that 
+   * everything in the environment is correct.  But this will help 
+   * point out the most common classpath and system property
+   * problems that we've seen.</p>   
+   *
+   * @return Hashtable full of useful environment info about Xalan 
+   * and related system properties, etc.
+   */
+  public Hashtable getEnvironmentHash()
+  {
+    // Setup a hash to store various environment information in
+    Hashtable hash = new Hashtable();
+
+    // Call various worker methods to fill in the hash
+    //  These are explicitly separate for maintenance and so 
+    //  advanced users could call them standalone
+    checkJAXPVersion(hash);
+    checkProcessorVersion(hash);
+    checkParserVersion(hash);
+    checkAntVersion(hash);
+    checkDOMVersion(hash);
+    checkSAXVersion(hash);
+    checkSystemProperties(hash);
+
+    return hash;
+  }
+
+  /**
+   * Dump a basic Xalan environment report to outWriter.  
+   *
+   * <p>This dumps a simple header and then each of the entries in 
+   * the Hashtable to our PrintWriter; it does special processing 
+   * for entries that are .jars found in the classpath.</p>
+   *
+   * @param h Hashtable of items to report on; presumably
+   * filled in by our various check*() methods
+   * @return true if your environment appears to have no major 
+   * problems; false if potential environment problems found
+   * @see #appendEnvironmentReport(Node, Document, Hashtable)
+   * for an equivalent that appends to a Node instead
+   */
+  protected boolean writeEnvironmentReport(Hashtable h)
+  {
+
+    if (null == h)
+    {
+      logMsg("# ERROR: writeEnvironmentReport called with null Hashtable");
+      return false;
+    }
+
+    boolean errors = false;
+
+    logMsg(
+      "#---- BEGIN writeEnvironmentReport($Revision: 468646 $): Useful stuff found: ----");
+
+    // Fake the Properties-like output
+    for (Enumeration keys = h.keys(); 
+         keys.hasMoreElements();
+        /* no increment portion */
+        )
+    {
+      Object key = keys.nextElement();
+      String keyStr = (String) key;
+      try
+      {
+        // Special processing for classes found..
+        if (keyStr.startsWith(FOUNDCLASSES))
+        {
+          Vector v = (Vector) h.get(keyStr);
+          errors |= logFoundJars(v, keyStr);
+        }
+        // ..normal processing for all other entries
+        else
+        {
+          // Note: we could just check for the ERROR key by itself, 
+          //    since we now set that, but since we have to go 
+          //    through the whole hash anyway, do it this way,
+          //    which is safer for maintenance
+          if (keyStr.startsWith(ERROR))
+          {
+            errors = true;
+          }
+          logMsg(keyStr + "=" + h.get(keyStr));
+        }
+      }
+      catch (Exception e)
+      {
+        logMsg("Reading-" + key + "= threw: " + e.toString());
+      }
+    }
+
+    logMsg(
+      "#----- END writeEnvironmentReport: Useful properties found: -----");
+
+    return errors;
+  }
+
+  /** Prefixed to hash keys that signify serious problems.  */
+  public static final String ERROR = "ERROR.";
+
+  /** Added to descriptions that signify potential problems.  */
+  public static final String WARNING = "WARNING.";
+
+  /** Value for any error found.  */
+  public static final String ERROR_FOUND = "At least one error was found!";
+
+  /** Prefixed to hash keys that signify version numbers.  */
+  public static final String VERSION = "version.";
+
+  /** Prefixed to hash keys that signify .jars found in classpath.  */
+  public static final String FOUNDCLASSES = "foundclasses.";
+
+  /** Marker that a class or .jar was found.  */
+  public static final String CLASS_PRESENT = "present-unknown-version";
+
+  /** Marker that a class or .jar was not found.  */
+  public static final String CLASS_NOTPRESENT = "not-present";
+
+  /** Listing of common .jar files that include Xalan-related classes.  */
+  public String[] jarNames =
+  {
+    "xalan.jar", "xalansamples.jar", "xalanj1compat.jar", "xalanservlet.jar",
+    "serializer.jar",   // Serializer (shared between Xalan & Xerces)
+    "xerces.jar",       // Xerces-J 1.x
+    "xercesImpl.jar",   // Xerces-J 2.x
+    "testxsl.jar", 
+    "crimson.jar", 
+    "lotusxsl.jar", 
+    "jaxp.jar", "parser.jar", "dom.jar", "sax.jar", "xml.jar", 
+    "xml-apis.jar",
+    "xsltc.jar"
+  };
+
+  /**
+   * Print out report of .jars found in a classpath. 
+   *
+   * Takes the information encoded from a checkPathForJars() 
+   * call and dumps it out to our PrintWriter.
+   *
+   * @param v Vector of Hashtables of .jar file info
+   * @param desc description to print out in header
+   *
+   * @return false if OK, true if any .jars were reported 
+   * as having errors
+   * @see #checkPathForJars(String, String[])
+   */
+  protected boolean logFoundJars(Vector v, String desc)
+  {
+
+    if ((null == v) || (v.size() < 1))
+      return false;
+
+    boolean errors = false;
+
+    logMsg("#---- BEGIN Listing XML-related jars in: " + desc + " ----");
+
+    for (int i = 0; i < v.size(); i++)
+    {
+      Hashtable subhash = (Hashtable) v.elementAt(i);
+
+      for (Enumeration keys = subhash.keys(); 
+           keys.hasMoreElements();
+           /* no increment portion */
+          )
+      {
+        Object key = keys.nextElement();
+        String keyStr = (String) key;
+        try
+        {
+          if (keyStr.startsWith(ERROR))
+          {
+            errors = true;
+          }
+          logMsg(keyStr + "=" + subhash.get(keyStr));
+
+        }
+        catch (Exception e)
+        {
+          errors = true;
+          logMsg("Reading-" + key + "= threw: " + e.toString());
+        }
+      }
+    }
+
+    logMsg("#----- END Listing XML-related jars in: " + desc + " -----");
+
+    return errors;
+  }
+
+  /**
+   * Stylesheet extension entrypoint: Dump a basic Xalan 
+   * environment report from getEnvironmentHash() to a Node.  
+   * 
+   * <p>Copy of writeEnvironmentReport that creates a Node suitable 
+   * for other processing instead of a properties-like text output.
+   * </p>
+   * @param container Node to append our report to
+   * @param factory Document providing createElement, etc. services
+   * @param h Hash presumably from {@link #getEnvironmentHash()}
+   * @see #writeEnvironmentReport(Hashtable)
+   * for an equivalent that writes to a PrintWriter instead
+   */
+  public void appendEnvironmentReport(Node container, Document factory, Hashtable h)
+  {
+    if ((null == container) || (null == factory))
+    {
+      return;
+    }
+  
+    try
+    {
+      Element envCheckNode = factory.createElement("EnvironmentCheck");
+      envCheckNode.setAttribute("version", "$Revision: 468646 $");
+      container.appendChild(envCheckNode);
+
+      if (null == h)
+      {
+        Element statusNode = factory.createElement("status");
+        statusNode.setAttribute("result", "ERROR");
+        statusNode.appendChild(factory.createTextNode("appendEnvironmentReport called with null Hashtable!"));
+        envCheckNode.appendChild(statusNode);
+        return;
+      }
+
+      boolean errors = false;
+
+      Element hashNode = factory.createElement("environment");
+      envCheckNode.appendChild(hashNode);
+      
+      for (Enumeration keys = h.keys(); 
+           keys.hasMoreElements();
+          /* no increment portion */
+          )
+      {
+        Object key = keys.nextElement();
+        String keyStr = (String) key;
+        try
+        {
+          // Special processing for classes found..
+          if (keyStr.startsWith(FOUNDCLASSES))
+          {
+            Vector v = (Vector) h.get(keyStr);
+            // errors |= logFoundJars(v, keyStr);
+            errors |= appendFoundJars(hashNode, factory, v, keyStr);
+          }
+          // ..normal processing for all other entries
+          else 
+          {
+            // Note: we could just check for the ERROR key by itself, 
+            //    since we now set that, but since we have to go 
+            //    through the whole hash anyway, do it this way,
+            //    which is safer for maintenance
+            if (keyStr.startsWith(ERROR))
+            {
+              errors = true;
+            }
+            Element node = factory.createElement("item");
+            node.setAttribute("key", keyStr);
+            node.appendChild(factory.createTextNode((String)h.get(keyStr)));
+            hashNode.appendChild(node);
+          }
+        }
+        catch (Exception e)
+        {
+          errors = true;
+          Element node = factory.createElement("item");
+          node.setAttribute("key", keyStr);
+          node.appendChild(factory.createTextNode(ERROR + " Reading " + key + " threw: " + e.toString()));
+          hashNode.appendChild(node);
+        }
+      } // end of for...
+
+      Element statusNode = factory.createElement("status");
+      statusNode.setAttribute("result", (errors ? "ERROR" : "OK" ));
+      envCheckNode.appendChild(statusNode);
+    }
+    catch (Exception e2)
+    {
+      System.err.println("appendEnvironmentReport threw: " + e2.toString());
+      e2.printStackTrace();
+    }
+  }    
+
+  /**
+   * Print out report of .jars found in a classpath. 
+   *
+   * Takes the information encoded from a checkPathForJars() 
+   * call and dumps it out to our PrintWriter.
+   *
+   * @param container Node to append our report to
+   * @param factory Document providing createElement, etc. services
+   * @param v Vector of Hashtables of .jar file info
+   * @param desc description to print out in header
+   *
+   * @return false if OK, true if any .jars were reported 
+   * as having errors
+   * @see #checkPathForJars(String, String[])
+   */
+  protected boolean appendFoundJars(Node container, Document factory, 
+        Vector v, String desc)
+  {
+
+    if ((null == v) || (v.size() < 1))
+      return false;
+
+    boolean errors = false;
+
+    for (int i = 0; i < v.size(); i++)
+    {
+      Hashtable subhash = (Hashtable) v.elementAt(i);
+
+      for (Enumeration keys = subhash.keys(); 
+           keys.hasMoreElements();
+           /* no increment portion */
+          )
+      {
+        Object key = keys.nextElement();
+        try
+        {
+          String keyStr = (String) key;
+          if (keyStr.startsWith(ERROR))
+          {
+            errors = true;
+          }
+          Element node = factory.createElement("foundJar");
+          node.setAttribute("name", keyStr.substring(0, keyStr.indexOf("-")));
+          node.setAttribute("desc", keyStr.substring(keyStr.indexOf("-") + 1));
+          node.appendChild(factory.createTextNode((String)subhash.get(keyStr)));
+          container.appendChild(node);
+        }
+        catch (Exception e)
+        {
+          errors = true;
+          Element node = factory.createElement("foundJar");
+          node.appendChild(factory.createTextNode(ERROR + " Reading " + key + " threw: " + e.toString()));
+          container.appendChild(node);
+        }
+      }
+    }
+    return errors;
+  }
+
+  /**
+   * Fillin hash with info about SystemProperties.  
+   *
+   * Logs java.class.path and other likely paths; then attempts 
+   * to search those paths for .jar files with Xalan-related classes.
+   *
+   * //@todo NOTE: We don't actually search java.ext.dirs for 
+   * //  *.jar files therein! This should be updated
+   *
+   * @param h Hashtable to put information in
+   * @see #jarNames
+   * @see #checkPathForJars(String, String[])
+   */
+  protected void checkSystemProperties(Hashtable h)
+  {
+
+    if (null == h)
+      h = new Hashtable();
+
+    // Grab java version for later use
+    try
+    {
+      String javaVersion = System.getProperty("java.version");
+
+      h.put("java.version", javaVersion);
+    }
+    catch (SecurityException se)
+    {
+
+      // For applet context, etc.
+      h.put(
+        "java.version",
+        "WARNING: SecurityException thrown accessing system version properties");
+    }
+
+    // Printout jar files on classpath(s) that may affect operation
+    //  Do this in order
+    try
+    {
+
+      // This is present in all JVM's
+      String cp = System.getProperty("java.class.path");
+
+      h.put("java.class.path", cp);
+
+      Vector classpathJars = checkPathForJars(cp, jarNames);
+
+      if (null != classpathJars)
+        h.put(FOUNDCLASSES + "java.class.path", classpathJars);
+
+      // Also check for JDK 1.2+ type classpaths
+      String othercp = System.getProperty("sun.boot.class.path");
+
+      if (null != othercp)
+      {
+        h.put("sun.boot.class.path", othercp);
+
+        classpathJars = checkPathForJars(othercp, jarNames);
+
+        if (null != classpathJars)
+          h.put(FOUNDCLASSES + "sun.boot.class.path", classpathJars);
+      }
+
+      //@todo NOTE: We don't actually search java.ext.dirs for 
+      //  *.jar files therein! This should be updated
+      othercp = System.getProperty("java.ext.dirs");
+
+      if (null != othercp)
+      {
+        h.put("java.ext.dirs", othercp);
+
+        classpathJars = checkPathForJars(othercp, jarNames);
+
+        if (null != classpathJars)
+          h.put(FOUNDCLASSES + "java.ext.dirs", classpathJars);
+      }
+
+      //@todo also check other System properties' paths?
+      //  v2 = checkPathForJars(System.getProperty("sun.boot.library.path"), jarNames);   // ?? may not be needed
+      //  v3 = checkPathForJars(System.getProperty("java.library.path"), jarNames);   // ?? may not be needed
+    }
+    catch (SecurityException se2)
+    {
+      // For applet context, etc.
+      h.put(
+        "java.class.path",
+        "WARNING: SecurityException thrown accessing system classpath properties");
+    }
+  }
+
+  /**
+   * Cheap-o listing of specified .jars found in the classpath. 
+   *
+   * cp should be separated by the usual File.pathSeparator.  We 
+   * then do a simplistic search of the path for any requested 
+   * .jar filenames, and return a listing of their names and 
+   * where (apparently) they came from.
+   *
+   * @param cp classpath to search
+   * @param jars array of .jar base filenames to look for
+   *
+   * @return Vector of Hashtables filled with info about found .jars
+   * @see #jarNames
+   * @see #logFoundJars(Vector, String)
+   * @see #appendFoundJars(Node, Document, Vector, String )
+   * @see #getApparentVersion(String, long)
+   */
+  protected Vector checkPathForJars(String cp, String[] jars)
+  {
+
+    if ((null == cp) || (null == jars) || (0 == cp.length())
+            || (0 == jars.length))
+      return null;
+
+    Vector v = new Vector();
+    StringTokenizer st = new StringTokenizer(cp, File.pathSeparator);
+
+    while (st.hasMoreTokens())
+    {
+
+      // Look at each classpath entry for each of our requested jarNames
+      String filename = st.nextToken();
+
+      for (int i = 0; i < jars.length; i++)
+      {
+        if (filename.indexOf(jars[i]) > -1)
+        {
+          File f = new File(filename);
+
+          if (f.exists())
+          {
+
+            // If any requested jarName exists, report on 
+            //  the details of that .jar file
+            try
+            {
+              Hashtable h = new Hashtable(2);
+              // Note "-" char is looked for in appendFoundJars
+              h.put(jars[i] + "-path", f.getAbsolutePath());
+             
+              // We won't bother reporting on the xalan.jar apparent version
+              // since this requires knowing the jar size of the xalan.jar
+              // before we build it. 
+              // For other jars, eg. xml-apis.jar and xercesImpl.jar, we 
+              // report the apparent version of the file we've found
+              if (!("xalan.jar".equalsIgnoreCase(jars[i]))) {              
+                h.put(jars[i] + "-apparent.version",
+                    getApparentVersion(jars[i], f.length()));
+              }
+              v.addElement(h);
+            }
+            catch (Exception e)
+            {
+
+              /* no-op, don't add it  */
+            }
+          }
+          else
+          {
+            Hashtable h = new Hashtable(2);
+            // Note "-" char is looked for in appendFoundJars
+            h.put(jars[i] + "-path", WARNING + " Classpath entry: " 
+                  + filename + " does not exist");
+            h.put(jars[i] + "-apparent.version", CLASS_NOTPRESENT);
+            v.addElement(h);
+          }
+        }
+      }
+    }
+
+    return v;
+  }
+
+  /**
+   * Cheap-o method to determine the product version of a .jar.   
+   *
+   * Currently does a lookup into a local table of some recent 
+   * shipped Xalan builds to determine where the .jar probably 
+   * came from.  Note that if you recompile Xalan or Xerces 
+   * yourself this will likely report a potential error, since 
+   * we can't certify builds other than the ones we ship.
+   * Only reports against selected posted Xalan-J builds.
+   *
+   * //@todo actually look up version info in manifests
+   *
+   * @param jarName base filename of the .jarfile
+   * @param jarSize size of the .jarfile
+   *
+   * @return String describing where the .jar file probably 
+   * came from
+   */
+  protected String getApparentVersion(String jarName, long jarSize)
+  {
+    // If we found a matching size and it's for our 
+    //  jar, then return it's description
+    // Lookup in static jarVersions Hashtable
+    String foundSize = (String) jarVersions.get(new Long(jarSize));
+
+    if ((null != foundSize) && (foundSize.startsWith(jarName)))
+    {
+      return foundSize;
+    }
+    else
+    {
+      if ("xerces.jar".equalsIgnoreCase(jarName)
+              || "xercesImpl.jar".equalsIgnoreCase(jarName))
+//              || "xalan.jar".equalsIgnoreCase(jarName))
+      {
+
+        // For xalan.jar and xerces.jar/xercesImpl.jar, which we ship together:
+        // The jar is not from a shipped copy of xalan-j, so 
+        //  it's up to the user to ensure that it's compatible
+        return jarName + " " + WARNING + CLASS_PRESENT;
+      }
+      else
+      {
+
+        // Otherwise, it's just a jar we don't have the version info calculated for
+        return jarName + " " + CLASS_PRESENT;
+      }
+    }
+  }
+
+  /**
+   * Report version information about JAXP interfaces.
+   *
+   * Currently distinguishes between JAXP 1.0.1 and JAXP 1.1, 
+   * and not found; only tests the interfaces, and does not 
+   * check for reference implementation versions.
+   *
+   * @param h Hashtable to put information in
+   */
+  protected void checkJAXPVersion(Hashtable h)
+  {
+
+    if (null == h)
+      h = new Hashtable();
+
+    final Class noArgs[] = new Class[0];
+    Class clazz = null;
+
+    try
+    {
+      final String JAXP1_CLASS = "javax.xml.parsers.DocumentBuilder";
+      final String JAXP11_METHOD = "getDOMImplementation";
+
+      clazz = ObjectFactory.findProviderClass(
+        JAXP1_CLASS, ObjectFactory.findClassLoader(), true);
+
+      Method method = clazz.getMethod(JAXP11_METHOD, noArgs);
+
+      // If we succeeded, we at least have JAXP 1.1 available
+      h.put(VERSION + "JAXP", "1.1 or higher");
+    }
+    catch (Exception e)
+    {
+      if (null != clazz)
+      {
+
+        // We must have found the class itself, just not the 
+        //  method, so we (probably) have JAXP 1.0.1
+        h.put(ERROR + VERSION + "JAXP", "1.0.1");
+        h.put(ERROR, ERROR_FOUND);
+      }
+      else
+      {
+        // We couldn't even find the class, and don't have 
+        //  any JAXP support at all, or only have the 
+        //  transform half of it
+        h.put(ERROR + VERSION + "JAXP", CLASS_NOTPRESENT);
+        h.put(ERROR, ERROR_FOUND);
+      }
+    }
+  }
+
+  /**
+   * Report product version information from Xalan-J.
+   *
+   * Looks for version info in xalan.jar from Xalan-J products.
+   *
+   * @param h Hashtable to put information in
+   */
+  protected void checkProcessorVersion(Hashtable h)
+  {
+
+    if (null == h)
+      h = new Hashtable();
+
+    try
+    {
+      final String XALAN1_VERSION_CLASS =
+        "org.apache.xalan.xslt.XSLProcessorVersion";
+
+      Class clazz = ObjectFactory.findProviderClass(
+        XALAN1_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
+
+      // Found Xalan-J 1.x, grab it's version fields
+      StringBuffer buf = new StringBuffer();
+      Field f = clazz.getField("PRODUCT");
+
+      buf.append(f.get(null));
+      buf.append(';');
+
+      f = clazz.getField("LANGUAGE");
+
+      buf.append(f.get(null));
+      buf.append(';');
+
+      f = clazz.getField("S_VERSION");
+
+      buf.append(f.get(null));
+      buf.append(';');
+      h.put(VERSION + "xalan1", buf.toString());
+    }
+    catch (Exception e1)
+    {
+      h.put(VERSION + "xalan1", CLASS_NOTPRESENT);
+    }
+
+    try
+    {
+      // NOTE: This is the old Xalan 2.0, 2.1, 2.2 version class, 
+      //    is being replaced by class below
+      final String XALAN2_VERSION_CLASS =
+        "org.apache.xalan.processor.XSLProcessorVersion";
+
+      Class clazz = ObjectFactory.findProviderClass(
+        XALAN2_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
+
+      // Found Xalan-J 2.x, grab it's version fields
+      StringBuffer buf = new StringBuffer();
+      Field f = clazz.getField("S_VERSION");
+      buf.append(f.get(null));
+
+      h.put(VERSION + "xalan2x", buf.toString());
+    }
+    catch (Exception e2)
+    {
+      h.put(VERSION + "xalan2x", CLASS_NOTPRESENT);
+    }
+    try
+    {
+      // NOTE: This is the new Xalan 2.2+ version class
+      final String XALAN2_2_VERSION_CLASS =
+        "org.apache.xalan.Version";
+      final String XALAN2_2_VERSION_METHOD = "getVersion";
+      final Class noArgs[] = new Class[0];
+
+      Class clazz = ObjectFactory.findProviderClass(
+        XALAN2_2_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
+
+      Method method = clazz.getMethod(XALAN2_2_VERSION_METHOD, noArgs);
+      Object returnValue = method.invoke(null, new Object[0]);
+
+      h.put(VERSION + "xalan2_2", (String)returnValue);
+    }
+    catch (Exception e2)
+    {
+      h.put(VERSION + "xalan2_2", CLASS_NOTPRESENT);
+    }
+  }
+
+  /**
+   * Report product version information from common parsers.
+   *
+   * Looks for version info in xerces.jar/xercesImpl.jar/crimson.jar.
+   *
+   * //@todo actually look up version info in crimson manifest
+   *
+   * @param h Hashtable to put information in
+   */
+  protected void checkParserVersion(Hashtable h)
+  {
+
+    if (null == h)
+      h = new Hashtable();
+
+    try
+    {
+      final String XERCES1_VERSION_CLASS = "org.apache.xerces.framework.Version";
+
+      Class clazz = ObjectFactory.findProviderClass(
+        XERCES1_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
+
+      // Found Xerces-J 1.x, grab it's version fields
+      Field f = clazz.getField("fVersion");
+      String parserVersion = (String) f.get(null);
+
+      h.put(VERSION + "xerces1", parserVersion);
+    }
+    catch (Exception e)
+    {
+      h.put(VERSION + "xerces1", CLASS_NOTPRESENT);
+    }
+
+    // Look for xerces1 and xerces2 parsers separately
+    try
+    {
+      final String XERCES2_VERSION_CLASS = "org.apache.xerces.impl.Version";
+
+      Class clazz = ObjectFactory.findProviderClass(
+        XERCES2_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
+
+      // Found Xerces-J 2.x, grab it's version fields
+      Field f = clazz.getField("fVersion");
+      String parserVersion = (String) f.get(null);
+
+      h.put(VERSION + "xerces2", parserVersion);
+    }
+    catch (Exception e)
+    {
+      h.put(VERSION + "xerces2", CLASS_NOTPRESENT);
+    }
+
+    try
+    {
+      final String CRIMSON_CLASS = "org.apache.crimson.parser.Parser2";
+
+      Class clazz = ObjectFactory.findProviderClass(
+        CRIMSON_CLASS, ObjectFactory.findClassLoader(), true);
+
+      //@todo determine specific crimson version
+      h.put(VERSION + "crimson", CLASS_PRESENT);
+    }
+    catch (Exception e)
+    {
+      h.put(VERSION + "crimson", CLASS_NOTPRESENT);
+    }
+  }
+
+  /**
+   * Report product version information from Ant.
+   *
+   * @param h Hashtable to put information in
+   */
+  protected void checkAntVersion(Hashtable h)
+  {
+
+    if (null == h)
+      h = new Hashtable();
+
+    try
+    {
+      final String ANT_VERSION_CLASS = "org.apache.tools.ant.Main";
+      final String ANT_VERSION_METHOD = "getAntVersion"; // noArgs
+      final Class noArgs[] = new Class[0];
+
+      Class clazz = ObjectFactory.findProviderClass(
+        ANT_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
+
+      Method method = clazz.getMethod(ANT_VERSION_METHOD, noArgs);
+      Object returnValue = method.invoke(null, new Object[0]);
+
+      h.put(VERSION + "ant", (String)returnValue);
+    }
+    catch (Exception e)
+    {
+      h.put(VERSION + "ant", CLASS_NOTPRESENT);
+    }
+  }
+
+  /**
+   * Report version info from DOM interfaces. 
+   *
+   * Currently distinguishes between pre-DOM level 2, the DOM 
+   * level 2 working draft, the DOM level 2 final draft, 
+   * and not found.
+   *
+   * @param h Hashtable to put information in
+   */
+  protected void checkDOMVersion(Hashtable h)
+  {
+
+    if (null == h)
+      h = new Hashtable();
+
+    final String DOM_LEVEL2_CLASS = "org.w3c.dom.Document";
+    final String DOM_LEVEL2_METHOD = "createElementNS";  // String, String
+    final String DOM_LEVEL2WD_CLASS = "org.w3c.dom.Node";
+    final String DOM_LEVEL2WD_METHOD = "supported";  // String, String
+    final String DOM_LEVEL2FD_CLASS = "org.w3c.dom.Node";
+    final String DOM_LEVEL2FD_METHOD = "isSupported";  // String, String
+    final Class twoStringArgs[] = { java.lang.String.class,
+                                    java.lang.String.class };
+
+    try
+    {
+      Class clazz = ObjectFactory.findProviderClass(
+        DOM_LEVEL2_CLASS, ObjectFactory.findClassLoader(), true);
+
+      Method method = clazz.getMethod(DOM_LEVEL2_METHOD, twoStringArgs);
+
+      // If we succeeded, we have loaded interfaces from a 
+      //  level 2 DOM somewhere
+      h.put(VERSION + "DOM", "2.0");
+
+      try
+      {
+        // Check for the working draft version, which is 
+        //  commonly found, but won't work anymore
+        clazz = ObjectFactory.findProviderClass(
+          DOM_LEVEL2WD_CLASS, ObjectFactory.findClassLoader(), true);
+
+        method = clazz.getMethod(DOM_LEVEL2WD_METHOD, twoStringArgs);
+
+        h.put(ERROR + VERSION + "DOM.draftlevel", "2.0wd");
+        h.put(ERROR, ERROR_FOUND);
+      }
+      catch (Exception e2)
+      {
+        try
+        {
+          // Check for the final draft version as well
+          clazz = ObjectFactory.findProviderClass(
+            DOM_LEVEL2FD_CLASS, ObjectFactory.findClassLoader(), true);
+
+          method = clazz.getMethod(DOM_LEVEL2FD_METHOD, twoStringArgs);
+
+          h.put(VERSION + "DOM.draftlevel", "2.0fd");
+        }
+        catch (Exception e3)
+        {
+          h.put(ERROR + VERSION + "DOM.draftlevel", "2.0unknown");
+          h.put(ERROR, ERROR_FOUND);
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      h.put(ERROR + VERSION + "DOM",
+            "ERROR attempting to load DOM level 2 class: " + e.toString());
+      h.put(ERROR, ERROR_FOUND);
+    }
+
+    //@todo load an actual DOM implmementation and query it as well
+    //@todo load an actual DOM implmementation and check if 
+    //  isNamespaceAware() == true, which is needed to parse 
+    //  xsl stylesheet files into a DOM
+  }
+
+  /**
+   * Report version info from SAX interfaces. 
+   *
+   * Currently distinguishes between SAX 2, SAX 2.0beta2, 
+   * SAX1, and not found.
+   *
+   * @param h Hashtable to put information in
+   */
+  protected void checkSAXVersion(Hashtable h)
+  {
+
+    if (null == h)
+      h = new Hashtable();
+
+    final String SAX_VERSION1_CLASS = "org.xml.sax.Parser";
+    final String SAX_VERSION1_METHOD = "parse";  // String
+    final String SAX_VERSION2_CLASS = "org.xml.sax.XMLReader";
+    final String SAX_VERSION2_METHOD = "parse";  // String
+    final String SAX_VERSION2BETA_CLASSNF = "org.xml.sax.helpers.AttributesImpl";
+    final String SAX_VERSION2BETA_METHODNF = "setAttributes";  // Attributes
+    final Class oneStringArg[] = { java.lang.String.class };
+    // Note this introduces a minor compile dependency on SAX...
+    final Class attributesArg[] = { org.xml.sax.Attributes.class };
+
+    try
+    {
+      // This method was only added in the final SAX 2.0 release; 
+      //  see changes.html "Changes from SAX 2.0beta2 to SAX 2.0prerelease"
+      Class clazz = ObjectFactory.findProviderClass(
+        SAX_VERSION2BETA_CLASSNF, ObjectFactory.findClassLoader(), true);
+
+      Method method = clazz.getMethod(SAX_VERSION2BETA_METHODNF, attributesArg);
+
+      // If we succeeded, we have loaded interfaces from a 
+      //  real, final SAX version 2.0 somewhere
+      h.put(VERSION + "SAX", "2.0");
+    }
+    catch (Exception e)
+    {
+      // If we didn't find the SAX 2.0 class, look for a 2.0beta2
+      h.put(ERROR + VERSION + "SAX",
+            "ERROR attempting to load SAX version 2 class: " + e.toString());
+      h.put(ERROR, ERROR_FOUND);
+            
+      try
+      {
+        Class clazz = ObjectFactory.findProviderClass(
+          SAX_VERSION2_CLASS, ObjectFactory.findClassLoader(), true);
+
+        Method method = clazz.getMethod(SAX_VERSION2_METHOD, oneStringArg);
+
+        // If we succeeded, we have loaded interfaces from a 
+        //  SAX version 2.0beta2 or earlier; these might work but 
+        //  you should really have the final SAX 2.0 
+        h.put(VERSION + "SAX-backlevel", "2.0beta2-or-earlier");
+      }
+      catch (Exception e2)
+      {
+        // If we didn't find the SAX 2.0beta2 class, look for a 1.0 one
+        h.put(ERROR + VERSION + "SAX",
+              "ERROR attempting to load SAX version 2 class: " + e.toString());
+        h.put(ERROR, ERROR_FOUND);
+          
+        try
+        {
+          Class clazz = ObjectFactory.findProviderClass(
+            SAX_VERSION1_CLASS, ObjectFactory.findClassLoader(), true);
+
+          Method method = clazz.getMethod(SAX_VERSION1_METHOD, oneStringArg);
+
+          // If we succeeded, we have loaded interfaces from a 
+          //  SAX version 1.0 somewhere; which won't work very 
+          //  well for JAXP 1.1 or beyond!
+          h.put(VERSION + "SAX-backlevel", "1.0");
+        }
+        catch (Exception e3)
+        {
+          // If we didn't find the SAX 2.0 class, look for a 1.0 one
+          // Note that either 1.0 or no SAX are both errors
+          h.put(ERROR + VERSION + "SAX-backlevel",
+                "ERROR attempting to load SAX version 1 class: " + e3.toString());
+            
+        }
+      }
+    }
+  }
+
+  /** 
+   * Manual table of known .jar sizes.  
+   * Only includes shipped versions of certain projects.
+   * key=jarsize, value=jarname ' from ' distro name
+   * Note assumption: two jars cannot have the same size!
+   *
+   * @see #getApparentVersion(String, long)
+   */
+  private static Hashtable jarVersions = new Hashtable();
+
+  /** 
+   * Static initializer for jarVersions table.  
+   * Doing this just once saves time and space.
+   *
+   * @see #getApparentVersion(String, long)
+   */
+  static 
+  {
+    // Note: hackish Hashtable, this could use improvement
+    jarVersions.put(new Long(857192), "xalan.jar from xalan-j_1_1");
+    jarVersions.put(new Long(440237), "xalan.jar from xalan-j_1_2");
+    jarVersions.put(new Long(436094), "xalan.jar from xalan-j_1_2_1");
+    jarVersions.put(new Long(426249), "xalan.jar from xalan-j_1_2_2");
+    jarVersions.put(new Long(702536), "xalan.jar from xalan-j_2_0_0");
+    jarVersions.put(new Long(720930), "xalan.jar from xalan-j_2_0_1");
+    jarVersions.put(new Long(732330), "xalan.jar from xalan-j_2_1_0");
+    jarVersions.put(new Long(872241), "xalan.jar from xalan-j_2_2_D10");
+    jarVersions.put(new Long(882739), "xalan.jar from xalan-j_2_2_D11");
+    jarVersions.put(new Long(923866), "xalan.jar from xalan-j_2_2_0");
+    jarVersions.put(new Long(905872), "xalan.jar from xalan-j_2_3_D1");
+    jarVersions.put(new Long(906122), "xalan.jar from xalan-j_2_3_0");
+    jarVersions.put(new Long(906248), "xalan.jar from xalan-j_2_3_1");
+    jarVersions.put(new Long(983377), "xalan.jar from xalan-j_2_4_D1");    
+    jarVersions.put(new Long(997276), "xalan.jar from xalan-j_2_4_0");
+    jarVersions.put(new Long(1031036), "xalan.jar from xalan-j_2_4_1");    
+    // Stop recording xalan.jar sizes as of Xalan Java 2.5.0    
+
+    jarVersions.put(new Long(596540), "xsltc.jar from xalan-j_2_2_0");
+    jarVersions.put(new Long(590247), "xsltc.jar from xalan-j_2_3_D1");
+    jarVersions.put(new Long(589914), "xsltc.jar from xalan-j_2_3_0");
+    jarVersions.put(new Long(589915), "xsltc.jar from xalan-j_2_3_1");
+    jarVersions.put(new Long(1306667), "xsltc.jar from xalan-j_2_4_D1");     
+    jarVersions.put(new Long(1328227), "xsltc.jar from xalan-j_2_4_0");
+    jarVersions.put(new Long(1344009), "xsltc.jar from xalan-j_2_4_1");
+    jarVersions.put(new Long(1348361), "xsltc.jar from xalan-j_2_5_D1");    
+    // Stop recording xsltc.jar sizes as of Xalan Java 2.5.0
+
+    jarVersions.put(new Long(1268634), "xsltc.jar-bundled from xalan-j_2_3_0");
+
+    jarVersions.put(new Long(100196), "xml-apis.jar from xalan-j_2_2_0 or xalan-j_2_3_D1");
+    jarVersions.put(new Long(108484), "xml-apis.jar from xalan-j_2_3_0, or xalan-j_2_3_1 from xml-commons-1.0.b2");
+    jarVersions.put(new Long(109049), "xml-apis.jar from xalan-j_2_4_0 from xml-commons RIVERCOURT1 branch");
+    jarVersions.put(new Long(113749), "xml-apis.jar from xalan-j_2_4_1 from factoryfinder-build of xml-commons RIVERCOURT1");
+    jarVersions.put(new Long(124704), "xml-apis.jar from tck-jaxp-1_2_0 branch of xml-commons");
+    jarVersions.put(new Long(124724), "xml-apis.jar from tck-jaxp-1_2_0 branch of xml-commons, tag: xml-commons-external_1_2_01");
+    jarVersions.put(new Long(194205), "xml-apis.jar from head branch of xml-commons, tag: xml-commons-external_1_3_02");
+
+    // If the below were more common I would update it to report 
+    //  errors better; but this is so old hardly anyone has it
+    jarVersions.put(new Long(424490), "xalan.jar from Xerces Tools releases - ERROR:DO NOT USE!");
+
+    jarVersions.put(new Long(1591855), "xerces.jar from xalan-j_1_1 from xerces-1...");
+    jarVersions.put(new Long(1498679), "xerces.jar from xalan-j_1_2 from xerces-1_2_0.bin");
+    jarVersions.put(new Long(1484896), "xerces.jar from xalan-j_1_2_1 from xerces-1_2_1.bin");
+    jarVersions.put(new Long(804460),  "xerces.jar from xalan-j_1_2_2 from xerces-1_2_2.bin");
+    jarVersions.put(new Long(1499244), "xerces.jar from xalan-j_2_0_0 from xerces-1_2_3.bin");
+    jarVersions.put(new Long(1605266), "xerces.jar from xalan-j_2_0_1 from xerces-1_3_0.bin");
+    jarVersions.put(new Long(904030), "xerces.jar from xalan-j_2_1_0 from xerces-1_4.bin");
+    jarVersions.put(new Long(904030), "xerces.jar from xerces-1_4_0.bin");
+    jarVersions.put(new Long(1802885), "xerces.jar from xerces-1_4_2.bin");
+    jarVersions.put(new Long(1734594), "xerces.jar from Xerces-J-bin.2.0.0.beta3");
+    jarVersions.put(new Long(1808883), "xerces.jar from xalan-j_2_2_D10,D11,D12 or xerces-1_4_3.bin");
+    jarVersions.put(new Long(1812019), "xerces.jar from xalan-j_2_2_0");
+    jarVersions.put(new Long(1720292), "xercesImpl.jar from xalan-j_2_3_D1");
+    jarVersions.put(new Long(1730053), "xercesImpl.jar from xalan-j_2_3_0 or xalan-j_2_3_1 from xerces-2_0_0");
+    jarVersions.put(new Long(1728861), "xercesImpl.jar from xalan-j_2_4_D1 from xerces-2_0_1");    
+    jarVersions.put(new Long(972027), "xercesImpl.jar from xalan-j_2_4_0 from xerces-2_1");
+    jarVersions.put(new Long(831587), "xercesImpl.jar from xalan-j_2_4_1 from xerces-2_2"); 
+    jarVersions.put(new Long(891817), "xercesImpl.jar from xalan-j_2_5_D1 from xerces-2_3");  
+    jarVersions.put(new Long(895924), "xercesImpl.jar from xerces-2_4");
+    jarVersions.put(new Long(1010806), "xercesImpl.jar from Xerces-J-bin.2.6.2"); 
+    jarVersions.put(new Long(1203860), "xercesImpl.jar from Xerces-J-bin.2.7.1");                           
+
+    jarVersions.put(new Long(37485), "xalanj1compat.jar from xalan-j_2_0_0");
+    jarVersions.put(new Long(38100), "xalanj1compat.jar from xalan-j_2_0_1");
+
+    jarVersions.put(new Long(18779), "xalanservlet.jar from xalan-j_2_0_0");
+    jarVersions.put(new Long(21453), "xalanservlet.jar from xalan-j_2_0_1");
+    jarVersions.put(new Long(24826), "xalanservlet.jar from xalan-j_2_3_1 or xalan-j_2_4_1");    
+    jarVersions.put(new Long(24831), "xalanservlet.jar from xalan-j_2_4_1");
+    // Stop recording xalanservlet.jar sizes as of Xalan Java 2.5.0; now a .war file
+    
+    // For those who've downloaded JAXP from sun
+    jarVersions.put(new Long(5618), "jaxp.jar from jaxp1.0.1");
+    jarVersions.put(new Long(136133), "parser.jar from jaxp1.0.1");
+    jarVersions.put(new Long(28404), "jaxp.jar from jaxp-1.1");
+    jarVersions.put(new Long(187162), "crimson.jar from jaxp-1.1");
+    jarVersions.put(new Long(801714), "xalan.jar from jaxp-1.1");
+    jarVersions.put(new Long(196399), "crimson.jar from crimson-1.1.1");
+    jarVersions.put(new Long(33323), "jaxp.jar from crimson-1.1.1 or jakarta-ant-1.4.1b1");
+    jarVersions.put(new Long(152717), "crimson.jar from crimson-1.1.2beta2");
+    jarVersions.put(new Long(88143), "xml-apis.jar from crimson-1.1.2beta2");
+    jarVersions.put(new Long(206384), "crimson.jar from crimson-1.1.3 or jakarta-ant-1.4.1b1");
+
+    // jakarta-ant: since many people use ant these days
+    jarVersions.put(new Long(136198), "parser.jar from jakarta-ant-1.3 or 1.2");
+    jarVersions.put(new Long(5537), "jaxp.jar from jakarta-ant-1.3 or 1.2");
+  }
+
+  /** Simple PrintWriter we send output to; defaults to System.out.  */
+  protected PrintWriter outWriter = new PrintWriter(System.out, true);
+
+  /**
+   * Bottleneck output: calls outWriter.println(s).  
+   * @param s String to print
+   */
+  protected void logMsg(String s)
+  {
+    outWriter.println(s);
+  }
+}
diff --git a/src/main/java/org/apache/xalan/xslt/ObjectFactory.java b/src/main/java/org/apache/xalan/xslt/ObjectFactory.java
new file mode 100755
index 0000000..1029b87
--- /dev/null
+++ b/src/main/java/org/apache/xalan/xslt/ObjectFactory.java
@@ -0,0 +1,661 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ObjectFactory.java 468646 2006-10-28 06:57:58Z minchau $
+ */
+
+package org.apache.xalan.xslt;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+
+import java.util.Properties;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ * This class is duplicated for each JAXP subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the JAXP
+ * API.
+ * <p>
+ * This code is designed to implement the JAXP 1.1 spec pluggability
+ * feature and is designed to run on JDK version 1.1 and
+ * later, and to compile on JDK 1.2 and onward.  
+ * The code also runs both as part of an unbundled jar file and
+ * when bundled as part of the JDK.
+ * <p>
+ * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
+ * class and modified to be used as a general utility for creating objects 
+ * dynamically.
+ *
+ * @version $Id: ObjectFactory.java 468646 2006-10-28 06:57:58Z minchau $
+ */
+class ObjectFactory {
+
+    //
+    // Constants
+    //
+
+    // name of default properties file to look for in JDK's jre/lib directory
+    private static final String DEFAULT_PROPERTIES_FILENAME =
+                                                     "xalan.properties";
+
+    private static final String SERVICES_PATH = "META-INF/services/";
+
+    /** Set to true for debugging */
+    private static final boolean DEBUG = false;
+
+    /** cache the contents of the xalan.properties file.
+     *  Until an attempt has been made to read this file, this will
+     * be null; if the file does not exist or we encounter some other error
+     * during the read, this will be empty.
+     */
+    private static Properties fXalanProperties = null;
+
+    /***
+     * Cache the time stamp of the xalan.properties file so
+     * that we know if it's been modified and can invalidate
+     * the cache when necessary.
+     */
+    private static long fLastModified = -1;
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, String fallbackClassName)
+        throws ConfigurationError {
+        return createObject(factoryId, null, fallbackClassName);
+    } // createObject(String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, 
+                                      String propertiesFilename,
+                                      String fallbackClassName)
+        throws ConfigurationError
+    {
+        Class factoryClass = lookUpFactoryClass(factoryId,
+                                                propertiesFilename,
+                                                fallbackClassName);
+
+        if (factoryClass == null) {
+            throw new ConfigurationError(
+                "Provider for " + factoryId + " cannot be found", null);
+        }
+
+        try{
+            Object instance = factoryClass.newInstance();
+            debugPrintln("created new instance of factory " + factoryId);
+            return instance;
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider for factory " + factoryId
+                    + " could not be instantiated: " + x, x);
+        }
+    } // createObject(String,String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId) 
+        throws ConfigurationError
+    {
+        return lookUpFactoryClass(factoryId, null, null);
+    } // lookUpFactoryClass(String):Class
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId,
+                                           String propertiesFilename,
+                                           String fallbackClassName)
+        throws ConfigurationError
+    {
+        String factoryClassName = lookUpFactoryClassName(factoryId,
+                                                         propertiesFilename,
+                                                         fallbackClassName);
+        ClassLoader cl = findClassLoader();
+
+        if (factoryClassName == null) {
+            factoryClassName = fallbackClassName;
+        }
+
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(factoryClassName,
+                                                    cl,
+                                                    true);
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return providerClass;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + factoryClassName + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider "+factoryClassName+" could not be instantiated: "+x,
+                x);
+        }
+    } // lookUpFactoryClass(String,String,String):Class
+
+    /**
+     * Finds the name of the required implementation class in the specified
+     * order.  The specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return name of class that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static String lookUpFactoryClassName(String factoryId,
+                                                String propertiesFilename,
+                                                String fallbackClassName)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Use the system property first
+        try {
+            String systemProp = ss.getSystemProperty(factoryId);
+            if (systemProp != null) {
+                debugPrintln("found system property, value=" + systemProp);
+                return systemProp;
+            }
+        } catch (SecurityException se) {
+            // Ignore and continue w/ next location
+        }
+
+        // Try to read from propertiesFilename, or
+        // $java.home/lib/xalan.properties
+        String factoryClassName = null;
+        // no properties file name specified; use
+        // $JAVA_HOME/lib/xalan.properties:
+        if (propertiesFilename == null) {
+            File propertiesFile = null;
+            boolean propertiesFileExists = false;
+            try {
+                String javah = ss.getSystemProperty("java.home");
+                propertiesFilename = javah + File.separator +
+                    "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
+                propertiesFile = new File(propertiesFilename);
+                propertiesFileExists = ss.getFileExists(propertiesFile);
+            } catch (SecurityException e) {
+                // try again...
+                fLastModified = -1;
+                fXalanProperties = null;
+            }
+
+            synchronized (ObjectFactory.class) {
+                boolean loadProperties = false;
+                FileInputStream fis = null;
+                try {
+                    // file existed last time
+                    if(fLastModified >= 0) {
+                        if(propertiesFileExists &&
+                                (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
+                            loadProperties = true;
+                        } else {
+                            // file has stopped existing...
+                            if(!propertiesFileExists) {
+                                fLastModified = -1;
+                                fXalanProperties = null;
+                            } // else, file wasn't modified!
+                        }
+                    } else {
+                        // file has started to exist:
+                        if(propertiesFileExists) {
+                            loadProperties = true;
+                            fLastModified = ss.getLastModified(propertiesFile);
+                        } // else, nothing's changed
+                    }
+                    if(loadProperties) {
+                        // must never have attempted to read xalan.properties
+                        // before (or it's outdeated)
+                        fXalanProperties = new Properties();
+                        fis = ss.getFileInputStream(propertiesFile);
+                        fXalanProperties.load(fis);
+                    }
+	        } catch (Exception x) {
+	            fXalanProperties = null;
+	            fLastModified = -1;
+                    // assert(x instanceof FileNotFoundException
+	            //        || x instanceof SecurityException)
+	            // In both cases, ignore and continue w/ next location
+	        }
+                finally {
+                    // try to close the input stream if one was opened.
+                    if (fis != null) {
+                        try {
+                            fis.close();
+                        }
+                        // Ignore the exception.
+                        catch (IOException exc) {}
+                    }
+                }	            
+            }
+            if(fXalanProperties != null) {
+                factoryClassName = fXalanProperties.getProperty(factoryId);
+            }
+        } else {
+            FileInputStream fis = null;
+            try {
+                fis = ss.getFileInputStream(new File(propertiesFilename));
+                Properties props = new Properties();
+                props.load(fis);
+                factoryClassName = props.getProperty(factoryId);
+            } catch (Exception x) {
+                // assert(x instanceof FileNotFoundException
+                //        || x instanceof SecurityException)
+                // In both cases, ignore and continue w/ next location
+            }
+            finally {
+                // try to close the input stream if one was opened.
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    }
+                    // Ignore the exception.
+                    catch (IOException exc) {}
+                }
+            }               
+        }
+        if (factoryClassName != null) {
+            debugPrintln("found in " + propertiesFilename + ", value="
+                          + factoryClassName);
+            return factoryClassName;
+        }
+
+        // Try Jar Service Provider Mechanism
+        return findJarServiceProviderName(factoryId);
+    } // lookUpFactoryClass(String,String):String
+
+    //
+    // Private static methods
+    //
+
+    /** Prints a message to standard error if debugging is enabled. */
+    private static void debugPrintln(String msg) {
+        if (DEBUG) {
+            System.err.println("JAXP: " + msg);
+        }
+    } // debugPrintln(String)
+
+    /**
+     * Figure out which ClassLoader to use.  For JDK 1.2 and later use
+     * the context ClassLoader.
+     */
+    static ClassLoader findClassLoader()
+        throws ConfigurationError
+    { 
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Figure out which ClassLoader to use for loading the provider
+        // class.  If there is a Context ClassLoader then use it.
+        ClassLoader context = ss.getContextClassLoader();
+        ClassLoader system = ss.getSystemClassLoader();
+
+        ClassLoader chain = system;
+        while (true) {
+            if (context == chain) {
+                // Assert: we are on JDK 1.1 or we have no Context ClassLoader
+                // or any Context ClassLoader in chain of system classloader
+                // (including extension ClassLoader) so extend to widest
+                // ClassLoader (always look in system ClassLoader if Xalan
+                // is in boot/extension/system classpath and in current
+                // ClassLoader otherwise); normal classloaders delegate
+                // back to system ClassLoader first so this widening doesn't
+                // change the fact that context ClassLoader will be consulted
+                ClassLoader current = ObjectFactory.class.getClassLoader();
+
+                chain = system;
+                while (true) {
+                    if (current == chain) {
+                        // Assert: Current ClassLoader in chain of
+                        // boot/extension/system ClassLoaders
+                        return system;
+                    }
+                    if (chain == null) {
+                        break;
+                    }
+                    chain = ss.getParentClassLoader(chain);
+                }
+
+                // Assert: Current ClassLoader not in chain of
+                // boot/extension/system ClassLoaders
+                return current;
+            }
+
+            if (chain == null) {
+                // boot ClassLoader reached
+                break;
+            }
+
+            // Check for any extension ClassLoaders in chain up to
+            // boot ClassLoader
+            chain = ss.getParentClassLoader(chain);
+        };
+
+        // Assert: Context ClassLoader not in chain of
+        // boot/extension/system ClassLoaders
+        return context;
+    } // findClassLoader():ClassLoader
+
+    /**
+     * Create an instance of a class using the specified ClassLoader
+     */ 
+    static Object newInstance(String className, ClassLoader cl,
+                                      boolean doFallback)
+        throws ConfigurationError
+    {
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(className, cl, doFallback);
+            Object instance = providerClass.newInstance();
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return instance;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + className + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider " + className + " could not be instantiated: " + x,
+                x);
+        }
+    }
+
+    /**
+     * Find a Class using the specified ClassLoader
+     */ 
+    static Class findProviderClass(String className, ClassLoader cl,
+                                           boolean doFallback)
+        throws ClassNotFoundException, ConfigurationError
+    {   
+        //throw security exception if the calling thread is not allowed to access the
+        //class. Restrict the access to the package classes as specified in java.security policy.
+        SecurityManager security = System.getSecurityManager();
+        try{
+                if (security != null){
+                    final int lastDot = className.lastIndexOf(".");
+                    String packageName = className;
+                    if (lastDot != -1) packageName = className.substring(0, lastDot);
+                    security.checkPackageAccess(packageName);
+                 }   
+        }catch(SecurityException e){
+            throw e;
+        }
+        
+        Class providerClass;
+        if (cl == null) {
+            // XXX Use the bootstrap ClassLoader.  There is no way to
+            // load a class using the bootstrap ClassLoader that works
+            // in both JDK 1.1 and Java 2.  However, this should still
+            // work b/c the following should be true:
+            //
+            // (cl == null) iff current ClassLoader == null
+            //
+            // Thus Class.forName(String) will use the current
+            // ClassLoader which will be the bootstrap ClassLoader.
+            providerClass = Class.forName(className);
+        } else {
+            try {
+                providerClass = cl.loadClass(className);
+            } catch (ClassNotFoundException x) {
+                if (doFallback) {
+                    // Fall back to current classloader
+                    ClassLoader current = ObjectFactory.class.getClassLoader();
+                    if (current == null) {
+                        providerClass = Class.forName(className);
+                    } else if (cl != current) {
+                        cl = current;
+                        providerClass = cl.loadClass(className);
+                    } else {
+                        throw x;
+                    }
+                } else {
+                    throw x;
+                }
+            }
+        }
+
+        return providerClass;
+    }
+
+    /**
+     * Find the name of service provider using Jar Service Provider Mechanism
+     *
+     * @return instance of provider class if found or null
+     */
+    private static String findJarServiceProviderName(String factoryId)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+        String serviceId = SERVICES_PATH + factoryId;
+        InputStream is = null;
+
+        // First try the Context ClassLoader
+        ClassLoader cl = findClassLoader();
+
+        is = ss.getResourceAsStream(cl, serviceId);
+
+        // If no provider found then try the current ClassLoader
+        if (is == null) {
+            ClassLoader current = ObjectFactory.class.getClassLoader();
+            if (cl != current) {
+                cl = current;
+                is = ss.getResourceAsStream(cl, serviceId);
+            }
+        }
+
+        if (is == null) {
+            // No provider found
+            return null;
+        }
+
+        debugPrintln("found jar resource=" + serviceId +
+               " using ClassLoader: " + cl);
+
+        // Read the service provider name in UTF-8 as specified in
+        // the jar spec.  Unfortunately this fails in Microsoft
+        // VJ++, which does not implement the UTF-8
+        // encoding. Theoretically, we should simply let it fail in
+        // that case, since the JVM is obviously broken if it
+        // doesn't support such a basic standard.  But since there
+        // are still some users attempting to use VJ++ for
+        // development, we have dropped in a fallback which makes a
+        // second attempt using the platform's default encoding. In
+        // VJ++ this is apparently ASCII, which is a subset of
+        // UTF-8... and since the strings we'll be reading here are
+        // also primarily limited to the 7-bit ASCII range (at
+        // least, in English versions), this should work well
+        // enough to keep us on the air until we're ready to
+        // officially decommit from VJ++. [Edited comment from
+        // jkesselm]
+        BufferedReader rd;
+        try {
+            rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+        } catch (java.io.UnsupportedEncodingException e) {
+            rd = new BufferedReader(new InputStreamReader(is));
+        }
+        
+        String factoryClassName = null;
+        try {
+            // XXX Does not handle all possible input as specified by the
+            // Jar Service Provider specification
+            factoryClassName = rd.readLine();
+        } catch (IOException x) {
+            // No provider found
+            return null;
+        }
+        finally {
+            try {
+                // try to close the reader.
+                rd.close();
+            }
+            // Ignore the exception.
+            catch (IOException exc) {}
+        }          
+
+        if (factoryClassName != null &&
+            ! "".equals(factoryClassName)) {
+            debugPrintln("found in resource, value="
+                   + factoryClassName);
+
+            // Note: here we do not want to fall back to the current
+            // ClassLoader because we want to avoid the case where the
+            // resource file was found using one ClassLoader and the
+            // provider class was instantiated using a different one.
+            return factoryClassName;
+        }
+
+        // No provider found
+        return null;
+    }
+
+    //
+    // Classes
+    //
+
+    /**
+     * A configuration error.
+     */
+    static class ConfigurationError 
+        extends Error {
+                static final long serialVersionUID = 2276082712114762609L;
+        //
+        // Data
+        //
+
+        /** Exception. */
+        private Exception exception;
+
+        //
+        // Constructors
+        //
+
+        /**
+         * Construct a new instance with the specified detail string and
+         * exception.
+         */
+        ConfigurationError(String msg, Exception x) {
+            super(msg);
+            this.exception = x;
+        } // <init>(String,Exception)
+
+        //
+        // Public methods
+        //
+
+        /** Returns the exception associated to this error. */
+        Exception getException() {
+            return exception;
+        } // getException():Exception
+
+    } // class ConfigurationError
+
+} // class ObjectFactory
diff --git a/src/main/java/org/apache/xalan/xslt/SecuritySupport.java b/src/main/java/org/apache/xalan/xslt/SecuritySupport.java
new file mode 100755
index 0000000..49e207a
--- /dev/null
+++ b/src/main/java/org/apache/xalan/xslt/SecuritySupport.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SecuritySupport.java 468646 2006-10-28 06:57:58Z minchau $
+ */
+
+package org.apache.xalan.xslt;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Base class with security related methods that work on JDK 1.1.
+ */
+class SecuritySupport {
+
+    /*
+     * Make this of type Object so that the verifier won't try to
+     * prove its type, thus possibly trying to load the SecuritySupport12
+     * class.
+     */
+    private static final Object securitySupport;
+
+    static {
+	SecuritySupport ss = null;
+	try {
+	    Class c = Class.forName("java.security.AccessController");
+	    // if that worked, we're on 1.2.
+	    /*
+	    // don't reference the class explicitly so it doesn't
+	    // get dragged in accidentally.
+	    c = Class.forName("javax.mail.SecuritySupport12");
+	    Constructor cons = c.getConstructor(new Class[] { });
+	    ss = (SecuritySupport)cons.newInstance(new Object[] { });
+	    */
+	    /*
+	     * Unfortunately, we can't load the class using reflection
+	     * because the class is package private.  And the class has
+	     * to be package private so the APIs aren't exposed to other
+	     * code that could use them to circumvent security.  Thus,
+	     * we accept the risk that the direct reference might fail
+	     * on some JDK 1.1 JVMs, even though we would never execute
+	     * this code in such a case.  Sigh...
+	     */
+	    ss = new SecuritySupport12();
+	} catch (Exception ex) {
+	    // ignore it
+	} finally {
+	    if (ss == null)
+		ss = new SecuritySupport();
+	    securitySupport = ss;
+	}
+    }
+
+    /**
+     * Return an appropriate instance of this class, depending on whether
+     * we're on a JDK 1.1 or J2SE 1.2 (or later) system.
+     */
+    static SecuritySupport getInstance() {
+	return (SecuritySupport)securitySupport;
+    }
+
+    ClassLoader getContextClassLoader() {
+	return null;
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return null;
+    }
+
+    ClassLoader getParentClassLoader(ClassLoader cl) {
+        return null;
+    }
+
+    String getSystemProperty(String propName) {
+        return System.getProperty(propName);
+    }
+
+    FileInputStream getFileInputStream(File file)
+        throws FileNotFoundException
+    {
+        return new FileInputStream(file);
+    }
+
+    InputStream getResourceAsStream(ClassLoader cl, String name) {
+        InputStream ris;
+        if (cl == null) {
+            ris = ClassLoader.getSystemResourceAsStream(name);
+        } else {
+            ris = cl.getResourceAsStream(name);
+        }
+        return ris;
+    }
+    
+    boolean getFileExists(File f) {
+        return f.exists();
+    }
+    
+    long getLastModified(File f) {
+        return f.lastModified();
+    }    
+}
diff --git a/src/main/java/org/apache/xalan/xslt/SecuritySupport12.java b/src/main/java/org/apache/xalan/xslt/SecuritySupport12.java
new file mode 100755
index 0000000..e751f8b
--- /dev/null
+++ b/src/main/java/org/apache/xalan/xslt/SecuritySupport12.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SecuritySupport12.java 468646 2006-10-28 06:57:58Z minchau $
+ */
+
+package org.apache.xalan.xslt;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Security related methods that only work on J2SE 1.2 and newer.
+ */
+class SecuritySupport12 extends SecuritySupport {
+
+    ClassLoader getContextClassLoader() {
+        return (ClassLoader)
+                AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                ClassLoader cl = null;
+                try {
+                    cl = Thread.currentThread().getContextClassLoader();
+                } catch (SecurityException ex) { }
+                return cl;
+            }
+        });
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader cl = null;
+                    try {
+                        cl = ClassLoader.getSystemClassLoader();
+                    } catch (SecurityException ex) {}
+                    return cl;
+                }
+            });
+    }
+
+    ClassLoader getParentClassLoader(final ClassLoader cl) {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader parent = null;
+                    try {
+                        parent = cl.getParent();
+                    } catch (SecurityException ex) {}
+
+                    // eliminate loops in case of the boot
+                    // ClassLoader returning itself as a parent
+                    return (parent == cl) ? null : parent;
+                }
+            });
+    }
+
+    String getSystemProperty(final String propName) {
+        return (String)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return System.getProperty(propName);
+                }
+            });
+    }
+
+    FileInputStream getFileInputStream(final File file)
+        throws FileNotFoundException
+    {
+        try {
+            return (FileInputStream)
+                AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    public Object run() throws FileNotFoundException {
+                        return new FileInputStream(file);
+                    }
+                });
+        } catch (PrivilegedActionException e) {
+            throw (FileNotFoundException)e.getException();
+        }
+    }
+
+    InputStream getResourceAsStream(final ClassLoader cl,
+                                           final String name)
+    {
+        return (InputStream)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    InputStream ris;
+                    if (cl == null) {
+                        ris = ClassLoader.getSystemResourceAsStream(name);
+                    } else {
+                        ris = cl.getResourceAsStream(name);
+                    }
+                    return ris;
+                }
+            });
+    }
+    
+    boolean getFileExists(final File f) {
+    return ((Boolean)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Boolean(f.exists());
+                }
+            })).booleanValue();
+    }
+    
+    long getLastModified(final File f) {
+    return ((Long)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Long(f.lastModified());
+                }
+            })).longValue();
+    }
+        
+}
diff --git a/src/main/java/org/apache/xalan/xslt/package.html b/src/main/java/org/apache/xalan/xslt/package.html
new file mode 100644
index 0000000..61018f4
--- /dev/null
+++ b/src/main/java/org/apache/xalan/xslt/package.html
@@ -0,0 +1,26 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468646 2006-10-28 06:57:58Z minchau $ -->
+<html>
+  <title>Xalan command-line interface.</title>
+  <body>
+    <p>Implementation of Xalan command-line interface.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xml/dtm/Axis.java b/src/main/java/org/apache/xml/dtm/Axis.java
new file mode 100644
index 0000000..915a358
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/Axis.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Axis.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+/**
+ * Specifies values related to XPath Axes.
+ * <p>The ancestor, descendant, following, preceding and self axes partition a
+ * document (ignoring attribute and namespace nodes): they do not overlap
+ * and together they contain all the nodes in the document.</p>
+ *
+ */
+public final class Axis
+{
+
+  /**
+   * The ancestor axis contains the ancestors of the context node;
+   *  the ancestors of the context node consist of the parent of context
+   *  node and the parent's parent and so on; thus, the ancestor axis will
+   *  always include the root node, unless the context node is the root node.
+   */
+  public static final int ANCESTOR = 0;
+
+  /**
+   * the ancestor-or-self axis contains the context node and the ancestors of
+   *  the context node; thus, the ancestor axis will always include the
+   *  root node.
+   */
+  public static final int ANCESTORORSELF = 1;
+
+  /**
+   * the attribute axis contains the attributes of the context node; the axis
+   *  will be empty unless the context node is an element.
+   */
+  public static final int ATTRIBUTE = 2;
+
+  /** The child axis contains the children of the context node. */
+  public static final int CHILD = 3;
+
+  /**
+   * The descendant axis contains the descendants of the context node;
+   *  a descendant is a child or a child of a child and so on; thus the
+   *  descendant axis never contains attribute or namespace nodes.
+   */
+  public static final int DESCENDANT = 4;
+
+  /**
+   * The descendant-or-self axis contains the context node and the
+   *  descendants of the context node.
+   */
+  public static final int DESCENDANTORSELF = 5;
+
+  /**
+   * the following axis contains all nodes in the same document as the
+   *  context node that are after the context node in document order, excluding
+   *  any descendants and excluding attribute nodes and namespace nodes.
+   */
+  public static final int FOLLOWING = 6;
+
+  /**
+   * The following-sibling axis contains all the following siblings of the
+   *  context node; if the context node is an attribute node or namespace node,
+   *  the following-sibling axis is empty.
+   */
+  public static final int FOLLOWINGSIBLING = 7;
+
+  /**
+   * The namespace axis contains the namespace nodes of the context node; the
+   *  axis will be empty unless the context node is an element.
+   */
+  public static final int NAMESPACEDECLS = 8;
+
+  /**
+   * The namespace axis contains the namespace nodes of the context node; the
+   *  axis will be empty unless the context node is an element.
+   */
+  public static final int NAMESPACE = 9;
+
+  /**
+   * The parent axis contains the parent of the context node,
+   *  if there is one.
+   */
+  public static final int PARENT = 10;
+
+  /**
+   * The preceding axis contains all nodes in the same document as the context
+   *  node that are before the context node in document order, excluding any
+   *  ancestors and excluding attribute nodes and namespace nodes
+   */
+  public static final int PRECEDING = 11;
+
+  /**
+   * The preceding-sibling axis contains all the preceding siblings of the
+   *  context node; if the context node is an attribute node or namespace node,
+   *  the preceding-sibling axis is empty.
+   */
+  public static final int PRECEDINGSIBLING = 12;
+
+  /** The self axis contains just the context node itself. */
+  public static final int SELF = 13;
+
+  /**
+   * A non-xpath axis, traversing the subtree including the subtree
+   *  root, descendants, attributes, and namespace node decls.
+   */
+  public static final int ALLFROMNODE = 14;
+
+  /**
+   * A non-xpath axis, traversing the the preceding and the ancestor nodes, 
+   * needed for inverseing select patterns to match patterns.
+   */
+  public static final int PRECEDINGANDANCESTOR = 15;
+  
+  // ===========================================
+  // All axis past this are absolute.
+  
+  /**
+   * A non-xpath axis, returns all nodes in the tree from and including the 
+   * root.
+   */
+  public static final int ALL = 16;
+
+  /**
+   * A non-xpath axis, returns all nodes that aren't namespaces or attributes, 
+   * from and including the root.
+   */
+  public static final int DESCENDANTSFROMROOT = 17;
+
+  /**
+   * A non-xpath axis, returns all nodes that aren't namespaces or attributes, 
+   * from and including the root.
+   */
+  public static final int DESCENDANTSORSELFFROMROOT = 18;
+
+  /**
+   * A non-xpath axis, returns root only.
+   */
+  public static final int ROOT = 19;
+
+  /**
+   * A non-xpath axis, for functions.
+   */
+  public static final int FILTEREDLIST = 20;
+
+  /**
+   * A table to identify whether an axis is a reverse axis;
+   */
+  private static final boolean[] isReverse = {
+      true,  // ancestor
+      true,  // ancestor-or-self
+      false, // attribute
+      false, // child
+      false, // descendant
+      false, // descendant-or-self
+      false, // following
+      false, // following-sibling
+      false, // namespace
+      false, // namespace-declarations
+      false, // parent (one node, has no order)
+      true,  // preceding
+      true,  // preceding-sibling
+      false  // self (one node, has no order)
+  };
+
+    /** The names of the axes for diagnostic purposes. */
+    private static final String[] names =
+    {
+      "ancestor",  // 0
+      "ancestor-or-self",  // 1
+      "attribute",  // 2
+      "child",  // 3
+      "descendant",  // 4
+      "descendant-or-self",  // 5
+      "following",  // 6
+      "following-sibling",  // 7
+      "namespace-decls",  // 8
+      "namespace",  // 9
+      "parent",  // 10
+      "preceding",  // 11
+      "preceding-sibling",  // 12
+      "self",  // 13
+      "all-from-node",  // 14
+      "preceding-and-ancestor",  // 15
+      "all",  // 16
+      "descendants-from-root",  // 17
+      "descendants-or-self-from-root",  // 18
+      "root",  // 19
+      "filtered-list"  // 20
+    };
+
+  public static boolean isReverse(int axis){
+      return isReverse[axis];
+  }
+    
+    public static String getNames(int index){
+    	return names[index];    
+    }
+    
+    public static int getNamesLength(){
+    	return names.length;
+    }
+
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTM.java b/src/main/java/org/apache/xml/dtm/DTM.java
new file mode 100644
index 0000000..0722ff7
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTM.java
@@ -0,0 +1,967 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTM.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+import javax.xml.transform.SourceLocator;
+
+import org.apache.xml.utils.XMLString;
+
+/**
+ * <code>DTM</code> is an XML document model expressed as a table
+ * rather than an object tree. It attempts to provide an interface to
+ * a parse tree that has very little object creation. (DTM
+ * implementations may also support incremental construction of the
+ * model, but that's hidden from the DTM API.)
+ *
+ * <p>Nodes in the DTM are identified by integer "handles".  A handle must
+ * be unique within a process, and carries both node identification and
+ * document identification.  It must be possible to compare two handles
+ * (and thus their nodes) for identity with "==".</p>
+ *
+ * <p>Namespace URLs, local-names, and expanded-names can all be
+ * represented by and tested as integer ID values.  An expanded name
+ * represents (and may or may not directly contain) a combination of
+ * the URL ID, and the local-name ID.  Note that the namespace URL id
+ * can be 0, which should have the meaning that the namespace is null.
+ * For consistancy, zero should not be used for a local-name index. </p>
+ *
+ * <p>Text content of a node is represented by an index and length,
+ * permitting efficient storage such as a shared FastStringBuffer.</p>
+ *
+ * <p>The model of the tree, as well as the general navigation model,
+ * is that of XPath 1.0, for the moment.  The model will eventually be
+ * adapted to match the XPath 2.0 data model, XML Schema, and
+ * InfoSet.</p>
+ *
+ * <p>DTM does _not_ directly support the W3C's Document Object
+ * Model. However, it attempts to come close enough that an
+ * implementation of DTM can be created that wraps a DOM and vice
+ * versa.</p>
+ *
+ * <p><strong>Please Note:</strong> The DTM API is still
+ * <strong>Subject To Change.</strong> This wouldn't affect most
+ * users, but might require updating some extensions.</p>
+ *
+ * <p> The largest change being contemplated is a reconsideration of
+ * the Node Handle representation.  We are still not entirely sure
+ * that an integer packed with two numeric subfields is really the
+ * best solution. It has been suggested that we move up to a Long, to
+ * permit more nodes per document without having to reduce the number
+ * of slots in the DTMManager. There's even been a proposal that we
+ * replace these integers with "cursor" objects containing the
+ * internal node id and a pointer to the actual DTM object; this might
+ * reduce the need to continuously consult the DTMManager to retrieve
+ * the latter, and might provide a useful "hook" back into normal Java
+ * heap management.  But changing this datatype would have huge impact
+ * on Xalan's internals -- especially given Java's lack of C-style
+ * typedefs -- so we won't cut over unless we're convinced the new
+ * solution really would be an improvement!</p>
+ * */
+public interface DTM
+{
+
+  /**
+   * Null node handles are represented by this value.
+   */
+  public static final int NULL = -1;
+
+  // These nodeType mnemonics and values are deliberately the same as those
+  // used by the DOM, for convenient mapping
+  //
+  // %REVIEW% Should we actually define these as initialized to,
+  // eg. org.w3c.dom.Document.ELEMENT_NODE?
+
+  /**
+   * The node is a <code>Root</code>.
+   */
+  public static final short ROOT_NODE = 0;
+  
+  /**
+   * The node is an <code>Element</code>.
+   */
+  public static final short ELEMENT_NODE = 1;
+
+  /**
+   * The node is an <code>Attr</code>.
+   */
+  public static final short ATTRIBUTE_NODE = 2;
+
+  /**
+   * The node is a <code>Text</code> node.
+   */
+  public static final short TEXT_NODE = 3;
+
+  /**
+   * The node is a <code>CDATASection</code>.
+   */
+  public static final short CDATA_SECTION_NODE = 4;
+
+  /**
+   * The node is an <code>EntityReference</code>.
+   */
+  public static final short ENTITY_REFERENCE_NODE = 5;
+
+  /**
+   * The node is an <code>Entity</code>.
+   */
+  public static final short ENTITY_NODE = 6;
+
+  /**
+   * The node is a <code>ProcessingInstruction</code>.
+   */
+  public static final short PROCESSING_INSTRUCTION_NODE = 7;
+
+  /**
+   * The node is a <code>Comment</code>.
+   */
+  public static final short COMMENT_NODE = 8;
+
+  /**
+   * The node is a <code>Document</code>.
+   */
+  public static final short DOCUMENT_NODE = 9;
+
+  /**
+   * The node is a <code>DocumentType</code>.
+   */
+  public static final short DOCUMENT_TYPE_NODE = 10;
+
+  /**
+   * The node is a <code>DocumentFragment</code>.
+   */
+  public static final short DOCUMENT_FRAGMENT_NODE = 11;
+
+  /**
+   * The node is a <code>Notation</code>.
+   */
+  public static final short NOTATION_NODE = 12;
+
+  /**
+   * The node is a <code>namespace node</code>. Note that this is not
+   * currently a node type defined by the DOM API.
+   */
+  public static final short NAMESPACE_NODE = 13;
+  
+  /**
+   * The number of valid nodetypes.
+   */
+  public static final short  NTYPES = 14;
+
+  // ========= DTM Implementation Control Functions. ==============
+  // %TBD% RETIRED -- do via setFeature if needed. Remove from impls.
+  // public void setParseBlockSize(int blockSizeSuggestion);
+
+  /**
+   * Set an implementation dependent feature.
+   * <p>
+   * %REVIEW% Do we really expect to set features on DTMs?
+   *
+   * @param featureId A feature URL.
+   * @param state true if this feature should be on, false otherwise.
+   */
+  public void setFeature(String featureId, boolean state);
+
+  /**
+   * Set a run time property for this DTM instance.
+   *
+   * @param property a <code>String</code> value
+   * @param value an <code>Object</code> value
+   */
+  public void setProperty(String property, Object value);
+
+  // ========= Document Navigation Functions =========
+
+  /**
+   * This returns a stateless "traverser", that can navigate over an
+   * XPath axis, though not in document order.
+   *
+   * @param axis One of Axes.ANCESTORORSELF, etc.
+   *
+   * @return A DTMAxisIterator, or null if the givin axis isn't supported.
+   */
+  public DTMAxisTraverser getAxisTraverser(final int axis);
+
+  /**
+   * This is a shortcut to the iterators that implement
+   * XPath axes.
+   * Returns a bare-bones iterator that must be initialized
+   * with a start node (using iterator.setStartNode()).
+   *
+   * @param axis One of Axes.ANCESTORORSELF, etc.
+   *
+   * @return A DTMAxisIterator, or null if the givin axis isn't supported.
+   */
+  public DTMAxisIterator getAxisIterator(final int axis);
+
+  /**
+   * Get an iterator that can navigate over an XPath Axis, predicated by
+   * the extended type ID.
+   *
+   * @param axis
+   * @param type An extended type ID.
+   *
+   * @return A DTMAxisIterator, or null if the givin axis isn't supported.
+   */
+  public DTMAxisIterator getTypedAxisIterator(final int axis, final int type);
+
+  /**
+   * Given a node handle, test if it has child nodes.
+   * <p> %REVIEW% This is obviously useful at the DOM layer, where it
+   * would permit testing this without having to create a proxy
+   * node. It's less useful in the DTM API, where
+   * (dtm.getFirstChild(nodeHandle)!=DTM.NULL) is just as fast and
+   * almost as self-evident. But it's a convenience, and eases porting
+   * of DOM code to DTM.  </p>
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int true if the given node has child nodes.
+   */
+  public boolean hasChildNodes(int nodeHandle);
+
+  /**
+   * Given a node handle, get the handle of the node's first child.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int DTM node-number of first child,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getFirstChild(int nodeHandle);
+
+  /**
+   * Given a node handle, get the handle of the node's last child.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int Node-number of last child,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getLastChild(int nodeHandle);
+
+  /**
+   * Retrieves an attribute node by local name and namespace URI
+   *
+   * %TBD% Note that we currently have no way to support
+   * the DOM's old getAttribute() call, which accesses only the qname.
+   *
+   * @param elementHandle Handle of the node upon which to look up this attribute.
+   * @param namespaceURI The namespace URI of the attribute to
+   *   retrieve, or null.
+   * @param name The local name of the attribute to
+   *   retrieve.
+   * @return The attribute node handle with the specified name (
+   *   <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
+   *   attribute.
+   */
+  public int getAttributeNode(int elementHandle, String namespaceURI,
+                              String name);
+
+  /**
+   * Given a node handle, get the index of the node's first attribute.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return Handle of first attribute, or DTM.NULL to indicate none exists.
+   */
+  public int getFirstAttribute(int nodeHandle);
+
+  /**
+   * Given a node handle, get the index of the node's first namespace node.
+   *
+   * @param nodeHandle handle to node, which should probably be an element
+   *                   node, but need not be.
+   *
+   * @param inScope true if all namespaces in scope should be
+   *                   returned, false if only the node's own
+   *                   namespace declarations should be returned.
+   * @return handle of first namespace,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getFirstNamespaceNode(int nodeHandle, boolean inScope);
+
+  /**
+   * Given a node handle, advance to its next sibling.
+   * @param nodeHandle int Handle of the node.
+   * @return int Node-number of next sibling,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getNextSibling(int nodeHandle);
+
+  /**
+   * Given a node handle, find its preceeding sibling.
+   * WARNING: DTM implementations may be asymmetric; in some,
+   * this operation has been resolved by search, and is relatively expensive.
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node-number of the previous sib,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getPreviousSibling(int nodeHandle);
+
+  /**
+   * Given a node handle, advance to the next attribute. If an
+   * element, we advance to its first attribute; if an attr, we advance to
+   * the next attr of the same element.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int DTM node-number of the resolved attr,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getNextAttribute(int nodeHandle);
+
+  /**
+   * Given a namespace handle, advance to the next namespace in the same scope
+   * (local or local-plus-inherited, as selected by getFirstNamespaceNode)
+   *
+   * @param baseHandle handle to original node from where the first child
+   * was relative to (needed to return nodes in document order).
+   * @param namespaceHandle handle to node which must be of type
+   * NAMESPACE_NODE.
+   * NEEDSDOC @param inScope
+   * @return handle of next namespace,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getNextNamespaceNode(int baseHandle, int namespaceHandle,
+                                  boolean inScope);
+
+  /**
+   * Given a node handle, find its parent node.
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node handle of parent,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getParent(int nodeHandle);
+
+  /**
+   * Given a DTM which contains only a single document, 
+   * find the Node Handle of the  Document node. Note 
+   * that if the DTM is configured so it can contain multiple
+   * documents, this call will return the Document currently
+   * under construction -- but may return null if it's between
+   * documents. Generally, you should use getOwnerDocument(nodeHandle)
+   * or getDocumentRoot(nodeHandle) instead.
+   *
+   * @return int Node handle of document, or DTM.NULL if a shared DTM
+   * can not tell us which Document is currently active.
+   */
+  public int getDocument();
+
+  /**
+   * Given a node handle, find the owning document node. This version mimics
+   * the behavior of the DOM call by the same name.
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node handle of owning document, or DTM.NULL if the node was
+   * a Document.
+   * @see #getDocumentRoot(int nodeHandle)
+   */
+  public int getOwnerDocument(int nodeHandle);
+
+  /**
+   * Given a node handle, find the owning document node.
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node handle of owning document, or the node itself if it was
+   * a Document. (Note difference from DOM, where getOwnerDocument returns
+   * null for the Document node.)
+   * @see #getOwnerDocument(int nodeHandle)
+   */
+  public int getDocumentRoot(int nodeHandle);
+
+  /**
+   * Get the string-value of a node as a String object
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A string object that represents the string-value of the given node.
+   */
+  public XMLString getStringValue(int nodeHandle);
+
+  /**
+   * Get number of character array chunks in
+   * the string-value of a node.
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   * Note that a single text node may have multiple text chunks.
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return number of character array chunks in
+   *         the string-value of a node.
+   */
+  public int getStringValueChunkCount(int nodeHandle);
+
+  /**
+   * Get a character array chunk in the string-value of a node.
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   * Note that a single text node may have multiple text chunks.
+   *
+   * @param nodeHandle The node ID.
+   * @param chunkIndex Which chunk to get.
+   * @param startAndLen  A two-integer array which, upon return, WILL
+   * BE FILLED with values representing the chunk's start position
+   * within the returned character buffer and the length of the chunk.
+   * @return The character array buffer within which the chunk occurs,
+   * setting startAndLen's contents as a side-effect.
+   */
+  public char[] getStringValueChunk(int nodeHandle, int chunkIndex,
+                                    int[] startAndLen);
+
+  /**
+   * Given a node handle, return an ID that represents the node's expanded name.
+   *
+   * @param nodeHandle The handle to the node in question.
+   *
+   * @return the expanded-name id of the node.
+   */
+  public int getExpandedTypeID(int nodeHandle);
+
+  /**
+   * Given an expanded name, return an ID.  If the expanded-name does not
+   * exist in the internal tables, the entry will be created, and the ID will
+   * be returned.  Any additional nodes that are created that have this
+   * expanded name will use this ID.
+   *
+   * NEEDSDOC @param namespace
+   * NEEDSDOC @param localName
+   * NEEDSDOC @param type
+   *
+   * @return the expanded-name id of the node.
+   */
+  public int getExpandedTypeID(String namespace, String localName, int type);
+
+  /**
+   * Given an expanded-name ID, return the local name part.
+   *
+   * @param ExpandedNameID an ID that represents an expanded-name.
+   * @return String Local name of this node.
+   */
+  public String getLocalNameFromExpandedNameID(int ExpandedNameID);
+
+  /**
+   * Given an expanded-name ID, return the namespace URI part.
+   *
+   * @param ExpandedNameID an ID that represents an expanded-name.
+   * @return String URI value of this node's namespace, or null if no
+   * namespace was resolved.
+   */
+  public String getNamespaceFromExpandedNameID(int ExpandedNameID);
+
+  /**
+   * Given a node handle, return its DOM-style node name. This will
+   * include names such as #text or #document.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   * %REVIEW% Document when empty string is possible...
+   */
+  public String getNodeName(int nodeHandle);
+
+  /**
+   * Given a node handle, return the XPath node name.  This should be
+   * the name as described by the XPath data model, NOT the DOM-style
+   * name.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node.
+   */
+  public String getNodeNameX(int nodeHandle);
+
+  /**
+   * Given a node handle, return its DOM-style localname.
+   * (As defined in Namespaces, this is the portion of the name after the
+   * prefix, if present, or the whole node name if no prefix exists)
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Local name of this node.
+   */
+  public String getLocalName(int nodeHandle);
+
+  /**
+   * Given a namespace handle, return the prefix that the namespace decl is
+   * mapping.
+   * Given a node handle, return the prefix used to map to the namespace.
+   * (As defined in Namespaces, this is the portion of the name before any
+   * colon character).
+   *
+   * <p> %REVIEW% Are you sure you want "" for no prefix?  </p>
+   *
+   * @param nodeHandle the id of the node.
+   * @return String prefix of this node's name, or "" if no explicit
+   * namespace prefix was given.
+   */
+  public String getPrefix(int nodeHandle);
+
+  /**
+   * Given a node handle, return its DOM-style namespace URI
+   * (As defined in Namespaces, this is the declared URI which this node's
+   * prefix -- or default in lieu thereof -- was mapped to.)
+   * @param nodeHandle the id of the node.
+   * @return String URI value of this node's namespace, or null if no
+   * namespace was resolved.
+   */
+  public String getNamespaceURI(int nodeHandle);
+
+  /**
+   * Given a node handle, return its node value. This is mostly
+   * as defined by the DOM, but may ignore some conveniences.
+   * <p>
+   * @param nodeHandle The node id.
+   * @return String Value of this node, or null if not
+   * meaningful for this node type.
+   */
+  public String getNodeValue(int nodeHandle);
+
+  /**
+   * Given a node handle, return its DOM-style node type.
+   *
+   * <p>%REVIEW% Generally, returning short is false economy. Return int?</p>
+   *
+   * @param nodeHandle The node id.
+   * @return int Node type, as per the DOM's Node._NODE constants.
+   */
+  public short getNodeType(int nodeHandle);
+
+  /**
+   * Get the depth level of this node in the tree (equals 1 for
+   * a parentless node).
+   *
+   * @param nodeHandle The node id.
+   * @return the number of ancestors, plus one
+   * @xsl.usage internal
+   */
+  public short getLevel(int nodeHandle);
+
+  // ============== Document query functions ==============
+
+  /**
+   * Tests whether DTM DOM implementation implements a specific feature and
+   * that feature is supported by this node.
+   * @param feature The name of the feature to test.
+   * @param version This is the version number of the feature to test.
+   *   If the version is not
+   *   specified, supporting any version of the feature will cause the
+   *   method to return <code>true</code>.
+   * @return Returns <code>true</code> if the specified feature is
+   *   supported on this node, <code>false</code> otherwise.
+   */
+  public boolean isSupported(String feature, String version);
+
+  /**
+   * Return the base URI of the document entity. If it is not known
+   * (because the document was parsed from a socket connection or from
+   * standard input, for example), the value of this property is unknown.
+   *
+   * @return the document base URI String object or null if unknown.
+   */
+  public String getDocumentBaseURI();
+
+  /**
+   * Set the base URI of the document entity.
+   *
+   * @param baseURI the document base URI String object or null if unknown.
+   */
+  public void setDocumentBaseURI(String baseURI);
+
+  /**
+   * Return the system identifier of the document entity. If
+   * it is not known, the value of this property is null.
+   *
+   * @param nodeHandle The node id, which can be any valid node handle.
+   * @return the system identifier String object or null if unknown.
+   */
+  public String getDocumentSystemIdentifier(int nodeHandle);
+
+  /**
+   * Return the name of the character encoding scheme
+   *        in which the document entity is expressed.
+   *
+   * @param nodeHandle The node id, which can be any valid node handle.
+   * @return the document encoding String object.
+   */
+  public String getDocumentEncoding(int nodeHandle);
+
+  /**
+   * Return an indication of the standalone status of the document,
+   *        either "yes" or "no". This property is derived from the optional
+   *        standalone document declaration in the XML declaration at the
+   *        beginning of the document entity, and has no value if there is no
+   *        standalone document declaration.
+   *
+   * @param nodeHandle The node id, which can be any valid node handle.
+   * @return the document standalone String object, either "yes", "no", or null.
+   */
+  public String getDocumentStandalone(int nodeHandle);
+
+  /**
+   * Return a string representing the XML version of the document. This
+   * property is derived from the XML declaration optionally present at the
+   * beginning of the document entity, and has no value if there is no XML
+   * declaration.
+   *
+   * @param documentHandle the document handle
+   * @return the document version String object
+   */
+  public String getDocumentVersion(int documentHandle);
+
+  /**
+   * Return an indication of
+   * whether the processor has read the complete DTD. Its value is a
+   * boolean. If it is false, then certain properties (indicated in their
+   * descriptions below) may be unknown. If it is true, those properties
+   * are never unknown.
+   *
+   * @return <code>true</code> if all declarations were processed;
+   *         <code>false</code> otherwise.
+   */
+  public boolean getDocumentAllDeclarationsProcessed();
+
+  /**
+   *   A document type declaration information item has the following properties:
+   *
+   *     1. [system identifier] The system identifier of the external subset, if
+   *        it exists. Otherwise this property has no value.
+   *
+   * @return the system identifier String object, or null if there is none.
+   */
+  public String getDocumentTypeDeclarationSystemIdentifier();
+
+  /**
+   * Return the public identifier of the external subset,
+   * normalized as described in 4.2.2 External Entities [XML]. If there is
+   * no external subset or if it has no public identifier, this property
+   * has no value.
+   *
+   * @return the public identifier String object, or null if there is none.
+   */
+  public String getDocumentTypeDeclarationPublicIdentifier();
+
+  /**
+   * Returns the <code>Element</code> whose <code>ID</code> is given by
+   * <code>elementId</code>. If no such element exists, returns
+   * <code>DTM.NULL</code>. Behavior is not defined if more than one element
+   * has this <code>ID</code>. Attributes (including those
+   * with the name "ID") are not of type ID unless so defined by DTD/Schema
+   * information available to the DTM implementation.
+   * Implementations that do not know whether attributes are of type ID or
+   * not are expected to return <code>DTM.NULL</code>.
+   *
+   * <p>%REVIEW% Presumably IDs are still scoped to a single document,
+   * and this operation searches only within a single document, right?
+   * Wouldn't want collisions between DTMs in the same process.</p>
+   *
+   * @param elementId The unique <code>id</code> value for an element.
+   * @return The handle of the matching element.
+   */
+  public int getElementById(String elementId);
+
+  /**
+   * The getUnparsedEntityURI function returns the URI of the unparsed
+   * entity with the specified name in the same document as the context
+   * node (see [3.3 Unparsed Entities]). It returns the empty string if
+   * there is no such entity.
+   * <p>
+   * XML processors may choose to use the System Identifier (if one
+   * is provided) to resolve the entity, rather than the URI in the
+   * Public Identifier. The details are dependent on the processor, and
+   * we would have to support some form of plug-in resolver to handle
+   * this properly. Currently, we simply return the System Identifier if
+   * present, and hope that it a usable URI or that our caller can
+   * map it to one.
+   * %REVIEW% Resolve Public Identifiers... or consider changing function name.
+   * <p>
+   * If we find a relative URI
+   * reference, XML expects it to be resolved in terms of the base URI
+   * of the document. The DOM doesn't do that for us, and it isn't
+   * entirely clear whether that should be done here; currently that's
+   * pushed up to a higher level of our application. (Note that DOM Level
+   * 1 didn't store the document's base URI.)
+   * %REVIEW% Consider resolving Relative URIs.
+   * <p>
+   * (The DOM's statement that "An XML processor may choose to
+   * completely expand entities before the structure model is passed
+   * to the DOM" refers only to parsed entities, not unparsed, and hence
+   * doesn't affect this function.)
+   *
+   * @param name A string containing the Entity Name of the unparsed
+   * entity.
+   *
+   * @return String containing the URI of the Unparsed Entity, or an
+   * empty string if no such entity exists.
+   */
+  public String getUnparsedEntityURI(String name);
+
+  // ============== Boolean methods ================
+
+  /**
+   * Return true if the xsl:strip-space or xsl:preserve-space was processed
+   * during construction of the document contained in this DTM.
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public boolean supportsPreStripping();
+
+  /**
+   * Figure out whether nodeHandle2 should be considered as being later
+   * in the document than nodeHandle1, in Document Order as defined
+   * by the XPath model. This may not agree with the ordering defined
+   * by other XML applications.
+   * <p>
+   * There are some cases where ordering isn't defined, and neither are
+   * the results of this function -- though we'll generally return true.
+   * <p>
+   * %REVIEW% Make sure this does the right thing with attribute nodes!!!
+   * <p>
+   * %REVIEW% Consider renaming for clarity. Perhaps isDocumentOrder(a,b)?
+   *
+   * @param firstNodeHandle DOM Node to perform position comparison on.
+   * @param secondNodeHandle DOM Node to perform position comparison on.
+   *
+   * @return false if secondNode comes before firstNode, otherwise return true.
+   * You can think of this as
+   * <code>(firstNode.documentOrderPosition &lt;= secondNode.documentOrderPosition)</code>.
+   */
+  public boolean isNodeAfter(int firstNodeHandle, int secondNodeHandle);
+
+  /**
+   * 2. [element content whitespace] A boolean indicating whether a
+   * text node represents white space appearing within element content
+   * (see [XML], 2.10 "White Space Handling").  Note that validating
+   * XML processors are required by XML 1.0 to provide this
+   * information... but that DOM Level 2 did not support it, since it
+   * depends on knowledge of the DTD which DOM2 could not guarantee
+   * would be available.
+   * <p>
+   * If there is no declaration for the containing element, an XML
+   * processor must assume that the whitespace could be meaningful and
+   * return false. If no declaration has been read, but the [all
+   * declarations processed] property of the document information item
+   * is false (so there may be an unread declaration), then the value
+   * of this property is indeterminate for white space characters and
+   * should probably be reported as false. It is always false for text
+   * nodes that contain anything other than (or in addition to) white
+   * space.
+   * <p>
+   * Note too that it always returns false for non-Text nodes.
+   * <p>
+   * %REVIEW% Joe wants to rename this isWhitespaceInElementContent() for clarity
+   *
+   * @param nodeHandle the node ID.
+   * @return <code>true</code> if the node definitely represents whitespace in
+   * element content; <code>false</code> otherwise.
+   */
+  public boolean isCharacterElementContentWhitespace(int nodeHandle);
+
+  /**
+   *    10. [all declarations processed] This property is not strictly speaking
+   *        part of the infoset of the document. Rather it is an indication of
+   *        whether the processor has read the complete DTD. Its value is a
+   *        boolean. If it is false, then certain properties (indicated in their
+   *        descriptions below) may be unknown. If it is true, those properties
+   *        are never unknown.
+   *
+   * @param documentHandle A node handle that must identify a document.
+   * @return <code>true</code> if all declarations were processed;
+   *         <code>false</code> otherwise.
+   */
+  public boolean isDocumentAllDeclarationsProcessed(int documentHandle);
+
+  /**
+   *     5. [specified] A flag indicating whether this attribute was actually
+   *        specified in the start-tag of its element, or was defaulted from the
+   *        DTD (or schema).
+   *
+   * @param attributeHandle The attribute handle
+   * @return <code>true</code> if the attribute was specified;
+   *         <code>false</code> if it was defaulted or the handle doesn't
+   *            refer to an attribute node.
+   */
+  public boolean isAttributeSpecified(int attributeHandle);
+
+  // ========== Direct SAX Dispatch, for optimization purposes ========
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value). Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   * @param normalize true if the content should be normalized according to
+   * the rules for the XPath
+   * <a href="http://www.w3.org/TR/xpath#function-normalize-space">normalize-space</a>
+   * function.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchCharactersEvents(
+    int nodeHandle, org.xml.sax.ContentHandler ch, boolean normalize)
+      throws org.xml.sax.SAXException;
+
+  /**
+   * Directly create SAX parser events representing the XML content of
+   * a DTM subtree. This is a "serialize" operation.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch)
+    throws org.xml.sax.SAXException;
+
+  /**
+   * Return an DOM node for the given node.
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A node representation of the DTM node.
+   */
+  public org.w3c.dom.Node getNode(int nodeHandle);
+
+  // ==== Construction methods (may not be supported by some implementations!) =====
+  // %REVIEW% What response occurs if not supported?
+
+  /**
+   * @return true iff we're building this model incrementally (eg
+   * we're partnered with a CoroutineParser) and thus require that the
+   * transformation and the parse run simultaneously. Guidance to the
+   * DTMManager.
+   */
+  public boolean needsTwoThreads();
+
+  // %REVIEW% Do these appends make any sense, should we support a
+  // wider set of methods (like the "append" methods in the
+  // current DTMDocumentImpl draft), or should we just support SAX
+  // listener interfaces?  Should it be a separate interface to
+  // make that distinction explicit?
+
+  /**
+   * Return this DTM's content handler, if it has one.
+   *
+   * @return null if this model doesn't respond to SAX events.
+   */
+  public org.xml.sax.ContentHandler getContentHandler();
+
+  /**
+   * Return this DTM's lexical handler, if it has one.
+   *
+   * %REVIEW% Should this return null if constrution already done/begun?
+   *
+   * @return null if this model doesn't respond to lexical SAX events.
+   */
+  public org.xml.sax.ext.LexicalHandler getLexicalHandler();
+
+  /**
+   * Return this DTM's EntityResolver, if it has one.
+   *
+   * @return null if this model doesn't respond to SAX entity ref events.
+   */
+  public org.xml.sax.EntityResolver getEntityResolver();
+
+  /**
+   * Return this DTM's DTDHandler, if it has one.
+   *
+   * @return null if this model doesn't respond to SAX dtd events.
+   */
+  public org.xml.sax.DTDHandler getDTDHandler();
+
+  /**
+   * Return this DTM's ErrorHandler, if it has one.
+   *
+   * @return null if this model doesn't respond to SAX error events.
+   */
+  public org.xml.sax.ErrorHandler getErrorHandler();
+
+  /**
+   * Return this DTM's DeclHandler, if it has one.
+   *
+   * @return null if this model doesn't respond to SAX Decl events.
+   */
+  public org.xml.sax.ext.DeclHandler getDeclHandler();
+
+  /**
+   * Append a child to "the end of the document". Please note that
+   * the node is always cloned in a base DTM, since our basic behavior
+   * is immutable so nodes can't be removed from their previous
+   * location.
+   *
+   * <p> %REVIEW%  DTM maintains an insertion cursor which
+   * performs a depth-first tree walk as nodes come in, and this operation
+   * is really equivalent to:
+   *    insertionCursor.appendChild(document.importNode(newChild)))
+   * where the insert point is the last element that was appended (or
+   * the last one popped back to by an end-element operation).</p>
+   *
+   * @param newChild Must be a valid new node handle.
+   * @param clone true if the child should be cloned into the document.
+   * @param cloneDepth if the clone argument is true, specifies that the
+   *                   clone should include all it's children.
+   */
+  public void appendChild(int newChild, boolean clone, boolean cloneDepth);
+
+  /**
+   * Append a text node child that will be constructed from a string,
+   * to the end of the document. Behavior is otherwise like appendChild().
+   *
+   * @param str Non-null reference to a string.
+   */
+  public void appendTextChild(String str);
+
+  /**
+   * Get the location of a node in the source document.
+   *
+   * @param node an <code>int</code> value
+   * @return a <code>SourceLocator</code> value or null if no location
+   * is available
+   */
+  public SourceLocator getSourceLocatorFor(int node);
+
+  /**
+   * As the DTM is registered with the DTMManager, this method
+   * will be called. This will give the DTM implementation a
+   * chance to initialize any subsystems that are required to
+   * build the DTM
+   */
+  public void documentRegistration();
+
+  /**
+   * As documents are released from the DTMManager, the DTM implementation
+   * will be notified of the event. This will allow the DTM implementation
+   * to shutdown any subsystem activity that may of been assoiated with
+   * the active DTM Implementation.
+   */
+
+   public void documentRelease();
+
+   /**
+    * Migrate a DTM built with an old DTMManager to a new DTMManager.
+    * After the migration, the new DTMManager will treat the DTM as
+    * one that is built by itself.
+    * This is used to support DTM sharing between multiple transformations.
+    * @param manager the DTMManager
+    */
+   public void migrateTo(DTMManager manager);
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMAxisIterator.java b/src/main/java/org/apache/xml/dtm/DTMAxisIterator.java
new file mode 100644
index 0000000..b84efaa
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMAxisIterator.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMAxisIterator.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+/**
+ * This class iterates over a single XPath Axis, and returns node handles.
+ */
+public interface DTMAxisIterator extends Cloneable
+{
+
+  /** Specifies the end of the iteration, and is the same as DTM.NULL.  */
+  public static final int END = DTM.NULL;
+
+  /**
+   * Get the next node in the iteration.
+   *
+   * @return The next node handle in the iteration, or END.
+   */
+  public int next();  
+  
+
+  /**
+   * Resets the iterator to the last start node.
+   *
+   * @return A DTMAxisIterator, which may or may not be the same as this 
+   *         iterator.
+   */
+  public DTMAxisIterator reset();
+
+  /**
+   * @return the number of nodes in this iterator.  This may be an expensive 
+   * operation when called the first time.
+   */
+  public int getLast();
+
+  /**
+   * @return The position of the current node in the set, as defined by XPath.
+   */
+  public int getPosition();
+
+  /**
+   * Remembers the current node for the next call to gotoMark().
+   */
+  public void setMark();
+
+  /**
+   * Restores the current node remembered by setMark().
+   */
+  public void gotoMark();
+
+  /**
+   * Set start to END should 'close' the iterator,
+   * i.e. subsequent call to next() should return END.
+   *
+   * @param node Sets the root of the iteration.
+   *
+   * @return A DTMAxisIterator set to the start of the iteration.
+   */
+  public DTMAxisIterator setStartNode(int node);
+
+  /**
+   * Get start to END should 'close' the iterator,
+   * i.e. subsequent call to next() should return END.
+   *
+   * @return The root node of the iteration.
+   */
+  public int getStartNode();
+
+  /**
+   * @return true if this iterator has a reversed axis, else false.
+   */
+  public boolean isReverse();
+
+  /**
+   * @return a deep copy of this iterator. The clone should not be reset 
+   * from its current position.
+   */
+  public DTMAxisIterator cloneIterator();
+  
+  /**
+   * Set if restartable.
+   */
+  public void setRestartable(boolean isRestartable);
+
+  /**
+   * Return the node at the given position.
+   * 
+   * @param position The position
+   * @return The node at the given position.
+   */
+  public int getNodeByPosition(int position);
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMAxisTraverser.java b/src/main/java/org/apache/xml/dtm/DTMAxisTraverser.java
new file mode 100644
index 0000000..9045235
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMAxisTraverser.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMAxisTraverser.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+/**
+ * A class that implements traverses DTMAxisTraverser interface can traverse
+ * a set of nodes, usually as defined by an XPath axis.  It is different from
+ * an iterator, because it does not need to hold state, and, in fact, must not
+ * hold any iteration-based state.  It is meant to be implemented as an inner
+ * class of a DTM, and returned by the getAxisTraverser(final int axis)
+ * function.
+ *
+ * <p>A DTMAxisTraverser can probably not traverse a reverse axis in
+ * document order.</p>
+ *
+ * <p>Typical usage:</p>
+ * <pre><code>
+ * for(int nodeHandle=myTraverser.first(myContext);
+ *     nodeHandle!=DTM.NULL;
+ *     nodeHandle=myTraverser.next(myContext,nodeHandle))
+ * { ... processing for node indicated by nodeHandle goes here ... }
+ * </code></pre>
+ *
+ * @author Scott Boag
+ */
+public abstract class DTMAxisTraverser
+{
+
+  /**
+   * By the nature of the stateless traversal, the context node can not be
+   * returned or the iteration will go into an infinate loop.  So to traverse 
+   * an axis, the first function must be used to get the first node.
+   *
+   * <p>This method needs to be overloaded only by those axis that process
+   * the self node. <\p>
+   *
+   * @param context The context node of this traversal. This is the point
+   * that the traversal starts from.
+   * @return the first node in the traversal.
+   */
+  public int first(int context)
+  {
+    return next(context, context);
+  }
+
+  /**
+   * By the nature of the stateless traversal, the context node can not be
+   * returned or the iteration will go into an infinate loop.  So to traverse 
+   * an axis, the first function must be used to get the first node.
+   *
+   * <p>This method needs to be overloaded only by those axis that process
+   * the self node. <\p>
+   *
+   * @param context The context node of this traversal. This is the point
+   * of origin for the traversal -- its "root node" or starting point.
+   * @param extendedTypeID The extended type ID that must match.
+   *
+   * @return the first node in the traversal.
+   */
+  public int first(int context, int extendedTypeID)
+  {
+    return next(context, context, extendedTypeID);
+  }
+
+  /**
+   * Traverse to the next node after the current node.
+   *
+   * @param context The context node of this traversal. This is the point
+   * of origin for the traversal -- its "root node" or starting point.
+   * @param current The current node of the traversal. This is the last known
+   * location in the traversal, typically the node-handle returned by the
+   * previous traversal step. For the first traversal step, context
+   * should be set equal to current. Note that in order to test whether
+   * context is in the set, you must use the first() method instead.
+   *
+   * @return the next node in the iteration, or DTM.NULL.
+   * @see #first(int)
+   */
+  public abstract int next(int context, int current);
+
+  /**
+   * Traverse to the next node after the current node that is matched
+   * by the extended type ID.
+   *
+   * @param context The context node of this traversal. This is the point
+   * of origin for the traversal -- its "root node" or starting point.
+   * @param current The current node of the traversal. This is the last known
+   * location in the traversal, typically the node-handle returned by the
+   * previous traversal step. For the first traversal step, context
+   * should be set equal to current. Note that in order to test whether
+   * context is in the set, you must use the first() method instead.
+   * @param extendedTypeID The extended type ID that must match.
+   *
+   * @return the next node in the iteration, or DTM.NULL.
+   * @see #first(int,int)
+   */
+  public abstract int next(int context, int current, int extendedTypeID);
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMConfigurationException.java b/src/main/java/org/apache/xml/dtm/DTMConfigurationException.java
new file mode 100644
index 0000000..5de4ca7
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMConfigurationException.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMConfigurationException.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * Indicates a serious configuration error.
+ */
+public class DTMConfigurationException extends DTMException {
+    static final long serialVersionUID = -4607874078818418046L;
+
+    /**
+     * Create a new <code>DTMConfigurationException</code> with no
+     * detail mesage.
+     */
+    public DTMConfigurationException() {
+        super("Configuration Error");
+    }
+
+    /**
+     * Create a new <code>DTMConfigurationException</code> with
+     * the <code>String </code> specified as an error message.
+     *
+     * @param msg The error message for the exception.
+     */
+    public DTMConfigurationException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Create a new <code>DTMConfigurationException</code> with a
+     * given <code>Exception</code> base cause of the error.
+     *
+     * @param e The exception to be encapsulated in a
+     * DTMConfigurationException.
+     */
+    public DTMConfigurationException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Create a new <code>DTMConfigurationException</code> with the
+     * given <code>Exception</code> base cause and detail message.
+     *
+     * @param msg The detail message.
+     * @param e The exception to be wrapped in a DTMConfigurationException
+     */
+    public DTMConfigurationException(String msg, Throwable e) {
+        super(msg, e);
+    }
+
+    /**
+     * Create a new DTMConfigurationException from a message and a Locator.
+     *
+     * <p>This constructor is especially useful when an application is
+     * creating its own exception from within a DocumentHandler
+     * callback.</p>
+     *
+     * @param message The error or warning message.
+     * @param locator The locator object for the error or warning.
+     */
+    public DTMConfigurationException(String message,
+                                             SourceLocator locator) {
+        super(message, locator);
+    }
+
+    /**
+     * Wrap an existing exception in a DTMConfigurationException.
+     *
+     * @param message The error or warning message, or null to
+     *                use the message from the embedded exception.
+     * @param locator The locator object for the error or warning.
+     * @param e Any exception.
+     */
+    public DTMConfigurationException(String message,
+                                             SourceLocator locator,
+                                             Throwable e) {
+        super(message, locator, e);
+    }
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMDOMException.java b/src/main/java/org/apache/xml/dtm/DTMDOMException.java
new file mode 100644
index 0000000..42a770c
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMDOMException.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMDOMException.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+/**
+ * Simple implementation of DOMException.
+ *
+ * %REVIEW% Several classes were implementing this internally;
+ * it makes more sense to have one shared version.
+ * @xsl.usage internal
+ */
+public class DTMDOMException extends org.w3c.dom.DOMException
+{
+    static final long serialVersionUID = 1895654266613192414L;
+  /**
+   * Constructs a DOM/DTM exception.
+   *
+   * @param code
+   * @param message
+   */
+  public DTMDOMException(short code, String message)
+  {
+    super(code, message);
+  }
+
+  /**
+   * Constructor DTMDOMException
+   *
+   *
+   * @param code
+   */
+  public DTMDOMException(short code)
+  {
+    super(code, "");
+  }
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMException.java b/src/main/java/org/apache/xml/dtm/DTMException.java
new file mode 100644
index 0000000..030dc1f
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMException.java
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMException.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.xml.transform.SourceLocator;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+
+/**
+ * This class specifies an exceptional condition that occured
+ * in the DTM module.
+ */
+public class DTMException extends RuntimeException {
+    static final long serialVersionUID = -775576419181334734L;
+
+    /** Field locator specifies where the error occured.
+     *  @serial */
+    SourceLocator locator;
+
+    /**
+     * Method getLocator retrieves an instance of a SourceLocator
+     * object that specifies where an error occured.
+     *
+     * @return A SourceLocator object, or null if none was specified.
+     */
+    public SourceLocator getLocator() {
+        return locator;
+    }
+
+    /**
+     * Method setLocator sets an instance of a SourceLocator
+     * object that specifies where an error occured.
+     *
+     * @param location A SourceLocator object, or null to clear the location.
+     */
+    public void setLocator(SourceLocator location) {
+        locator = location;
+    }
+
+    /** Field containedException specifies a wrapped exception.  May be null.
+     *  @serial */
+    Throwable containedException;
+
+    /**
+     * This method retrieves an exception that this exception wraps.
+     *
+     * @return An Throwable object, or null.
+     * @see #getCause
+     */
+    public Throwable getException() {
+        return containedException;
+    }
+
+    /**
+     * Returns the cause of this throwable or <code>null</code> if the
+     * cause is nonexistent or unknown.  (The cause is the throwable that
+     * caused this throwable to get thrown.)
+     */
+    public Throwable getCause() {
+
+        return ((containedException == this)
+                ? null
+                : containedException);
+    }
+
+    /**
+     * Initializes the <i>cause</i> of this throwable to the specified value.
+     * (The cause is the throwable that caused this throwable to get thrown.)
+     *
+     * <p>This method can be called at most once.  It is generally called from
+     * within the constructor, or immediately after creating the
+     * throwable.  If this throwable was created
+     * with {@link #DTMException(Throwable)} or
+     * {@link #DTMException(String,Throwable)}, this method cannot be called
+     * even once.
+     *
+     * @param  cause the cause (which is saved for later retrieval by the
+     *         {@link #getCause()} method).  (A <tt>null</tt> value is
+     *         permitted, and indicates that the cause is nonexistent or
+     *         unknown.)
+     * @return  a reference to this <code>Throwable</code> instance.
+     * @throws IllegalArgumentException if <code>cause</code> is this
+     *         throwable.  (A throwable cannot
+     *         be its own cause.)
+     * @throws IllegalStateException if this throwable was
+     *         created with {@link #DTMException(Throwable)} or
+     *         {@link #DTMException(String,Throwable)}, or this method has already
+     *         been called on this throwable.
+     */
+    public synchronized Throwable initCause(Throwable cause) {
+
+        if ((this.containedException == null) && (cause != null)) {
+            throw new IllegalStateException(XMLMessages.createXMLMessage(XMLErrorResources.ER_CANNOT_OVERWRITE_CAUSE, null)); //"Can't overwrite cause");
+        }
+
+        if (cause == this) {
+            throw new IllegalArgumentException(
+                XMLMessages.createXMLMessage(XMLErrorResources.ER_SELF_CAUSATION_NOT_PERMITTED, null)); //"Self-causation not permitted");
+        }
+
+        this.containedException = cause;
+
+        return this;
+    }
+
+    /**
+     * Create a new DTMException.
+     *
+     * @param message The error or warning message.
+     */
+    public DTMException(String message) {
+
+        super(message);
+
+        this.containedException = null;
+        this.locator            = null;
+    }
+
+    /**
+     * Create a new DTMException wrapping an existing exception.
+     *
+     * @param e The exception to be wrapped.
+     */
+    public DTMException(Throwable e) {
+
+        super(e.getMessage());
+
+        this.containedException = e;
+        this.locator            = null;
+    }
+
+    /**
+     * Wrap an existing exception in a DTMException.
+     *
+     * <p>This is used for throwing processor exceptions before
+     * the processing has started.</p>
+     *
+     * @param message The error or warning message, or null to
+     *                use the message from the embedded exception.
+     * @param e Any exception
+     */
+    public DTMException(String message, Throwable e) {
+
+        super(((message == null) || (message.length() == 0))
+              ? e.getMessage()
+              : message);
+
+        this.containedException = e;
+        this.locator            = null;
+    }
+
+    /**
+     * Create a new DTMException from a message and a Locator.
+     *
+     * <p>This constructor is especially useful when an application is
+     * creating its own exception from within a DocumentHandler
+     * callback.</p>
+     *
+     * @param message The error or warning message.
+     * @param locator The locator object for the error or warning.
+     */
+    public DTMException(String message, SourceLocator locator) {
+
+        super(message);
+
+        this.containedException = null;
+        this.locator            = locator;
+    }
+
+    /**
+     * Wrap an existing exception in a DTMException.
+     *
+     * @param message The error or warning message, or null to
+     *                use the message from the embedded exception.
+     * @param locator The locator object for the error or warning.
+     * @param e Any exception
+     */
+    public DTMException(String message, SourceLocator locator,
+                                Throwable e) {
+
+        super(message);
+
+        this.containedException = e;
+        this.locator            = locator;
+    }
+
+    /**
+     * Get the error message with location information
+     * appended.
+     */
+    public String getMessageAndLocation() {
+
+        StringBuffer sbuffer = new StringBuffer();
+        String       message = super.getMessage();
+
+        if (null != message) {
+            sbuffer.append(message);
+        }
+
+        if (null != locator) {
+            String systemID = locator.getSystemId();
+            int    line     = locator.getLineNumber();
+            int    column   = locator.getColumnNumber();
+
+            if (null != systemID) {
+                sbuffer.append("; SystemID: ");
+                sbuffer.append(systemID);
+            }
+
+            if (0 != line) {
+                sbuffer.append("; Line#: ");
+                sbuffer.append(line);
+            }
+
+            if (0 != column) {
+                sbuffer.append("; Column#: ");
+                sbuffer.append(column);
+            }
+        }
+
+        return sbuffer.toString();
+    }
+
+    /**
+     * Get the location information as a string.
+     *
+     * @return A string with location info, or null
+     * if there is no location information.
+     */
+    public String getLocationAsString() {
+
+        if (null != locator) {
+            StringBuffer sbuffer  = new StringBuffer();
+            String       systemID = locator.getSystemId();
+            int          line     = locator.getLineNumber();
+            int          column   = locator.getColumnNumber();
+
+            if (null != systemID) {
+                sbuffer.append("; SystemID: ");
+                sbuffer.append(systemID);
+            }
+
+            if (0 != line) {
+                sbuffer.append("; Line#: ");
+                sbuffer.append(line);
+            }
+
+            if (0 != column) {
+                sbuffer.append("; Column#: ");
+                sbuffer.append(column);
+            }
+
+            return sbuffer.toString();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Print the the trace of methods from where the error
+     * originated.  This will trace all nested exception
+     * objects, as well as this object.
+     */
+    public void printStackTrace() {
+        printStackTrace(new java.io.PrintWriter(System.err, true));
+    }
+
+    /**
+     * Print the the trace of methods from where the error
+     * originated.  This will trace all nested exception
+     * objects, as well as this object.
+     * @param s The stream where the dump will be sent to.
+     */
+    public void printStackTrace(java.io.PrintStream s) {
+        printStackTrace(new java.io.PrintWriter(s));
+    }
+
+    /**
+     * Print the the trace of methods from where the error
+     * originated.  This will trace all nested exception
+     * objects, as well as this object.
+     * @param s The writer where the dump will be sent to.
+     */
+    public void printStackTrace(java.io.PrintWriter s) {
+
+        if (s == null) {
+            s = new java.io.PrintWriter(System.err, true);
+        }
+
+        try {
+            String locInfo = getLocationAsString();
+
+            if (null != locInfo) {
+                s.println(locInfo);
+            }
+
+            super.printStackTrace(s);
+        } catch (Throwable e) {}
+
+        boolean isJdk14OrHigher = false;
+        try {
+            Throwable.class.getMethod("getCause", (Class<?>) null);
+            isJdk14OrHigher = true;
+        } catch (NoSuchMethodException nsme) {
+            // do nothing
+        }        
+
+        // The printStackTrace method of the Throwable class in jdk 1.4 
+        // and higher will include the cause when printing the backtrace.
+        // The following code is only required when using jdk 1.3 or lower                
+        if (!isJdk14OrHigher) {
+            Throwable exception = getException();
+    
+            for (int i = 0; (i < 10) && (null != exception); i++) {
+                s.println("---------");
+    
+                try {
+                    if (exception instanceof DTMException) {
+                        String locInfo =
+                            ((DTMException) exception)
+                                .getLocationAsString();
+    
+                        if (null != locInfo) {
+                            s.println(locInfo);
+                        }
+                    }
+    
+                    exception.printStackTrace(s);
+                } catch (Throwable e) {
+                    s.println("Could not print stack trace...");
+                }
+    
+                try {
+                    Method meth =
+                        ((Object) exception).getClass().getMethod("getException",
+                            (Class<?>) null);
+    
+                    if (null != meth) {
+                        Throwable prev = exception;
+    
+                        exception = (Throwable) meth.invoke(exception, (Class<?>) null);
+    
+                        if (prev == exception) {
+                            break;
+                        }
+                    } else {
+                        exception = null;
+                    }
+                } catch (InvocationTargetException ite) {
+                    exception = null;
+                } catch (IllegalAccessException iae) {
+                    exception = null;
+                } catch (NoSuchMethodException nsme) {
+                    exception = null;
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMFilter.java b/src/main/java/org/apache/xml/dtm/DTMFilter.java
new file mode 100644
index 0000000..5d0dfe0
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMFilter.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMFilter.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+/**
+ * Simple filter for doing node tests.  Note the semantics of this are
+ * somewhat different that the DOM's NodeFilter.
+ */
+public interface DTMFilter
+{
+
+  // Constants for whatToShow.  These are used to set the node type that will 
+  // be traversed. These values may be ORed together before being passed to
+  // the DTMIterator.
+
+  /**
+   * Show all <code>Nodes</code>.
+   */
+  public static final int SHOW_ALL = 0xFFFFFFFF;
+
+  /**
+   * Show <code>Element</code> nodes.
+   */
+  public static final int SHOW_ELEMENT = 0x00000001;
+
+  /**
+   * Show <code>Attr</code> nodes. This is meaningful only when creating an
+   * iterator or tree-walker with an attribute node as its
+   * <code>root</code>; in this case, it means that the attribute node
+   * will appear in the first position of the iteration or traversal.
+   * Since attributes are never children of other nodes, they do not
+   * appear when traversing over the main document tree.
+   */
+  public static final int SHOW_ATTRIBUTE = 0x00000002;
+
+  /**
+   * Show <code>Text</code> nodes.
+   */
+  public static final int SHOW_TEXT = 0x00000004;
+
+  /**
+   * Show <code>CDATASection</code> nodes.
+   */
+  public static final int SHOW_CDATA_SECTION = 0x00000008;
+
+  /**
+   * Show <code>EntityReference</code> nodes. Note that if Entity References
+   * have been fully expanded while the tree was being constructed, these
+   * nodes will not appear and this mask has no effect.
+   */
+  public static final int SHOW_ENTITY_REFERENCE = 0x00000010;
+
+  /**
+   * Show <code>Entity</code> nodes. This is meaningful only when creating
+   * an iterator or tree-walker with an<code> Entity</code> node as its
+   * <code>root</code>; in this case, it means that the <code>Entity</code>
+   *  node will appear in the first position of the traversal. Since
+   * entities are not part of the document tree, they do not appear when
+   * traversing over the main document tree.
+   */
+  public static final int SHOW_ENTITY = 0x00000020;
+
+  /**
+   * Show <code>ProcessingInstruction</code> nodes.
+   */
+  public static final int SHOW_PROCESSING_INSTRUCTION = 0x00000040;
+
+  /**
+   * Show <code>Comment</code> nodes.
+   */
+  public static final int SHOW_COMMENT = 0x00000080;
+
+  /**
+   * Show <code>Document</code> nodes. (Of course, as with Attributes
+   * and such, this is meaningful only when the iteration root is the
+   * Document itself, since Document has no parent.)
+   */
+  public static final int SHOW_DOCUMENT = 0x00000100;
+
+  /**
+   * Show <code>DocumentType</code> nodes. 
+   */
+  public static final int SHOW_DOCUMENT_TYPE = 0x00000200;
+
+  /**
+   * Show <code>DocumentFragment</code> nodes. (Of course, as with
+   * Attributes and such, this is meaningful only when the iteration
+   * root is the Document itself, since DocumentFragment has no parent.)
+   */
+  public static final int SHOW_DOCUMENT_FRAGMENT = 0x00000400;
+
+  /**
+   * Show <code>Notation</code> nodes. This is meaningful only when creating
+   * an iterator or tree-walker with a <code>Notation</code> node as its
+   * <code>root</code>; in this case, it means that the
+   * <code>Notation</code> node will appear in the first position of the
+   * traversal. Since notations are not part of the document tree, they do
+   * not appear when traversing over the main document tree.
+   */
+  public static final int SHOW_NOTATION = 0x00000800;
+  
+  /**
+
+   * This bit instructs the iterator to show namespace nodes, which
+   * are modeled by DTM but not by the DOM.  Make sure this does not
+   * conflict with {@link org.w3c.dom.traversal.NodeFilter}.
+   * <p>
+   * %REVIEW% Might be safer to start from higher bits and work down,
+   * to leave room for the DOM to expand its set of constants... Or,
+   * possibly, to create a DTM-specific field for these additional bits.
+   */
+  public static final int SHOW_NAMESPACE = 0x00001000;
+
+  /**
+   * Special bit for filters implementing match patterns starting with
+   * a function.  Make sure this does not conflict with
+   * {@link org.w3c.dom.traversal.NodeFilter}.
+   * <p>
+   * %REVIEW% Might be safer to start from higher bits and work down,
+   * to leave room for the DOM to expand its set of constants... Or,
+   * possibly, to create a DTM-specific field for these additional bits.
+   */
+  public static final int SHOW_BYFUNCTION = 0x00010000;
+
+  /**
+   * Test whether a specified node is visible in the logical view of a
+   * <code>DTMIterator</code>. Normally, this function
+   * will be called by the implementation of <code>DTMIterator</code>; 
+   * it is not normally called directly from
+   * user code.
+   * 
+   * @param nodeHandle int Handle of the node.
+   * @param whatToShow one of SHOW_XXX values.
+   * @return one of FILTER_ACCEPT, FILTER_REJECT, or FILTER_SKIP.
+   */
+  public short acceptNode(int nodeHandle, int whatToShow);
+  
+  /**
+   * Test whether a specified node is visible in the logical view of a
+   * <code>DTMIterator</code>. Normally, this function
+   * will be called by the implementation of <code>DTMIterator</code>; 
+   * it is not normally called directly from
+   * user code.
+   * <p>
+   * TODO: Should this be setNameMatch(expandedName) followed by accept()?
+   * Or will we really be testing a different name at every invocation?
+   * 
+   * <p>%REVIEW% Under what circumstances will this be used? The cases
+   * I've considered are just as easy and just about as efficient if
+   * the name test is performed in the DTMIterator... -- Joe</p>
+   * 
+   * <p>%REVIEW% Should that 0xFFFF have a mnemonic assigned to it?
+   * Also: This representation is assuming the expanded name is indeed
+   * split into high/low 16-bit halfwords. If we ever change the
+   * balance between namespace and localname bits (eg because we
+   * decide there are many more localnames than namespaces, which is
+   * fairly likely), this is going to break. It might be safer to
+   * encapsulate the details with a makeExpandedName method and make
+   * that responsible for setting up the wildcard version as well.</p>
+   * 
+   * @param nodeHandle int Handle of the node.
+   * @param whatToShow one of SHOW_XXX values.
+   * @param expandedName a value defining the exanded name as defined in 
+   *                     the DTM interface.  Wild cards will be defined 
+   *                     by 0xFFFF in the namespace and/or localname
+   *			 portion of the expandedName.
+   * @return one of FILTER_ACCEPT, FILTER_REJECT, or FILTER_SKIP.  */
+  public short acceptNode(int nodeHandle, int whatToShow, int expandedName);
+ 
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMIterator.java b/src/main/java/org/apache/xml/dtm/DTMIterator.java
new file mode 100644
index 0000000..a251c11
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMIterator.java
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMIterator.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+/**
+
+ * <code>DTMIterators</code> are used to step through a (possibly
+ * filtered) set of nodes.  Their API is modeled largely after the DOM
+ * NodeIterator.
+ * 
+ * <p>A DTMIterator is a somewhat unusual type of iterator, in that it 
+ * can serve both single node iteration and random access.</p>
+ * 
+ * <p>The DTMIterator's traversal semantics, i.e. how it walks the tree,
+ * are specified when it is created, possibly and probably by an XPath
+ * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or 
+ * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.</p>
+ * 
+ * <p>A DTMIterator is meant to be created once as a master static object, and 
+ * then cloned many times for runtime use.  Or the master object itself may 
+ * be used for simpler use cases.</p>
+ *
+ * <p>At this time, we do not expect DTMIterator to emulate
+ * NodeIterator's "maintain relative position" semantics under
+ * document mutation.  It's likely to respond more like the
+ * TreeWalker's "current node" semantics. However, since the base DTM
+ * is immutable, this issue currently makes no practical
+ * difference.</p>
+ *
+ * <p>State: In progress!!</p> */
+public interface DTMIterator
+{
+
+  // Constants returned by acceptNode, borrowed from the DOM Traversal chapter
+  // %REVIEW% Should we explicitly initialize them from, eg,
+  // org.w3c.dom.traversal.NodeFilter.FILTER_ACCEPT?
+
+  /**
+   * Accept the node.
+   */
+  public static final short FILTER_ACCEPT = 1;
+
+  /**
+   * Reject the node. Same behavior as FILTER_SKIP. (In the DOM these
+   * differ when applied to a TreeWalker but have the same result when
+   * applied to a NodeIterator).
+   */
+  public static final short FILTER_REJECT = 2;
+
+  /**
+   * Skip this single node. 
+   */
+  public static final short FILTER_SKIP = 3;
+    
+  /**
+   * Get an instance of a DTM that "owns" a node handle.  Since a node 
+   * iterator may be passed without a DTMManager, this allows the 
+   * caller to easily get the DTM using just the iterator.
+   *
+   * @param nodeHandle the nodeHandle.
+   *
+   * @return a non-null DTM reference.
+   */
+  public DTM getDTM(int nodeHandle);
+  
+  /**
+   * Get an instance of the DTMManager.  Since a node 
+   * iterator may be passed without a DTMManager, this allows the 
+   * caller to easily get the DTMManager using just the iterator.
+   *
+   * @return a non-null DTMManager reference.
+   */
+  public DTMManager getDTMManager();
+
+  /**
+   * The root node of the <code>DTMIterator</code>, as specified when it
+   * was created.  Note the root node is not the root node of the 
+   * document tree, but the context node from where the iteration 
+   * begins and ends.
+   *
+   * @return nodeHandle int Handle of the context node.
+   */
+  public int getRoot();
+
+  /**
+   * Reset the root node of the <code>DTMIterator</code>, overriding
+   * the value specified when it was created.  Note the root node is
+   * not the root node of the document tree, but the context node from
+   * where the iteration begins.
+   *
+   * @param nodeHandle int Handle of the context node.
+   * @param environment The environment object.  
+   * The environment in which this iterator operates, which should provide:
+   * <ul>
+   * <li>a node (the context node... same value as "root" defined below) </li>
+   * <li>a pair of non-zero positive integers (the context position and the context size) </li>
+   * <li>a set of variable bindings </li>
+   * <li>a function library </li>
+   * <li>the set of namespace declarations in scope for the expression.</li>
+   * <ul>
+   * 
+   * <p>At this time the exact implementation of this environment is application 
+   * dependent.  Probably a proper interface will be created fairly soon.</p>
+   * 
+   */
+  public void setRoot(int nodeHandle, Object environment);
+  
+  /**
+   * Reset the iterator to the start. After resetting, the next node returned
+   * will be the root node -- or, if that's filtered out, the first node
+   * within the root's subtree which is _not_ skipped by the filters.
+   */
+  public void reset();
+
+  /**
+   * This attribute determines which node types are presented via the
+   * iterator. The available set of constants is defined above.  
+   * Nodes not accepted by
+   * <code>whatToShow</code> will be skipped, but their children may still
+   * be considered.
+   *
+   * @return one of the SHOW_XXX constants, or several ORed together.
+   */
+  public int getWhatToShow();
+
+  /**
+   * <p>The value of this flag determines whether the children of entity
+   * reference nodes are visible to the iterator. If false, they  and
+   * their descendants will be rejected. Note that this rejection takes
+   * precedence over <code>whatToShow</code> and the filter. </p>
+   * 
+   * <p> To produce a view of the document that has entity references
+   * expanded and does not expose the entity reference node itself, use
+   * the <code>whatToShow</code> flags to hide the entity reference node
+   * and set <code>expandEntityReferences</code> to true when creating the
+   * iterator. To produce a view of the document that has entity reference
+   * nodes but no entity expansion, use the <code>whatToShow</code> flags
+   * to show the entity reference node and set
+   * <code>expandEntityReferences</code> to false.</p>
+   *
+   * <p>NOTE: In Xalan's use of DTM we will generally have fully expanded
+   * entity references when the document tree was built, and thus this
+   * flag will have no effect.</p>
+   *
+   * @return true if entity references will be expanded.  */
+  public boolean getExpandEntityReferences();
+
+  /**
+   * Returns the next node in the set and advances the position of the
+   * iterator in the set. After a <code>DTMIterator</code> has setRoot called,
+   * the first call to <code>nextNode()</code> returns that root or (if it
+   * is rejected by the filters) the first node within its subtree which is
+   * not filtered out.
+   * @return The next node handle in the set being iterated over, or
+   *  <code>DTM.NULL</code> if there are no more members in that set.
+   */
+  public int nextNode();
+
+  /**
+   * Returns the previous node in the set and moves the position of the
+   * <code>DTMIterator</code> backwards in the set.
+   * @return The previous node handle in the set being iterated over,
+   *   or <code>DTM.NULL</code> if there are no more members in that set.
+   */
+  public int previousNode();
+
+  /**
+   * Detaches the <code>DTMIterator</code> from the set which it iterated
+   * over, releasing any computational resources and placing the iterator
+   * in the INVALID state. After <code>detach</code> has been invoked,
+   * calls to <code>nextNode</code> or <code>previousNode</code> will
+   * raise a runtime exception.
+   */
+  public void detach();
+  
+  /**
+   * Specify if it's OK for detach to release the iterator for reuse.
+   * 
+   * @param allowRelease true if it is OK for detach to release this iterator 
+   * for pooling.
+   */
+  public void allowDetachToRelease(boolean allowRelease);
+
+  /**
+   * Get the current node in the iterator. Note that this differs from
+   * the DOM's NodeIterator, where the current position lies between two
+   * nodes (as part of the maintain-relative-position semantic).
+   *
+   * @return The current node handle, or -1.
+   */
+  public int getCurrentNode();
+
+  /**
+   * Tells if this NodeSetDTM is "fresh", in other words, if
+   * the first nextNode() that is called will return the
+   * first node in the set.
+   *
+   * @return true if the iteration of this list has not yet begun.
+   */
+  public boolean isFresh();
+
+  //========= Random Access ==========
+
+  /**
+   * If setShouldCacheNodes(true) is called, then nodes will
+   * be cached, enabling random access, and giving the ability to do 
+   * sorts and the like.  They are not cached by default.
+   *
+   * %REVIEW% Shouldn't the other random-access methods throw an exception
+   * if they're called on a DTMIterator with this flag set false?
+   *
+   * @param b true if the nodes should be cached.
+   */
+  public void setShouldCacheNodes(boolean b);
+  
+  /**
+   * Tells if this iterator can have nodes added to it or set via 
+   * the <code>setItem(int node, int index)</code> method.
+   * 
+   * @return True if the nodelist can be mutated.
+   */
+  public boolean isMutable();
+
+  /** Get the current position within the cached list, which is one
+   * less than the next nextNode() call will retrieve.  i.e. if you
+   * call getCurrentPos() and the return is 0, the next fetch will
+   * take place at index 1.
+   *
+   * @return The position of the iteration.
+   */
+  public int getCurrentPos();
+
+  /**
+   * If an index is requested, NodeSetDTM will call this method
+   * to run the iterator to the index.  By default this sets
+   * m_next to the index.  If the index argument is -1, this
+   * signals that the iterator should be run to the end and
+   * completely fill the cache.
+   *
+   * @param index The index to run to, or -1 if the iterator should be run
+   *              to the end.
+   */
+  public void runTo(int index);
+
+  /**
+   * Set the current position in the node set.
+   * 
+   * @param i Must be a valid index.
+   */
+  public void setCurrentPos(int i);
+
+  /**
+   * Returns the <code>node handle</code> of an item in the collection. If
+   * <code>index</code> is greater than or equal to the number of nodes in
+   * the list, this returns <code>null</code>.
+   *
+   * @param index of the item.
+   * @return The node handle at the <code>index</code>th position in the
+   *   <code>DTMIterator</code>, or <code>-1</code> if that is not a valid
+   *   index.
+   */
+  public int item(int index);
+  
+  /**
+   * Sets the node at the specified index of this vector to be the
+   * specified node. The previous component at that position is discarded.
+   *
+   * <p>The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.  
+   * The iterator must be in cached mode.</p>
+   * 
+   * <p>Meant to be used for sorted iterators.</p>
+   *
+   * @param node Node to set
+   * @param index Index of where to set the node
+   */
+  public void setItem(int node, int index);
+  
+  /**
+   * The number of nodes in the list. The range of valid child node indices
+   * is 0 to <code>length-1</code> inclusive. Note that this requires running
+   * the iterator to completion, and presumably filling the cache.
+   *
+   * @return The number of nodes in the list.
+   */
+  public int getLength();
+    
+  //=========== Cloning operations. ============
+  
+  /**
+   * Get a cloned Iterator that is reset to the start of the iteration.
+   *
+   * @return A clone of this iteration that has been reset.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public DTMIterator cloneWithReset() throws CloneNotSupportedException;
+
+  /**
+   * Get a clone of this iterator, but don't reset the iteration in the 
+   * process, so that it may be used from the current position.
+   *
+   * @return A clone of this object.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException;
+  
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * 
+   * @return true if all the nodes in the iteration well be returned in document 
+   * order.
+   */
+  public boolean isDocOrdered();
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis();
+
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMManager.java b/src/main/java/org/apache/xml/dtm/DTMManager.java
new file mode 100644
index 0000000..d716c57
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMManager.java
@@ -0,0 +1,430 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMManager.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.XMLStringFactory;
+
+/**
+ * A DTMManager instance can be used to create DTM and
+ * DTMIterator objects, and manage the DTM objects in the system.
+ *
+ * <p>The system property that determines which Factory implementation
+ * to create is named "org.apache.xml.utils.DTMFactory". This
+ * property names a concrete subclass of the DTMFactory abstract
+ *  class. If the property is not defined, a platform default is be used.</p>
+ *
+ * <p>An instance of this class <emph>must</emph> be safe to use across
+ * thread instances.  It is expected that a client will create a single instance
+ * of a DTMManager to use across multiple threads.  This will allow sharing
+ * of DTMs across multiple processes.</p>
+ *
+ * <p>Note: this class is incomplete right now.  It will be pretty much
+ * modeled after javax.xml.transform.TransformerFactory in terms of its
+ * factory support.</p>
+ *
+ * <p>State: In progress!!</p>
+ */
+public abstract class DTMManager
+{
+
+  /** The default property name to load the manager. */
+  private static final String defaultPropName =
+    "org.apache.xml.dtm.DTMManager";
+  
+  /** The default class name to use as the manager. */
+  private static String defaultClassName =
+    "org.apache.xml.dtm.ref.DTMManagerDefault";
+
+  /**
+   * Factory for creating XMLString objects.
+   *  %TBD% Make this set by the caller.
+   */
+  protected XMLStringFactory m_xsf = null;
+
+  /**
+   * Default constructor is protected on purpose.
+   */
+  protected DTMManager(){}
+
+  /**
+   * Get the XMLStringFactory used for the DTMs.
+   *
+   *
+   * @return a valid XMLStringFactory object, or null if it hasn't been set yet.
+   */
+  public XMLStringFactory getXMLStringFactory()
+  {
+    return m_xsf;
+  }
+
+  /**
+   * Set the XMLStringFactory used for the DTMs.
+   *
+   *
+   * @param xsf a valid XMLStringFactory object, should not be null.
+   */
+  public void setXMLStringFactory(XMLStringFactory xsf)
+  {
+    m_xsf = xsf;
+  }
+
+  /**
+   * Obtain a new instance of a <code>DTMManager</code>.
+   * This static method creates a new factory instance
+   * This method uses the following ordered lookup procedure to determine
+   * the <code>DTMManager</code> implementation class to
+   * load:
+   * <ul>
+   * <li>
+   * Use the <code>org.apache.xml.dtm.DTMManager</code> system
+   * property.
+   * </li>
+   * <li>
+   * Use the JAVA_HOME(the parent directory where jdk is
+   * installed)/lib/xalan.properties for a property file that contains the
+   * name of the implementation class keyed on the same value as the
+   * system property defined above.
+   * </li>
+   * <li>
+   * Use the Services API (as detailed in the JAR specification), if
+   * available, to determine the classname. The Services API will look
+   * for a classname in the file
+   * <code>META-INF/services/org.apache.xml.dtm.DTMManager</code>
+   * in jars available to the runtime.
+   * </li>
+   * <li>
+   * Use the default <code>DTMManager</code> classname, which is
+   * <code>org.apache.xml.dtm.ref.DTMManagerDefault</code>.
+   * </li>
+   * </ul>
+   *
+   * Once an application has obtained a reference to a <code>
+   * DTMManager</code> it can use the factory to configure
+   * and obtain parser instances.
+   *
+   * @return new DTMManager instance, never null.
+   *
+   * @throws DTMConfigurationException
+   * if the implementation is not available or cannot be instantiated.
+   */
+  public static DTMManager newInstance(XMLStringFactory xsf) 
+           throws DTMConfigurationException
+  {
+    DTMManager factoryImpl = null;
+    try
+    {
+      factoryImpl = (DTMManager) ObjectFactory
+        .createObject(defaultPropName, defaultClassName);
+    }
+    catch (ObjectFactory.ConfigurationError e)
+    {
+      throw new DTMConfigurationException(XMLMessages.createXMLMessage(
+        XMLErrorResources.ER_NO_DEFAULT_IMPL, null), e.getException());
+        //"No default implementation found");
+    }
+
+    if (factoryImpl == null)
+    {
+      throw new DTMConfigurationException(XMLMessages.createXMLMessage(
+        XMLErrorResources.ER_NO_DEFAULT_IMPL, null));
+        //"No default implementation found");
+    }
+
+    factoryImpl.setXMLStringFactory(xsf);
+
+    return factoryImpl;
+  }
+
+  /**
+   * Get an instance of a DTM, loaded with the content from the
+   * specified source.  If the unique flag is true, a new instance will
+   * always be returned.  Otherwise it is up to the DTMManager to return a
+   * new instance or an instance that it already created and may be being used
+   * by someone else.
+   * 
+   * (More parameters may eventually need to be added for error handling
+   * and entity resolution, and to better control selection of implementations.)
+   *
+   * @param source the specification of the source object, which may be null,
+   *               in which case it is assumed that node construction will take
+   *               by some other means.
+   * @param unique true if the returned DTM must be unique, probably because it
+   * is going to be mutated.
+   * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
+   *                         be null.
+   * @param incremental true if the DTM should be built incrementally, if
+   *                    possible.
+   * @param doIndexing true if the caller considers it worth it to use 
+   *                   indexing schemes.
+   *
+   * @return a non-null DTM reference.
+   */
+  public abstract DTM getDTM(javax.xml.transform.Source source,
+                             boolean unique, DTMWSFilter whiteSpaceFilter,
+                             boolean incremental, boolean doIndexing);
+
+  /**
+   * Get the instance of DTM that "owns" a node handle.
+   *
+   * @param nodeHandle the nodeHandle.
+   *
+   * @return a non-null DTM reference.
+   */
+  public abstract DTM getDTM(int nodeHandle);
+
+  /**
+   * Given a W3C DOM node, try and return a DTM handle.
+   * Note: calling this may be non-optimal.
+   *
+   * @param node Non-null reference to a DOM node.
+   *
+   * @return a valid DTM handle.
+   */
+  public abstract int getDTMHandleFromNode(org.w3c.dom.Node node);
+
+  /**
+   * Creates a DTM representing an empty <code>DocumentFragment</code> object.
+   * @return a non-null DTM reference.
+   */
+  public abstract DTM createDocumentFragment();
+
+  /**
+   * Release a DTM either to a lru pool, or completely remove reference.
+   * DTMs without system IDs are always hard deleted.
+   * State: experimental.
+   *
+   * @param dtm The DTM to be released.
+   * @param shouldHardDelete True if the DTM should be removed no matter what.
+   * @return true if the DTM was removed, false if it was put back in a lru pool.
+   */
+  public abstract boolean release(DTM dtm, boolean shouldHardDelete);
+
+  /**
+   * Create a new <code>DTMIterator</code> based on an XPath
+   * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
+   * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
+   *
+   * @param xpathCompiler ??? Somehow we need to pass in a subpart of the
+   * expression.  I hate to do this with strings, since the larger expression
+   * has already been parsed.
+   *
+   * @param pos The position in the expression.
+   * @return The newly created <code>DTMIterator</code>.
+   */
+  public abstract DTMIterator createDTMIterator(Object xpathCompiler,
+          int pos);
+
+  /**
+   * Create a new <code>DTMIterator</code> based on an XPath
+   * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
+   * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
+   *
+   * @param xpathString Must be a valid string expressing a
+   * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
+   * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
+   *
+   * @param presolver An object that can resolve prefixes to namespace URLs.
+   *
+   * @return The newly created <code>DTMIterator</code>.
+   */
+  public abstract DTMIterator createDTMIterator(String xpathString,
+          PrefixResolver presolver);
+
+  /**
+   * Create a new <code>DTMIterator</code> based only on a whatToShow
+   * and a DTMFilter.  The traversal semantics are defined as the
+   * descendant access.
+   * <p>
+   * Note that DTMIterators may not be an exact match to DOM
+   * NodeIterators. They are initialized and used in much the same way
+   * as a NodeIterator, but their response to document mutation is not
+   * currently defined.
+   *
+   * @param whatToShow This flag specifies which node types may appear in
+   *   the logical view of the tree presented by the iterator. See the
+   *   description of <code>NodeFilter</code> for the set of possible
+   *   <code>SHOW_</code> values.These flags can be combined using
+   *   <code>OR</code>.
+   * @param filter The <code>NodeFilter</code> to be used with this
+   *   <code>DTMFilter</code>, or <code>null</code> to indicate no filter.
+   * @param entityReferenceExpansion The value of this flag determines
+   *   whether entity reference nodes are expanded.
+   *
+   * @return The newly created <code>DTMIterator</code>.
+   */
+  public abstract DTMIterator createDTMIterator(int whatToShow,
+          DTMFilter filter, boolean entityReferenceExpansion);
+
+  /**
+   * Create a new <code>DTMIterator</code> that holds exactly one node.
+   *
+   * @param node The node handle that the DTMIterator will iterate to.
+   *
+   * @return The newly created <code>DTMIterator</code>.
+   */
+  public abstract DTMIterator createDTMIterator(int node);
+  
+  /* Flag indicating whether an incremental transform is desired */
+  public boolean m_incremental = false;
+  
+  /*
+   * Flag set by FEATURE_SOURCE_LOCATION.
+   * This feature specifies whether the transformation phase should
+   * keep track of line and column numbers for the input source
+   * document. 
+   */
+  public boolean m_source_location = false; 
+  
+  /**
+   * Get a flag indicating whether an incremental transform is desired 
+   * @return incremental boolean.
+   *
+   */
+  public boolean getIncremental()
+  {
+    return m_incremental;  
+  }
+  
+  /**
+   * Set a flag indicating whether an incremental transform is desired
+   * This flag should have the same value as the FEATURE_INCREMENTAL feature
+   * which is set by the TransformerFactory.setAttribut() method before a
+   * DTMManager is created
+   * @param incremental boolean to use to set m_incremental.
+   *
+   */
+  public void setIncremental(boolean incremental)
+  {
+    m_incremental = incremental;  
+  }
+  
+  /**
+   * Get a flag indicating whether the transformation phase should
+   * keep track of line and column numbers for the input source
+   * document.
+   * @return source location boolean
+   *
+   */
+  public boolean getSource_location()
+  {
+    return m_source_location;  
+  }  
+  
+  /**
+   * Set a flag indicating whether the transformation phase should
+   * keep track of line and column numbers for the input source
+   * document.
+   * This flag should have the same value as the FEATURE_SOURCE_LOCATION feature
+   * which is set by the TransformerFactory.setAttribut() method before a
+   * DTMManager is created
+   * @param sourceLocation boolean to use to set m_source_location
+   */
+  public void setSource_location(boolean sourceLocation){
+    m_source_location = sourceLocation;
+  }
+  
+
+  // -------------------- private methods --------------------
+
+   /**
+   * Temp debug code - this will be removed after we test everything
+   */
+  private static boolean debug;
+
+  static
+  {
+    try
+    {
+      debug = System.getProperty("dtm.debug") != null;
+    }
+    catch (SecurityException ex){}
+  }
+
+  /** This value, set at compile time, controls how many bits of the
+   * DTM node identifier numbers are used to identify a node within a
+   * document, and thus sets the maximum number of nodes per
+   * document. The remaining bits are used to identify the DTM
+   * document which contains this node.
+   *
+   * If you change IDENT_DTM_NODE_BITS, be sure to rebuild _ALL_ the
+   * files which use it... including the IDKey testcases.
+   *
+   * (FuncGenerateKey currently uses the node identifier directly and
+   * thus is affected when this changes. The IDKEY results will still be
+   * _correct_ (presuming no other breakage), but simple equality
+   * comparison against the previous "golden" files will probably
+   * complain.)
+   * */
+  public static final int IDENT_DTM_NODE_BITS = 16;
+    
+
+  /** When this bitmask is ANDed with a DTM node handle number, the result
+   * is the low bits of the node's index number within that DTM. To obtain
+   * the high bits, add the DTM ID portion's offset as assigned in the DTM 
+   * Manager.
+   */
+  public static final int IDENT_NODE_DEFAULT = (1<<IDENT_DTM_NODE_BITS)-1;
+
+
+  /** When this bitmask is ANDed with a DTM node handle number, the result
+   * is the DTM's document identity number.
+   */
+  public static final int IDENT_DTM_DEFAULT = ~IDENT_NODE_DEFAULT;
+
+  /** This is the maximum number of DTMs available.  The highest DTM is
+    * one less than this.
+   */
+  public static final int IDENT_MAX_DTMS = (IDENT_DTM_DEFAULT >>> IDENT_DTM_NODE_BITS) + 1;
+
+
+  /**
+   * %TBD% Doc
+   *
+   * NEEDSDOC @param dtm
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public abstract int getDTMIdentity(DTM dtm);
+
+  /**
+   * %TBD% Doc
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public int getDTMIdentityMask()
+  {
+    return IDENT_DTM_DEFAULT;
+  }
+
+  /**
+   * %TBD% Doc
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public int getNodeIdentityMask()
+  {
+    return IDENT_NODE_DEFAULT;
+  }
+
+}
diff --git a/src/main/java/org/apache/xml/dtm/DTMWSFilter.java b/src/main/java/org/apache/xml/dtm/DTMWSFilter.java
new file mode 100644
index 0000000..7a16fe1
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/DTMWSFilter.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMWSFilter.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm;
+
+/**
+ * This interface is meant to be implemented by a client of the DTM, and allows
+ * stripping of whitespace nodes.
+ */
+public interface DTMWSFilter
+{
+  /**
+   * Do not strip whitespace child nodes of this element.
+   */
+  public static final short NOTSTRIP = 1;
+
+  /**
+   * Strip whitespace child nodes of this element.
+   */
+  public static final short STRIP = 2;
+
+  /**
+   * Inherit whitespace stripping behavior of the parent node.
+   */
+  public static final short INHERIT = 3;
+
+  /**
+   * Test whether whitespace-only text nodes are visible in the logical 
+   * view of <code>DTM</code>. Normally, this function
+   * will be called by the implementation of <code>DTM</code>; 
+   * it is not normally called directly from
+   * user code.
+   * 
+   * @param elementHandle int Handle of the element.
+   * @return one of NOTSTRIP, STRIP, or INHERIT.
+   */
+  public short getShouldStripSpace(int elementHandle, DTM dtm);
+  
+}
diff --git a/src/main/java/org/apache/xml/dtm/ObjectFactory.java b/src/main/java/org/apache/xml/dtm/ObjectFactory.java
new file mode 100755
index 0000000..09f308d
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ObjectFactory.java
@@ -0,0 +1,661 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ObjectFactory.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+
+package org.apache.xml.dtm;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+
+import java.util.Properties;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ * This class is duplicated for each JAXP subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the JAXP
+ * API.
+ * <p>
+ * This code is designed to implement the JAXP 1.1 spec pluggability
+ * feature and is designed to run on JDK version 1.1 and
+ * later, and to compile on JDK 1.2 and onward.  
+ * The code also runs both as part of an unbundled jar file and
+ * when bundled as part of the JDK.
+ * <p>
+ * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
+ * class and modified to be used as a general utility for creating objects 
+ * dynamically.
+ *
+ * @version $Id: ObjectFactory.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+class ObjectFactory {
+
+    //
+    // Constants
+    //
+
+    // name of default properties file to look for in JDK's jre/lib directory
+    private static final String DEFAULT_PROPERTIES_FILENAME =
+                                                     "xalan.properties";
+
+    private static final String SERVICES_PATH = "META-INF/services/";
+
+    /** Set to true for debugging */
+    private static final boolean DEBUG = false;
+
+    /** cache the contents of the xalan.properties file.
+     *  Until an attempt has been made to read this file, this will
+     * be null; if the file does not exist or we encounter some other error
+     * during the read, this will be empty.
+     */
+    private static Properties fXalanProperties = null;
+
+    /***
+     * Cache the time stamp of the xalan.properties file so
+     * that we know if it's been modified and can invalidate
+     * the cache when necessary.
+     */
+    private static long fLastModified = -1;
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, String fallbackClassName)
+        throws ConfigurationError {
+        return createObject(factoryId, null, fallbackClassName);
+    } // createObject(String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, 
+                                      String propertiesFilename,
+                                      String fallbackClassName)
+        throws ConfigurationError
+    {
+        Class factoryClass = lookUpFactoryClass(factoryId,
+                                                propertiesFilename,
+                                                fallbackClassName);
+
+        if (factoryClass == null) {
+            throw new ConfigurationError(
+                "Provider for " + factoryId + " cannot be found", null);
+        }
+
+        try{
+            Object instance = factoryClass.newInstance();
+            debugPrintln("created new instance of factory " + factoryId);
+            return instance;
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider for factory " + factoryId
+                    + " could not be instantiated: " + x, x);
+        }
+    } // createObject(String,String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId) 
+        throws ConfigurationError
+    {
+        return lookUpFactoryClass(factoryId, null, null);
+    } // lookUpFactoryClass(String):Class
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId,
+                                           String propertiesFilename,
+                                           String fallbackClassName)
+        throws ConfigurationError
+    {
+        String factoryClassName = lookUpFactoryClassName(factoryId,
+                                                         propertiesFilename,
+                                                         fallbackClassName);
+        ClassLoader cl = findClassLoader();
+
+        if (factoryClassName == null) {
+            factoryClassName = fallbackClassName;
+        }
+
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(factoryClassName,
+                                                    cl,
+                                                    true);
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return providerClass;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + factoryClassName + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider "+factoryClassName+" could not be instantiated: "+x,
+                x);
+        }
+    } // lookUpFactoryClass(String,String,String):Class
+
+    /**
+     * Finds the name of the required implementation class in the specified
+     * order.  The specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return name of class that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static String lookUpFactoryClassName(String factoryId,
+                                                String propertiesFilename,
+                                                String fallbackClassName)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Use the system property first
+        try {
+            String systemProp = ss.getSystemProperty(factoryId);
+            if (systemProp != null) {
+                debugPrintln("found system property, value=" + systemProp);
+                return systemProp;
+            }
+        } catch (SecurityException se) {
+            // Ignore and continue w/ next location
+        }
+
+        // Try to read from propertiesFilename, or
+        // $java.home/lib/xalan.properties
+        String factoryClassName = null;
+        // no properties file name specified; use
+        // $JAVA_HOME/lib/xalan.properties:
+        if (propertiesFilename == null) {
+            File propertiesFile = null;
+            boolean propertiesFileExists = false;
+            try {
+                String javah = ss.getSystemProperty("java.home");
+                propertiesFilename = javah + File.separator +
+                    "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
+                propertiesFile = new File(propertiesFilename);
+                propertiesFileExists = ss.getFileExists(propertiesFile);
+            } catch (SecurityException e) {
+                // try again...
+                fLastModified = -1;
+                fXalanProperties = null;
+            }
+
+            synchronized (ObjectFactory.class) {
+                boolean loadProperties = false;
+                FileInputStream fis = null;
+                try {
+                    // file existed last time
+                    if(fLastModified >= 0) {
+                        if(propertiesFileExists &&
+                                (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
+                            loadProperties = true;
+                        } else {
+                            // file has stopped existing...
+                            if(!propertiesFileExists) {
+                                fLastModified = -1;
+                                fXalanProperties = null;
+                            } // else, file wasn't modified!
+                        }
+                    } else {
+                        // file has started to exist:
+                        if(propertiesFileExists) {
+                            loadProperties = true;
+                            fLastModified = ss.getLastModified(propertiesFile);
+                        } // else, nothing's changed
+                    }
+                    if(loadProperties) {
+                        // must never have attempted to read xalan.properties
+                        // before (or it's outdeated)
+                        fXalanProperties = new Properties();
+                        fis = ss.getFileInputStream(propertiesFile);
+                        fXalanProperties.load(fis);
+                    }
+	        } catch (Exception x) {
+	            fXalanProperties = null;
+	            fLastModified = -1;
+                    // assert(x instanceof FileNotFoundException
+	            //        || x instanceof SecurityException)
+	            // In both cases, ignore and continue w/ next location
+	        }
+                finally {
+                    // try to close the input stream if one was opened.
+                    if (fis != null) {
+                        try {
+                            fis.close();
+                        }
+                        // Ignore the exception.
+                        catch (IOException exc) {}
+                    }
+                }	            
+            }
+            if(fXalanProperties != null) {
+                factoryClassName = fXalanProperties.getProperty(factoryId);
+            }
+        } else {
+            FileInputStream fis = null;
+            try {
+                fis = ss.getFileInputStream(new File(propertiesFilename));
+                Properties props = new Properties();
+                props.load(fis);
+                factoryClassName = props.getProperty(factoryId);
+            } catch (Exception x) {
+                // assert(x instanceof FileNotFoundException
+                //        || x instanceof SecurityException)
+                // In both cases, ignore and continue w/ next location
+            }
+            finally {
+                // try to close the input stream if one was opened.
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    }
+                    // Ignore the exception.
+                    catch (IOException exc) {}
+                }
+            }               
+        }
+        if (factoryClassName != null) {
+            debugPrintln("found in " + propertiesFilename + ", value="
+                          + factoryClassName);
+            return factoryClassName;
+        }
+
+        // Try Jar Service Provider Mechanism
+        return findJarServiceProviderName(factoryId);
+    } // lookUpFactoryClass(String,String):String
+
+    //
+    // Private static methods
+    //
+
+    /** Prints a message to standard error if debugging is enabled. */
+    private static void debugPrintln(String msg) {
+        if (DEBUG) {
+            System.err.println("JAXP: " + msg);
+        }
+    } // debugPrintln(String)
+
+    /**
+     * Figure out which ClassLoader to use.  For JDK 1.2 and later use
+     * the context ClassLoader.
+     */
+    static ClassLoader findClassLoader()
+        throws ConfigurationError
+    { 
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Figure out which ClassLoader to use for loading the provider
+        // class.  If there is a Context ClassLoader then use it.
+        ClassLoader context = ss.getContextClassLoader();
+        ClassLoader system = ss.getSystemClassLoader();
+
+        ClassLoader chain = system;
+        while (true) {
+            if (context == chain) {
+                // Assert: we are on JDK 1.1 or we have no Context ClassLoader
+                // or any Context ClassLoader in chain of system classloader
+                // (including extension ClassLoader) so extend to widest
+                // ClassLoader (always look in system ClassLoader if Xalan
+                // is in boot/extension/system classpath and in current
+                // ClassLoader otherwise); normal classloaders delegate
+                // back to system ClassLoader first so this widening doesn't
+                // change the fact that context ClassLoader will be consulted
+                ClassLoader current = ObjectFactory.class.getClassLoader();
+
+                chain = system;
+                while (true) {
+                    if (current == chain) {
+                        // Assert: Current ClassLoader in chain of
+                        // boot/extension/system ClassLoaders
+                        return system;
+                    }
+                    if (chain == null) {
+                        break;
+                    }
+                    chain = ss.getParentClassLoader(chain);
+                }
+
+                // Assert: Current ClassLoader not in chain of
+                // boot/extension/system ClassLoaders
+                return current;
+            }
+
+            if (chain == null) {
+                // boot ClassLoader reached
+                break;
+            }
+
+            // Check for any extension ClassLoaders in chain up to
+            // boot ClassLoader
+            chain = ss.getParentClassLoader(chain);
+        };
+
+        // Assert: Context ClassLoader not in chain of
+        // boot/extension/system ClassLoaders
+        return context;
+    } // findClassLoader():ClassLoader
+
+    /**
+     * Create an instance of a class using the specified ClassLoader
+     */ 
+    static Object newInstance(String className, ClassLoader cl,
+                                      boolean doFallback)
+        throws ConfigurationError
+    {
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(className, cl, doFallback);
+            Object instance = providerClass.newInstance();
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return instance;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + className + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider " + className + " could not be instantiated: " + x,
+                x);
+        }
+    }
+
+    /**
+     * Find a Class using the specified ClassLoader
+     */ 
+    static Class findProviderClass(String className, ClassLoader cl,
+                                           boolean doFallback)
+        throws ClassNotFoundException, ConfigurationError
+    {   
+        //throw security exception if the calling thread is not allowed to access the
+        //class. Restrict the access to the package classes as specified in java.security policy.
+        SecurityManager security = System.getSecurityManager();
+        try{
+                if (security != null){
+                    final int lastDot = className.lastIndexOf(".");
+                    String packageName = className;
+                    if (lastDot != -1) packageName = className.substring(0, lastDot);
+                    security.checkPackageAccess(packageName);
+                 }   
+        }catch(SecurityException e){
+            throw e;
+        }
+        
+        Class providerClass;
+        if (cl == null) {
+            // XXX Use the bootstrap ClassLoader.  There is no way to
+            // load a class using the bootstrap ClassLoader that works
+            // in both JDK 1.1 and Java 2.  However, this should still
+            // work b/c the following should be true:
+            //
+            // (cl == null) iff current ClassLoader == null
+            //
+            // Thus Class.forName(String) will use the current
+            // ClassLoader which will be the bootstrap ClassLoader.
+            providerClass = Class.forName(className);
+        } else {
+            try {
+                providerClass = cl.loadClass(className);
+            } catch (ClassNotFoundException x) {
+                if (doFallback) {
+                    // Fall back to current classloader
+                    ClassLoader current = ObjectFactory.class.getClassLoader();
+                    if (current == null) {
+                        providerClass = Class.forName(className);
+                    } else if (cl != current) {
+                        cl = current;
+                        providerClass = cl.loadClass(className);
+                    } else {
+                        throw x;
+                    }
+                } else {
+                    throw x;
+                }
+            }
+        }
+
+        return providerClass;
+    }
+
+    /**
+     * Find the name of service provider using Jar Service Provider Mechanism
+     *
+     * @return instance of provider class if found or null
+     */
+    private static String findJarServiceProviderName(String factoryId)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+        String serviceId = SERVICES_PATH + factoryId;
+        InputStream is = null;
+
+        // First try the Context ClassLoader
+        ClassLoader cl = findClassLoader();
+
+        is = ss.getResourceAsStream(cl, serviceId);
+
+        // If no provider found then try the current ClassLoader
+        if (is == null) {
+            ClassLoader current = ObjectFactory.class.getClassLoader();
+            if (cl != current) {
+                cl = current;
+                is = ss.getResourceAsStream(cl, serviceId);
+            }
+        }
+
+        if (is == null) {
+            // No provider found
+            return null;
+        }
+
+        debugPrintln("found jar resource=" + serviceId +
+               " using ClassLoader: " + cl);
+
+        // Read the service provider name in UTF-8 as specified in
+        // the jar spec.  Unfortunately this fails in Microsoft
+        // VJ++, which does not implement the UTF-8
+        // encoding. Theoretically, we should simply let it fail in
+        // that case, since the JVM is obviously broken if it
+        // doesn't support such a basic standard.  But since there
+        // are still some users attempting to use VJ++ for
+        // development, we have dropped in a fallback which makes a
+        // second attempt using the platform's default encoding. In
+        // VJ++ this is apparently ASCII, which is a subset of
+        // UTF-8... and since the strings we'll be reading here are
+        // also primarily limited to the 7-bit ASCII range (at
+        // least, in English versions), this should work well
+        // enough to keep us on the air until we're ready to
+        // officially decommit from VJ++. [Edited comment from
+        // jkesselm]
+        BufferedReader rd;
+        try {
+            rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+        } catch (java.io.UnsupportedEncodingException e) {
+            rd = new BufferedReader(new InputStreamReader(is));
+        }
+        
+        String factoryClassName = null;
+        try {
+            // XXX Does not handle all possible input as specified by the
+            // Jar Service Provider specification
+            factoryClassName = rd.readLine();
+        } catch (IOException x) {
+            // No provider found
+            return null;
+        }
+        finally {
+            try {
+                // try to close the reader.
+                rd.close();
+            }
+            // Ignore the exception.
+            catch (IOException exc) {}
+        }          
+
+        if (factoryClassName != null &&
+            ! "".equals(factoryClassName)) {
+            debugPrintln("found in resource, value="
+                   + factoryClassName);
+
+            // Note: here we do not want to fall back to the current
+            // ClassLoader because we want to avoid the case where the
+            // resource file was found using one ClassLoader and the
+            // provider class was instantiated using a different one.
+            return factoryClassName;
+        }
+
+        // No provider found
+        return null;
+    }
+
+    //
+    // Classes
+    //
+
+    /**
+     * A configuration error.
+     */
+    static class ConfigurationError 
+        extends Error {
+                static final long serialVersionUID = 5122054096615067992L;
+        //
+        // Data
+        //
+
+        /** Exception. */
+        private Exception exception;
+
+        //
+        // Constructors
+        //
+
+        /**
+         * Construct a new instance with the specified detail string and
+         * exception.
+         */
+        ConfigurationError(String msg, Exception x) {
+            super(msg);
+            this.exception = x;
+        } // <init>(String,Exception)
+
+        //
+        // Public methods
+        //
+
+        /** Returns the exception associated to this error. */
+        Exception getException() {
+            return exception;
+        } // getException():Exception
+
+    } // class ConfigurationError
+
+} // class ObjectFactory
diff --git a/src/main/java/org/apache/xml/dtm/SecuritySupport.java b/src/main/java/org/apache/xml/dtm/SecuritySupport.java
new file mode 100644
index 0000000..6ea002c
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/SecuritySupport.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xml.dtm;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Base class with security related methods that work on JDK 1.1.
+ */
+class SecuritySupport {
+
+    /*
+     * Make this of type Object so that the verifier won't try to
+     * prove its type, thus possibly trying to load the SecuritySupport12
+     * class.
+     */
+    private static final Object securitySupport;
+
+    static {
+	SecuritySupport ss = null;
+	try {
+	    Class c = Class.forName("java.security.AccessController");
+	    // if that worked, we're on 1.2.
+	    /*
+	    // don't reference the class explicitly so it doesn't
+	    // get dragged in accidentally.
+	    c = Class.forName("javax.mail.SecuritySupport12");
+	    Constructor cons = c.getConstructor(new Class[] { });
+	    ss = (SecuritySupport)cons.newInstance(new Object[] { });
+	    */
+	    /*
+	     * Unfortunately, we can't load the class using reflection
+	     * because the class is package private.  And the class has
+	     * to be package private so the APIs aren't exposed to other
+	     * code that could use them to circumvent security.  Thus,
+	     * we accept the risk that the direct reference might fail
+	     * on some JDK 1.1 JVMs, even though we would never execute
+	     * this code in such a case.  Sigh...
+	     */
+	    ss = new SecuritySupport12();
+	} catch (Exception ex) {
+	    // ignore it
+	} finally {
+	    if (ss == null)
+		ss = new SecuritySupport();
+	    securitySupport = ss;
+	}
+    }
+
+    /**
+     * Return an appropriate instance of this class, depending on whether
+     * we're on a JDK 1.1 or J2SE 1.2 (or later) system.
+     */
+    static SecuritySupport getInstance() {
+	return (SecuritySupport)securitySupport;
+    }
+
+    ClassLoader getContextClassLoader() {
+	return null;
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return null;
+    }
+
+    ClassLoader getParentClassLoader(ClassLoader cl) {
+        return null;
+    }
+
+    String getSystemProperty(String propName) {
+        return System.getProperty(propName);
+    }
+
+    FileInputStream getFileInputStream(File file)
+        throws FileNotFoundException
+    {
+        return new FileInputStream(file);
+    }
+
+    InputStream getResourceAsStream(ClassLoader cl, String name) {
+        InputStream ris;
+        if (cl == null) {
+            ris = ClassLoader.getSystemResourceAsStream(name);
+        } else {
+            ris = cl.getResourceAsStream(name);
+        }
+        return ris;
+    }
+    
+    boolean getFileExists(File f) {
+        return f.exists();
+    }
+    
+    long getLastModified(File f) {
+        return f.lastModified();
+    }    
+}
diff --git a/src/main/java/org/apache/xml/dtm/SecuritySupport12.java b/src/main/java/org/apache/xml/dtm/SecuritySupport12.java
new file mode 100644
index 0000000..7b083d0
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/SecuritySupport12.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xml.dtm;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Security related methods that only work on J2SE 1.2 and newer.
+ */
+class SecuritySupport12 extends SecuritySupport {
+
+    ClassLoader getContextClassLoader() {
+        return (ClassLoader)
+                AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                ClassLoader cl = null;
+                try {
+                    cl = Thread.currentThread().getContextClassLoader();
+                } catch (SecurityException ex) { }
+                return cl;
+            }
+        });
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader cl = null;
+                    try {
+                        cl = ClassLoader.getSystemClassLoader();
+                    } catch (SecurityException ex) {}
+                    return cl;
+                }
+            });
+    }
+
+    ClassLoader getParentClassLoader(final ClassLoader cl) {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader parent = null;
+                    try {
+                        parent = cl.getParent();
+                    } catch (SecurityException ex) {}
+
+                    // eliminate loops in case of the boot
+                    // ClassLoader returning itself as a parent
+                    return (parent == cl) ? null : parent;
+                }
+            });
+    }
+
+    String getSystemProperty(final String propName) {
+        return (String)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return System.getProperty(propName);
+                }
+            });
+    }
+
+    FileInputStream getFileInputStream(final File file)
+        throws FileNotFoundException
+    {
+        try {
+            return (FileInputStream)
+                AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    public Object run() throws FileNotFoundException {
+                        return new FileInputStream(file);
+                    }
+                });
+        } catch (PrivilegedActionException e) {
+            throw (FileNotFoundException)e.getException();
+        }
+    }
+
+    InputStream getResourceAsStream(final ClassLoader cl,
+                                           final String name)
+    {
+        return (InputStream)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    InputStream ris;
+                    if (cl == null) {
+                        ris = ClassLoader.getSystemResourceAsStream(name);
+                    } else {
+                        ris = cl.getResourceAsStream(name);
+                    }
+                    return ris;
+                }
+            });
+    }
+    
+    boolean getFileExists(final File f) {
+    return ((Boolean)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Boolean(f.exists());
+                }
+            })).booleanValue();
+    }
+    
+    long getLastModified(final File f) {
+    return ((Long)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Long(f.lastModified());
+                }
+            })).longValue();
+    }
+        
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/ChunkedIntArray.java b/src/main/java/org/apache/xml/dtm/ref/ChunkedIntArray.java
new file mode 100644
index 0000000..31517a7
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/ChunkedIntArray.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ChunkedIntArray.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+ 
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+/**
+ * <code>ChunkedIntArray</code> is an extensible array of blocks of integers.
+ * (I'd consider Vector, but it's unable to handle integers except by
+ * turning them into Objects.)
+
+ * <p>Making this a separate class means some call-and-return overhead. But
+ * doing it all inline tends to be fragile and expensive in coder time,
+ * not to mention driving up code size. If you want to inline it, feel free.
+ * The Java text suggest that private and Final methods may be inlined, 
+ * and one can argue that this beast need not be made subclassable...</p>
+ *
+ * <p>%REVIEW% This has strong conceptual overlap with the IntVector class.
+ * It would probably be a good thing to merge the two, when time permits.<p>
+ */
+final class ChunkedIntArray
+{
+  final int slotsize=4; // Locked, MUST be power of two in current code
+  // Debugging tip: Cranking lowbits down to 4 or so is a good
+  // way to pound on the array addressing code.
+  static final int lowbits=10; // How many bits address within chunks
+  static final int chunkalloc=1<<lowbits;
+  static final int lowmask=chunkalloc-1;
+  
+  ChunksVector chunks=new ChunksVector();
+  final int fastArray[] = new int[chunkalloc];
+  int lastUsed=0;
+
+  /**
+   * Create a new CIA with specified record size. Currently record size MUST
+   * be a power of two... and in fact is hardcoded to 4.
+   */
+  ChunkedIntArray(int slotsize)
+  {
+    if(this.slotsize<slotsize)
+      throw new ArrayIndexOutOfBoundsException(XMLMessages.createXMLMessage(XMLErrorResources.ER_CHUNKEDINTARRAY_NOT_SUPPORTED, new Object[]{Integer.toString(slotsize)})); //"ChunkedIntArray("+slotsize+") not currently supported");
+    else if (this.slotsize>slotsize)
+      System.out.println("*****WARNING: ChunkedIntArray("+slotsize+") wasting "+(this.slotsize-slotsize)+" words per slot");
+    chunks.addElement(fastArray);
+  }
+  /**
+   * Append a 4-integer record to the CIA, starting with record 1. (Since
+   * arrays are initialized to all-0, 0 has been reserved as the "unknown"
+   * value in DTM.)
+   * @return the index at which this record was inserted.
+   */
+  int appendSlot(int w0, int w1, int w2, int w3)
+  {
+    /*
+    try
+    {
+      int newoffset = (lastUsed+1)*slotsize;
+      fastArray[newoffset] = w0;
+      fastArray[newoffset+1] = w1;
+      fastArray[newoffset+2] = w2;
+      fastArray[newoffset+3] = w3;
+      return ++lastUsed;
+    }
+    catch(ArrayIndexOutOfBoundsException aioobe)
+    */
+    {
+      final int slotsize=4;
+      int newoffset = (lastUsed+1)*slotsize;
+      int chunkpos = newoffset >> lowbits;
+      int slotpos = (newoffset & lowmask);
+
+      // Grow if needed
+      if (chunkpos > chunks.size() - 1)
+        chunks.addElement(new int[chunkalloc]);
+      int[] chunk = chunks.elementAt(chunkpos);
+      chunk[slotpos] = w0;
+      chunk[slotpos+1] = w1;
+      chunk[slotpos+2] = w2;
+      chunk[slotpos+3] = w3;
+
+      return ++lastUsed;
+    }
+  }
+  /**
+   * Retrieve an integer from the CIA by record number and column within
+   * the record, both 0-based (though position 0 is reserved for special
+   * purposes).
+   * @param position int Record number
+   * @param slotpos int Column number
+   */
+  int readEntry(int position, int offset) throws ArrayIndexOutOfBoundsException
+  {
+    /*
+    try
+    {
+      return fastArray[(position*slotsize)+offset];
+    }
+    catch(ArrayIndexOutOfBoundsException aioobe)
+    */
+    {
+      // System.out.println("Using slow read (1)");
+      if (offset>=slotsize)
+        throw new ArrayIndexOutOfBoundsException(XMLMessages.createXMLMessage(XMLErrorResources.ER_OFFSET_BIGGER_THAN_SLOT, null)); //"Offset bigger than slot");
+      position*=slotsize;
+      int chunkpos = position >> lowbits;
+      int slotpos = position & lowmask;
+      int[] chunk = chunks.elementAt(chunkpos);
+      return chunk[slotpos + offset];
+    }
+  }
+  
+  // Check that the node at index "position" is not an ancestor
+  // of the node at index "startPos". IF IT IS, DO NOT ACCEPT IT AND
+  // RETURN -1. If position is NOT an ancestor, return position.
+  // Special case: The Document node (position==0) is acceptable.
+  //
+  // This test supports DTM.getNextPreceding.
+  int specialFind(int startPos, int position)
+  {
+          // We have to look all the way up the ancestor chain
+          // to make sure we don't have an ancestor.
+          int ancestor = startPos;
+          while(ancestor > 0)
+          {
+                // Get the node whose index == ancestor
+                ancestor*=slotsize;
+                int chunkpos = ancestor >> lowbits;
+                int slotpos = ancestor & lowmask;
+                int[] chunk = chunks.elementAt(chunkpos);
+                                                        
+                // Get that node's parent (Note that this assumes w[1]
+                // is the parent node index. That's really a DTM feature
+                // rather than a ChunkedIntArray feature.)
+                ancestor = chunk[slotpos + 1];
+
+                if(ancestor == position)
+                         break;
+          }
+
+          if (ancestor <= 0) 
+          {
+                  return position;
+          }
+          return -1;
+  }
+  
+  /**
+   * @return int index of highest-numbered record currently in use
+   */
+  int slotsUsed()
+  {
+    return lastUsed;
+  }
+
+  /** Disard the highest-numbered record. This is used in the string-buffer
+   CIA; when only a single characters() chunk has been recieved, its index
+   is moved into the Text node rather than being referenced by indirection
+   into the text accumulator.
+   */
+  void discardLast()
+  {
+    --lastUsed;
+  }
+
+  /**
+   * Overwrite the integer found at a specific record and column.
+   * Used to back-patch existing records, most often changing their
+   * "next sibling" reference from 0 (unknown) to something meaningful
+   * @param position int Record number
+   * @param offset int Column number
+   * @param value int New contents
+   */
+  void writeEntry(int position, int offset, int value) throws ArrayIndexOutOfBoundsException
+  {
+    /*
+    try
+    {
+      fastArray[( position*slotsize)+offset] = value;
+    }
+    catch(ArrayIndexOutOfBoundsException aioobe)
+    */
+    {
+      if (offset >= slotsize)
+        throw new ArrayIndexOutOfBoundsException(XMLMessages.createXMLMessage(XMLErrorResources.ER_OFFSET_BIGGER_THAN_SLOT, null)); //"Offset bigger than slot");
+      position*=slotsize;
+      int chunkpos = position >> lowbits;
+      int slotpos = position & lowmask;
+      int[] chunk = chunks.elementAt(chunkpos);
+      chunk[slotpos + offset] = value; // ATOMIC!
+    }
+  }
+
+  /**
+   * Overwrite an entire (4-integer) record at the specified index.
+   * Mostly used to create record 0, the Document node.
+   * @param position integer Record number
+   * @param w0 int 
+   * @param w1 int
+   * @param w2 int
+   * @param w3 int
+   */
+  void writeSlot(int position, int w0, int w1, int w2, int w3)
+  {
+      position *= slotsize;
+      int chunkpos = position >> lowbits;
+      int slotpos = (position & lowmask);
+
+    // Grow if needed
+    if (chunkpos > chunks.size() - 1)
+      chunks.addElement(new int[chunkalloc]);
+    int[] chunk = chunks.elementAt(chunkpos);
+    chunk[slotpos] = w0;
+    chunk[slotpos + 1] = w1;
+    chunk[slotpos + 2] = w2;
+    chunk[slotpos + 3] = w3;
+  }
+
+  /**
+   * Retrieve the contents of a record into a user-supplied buffer array.
+   * Used to reduce addressing overhead when code will access several
+   * columns of the record.
+   * @param position int Record number
+   * @param buffer int[] Integer array provided by user, must be large enough
+   * to hold a complete record.
+   */
+  void readSlot(int position, int[] buffer)
+  {
+    /*
+    try
+    {
+      System.arraycopy(fastArray, position*slotsize, buffer, 0, slotsize);
+    }
+    catch(ArrayIndexOutOfBoundsException aioobe)
+    */
+    {
+      // System.out.println("Using slow read (2): "+position);
+      position *= slotsize;
+      int chunkpos = position >> lowbits;
+      int slotpos = (position & lowmask);
+
+      // Grow if needed
+      if (chunkpos > chunks.size() - 1)
+        chunks.addElement(new int[chunkalloc]);
+      int[] chunk = chunks.elementAt(chunkpos);
+      System.arraycopy(chunk,slotpos,buffer,0,slotsize);
+    }
+  }
+
+  class ChunksVector
+  {
+    final int BLOCKSIZE = 64;
+    int[] m_map[] = new int[BLOCKSIZE][];
+    int m_mapSize = BLOCKSIZE;
+    int pos = 0;
+    
+    ChunksVector()
+    {
+    }
+    
+    final int size()
+    {
+      return pos;
+    }
+    
+    void addElement(int[] value)
+    {
+      if(pos >= m_mapSize)
+      {
+        int orgMapSize = m_mapSize;
+        while(pos >= m_mapSize)
+          m_mapSize+=BLOCKSIZE;
+        int[] newMap[] = new int[m_mapSize][];
+        System.arraycopy(m_map, 0, newMap, 0, orgMapSize);
+        m_map = newMap;
+      }
+      // For now, just do a simple append.  A sorted insert only 
+      // makes sense if we're doing an binary search or some such.
+      m_map[pos] = value;
+      pos++;
+    }
+    
+    final int[] elementAt(int pos)
+    {
+      return m_map[pos];
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/CoroutineManager.java b/src/main/java/org/apache/xml/dtm/ref/CoroutineManager.java
new file mode 100644
index 0000000..d353149
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/CoroutineManager.java
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: CoroutineManager.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import java.util.BitSet;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+
+/**
+ * <p>Support the coroutine design pattern.</p>
+ * 
+ * <p>A coroutine set is a very simple cooperative non-preemptive
+ * multitasking model, where the switch from one task to another is
+ * performed via an explicit request. Coroutines interact according to
+ * the following rules:</p>
+ *
+ * <ul>
+ * <li>One coroutine in the set has control, which it retains until it
+ * either exits or resumes another coroutine.</li>
+ * <li>A coroutine is activated when it is resumed by some other coroutine
+ * for the first time.</li>
+ * <li>An active coroutine that gives up control by resuming another in
+ * the set retains its context -- including call stack and local variables
+ * -- so that if/when it is resumed, it will proceed from the point at which
+ * it last gave up control.</li>
+ * </ul>
+ *
+ * <p>Coroutines can be thought of as falling somewhere between pipes and
+ * subroutines. Like call/return, there is an explicit flow of control
+ * from one coroutine to another. Like pipes, neither coroutine is
+ * actually "in charge", and neither must exit in order to transfer
+ * control to the other. </p>
+ * 
+ * <p>One classic application of coroutines is in compilers, where both
+ * the parser and the lexer are maintaining complex state
+ * information. The parser resumes the lexer to process incoming
+ * characters into lexical tokens, and the lexer resumes the parser
+ * when it has reached a point at which it has a reliably interpreted
+ * set of tokens available for semantic processing. Structuring this
+ * as call-and-return would require saving and restoring a
+ * considerable amount of state each time. Structuring it as two tasks
+ * connected by a queue may involve higher overhead (in systems which
+ * can optimize the coroutine metaphor), isn't necessarily as clear in
+ * intent, may have trouble handling cases where data flows in both
+ * directions, and may not handle some of the more complex cases where
+ * more than two coroutines are involved.</p>
+ * 
+ * <p>Most coroutine systems also provide a way to pass data between the
+ * source and target of a resume operation; this is sometimes referred
+ * to as "yielding" a value.  Others rely on the fact that, since only
+ * one member of a coroutine set is running at a time and does not
+ * lose control until it chooses to do so, data structures may be
+ * directly shared between them with only minimal precautions.</p>
+ * 
+ * <p>"Note: This should not be taken to mean that producer/consumer
+ * problems should be always be done with coroutines." Queueing is
+ * often a better solution when only two threads of execution are
+ * involved and full two-way handshaking is not required. It's a bit
+ * difficult to find short pedagogical examples that require
+ * coroutines for a clear solution.</p>
+ * 
+ * <p>The fact that only one of a group of coroutines is running at a
+ * time, and the control transfer between them is explicit, simplifies
+ * their possible interactions, and in some implementations permits
+ * them to be implemented more efficiently than general multitasking.
+ * In some situations, coroutines can be compiled out entirely;
+ * in others, they may only require a few instructions more than a
+ * simple function call.</p>
+ *
+ * <p>This version is built on top of standard Java threading, since
+ * that's all we have available right now. It's been encapsulated for
+ * code clarity and possible future optimization.</p>
+ * 
+ * <p>(Two possible approaches: wait-notify based and queue-based. Some
+ * folks think that a one-item queue is a cleaner solution because it's
+ * more abstract -- but since coroutine _is_ an abstraction I'm not really
+ * worried about that; folks should be able to switch this code without
+ * concern.)</p>
+ * 
+ * <p>%TBD% THIS SHOULD BE AN INTERFACE, to facilitate building other
+ * implementations... perhaps including a true coroutine system
+ * someday, rather than controlled threading. Arguably Coroutine
+ * itself should be an interface much like Runnable, but I think that
+ * can be built on top of this.</p>
+ * */
+public class CoroutineManager
+{
+  /** "Is this coroutine ID number already in use" lookup table.
+   * Currently implemented as a bitset as a compromise between
+   * compactness and speed of access, but obviously other solutions
+   * could be applied.
+   * */
+  BitSet m_activeIDs=new BitSet();
+
+  /** Limit on the coroutine ID numbers accepted. I didn't want the
+   * in-use table to grow without bound. If we switch to a more efficient
+   * sparse-array mechanism, it may be possible to raise or eliminate
+   * this boundary.
+   * @xsl.usage internal
+   */
+  static final int m_unreasonableId=1024;
+
+  /** Internal field used to hold the data being explicitly passed
+   * from one coroutine to another during a co_resume() operation.
+   * (Of course implicit data sharing may also occur; one of the reasons
+   * for using coroutines is that you're guaranteed that none of the
+   * other coroutines in your set are using shared structures at the time
+   * you access them.)
+   *
+   * %REVIEW% It's been proposed that we be able to pass types of data
+   * other than Object -- more specific object types, or
+   * lighter-weight primitives.  This would seem to create a potential
+   * explosion of "pass x recieve y back" methods (or require
+   * fracturing resume into two calls, resume-other and
+   * wait-to-be-resumed), and the weight issue could be managed by
+   * reusing a mutable buffer object to contain the primitive
+   * (remember that only one coroutine runs at a time, so once the
+   * buffer's set it won't be walked on). Typechecking objects is
+   * interesting from a code-robustness point of view, but it's
+   * unclear whether it makes sense to encapsulate that in the
+   * coroutine code or let the callers do it, since it depends on RTTI
+   * either way. Restricting the parameters to objects implementing a
+   * specific CoroutineParameter interface does _not_ seem to be a net
+   * win; applications can do so if they want via front-end code, but
+   * there seem to be too many use cases involving passing an existing
+   * object type that you may not have the freedom to alter and may
+   * not want to spend time wrapping another object around.
+   * */
+  Object m_yield=null;
+
+  // Expose???
+  final static int NOBODY=-1;
+  final static int ANYBODY=-1;
+
+  /** Internal field used to confirm that the coroutine now waking up is
+   * in fact the one we intended to resume. Some such selection mechanism
+   * is needed when more that two coroutines are operating within the same
+   * group.
+   */
+  int m_nextCoroutine=NOBODY;
+  
+  /** <p>Each coroutine in the set managed by a single
+   * CoroutineManager is identified by a small positive integer. This
+   * brings up the question of how to manage those integers to avoid
+   * reuse... since if two coroutines use the same ID number, resuming
+   * that ID could resume either. I can see arguments for either
+   * allowing applications to select their own numbers (they may want
+   * to declare mnemonics via manefest constants) or generating
+   * numbers on demand.  This routine's intended to support both
+   * approaches.</p>
+   *
+   * <p>%REVIEW% We could use an object as the identifier. Not sure
+   * it's a net gain, though it would allow the thread to be its own
+   * ID. Ponder.</p>
+   *
+   * @param coroutineID  If >=0, requests that we reserve this number.
+   * If <0, requests that we find, reserve, and return an available ID
+   * number.
+   *
+   * @return If >=0, the ID number to be used by this coroutine. If <0,
+   * an error occurred -- the ID requested was already in use, or we
+   * couldn't assign one without going over the "unreasonable value" mark
+   * */
+  public synchronized int co_joinCoroutineSet(int coroutineID)
+  {
+    if(coroutineID>=0)
+      {
+        if(coroutineID>=m_unreasonableId || m_activeIDs.get(coroutineID))
+          return -1;
+      }
+    else
+      {
+        // What I want is "Find first clear bit". That doesn't exist.
+        // JDK1.2 added "find last set bit", but that doesn't help now.
+        coroutineID=0;
+        while(coroutineID<m_unreasonableId)
+          {
+            if(m_activeIDs.get(coroutineID))
+              ++coroutineID;
+            else
+              break;
+          }
+        if(coroutineID>=m_unreasonableId)
+          return -1;
+      }
+
+    m_activeIDs.set(coroutineID);
+    return coroutineID;
+  }
+
+  /** In the standard coroutine architecture, coroutines are
+   * identified by their method names and are launched and run up to
+   * their first yield by simply resuming them; its's presumed that
+   * this recognizes the not-already-running case and does the right
+   * thing. We seem to need a way to achieve that same threadsafe
+   * run-up...  eg, start the coroutine with a wait.
+   *
+   * %TBD% whether this makes any sense...
+   *
+   * @param thisCoroutine the identifier of this coroutine, so we can
+   * recognize when we are being resumed.
+   * @exception java.lang.NoSuchMethodException if thisCoroutine isn't
+   * a registered member of this group. %REVIEW% whether this is the
+   * best choice.
+   * */
+  public synchronized Object co_entry_pause(int thisCoroutine) throws java.lang.NoSuchMethodException
+  {
+    if(!m_activeIDs.get(thisCoroutine))
+      throw new java.lang.NoSuchMethodException();
+
+    while(m_nextCoroutine != thisCoroutine)
+      {
+        try 
+          {
+            wait();
+          }
+        catch(java.lang.InterruptedException e)
+          {
+            // %TBD% -- Declare? Encapsulate? Ignore? Or
+            // dance widdershins about the instruction cache?
+          }
+      }
+    
+    return m_yield;
+  }
+
+  /** Transfer control to another coroutine which has already been started and
+   * is waiting on this CoroutineManager. We won't return from this call
+   * until that routine has relinquished control.
+   *
+   * %TBD% What should we do if toCoroutine isn't registered? Exception?
+   *
+   * @param arg_object A value to be passed to the other coroutine.
+   * @param thisCoroutine Integer identifier for this coroutine. This is the
+   * ID we watch for to see if we're the ones being resumed.
+   * @param toCoroutine  Integer identifier for the coroutine we wish to
+   * invoke. 
+   * @exception java.lang.NoSuchMethodException if toCoroutine isn't a
+   * registered member of this group. %REVIEW% whether this is the best choice.
+   * */
+  public synchronized Object co_resume(Object arg_object,int thisCoroutine,int toCoroutine) throws java.lang.NoSuchMethodException
+  {
+    if(!m_activeIDs.get(toCoroutine))
+      throw new java.lang.NoSuchMethodException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_NOT_AVAIL, new Object[]{Integer.toString(toCoroutine)})); //"Coroutine not available, id="+toCoroutine);
+
+    // We expect these values to be overwritten during the notify()/wait()
+    // periods, as other coroutines in this set get their opportunity to run.
+    m_yield=arg_object;
+    m_nextCoroutine=toCoroutine;
+
+    notify();
+    while(m_nextCoroutine != thisCoroutine || m_nextCoroutine==ANYBODY || m_nextCoroutine==NOBODY)
+      {
+        try 
+          {
+            // System.out.println("waiting...");
+            wait();
+          }
+        catch(java.lang.InterruptedException e)
+          {
+            // %TBD% -- Declare? Encapsulate? Ignore? Or
+            // dance deasil about the program counter?
+          }
+      }
+
+    if(m_nextCoroutine==NOBODY)
+      {
+        // Pass it along
+        co_exit(thisCoroutine);
+        // And inform this coroutine that its partners are Going Away
+        // %REVIEW% Should this throw/return something more useful?
+        throw new java.lang.NoSuchMethodException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_CO_EXIT, null)); //"CoroutineManager recieved co_exit() request");
+      }
+    
+    return m_yield;
+  }
+  
+  /** Terminate this entire set of coroutines. The others will be
+   * deregistered and have exceptions thrown at them. Note that this
+   * is intended as a panic-shutdown operation; under normal
+   * circumstances a coroutine should always end with co_exit_to() in
+   * order to politely inform at least one of its partners that it is
+   * going away.
+   *
+   * %TBD% This may need significantly more work. 
+   *
+   * %TBD% Should this just be co_exit_to(,,CoroutineManager.PANIC)?
+   *
+   * @param thisCoroutine Integer identifier for the coroutine requesting exit.
+   * */
+  public synchronized void co_exit(int thisCoroutine)
+  {
+    m_activeIDs.clear(thisCoroutine);
+    m_nextCoroutine=NOBODY; // %REVIEW%
+    notify();
+  }
+
+  /** Make the ID available for reuse and terminate this coroutine,
+   * transferring control to the specified coroutine. Note that this
+   * returns immediately rather than waiting for any further coroutine
+   * traffic, so the thread can proceed with other shutdown activities.
+   *
+   * @param arg_object    A value to be passed to the other coroutine.
+   * @param thisCoroutine Integer identifier for the coroutine leaving the set.
+   * @param toCoroutine   Integer identifier for the coroutine we wish to
+   * invoke. 
+   * @exception java.lang.NoSuchMethodException if toCoroutine isn't a
+   * registered member of this group. %REVIEW% whether this is the best choice.
+   * */
+  public synchronized void co_exit_to(Object arg_object,int thisCoroutine,int toCoroutine) throws java.lang.NoSuchMethodException
+  {
+    if(!m_activeIDs.get(toCoroutine))
+      throw new java.lang.NoSuchMethodException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COROUTINE_NOT_AVAIL, new Object[]{Integer.toString(toCoroutine)})); //"Coroutine not available, id="+toCoroutine);
+    
+    // We expect these values to be overwritten during the notify()/wait()
+    // periods, as other coroutines in this set get their opportunity to run.
+    m_yield=arg_object;
+    m_nextCoroutine=toCoroutine;
+
+    m_activeIDs.clear(thisCoroutine);
+
+    notify();
+  }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMAxisIterNodeList.java b/src/main/java/org/apache/xml/dtm/ref/DTMAxisIterNodeList.java
new file mode 100644
index 0000000..d4acca4
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMAxisIterNodeList.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMAxisIterNodeList.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisIterator;
+import org.apache.xml.utils.IntVector;
+
+import org.w3c.dom.Node;
+
+/**
+ * <code>DTMAxisNodeList</code> gives us an implementation of the DOM's
+ * NodeList interface wrapped around a DTM Iterator. The author
+ * considers this something of an abominations, since NodeList was not
+ * intended to be a general purpose "list of nodes" API and is
+ * generally considered by the DOM WG to have be a mistake... but I'm
+ * told that some of the XPath/XSLT folks say they must have this
+ * solution.
+ *
+ * Please note that this is not necessarily equivlaent to a DOM
+ * NodeList operating over the same document. In particular:
+ * <ul>
+ *
+ * <li>If there are several Text nodes in logical succession (ie,
+ * across CDATASection and EntityReference boundaries), we will return
+ * only the first; the caller is responsible for stepping through
+ * them.
+ * (%REVIEW% Provide a convenience routine here to assist, pending
+ * proposed DOM Level 3 getAdjacentText() operation?) </li>
+ *
+ * <li>Since the whole XPath/XSLT architecture assumes that the source
+ * document is not altered while we're working with it, we do not
+ * promise to implement the DOM NodeList's "live view" response to
+ * document mutation. </li>
+ *
+ * </ul>
+ *
+ * <p>State: In progress!!</p>
+ * */
+public class DTMAxisIterNodeList extends DTMNodeListBase {
+    private DTM m_dtm;
+    private DTMAxisIterator m_iter;
+    private IntVector m_cachedNodes;
+    private int m_last = -1;
+    //================================================================
+    // Methods unique to this class
+    private DTMAxisIterNodeList() {
+    }
+
+    /**
+     * Public constructor: Wrap a DTMNodeList around an existing
+     * and preconfigured DTMAxisIterator
+     */
+    public DTMAxisIterNodeList(DTM dtm, DTMAxisIterator dtmAxisIterator) {
+        if (dtmAxisIterator == null) {
+            m_last = 0;
+        } else {
+            m_cachedNodes = new IntVector();
+            m_dtm = dtm;
+        }
+        m_iter = dtmAxisIterator;
+    }
+
+    /**
+     * Access the wrapped DTMIterator. I'm not sure whether anyone will
+     * need this or not, but let's write it and think about it.
+     *
+     */
+    public DTMAxisIterator getDTMAxisIterator() {
+        return m_iter;
+    }
+  
+
+    //================================================================
+    // org.w3c.dom.NodeList API follows
+
+    /**
+     * Returns the <code>index</code>th item in the collection. If 
+     * <code>index</code> is greater than or equal to the number of nodes in 
+     * the list, this returns <code>null</code>.
+     * @param index Index into the collection.
+     * @return The node at the <code>index</code>th position in the 
+     *   <code>NodeList</code>, or <code>null</code> if that is not a valid 
+     *   index.
+     */
+    public Node item(int index) {
+        if (m_iter != null) {
+            int node;
+            int count = m_cachedNodes.size();
+
+            if (count > index) {
+                node = m_cachedNodes.elementAt(index);
+                return m_dtm.getNode(node);
+            } else if (m_last == -1) {
+                while (((node = m_iter.next()) != DTMAxisIterator.END)
+                           && count <= index) {
+                    m_cachedNodes.addElement(node);
+                    count++;
+                }
+                if (node == DTMAxisIterator.END) {
+                    m_last = count;
+                } else {
+                    return m_dtm.getNode(node);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * The number of nodes in the list. The range of valid child node indices 
+     * is 0 to <code>length-1</code> inclusive. 
+     */
+    public int getLength() {
+        if (m_last == -1) {
+            int node;
+            while ((node = m_iter.next()) != DTMAxisIterator.END) {
+                m_cachedNodes.addElement(node);
+            }
+            m_last = m_cachedNodes.size();
+        }
+        return m_last;
+    }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMAxisIteratorBase.java b/src/main/java/org/apache/xml/dtm/ref/DTMAxisIteratorBase.java
new file mode 100644
index 0000000..99c176b
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMAxisIteratorBase.java
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMAxisIteratorBase.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.DTMAxisIterator;
+
+/**
+ * This class serves as a default base for implementations of mutable
+ * DTMAxisIterators.
+ */
+public abstract class DTMAxisIteratorBase implements DTMAxisIterator
+{
+
+  /** The position of the last node within the iteration, as defined by XPath.
+   * Note that this is _not_ the node's handle within the DTM. Also, don't
+   * confuse it with the current (most recently returned) position.
+   */
+  protected int _last = -1;
+
+  /** The position of the current node within the iteration, as defined by XPath.
+   * Note that this is _not_ the node's handle within the DTM!
+   */
+  protected int _position = 0;
+
+  /** The position of the marked node within the iteration;
+   * a saved itaration state that we may want to come back to.
+   * Note that only one mark is maintained; there is no stack.
+   */
+  protected int _markedNode;
+
+  /** The handle to the start, or root, of the iteration.
+   * Set this to END to construct an empty iterator.
+   */
+  protected int _startNode = DTMAxisIterator.END;
+
+  /** True if the start node should be considered part of the iteration.
+   * False will cause it to be skipped.
+   */
+  protected boolean _includeSelf = false;
+
+  /** True if this iteration can be restarted. False otherwise (eg, if
+   * we are iterating over a stream that can not be re-scanned, or if
+   * the iterator was produced by cloning another iterator.)
+   */
+  protected boolean _isRestartable = true;
+  
+  /**
+   * Get start to END should 'close' the iterator,
+   * i.e. subsequent call to next() should return END.
+   *
+   * @return The root node of the iteration.
+   */
+  public int getStartNode()
+  {
+    return _startNode;
+  }
+
+  /**
+   * @return A DTMAxisIterator which has been reset to the start node,
+   * which may or may not be the same as this iterator.
+   * */
+  public DTMAxisIterator reset()
+  {
+
+    final boolean temp = _isRestartable;
+
+    _isRestartable = true;
+
+    setStartNode(_startNode);
+
+    _isRestartable = temp;
+
+    return this;
+  }
+
+  /**
+   * Set the flag to include the start node in the iteration. 
+   *
+   *
+   * @return This default method returns just returns this DTMAxisIterator,
+   * after setting the flag.
+   * (Returning "this" permits C++-style chaining of
+   * method calls into a single expression.)
+   */
+  public DTMAxisIterator includeSelf()
+  {
+
+    _includeSelf = true;
+
+    return this;
+  }
+
+  /** Returns the position of the last node within the iteration, as
+   * defined by XPath.  In a forward iterator, I believe this equals the number of nodes which this
+   * iterator will yield. In a reverse iterator, I believe it should return
+   * 1 (since the "last" is the first produced.)
+   *
+   * This may be an expensive operation when called the first time, since
+   * it may have to iterate through a large part of the document to produce
+   * its answer.
+   *
+   * @return The number of nodes in this iterator (forward) or 1 (reverse).
+   */
+  public int getLast()
+  {
+
+    if (_last == -1)		// Not previously established
+    {
+      // Note that we're doing both setMark() -- which saves _currentChild
+      // -- and explicitly saving our position counter (number of nodes
+      // yielded so far).
+      //
+      // %REVIEW% Should position also be saved by setMark()?
+      // (It wasn't in the XSLTC version, but I don't understand why not.)
+
+      final int temp = _position; // Save state
+      setMark();
+
+      reset();			// Count the nodes found by this iterator
+      do
+      {
+        _last++;
+      }
+      while (next() != END);
+
+      gotoMark();		// Restore saved state
+      _position = temp;
+    }
+
+    return _last;
+  }
+
+  /**
+   * @return The position of the current node within the set, as defined by
+   * XPath. Note that this is one-based, not zero-based.
+   */
+  public int getPosition()
+  {
+    return _position == 0 ? 1 : _position;
+  }
+
+  /**
+   * @return true if this iterator has a reversed axis, else false
+   */
+  public boolean isReverse()
+  {
+    return false;
+  }
+
+  /**
+   * Returns a deep copy of this iterator. Cloned iterators may not be
+   * restartable. The iterator being cloned may or may not become
+   * non-restartable as a side effect of this operation.
+   *
+   * @return a deep copy of this iterator.
+   */
+  public DTMAxisIterator cloneIterator()
+  {
+
+    try
+    {
+      final DTMAxisIteratorBase clone = (DTMAxisIteratorBase) super.clone();
+
+      clone._isRestartable = false;
+
+      // return clone.reset();
+      return clone;
+    }
+    catch (CloneNotSupportedException e)
+    {
+      throw new org.apache.xml.utils.WrappedRuntimeException(e);
+    }
+  }
+
+  /**
+   * Do any final cleanup that is required before returning the node that was
+   * passed in, and then return it. The intended use is
+   * <br />
+   * <code>return returnNode(node);</code>
+   *
+   * %REVIEW% If we're calling it purely for side effects, should we really
+   * be bothering with a return value? Something like
+   * <br />
+   * <code> accept(node); return node; </code>
+   * <br />
+   * would probably optimize just about as well and avoid questions
+   * about whether what's returned could ever be different from what's
+   * passed in.
+   *
+   * @param node Node handle which iteration is about to yield.
+   *
+   * @return The node handle passed in.  */
+  protected final int returnNode(final int node)
+  {
+    _position++;
+
+    return node;
+  }
+
+  /**
+   * Reset the position to zero. NOTE that this does not change the iteration
+   * state, only the position number associated with that state.
+   *
+   * %REVIEW% Document when this would be used?
+   *
+   * @return This instance.
+   */
+  protected final DTMAxisIterator resetPosition()
+  {
+
+    _position = 0;
+
+    return this;
+  }
+  
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * 
+   * @return true as a default.
+   */
+  public boolean isDocOrdered()
+  {
+    return true;
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return -1;
+  }
+  
+  public void setRestartable(boolean isRestartable) {
+    _isRestartable = isRestartable;
+  }  
+
+  /**
+   * Return the node at the given position.
+   * 
+   * @param position The position
+   * @return The node at the given position.
+   */
+  public int getNodeByPosition(int position)
+  {
+    if (position > 0) {
+      final int pos = isReverse() ? getLast() - position + 1
+                                   : position;
+      int node;
+      while ((node = next()) != DTMAxisIterator.END) {
+        if (pos == getPosition()) {
+          return node;
+        }
+      }
+    }
+    return END;
+  }
+  
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMChildIterNodeList.java b/src/main/java/org/apache/xml/dtm/ref/DTMChildIterNodeList.java
new file mode 100644
index 0000000..29104db
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMChildIterNodeList.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMChildIterNodeList.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.DTM;
+import org.w3c.dom.Node;
+
+/**
+ * <code>DTMNodeList</code> gives us an implementation of the DOM's
+ * NodeList interface wrapped around a DTM Iterator. The author
+ * considers this something of an abominations, since NodeList was not
+ * intended to be a general purpose "list of nodes" API and is
+ * generally considered by the DOM WG to have be a mistake... but I'm
+ * told that some of the XPath/XSLT folks say they must have this
+ * solution.
+ *
+ * Please note that this is not necessarily equivlaent to a DOM
+ * NodeList operating over the same document. In particular:
+ * <ul>
+ *
+ * <li>If there are several Text nodes in logical succession (ie,
+ * across CDATASection and EntityReference boundaries), we will return
+ * only the first; the caller is responsible for stepping through
+ * them.
+ * (%REVIEW% Provide a convenience routine here to assist, pending
+ * proposed DOM Level 3 getAdjacentText() operation?) </li>
+ *
+ * <li>Since the whole XPath/XSLT architecture assumes that the source
+ * document is not altered while we're working with it, we do not
+ * promise to implement the DOM NodeList's "live view" response to
+ * document mutation. </li>
+ *
+ * </ul>
+ *
+ * <p>State: In progress!!</p>
+ * */
+public class DTMChildIterNodeList extends DTMNodeListBase {
+    private int m_firstChild;
+    private DTM m_parentDTM;
+
+    //================================================================
+    // Methods unique to this class
+    private DTMChildIterNodeList() {
+    }
+
+    /**
+     * Public constructor: Create a NodeList to support
+     * DTMNodeProxy.getChildren().
+     *
+     * Unfortunately AxisIterators and DTMIterators don't share an API,
+     * so I can't use the existing Axis.CHILD iterator. Rather than
+     * create Yet Another Class, let's set up a special case of this
+     * one.
+     *
+     * @param parentDTM The DTM containing this node
+     * @param parentHandle DTM node-handle integer
+     *
+     */
+    public DTMChildIterNodeList(DTM parentDTM,int parentHandle) {
+        m_parentDTM=parentDTM;
+        m_firstChild=parentDTM.getFirstChild(parentHandle);
+    }
+
+
+    //================================================================
+    // org.w3c.dom.NodeList API follows
+
+    /**
+     * Returns the <code>index</code>th item in the collection. If 
+     * <code>index</code> is greater than or equal to the number of nodes in 
+     * the list, this returns <code>null</code>.
+     * @param index Index into the collection.
+     * @return The node at the <code>index</code>th position in the 
+     *   <code>NodeList</code>, or <code>null</code> if that is not a valid 
+     *   index.
+     */
+    public Node item(int index) {
+        int handle=m_firstChild;
+        while(--index>=0 && handle!=DTM.NULL) {
+            handle=m_parentDTM.getNextSibling(handle);
+        }
+        if (handle == DTM.NULL) {
+            return null;
+        }
+        return m_parentDTM.getNode(handle);
+    }
+
+    /**
+     * The number of nodes in the list. The range of valid child node indices 
+     * is 0 to <code>length-1</code> inclusive. 
+     */
+    public int getLength() {
+        int count=0;
+        for (int handle=m_firstChild;
+             handle!=DTM.NULL;
+             handle=m_parentDTM.getNextSibling(handle)) {
+            ++count;
+        }
+        return count;
+    }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBase.java b/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBase.java
new file mode 100644
index 0000000..d0647bf
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBase.java
@@ -0,0 +1,2371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMDefaultBase.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.*;
+import org.apache.xml.utils.SuballocatedIntVector;
+import org.apache.xml.utils.BoolStack;
+
+import java.util.Vector;
+
+import javax.xml.transform.Source;
+
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringFactory;
+
+import org.apache.xml.res.XMLMessages;
+import org.apache.xml.res.XMLErrorResources;
+
+import java.io.*; // for dumpDTM
+
+/**
+ * The <code>DTMDefaultBase</code> class serves as a helper base for DTMs.
+ * It sets up structures for navigation and type, while leaving data
+ * management and construction to the derived classes.
+ */
+public abstract class DTMDefaultBase implements DTM
+{
+    static final boolean JJK_DEBUG=false;
+
+  // This constant is likely to be removed in the future. Use the 
+  // getDocument() method instead of ROOTNODE to get at the root 
+  // node of a DTM.
+  /** The identity of the root node. */
+  public static final int ROOTNODE = 0;
+	
+  /**
+   * The number of nodes, which is also used to determine the next
+   *  node index.
+   */
+  protected int m_size = 0;
+
+  /** The expanded names, one array element for each node. */
+  protected SuballocatedIntVector m_exptype;
+
+  /** First child values, one array element for each node. */
+  protected SuballocatedIntVector m_firstch;
+
+  /** Next sibling values, one array element for each node. */
+  protected SuballocatedIntVector m_nextsib;
+
+  /** Previous sibling values, one array element for each node. */
+  protected SuballocatedIntVector m_prevsib;
+
+  /** Previous sibling values, one array element for each node. */
+  protected SuballocatedIntVector m_parent;
+
+  /** Vector of SuballocatedIntVectors of NS decl sets */
+  protected Vector m_namespaceDeclSets = null;
+
+  /** SuballocatedIntVector  of elements at which corresponding
+   * namespaceDeclSets were defined */
+  protected SuballocatedIntVector m_namespaceDeclSetElements = null;
+
+  /**
+   * These hold indexes to elements based on namespace and local name.
+   * The base lookup is the the namespace.  The second lookup is the local
+   * name, and the last array contains the the first free element
+   * at the start, and the list of element handles following.
+   */
+  protected int[][][] m_elemIndexes;
+
+  /** The default block size of the node arrays */
+  public static final int DEFAULT_BLOCKSIZE = 512;  // favor small docs.
+  
+  /** The number of blocks for the node arrays */
+  public static final int DEFAULT_NUMBLOCKS = 32;
+  
+  /** The number of blocks used for small documents & RTFs */
+  public static final int DEFAULT_NUMBLOCKS_SMALL = 4;
+  
+  /** The block size of the node arrays */
+  //protected final int m_blocksize;
+
+  /**
+   * The value to use when the information has not been built yet.
+   */
+  protected static final int NOTPROCESSED = DTM.NULL - 1;
+
+  /**
+   * The DTM manager who "owns" this DTM.
+   */
+
+  public DTMManager m_mgr;
+
+  /**
+   * m_mgr cast to DTMManagerDefault, or null if it isn't an instance
+   * (Efficiency hook)
+   */
+  protected DTMManagerDefault m_mgrDefault=null;
+
+
+  /** The document identity number(s). If we have overflowed the addressing
+   * range of the first that was assigned to us, we may add others. */
+  protected SuballocatedIntVector m_dtmIdent;
+
+  /** The mask for the identity.
+      %REVIEW% Should this really be set to the _DEFAULT? What if
+      a particular DTM wanted to use another value? */
+  //protected final static int m_mask = DTMManager.IDENT_NODE_DEFAULT;
+
+  /** The base URI for this document. */
+  protected String m_documentBaseURI;
+
+  /**
+   * The whitespace filter that enables elements to strip whitespace or not.
+   */
+  protected DTMWSFilter m_wsfilter;
+
+  /** Flag indicating whether to strip whitespace nodes */
+  protected boolean m_shouldStripWS = false;
+
+  /** Stack of flags indicating whether to strip whitespace nodes */
+  protected BoolStack m_shouldStripWhitespaceStack;
+
+  /** The XMLString factory for creating XMLStrings. */
+  protected XMLStringFactory m_xstrf;
+
+  /**
+   * The table for exandedNameID lookups.  This may or may not be the same
+   * table as is contained in the DTMManagerDefault.
+   */
+  protected ExpandedNameTable m_expandedNameTable;
+
+  /** true if indexing is turned on. */
+  protected boolean m_indexing;
+
+  /**
+   * Construct a DTMDefaultBase object using the default block size.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param source The object that is used to specify the construction source.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may
+   *                         be null.
+   * @param xstringfactory The factory to use for creating XMLStrings.
+   * @param doIndexing true if the caller considers it worth it to use
+   *                   indexing schemes.
+   */
+  public DTMDefaultBase(DTMManager mgr, Source source, int dtmIdentity,
+  			DTMWSFilter whiteSpaceFilter,
+  			XMLStringFactory xstringfactory, boolean doIndexing)
+  {
+    this(mgr, source, dtmIdentity, whiteSpaceFilter, xstringfactory,
+         doIndexing, DEFAULT_BLOCKSIZE, true, false);
+  }
+
+  /**
+   * Construct a DTMDefaultBase object from a DOM node.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param source The object that is used to specify the construction source.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may
+   *                         be null.
+   * @param xstringfactory The factory to use for creating XMLStrings.
+   * @param doIndexing true if the caller considers it worth it to use
+   *                   indexing schemes.
+   * @param blocksize The block size of the DTM.
+   * @param usePrevsib true if we want to build the previous sibling node array.
+   * @param newNameTable true if we want to use a new ExpandedNameTable for this DTM.
+   */
+  public DTMDefaultBase(DTMManager mgr, Source source, int dtmIdentity,
+                        DTMWSFilter whiteSpaceFilter,
+                        XMLStringFactory xstringfactory, boolean doIndexing,
+                        int blocksize, boolean usePrevsib,
+                        boolean newNameTable)
+  {    
+    // Use smaller sizes for the internal node arrays if the block size
+    // is small.
+    int numblocks;    
+    if (blocksize <= 64)
+    {
+      numblocks = DEFAULT_NUMBLOCKS_SMALL;
+      m_dtmIdent= new SuballocatedIntVector(4, 1);
+    }
+    else
+    {
+      numblocks = DEFAULT_NUMBLOCKS;
+      m_dtmIdent= new SuballocatedIntVector(32);
+    }
+    
+    m_exptype = new SuballocatedIntVector(blocksize, numblocks);
+    m_firstch = new SuballocatedIntVector(blocksize, numblocks);
+    m_nextsib = new SuballocatedIntVector(blocksize, numblocks);
+    m_parent  = new SuballocatedIntVector(blocksize, numblocks);
+    
+    // Only create the m_prevsib array if the usePrevsib flag is true.
+    // Some DTM implementations (e.g. SAXImpl) do not need this array.
+    // We can save the time to build it in those cases.
+    if (usePrevsib)
+      m_prevsib = new SuballocatedIntVector(blocksize, numblocks);
+
+    m_mgr = mgr;
+    if(mgr instanceof DTMManagerDefault)
+      m_mgrDefault=(DTMManagerDefault)mgr;
+    
+    m_documentBaseURI = (null != source) ? source.getSystemId() : null;
+    m_dtmIdent.setElementAt(dtmIdentity,0);
+    m_wsfilter = whiteSpaceFilter;
+    m_xstrf = xstringfactory;
+    m_indexing = doIndexing;
+
+    if (doIndexing)
+    {
+      m_expandedNameTable = new ExpandedNameTable();
+    }
+    else
+    {
+      // Note that this fails if we aren't talking to an instance of
+      // DTMManagerDefault
+      m_expandedNameTable = m_mgrDefault.getExpandedNameTable(this);
+    }
+
+    if (null != whiteSpaceFilter)
+    {
+      m_shouldStripWhitespaceStack = new BoolStack();
+
+      pushShouldStripWhitespace(false);
+    }
+  }
+
+  /**
+   * Ensure that the size of the element indexes can hold the information.
+   *
+   * @param namespaceID Namespace ID index.
+   * @param LocalNameID Local name ID.
+   */
+  protected void ensureSizeOfIndex(int namespaceID, int LocalNameID)
+  {
+
+    if (null == m_elemIndexes)
+    {
+      m_elemIndexes = new int[namespaceID + 20][][];
+    }
+    else if (m_elemIndexes.length <= namespaceID)
+    {
+      int[][][] indexes = m_elemIndexes;
+
+      m_elemIndexes = new int[namespaceID + 20][][];
+
+      System.arraycopy(indexes, 0, m_elemIndexes, 0, indexes.length);
+    }
+
+    int[][] localNameIndex = m_elemIndexes[namespaceID];
+
+    if (null == localNameIndex)
+    {
+      localNameIndex = new int[LocalNameID + 100][];
+      m_elemIndexes[namespaceID] = localNameIndex;
+    }
+    else if (localNameIndex.length <= LocalNameID)
+    {
+      int[][] indexes = localNameIndex;
+
+      localNameIndex = new int[LocalNameID + 100][];
+
+      System.arraycopy(indexes, 0, localNameIndex, 0, indexes.length);
+
+      m_elemIndexes[namespaceID] = localNameIndex;
+    }
+
+    int[] elemHandles = localNameIndex[LocalNameID];
+
+    if (null == elemHandles)
+    {
+      elemHandles = new int[128];
+      localNameIndex[LocalNameID] = elemHandles;
+      elemHandles[0] = 1;
+    }
+    else if (elemHandles.length <= elemHandles[0] + 1)
+    {
+      int[] indexes = elemHandles;
+
+      elemHandles = new int[elemHandles[0] + 1024];
+
+      System.arraycopy(indexes, 0, elemHandles, 0, indexes.length);
+
+      localNameIndex[LocalNameID] = elemHandles;
+    }
+  }
+
+  /**
+   * Add a node to the element indexes. The node will not be added unless
+   * it's an element.
+   *
+   * @param expandedTypeID The expanded type ID of the node.
+   * @param identity The node identity index.
+   */
+  protected void indexNode(int expandedTypeID, int identity)
+  {
+
+    ExpandedNameTable ent = m_expandedNameTable;
+    short type = ent.getType(expandedTypeID);
+
+    if (DTM.ELEMENT_NODE == type)
+    {
+      int namespaceID = ent.getNamespaceID(expandedTypeID);
+      int localNameID = ent.getLocalNameID(expandedTypeID);
+
+      ensureSizeOfIndex(namespaceID, localNameID);
+
+      int[] index = m_elemIndexes[namespaceID][localNameID];
+
+      index[index[0]] = identity;
+
+      index[0]++;
+    }
+  }
+
+  /**
+   * Find the first index that occurs in the list that is greater than or
+   * equal to the given value.
+   *
+   * @param list A list of integers.
+   * @param start The start index to begin the search.
+   * @param len The number of items to search.
+   * @param value Find the slot that has a value that is greater than or
+   * identical to this argument.
+   *
+   * @return The index in the list of the slot that is higher or identical
+   * to the identity argument, or -1 if no node is higher or equal.
+   */
+  protected int findGTE(int[] list, int start, int len, int value)
+  {
+
+    int low = start;
+    int high = start + (len - 1);
+    int end = high;
+
+    while (low <= high)
+    {
+      int mid = (low + high) / 2;
+      int c = list[mid];
+
+      if (c > value)
+        high = mid - 1;
+      else if (c < value)
+        low = mid + 1;
+      else
+        return mid;
+    }
+
+    return (low <= end && list[low] > value) ? low : -1;
+  }
+
+  /**
+   * Find the first matching element from the index at or after the
+   * given node.
+   *
+   * @param nsIndex The namespace index lookup.
+   * @param lnIndex The local name index lookup.
+   * @param firstPotential The first potential match that is worth looking at.
+   *
+   * @return The first node that is greater than or equal to the
+   *         firstPotential argument, or DTM.NOTPROCESSED if not found.
+   */
+  int findElementFromIndex(int nsIndex, int lnIndex, int firstPotential)
+  {
+
+    int[][][] indexes = m_elemIndexes;
+
+    if (null != indexes && nsIndex < indexes.length)
+    {
+      int[][] lnIndexs = indexes[nsIndex];
+
+      if (null != lnIndexs && lnIndex < lnIndexs.length)
+      {
+        int[] elems = lnIndexs[lnIndex];
+
+        if (null != elems)
+        {
+          int pos = findGTE(elems, 1, elems[0], firstPotential);
+
+          if (pos > -1)
+          {
+            return elems[pos];
+          }
+        }
+      }
+    }
+
+    return NOTPROCESSED;
+  }
+
+  /**
+   * Get the next node identity value in the list, and call the iterator
+   * if it hasn't been added yet.
+   *
+   * @param identity The node identity (index).
+   * @return identity+1, or DTM.NULL.
+   */
+  protected abstract int getNextNodeIdentity(int identity);
+
+  /**
+   * This method should try and build one or more nodes in the table.
+   *
+   * @return The true if a next node is found or false if
+   *         there are no more nodes.
+   */
+  protected abstract boolean nextNode();
+
+  /**
+   * Get the number of nodes that have been added.
+   *
+   * @return the number of nodes that have been mapped.
+   */
+  protected abstract int getNumberOfNodes();
+
+  /** Stateless axis traversers, lazely built. */
+  protected DTMAxisTraverser[] m_traversers;
+
+//    /**
+//     * Ensure that the size of the information arrays can hold another entry
+//     * at the given index.
+//     *
+//     * @param index On exit from this function, the information arrays sizes must be
+//     * at least index+1.
+//     */
+//    protected void ensureSize(int index)
+//    {
+//        // We've cut over to Suballocated*Vector, which are self-sizing.
+//    }
+
+  /**
+   * Get the simple type ID for the given node identity.
+   *
+   * @param identity The node identity.
+   *
+   * @return The simple type ID, or DTM.NULL.
+   */
+  protected short _type(int identity)
+  {
+
+    int info = _exptype(identity);
+
+    if (NULL != info)
+      return m_expandedNameTable.getType(info);
+    else
+      return NULL;
+  }
+
+  /**
+   * Get the expanded type ID for the given node identity.
+   *
+   * @param identity The node identity.
+   *
+   * @return The expanded type ID, or DTM.NULL.
+   */
+  protected int _exptype(int identity)
+  {
+  	if (identity == DTM.NULL)
+  	return NULL;
+    // Reorganized test and loop into single flow
+    // Tiny performance improvement, saves a few bytes of code, clearer.
+    // %OPT% Other internal getters could be treated simliarly
+    while (identity>=m_size)
+    {
+      if (!nextNode() && identity >= m_size)
+        return NULL;
+    }
+    return m_exptype.elementAt(identity);
+
+  }
+
+  /**
+   * Get the level in the tree for the given node identity.
+   *
+   * @param identity The node identity.
+   *
+   * @return The tree level, or DTM.NULL.
+   */
+  protected int _level(int identity)
+  {
+    while (identity>=m_size)
+    {
+      boolean isMore = nextNode();
+      if (!isMore && identity >= m_size)
+        return NULL;
+    }
+
+    int i=0;
+    while(NULL != (identity=_parent(identity)))
+      ++i;
+    return i;
+  }
+
+  /**
+   * Get the first child for the given node identity.
+   *
+   * @param identity The node identity.
+   *
+   * @return The first child identity, or DTM.NULL.
+   */
+  protected int _firstch(int identity)
+  {
+
+    // Boiler-plate code for each of the _xxx functions, except for the array.
+    int info = (identity >= m_size) ? NOTPROCESSED : m_firstch.elementAt(identity);
+
+    // Check to see if the information requested has been processed, and,
+    // if not, advance the iterator until we the information has been
+    // processed.
+    while (info == NOTPROCESSED)
+    {
+      boolean isMore = nextNode();
+
+      if (identity >= m_size &&!isMore)
+        return NULL;
+      else
+      {
+        info = m_firstch.elementAt(identity);
+        if(info == NOTPROCESSED && !isMore)
+          return NULL;
+      }
+    }
+
+    return info;
+  }
+
+  /**
+   * Get the next sibling for the given node identity.
+   *
+   * @param identity The node identity.
+   *
+   * @return The next sibling identity, or DTM.NULL.
+   */
+  protected int _nextsib(int identity)
+  {
+    // Boiler-plate code for each of the _xxx functions, except for the array.
+    int info = (identity >= m_size) ? NOTPROCESSED : m_nextsib.elementAt(identity);
+
+    // Check to see if the information requested has been processed, and,
+    // if not, advance the iterator until we the information has been
+    // processed.
+    while (info == NOTPROCESSED)
+    {
+      boolean isMore = nextNode();
+
+      if (identity >= m_size &&!isMore)
+        return NULL;
+      else
+      {
+        info = m_nextsib.elementAt(identity);
+        if(info == NOTPROCESSED && !isMore)
+          return NULL;
+      }
+    }
+
+    return info;
+  }
+
+  /**
+   * Get the previous sibling for the given node identity.
+   *
+   * @param identity The node identity.
+   *
+   * @return The previous sibling identity, or DTM.NULL.
+   */
+  protected int _prevsib(int identity)
+  {
+
+    if (identity < m_size)
+      return m_prevsib.elementAt(identity);
+
+    // Check to see if the information requested has been processed, and,
+    // if not, advance the iterator until we the information has been
+    // processed.
+    while (true)
+    {
+      boolean isMore = nextNode();
+
+      if (identity >= m_size && !isMore)
+        return NULL;
+      else if (identity < m_size)
+        return m_prevsib.elementAt(identity);
+    }
+  }
+
+  /**
+   * Get the parent for the given node identity.
+   *
+   * @param identity The node identity.
+   *
+   * @return The parent identity, or DTM.NULL.
+   */
+  protected int _parent(int identity)
+  {
+
+    if (identity < m_size)
+      return m_parent.elementAt(identity);
+
+    // Check to see if the information requested has been processed, and,
+    // if not, advance the iterator until we the information has been
+    // processed.
+    while (true)
+    {
+      boolean isMore = nextNode();
+
+      if (identity >= m_size && !isMore)
+        return NULL;
+      else if (identity < m_size)
+        return m_parent.elementAt(identity);
+    }
+  }
+
+  /**
+   * Diagnostics function to dump the DTM.
+   */
+  public void dumpDTM(OutputStream os)
+  {
+    try
+    {
+      if(os==null)
+      {
+	      File f = new File("DTMDump"+((Object)this).hashCode()+".txt");
+ 	      System.err.println("Dumping... "+f.getAbsolutePath());
+ 	      os=new FileOutputStream(f);
+      }
+      PrintStream ps = new PrintStream(os);
+
+      while (nextNode()){}
+
+      int nRecords = m_size;
+
+      ps.println("Total nodes: " + nRecords);
+
+      for (int index = 0; index < nRecords; ++index)
+      {
+      	int i=makeNodeHandle(index);
+        ps.println("=========== index=" + index + " handle=" + i + " ===========");
+        ps.println("NodeName: " + getNodeName(i));
+        ps.println("NodeNameX: " + getNodeNameX(i));
+        ps.println("LocalName: " + getLocalName(i));
+        ps.println("NamespaceURI: " + getNamespaceURI(i));
+        ps.println("Prefix: " + getPrefix(i));
+
+        int exTypeID = _exptype(index);
+
+        ps.println("Expanded Type ID: "
+                           + Integer.toHexString(exTypeID));
+
+        int type = _type(index);
+        String typestring;
+
+        switch (type)
+        {
+        case DTM.ATTRIBUTE_NODE :
+          typestring = "ATTRIBUTE_NODE";
+          break;
+        case DTM.CDATA_SECTION_NODE :
+          typestring = "CDATA_SECTION_NODE";
+          break;
+        case DTM.COMMENT_NODE :
+          typestring = "COMMENT_NODE";
+          break;
+        case DTM.DOCUMENT_FRAGMENT_NODE :
+          typestring = "DOCUMENT_FRAGMENT_NODE";
+          break;
+        case DTM.DOCUMENT_NODE :
+          typestring = "DOCUMENT_NODE";
+          break;
+        case DTM.DOCUMENT_TYPE_NODE :
+          typestring = "DOCUMENT_NODE";
+          break;
+        case DTM.ELEMENT_NODE :
+          typestring = "ELEMENT_NODE";
+          break;
+        case DTM.ENTITY_NODE :
+          typestring = "ENTITY_NODE";
+          break;
+        case DTM.ENTITY_REFERENCE_NODE :
+          typestring = "ENTITY_REFERENCE_NODE";
+          break;
+        case DTM.NAMESPACE_NODE :
+          typestring = "NAMESPACE_NODE";
+          break;
+        case DTM.NOTATION_NODE :
+          typestring = "NOTATION_NODE";
+          break;
+        case DTM.NULL :
+          typestring = "NULL";
+          break;
+        case DTM.PROCESSING_INSTRUCTION_NODE :
+          typestring = "PROCESSING_INSTRUCTION_NODE";
+          break;
+        case DTM.TEXT_NODE :
+          typestring = "TEXT_NODE";
+          break;
+        default :
+          typestring = "Unknown!";
+          break;
+        }
+
+        ps.println("Type: " + typestring);
+
+        int firstChild = _firstch(index);
+
+        if (DTM.NULL == firstChild)
+          ps.println("First child: DTM.NULL");
+        else if (NOTPROCESSED == firstChild)
+          ps.println("First child: NOTPROCESSED");
+        else
+          ps.println("First child: " + firstChild);
+
+        if (m_prevsib != null)
+        {
+          int prevSibling = _prevsib(index);
+
+          if (DTM.NULL == prevSibling)
+            ps.println("Prev sibling: DTM.NULL");
+          else if (NOTPROCESSED == prevSibling)
+            ps.println("Prev sibling: NOTPROCESSED");
+          else
+            ps.println("Prev sibling: " + prevSibling);
+        }
+
+        int nextSibling = _nextsib(index);
+
+        if (DTM.NULL == nextSibling)
+          ps.println("Next sibling: DTM.NULL");
+        else if (NOTPROCESSED == nextSibling)
+          ps.println("Next sibling: NOTPROCESSED");
+        else
+          ps.println("Next sibling: " + nextSibling);
+
+        int parent = _parent(index);
+
+        if (DTM.NULL == parent)
+          ps.println("Parent: DTM.NULL");
+        else if (NOTPROCESSED == parent)
+          ps.println("Parent: NOTPROCESSED");
+        else
+          ps.println("Parent: " + parent);
+
+        int level = _level(index);
+
+        ps.println("Level: " + level);
+        ps.println("Node Value: " + getNodeValue(i));
+        ps.println("String Value: " + getStringValue(i));
+      }
+    }
+    catch(IOException ioe)
+    {
+      ioe.printStackTrace(System.err);
+        throw new RuntimeException(ioe.getMessage());
+    }
+  }
+  
+  /**
+   * Diagnostics function to dump a single node.
+   * 
+   * %REVIEW% KNOWN GLITCH: If you pass it a node index rather than a 
+   * node handle, it works just fine... but the displayed identity 
+   * number before the colon is different, which complicates comparing
+   * it with nodes printed the other way. We could always OR the DTM ID
+   * into the value, to suppress that distinction...
+   * 
+   * %REVIEW% This might want to be moved up to DTMDefaultBase, or possibly
+   * DTM itself, since it's a useful diagnostic and uses only DTM's public
+   * APIs.
+   */
+  public String dumpNode(int nodeHandle)
+  {	  
+	  if(nodeHandle==DTM.NULL)
+		  return "[null]";
+		  
+        String typestring;
+        switch (getNodeType(nodeHandle))
+        {
+        case DTM.ATTRIBUTE_NODE :
+          typestring = "ATTR";
+          break;
+        case DTM.CDATA_SECTION_NODE :
+          typestring = "CDATA";
+          break;
+        case DTM.COMMENT_NODE :
+          typestring = "COMMENT";
+          break;
+        case DTM.DOCUMENT_FRAGMENT_NODE :
+          typestring = "DOC_FRAG";
+          break;
+        case DTM.DOCUMENT_NODE :
+          typestring = "DOC";
+          break;
+        case DTM.DOCUMENT_TYPE_NODE :
+          typestring = "DOC_TYPE";
+          break;
+        case DTM.ELEMENT_NODE :
+          typestring = "ELEMENT";
+          break;
+        case DTM.ENTITY_NODE :
+          typestring = "ENTITY";
+          break;
+        case DTM.ENTITY_REFERENCE_NODE :
+          typestring = "ENT_REF";
+          break;
+        case DTM.NAMESPACE_NODE :
+          typestring = "NAMESPACE";
+          break;
+        case DTM.NOTATION_NODE :
+          typestring = "NOTATION";
+          break;
+        case DTM.NULL :
+          typestring = "null";
+          break;
+        case DTM.PROCESSING_INSTRUCTION_NODE :
+          typestring = "PI";
+          break;
+        case DTM.TEXT_NODE :
+          typestring = "TEXT";
+          break;
+        default :
+          typestring = "Unknown!";
+          break;
+        }
+
+      StringBuffer sb=new StringBuffer();
+	  sb.append("["+nodeHandle+": "+typestring+
+				"(0x"+Integer.toHexString(getExpandedTypeID(nodeHandle))+") "+
+				getNodeNameX(nodeHandle)+" {"+getNamespaceURI(nodeHandle)+"}"+
+				"=\""+ getNodeValue(nodeHandle)+"\"]");
+	  return sb.toString();
+  }
+
+  // ========= DTM Implementation Control Functions. ==============
+
+  /**
+   * Set an implementation dependent feature.
+   * <p>
+   * %REVIEW% Do we really expect to set features on DTMs?
+   *
+   * @param featureId A feature URL.
+   * @param state true if this feature should be on, false otherwise.
+   */
+  public void setFeature(String featureId, boolean state){}
+
+  // ========= Document Navigation Functions =========
+
+  /**
+   * Given a node handle, test if it has child nodes.
+   * <p> %REVIEW% This is obviously useful at the DOM layer, where it
+   * would permit testing this without having to create a proxy
+   * node. It's less useful in the DTM API, where
+   * (dtm.getFirstChild(nodeHandle)!=DTM.NULL) is just as fast and
+   * almost as self-evident. But it's a convenience, and eases porting
+   * of DOM code to DTM.  </p>
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int true if the given node has child nodes.
+   */
+  public boolean hasChildNodes(int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+    int firstChild = _firstch(identity);
+
+    return firstChild != DTM.NULL;
+  }
+	
+  /** Given a node identity, return a node handle. If extended addressing
+   * has been used (multiple DTM IDs), we need to map the high bits of the
+   * identity into the proper DTM ID.
+   * 
+   * This has been made FINAL to facilitate inlining, since we do not expect
+   * any subclass of DTMDefaultBase to ever change the algorithm. (I don't
+   * really like doing so, and would love to have an excuse not to...)
+   * 
+   * %REVIEW% Is it worth trying to specialcase small documents?
+   * %REVIEW% Should this be exposed at the package/public layers?
+   * 
+   * @param nodeIdentity Internal offset to this node's records.
+   * @return NodeHandle (external representation of node)
+   * */
+  final public int makeNodeHandle(int nodeIdentity)
+  {
+    if(NULL==nodeIdentity) return NULL;
+		
+    if(JJK_DEBUG && nodeIdentity>DTMManager.IDENT_NODE_DEFAULT)
+      System.err.println("GONK! (only useful in limited situations)");
+
+    return m_dtmIdent.elementAt(nodeIdentity >>> DTMManager.IDENT_DTM_NODE_BITS)
+      + (nodeIdentity & DTMManager.IDENT_NODE_DEFAULT) ;											
+  }
+	
+  /** Given a node handle, return a node identity. If extended addressing
+   * has been used (multiple DTM IDs), we need to map the high bits of the
+   * identity into the proper DTM ID and thence find the proper offset
+   * to add to the low bits of the identity
+   * 
+   * This has been made FINAL to facilitate inlining, since we do not expect
+   * any subclass of DTMDefaultBase to ever change the algorithm. (I don't
+   * really like doing so, and would love to have an excuse not to...)
+   * 
+   * %OPT% Performance is critical for this operation.
+   *
+   * %REVIEW% Should this be exposed at the package/public layers?
+   * 
+   * @param nodeHandle (external representation of node)
+   * @return nodeIdentity Internal offset to this node's records.
+   * */
+  final public int makeNodeIdentity(int nodeHandle)
+  {
+    if(NULL==nodeHandle) return NULL;
+
+    if(m_mgrDefault!=null)
+    {
+      // Optimization: use the DTMManagerDefault's fast DTMID-to-offsets
+      // table.  I'm not wild about this solution but this operation
+      // needs need extreme speed.
+
+      int whichDTMindex=nodeHandle>>>DTMManager.IDENT_DTM_NODE_BITS;
+
+      // %REVIEW% Wish I didn't have to perform the pre-test, but
+      // someone is apparently asking DTMs whether they contain nodes
+      // which really don't belong to them. That's probably a bug
+      // which should be fixed, but until it is:
+      if(m_mgrDefault.m_dtms[whichDTMindex]!=this)
+	return NULL;
+      else
+	return
+	  m_mgrDefault.m_dtm_offsets[whichDTMindex]
+	  | (nodeHandle & DTMManager.IDENT_NODE_DEFAULT);
+    }
+	  
+    int whichDTMid=m_dtmIdent.indexOf(nodeHandle & DTMManager.IDENT_DTM_DEFAULT);
+    return (whichDTMid==NULL) 
+      ? NULL
+      : (whichDTMid << DTMManager.IDENT_DTM_NODE_BITS)
+      + (nodeHandle & DTMManager.IDENT_NODE_DEFAULT);
+  }
+
+
+  /**
+   * Given a node handle, get the handle of the node's first child.
+   * If not yet resolved, waits for more nodes to be added to the document and
+   * tries again.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int DTM node-number of first child, or DTM.NULL to indicate none exists.
+   */
+  public int getFirstChild(int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+    int firstChild = _firstch(identity);
+
+    return makeNodeHandle(firstChild);
+  }
+  
+  /**
+   * Given a node handle, get the handle of the node's first child.
+   * If not yet resolved, waits for more nodes to be added to the document and
+   * tries again.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int DTM node-number of first child, or DTM.NULL to indicate none exists.
+   */
+  public int getTypedFirstChild(int nodeHandle, int nodeType)
+  {
+
+    int firstChild, eType;
+    if (nodeType < DTM.NTYPES) {
+      for (firstChild = _firstch(makeNodeIdentity(nodeHandle));
+           firstChild != DTM.NULL;
+           firstChild = _nextsib(firstChild)) {
+        eType = _exptype(firstChild);
+        if (eType == nodeType
+               || (eType >= DTM.NTYPES
+                      && m_expandedNameTable.getType(eType) == nodeType)) {
+          return makeNodeHandle(firstChild);
+        }
+      }
+    } else {
+      for (firstChild = _firstch(makeNodeIdentity(nodeHandle));
+           firstChild != DTM.NULL;
+           firstChild = _nextsib(firstChild)) {
+        if (_exptype(firstChild) == nodeType) {
+          return makeNodeHandle(firstChild);
+        }
+      }
+    }
+    return DTM.NULL;
+  }
+
+  /**
+   * Given a node handle, advance to its last child.
+   * If not yet resolved, waits for more nodes to be added to the document and
+   * tries again.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int Node-number of last child,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getLastChild(int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+    int child = _firstch(identity);
+    int lastChild = DTM.NULL;
+
+    while (child != DTM.NULL)
+    {
+      lastChild = child;
+      child = _nextsib(child);
+    }
+
+    return makeNodeHandle(lastChild);
+  }
+
+  /**
+   * Retrieves an attribute node by by qualified name and namespace URI.
+   *
+   * @param nodeHandle int Handle of the node upon which to look up this attribute..
+   * @param namespaceURI The namespace URI of the attribute to
+   *   retrieve, or null.
+   * @param name The local name of the attribute to
+   *   retrieve.
+   * @return The attribute node handle with the specified name (
+   *   <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
+   *   attribute.
+   */
+  public abstract int getAttributeNode(int nodeHandle, String namespaceURI,
+                                       String name);
+
+  /**
+   * Given a node handle, get the index of the node's first attribute.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return Handle of first attribute, or DTM.NULL to indicate none exists.
+   */
+  public int getFirstAttribute(int nodeHandle)
+  {
+    int nodeID = makeNodeIdentity(nodeHandle);
+
+    return makeNodeHandle(getFirstAttributeIdentity(nodeID));
+  }
+
+  /**
+   * Given a node identity, get the index of the node's first attribute.
+   *
+   * @param identity int identity of the node.
+   * @return Identity of first attribute, or DTM.NULL to indicate none exists.
+   */
+  protected int getFirstAttributeIdentity(int identity) {
+    int type = _type(identity);
+
+    if (DTM.ELEMENT_NODE == type)
+    {
+      // Assume that attributes and namespaces immediately follow the element.
+      while (DTM.NULL != (identity = getNextNodeIdentity(identity)))
+      {
+
+        // Assume this can not be null.
+        type = _type(identity);
+
+        if (type == DTM.ATTRIBUTE_NODE)
+        {
+          return identity;
+        }
+        else if (DTM.NAMESPACE_NODE != type)
+        {
+          break;
+        }
+      }
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * Given a node handle and an expanded type ID, get the index of the node's
+   * attribute of that type, if any.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @param attType int expanded type ID of the required attribute.
+   * @return Handle of attribute of the required type, or DTM.NULL to indicate
+   * none exists.
+   */
+  protected int getTypedAttribute(int nodeHandle, int attType) {
+    int type = getNodeType(nodeHandle);
+    if (DTM.ELEMENT_NODE == type) {
+      int identity = makeNodeIdentity(nodeHandle);
+
+      while (DTM.NULL != (identity = getNextNodeIdentity(identity)))
+      {
+        type = _type(identity);
+
+        if (type == DTM.ATTRIBUTE_NODE)
+        {
+          if (_exptype(identity) == attType) return makeNodeHandle(identity);
+        }
+        else if (DTM.NAMESPACE_NODE != type)
+        {
+          break;
+        }
+      }
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * Given a node handle, advance to its next sibling.
+   * If not yet resolved, waits for more nodes to be added to the document and
+   * tries again.
+   * @param nodeHandle int Handle of the node.
+   * @return int Node-number of next sibling,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getNextSibling(int nodeHandle)
+  {
+  	if (nodeHandle == DTM.NULL)
+  	return DTM.NULL;
+    return makeNodeHandle(_nextsib(makeNodeIdentity(nodeHandle)));
+  }
+  
+  /**
+   * Given a node handle, advance to its next sibling.
+   * If not yet resolved, waits for more nodes to be added to the document and
+   * tries again.
+   * @param nodeHandle int Handle of the node.
+   * @return int Node-number of next sibling,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getTypedNextSibling(int nodeHandle, int nodeType)
+  {
+  	if (nodeHandle == DTM.NULL)
+  	return DTM.NULL;
+  	int node = makeNodeIdentity(nodeHandle);
+  	int eType;
+  	while ((node = _nextsib(node)) != DTM.NULL && 
+  	((eType = _exptype(node)) != nodeType && 
+  	m_expandedNameTable.getType(eType)!= nodeType)); 
+  	//_type(node) != nodeType));
+        
+    return (node == DTM.NULL ? DTM.NULL : makeNodeHandle(node));
+  }
+
+  /**
+   * Given a node handle, find its preceeding sibling.
+   * WARNING: DTM is asymmetric; this operation is resolved by search, and is
+   * relatively expensive.
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node-number of the previous sib,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getPreviousSibling(int nodeHandle)
+  {
+    if (nodeHandle == DTM.NULL)
+      return DTM.NULL;
+    
+    if (m_prevsib != null)
+      return makeNodeHandle(_prevsib(makeNodeIdentity(nodeHandle)));
+    else
+    {
+      // If the previous sibling array is not built, we get at
+      // the previous sibling using the parent, firstch and 
+      // nextsib arrays. 
+      int nodeID = makeNodeIdentity(nodeHandle);
+      int parent = _parent(nodeID);
+      int node = _firstch(parent);
+      int result = DTM.NULL;
+      while (node != nodeID)
+      {
+        result = node;
+        node = _nextsib(node);
+      }
+      return makeNodeHandle(result);
+    }
+  }
+
+  /**
+   * Given a node handle, advance to the next attribute.
+   * If an attr, we advance to
+   * the next attr on the same node.  If not an attribute, we return NULL.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return int DTM node-number of the resolved attr,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getNextAttribute(int nodeHandle) {
+    int nodeID = makeNodeIdentity(nodeHandle);
+
+    if (_type(nodeID) == DTM.ATTRIBUTE_NODE) {
+      return makeNodeHandle(getNextAttributeIdentity(nodeID));
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * Given a node identity for an attribute, advance to the next attribute.
+   *
+   * @param identity int identity of the attribute node.  This
+   * <strong>must</strong> be an attribute node.
+   *
+   * @return int DTM node-identity of the resolved attr,
+   * or DTM.NULL to indicate none exists.
+   *
+   */
+  protected int getNextAttributeIdentity(int identity) {
+    // Assume that attributes and namespace nodes immediately follow the element
+    while (DTM.NULL != (identity = getNextNodeIdentity(identity))) {
+      int type = _type(identity);
+
+      if (type == DTM.ATTRIBUTE_NODE) {
+        return identity;
+      } else if (type != DTM.NAMESPACE_NODE) {
+        break;
+      }
+    }
+
+    return DTM.NULL;
+  }
+
+  /** Lazily created namespace lists. */
+  private Vector m_namespaceLists = null;  // on demand
+
+
+  /** Build table of namespace declaration
+   * locations during DTM construction. Table is a Vector of
+   * SuballocatedIntVectors containing the namespace node HANDLES declared at
+   * that ID, plus an SuballocatedIntVector of the element node INDEXES at which
+   * these declarations appeared.
+   *
+   * NOTE: Since this occurs during model build, nodes will be encountered
+   * in doucment order and thus the table will be ordered by element,
+   * permitting binary-search as a possible retrieval optimization.
+   *
+   * %REVIEW% Directly managed arrays rather than vectors?
+   * %REVIEW% Handles or IDs? Given usage, I think handles.
+   * */
+  protected void declareNamespaceInContext(int elementNodeIndex,int namespaceNodeIndex)
+  {
+    SuballocatedIntVector nsList=null;
+    if(m_namespaceDeclSets==null)
+      {
+
+        // First
+        m_namespaceDeclSetElements=new SuballocatedIntVector(32);
+        m_namespaceDeclSetElements.addElement(elementNodeIndex);
+        m_namespaceDeclSets=new Vector();
+        nsList=new SuballocatedIntVector(32);
+        m_namespaceDeclSets.addElement(nsList);
+      }
+    else
+      {
+        // Most recent. May be -1 (none) if DTM was pruned.
+        // %OPT% Is there a lastElement() method? Should there be?
+        int last=m_namespaceDeclSetElements.size()-1;
+	        
+        if(last>=0 && elementNodeIndex==m_namespaceDeclSetElements.elementAt(last))
+          {
+            nsList=(SuballocatedIntVector)m_namespaceDeclSets.elementAt(last);
+          }
+      }
+    if(nsList==null)
+      {
+        m_namespaceDeclSetElements.addElement(elementNodeIndex);
+
+        SuballocatedIntVector inherited =
+                                findNamespaceContext(_parent(elementNodeIndex));
+
+        if (inherited!=null) {
+            // %OPT% Count-down might be faster, but debuggability may
+            // be better this way, and if we ever decide we want to
+            // keep this ordered by expanded-type...
+            int isize=inherited.size();
+
+            // Base the size of a new namespace list on the
+            // size of the inherited list - but within reason!
+            nsList=new SuballocatedIntVector(Math.max(Math.min(isize+16,2048),
+                                                      32));
+
+            for(int i=0;i<isize;++i)
+              {
+                nsList.addElement(inherited.elementAt(i));
+              }
+        } else {
+            nsList=new SuballocatedIntVector(32);
+        }
+
+        m_namespaceDeclSets.addElement(nsList);
+      }
+
+    // Handle overwriting inherited.
+    // %OPT% Keep sorted? (By expanded-name rather than by doc order...)
+    // Downside: Would require insertElementAt if not found,
+    // which has recopying costs. But these are generally short lists...
+    int newEType=_exptype(namespaceNodeIndex);
+
+    for(int i=nsList.size()-1;i>=0;--i)
+      {
+        if(newEType==getExpandedTypeID(nsList.elementAt(i)))
+          {
+            nsList.setElementAt(makeNodeHandle(namespaceNodeIndex),i);
+            return;
+          }
+      }
+    nsList.addElement(makeNodeHandle(namespaceNodeIndex));
+  }
+
+  /** Retrieve list of namespace declaration locations
+     * active at this node. List is an SuballocatedIntVector whose
+     * entries are the namespace node HANDLES declared at that ID.
+     *
+     * %REVIEW% Directly managed arrays rather than vectors?
+     * %REVIEW% Handles or IDs? Given usage, I think handles.
+     * */
+  protected SuballocatedIntVector findNamespaceContext(int elementNodeIndex)
+  {
+    if (null!=m_namespaceDeclSetElements)
+      {
+        // %OPT% Is binary-search really saving us a lot versus linear?
+        // (... It may be, in large docs with many NS decls.)
+        int wouldBeAt=findInSortedSuballocatedIntVector(m_namespaceDeclSetElements,
+                                            elementNodeIndex);
+        if(wouldBeAt>=0) // Found it
+          return (SuballocatedIntVector) m_namespaceDeclSets.elementAt(wouldBeAt);
+        if(wouldBeAt == -1) // -1-wouldbeat == 0
+          return null; // Not after anything; definitely not found
+
+        // Not found, but we know where it should have been.
+        // Search back until we find an ancestor or run out.
+        wouldBeAt=-1-wouldBeAt;
+
+        // Decrement wouldBeAt to find last possible ancestor
+        int candidate=m_namespaceDeclSetElements.elementAt(-- wouldBeAt);
+        int ancestor=_parent(elementNodeIndex);
+
+        // Special case: if the candidate is before the given node, and
+        // is in the earliest possible position in the document, it
+        // must have the namespace declarations we're interested in.
+        if (wouldBeAt == 0 && candidate < ancestor) {
+          int rootHandle = getDocumentRoot(makeNodeHandle(elementNodeIndex));
+          int rootID = makeNodeIdentity(rootHandle);
+          int uppermostNSCandidateID;
+
+          if (getNodeType(rootHandle) == DTM.DOCUMENT_NODE) {
+            int ch = _firstch(rootID);
+            uppermostNSCandidateID = (ch != DTM.NULL) ? ch : rootID;
+          } else {
+            uppermostNSCandidateID = rootID;
+          }
+
+          if (candidate == uppermostNSCandidateID) {
+            return (SuballocatedIntVector)m_namespaceDeclSets.elementAt(wouldBeAt);
+          }
+        }
+
+        while(wouldBeAt>=0 && ancestor>0)
+          {
+            if (candidate==ancestor) {
+                // Found ancestor in list
+                return (SuballocatedIntVector)m_namespaceDeclSets.elementAt(wouldBeAt);
+            } else if (candidate<ancestor) {
+                // Too deep in tree
+                do {
+                  ancestor=_parent(ancestor);
+                } while (candidate < ancestor);
+            } else if(wouldBeAt > 0){
+              // Too late in list
+              candidate=m_namespaceDeclSetElements.elementAt(--wouldBeAt);
+            }
+            else
+            	break;
+          }
+      }
+
+    return null; // No namespaces known at this node
+  }
+
+  /**
+     * Subroutine: Locate the specified node within
+     * m_namespaceDeclSetElements, or the last element which
+     * preceeds it in document order
+     *
+     * %REVIEW% Inlne this into findNamespaceContext? Create SortedSuballocatedIntVector type?
+     *
+     * @return If positive or zero, the index of the found item.
+     * If negative, index of the point at which it would have appeared,
+     * encoded as -1-index and hence reconvertable by subtracting
+     * it from -1. (Encoding because I don't want to recompare the strings
+     * but don't want to burn bytes on a datatype to hold a flagged value.)
+     */
+  protected int findInSortedSuballocatedIntVector(SuballocatedIntVector vector, int lookfor)
+  {
+    // Binary search
+    int i = 0;
+    if(vector != null) {
+      int first = 0;
+      int last  = vector.size() - 1;
+
+      while (first <= last) {
+        i = (first + last) / 2;
+        int test = lookfor-vector.elementAt(i);
+        if(test == 0) {
+          return i; // Name found
+        }
+        else if (test < 0) {
+          last = i - 1; // looked too late
+        }
+        else {
+          first = i + 1; // looked ot early
+        }
+      }
+
+      if (first > i) {
+        i = first; // Clean up at loop end
+      }
+    }
+
+    return -1 - i; // not-found has to be encoded.
+  }
+
+
+  /**
+   * Given a node handle, get the index of the node's first child.
+   * If not yet resolved, waits for more nodes to be added to the document and
+   * tries again
+   *
+   * @param nodeHandle handle to node, which should probably be an element
+   *                   node, but need not be.
+   *
+   * @param inScope    true if all namespaces in scope should be returned,
+   *                   false if only the namespace declarations should be
+   *                   returned.
+   * @return handle of first namespace, or DTM.NULL to indicate none exists.
+   */
+  public int getFirstNamespaceNode(int nodeHandle, boolean inScope)
+  {
+        if(inScope)
+        {
+            int identity = makeNodeIdentity(nodeHandle);
+            if (_type(identity) == DTM.ELEMENT_NODE)
+            {
+              SuballocatedIntVector nsContext=findNamespaceContext(identity);
+              if(nsContext==null || nsContext.size()<1)
+                return NULL;
+
+              return nsContext.elementAt(0);
+            }
+            else
+              return NULL;
+          }
+        else
+          {
+            // Assume that attributes and namespaces immediately
+            // follow the element.
+            //
+            // %OPT% Would things be faster if all NS nodes were built
+            // before all Attr nodes? Some costs at build time for 2nd
+            // pass...
+            int identity = makeNodeIdentity(nodeHandle);
+            if (_type(identity) == DTM.ELEMENT_NODE)
+            {
+              while (DTM.NULL != (identity = getNextNodeIdentity(identity)))
+              {
+                int type = _type(identity);
+                if (type == DTM.NAMESPACE_NODE)
+                    return makeNodeHandle(identity);
+                else if (DTM.ATTRIBUTE_NODE != type)
+                    break;
+              }
+              return NULL;
+            }
+            else
+              return NULL;
+          }
+  }
+
+  /**
+   * Given a namespace handle, advance to the next namespace.
+   *
+   * @param baseHandle handle to original node from where the first namespace
+   * was relative to (needed to return nodes in document order).
+   * @param nodeHandle A namespace handle for which we will find the next node.
+   * @param inScope true if all namespaces that are in scope should be processed,
+   * otherwise just process the nodes in the given element handle.
+   * @return handle of next namespace, or DTM.NULL to indicate none exists.
+   */
+  public int getNextNamespaceNode(int baseHandle, int nodeHandle,
+                                  boolean inScope)
+  {
+        if(inScope)
+          {
+            //Since we've been given the base, try direct lookup
+            //(could look from nodeHandle but this is at least one
+            //comparison/get-parent faster)
+            //SuballocatedIntVector nsContext=findNamespaceContext(nodeHandle & m_mask);
+
+                SuballocatedIntVector nsContext=findNamespaceContext(makeNodeIdentity(baseHandle));
+
+            if(nsContext==null)
+              return NULL;
+            int i=1 + nsContext.indexOf(nodeHandle);
+            if(i<=0 || i==nsContext.size())
+              return NULL;
+
+            return nsContext.elementAt(i);
+          }
+        else
+          {
+            // Assume that attributes and namespace nodes immediately follow the element.
+            int identity = makeNodeIdentity(nodeHandle);
+            while (DTM.NULL != (identity = getNextNodeIdentity(identity)))
+              {
+                int type = _type(identity);
+                if (type == DTM.NAMESPACE_NODE)
+                  {
+                    return makeNodeHandle(identity);
+                  }
+                else if (type != DTM.ATTRIBUTE_NODE)
+                  {
+                    break;
+                  }
+              }
+          }
+     return DTM.NULL;
+  }
+
+  /**
+   * Given a node handle, find its parent node.
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node-number of parent,
+   * or DTM.NULL to indicate none exists.
+   */
+  public int getParent(int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+
+    if (identity > 0)
+      return makeNodeHandle(_parent(identity));
+    else
+      return DTM.NULL;
+  }
+
+  /**
+   * Find the Document node handle for the document currently under construction.
+   * PLEASE NOTE that most people should use getOwnerDocument(nodeHandle) instead;
+   * this version of the operation is primarily intended for use during negotiation
+   * with the DTM Manager.
+   * 
+   *  @return int Node handle of document, which should always be valid.
+   */
+  public int getDocument()
+  {
+    return m_dtmIdent.elementAt(0); // makeNodeHandle(0)
+  }
+
+  /**
+   * Given a node handle, find the owning document node.  This has the exact
+   * same semantics as the DOM Document method of the same name, in that if
+   * the nodeHandle is a document node, it will return NULL.
+   *
+   * <p>%REVIEW% Since this is DOM-specific, it may belong at the DOM
+   * binding layer. Included here as a convenience function and to
+   * aid porting of DOM code to DTM.</p>
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node handle of owning document, or -1 if the node was a Docment
+   */
+  public int getOwnerDocument(int nodeHandle)
+  {
+
+    if (DTM.DOCUMENT_NODE == getNodeType(nodeHandle))
+  	    return DTM.NULL;
+
+    return getDocumentRoot(nodeHandle);
+  }
+
+  /**
+   * Given a node handle, find the owning document node.  Unlike the DOM,
+   * this considers the owningDocument of a Document to be itself.
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node handle of owning document, or the nodeHandle if it is
+   *             a Document.
+   */
+  public int getDocumentRoot(int nodeHandle)
+  {
+    return getManager().getDTM(nodeHandle).getDocument();
+  }
+
+  /**
+   * Get the string-value of a node as a String object
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A string object that represents the string-value of the given node.
+   */
+  public abstract XMLString getStringValue(int nodeHandle);
+
+  /**
+   * Get number of character array chunks in
+   * the string-value of a node.
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   * Note that a single text node may have multiple text chunks.
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return number of character array chunks in
+   *         the string-value of a node.
+   */
+  public int getStringValueChunkCount(int nodeHandle)
+  {
+
+    // %TBD%
+    error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//("getStringValueChunkCount not yet supported!");
+
+    return 0;
+  }
+
+  /**
+   * Get a character array chunk in the string-value of a node.
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   * Note that a single text node may have multiple text chunks.
+   *
+   * @param nodeHandle The node ID.
+   * @param chunkIndex Which chunk to get.
+   * @param startAndLen An array of 2 where the start position and length of
+   *                    the chunk will be returned.
+   *
+   * @return The character array reference where the chunk occurs.
+   */
+  public char[] getStringValueChunk(int nodeHandle, int chunkIndex,
+                                    int[] startAndLen)
+  {
+
+    // %TBD%
+    error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"getStringValueChunk not yet supported!");
+
+    return null;
+  }
+
+  /**
+   * Given a node handle, return an ID that represents the node's expanded name.
+   *
+   * @param nodeHandle The handle to the node in question.
+   *
+   * @return the expanded-name id of the node.
+   */
+  public int getExpandedTypeID(int nodeHandle)
+  {
+    // %REVIEW% This _should_ only be null if someone asked the wrong DTM about the node...
+    // which one would hope would never happen...
+    int id=makeNodeIdentity(nodeHandle);
+    if(id==NULL)
+      return NULL;
+    return _exptype(id);
+  }
+
+  /**
+   * Given an expanded name, return an ID.  If the expanded-name does not
+   * exist in the internal tables, the entry will be created, and the ID will
+   * be returned.  Any additional nodes that are created that have this
+   * expanded name will use this ID.
+   *
+   * @param type The simple type, i.e. one of ELEMENT, ATTRIBUTE, etc.
+   *
+   * @param namespace The namespace URI, which may be null, may be an empty
+   *                  string (which will be the same as null), or may be a
+   *                  namespace URI.
+   * @param localName The local name string, which must be a valid
+   *                  <a href="http://www.w3.org/TR/REC-xml-names/">NCName</a>.
+   *
+   * @return the expanded-name id of the node.
+   */
+  public int getExpandedTypeID(String namespace, String localName, int type)
+  {
+
+    ExpandedNameTable ent = m_expandedNameTable;
+
+    return ent.getExpandedTypeID(namespace, localName, type);
+  }
+
+  /**
+   * Given an expanded-name ID, return the local name part.
+   *
+   * @param expandedNameID an ID that represents an expanded-name.
+   * @return String Local name of this node.
+   */
+  public String getLocalNameFromExpandedNameID(int expandedNameID)
+  {
+    return m_expandedNameTable.getLocalName(expandedNameID);
+  }
+
+  /**
+   * Given an expanded-name ID, return the namespace URI part.
+   *
+   * @param expandedNameID an ID that represents an expanded-name.
+   * @return String URI value of this node's namespace, or null if no
+   * namespace was resolved.
+   */
+  public String getNamespaceFromExpandedNameID(int expandedNameID)
+  {
+    return m_expandedNameTable.getNamespace(expandedNameID);
+  }
+
+  /**
+   * Returns the namespace type of a specific node
+   * @param nodeHandle the id of the node.
+   * @return the ID of the namespace.
+   */
+  public int getNamespaceType(final int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+    int expandedNameID = _exptype(identity);
+
+    return m_expandedNameTable.getNamespaceID(expandedNameID);
+  }
+
+  /**
+   * Given a node handle, return its DOM-style node name. This will
+   * include names such as #text or #document.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   * %REVIEW% Document when empty string is possible...
+   * %REVIEW-COMMENT% It should never be empty, should it?
+   */
+  public abstract String getNodeName(int nodeHandle);
+
+  /**
+   * Given a node handle, return the XPath node name.  This should be
+   * the name as described by the XPath data model, NOT the DOM-style
+   * name.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   */
+  public String getNodeNameX(int nodeHandle)
+  {
+
+    /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */
+    error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!");
+
+    return null;
+  }
+
+  /**
+   * Given a node handle, return its XPath-style localname.
+   * (As defined in Namespaces, this is the portion of the name after any
+   * colon character).
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Local name of this node.
+   */
+  public abstract String getLocalName(int nodeHandle);
+
+  /**
+   * Given a namespace handle, return the prefix that the namespace decl is
+   * mapping.
+   * Given a node handle, return the prefix used to map to the namespace.
+   *
+   * <p> %REVIEW% Are you sure you want "" for no prefix?  </p>
+   * <p> %REVIEW-COMMENT% I think so... not totally sure. -sb  </p>
+   *
+   * @param nodeHandle the id of the node.
+   * @return String prefix of this node's name, or "" if no explicit
+   * namespace prefix was given.
+   */
+  public abstract String getPrefix(int nodeHandle);
+
+  /**
+   * Given a node handle, return its DOM-style namespace URI
+   * (As defined in Namespaces, this is the declared URI which this node's
+   * prefix -- or default in lieu thereof -- was mapped to.)
+   *
+   * <p>%REVIEW% Null or ""? -sb</p>
+   *
+   * @param nodeHandle the id of the node.
+   * @return String URI value of this node's namespace, or null if no
+   * namespace was resolved.
+   */
+  public abstract String getNamespaceURI(int nodeHandle);
+
+  /**
+   * Given a node handle, return its node value. This is mostly
+   * as defined by the DOM, but may ignore some conveniences.
+   * <p>
+   *
+   * @param nodeHandle The node id.
+   * @return String Value of this node, or null if not
+   * meaningful for this node type.
+   */
+  public abstract String getNodeValue(int nodeHandle);
+
+  /**
+   * Given a node handle, return its DOM-style node type.
+   * <p>
+   * %REVIEW% Generally, returning short is false economy. Return int?
+   * %REVIEW% Make assumption that node has already arrived.  Is OK?
+   *
+   * @param nodeHandle The node id.
+   * @return int Node type, as per the DOM's Node._NODE constants.
+   */
+  public short getNodeType(int nodeHandle)
+  {
+  	if (nodeHandle == DTM.NULL)
+  	return DTM.NULL;
+    return m_expandedNameTable.getType(_exptype(makeNodeIdentity(nodeHandle)));
+  }
+
+  /**
+   * Get the depth level of this node in the tree (equals 1 for
+   * a parentless node).
+   *
+   * @param nodeHandle The node id.
+   * @return the number of ancestors, plus one
+   * @xsl.usage internal
+   */
+  public short getLevel(int nodeHandle)
+  {
+    // Apparently, the axis walker stuff requires levels to count from 1.
+    int identity = makeNodeIdentity(nodeHandle);
+    return (short) (_level(identity) + 1);
+  }
+  
+  /**
+   * Get the identity of this node in the tree 
+   *
+   * @param nodeHandle The node handle.
+   * @return the node identity
+   * @xsl.usage internal
+   */
+  public int getNodeIdent(int nodeHandle)
+  {
+    /*if (nodeHandle != DTM.NULL)
+      return nodeHandle & m_mask;
+    else 
+      return DTM.NULL;*/
+      
+      return makeNodeIdentity(nodeHandle); 
+  }
+  
+  /**
+   * Get the handle of this node in the tree 
+   *
+   * @param nodeId The node identity.
+   * @return the node handle
+   * @xsl.usage internal
+   */
+  public int getNodeHandle(int nodeId)
+  {
+    /*if (nodeId != DTM.NULL)
+      return nodeId | m_dtmIdent;
+    else 
+      return DTM.NULL;*/
+      
+      return makeNodeHandle(nodeId);
+  }
+
+  // ============== Document query functions ==============
+
+  /**
+   * Tests whether DTM DOM implementation implements a specific feature and
+   * that feature is supported by this node.
+   *
+   * @param feature The name of the feature to test.
+   * @param version This is the version number of the feature to test.
+   *   If the version is not
+   *   specified, supporting any version of the feature will cause the
+   *   method to return <code>true</code>.
+   * @return Returns <code>true</code> if the specified feature is
+   *   supported on this node, <code>false</code> otherwise.
+   */
+  public boolean isSupported(String feature, String version)
+  {
+
+    // %TBD%
+    return false;
+  }
+
+  /**
+   * Return the base URI of the document entity. If it is not known
+   * (because the document was parsed from a socket connection or from
+   * standard input, for example), the value of this property is unknown.
+   *
+   * @return the document base URI String object or null if unknown.
+   */
+  public String getDocumentBaseURI()
+  {
+    return m_documentBaseURI;
+  }
+
+  /**
+   * Set the base URI of the document entity.
+   *
+   * @param baseURI the document base URI String object or null if unknown.
+   */
+  public void setDocumentBaseURI(String baseURI)
+  {
+    m_documentBaseURI = baseURI;
+  }
+
+  /**
+   * Return the system identifier of the document entity. If
+   * it is not known, the value of this property is unknown.
+   *
+   * @param nodeHandle The node id, which can be any valid node handle.
+   * @return the system identifier String object or null if unknown.
+   */
+  public String getDocumentSystemIdentifier(int nodeHandle)
+  {
+
+    // %REVIEW%  OK? -sb
+    return m_documentBaseURI;
+  }
+
+  /**
+   * Return the name of the character encoding scheme
+   *        in which the document entity is expressed.
+   *
+   * @param nodeHandle The node id, which can be any valid node handle.
+   * @return the document encoding String object.
+   * @xsl.usage internal
+   */
+  public String getDocumentEncoding(int nodeHandle)
+  {
+
+    // %REVIEW%  OK??  -sb
+    return "UTF-8";
+  }
+
+  /**
+   * Return an indication of the standalone status of the document,
+   *        either "yes" or "no". This property is derived from the optional
+   *        standalone document declaration in the XML declaration at the
+   *        beginning of the document entity, and has no value if there is no
+   *        standalone document declaration.
+   *
+   * @param nodeHandle The node id, which can be any valid node handle.
+   * @return the document standalone String object, either "yes", "no", or null.
+   */
+  public String getDocumentStandalone(int nodeHandle)
+  {
+    return null;
+  }
+
+  /**
+   * Return a string representing the XML version of the document. This
+   * property is derived from the XML declaration optionally present at the
+   * beginning of the document entity, and has no value if there is no XML
+   * declaration.
+   *
+   * @param documentHandle The document handle
+   *
+   * @return the document version String object.
+   */
+  public String getDocumentVersion(int documentHandle)
+  {
+    return null;
+  }
+
+  /**
+   * Return an indication of
+   * whether the processor has read the complete DTD. Its value is a
+   * boolean. If it is false, then certain properties (indicated in their
+   * descriptions below) may be unknown. If it is true, those properties
+   * are never unknown.
+   *
+   * @return <code>true</code> if all declarations were processed;
+   *         <code>false</code> otherwise.
+   */
+  public boolean getDocumentAllDeclarationsProcessed()
+  {
+
+    // %REVIEW% OK?
+    return true;
+  }
+
+  /**
+   *   A document type declaration information item has the following properties:
+   *
+   *     1. [system identifier] The system identifier of the external subset, if
+   *        it exists. Otherwise this property has no value.
+   *
+   * @return the system identifier String object, or null if there is none.
+   */
+  public abstract String getDocumentTypeDeclarationSystemIdentifier();
+
+  /**
+   * Return the public identifier of the external subset,
+   * normalized as described in 4.2.2 External Entities [XML]. If there is
+   * no external subset or if it has no public identifier, this property
+   * has no value.
+   *
+   * @return the public identifier String object, or null if there is none.
+   */
+  public abstract String getDocumentTypeDeclarationPublicIdentifier();
+
+  /**
+   * Returns the <code>Element</code> whose <code>ID</code> is given by
+   * <code>elementId</code>. If no such element exists, returns
+   * <code>DTM.NULL</code>. Behavior is not defined if more than one element
+   * has this <code>ID</code>. Attributes (including those
+   * with the name "ID") are not of type ID unless so defined by DTD/Schema
+   * information available to the DTM implementation.
+   * Implementations that do not know whether attributes are of type ID or
+   * not are expected to return <code>DTM.NULL</code>.
+   *
+   * <p>%REVIEW% Presumably IDs are still scoped to a single document,
+   * and this operation searches only within a single document, right?
+   * Wouldn't want collisions between DTMs in the same process.</p>
+   *
+   * @param elementId The unique <code>id</code> value for an element.
+   * @return The handle of the matching element.
+   */
+  public abstract int getElementById(String elementId);
+
+  /**
+   * The getUnparsedEntityURI function returns the URI of the unparsed
+   * entity with the specified name in the same document as the context
+   * node (see [3.3 Unparsed Entities]). It returns the empty string if
+   * there is no such entity.
+   * <p>
+   * XML processors may choose to use the System Identifier (if one
+   * is provided) to resolve the entity, rather than the URI in the
+   * Public Identifier. The details are dependent on the processor, and
+   * we would have to support some form of plug-in resolver to handle
+   * this properly. Currently, we simply return the System Identifier if
+   * present, and hope that it a usable URI or that our caller can
+   * map it to one.
+   * TODO: Resolve Public Identifiers... or consider changing function name.
+   * <p>
+   * If we find a relative URI
+   * reference, XML expects it to be resolved in terms of the base URI
+   * of the document. The DOM doesn't do that for us, and it isn't
+   * entirely clear whether that should be done here; currently that's
+   * pushed up to a higher level of our application. (Note that DOM Level
+   * 1 didn't store the document's base URI.)
+   * TODO: Consider resolving Relative URIs.
+   * <p>
+   * (The DOM's statement that "An XML processor may choose to
+   * completely expand entities before the structure model is passed
+   * to the DOM" refers only to parsed entities, not unparsed, and hence
+   * doesn't affect this function.)
+   *
+   * @param name A string containing the Entity Name of the unparsed
+   * entity.
+   *
+   * @return String containing the URI of the Unparsed Entity, or an
+   * empty string if no such entity exists.
+   */
+  public abstract String getUnparsedEntityURI(String name);
+
+  // ============== Boolean methods ================
+
+  /**
+   * Return true if the xsl:strip-space or xsl:preserve-space was processed
+   * during construction of the DTM document.
+   *
+   * @return true if this DTM supports prestripping.
+   */
+  public boolean supportsPreStripping()
+  {
+    return true;
+  }
+
+  /**
+   * Figure out whether nodeHandle2 should be considered as being later
+   * in the document than nodeHandle1, in Document Order as defined
+   * by the XPath model. This may not agree with the ordering defined
+   * by other XML applications.
+   * <p>
+   * There are some cases where ordering isn't defined, and neither are
+   * the results of this function -- though we'll generally return false.
+   *
+   * @param nodeHandle1 Node handle to perform position comparison on.
+   * @param nodeHandle2 Second Node handle to perform position comparison on .
+   *
+   * @return true if node1 comes before node2, otherwise return false.
+   * You can think of this as
+   * <code>(node1.documentOrderPosition &lt;= node2.documentOrderPosition)</code>.
+   */
+  public boolean isNodeAfter(int nodeHandle1, int nodeHandle2)
+  {
+		// These return NULL if the node doesn't belong to this document.
+    int index1 = makeNodeIdentity(nodeHandle1);
+    int index2 = makeNodeIdentity(nodeHandle2);
+
+    return index1!=NULL && index2!=NULL && index1 <= index2;
+  }
+
+  /**
+   *     2. [element content whitespace] A boolean indicating whether the
+   *        character is white space appearing within element content (see [XML],
+   *        2.10 "White Space Handling"). Note that validating XML processors are
+   *        required by XML 1.0 to provide this information. If there is no
+   *        declaration for the containing element, this property has no value for
+   *        white space characters. If no declaration has been read, but the [all
+   *        declarations processed] property of the document information item is
+   *        false (so there may be an unread declaration), then the value of this
+   *        property is unknown for white space characters. It is always false for
+   *        characters that are not white space.
+   *
+   * @param nodeHandle the node ID.
+   * @return <code>true</code> if the character data is whitespace;
+   *         <code>false</code> otherwise.
+   */
+  public boolean isCharacterElementContentWhitespace(int nodeHandle)
+  {
+
+    // %TBD%
+    return false;
+  }
+
+  /**
+   *    10. [all declarations processed] This property is not strictly speaking
+   *        part of the infoset of the document. Rather it is an indication of
+   *        whether the processor has read the complete DTD. Its value is a
+   *        boolean. If it is false, then certain properties (indicated in their
+   *        descriptions below) may be unknown. If it is true, those properties
+   *        are never unknown.
+   *
+   * @param documentHandle A node handle that must identify a document.
+   * @return <code>true</code> if all declarations were processed;
+   *         <code>false</code> otherwise.
+   */
+  public boolean isDocumentAllDeclarationsProcessed(int documentHandle)
+  {
+    return true;
+  }
+
+  /**
+   *     5. [specified] A flag indicating whether this attribute was actually
+   *        specified in the start-tag of its element, or was defaulted from the
+   *        DTD.
+   *
+   * @param attributeHandle The attribute handle in question.
+   *
+   * @return <code>true</code> if the attribute was specified;
+   *         <code>false</code> if it was defaulted.
+   */
+  public abstract boolean isAttributeSpecified(int attributeHandle);
+
+  // ========== Direct SAX Dispatch, for optimization purposes ========
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value). Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   * @param normalize true if the content should be normalized according to
+   * the rules for the XPath
+   * <a href="http://www.w3.org/TR/xpath#function-normalize-space">normalize-space</a>
+   * function.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public abstract void dispatchCharactersEvents(
+    int nodeHandle, org.xml.sax.ContentHandler ch, boolean normalize)
+      throws org.xml.sax.SAXException;
+
+  /**
+   * Directly create SAX parser events from a subtree.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public abstract void dispatchToEvents(
+    int nodeHandle, org.xml.sax.ContentHandler ch)
+      throws org.xml.sax.SAXException;
+
+  /**
+   * Return an DOM node for the given node.
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A node representation of the DTM node.
+   */
+  public org.w3c.dom.Node getNode(int nodeHandle)
+  {
+    return new DTMNodeProxy(this, nodeHandle);
+  }
+
+  // ==== Construction methods (may not be supported by some implementations!) =====
+
+  /**
+   * Append a child to the end of the document. Please note that the node
+   * is always cloned if it is owned by another document.
+   *
+   * <p>%REVIEW% "End of the document" needs to be defined more clearly.
+   * Does it become the last child of the Document? Of the root element?</p>
+   *
+   * @param newChild Must be a valid new node handle.
+   * @param clone true if the child should be cloned into the document.
+   * @param cloneDepth if the clone argument is true, specifies that the
+   *                   clone should include all it's children.
+   */
+  public void appendChild(int newChild, boolean clone, boolean cloneDepth)
+  {
+    error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"appendChild not yet supported!");
+  }
+
+  /**
+   * Append a text node child that will be constructed from a string,
+   * to the end of the document.
+   *
+   * <p>%REVIEW% "End of the document" needs to be defined more clearly.
+   * Does it become the last child of the Document? Of the root element?</p>
+   *
+   * @param str Non-null reverence to a string.
+   */
+  public void appendTextChild(String str)
+  {
+    error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"appendTextChild not yet supported!");
+  }
+
+  /**
+   * Simple error for asserts and the like.
+   *
+   * @param msg Error message to report.
+   */
+  protected void error(String msg)
+  {
+    throw new DTMException(msg);
+  }
+
+  /**
+   * Find out whether or not to strip whispace nodes.
+   *
+   *
+   * @return whether or not to strip whispace nodes.
+   */
+  protected boolean getShouldStripWhitespace()
+  {
+    return m_shouldStripWS;
+  }
+
+  /**
+   * Set whether to strip whitespaces and push in current value of
+   * m_shouldStripWS in m_shouldStripWhitespaceStack.
+   *
+   * @param shouldStrip Flag indicating whether to strip whitespace nodes
+   */
+  protected void pushShouldStripWhitespace(boolean shouldStrip)
+  {
+
+    m_shouldStripWS = shouldStrip;
+
+    if (null != m_shouldStripWhitespaceStack)
+      m_shouldStripWhitespaceStack.push(shouldStrip);
+  }
+
+  /**
+   * Set whether to strip whitespaces at this point by popping out
+   * m_shouldStripWhitespaceStack.
+   *
+   */
+  protected void popShouldStripWhitespace()
+  {
+    if (null != m_shouldStripWhitespaceStack)
+      m_shouldStripWS = m_shouldStripWhitespaceStack.popAndTop();
+  }
+
+  /**
+   * Set whether to strip whitespaces and set the top of the stack to
+   * the current value of m_shouldStripWS.
+   *
+   *
+   * @param shouldStrip Flag indicating whether to strip whitespace nodes
+   */
+  protected void setShouldStripWhitespace(boolean shouldStrip)
+  {
+
+    m_shouldStripWS = shouldStrip;
+
+    if (null != m_shouldStripWhitespaceStack)
+      m_shouldStripWhitespaceStack.setTop(shouldStrip);
+  }
+
+  /**
+   * A dummy routine to satisify the abstract interface. If the DTM
+   * implememtation that extends the default base requires notification
+   * of registration, they can override this method.
+   */
+   public void documentRegistration()
+   {
+   }
+
+  /**
+   * A dummy routine to satisify the abstract interface. If the DTM
+   * implememtation that extends the default base requires notification
+   * when the document is being released, they can override this method
+   */
+   public void documentRelease()
+   {
+   }
+
+   /**
+    * Migrate a DTM built with an old DTMManager to a new DTMManager.
+    * After the migration, the new DTMManager will treat the DTM as
+    * one that is built by itself.
+    * This is used to support DTM sharing between multiple transformations.
+    * @param mgr the DTMManager
+    */
+   public void migrateTo(DTMManager mgr)
+   {
+     m_mgr = mgr;
+     if(mgr instanceof DTMManagerDefault)
+       m_mgrDefault=(DTMManagerDefault)mgr;     
+   }      
+
+	 /** Query which DTMManager this DTM is currently being handled by.
+	  * 
+	  * %REVEW% Should this become part of the base DTM API?
+	  * 
+	  * @return a DTMManager, or null if this is a "stand-alone" DTM.
+	  */
+	 public DTMManager getManager()
+	 {
+		 return m_mgr;
+	 }
+
+	 /** Query which DTMIDs this DTM is currently using within the DTMManager.
+	  * 
+	  * %REVEW% Should this become part of the base DTM API?
+	  * 
+	  * @return an IntVector, or null if this is a "stand-alone" DTM.
+	  */
+	 public SuballocatedIntVector getDTMIDs()
+	 {
+		 if(m_mgr==null) return null;
+		 return m_dtmIdent;
+	 }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBaseIterators.java b/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBaseIterators.java
new file mode 100644
index 0000000..d554119
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBaseIterators.java
@@ -0,0 +1,2192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMDefaultBaseIterators.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.*;
+
+import javax.xml.transform.Source;
+
+import org.apache.xml.utils.XMLStringFactory;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+/**
+ * This class implements the traversers for DTMDefaultBase.
+ */
+public abstract class DTMDefaultBaseIterators extends DTMDefaultBaseTraversers
+{
+
+  /**
+   * Construct a DTMDefaultBaseTraversers object from a DOM node.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param source The object that is used to specify the construction source.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may
+   *                         be null.
+   * @param xstringfactory The factory to use for creating XMLStrings.
+   * @param doIndexing true if the caller considers it worth it to use 
+   *                   indexing schemes.
+   */
+  public DTMDefaultBaseIterators(DTMManager mgr, Source source,
+                                 int dtmIdentity,
+                                 DTMWSFilter whiteSpaceFilter,
+                                 XMLStringFactory xstringfactory,
+                                 boolean doIndexing)
+  {
+    super(mgr, source, dtmIdentity, whiteSpaceFilter, 
+          xstringfactory, doIndexing);
+  }
+
+  /**
+   * Construct a DTMDefaultBaseTraversers object from a DOM node.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param source The object that is used to specify the construction source.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may
+   *                         be null.
+   * @param xstringfactory The factory to use for creating XMLStrings.
+   * @param doIndexing true if the caller considers it worth it to use 
+   *                   indexing schemes.
+   * @param blocksize The block size of the DTM.
+   * @param usePrevsib true if we want to build the previous sibling node array.
+   * @param newNameTable true if we want to use a new ExpandedNameTable for this DTM.
+   */
+  public DTMDefaultBaseIterators(DTMManager mgr, Source source,
+                                 int dtmIdentity,
+                                 DTMWSFilter whiteSpaceFilter,
+                                 XMLStringFactory xstringfactory,
+                                 boolean doIndexing,
+                                 int blocksize,
+                                 boolean usePrevsib,
+                                 boolean newNameTable)
+  {
+    super(mgr, source, dtmIdentity, whiteSpaceFilter, 
+          xstringfactory, doIndexing, blocksize, usePrevsib, newNameTable);
+  }
+
+  /**
+   * Get an iterator that can navigate over an XPath Axis, predicated by
+   * the extended type ID.
+   * Returns an iterator that must be initialized
+   * with a start node (using iterator.setStartNode()).
+   *
+   * @param axis One of Axes.ANCESTORORSELF, etc.
+   * @param type An extended type ID.
+   *
+   * @return A DTMAxisIterator, or null if the given axis isn't supported.
+   */
+  public DTMAxisIterator getTypedAxisIterator(int axis, int type)
+  {
+
+    DTMAxisIterator iterator = null;
+
+    /* This causes an error when using patterns for elements that
+       do not exist in the DOM (translet types which do not correspond
+       to a DOM type are mapped to the DOM.ELEMENT type).
+    */
+
+    //        if (type == NO_TYPE) {
+    //            return(EMPTYITERATOR);
+    //        }
+    //        else if (type == ELEMENT) {
+    //            iterator = new FilterIterator(getAxisIterator(axis),
+    //                                          getElementFilter());
+    //        }
+    //        else 
+    {
+      switch (axis)
+      {
+      case Axis.SELF :
+        iterator = new TypedSingletonIterator(type);
+        break;
+      case Axis.CHILD :
+        iterator = new TypedChildrenIterator(type);
+        break;
+      case Axis.PARENT :
+        return (new ParentIterator().setNodeType(type));
+      case Axis.ANCESTOR :
+        return (new TypedAncestorIterator(type));
+      case Axis.ANCESTORORSELF :
+        return ((new TypedAncestorIterator(type)).includeSelf());
+      case Axis.ATTRIBUTE :
+        return (new TypedAttributeIterator(type));
+      case Axis.DESCENDANT :
+        iterator = new TypedDescendantIterator(type);
+        break;
+      case Axis.DESCENDANTORSELF :
+        iterator = (new TypedDescendantIterator(type)).includeSelf();
+        break;
+      case Axis.FOLLOWING :
+        iterator = new TypedFollowingIterator(type);
+        break;
+      case Axis.PRECEDING :
+        iterator = new TypedPrecedingIterator(type);
+        break;
+      case Axis.FOLLOWINGSIBLING :
+        iterator = new TypedFollowingSiblingIterator(type);
+        break;
+      case Axis.PRECEDINGSIBLING :
+        iterator = new TypedPrecedingSiblingIterator(type);
+        break;
+      case Axis.NAMESPACE :
+        iterator = new TypedNamespaceIterator(type);
+        break;
+      case Axis.ROOT :
+        iterator = new TypedRootIterator(type);
+        break;
+      default :
+        throw new DTMException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_TYPED_ITERATOR_AXIS_NOT_IMPLEMENTED, 
+            new Object[]{Axis.getNames(axis)})); 
+            //"Error: typed iterator for axis "
+                               //+ Axis.names[axis] + "not implemented");
+      }
+    }
+
+    return (iterator);
+  }
+
+  /**
+   * This is a shortcut to the iterators that implement the
+   * XPath axes.
+   * Returns a bare-bones iterator that must be initialized
+   * with a start node (using iterator.setStartNode()).
+   *
+   * @param axis One of Axes.ANCESTORORSELF, etc.
+   *
+   * @return A DTMAxisIterator, or null if the given axis isn't supported.
+   */
+  public DTMAxisIterator getAxisIterator(final int axis)
+  {
+
+    DTMAxisIterator iterator = null;
+
+    switch (axis)
+    {
+    case Axis.SELF :
+      iterator = new SingletonIterator();
+      break;
+    case Axis.CHILD :
+      iterator = new ChildrenIterator();
+      break;
+    case Axis.PARENT :
+      return (new ParentIterator());
+    case Axis.ANCESTOR :
+      return (new AncestorIterator());
+    case Axis.ANCESTORORSELF :
+      return ((new AncestorIterator()).includeSelf());
+    case Axis.ATTRIBUTE :
+      return (new AttributeIterator());
+    case Axis.DESCENDANT :
+      iterator = new DescendantIterator();
+      break;
+    case Axis.DESCENDANTORSELF :
+      iterator = (new DescendantIterator()).includeSelf();
+      break;
+    case Axis.FOLLOWING :
+      iterator = new FollowingIterator();
+      break;
+    case Axis.PRECEDING :
+      iterator = new PrecedingIterator();
+      break;
+    case Axis.FOLLOWINGSIBLING :
+      iterator = new FollowingSiblingIterator();
+      break;
+    case Axis.PRECEDINGSIBLING :
+      iterator = new PrecedingSiblingIterator();
+      break;
+    case Axis.NAMESPACE :
+      iterator = new NamespaceIterator();
+      break;
+    case Axis.ROOT :
+      iterator = new RootIterator();
+      break;
+    default :
+      throw new DTMException(XMLMessages.createXMLMessage(
+        XMLErrorResources.ER_ITERATOR_AXIS_NOT_IMPLEMENTED, 
+        new Object[]{Axis.getNames(axis)})); 
+        //"Error: iterator for axis '" + Axis.names[axis]
+                             //+ "' not implemented");
+    }
+
+    return (iterator);
+  }
+
+  /**
+   * Abstract superclass defining behaviors shared by all DTMDefault's
+   * internal implementations of DTMAxisIterator. Subclass this (and
+   * override, if necessary) to implement the specifics of an
+   * individual axis iterator.
+   *
+   * Currently there isn't a lot here
+   */
+  public abstract class InternalAxisIteratorBase extends DTMAxisIteratorBase
+  {
+
+    // %REVIEW% We could opt to share _nodeType and setNodeType() as
+    // well, and simply ignore them in iterators which don't use them.
+    // But Scott's worried about the overhead involved in cloning
+    // these, and wants them to have as few fields as possible. Note
+    // that we can't create a TypedInternalAxisIteratorBase because
+    // those are often based on the untyped versions and Java doesn't
+    // support multiple inheritance. <sigh/>
+
+    /**
+     * Current iteration location. Usually this is the last location
+     * returned (starting point for the next() search); for single-node
+     * iterators it may instead be initialized to point to that single node.
+     */
+    protected int _currentNode;
+
+    /**
+     * Remembers the current node for the next call to gotoMark().
+     *
+     * %REVIEW% Should this save _position too?
+     */
+    public void setMark()
+    {
+      _markedNode = _currentNode;
+    }
+
+    /**
+     * Restores the current node remembered by setMark().
+     *
+     * %REVEIW% Should this restore _position too?
+     */
+    public void gotoMark()
+    {
+      _currentNode = _markedNode;
+    }
+        
+  }  // end of InternalAxisIteratorBase 
+
+  /**
+   * Iterator that returns all immediate children of a given node
+   */
+  public final class ChildrenIterator extends InternalAxisIteratorBase
+  {
+
+    /**
+     * Setting start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * If the iterator is not restartable, this has no effect.
+     * %REVIEW% Should it return/throw something in that case,
+     * or set current node to END, to indicate request-not-honored?
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = (node == DTM.NULL) ? DTM.NULL
+                                          : _firstch(makeNodeIdentity(node));
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END if no more
+     * are available.
+     */
+    public int next()
+    {
+      if (_currentNode != NULL) {
+        int node = _currentNode;
+        _currentNode = _nextsib(node);
+        return returnNode(makeNodeHandle(node));
+      }
+
+      return END;
+    }
+  }  // end of ChildrenIterator
+
+  /**
+   * Iterator that returns the parent of a given node. Note that
+   * this delivers only a single node; if you want all the ancestors,
+   * see AncestorIterator.
+   */
+  public final class ParentIterator extends InternalAxisIteratorBase
+  {
+
+    /** The extended type ID that was requested. */
+    private int _nodeType = -1;
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = getParent(node);
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Set the node type of the parent that we're looking for.
+     * Note that this does _not_ mean "find the nearest ancestor of
+     * this type", but "yield the parent if it is of this type".
+     *
+     *
+     * @param type extended type ID.
+     *
+     * @return ParentIterator configured with the type filter set.
+     */
+    public DTMAxisIterator setNodeType(final int type)
+    {
+
+      _nodeType = type;
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration. In this case, we return
+     * only the immediate parent, _if_ it matches the requested nodeType.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int result = _currentNode;
+
+      if (_nodeType >= DTM.NTYPES) {
+        if (_nodeType != getExpandedTypeID(_currentNode)) {
+          result = END;
+        }
+      } else if (_nodeType != NULL) {
+        if (_nodeType != getNodeType(_currentNode)) {
+          result = END;
+        }
+      }
+
+      _currentNode = END;
+
+      return returnNode(result);
+    }
+  }  // end of ParentIterator
+
+  /**
+   * Iterator that returns children of a given type for a given node.
+   * The functionality chould be achieved by putting a filter on top
+   * of a basic child iterator, but a specialised iterator is used
+   * for efficiency (both speed and size of translet).
+   */
+  public final class TypedChildrenIterator extends InternalAxisIteratorBase
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedChildrenIterator
+     *
+     *
+     * @param nodeType The extended type ID being requested.
+     */
+    public TypedChildrenIterator(int nodeType)
+    {
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = (node == DTM.NULL)
+                                   ? DTM.NULL
+                                   : _firstch(makeNodeIdentity(_startNode));
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int eType;
+      int node = _currentNode;
+
+      int nodeType = _nodeType;
+
+      if (nodeType >= DTM.NTYPES) {
+        while (node != DTM.NULL && _exptype(node) != nodeType) {
+          node = _nextsib(node);
+        }
+      } else {
+        while (node != DTM.NULL) {
+          eType = _exptype(node);
+          if (eType < DTM.NTYPES) {
+            if (eType == nodeType) {
+              break;
+            }
+          } else if (m_expandedNameTable.getType(eType) == nodeType) {
+            break;
+          }
+          node = _nextsib(node);
+        }
+      }
+
+      if (node == DTM.NULL) {
+        _currentNode = DTM.NULL;
+        return DTM.NULL;
+      } else {
+        _currentNode = _nextsib(node);
+        return returnNode(makeNodeHandle(node));
+      }
+
+    }
+  }  // end of TypedChildrenIterator
+
+  /**
+   * Iterator that returns children within a given namespace for a
+   * given node. The functionality chould be achieved by putting a
+   * filter on top of a basic child iterator, but a specialised
+   * iterator is used for efficiency (both speed and size of translet).
+   */
+  public final class NamespaceChildrenIterator
+          extends InternalAxisIteratorBase
+  {
+
+    /** The extended type ID being requested. */
+    private final int _nsType;
+
+    /**
+     * Constructor NamespaceChildrenIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public NamespaceChildrenIterator(final int type)
+    {
+      _nsType = type;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = (node == DTM.NULL) ? DTM.NULL : NOTPROCESSED;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      if (_currentNode != DTM.NULL) {
+        for (int node = (NOTPROCESSED == _currentNode)
+                                  ? _firstch(makeNodeIdentity(_startNode))
+                                  : _nextsib(_currentNode);
+             node != END;
+             node = _nextsib(node)) {
+          if (m_expandedNameTable.getNamespaceID(_exptype(node)) == _nsType) {
+            _currentNode = node;
+
+            return returnNode(node);
+          }
+        }
+      }
+
+      return END;
+    }
+  }  // end of NamespaceChildrenIterator
+  
+  /**
+   * Iterator that returns the namespace nodes as defined by the XPath data model 
+   * for a given node.
+   */
+  public class NamespaceIterator
+          extends InternalAxisIteratorBase
+  {
+
+    /**
+     * Constructor NamespaceAttributeIterator
+     */
+    public NamespaceIterator()
+    {
+
+      super();
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = getFirstNamespaceNode(node, true);
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      int node = _currentNode;
+
+      if (DTM.NULL != node)
+        _currentNode = getNextNamespaceNode(_startNode, node, true);
+
+      return returnNode(node);
+    }
+  }  // end of NamespaceIterator
+  
+  /**
+   * Iterator that returns the namespace nodes as defined by the XPath data model 
+   * for a given node, filtered by extended type ID.
+   */
+  public class TypedNamespaceIterator extends NamespaceIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedNamespaceIterator
+     *
+     *
+     * @param nodeType The extended type ID being requested.
+     */
+    public TypedNamespaceIterator(int nodeType)
+    {
+      super();
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+    	int node;
+
+      for (node = _currentNode;
+           node != END;
+           node = getNextNamespaceNode(_startNode, node, true)) {
+        if (getExpandedTypeID(node) == _nodeType
+            || getNodeType(node) == _nodeType
+            || getNamespaceType(node) == _nodeType) {
+          _currentNode = node;
+
+          return returnNode(node);
+        }
+      }
+
+      return (_currentNode =END);
+    }
+  }  // end of TypedNamespaceIterator
+  
+  /**
+   * Iterator that returns the the root node as defined by the XPath data model 
+   * for a given node.
+   */
+  public class RootIterator
+          extends InternalAxisIteratorBase
+  {
+
+    /**
+     * Constructor RootIterator
+     */
+    public RootIterator()
+    {
+
+      super();
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+
+      if (_isRestartable)
+      {
+        _startNode = getDocumentRoot(node);
+        _currentNode = NULL;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      if(_startNode == _currentNode)
+        return NULL;
+
+      _currentNode = _startNode;
+
+      return returnNode(_startNode);
+    }
+  }  // end of RootIterator
+  
+  /**
+   * Iterator that returns the namespace nodes as defined by the XPath data model 
+   * for a given node, filtered by extended type ID.
+   */
+  public class TypedRootIterator extends RootIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedRootIterator
+     *
+     * @param nodeType The extended type ID being requested.
+     */
+    public TypedRootIterator(int nodeType)
+    {
+      super();
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+    	if(_startNode == _currentNode)
+        return NULL;
+
+      int nodeType = _nodeType;
+      int node = _startNode;
+      int expType = getExpandedTypeID(node);
+
+      _currentNode = node;
+
+      if (nodeType >= DTM.NTYPES) {
+        if (nodeType == expType) {
+          return returnNode(node);
+        }
+      } else {
+        if (expType < DTM.NTYPES) {
+          if (expType == nodeType) {
+            return returnNode(node);
+          }
+        } else {
+          if (m_expandedNameTable.getType(expType) == nodeType) {
+            return returnNode(node);
+          }
+        }
+      }
+
+      return END;
+    }
+  }  // end of TypedRootIterator
+
+  /**
+   * Iterator that returns attributes within a given namespace for a node.
+   */
+  public final class NamespaceAttributeIterator
+          extends InternalAxisIteratorBase
+  {
+
+    /** The extended type ID being requested. */
+    private final int _nsType;
+
+    /**
+     * Constructor NamespaceAttributeIterator
+     *
+     *
+     * @param nsType The extended type ID being requested.
+     */
+    public NamespaceAttributeIterator(int nsType)
+    {
+
+      super();
+
+      _nsType = nsType;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = getFirstNamespaceNode(node, false);
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      int node = _currentNode;
+
+      if (DTM.NULL != node)
+        _currentNode = getNextNamespaceNode(_startNode, node, false);
+
+      return returnNode(node);
+    }
+  }  // end of NamespaceAttributeIterator
+
+  /**
+   * Iterator that returns all siblings of a given node.
+   */
+  public class FollowingSiblingIterator extends InternalAxisIteratorBase
+  {
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = makeNodeIdentity(node);
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      _currentNode = (_currentNode == DTM.NULL) ? DTM.NULL
+                                                : _nextsib(_currentNode);
+      return returnNode(makeNodeHandle(_currentNode));
+    }
+  }  // end of FollowingSiblingIterator
+
+  /**
+   * Iterator that returns all following siblings of a given node.
+   */
+  public final class TypedFollowingSiblingIterator
+          extends FollowingSiblingIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedFollowingSiblingIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedFollowingSiblingIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      if (_currentNode == DTM.NULL) {
+        return DTM.NULL;
+      }
+
+      int node = _currentNode;
+      int eType;
+      int nodeType = _nodeType;
+
+      if (nodeType >= DTM.NTYPES) {
+        do {
+          node = _nextsib(node);
+        } while (node != DTM.NULL && _exptype(node) != nodeType);
+      } else {
+        while ((node = _nextsib(node)) != DTM.NULL) {
+          eType = _exptype(node);
+          if (eType < DTM.NTYPES) {
+            if (eType == nodeType) {
+              break;
+            }
+          } else if (m_expandedNameTable.getType(eType) == nodeType) {
+            break;
+          }
+        }
+      }
+
+      _currentNode = node;
+
+      return (_currentNode == DTM.NULL)
+                      ? DTM.NULL
+                      : returnNode(makeNodeHandle(_currentNode));
+    }
+  }  // end of TypedFollowingSiblingIterator
+
+  /**
+   * Iterator that returns attribute nodes (of what nodes?)
+   */
+  public final class AttributeIterator extends InternalAxisIteratorBase
+  {
+
+    // assumes caller will pass element nodes
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = getFirstAttributeIdentity(makeNodeIdentity(node));
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      final int node = _currentNode;
+
+      if (node != NULL) {
+        _currentNode = getNextAttributeIdentity(node);
+        return returnNode(makeNodeHandle(node));
+      }
+
+      return NULL;
+    }
+  }  // end of AttributeIterator
+
+  /**
+   * Iterator that returns attribute nodes of a given type
+   */
+  public final class TypedAttributeIterator extends InternalAxisIteratorBase
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedAttributeIterator
+     *
+     *
+     * @param nodeType The extended type ID that is requested.
+     */
+    public TypedAttributeIterator(int nodeType)
+    {
+      _nodeType = nodeType;
+    }
+
+    // assumes caller will pass element nodes
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+      if (_isRestartable)
+      {
+        _startNode = node;
+
+        _currentNode = getTypedAttribute(node, _nodeType);
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      final int node = _currentNode;
+
+      // singleton iterator, since there can only be one attribute of 
+      // a given type.
+      _currentNode = NULL;
+
+      return returnNode(node);
+    }
+  }  // end of TypedAttributeIterator
+
+  /**
+   * Iterator that returns preceding siblings of a given node
+   */
+  public class PrecedingSiblingIterator extends InternalAxisIteratorBase
+  {
+
+    /**
+     * The node identity of _startNode for this iterator
+     */
+    protected int _startNodeID;
+
+    /**
+     * True if this iterator has a reversed axis.
+     *
+     * @return true.
+     */
+    public boolean isReverse()
+    {
+      return true;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        node = _startNodeID = makeNodeIdentity(node);
+
+        if(node == NULL)
+        {
+          _currentNode = node;
+          return resetPosition();
+        }
+
+        int type = m_expandedNameTable.getType(_exptype(node));
+        if(ExpandedNameTable.ATTRIBUTE == type 
+           || ExpandedNameTable.NAMESPACE == type )
+        {
+          _currentNode = node;
+        }
+        else
+        {
+          // Be careful to handle the Document node properly
+          _currentNode = _parent(node);
+          if(NULL!=_currentNode)	
+            _currentNode = _firstch(_currentNode);
+          else
+            _currentNode = node;
+        }
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      if (_currentNode == _startNodeID || _currentNode == DTM.NULL)
+      {
+        return NULL;
+      }
+      else
+      {
+        final int node = _currentNode;
+        _currentNode = _nextsib(node);
+
+        return returnNode(makeNodeHandle(node));
+      }
+    }
+  }  // end of PrecedingSiblingIterator
+
+  /**
+   * Iterator that returns preceding siblings of a given type for
+   * a given node
+   */
+  public final class TypedPrecedingSiblingIterator
+          extends PrecedingSiblingIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedPrecedingSiblingIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedPrecedingSiblingIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int node = _currentNode;
+      int expType;
+
+      int nodeType = _nodeType;
+      int startID = _startNodeID;
+
+      if (nodeType >= DTM.NTYPES) {
+        while (node != NULL && node != startID && _exptype(node) != nodeType) {
+          node = _nextsib(node);
+        }
+      } else {
+        while (node != NULL && node != startID) {
+          expType = _exptype(node);
+          if (expType < DTM.NTYPES) {
+            if (expType == nodeType) {
+              break;
+            }
+          } else {
+            if (m_expandedNameTable.getType(expType) == nodeType) {
+              break;
+            }
+          }
+          node = _nextsib(node);
+        }
+      }
+
+      if (node == DTM.NULL || node == _startNodeID) {
+        _currentNode = NULL;
+        return NULL;
+      } else {
+        _currentNode = _nextsib(node);
+        return returnNode(makeNodeHandle(node));
+      }
+    }
+  }  // end of TypedPrecedingSiblingIterator
+
+  /**
+   * Iterator that returns preceding nodes of a given node.
+   * This includes the node set {root+1, start-1}, but excludes
+   * all ancestors, attributes, and namespace nodes.
+   */
+  public class PrecedingIterator extends InternalAxisIteratorBase
+  {
+
+    /** The max ancestors, but it can grow... */
+    private final int _maxAncestors = 8;
+
+    /**
+     * The stack of start node + ancestors up to the root of the tree,
+     *  which we must avoid.
+     */
+    protected int[] _stack = new int[_maxAncestors];
+
+    /** (not sure yet... -sb) */
+    protected int _sp, _oldsp;
+
+    protected int _markedsp, _markedNode, _markedDescendant;
+
+    /* _currentNode precedes candidates.  This is the identity, not the handle! */
+
+    /**
+     * True if this iterator has a reversed axis.
+     *
+     * @return true since this iterator is a reversed axis.
+     */
+    public boolean isReverse()
+    {
+      return true;
+    }
+
+    /**
+     * Returns a deep copy of this iterator.   The cloned iterator is not reset.
+     *
+     * @return a deep copy of this iterator.
+     */
+    public DTMAxisIterator cloneIterator()
+    {
+      _isRestartable = false;
+
+      try
+      {
+        final PrecedingIterator clone = (PrecedingIterator) super.clone();
+        final int[] stackCopy = new int[_stack.length];
+        System.arraycopy(_stack, 0, stackCopy, 0, _stack.length);
+
+        clone._stack = stackCopy;
+
+        // return clone.reset();
+        return clone;
+      }
+      catch (CloneNotSupportedException e)
+      {
+        throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_ITERATOR_CLONE_NOT_SUPPORTED, null)); //"Iterator clone not supported.");
+      }
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        node = makeNodeIdentity(node);
+
+        // iterator is not a clone
+        int parent, index;
+
+       if (_type(node) == DTM.ATTRIBUTE_NODE)
+        node = _parent(node);
+
+        _startNode = node;
+        _stack[index = 0] = node;
+        
+       
+
+		parent=node;
+		while ((parent = _parent(parent)) != NULL)
+		{
+			if (++index == _stack.length)
+			{
+				final int[] stack = new int[index + 4];
+				System.arraycopy(_stack, 0, stack, 0, index);
+				_stack = stack;
+			}
+			_stack[index] = parent;
+        }
+        if(index>0)
+	        --index; // Pop actual root node (if not start) back off the stack
+
+        _currentNode=_stack[index]; // Last parent before root node
+
+        _oldsp = _sp = index;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+    	// Bugzilla 8324: We were forgetting to skip Attrs and NS nodes.
+    	// Also recoded the loop controls for clarity and to flatten out
+    	// the tail-recursion.
+   		for(++_currentNode; 
+   			_sp>=0; 
+   			++_currentNode)
+   		{
+   			if(_currentNode < _stack[_sp])
+   			{
+   				if(_type(_currentNode) != ATTRIBUTE_NODE &&
+   					_type(_currentNode) != NAMESPACE_NODE)
+   					return returnNode(makeNodeHandle(_currentNode));
+   			}
+   			else
+   				--_sp;
+   		}
+   		return NULL;
+    }
+
+    // redefine DTMAxisIteratorBase's reset
+
+    /**
+     * Resets the iterator to the last start node.
+     *
+     * @return A DTMAxisIterator, which may or may not be the same as this
+     *         iterator.
+     */
+    public DTMAxisIterator reset()
+    {
+
+      _sp = _oldsp;
+
+      return resetPosition();
+    }
+
+    public void setMark() {
+        _markedsp = _sp;
+        _markedNode = _currentNode;
+        _markedDescendant = _stack[0];
+    }
+
+    public void gotoMark() {
+        _sp = _markedsp;
+        _currentNode = _markedNode;
+    }
+  }  // end of PrecedingIterator
+
+  /**
+   * Iterator that returns preceding nodes of agiven type for a
+   * given node. This includes the node set {root+1, start-1}, but
+   * excludes all ancestors.
+   */
+  public final class TypedPrecedingIterator extends PrecedingIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedPrecedingIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedPrecedingIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int node = _currentNode;
+      int nodeType = _nodeType;
+
+      if (nodeType >= DTM.NTYPES) {
+        while (true) {
+          node = node + 1;
+
+          if (_sp < 0) {
+            node = NULL;
+            break;
+          } else if (node >= _stack[_sp]) {
+            if (--_sp < 0) {
+              node = NULL;
+              break;
+            }
+          } else if (_exptype(node) == nodeType) {
+            break;
+          }
+        }
+      } else {
+        int expType;
+
+        while (true) {
+          node = node + 1;
+
+          if (_sp < 0) {
+            node = NULL;
+            break;
+          } else if (node >= _stack[_sp]) {
+            if (--_sp < 0) {
+              node = NULL;
+              break;
+            }
+          } else {
+            expType = _exptype(node);
+            if (expType < DTM.NTYPES) {
+              if (expType == nodeType) {
+                break;
+              }
+            } else {
+              if (m_expandedNameTable.getType(expType) == nodeType) {
+                break;
+              }
+            }
+          }
+        }
+      }
+
+      _currentNode = node;
+             
+      return (node == NULL) ? NULL : returnNode(makeNodeHandle(node));
+    }
+  }  // end of TypedPrecedingIterator
+
+  /**
+   * Iterator that returns following nodes of for a given node.
+   */
+  public class FollowingIterator extends InternalAxisIteratorBase
+  {
+    DTMAxisTraverser m_traverser; // easier for now
+    
+    public FollowingIterator()
+    {
+      m_traverser = getAxisTraverser(Axis.FOLLOWING);
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+
+        // ?? -sb
+        // find rightmost descendant (or self)
+        // int current;
+        // while ((node = getLastChild(current = node)) != NULL){}
+        // _currentNode = current;
+        _currentNode = m_traverser.first(node);
+
+        // _currentNode precedes possible following(node) nodes
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      int node = _currentNode;
+
+      _currentNode = m_traverser.next(_startNode, _currentNode);
+
+      return returnNode(node);
+    }
+  }  // end of FollowingIterator
+
+  /**
+   * Iterator that returns following nodes of a given type for a given node.
+   */
+  public final class TypedFollowingIterator extends FollowingIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedFollowingIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedFollowingIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      int node;
+
+      do{
+       node = _currentNode;
+
+      _currentNode = m_traverser.next(_startNode, _currentNode);
+
+      } 
+      while (node != DTM.NULL
+             && (getExpandedTypeID(node) != _nodeType && getNodeType(node) != _nodeType));
+
+      return (node == DTM.NULL ? DTM.NULL :returnNode(node));
+    }
+  }  // end of TypedFollowingIterator
+
+  /**
+   * Iterator that returns the ancestors of a given node in document
+   * order.  (NOTE!  This was changed from the XSLTC code!)
+   */
+  public class AncestorIterator extends InternalAxisIteratorBase
+  {
+    org.apache.xml.utils.NodeVector m_ancestors = 
+         new org.apache.xml.utils.NodeVector();
+         
+    int m_ancestorsPos;
+
+    int m_markedPos;
+    
+    /** The real start node for this axes, since _startNode will be adjusted. */
+    int m_realStartNode;
+    
+    /**
+     * Get start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @return The root node of the iteration.
+     */
+    public int getStartNode()
+    {
+      return m_realStartNode;
+    }
+
+    /**
+     * True if this iterator has a reversed axis.
+     *
+     * @return true since this iterator is a reversed axis.
+     */
+    public final boolean isReverse()
+    {
+      return true;
+    }
+
+    /**
+     * Returns a deep copy of this iterator.  The cloned iterator is not reset.
+     *
+     * @return a deep copy of this iterator.
+     */
+    public DTMAxisIterator cloneIterator()
+    {
+      _isRestartable = false;  // must set to false for any clone
+
+      try
+      {
+        final AncestorIterator clone = (AncestorIterator) super.clone();
+
+        clone._startNode = _startNode;
+
+        // return clone.reset();
+        return clone;
+      }
+      catch (CloneNotSupportedException e)
+      {
+        throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_ITERATOR_CLONE_NOT_SUPPORTED, null)); //"Iterator clone not supported.");
+      }
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      m_realStartNode = node;
+
+      if (_isRestartable)
+      {
+        int nodeID = makeNodeIdentity(node);
+
+        if (!_includeSelf && node != DTM.NULL) {
+          nodeID = _parent(nodeID);
+          node = makeNodeHandle(nodeID);
+        }
+
+        _startNode = node;
+
+        while (nodeID != END) {
+          m_ancestors.addElement(node);
+          nodeID = _parent(nodeID);
+          node = makeNodeHandle(nodeID);
+        }
+        m_ancestorsPos = m_ancestors.size()-1;
+
+        _currentNode = (m_ancestorsPos>=0)
+                               ? m_ancestors.elementAt(m_ancestorsPos)
+                               : DTM.NULL;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Resets the iterator to the last start node.
+     *
+     * @return A DTMAxisIterator, which may or may not be the same as this
+     *         iterator.
+     */
+    public DTMAxisIterator reset()
+    {
+
+      m_ancestorsPos = m_ancestors.size()-1;
+
+      _currentNode = (m_ancestorsPos>=0) ? m_ancestors.elementAt(m_ancestorsPos)
+                                         : DTM.NULL;
+
+      return resetPosition();
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      int next = _currentNode;
+      
+      int pos = --m_ancestorsPos;
+
+      _currentNode = (pos >= 0) ? m_ancestors.elementAt(m_ancestorsPos)
+                                : DTM.NULL;
+      
+      return returnNode(next);
+    }
+
+    public void setMark() {
+        m_markedPos = m_ancestorsPos;
+    }
+
+    public void gotoMark() {
+        m_ancestorsPos = m_markedPos;
+        _currentNode = m_ancestorsPos>=0 ? m_ancestors.elementAt(m_ancestorsPos)
+                                         : DTM.NULL;
+    }
+  }  // end of AncestorIterator
+
+  /**
+   * Typed iterator that returns the ancestors of a given node.
+   */
+  public final class TypedAncestorIterator extends AncestorIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedAncestorIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedAncestorIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      m_realStartNode = node;
+
+      if (_isRestartable)
+      {
+        int nodeID = makeNodeIdentity(node);
+        int nodeType = _nodeType;
+
+        if (!_includeSelf && node != DTM.NULL) {
+          nodeID = _parent(nodeID);
+        }
+
+        _startNode = node;
+
+        if (nodeType >= DTM.NTYPES) {
+          while (nodeID != END) {
+            int eType = _exptype(nodeID);
+
+            if (eType == nodeType) {
+              m_ancestors.addElement(makeNodeHandle(nodeID));
+            }
+            nodeID = _parent(nodeID);
+          }
+        } else {
+          while (nodeID != END) {
+            int eType = _exptype(nodeID);
+
+            if ((eType >= DTM.NTYPES
+                    && m_expandedNameTable.getType(eType) == nodeType)
+                || (eType < DTM.NTYPES && eType == nodeType)) {
+              m_ancestors.addElement(makeNodeHandle(nodeID));
+            }
+            nodeID = _parent(nodeID);
+          }
+        }
+        m_ancestorsPos = m_ancestors.size()-1;
+
+        _currentNode = (m_ancestorsPos>=0)
+                               ? m_ancestors.elementAt(m_ancestorsPos)
+                               : DTM.NULL;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+  }  // end of TypedAncestorIterator
+
+  /**
+   * Iterator that returns the descendants of a given node.
+   */
+  public class DescendantIterator extends InternalAxisIteratorBase
+  {
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        node = makeNodeIdentity(node);
+        _startNode = node;
+
+        if (_includeSelf)
+          node--;
+
+        _currentNode = node;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Tell if this node identity is a descendant.  Assumes that
+     * the node info for the element has already been obtained.
+     *
+     * This one-sided test works only if the parent has been
+     * previously tested and is known to be a descendent. It fails if
+     * the parent is the _startNode's next sibling, or indeed any node
+     * that follows _startNode in document order.  That may suffice
+     * for this iterator, but it's not really an isDescendent() test.
+     * %REVIEW% rename?
+     *
+     * @param identity The index number of the node in question.
+     * @return true if the index is a descendant of _startNode.
+     */
+    protected boolean isDescendant(int identity)
+    {
+      return (_parent(identity) >= _startNode) || (_startNode == identity);
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      if (_startNode == NULL) {
+        return NULL;
+      }
+
+      if (_includeSelf && (_currentNode + 1) == _startNode)
+          return returnNode(makeNodeHandle(++_currentNode)); // | m_dtmIdent);
+
+      int node = _currentNode;
+      int type;
+
+      do {
+        node++;
+        type = _type(node);
+
+        if (NULL == type ||!isDescendant(node)) {
+          _currentNode = NULL;
+          return END;
+        }
+      } while(ATTRIBUTE_NODE == type || TEXT_NODE == type
+                 || NAMESPACE_NODE == type);
+
+      _currentNode = node;
+      return returnNode(makeNodeHandle(node));  // make handle.
+    }
+  
+    /**
+     * Reset.
+     *
+     */ 
+  public DTMAxisIterator reset()
+  {
+
+    final boolean temp = _isRestartable;
+
+    _isRestartable = true;
+
+    setStartNode(makeNodeHandle(_startNode));
+
+    _isRestartable = temp;
+
+    return this;
+  }
+    
+  }  // end of DescendantIterator
+
+  /**
+   * Typed iterator that returns the descendants of a given node.
+   */
+  public final class TypedDescendantIterator extends DescendantIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedDescendantIterator
+     *
+     *
+     * @param nodeType Extended type ID being requested.
+     */
+    public TypedDescendantIterator(int nodeType)
+    {
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int node;
+      int type;
+
+      if (_startNode == NULL) {
+        return NULL;
+      }
+
+      node = _currentNode;
+
+      do
+      {
+        node++;
+        type = _type(node);
+
+        if (NULL == type ||!isDescendant(node)) {
+          _currentNode = NULL;
+          return END;
+        }
+      }
+      while (type != _nodeType && _exptype(node) != _nodeType);
+
+      _currentNode = node;
+      return returnNode(makeNodeHandle(node));
+    }
+  }  // end of TypedDescendantIterator
+
+  /**
+   * Iterator that returns the descendants of a given node.
+   * I'm not exactly clear about this one... -sb
+   */
+  public class NthDescendantIterator extends DescendantIterator
+  {
+
+    /** The current nth position. */
+    int _pos;
+
+    /**
+     * Constructor NthDescendantIterator
+     *
+     *
+     * @param pos The nth position being requested.
+     */
+    public NthDescendantIterator(int pos)
+    {
+      _pos = pos;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      // I'm not exactly clear yet what this is doing... -sb
+      int node;
+
+      while ((node = super.next()) != END)
+      {
+        node = makeNodeIdentity(node);
+
+        int parent = _parent(node);
+        int child = _firstch(parent);
+        int pos = 0;
+
+        do
+        {
+          int type = _type(child);
+
+          if (ELEMENT_NODE == type)
+            pos++;
+        }
+        while ((pos < _pos) && (child = _nextsib(child)) != END);
+
+        if (node == child)
+          return node;
+      }
+
+      return (END);
+    }
+  }  // end of NthDescendantIterator
+
+  /**
+   * Class SingletonIterator.
+   */
+  public class SingletonIterator extends InternalAxisIteratorBase
+  {
+
+    /** (not sure yet what this is.  -sb)  (sc & sb remove final to compile in JDK 1.1.8) */
+    private boolean _isConstant;
+
+    /**
+     * Constructor SingletonIterator
+     *
+     */
+    public SingletonIterator()
+    {
+      this(Integer.MIN_VALUE, false);
+    }
+
+    /**
+     * Constructor SingletonIterator
+     *
+     *
+     * @param node The node handle to return.
+     */
+    public SingletonIterator(int node)
+    {
+      this(node, false);
+    }
+
+    /**
+     * Constructor SingletonIterator
+     *
+     *
+     * @param node the node handle to return.
+     * @param constant (Not sure what this is yet.  -sb)
+     */
+    public SingletonIterator(int node, boolean constant)
+    {
+      _currentNode = _startNode = node;
+      _isConstant = constant;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isConstant)
+      {
+        _currentNode = _startNode;
+
+        return resetPosition();
+      }
+      else if (_isRestartable)
+      {
+        _currentNode = _startNode = node;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Resets the iterator to the last start node.
+     *
+     * @return A DTMAxisIterator, which may or may not be the same as this
+     *         iterator.
+     */
+    public DTMAxisIterator reset()
+    {
+
+      if (_isConstant)
+      {
+        _currentNode = _startNode;
+
+        return resetPosition();
+      }
+      else
+      {
+        final boolean temp = _isRestartable;
+
+        _isRestartable = true;
+
+        setStartNode(_startNode);
+
+        _isRestartable = temp;
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      final int result = _currentNode;
+
+      _currentNode = END;
+
+      return returnNode(result);
+    }
+  }  // end of SingletonIterator
+
+  /**
+   * Iterator that returns a given node only if it is of a given type.
+   */
+  public final class TypedSingletonIterator extends SingletonIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedSingletonIterator
+     *
+     *
+     * @param nodeType The extended type ID being requested.
+     */
+    public TypedSingletonIterator(int nodeType)
+    {
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      //final int result = super.next();
+      final int result = _currentNode;
+      int nodeType = _nodeType;
+
+      _currentNode = END;
+
+      if (nodeType >= DTM.NTYPES) {
+        if (getExpandedTypeID(result) == nodeType) {
+          return returnNode(result);
+        }
+      } else {
+        if (getNodeType(result) == nodeType) {
+          return returnNode(result);
+        }
+      }
+
+      return NULL;
+    }
+  }  // end of TypedSingletonIterator
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBaseTraversers.java b/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBaseTraversers.java
new file mode 100644
index 0000000..0984fec
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMDefaultBaseTraversers.java
@@ -0,0 +1,1744 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMDefaultBaseTraversers.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.*;
+
+import javax.xml.transform.Source;
+
+import org.apache.xml.utils.XMLStringFactory;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+/**
+ * This class implements the traversers for DTMDefaultBase.
+ *
+ * PLEASE NOTE that the public interface for all traversers should be
+ * in terms of DTM Node Handles... but they may use the internal node
+ * identity indices within their logic, for efficiency's sake. Be very
+ * careful to avoid confusing these when maintaining this code.
+ * */
+public abstract class DTMDefaultBaseTraversers extends DTMDefaultBase
+{
+
+  /**
+   * Construct a DTMDefaultBaseTraversers object from a DOM node.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param source The object that is used to specify the construction source.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may
+   *                         be null.
+   * @param xstringfactory The factory to use for creating XMLStrings.
+   * @param doIndexing true if the caller considers it worth it to use
+   *                   indexing schemes.
+   */
+  public DTMDefaultBaseTraversers(DTMManager mgr, Source source,
+                                  int dtmIdentity,
+                                  DTMWSFilter whiteSpaceFilter,
+                                  XMLStringFactory xstringfactory,
+                                  boolean doIndexing)
+  {
+    super(mgr, source, dtmIdentity, whiteSpaceFilter, xstringfactory,
+          doIndexing);
+  }
+
+  /**
+   * Construct a DTMDefaultBaseTraversers object from a DOM node.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param source The object that is used to specify the construction source.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may
+   *                         be null.
+   * @param xstringfactory The factory to use for creating XMLStrings.
+   * @param doIndexing true if the caller considers it worth it to use
+   *                   indexing schemes.
+   * @param blocksize The block size of the DTM.
+   * @param usePrevsib true if we want to build the previous sibling node array.
+   * @param newNameTable true if we want to use a new ExpandedNameTable for this DTM.
+   */
+  public DTMDefaultBaseTraversers(DTMManager mgr, Source source,
+                                  int dtmIdentity,
+                                  DTMWSFilter whiteSpaceFilter,
+                                  XMLStringFactory xstringfactory,
+                                  boolean doIndexing,
+                                  int blocksize,
+                                  boolean usePrevsib,
+                                  boolean newNameTable)
+  {
+    super(mgr, source, dtmIdentity, whiteSpaceFilter, xstringfactory,
+          doIndexing, blocksize, usePrevsib, newNameTable);
+  }
+
+  /**
+   * This returns a stateless "traverser", that can navigate
+   * over an XPath axis, though perhaps not in document order.
+   *
+   * @param axis One of Axes.ANCESTORORSELF, etc.
+   *
+   * @return A DTMAxisTraverser, or null if the given axis isn't supported.
+   */
+  public DTMAxisTraverser getAxisTraverser(final int axis)
+  {
+
+    DTMAxisTraverser traverser;
+
+    if (null == m_traversers)  // Cache of stateless traversers for this DTM
+    {
+      m_traversers = new DTMAxisTraverser[Axis.getNamesLength()];
+      traverser = null;
+    }
+    else
+    {
+      traverser = m_traversers[axis];  // Share/reuse existing traverser
+
+      if (traverser != null)
+        return traverser;
+    }
+
+    switch (axis)  // Generate new traverser
+    {
+    case Axis.ANCESTOR :
+      traverser = new AncestorTraverser();
+      break;
+    case Axis.ANCESTORORSELF :
+      traverser = new AncestorOrSelfTraverser();
+      break;
+    case Axis.ATTRIBUTE :
+      traverser = new AttributeTraverser();
+      break;
+    case Axis.CHILD :
+      traverser = new ChildTraverser();
+      break;
+    case Axis.DESCENDANT :
+      traverser = new DescendantTraverser();
+      break;
+    case Axis.DESCENDANTORSELF :
+      traverser = new DescendantOrSelfTraverser();
+      break;
+    case Axis.FOLLOWING :
+      traverser = new FollowingTraverser();
+      break;
+    case Axis.FOLLOWINGSIBLING :
+      traverser = new FollowingSiblingTraverser();
+      break;
+    case Axis.NAMESPACE :
+      traverser = new NamespaceTraverser();
+      break;
+    case Axis.NAMESPACEDECLS :
+      traverser = new NamespaceDeclsTraverser();
+      break;
+    case Axis.PARENT :
+      traverser = new ParentTraverser();
+      break;
+    case Axis.PRECEDING :
+      traverser = new PrecedingTraverser();
+      break;
+    case Axis.PRECEDINGSIBLING :
+      traverser = new PrecedingSiblingTraverser();
+      break;
+    case Axis.SELF :
+      traverser = new SelfTraverser();
+      break;
+    case Axis.ALL :
+      traverser = new AllFromRootTraverser();
+      break;
+    case Axis.ALLFROMNODE :
+      traverser = new AllFromNodeTraverser();
+      break;
+    case Axis.PRECEDINGANDANCESTOR :
+      traverser = new PrecedingAndAncestorTraverser();
+      break;
+    case Axis.DESCENDANTSFROMROOT :
+      traverser = new DescendantFromRootTraverser();
+      break;
+    case Axis.DESCENDANTSORSELFFROMROOT :
+      traverser = new DescendantOrSelfFromRootTraverser();
+      break;
+    case Axis.ROOT :
+      traverser = new RootTraverser();
+      break;
+    case Axis.FILTEREDLIST :
+      return null; // Don't want to throw an exception for this one.
+    default :
+      throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_UNKNOWN_AXIS_TYPE, new Object[]{Integer.toString(axis)})); //"Unknown axis traversal type: "+axis);
+    }
+
+    if (null == traverser)
+      throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_AXIS_TRAVERSER_NOT_SUPPORTED, new Object[]{Axis.getNames(axis)}));
+      // "Axis traverser not supported: "
+      //                       + Axis.names[axis]);
+
+    m_traversers[axis] = traverser;
+
+    return traverser;
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class AncestorTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node if this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+			return getParent(current);
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+			// Process using identities
+      current = makeNodeIdentity(current);
+
+      while (DTM.NULL != (current = m_parent.elementAt(current)))
+      {
+        if (m_exptype.elementAt(current) == expandedTypeID)
+          return makeNodeHandle(current);
+      }
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class AncestorOrSelfTraverser extends AncestorTraverser
+  {
+
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  To see if
+     * the self node should be processed, use this function.
+     *
+     * @param context The context node of this traversal.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+      return context;
+    }
+
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  To see if
+     * the self node should be processed, use this function.  If the context
+     * node does not match the expanded type ID, this function will return
+     * false.
+     *
+     * @param context The context node of this traversal.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+			return (getExpandedTypeID(context) == expandedTypeID)
+             ? context : next(context, context, expandedTypeID);
+    }
+  }
+
+  /**
+   * Implements traversal of the Attribute access
+   */
+  private class AttributeTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+      return (context == current)
+             ? getFirstAttribute(context) : getNextAttribute(current);
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+
+      current = (context == current)
+                ? getFirstAttribute(context) : getNextAttribute(current);
+
+      do
+      {
+        if (getExpandedTypeID(current) == expandedTypeID)
+          return current;
+      }
+      while (DTM.NULL != (current = getNextAttribute(current)));
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class ChildTraverser extends DTMAxisTraverser
+  {
+    
+    /**
+     * Get the next indexed node that matches the expanded type ID.  Before 
+     * calling this function, one should first call 
+     * {@link #isIndexed(int) isIndexed} to make sure that the index can 
+     * contain nodes that match the given expanded type ID.
+     *
+     * @param axisRoot The root identity of the axis.
+     * @param nextPotential The node found must match or occur after this node.
+     * @param expandedTypeID The expanded type ID for the request.
+     *
+     * @return The node ID or NULL if not found.
+     */
+    protected int getNextIndexed(int axisRoot, int nextPotential,
+                                 int expandedTypeID)
+    {
+
+      int nsIndex = m_expandedNameTable.getNamespaceID(expandedTypeID);
+      int lnIndex = m_expandedNameTable.getLocalNameID(expandedTypeID);
+
+      for (; ; ) 
+      {
+        int nextID = findElementFromIndex(nsIndex, lnIndex, nextPotential);
+
+        if (NOTPROCESSED != nextID)
+        {
+          int parentID = m_parent.elementAt(nextID);
+          
+          // Is it a child?
+          if(parentID == axisRoot)
+            return nextID;
+          
+          // If the parent occured before the subtree root, then 
+          // we know it is past the child axis.
+          if(parentID < axisRoot)
+              return NULL;
+          
+          // Otherwise, it could be a descendant below the subtree root 
+          // children, or it could be after the subtree root.  So we have 
+          // to climb up until the parent is less than the subtree root, in 
+          // which case we return NULL, or until it is equal to the subtree 
+          // root, in which case we continue to look.
+          do
+          {
+            parentID = m_parent.elementAt(parentID);
+            if(parentID < axisRoot)
+              return NULL;
+          }
+            while(parentID > axisRoot);
+          
+          // System.out.println("Found node via index: "+first);
+          nextPotential = nextID+1;
+          continue;
+        }
+
+        nextNode();
+        
+        if(!(m_nextsib.elementAt(axisRoot) == NOTPROCESSED))
+          break;
+      }
+
+      return DTM.NULL;
+    }
+        
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  So to traverse 
+     * an axis, the first function must be used to get the first node.
+     *
+     * <p>This method needs to be overloaded only by those axis that process
+     * the self node. <\p>
+     *
+     * @param context The context node of this traversal. This is the point
+     * that the traversal starts from.
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+      return getFirstChild(context);
+    }
+  
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  So to traverse 
+     * an axis, the first function must be used to get the first node.
+     *
+     * <p>This method needs to be overloaded only by those axis that process
+     * the self node. <\p>
+     *
+     * @param context The context node of this traversal. This is the point
+     * of origin for the traversal -- its "root node" or starting point.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+      if(true)
+      {
+        int identity = makeNodeIdentity(context);
+        
+        int firstMatch = getNextIndexed(identity, _firstch(identity),
+                                 expandedTypeID);
+       
+        return makeNodeHandle(firstMatch);
+      }
+      else
+      {
+				// %REVIEW% Dead code. Eliminate?
+        for (int current = _firstch(makeNodeIdentity(context)); 
+             DTM.NULL != current; 
+             current = _nextsib(current)) 
+        {
+          if (m_exptype.elementAt(current) == expandedTypeID)
+              return makeNodeHandle(current);
+        }
+        return NULL;
+      }
+    }
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+      return getNextSibling(current);
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+			// Process in Identifier space
+      for (current = _nextsib(makeNodeIdentity(current)); 
+           DTM.NULL != current; 
+           current = _nextsib(current)) 
+      {
+        if (m_exptype.elementAt(current) == expandedTypeID)
+            return makeNodeHandle(current);
+      }
+      
+      return NULL;
+    }
+  }
+
+  /**
+   * Super class for derived classes that want a convenient way to access 
+   * the indexing mechanism.
+   */
+  private abstract class IndexedDTMAxisTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Tell if the indexing is on and the given expanded type ID matches 
+     * what is in the indexes.  Derived classes should call this before 
+     * calling {@link #getNextIndexed(int, int, int) getNextIndexed} method.
+     *
+     * @param expandedTypeID The expanded type ID being requested.
+     *
+     * @return true if it is OK to call the 
+     *         {@link #getNextIndexed(int, int, int) getNextIndexed} method.
+     */
+    protected final boolean isIndexed(int expandedTypeID)
+    {
+      return (m_indexing
+              && ExpandedNameTable.ELEMENT
+                 == m_expandedNameTable.getType(expandedTypeID)); 
+    }
+
+    /**
+     * Tell if a node is outside the axis being traversed.  This method must be 
+     * implemented by derived classes, and must be robust enough to handle any 
+     * node that occurs after the axis root.
+     *
+     * @param axisRoot The root identity of the axis.
+     * @param identity The node in question.
+     *
+     * @return true if the given node falls outside the axis being traversed.
+     */
+    protected abstract boolean isAfterAxis(int axisRoot, int identity);
+
+    /**
+     * Tell if the axis has been fully processed to tell if a the wait for 
+     * an arriving node should terminate.  This method must be implemented 
+     * be a derived class.
+     *
+     * @param axisRoot The root identity of the axis.
+     *
+     * @return true if the axis has been fully processed.
+     */
+    protected abstract boolean axisHasBeenProcessed(int axisRoot);
+
+    /**
+     * Get the next indexed node that matches the expanded type ID.  Before 
+     * calling this function, one should first call 
+     * {@link #isIndexed(int) isIndexed} to make sure that the index can 
+     * contain nodes that match the given expanded type ID.
+     *
+     * @param axisRoot The root identity of the axis.
+     * @param nextPotential The node found must match or occur after this node.
+     * @param expandedTypeID The expanded type ID for the request.
+     *
+     * @return The node ID or NULL if not found.
+     */
+    protected int getNextIndexed(int axisRoot, int nextPotential,
+                                 int expandedTypeID)
+    {
+
+      int nsIndex = m_expandedNameTable.getNamespaceID(expandedTypeID);
+      int lnIndex = m_expandedNameTable.getLocalNameID(expandedTypeID);
+
+      while(true)
+      {
+        int next = findElementFromIndex(nsIndex, lnIndex, nextPotential);
+
+        if (NOTPROCESSED != next)
+        {
+          if (isAfterAxis(axisRoot, next))
+            return NULL;
+
+          // System.out.println("Found node via index: "+first);
+          return next;
+        }
+        else if(axisHasBeenProcessed(axisRoot))
+          break;
+
+        nextNode();
+      }
+
+      return DTM.NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class DescendantTraverser extends IndexedDTMAxisTraverser
+  {
+    /**
+     * Get the first potential identity that can be returned.  This should 
+     * be overridded by classes that need to return the self node.
+     *
+     * @param identity The node identity of the root context of the traversal.
+     *
+     * @return The first potential node that can be in the traversal.
+     */
+    protected int getFirstPotential(int identity)
+    {
+      return identity + 1;
+    }
+    
+    /**
+     * Tell if the axis has been fully processed to tell if a the wait for 
+     * an arriving node should terminate.
+     *
+     * @param axisRoot The root identity of the axis.
+     *
+     * @return true if the axis has been fully processed.
+     */
+    protected boolean axisHasBeenProcessed(int axisRoot)
+    {
+      return !(m_nextsib.elementAt(axisRoot) == NOTPROCESSED);
+    }
+    
+    /**
+     * Get the subtree root identity from the handle that was passed in by 
+     * the caller.  Derived classes may override this to change the root 
+     * context of the traversal.
+     * 
+     * @param handle handle to the root context.
+     * @return identity of the root of the subtree.
+     */
+    protected int getSubtreeRoot(int handle)
+    {
+      return makeNodeIdentity(handle);
+    }
+
+    /**
+     * Tell if this node identity is a descendant.  Assumes that
+     * the node info for the element has already been obtained.
+     *
+     * %REVIEW% This is really parentFollowsRootInDocumentOrder ...
+     * which fails if the parent starts after the root ends.
+     * May be sufficient for this class's logic, but misleadingly named!
+     *
+     * @param subtreeRootIdentity The root context of the subtree in question.
+     * @param identity The index number of the node in question.
+     * @return true if the index is a descendant of _startNode.
+     */
+    protected boolean isDescendant(int subtreeRootIdentity, int identity)
+    {
+      return _parent(identity) >= subtreeRootIdentity;
+    }
+
+    /**
+     * Tell if a node is outside the axis being traversed.  This method must be 
+     * implemented by derived classes, and must be robust enough to handle any 
+     * node that occurs after the axis root.
+     *
+     * @param axisRoot The root identity of the axis.
+     * @param identity The node in question.
+     *
+     * @return true if the given node falls outside the axis being traversed.
+     */
+    protected boolean isAfterAxis(int axisRoot, int identity)
+    {   
+      // %REVIEW% Is there *any* cheaper way to do this?
+			// Yes. In ID space, compare to axisRoot's successor
+			// (next-sib or ancestor's-next-sib). Probably shallower search.
+      do
+      {
+        if(identity == axisRoot)
+          return false;
+        identity = m_parent.elementAt(identity);
+      }
+        while(identity >= axisRoot);
+        
+      return true;
+    }
+
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  So to traverse
+     * an axis, the first function must be used to get the first node.
+     *
+     * <p>This method needs to be overloaded only by those axis that process
+     * the self node. <\p>
+     *
+     * @param context The context node of this traversal. This is the point
+     * of origin for the traversal -- its "root node" or starting point.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+
+      if (isIndexed(expandedTypeID))
+      {
+        int identity = getSubtreeRoot(context);
+        int firstPotential = getFirstPotential(identity);
+
+        return makeNodeHandle(getNextIndexed(identity, firstPotential, expandedTypeID));
+      }
+
+      return next(context, context, expandedTypeID);
+    }
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+
+      int subtreeRootIdent = getSubtreeRoot(context);
+
+      for (current = makeNodeIdentity(current) + 1; ; current++)
+      {
+        int type = _type(current);  // may call nextNode()
+
+        if (!isDescendant(subtreeRootIdent, current))
+          return NULL;
+
+        if (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type)
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+
+      int subtreeRootIdent = getSubtreeRoot(context);
+
+      current = makeNodeIdentity(current) + 1;
+
+      if (isIndexed(expandedTypeID))
+      {
+        return makeNodeHandle(getNextIndexed(subtreeRootIdent, current, expandedTypeID));
+      }
+
+      for (; ; current++)
+      {
+        int exptype = _exptype(current);  // may call nextNode()
+
+        if (!isDescendant(subtreeRootIdent, current))
+          return NULL;
+
+        if (exptype != expandedTypeID)
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class DescendantOrSelfTraverser extends DescendantTraverser
+  {
+
+    /**
+     * Get the first potential identity that can be returned, which is the 
+     * axis context, in this case.
+     *
+     * @param identity The node identity of the root context of the traversal.
+     *
+     * @return The axis context.
+     */
+    protected int getFirstPotential(int identity)
+    {
+      return identity;
+    }
+
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  To see if
+     * the self node should be processed, use this function.
+     *
+     * @param context The context node of this traversal.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+      return context;
+    }
+  }
+
+  /**
+   * Implements traversal of the entire subtree, including the root node.
+   */
+  private class AllFromNodeTraverser extends DescendantOrSelfTraverser
+  {
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+
+      int subtreeRootIdent = makeNodeIdentity(context);
+
+      for (current = makeNodeIdentity(current) + 1; ; current++)
+      {
+        // Trickological code: _exptype() has the side-effect of
+        // running nextNode until the specified node has been loaded,
+        // and thus can be used to ensure that incremental construction of
+        // the DTM has gotten this far. Using it just for that side-effect
+        // is quite a kluge...
+        _exptype(current);  // make sure it's here.
+
+        if (!isDescendant(subtreeRootIdent, current))
+          return NULL;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+    }
+  }
+
+  /**
+   * Implements traversal of the following access, in document order.
+   */
+  private class FollowingTraverser extends DescendantTraverser
+  {
+
+    /**
+     * Get the first of the following.
+     *
+     * @param context The context node of this traversal. This is the point
+     * that the traversal starts from.
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+			// Compute in ID space
+			context=makeNodeIdentity(context);
+
+      int first;
+      int type = _type(context);
+
+      if ((DTM.ATTRIBUTE_NODE == type) || (DTM.NAMESPACE_NODE == type))
+      {
+        context = _parent(context);
+        first = _firstch(context);
+
+        if (NULL != first)
+          return makeNodeHandle(first);
+      }
+
+      do
+      {
+        first = _nextsib(context);
+
+        if (NULL == first)
+          context = _parent(context);
+      }
+      while (NULL == first && NULL != context);
+
+      return makeNodeHandle(first);
+    }
+
+    /**
+     * Get the first of the following.
+     *
+     * @param context The context node of this traversal. This is the point
+     * of origin for the traversal -- its "root node" or starting point.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+			// %REVIEW% This looks like it might want shift into identity space
+			// to avoid repeated conversion in the individual functions
+      int first;
+      int type = getNodeType(context);
+
+      if ((DTM.ATTRIBUTE_NODE == type) || (DTM.NAMESPACE_NODE == type))
+      {
+        context = getParent(context);
+        first = getFirstChild(context);
+
+        if (NULL != first)
+        {
+          if (getExpandedTypeID(first) == expandedTypeID)
+            return first;
+          else
+            return next(context, first, expandedTypeID);
+        }
+      }
+
+      do
+      {
+        first = getNextSibling(context);
+
+        if (NULL == first)
+          context = getParent(context);
+        else
+        {
+          if (getExpandedTypeID(first) == expandedTypeID)
+            return first;
+          else
+            return next(context, first, expandedTypeID);
+        }
+      }
+      while (NULL == first && NULL != context);
+
+      return first;
+    }
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+			// Compute in identity space
+			current=makeNodeIdentity(current);
+
+      while (true)
+      {
+        current++; // Only works on IDs, not handles.
+
+				// %REVIEW% Are we using handles or indexes?
+        int type = _type(current);  // may call nextNode()
+
+        if (NULL == type)
+          return NULL;
+
+        if (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type)
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+			// Compute in ID space
+			current=makeNodeIdentity(current);
+
+      while (true)
+      {
+        current++;
+
+        int etype = _exptype(current);  // may call nextNode()
+
+        if (NULL == etype)
+          return NULL;
+
+        if (etype != expandedTypeID)
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class FollowingSiblingTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+      return getNextSibling(current);
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+
+      while (DTM.NULL != (current = getNextSibling(current)))
+      {
+        if (getExpandedTypeID(current) == expandedTypeID)
+          return current;
+      }
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class NamespaceDeclsTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+
+      return (context == current)
+             ? getFirstNamespaceNode(context, false)
+             : getNextNamespaceNode(context, current, false);
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+
+      current = (context == current)
+                ? getFirstNamespaceNode(context, false)
+                : getNextNamespaceNode(context, current, false);
+
+      do
+      {
+        if (getExpandedTypeID(current) == expandedTypeID)
+          return current;
+      }
+      while (DTM.NULL
+             != (current = getNextNamespaceNode(context, current, false)));
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class NamespaceTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+
+      return (context == current)
+             ? getFirstNamespaceNode(context, true)
+             : getNextNamespaceNode(context, current, true);
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+
+      current = (context == current)
+                ? getFirstNamespaceNode(context, true)
+                : getNextNamespaceNode(context, current, true);
+
+      do
+      {
+        if (getExpandedTypeID(current) == expandedTypeID)
+          return current;
+      }
+      while (DTM.NULL
+             != (current = getNextNamespaceNode(context, current, true)));
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class ParentTraverser extends DTMAxisTraverser
+  {
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  So to traverse 
+     * an axis, the first function must be used to get the first node.
+     *
+     * <p>This method needs to be overloaded only by those axis that process
+     * the self node. <\p>
+     *
+     * @param context The context node of this traversal. This is the point
+     * that the traversal starts from.
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+      return getParent(context);
+    }
+  
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  So to traverse 
+     * an axis, the first function must be used to get the first node.
+     *
+     * <p>This method needs to be overloaded only by those axis that process
+     * the self node. <\p>
+     *
+     * @param context The context node of this traversal. This is the point
+     * of origin for the traversal -- its "root node" or starting point.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int current, int expandedTypeID)
+    {
+			// Compute in ID space
+      current = makeNodeIdentity(current);
+
+      while (NULL != (current = m_parent.elementAt(current)))
+      {
+        if (m_exptype.elementAt(current) == expandedTypeID)
+          return makeNodeHandle(current);
+      }
+
+      return NULL;
+    }
+
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+
+      return NULL;
+    }
+    
+
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class PrecedingTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Tell if the current identity is an ancestor of the context identity.
+     * This is an expensive operation, made worse by the stateless traversal.
+     * But the preceding axis is used fairly infrequently.
+     *
+     * @param contextIdent The context node of the axis traversal.
+     * @param currentIdent The node in question.
+     * @return true if the currentIdent node is an ancestor of contextIdent.
+     */
+    protected boolean isAncestor(int contextIdent, int currentIdent)
+    {
+			// %REVIEW% See comments in IsAfterAxis; using the "successor" of
+			// contextIdent is probably more efficient.
+      for (contextIdent = m_parent.elementAt(contextIdent); DTM.NULL != contextIdent;
+              contextIdent = m_parent.elementAt(contextIdent))
+      {
+        if (contextIdent == currentIdent)
+          return true;
+      }
+
+      return false;
+    }
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+			// compute in ID space
+      int subtreeRootIdent = makeNodeIdentity(context);
+
+      for (current = makeNodeIdentity(current) - 1; current >= 0; current--)
+      {
+        short type = _type(current);
+
+        if (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type
+                || isAncestor(subtreeRootIdent, current))
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+
+      return NULL;
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+			// Compute in ID space
+      int subtreeRootIdent = makeNodeIdentity(context);
+
+      for (current = makeNodeIdentity(current) - 1; current >= 0; current--)
+      {
+        int exptype = m_exptype.elementAt(current);
+
+        if (exptype != expandedTypeID
+                || isAncestor(subtreeRootIdent, current))
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor and the Preceding axis,
+   * in reverse document order.
+   */
+  private class PrecedingAndAncestorTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+			// Compute in ID space
+      int subtreeRootIdent = makeNodeIdentity(context );
+
+      for (current = makeNodeIdentity(current) - 1; current >= 0; current--)
+      {
+        short type = _type(current);
+
+        if (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type)
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+
+      return NULL;
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+			// Compute in ID space
+      int subtreeRootIdent = makeNodeIdentity(context);
+
+      for (current = makeNodeIdentity(current) - 1; current >= 0; current--)
+      {
+        int exptype = m_exptype.elementAt(current);
+
+        if (exptype != expandedTypeID)
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class PrecedingSiblingTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+      return getPreviousSibling(current);
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+
+      while (DTM.NULL != (current = getPreviousSibling(current)))
+      {
+        if (getExpandedTypeID(current) == expandedTypeID)
+          return current;
+      }
+
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Self axis.
+   */
+  private class SelfTraverser extends DTMAxisTraverser
+  {
+
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  To see if
+     * the self node should be processed, use this function.
+     *
+     * @param context The context node of this traversal.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+      return context;
+    }
+
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  To see if
+     * the self node should be processed, use this function.  If the context
+     * node does not match the expanded type ID, this function will return
+     * false.
+     *
+     * @param context The context node of this traversal.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+      return (getExpandedTypeID(context) == expandedTypeID) ? context : NULL;
+    }
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return Always return NULL for this axis.
+     */
+    public int next(int context, int current)
+    {
+      return NULL;
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+      return NULL;
+    }
+  }
+
+  /**
+   * Implements traversal of the Ancestor access, in reverse document order.
+   */
+  private class AllFromRootTraverser extends AllFromNodeTraverser
+  {
+
+    /**
+     * Return the root.
+     *
+     * @param context The context node of this traversal.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+      return getDocumentRoot(context);
+    }
+
+    /**
+     * Return the root if it matches the expanded type ID.
+     *
+     * @param context The context node of this traversal.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+      return (getExpandedTypeID(getDocumentRoot(context)) == expandedTypeID)
+             ? context : next(context, context, expandedTypeID);
+    }
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current)
+    {
+			// Compute in ID space
+      int subtreeRootIdent = makeNodeIdentity(context);
+
+      for (current = makeNodeIdentity(current) + 1; ; current++)
+      {
+				// Kluge test: Just make sure +1 yielded a real node
+        int type = _type(current);  // may call nextNode()
+        if (type == NULL)
+          return NULL;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+			// Compute in ID space
+      int subtreeRootIdent = makeNodeIdentity(context);
+
+      for (current = makeNodeIdentity(current) + 1; ; current++)
+      {
+        int exptype = _exptype(current);  // may call nextNode()
+
+        if (exptype == NULL)
+          return NULL;
+
+        if (exptype != expandedTypeID)
+          continue;
+
+        return makeNodeHandle(current);  // make handle.
+      }
+    }
+  }
+
+  /**
+   * Implements traversal of the Self axis.
+   */
+  private class RootTraverser extends AllFromRootTraverser
+  {
+    /**
+     * Return the root if it matches the expanded type ID,
+     * else return null (nothing found)
+     *
+     * @param context The context node of this traversal.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+      int root=getDocumentRoot(context);
+      return (getExpandedTypeID(root) == expandedTypeID)
+	? root : NULL;
+    }
+
+    /**
+     * Traverse to the next node after the current node.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     *
+     * @return Always return NULL for this axis.
+     */
+    public int next(int context, int current)
+    {
+      return NULL;
+    }
+
+    /**
+     * Traverse to the next node after the current node that is matched
+     * by the expanded type ID.
+     *
+     * @param context The context node of this iteration.
+     * @param current The current node of the iteration.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the next node in the iteration, or DTM.NULL.
+     */
+    public int next(int context, int current, int expandedTypeID)
+    {
+      return NULL;
+    }
+  }
+
+  /**
+   * A non-xpath axis, returns all nodes that aren't namespaces or attributes,
+   * from and including the root.
+   */
+  private class DescendantOrSelfFromRootTraverser extends DescendantTraverser
+  {
+
+    /**
+     * Get the first potential identity that can be returned, which is the axis 
+     * root context in this case.
+     *
+     * @param identity The node identity of the root context of the traversal.
+     *
+     * @return The identity argument.
+     */
+    protected int getFirstPotential(int identity)
+    {
+      return identity;
+    }
+
+    /**
+     * Get the first potential identity that can be returned.
+     * @param handle handle to the root context.
+     * @return identity of the root of the subtree.
+     */
+    protected int getSubtreeRoot(int handle)
+    {
+			// %REVIEW% Shouldn't this always be 0?
+      return makeNodeIdentity(getDocument());
+    }
+
+    /**
+     * Return the root.
+     *
+     * @param context The context node of this traversal.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+      return getDocumentRoot(context);
+    }
+    
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  So to traverse
+     * an axis, the first function must be used to get the first node.
+     *
+     * <p>This method needs to be overloaded only by those axis that process
+     * the self node. <\p>
+     *
+     * @param context The context node of this traversal. This is the point
+     * of origin for the traversal -- its "root node" or starting point.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+      if (isIndexed(expandedTypeID))
+      {
+        int identity = 0;
+        int firstPotential = getFirstPotential(identity);
+
+        return makeNodeHandle(getNextIndexed(identity, firstPotential, expandedTypeID));
+      }
+
+      int root = first(context); 
+      return next(root, root, expandedTypeID);
+    }
+  }
+  
+  /**
+   * A non-xpath axis, returns all nodes that aren't namespaces or attributes,
+   * from but not including the root.
+   */
+  private class DescendantFromRootTraverser extends DescendantTraverser
+  {
+
+    /**
+     * Get the first potential identity that can be returned, which is the axis 
+     * root context in this case.
+     *
+     * @param identity The node identity of the root context of the traversal.
+     *
+     * @return The identity argument.
+     */
+    protected int getFirstPotential(int identity)
+    {
+      return _firstch(0);
+    }
+
+    /**
+     * Get the first potential identity that can be returned.
+     * @param handle handle to the root context.
+     * @return identity of the root of the subtree.
+     */
+    protected int getSubtreeRoot(int handle)
+    {
+      return 0;
+    }
+
+    /**
+     * Return the root.
+     *
+     * @param context The context node of this traversal.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context)
+    {
+      return makeNodeHandle(_firstch(0));
+    }
+    
+    /**
+     * By the nature of the stateless traversal, the context node can not be
+     * returned or the iteration will go into an infinate loop.  So to traverse
+     * an axis, the first function must be used to get the first node.
+     *
+     * <p>This method needs to be overloaded only by those axis that process
+     * the self node. <\p>
+     *
+     * @param context The context node of this traversal. This is the point
+     * of origin for the traversal -- its "root node" or starting point.
+     * @param expandedTypeID The expanded type ID that must match.
+     *
+     * @return the first node in the traversal.
+     */
+    public int first(int context, int expandedTypeID)
+    {
+      if (isIndexed(expandedTypeID))
+      {
+        int identity = 0; 
+        int firstPotential = getFirstPotential(identity);
+
+        return makeNodeHandle(getNextIndexed(identity, firstPotential, expandedTypeID));
+      }
+
+      int root = getDocumentRoot(context); 
+      return next(root, root, expandedTypeID);
+    }
+    
+  }
+
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMDocumentImpl.java b/src/main/java/org/apache/xml/dtm/ref/DTMDocumentImpl.java
new file mode 100644
index 0000000..a564115
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMDocumentImpl.java
@@ -0,0 +1,2411 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMDocumentImpl.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import javax.xml.transform.SourceLocator;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisIterator;
+import org.apache.xml.dtm.DTMAxisTraverser;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.dtm.DTMWSFilter;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * This is the implementation of the DTM document interface.  It receives
+ * requests from an XML content handler similar to that of an XML DOM or SAX parser
+ * to store information from the xml document in an array based
+ * dtm table structure.  This informtion is used later for document navigation,
+ * query, and SAX event dispatch functions. The DTM can also be used directly as a
+ * document composition model for an application.  The requests received are:
+ * <ul>
+ * <li>initiating DTM to set the doc handle</li>
+ * <li>resetting DTM for data structure reuse</li>
+ * <li>hinting the end of document to adjust the end of data structure pointers</li>
+ * <li>createnodes (element, comment, text, attribute, ....)</li>
+ * <li>hinting the end of an element to patch parent and siblings<li>
+ * <li>setting application provided symbol name stringpool data structures</li>
+ * </ul>
+ * <p>State: In progress!!</p>
+ *
+ * %REVIEW% I _think_ the SAX convention is that "no namespace" is expressed
+ * as "" rather than as null (which is the DOM's convention). What should
+ * DTM expect? What should it do with the other?
+ *
+ * <p>Origin: the implemention is a composite logic based on the DTM of XalanJ1 and
+ *     DocImpl, DocumentImpl, ElementImpl, TextImpl, etc. of XalanJ2</p>
+ */
+public class DTMDocumentImpl
+implements DTM, org.xml.sax.ContentHandler, org.xml.sax.ext.LexicalHandler
+{
+
+        // Number of lower bits used to represent node index.
+        protected static final byte DOCHANDLE_SHIFT = 22;
+        // Masks the lower order of node handle.
+        // Same as {@link DTMConstructor.IDENT_NODE_DEFAULT}
+        protected static final int NODEHANDLE_MASK = (1 << (DOCHANDLE_SHIFT + 1)) - 1;
+        // Masks the higher order Document handle
+        // Same as {@link DTMConstructor.IDENT_DOC_DEFAULT}
+        protected static final int DOCHANDLE_MASK = -1 - NODEHANDLE_MASK;
+
+        int m_docHandle = NULL;		 // masked document handle for this dtm document
+        int m_docElement = NULL;	 // nodeHandle to the root of the actual dtm doc content
+
+        // Context for parse-and-append operations
+        int currentParent = 0;			// current parent - default is document root
+        int previousSibling = 0;		// previous sibling - no previous sibling
+        protected int m_currentNode = -1;		// current node
+
+        // The tree under construction can itself be used as
+        // the element stack, so m_elemStack isn't needed.
+        //protected Stack m_elemStack = new Stack();	 // element stack
+
+        private boolean previousSiblingWasParent = false;
+        // Local cache for record-at-a-time fetch
+        int gotslot[] = new int[4];
+
+        // endDocument recieved?
+        private boolean done = false;
+        boolean m_isError = false;
+
+        private final boolean DEBUG = false;
+
+        /** The document base URI. */
+        protected String m_documentBaseURI;
+
+  /** If we're building the model incrementally on demand, we need to
+   * be able to tell the source when to send us more data.
+   *
+   * Note that if this has not been set, and you attempt to read ahead
+   * of the current build point, we'll probably throw a null-pointer
+   * exception. We could try to wait-and-retry instead, as a very poor
+   * fallback, but that has all the known problems with multithreading
+   * on multiprocessors and we Don't Want to Go There.
+   *
+   * @see setIncrementalSAXSource
+   */
+  private IncrementalSAXSource m_incrSAXSource=null;
+
+
+        // ========= DTM data structure declarations. ==============
+
+        // nodes array: integer array blocks to hold the first level reference of the nodes,
+        // each reference slot is addressed by a nodeHandle index value.
+        // Assumes indices are not larger than {@link NODEHANDLE_MASK}
+        // ({@link DOCHANDLE_SHIFT} bits).
+        ChunkedIntArray nodes = new ChunkedIntArray(4);
+
+        // text/comment table: string buffer to hold the text string values of the document,
+        // each of which is addressed by the absolute offset and length in the buffer
+        private FastStringBuffer m_char = new FastStringBuffer();
+        // Start of string currently being accumulated into m_char;
+        // needed because the string may be appended in several chunks.
+        private int m_char_current_start=0;
+
+        // %TBD% INITIALIZATION/STARTUP ISSUES
+        // -- Should we really be creating these, or should they be
+        // passed in from outside? Scott want to be able to share
+        // pools across multiple documents, so setting them here is
+        // probably not the right default.
+        private DTMStringPool m_localNames = new DTMStringPool();
+        private DTMStringPool m_nsNames = new DTMStringPool();
+        private DTMStringPool m_prefixNames = new DTMStringPool();
+
+        // %TBD% If we use the current ExpandedNameTable mapper, it
+        // needs to be bound to the NS and local name pools. Which
+        // means it needs to attach to them AFTER we've resolved their
+        // startup. Or it needs to attach to this document and
+        // retrieve them each time. Or this needs to be
+        // an interface _implemented_ by this class... which might be simplest!
+        private ExpandedNameTable m_expandedNames=
+                new ExpandedNameTable();
+
+        private XMLStringFactory m_xsf;
+
+
+        /**
+         * Construct a DTM.
+         *
+         * @param documentNumber the ID number assigned to this document.
+         * It will be shifted up into the high bits and returned as part of
+         * all node ID numbers, so those IDs indicate which document they
+         * came from as well as a location within the document. It is the
+         * DTMManager's responsibility to assign a unique number to each
+         * document.
+         */
+        public DTMDocumentImpl(DTMManager mgr, int documentNumber,
+                               DTMWSFilter whiteSpaceFilter,
+                               XMLStringFactory xstringfactory){
+                initDocument(documentNumber);	 // clear nodes and document handle
+                m_xsf = xstringfactory;
+        }
+
+  /** Bind a IncrementalSAXSource to this DTM. If we discover we need nodes
+   * that have not yet been built, we will ask this object to send us more
+   * events, and it will manage interactions with its data sources.
+   *
+   * Note that we do not actually build the IncrementalSAXSource, since we don't
+   * know what source it's reading from, what thread that source will run in,
+   * or when it will run.
+   *
+   * @param source The IncrementalSAXSource that we want to recieve events from
+   * on demand.
+   */
+  public void setIncrementalSAXSource(IncrementalSAXSource source)
+  {
+    m_incrSAXSource=source;
+
+    // Establish SAX-stream link so we can receive the requested data
+    source.setContentHandler(this);
+    source.setLexicalHandler(this);
+
+    // Are the following really needed? IncrementalSAXSource doesn't yet
+    // support them, and they're mostly no-ops here...
+    //source.setErrorHandler(this);
+    //source.setDTDHandler(this);
+    //source.setDeclHandler(this);
+  }
+
+        /**
+         * Wrapper for ChunkedIntArray.append, to automatically update the
+         * previous sibling's "next" reference (if necessary) and periodically
+         * wake a reader who may have encountered incomplete data and entered
+         * a wait state.
+         * @param w0 int As in ChunkedIntArray.append
+         * @param w1 int As in ChunkedIntArray.append
+         * @param w2 int As in ChunkedIntArray.append
+         * @param w3 int As in ChunkedIntArray.append
+         * @return int As in ChunkedIntArray.append
+         * @see ChunkedIntArray.append
+         */
+        private final int appendNode(int w0, int w1, int w2, int w3)
+        {
+                // A decent compiler may inline this.
+                int slotnumber = nodes.appendSlot(w0, w1, w2, w3);
+
+                if (DEBUG) System.out.println(slotnumber+": "+w0+" "+w1+" "+w2+" "+w3);
+
+                if (previousSiblingWasParent)
+                        nodes.writeEntry(previousSibling,2,slotnumber);
+
+                previousSiblingWasParent = false;	// Set the default; endElement overrides
+
+                return slotnumber;
+        }
+
+        // ========= DTM Implementation Control Functions. ==============
+
+        /**
+         * Set an implementation dependent feature.
+         * <p>
+         * %REVIEW% Do we really expect to set features on DTMs?
+         *
+         * @param featureId A feature URL.
+         * @param state true if this feature should be on, false otherwise.
+         */
+        public void setFeature(String featureId, boolean state) {};
+
+        /**
+         * Set a reference pointer to the element name symbol table.
+         * %REVIEW% Should this really be Public? Changing it while
+         * DTM is in use would be a disaster.
+         *
+         * @param poolRef DTMStringPool reference to an instance of table.
+         */
+        public void setLocalNameTable(DTMStringPool poolRef) {
+                m_localNames = poolRef;
+        }
+
+        /**
+         * Get a reference pointer to the element name symbol table.
+         *
+         * @return DTMStringPool reference to an instance of table.
+         */
+        public DTMStringPool getLocalNameTable() {
+                 return m_localNames;
+         }
+
+        /**
+         * Set a reference pointer to the namespace URI symbol table.
+         * %REVIEW% Should this really be Public? Changing it while
+         * DTM is in use would be a disaster.
+         *
+         * @param poolRef DTMStringPool reference to an instance of table.
+         */
+        public void setNsNameTable(DTMStringPool poolRef) {
+                m_nsNames = poolRef;
+        }
+
+        /**
+         * Get a reference pointer to the namespace URI symbol table.
+         *
+         * @return DTMStringPool reference to an instance of table.
+         */
+        public DTMStringPool getNsNameTable() {
+                 return m_nsNames;
+         }
+
+        /**
+         * Set a reference pointer to the prefix name symbol table.
+         * %REVIEW% Should this really be Public? Changing it while
+         * DTM is in use would be a disaster.
+         *
+         * @param poolRef DTMStringPool reference to an instance of table.
+         */
+        public void setPrefixNameTable(DTMStringPool poolRef) {
+                m_prefixNames = poolRef;
+        }
+
+        /**
+         * Get a reference pointer to the prefix name symbol table.
+         *
+         * @return DTMStringPool reference to an instance of table.
+         */
+        public DTMStringPool getPrefixNameTable() {
+                return m_prefixNames;
+        }
+
+         /**
+          * Set a reference pointer to the content-text repository
+          *
+          * @param buffer FastStringBuffer reference to an instance of
+          * buffer
+          */
+         void setContentBuffer(FastStringBuffer buffer) {
+                 m_char = buffer;
+         }
+
+         /**
+          * Get a reference pointer to the content-text repository
+          *
+          * @return FastStringBuffer reference to an instance of buffer
+          */
+         FastStringBuffer getContentBuffer() {
+                 return m_char;
+         }
+
+  /** getContentHandler returns "our SAX builder" -- the thing that
+   * someone else should send SAX events to in order to extend this
+   * DTM model.
+   *
+   * @return null if this model doesn't respond to SAX events,
+   * "this" if the DTM object has a built-in SAX ContentHandler,
+   * the IncrementalSAXSource if we're bound to one and should receive
+   * the SAX stream via it for incremental build purposes...
+   * */
+  public org.xml.sax.ContentHandler getContentHandler()
+  {
+    if (m_incrSAXSource instanceof IncrementalSAXSource_Filter)
+      return (ContentHandler) m_incrSAXSource;
+    else
+      return this;
+  }
+
+  /**
+   * Return this DTM's lexical handler.
+   *
+   * %REVIEW% Should this return null if constrution already done/begun?
+   *
+   * @return null if this model doesn't respond to lexical SAX events,
+   * "this" if the DTM object has a built-in SAX ContentHandler,
+   * the IncrementalSAXSource if we're bound to one and should receive
+   * the SAX stream via it for incremental build purposes...
+   */
+  public LexicalHandler getLexicalHandler()
+  {
+
+    if (m_incrSAXSource instanceof IncrementalSAXSource_Filter)
+      return (LexicalHandler) m_incrSAXSource;
+    else
+      return this;
+  }
+
+  /**
+   * Return this DTM's EntityResolver.
+   *
+   * @return null if this model doesn't respond to SAX entity ref events.
+   */
+  public org.xml.sax.EntityResolver getEntityResolver()
+  {
+
+    return null;
+  }
+
+  /**
+   * Return this DTM's DTDHandler.
+   *
+   * @return null if this model doesn't respond to SAX dtd events.
+   */
+  public org.xml.sax.DTDHandler getDTDHandler()
+  {
+
+    return null;
+  }
+
+  /**
+   * Return this DTM's ErrorHandler.
+   *
+   * @return null if this model doesn't respond to SAX error events.
+   */
+  public org.xml.sax.ErrorHandler getErrorHandler()
+  {
+
+    return null;
+  }
+
+  /**
+   * Return this DTM's DeclHandler.
+   *
+   * @return null if this model doesn't respond to SAX Decl events.
+   */
+  public org.xml.sax.ext.DeclHandler getDeclHandler()
+  {
+
+    return null;
+  }
+
+  /** @return true iff we're building this model incrementally (eg
+   * we're partnered with a IncrementalSAXSource) and thus require that the
+   * transformation and the parse run simultaneously. Guidance to the
+   * DTMManager.
+   * */
+  public boolean needsTwoThreads()
+  {
+    return null!=m_incrSAXSource;
+  }
+
+  //================================================================
+  // ========= SAX2 ContentHandler methods =========
+  // Accept SAX events, use them to build/extend the DTM tree.
+  // Replaces the deprecated DocumentHandler interface.
+
+  public void characters(char[] ch, int start, int length)
+       throws org.xml.sax.SAXException
+  {
+    // Actually creating the text node is handled by
+    // processAccumulatedText(); here we just accumulate the
+    // characters into the buffer.
+    m_char.append(ch,start,length);
+  }
+
+  // Flush string accumulation into a text node
+  private void processAccumulatedText()
+  {
+    int len=m_char.length();
+    if(len!=m_char_current_start)
+      {
+        // The FastStringBuffer has been previously agreed upon
+        appendTextChild(m_char_current_start,len-m_char_current_start);
+        m_char_current_start=len;
+      }
+  }
+  public void endDocument()
+       throws org.xml.sax.SAXException
+  {
+    // May need to tell the low-level builder code to pop up a level.
+    // There _should't_ be any significant pending text at this point.
+    appendEndDocument();
+  }
+  public void endElement(java.lang.String namespaceURI, java.lang.String localName,
+      java.lang.String qName)
+       throws org.xml.sax.SAXException
+  {
+    processAccumulatedText();
+    // No args but we do need to tell the low-level builder code to
+    // pop up a level.
+    appendEndElement();
+  }
+  public void endPrefixMapping(java.lang.String prefix)
+       throws org.xml.sax.SAXException
+  {
+    // No-op
+  }
+  public void ignorableWhitespace(char[] ch, int start, int length)
+       throws org.xml.sax.SAXException
+  {
+    // %TBD% I believe ignorable text isn't part of the DTM model...?
+  }
+  public void processingInstruction(java.lang.String target, java.lang.String data)
+       throws org.xml.sax.SAXException
+  {
+    processAccumulatedText();
+    // %TBD% Which pools do target and data go into?
+  }
+  public void setDocumentLocator(Locator locator)
+  {
+    // No-op for DTM
+  }
+  public void skippedEntity(java.lang.String name)
+       throws org.xml.sax.SAXException
+  {
+    processAccumulatedText();
+    //%TBD%
+  }
+  public void startDocument()
+       throws org.xml.sax.SAXException
+  {
+    appendStartDocument();
+  }
+  public void startElement(java.lang.String namespaceURI, java.lang.String localName,
+      java.lang.String qName, Attributes atts)
+       throws org.xml.sax.SAXException
+  {
+    processAccumulatedText();
+
+    // %TBD% Split prefix off qname
+    String prefix=null;
+    int colon=qName.indexOf(':');
+    if(colon>0)
+      prefix=qName.substring(0,colon);
+
+    // %TBD% Where do we pool expandedName, or is it just the union, or...
+    /**/System.out.println("Prefix="+prefix+" index="+m_prefixNames.stringToIndex(prefix));
+    appendStartElement(m_nsNames.stringToIndex(namespaceURI),
+                     m_localNames.stringToIndex(localName),
+                     m_prefixNames.stringToIndex(prefix)); /////// %TBD%
+
+    // %TBD% I'm assuming that DTM will require resequencing of
+    // NS decls before other attrs, hence two passes are taken.
+    // %TBD% Is there an easier way to test for NSDecl?
+    int nAtts=(atts==null) ? 0 : atts.getLength();
+    // %TBD% Countdown is more efficient if nobody cares about sequence.
+    for(int i=nAtts-1;i>=0;--i)
+      {
+        qName=atts.getQName(i);
+        if(qName.startsWith("xmlns:") || "xmlns".equals(qName))
+          {
+            prefix=null;
+            colon=qName.indexOf(':');
+            if(colon>0)
+              {
+                prefix=qName.substring(0,colon);
+              }
+            else
+              {
+                // %REVEIW% Null or ""?
+                prefix=null; // Default prefix
+              }
+
+
+            appendNSDeclaration(
+                                    m_prefixNames.stringToIndex(prefix),
+                                    m_nsNames.stringToIndex(atts.getValue(i)),
+                                    atts.getType(i).equalsIgnoreCase("ID"));
+          }
+      }
+
+    for(int i=nAtts-1;i>=0;--i)
+      {
+        qName=atts.getQName(i);
+        if(!(qName.startsWith("xmlns:") || "xmlns".equals(qName)))
+          {
+            // %TBD% I hate having to extract the prefix into a new
+            // string when we may never use it. Consider pooling whole
+            // qNames, which are already strings?
+            prefix=null;
+            colon=qName.indexOf(':');
+            if(colon>0)
+              {
+                prefix=qName.substring(0,colon);
+                localName=qName.substring(colon+1);
+              }
+            else
+              {
+                prefix=""; // Default prefix
+                localName=qName;
+              }
+
+
+            m_char.append(atts.getValue(i)); // Single-string value
+            int contentEnd=m_char.length();
+
+            if(!("xmlns".equals(prefix) || "xmlns".equals(qName)))
+              appendAttribute(m_nsNames.stringToIndex(atts.getURI(i)),
+                                  m_localNames.stringToIndex(localName),
+                                  m_prefixNames.stringToIndex(prefix),
+                                  atts.getType(i).equalsIgnoreCase("ID"),
+                                  m_char_current_start, contentEnd-m_char_current_start);
+            m_char_current_start=contentEnd;
+          }
+      }
+  }
+  public void startPrefixMapping(java.lang.String prefix, java.lang.String uri)
+       throws org.xml.sax.SAXException
+  {
+    // No-op in DTM, handled during element/attr processing?
+  }
+
+  //
+  // LexicalHandler support. Not all SAX2 parsers support these events
+  // but we may want to pass them through when they exist...
+  //
+  public void comment(char[] ch, int start, int length)
+       throws org.xml.sax.SAXException
+  {
+    processAccumulatedText();
+
+    m_char.append(ch,start,length); // Single-string value
+    appendComment(m_char_current_start,length);
+    m_char_current_start+=length;
+  }
+  public void endCDATA()
+       throws org.xml.sax.SAXException
+  {
+    // No-op in DTM
+  }
+  public void endDTD()
+       throws org.xml.sax.SAXException
+  {
+    // No-op in DTM
+  }
+  public void endEntity(java.lang.String name)
+       throws org.xml.sax.SAXException
+  {
+    // No-op in DTM
+  }
+  public void startCDATA()
+       throws org.xml.sax.SAXException
+  {
+    // No-op in DTM
+  }
+  public void startDTD(java.lang.String name, java.lang.String publicId,
+      java.lang.String systemId)
+       throws org.xml.sax.SAXException
+  {
+    // No-op in DTM
+  }
+  public void startEntity(java.lang.String name)
+       throws org.xml.sax.SAXException
+  {
+    // No-op in DTM
+  }
+
+
+  //================================================================
+  // ========= Document Handler Functions =========
+  // %REVIEW% jjk -- DocumentHandler is  SAX Level 1, and deprecated....
+  // and this wasn't a fully compliant or declared implementation of that API
+  // in any case. Phase out in favor of SAX2 ContentHandler/LexicalHandler
+
+        /**
+         * Reset a dtm document to its initial (empty) state.
+         *
+         * The DTMManager will invoke this method when the dtm is created.
+         *
+         * @param documentNumber the handle for the DTM document.
+         */
+        final void initDocument(int documentNumber)
+        {
+                // save masked DTM document handle
+                m_docHandle = documentNumber<<DOCHANDLE_SHIFT;
+
+                // Initialize the doc -- no parent, no next-sib
+                nodes.writeSlot(0,DOCUMENT_NODE,-1,-1,0);
+                // wait for the first startElement to create the doc root node
+                done = false;
+        }
+
+// 	/**
+// 	 * Receive hint of the end of a document.
+// 	 *
+// 	 * <p>The content handler will invoke this method only once, and it will
+// 	 * be the last method invoked during the parse.  The handler shall not
+// 	 * not invoke this method until it has either abandoned parsing
+// 	 * (because of an unrecoverable error) or reached the end of
+// 	 * input.</p>
+// 	 */
+// 	public void documentEnd()
+// 	{
+// 		done = true;
+// 		// %TBD% may need to notice the last slot number and slot count to avoid
+// 		// residual data from provious use of this DTM
+// 	}
+
+// 	/**
+// 	 * Receive notification of the beginning of a document.
+// 	 *
+// 	 * <p>The SAX parser will invoke this method only once, before any
+// 	 * other methods in this interface.</p>
+// 	 */
+// 	public void reset()
+// 	{
+
+// 		// %TBD% reset slot 0 to indicate ChunkedIntArray reuse or wait for
+// 		//       the next initDocument().
+// 		m_docElement = NULL;	 // reset nodeHandle to the root of the actual dtm doc content
+// 		initDocument(0);
+// 	}
+
+// 	/**
+// 	 * Factory method; creates an Element node in this document.
+// 	 *
+// 	 * The node created will be chained according to its natural order of request
+// 	 * received.  %TBD% It can be rechained later via the optional DTM writable interface.
+// 	 *
+// 	 * <p>The XML content handler will invoke endElement() method after all
+// 	 * of the element's content are processed in order to give DTM the indication
+// 	 * to prepare and patch up parent and sibling node pointers.</p>
+// 	 *
+// 	 * <p>The following interface for createElement will use an index value corresponds
+// 	 * to the symbol entry in the DTMDStringPool based symbol tables.</p>
+// 	 *
+// 	 * @param nsIndex The namespace of the node
+// 	 * @param nameIndex The element name.
+// 	 * @see #endElement
+// 	 * @see org.xml.sax.Attributes
+// 	 * @return nodeHandle int of the element created
+// 	 */
+// 	public int createElement(int nsIndex, int nameIndex, Attributes atts)
+// 	{
+// 		// do document root node creation here on the first element, create nodes for
+// 		// this element and its attributes, store the element, namespace, and attritute
+// 		// name indexes to the nodes array, keep track of the current node and parent
+// 		// element used
+
+// 		// W0  High:  Namespace  Low:  Node Type
+// 		int w0 = (nsIndex << 16) | ELEMENT_NODE;
+// 		// W1: Parent
+// 		int w1 = currentParent;
+// 		// W2: Next  (initialized as 0)
+// 		int w2 = 0;
+// 		// W3: Tagname
+// 		int w3 = nameIndex;
+// 		//int ourslot = nodes.appendSlot(w0, w1, w2, w3);
+// 		int ourslot = appendNode(w0, w1, w2, w3);
+// 		currentParent = ourslot;
+// 		previousSibling = 0;
+// 		setAttributes(atts);
+
+// 		// set the root element pointer when creating the first element node
+// 		if (m_docElement == NULL)
+// 			m_docElement = ourslot;
+// 		return (m_docHandle | ourslot);
+// 	}
+
+// 	// Factory method to create an Element node not associated with a given name space
+// 	// using String value parameters passed in from a content handler or application
+// 	/**
+// 	 * Factory method; creates an Element node not associated with a given name space in this document.
+// 	 *
+// 	 * The node created will be chained according to its natural order of request
+// 	 * received.  %TBD% It can be rechained later via the optional DTM writable interface.
+// 	 *
+// 	 * <p>The XML content handler or application will invoke endElement() method after all
+// 	 * of the element's content are processed in order to give DTM the indication
+// 	 * to prepare and patch up parent and sibling node pointers.</p>
+// 	 *
+// 	 * <p>The following parameters for createElement contains raw string values for name
+// 	 * symbols used in an Element node.</p>
+// 	 *
+// 	 * @param name String the element name, including the prefix if any.
+// 	 * @param atts The attributes attached to the element, if any.
+// 	 * @see #endElement
+// 	 * @see org.xml.sax.Attributes
+// 	 */
+// 	public int createElement(String name, Attributes atts)
+// 	{
+// 		// This method wraps around the index valued interface of the createElement interface.
+// 		// The raw string values are stored into the current DTM name symbol tables.  The method
+// 		// method will then use the index values returned to invoke the other createElement()
+// 		// onverted to index values modified to match a
+// 		// method.
+// 		int nsIndex = NULL;
+// 		int nameIndex = m_localNames.stringToIndex(name);
+// 		// note - there should be no prefix separator in the name because it is not associated
+// 		// with a name space
+
+// 		return createElement(nsIndex, nameIndex, atts);
+// 	}
+
+// 	// Factory method to create an Element node associated with a given name space
+// 	// using String value parameters passed in from a content handler or application
+// 	/**
+// 	 * Factory method; creates an Element node associated with a given name space in this document.
+// 	 *
+// 	 * The node created will be chained according to its natural order of request
+// 	 * received.  %TBD% It can be rechained later via the optional DTM writable interface.
+// 	 *
+// 	 * <p>The XML content handler or application will invoke endElement() method after all
+// 	 * of the element's content are processed in order to give DTM the indication
+// 	 * to prepare and patch up parent and sibling node pointers.</p>
+// 	 *
+// 	 * <p>The following parameters for createElementNS contains raw string values for name
+// 	 * symbols used in an Element node.</p>
+// 	 *
+// 	 * @param ns String the namespace of the node
+// 	 * @param name String the element name, including the prefix if any.
+// 	 * @param atts The attributes attached to the element, if any.
+// 	 * @see #endElement
+// 	 * @see org.xml.sax.Attributes
+// 	 */
+// 	public int createElementNS(String ns, String name, Attributes atts)
+// 	{
+// 		// This method wraps around the index valued interface of the createElement interface.
+// 		// The raw string values are stored into the current DTM name symbol tables.  The method
+// 		// method will then use the index values returned to invoke the other createElement()
+// 		// onverted to index values modified to match a
+// 		// method.
+// 		int nsIndex = m_nsNames.stringToIndex(ns);
+// 		int nameIndex = m_localNames.stringToIndex(name);
+// 		// The prefixIndex is not needed by the indexed interface of the createElement method
+// 		int prefixSep = name.indexOf(":");
+// 		int prefixIndex = m_prefixNames.stringToIndex(name.substring(0, prefixSep));
+// 		return createElement(nsIndex, nameIndex, atts);
+// 	}
+
+// 	/**
+// 	 * Receive an indication for the end of an element.
+// 	 *
+// 	 * <p>The XML content handler will invoke this method at the end of every
+// 	 * element in the XML document to give hint its time to pop up the current
+// 	 * element and parent and patch up parent and sibling pointers if necessary
+// 	 *
+// 	 * <p>%tbd% The following interface may need to be modified to match a
+// 	 * coordinated access to the DTMDStringPool based symbol tables.</p>
+// 		 *
+// 	 * @param ns the namespace of the element
+// 	 * @param name The element name
+// 	 */
+// 	public void endElement(String ns, String name)
+// 	{
+// 		// pop up the stacks
+
+// 		//
+// 		if (previousSiblingWasParent)
+// 			nodes.writeEntry(previousSibling, 2, NULL);
+
+// 		// Pop parentage
+// 		previousSibling = currentParent;
+// 		nodes.readSlot(currentParent, gotslot);
+// 		currentParent = gotslot[1] & 0xFFFF;
+
+// 		// The element just being finished will be
+// 		// the previous sibling for the next operation
+// 		previousSiblingWasParent = true;
+
+// 		// Pop a level of namespace table
+// 		// namespaceTable.removeLastElem();
+// 	}
+
+// 	/**
+// 	 * Creates attributes for the current node.
+// 	 *
+// 	 * @param atts Attributes to be created.
+// 	 */
+// 	void setAttributes(Attributes atts) {
+// 		int atLength = (null == atts) ? 0 : atts.getLength();
+// 		for (int i=0; i < atLength; i++) {
+// 			String qname = atts.getQName(i);
+// 			createAttribute(atts.getQName(i), atts.getValue(i));
+// 		}
+// 	}
+
+// 	/**
+// 	 * Appends an attribute to the document.
+// 	 * @param qname Qualified Name of the attribute
+// 	 * @param value Value of the attribute
+// 	 * @return Handle of node
+// 	 */
+// 	public int createAttribute(String qname, String value) {
+// 		int colonpos = qname.indexOf(":");
+// 		String attName = qname.substring(colonpos+1);
+// 		int w0 = 0;
+// 		if (colonpos > 0) {
+// 			String prefix = qname.substring(0, colonpos);
+// 			if (prefix.equals("xml")) {
+// 				//w0 = ATTRIBUTE_NODE |
+// 				//	(org.apache.xalan.templates.Constants.S_XMLNAMESPACEURI << 16);
+// 			} else {
+// 				//w0 = ATTRIBUTE_NODE |
+// 			}
+// 		} else {
+// 			w0 = ATTRIBUTE_NODE;
+// 		}
+// 		// W1:  Parent
+// 		int w1 = currentParent;
+// 		// W2:  Next (not yet resolved)
+// 		int w2 = 0;
+// 		// W3:  Tag name
+// 		int w3 = m_localNames.stringToIndex(attName);
+// 		// Add node
+// 		int ourslot = appendNode(w0, w1, w2, w3);
+// 		previousSibling = ourslot;	// Should attributes be previous siblings
+
+// 		// W0: Node Type
+// 		w0 = TEXT_NODE;
+// 		// W1: Parent
+// 		w1 = ourslot;
+// 		// W2: Start Position within buffer
+// 		w2 = m_char.length();
+// 		m_char.append(value);
+// 		// W3: Length
+// 		w3 = m_char.length() - w2;
+// 		appendNode(w0, w1, w2, w3);
+// 		charStringStart=m_char.length();
+// 		charStringLength = 0;
+// 		//previousSibling = ourslot;
+// 		// Attrs are Parents
+// 		previousSiblingWasParent = true;
+// 		return (m_docHandle | ourslot);
+// 	}
+
+// 	/**
+// 	 * Factory method; creates a Text node in this document.
+// 	 *
+// 	 * The node created will be chained according to its natural order of request
+// 	 * received.  %TBD% It can be rechained later via the optional DTM writable interface.
+// 	 *
+// 	 * @param text String The characters text string from the XML document.
+// 	 * @return int DTM node-number of the text node created
+// 	 */
+// 	public int createTextNode(String text)
+// 	throws DTMException
+// 	{
+// 		// wraps around the index value based createTextNode method
+// 		return createTextNode(text.toCharArray(), 0, text.length());
+// 	}
+
+// 	/**
+// 	 * Factory method; creates a Text node in this document.
+// 	 *
+// 	 * The node created will be chained according to its natural order of request
+// 	 * received.  %TBD% It can be rechained later via the optional DTM writable interface.
+// 	 *
+// 	 * %REVIEW% for text normalization issues, unless we are willing to
+// 	 * insist that all adjacent text must be merged before this method
+// 	 * is called.
+// 	 *
+// 	 * @param ch The characters from the XML document.
+// 	 * @param start The start position in the array.
+// 	 * @param length The number of characters to read from the array.
+// 	 */
+// 	public int createTextNode(char ch[], int start, int length)
+// 	throws DTMException
+// 	{
+// 		m_char.append(ch, start, length);		// store the chunk to the text/comment string table
+
+// 		// create a Text Node
+// 		// %TBD% may be possible to combine with appendNode()to replace the next chunk of code
+// 		int w0 = TEXT_NODE;
+// 		// W1: Parent
+// 		int w1 = currentParent;
+// 		// W2: Start position within m_char
+// 		int w2 = charStringStart;
+// 		// W3: Length of the full string
+// 		int w3 = length;
+// 		int ourslot = appendNode(w0, w1, w2, w3);
+// 		previousSibling = ourslot;
+
+// 		charStringStart=m_char.length();
+// 		charStringLength = 0;
+// 		return (m_docHandle | ourslot);
+// 	}
+
+// 	/**
+// 	 * Factory method; creates a Comment node in this document.
+// 	 *
+// 	 * The node created will be chained according to its natural order of request
+// 	 * received.  %TBD% It can be rechained later via the optional DTM writable interface.
+// 	 *
+// 	 * @param text String The characters text string from the XML document.
+// 	 * @return int DTM node-number of the text node created
+// 	 */
+// 	public int createComment(String text)
+// 	throws DTMException
+// 	{
+// 		// wraps around the index value based createTextNode method
+// 		return createComment(text.toCharArray(), 0, text.length());
+// 	}
+
+// 	/**
+// 	 * Factory method; creates a Comment node in this document.
+// 	 *
+// 	 * The node created will be chained according to its natural order of request
+// 	 * received.  %TBD% It can be rechained later via the optional DTM writable interface.
+// 	 *
+// 	 * @param ch An array holding the characters in the comment.
+// 	 * @param start The starting position in the array.
+// 	 * @param length The number of characters to use from the array.
+// 	 * @see DTMException
+// 	 */
+// 	public int createComment(char ch[], int start, int length)
+// 	throws DTMException
+// 	{
+// 		m_char.append(ch, start, length);		// store the comment string to the text/comment string table
+
+// 		// create a Comment Node
+// 		// %TBD% may be possible to combine with appendNode()to replace the next chunk of code
+// 		int w0 = COMMENT_NODE;
+// 		// W1: Parent
+// 		int w1 = currentParent;
+// 		// W2: Start position within m_char
+// 		int w2 = charStringStart;
+// 		// W3: Length of the full string
+// 		int w3 = length;
+// 		int ourslot = appendNode(w0, w1, w2, w3);
+// 		previousSibling = ourslot;
+
+// 		charStringStart=m_char.length();
+// 		charStringLength = 0;
+// 		return (m_docHandle | ourslot);
+// 	}
+
+// 	// Counters to keep track of the current text string being accumulated with respect
+// 	// to the text/comment string table: charStringStart should point to the starting
+// 	// offset of the string in the table and charStringLength the acccumulated length when
+// 	// appendAccumulatedText starts, and reset to the end of the table and 0 at the end
+// 	// of appendAccumulatedText for the next set of characters receives
+// 	int charStringStart=0,charStringLength=0;
+
+        // ========= Document Navigation Functions =========
+
+        /** Given a node handle, test if it has child nodes.
+         * <p> %REVIEW% This is obviously useful at the DOM layer, where it
+         * would permit testing this without having to create a proxy
+         * node. It's less useful in the DTM API, where
+         * (dtm.getFirstChild(nodeHandle)!=DTM.NULL) is just as fast and
+         * almost as self-evident. But it's a convenience, and eases porting
+         * of DOM code to DTM.  </p>
+         *
+         * @param nodeHandle int Handle of the node.
+         * @return int true if the given node has child nodes.
+         */
+        public boolean hasChildNodes(int nodeHandle) {
+                return(getFirstChild(nodeHandle) != NULL);
+        }
+
+        /**
+         * Given a node handle, get the handle of the node's first child.
+         * If not yet resolved, waits for more nodes to be added to the document and
+         * tries again.
+         *
+         * @param nodeHandle int Handle of the node.
+         * @return int DTM node-number of first child, or DTM.NULL to indicate none exists.
+         */
+        public int getFirstChild(int nodeHandle) {
+
+                // ###shs worry about tracing/debug later
+                nodeHandle &= NODEHANDLE_MASK;
+                // Read node into variable
+                nodes.readSlot(nodeHandle, gotslot);
+
+                // type is the last half of first slot
+                short type = (short) (gotslot[0] & 0xFFFF);
+
+                // Check to see if Element or Document node
+                if ((type == ELEMENT_NODE) || (type == DOCUMENT_NODE) ||
+                                (type == ENTITY_REFERENCE_NODE)) {
+
+                        // In case when Document root is given
+                        //	if (nodeHandle == 0) nodeHandle = 1;
+                        // %TBD% Probably was a mistake.
+                        // If someone explicitly asks for first child
+                        // of Document, I would expect them to want
+                        // that and only that.
+
+                        int kid = nodeHandle + 1;
+                        nodes.readSlot(kid, gotslot);
+                        while (ATTRIBUTE_NODE == (gotslot[0] & 0xFFFF)) {
+                                // points to next sibling
+                                kid = gotslot[2];
+                                // Return NULL if node has only attributes
+                                if (kid == NULL) return NULL;
+                                nodes.readSlot(kid, gotslot);
+                        }
+                        // If parent slot matches given parent, return kid
+                        if (gotslot[1] == nodeHandle)
+                        {
+                          int firstChild = kid | m_docHandle;
+
+                          return firstChild;
+                        }
+                }
+                // No child found
+
+                return NULL;
+        }
+
+        /**
+        * Given a node handle, advance to its last child.
+        * If not yet resolved, waits for more nodes to be added to the document and
+        * tries again.
+        *
+        * @param nodeHandle int Handle of the node.
+        * @return int Node-number of last child,
+        * or DTM.NULL to indicate none exists.
+        */
+        public int getLastChild(int nodeHandle) {
+                // ###shs put trace/debug later
+                nodeHandle &= NODEHANDLE_MASK;
+                // do not need to test node type since getFirstChild does that
+                int lastChild = NULL;
+                for (int nextkid = getFirstChild(nodeHandle); nextkid != NULL;
+                                nextkid = getNextSibling(nextkid)) {
+                        lastChild = nextkid;
+                }
+                return lastChild | m_docHandle;
+        }
+
+        /**
+         * Retrieves an attribute node by by qualified name and namespace URI.
+         *
+         * @param nodeHandle int Handle of the node upon which to look up this attribute.
+         * @param namespaceURI The namespace URI of the attribute to
+         *   retrieve, or null.
+         * @param name The local name of the attribute to
+         *   retrieve.
+         * @return The attribute node handle with the specified name (
+         *   <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
+         *   attribute.
+         */
+        public int getAttributeNode(int nodeHandle, String namespaceURI, String name) {
+                int nsIndex = m_nsNames.stringToIndex(namespaceURI),
+                                                                        nameIndex = m_localNames.stringToIndex(name);
+                nodeHandle &= NODEHANDLE_MASK;
+                nodes.readSlot(nodeHandle, gotslot);
+                short type = (short) (gotslot[0] & 0xFFFF);
+                // If nodeHandle points to element next slot would be first attribute
+                if (type == ELEMENT_NODE)
+                        nodeHandle++;
+                // Iterate through Attribute Nodes
+                while (type == ATTRIBUTE_NODE) {
+                        if ((nsIndex == (gotslot[0] << 16)) && (gotslot[3] == nameIndex))
+                                return nodeHandle | m_docHandle;
+                        // Goto next sibling
+                        nodeHandle = gotslot[2];
+                        nodes.readSlot(nodeHandle, gotslot);
+                }
+                return NULL;
+        }
+
+        /**
+         * Given a node handle, get the index of the node's first attribute.
+         *
+         * @param nodeHandle int Handle of the Element node.
+         * @return Handle of first attribute, or DTM.NULL to indicate none exists.
+         */
+        public int getFirstAttribute(int nodeHandle) {
+                nodeHandle &= NODEHANDLE_MASK;
+
+                // %REVIEW% jjk: Just a quick observation: If you're going to
+                // call readEntry repeatedly on the same node, it may be
+                // more efficiently to do a readSlot to get the data locally,
+                // reducing the addressing and call-and-return overhead.
+
+                // Should we check if handle is element (do we want sanity checks?)
+                if (ELEMENT_NODE != (nodes.readEntry(nodeHandle, 0) & 0xFFFF))
+                        return NULL;
+                // First Attribute (if any) should be at next position in table
+                nodeHandle++;
+                return(ATTRIBUTE_NODE == (nodes.readEntry(nodeHandle, 0) & 0xFFFF)) ?
+                nodeHandle | m_docHandle : NULL;
+        }
+
+        /**
+         * Given a node handle, get the index of the node's first child.
+         * If not yet resolved, waits for more nodes to be added to the document and
+         * tries again
+         *
+         * @param nodeHandle handle to node, which should probably be an element
+         *                   node, but need not be.
+         *
+         * @param inScope    true if all namespaces in scope should be returned,
+         *                   false if only the namespace declarations should be
+         *                   returned.
+         * @return handle of first namespace, or DTM.NULL to indicate none exists.
+         */
+        public int getFirstNamespaceNode(int nodeHandle, boolean inScope) {
+
+                return NULL;
+        }
+
+        /**
+         * Given a node handle, advance to its next sibling.
+         *
+         * %TBD% This currently uses the DTM-internal definition of
+         * sibling; eg, the last attr's next sib is the first
+         * child. In the old DTM, the DOM proxy layer provided the
+         * additional logic for the public view.  If we're rewriting
+         * for XPath emulation, that test must be done here.
+         *
+         * %TBD% CODE INTERACTION WITH INCREMENTAL PARSE - If not yet
+         * resolved, should wait for more nodes to be added to the document
+         * and tries again.
+         *
+         * @param nodeHandle int Handle of the node.
+         * @return int Node-number of next sibling,
+         * or DTM.NULL to indicate none exists.
+         * */
+        public int getNextSibling(int nodeHandle) {
+                nodeHandle &= NODEHANDLE_MASK;
+                // Document root has no next sibling
+                if (nodeHandle == 0)
+                        return NULL;
+
+                short type = (short) (nodes.readEntry(nodeHandle, 0) & 0xFFFF);
+                if ((type == ELEMENT_NODE) || (type == ATTRIBUTE_NODE) ||
+                                (type == ENTITY_REFERENCE_NODE)) {
+                        int nextSib = nodes.readEntry(nodeHandle, 2);
+                        if (nextSib == NULL)
+                                return NULL;
+                        if (nextSib != 0)
+                                return (m_docHandle | nextSib);
+                        // ###shs should cycle/wait if nextSib is 0? Working on threading next
+                }
+                // Next Sibling is in the next position if it shares the same parent
+                int thisParent = nodes.readEntry(nodeHandle, 1);
+
+                if (nodes.readEntry(++nodeHandle, 1) == thisParent)
+                        return (m_docHandle | nodeHandle);
+
+                return NULL;
+        }
+
+        /**
+         * Given a node handle, find its preceeding sibling.
+         * WARNING: DTM is asymmetric; this operation is resolved by search, and is
+         * relatively expensive.
+         *
+         * @param nodeHandle the id of the node.
+         * @return int Node-number of the previous sib,
+         * or DTM.NULL to indicate none exists.
+         */
+        public int getPreviousSibling(int nodeHandle) {
+                nodeHandle &= NODEHANDLE_MASK;
+                // Document root has no previous sibling
+                if (nodeHandle == 0)
+                        return NULL;
+
+                int parent = nodes.readEntry(nodeHandle, 1);
+                int kid = NULL;
+                for (int nextkid = getFirstChild(parent); nextkid != nodeHandle;
+                                nextkid = getNextSibling(nextkid)) {
+                        kid = nextkid;
+                }
+                return kid | m_docHandle;
+        }
+
+        /**
+         * Given a node handle, advance to the next attribute. If an
+         * element, we advance to its first attribute; if an attr, we advance to
+         * the next attr on the same node.
+         *
+         * @param nodeHandle int Handle of the node.
+         * @return int DTM node-number of the resolved attr,
+         * or DTM.NULL to indicate none exists.
+         */
+        public int getNextAttribute(int nodeHandle) {
+                nodeHandle &= NODEHANDLE_MASK;
+                nodes.readSlot(nodeHandle, gotslot);
+
+                //%REVIEW% Why are we using short here? There's no storage
+                //reduction for an automatic variable, especially one used
+                //so briefly, and it typically costs more cycles to process
+                //than an int would.
+                short type = (short) (gotslot[0] & 0xFFFF);
+
+                if (type == ELEMENT_NODE) {
+                        return getFirstAttribute(nodeHandle);
+                } else if (type == ATTRIBUTE_NODE) {
+                        if (gotslot[2] != NULL)
+                                return (m_docHandle | gotslot[2]);
+                }
+                return NULL;
+        }
+
+        /**
+         * Given a namespace handle, advance to the next namespace.
+         *
+         * %TBD% THIS METHOD DOES NOT MATCH THE CURRENT SIGNATURE IN
+         * THE DTM INTERFACE.  FIX IT, OR JUSTIFY CHANGING THE DTM
+         * API.
+         *
+         * @param namespaceHandle handle to node which must be of type NAMESPACE_NODE.
+         * @return handle of next namespace, or DTM.NULL to indicate none exists.
+         */
+        public int getNextNamespaceNode(int baseHandle,int namespaceHandle, boolean inScope) {
+                // ###shs need to work on namespace
+                return NULL;
+        }
+
+        /**
+         * Given a node handle, advance to its next descendant.
+         * If not yet resolved, waits for more nodes to be added to the document and
+         * tries again.
+         *
+         * @param subtreeRootHandle
+         * @param nodeHandle int Handle of the node.
+         * @return handle of next descendant,
+         * or DTM.NULL to indicate none exists.
+         */
+        public int getNextDescendant(int subtreeRootHandle, int nodeHandle) {
+                subtreeRootHandle &= NODEHANDLE_MASK;
+                nodeHandle &= NODEHANDLE_MASK;
+                // Document root [Document Node? -- jjk] - no next-sib
+                if (nodeHandle == 0)
+                        return NULL;
+                while (!m_isError) {
+                        // Document done and node out of bounds
+                        if (done && (nodeHandle > nodes.slotsUsed()))
+                                break;
+                        if (nodeHandle > subtreeRootHandle) {
+                                nodes.readSlot(nodeHandle+1, gotslot);
+                                if (gotslot[2] != 0) {
+                                        short type = (short) (gotslot[0] & 0xFFFF);
+                                        if (type == ATTRIBUTE_NODE) {
+                                                nodeHandle +=2;
+                                        } else {
+                                                int nextParentPos = gotslot[1];
+                                                if (nextParentPos >= subtreeRootHandle)
+                                                        return (m_docHandle | (nodeHandle+1));
+                                                else
+                                                        break;
+                                        }
+                                } else if (!done) {
+                                        // Add wait logic here
+                                } else
+                                        break;
+                        } else {
+                                nodeHandle++;
+                        }
+                }
+                // Probably should throw error here like original instead of returning
+                return NULL;
+        }
+
+        /**
+         * Given a node handle, advance to the next node on the following axis.
+         *
+         * @param axisContextHandle the start of the axis that is being traversed.
+         * @param nodeHandle
+         * @return handle of next sibling,
+         * or DTM.NULL to indicate none exists.
+         */
+        public int getNextFollowing(int axisContextHandle, int nodeHandle) {
+                //###shs still working on
+                return NULL;
+        }
+
+        /**
+         * Given a node handle, advance to the next node on the preceding axis.
+         *
+         * @param axisContextHandle the start of the axis that is being traversed.
+         * @param nodeHandle the id of the node.
+         * @return int Node-number of preceding sibling,
+         * or DTM.NULL to indicate none exists.
+         */
+        public int getNextPreceding(int axisContextHandle, int nodeHandle) {
+                // ###shs copied from Xalan 1, what is this suppose to do?
+                nodeHandle &= NODEHANDLE_MASK;
+                while (nodeHandle > 1) {
+                        nodeHandle--;
+                        if (ATTRIBUTE_NODE == (nodes.readEntry(nodeHandle, 0) & 0xFFFF))
+                                continue;
+
+                        // if nodeHandle is _not_ an ancestor of
+                        // axisContextHandle, specialFind will return it.
+                        // If it _is_ an ancestor, specialFind will return -1
+
+                        // %REVIEW% unconditional return defeats the
+                        // purpose of the while loop -- does this
+                        // logic make any sense?
+
+                        return (m_docHandle | nodes.specialFind(axisContextHandle, nodeHandle));
+                }
+                return NULL;
+        }
+
+        /**
+         * Given a node handle, find its parent node.
+         *
+         * @param nodeHandle the id of the node.
+         * @return int Node-number of parent,
+         * or DTM.NULL to indicate none exists.
+         */
+        public int getParent(int nodeHandle) {
+                // Should check to see within range?
+
+                // Document Root should not have to be handled differently
+                return (m_docHandle | nodes.readEntry(nodeHandle, 1));
+        }
+
+        /**
+         * Returns the root element of the document.
+         * @return nodeHandle to the Document Root.
+         */
+        public int getDocumentRoot() {
+                return (m_docHandle | m_docElement);
+        }
+
+        /**
+         * Given a node handle, find the owning document node.
+         *
+         * @return int Node handle of document, which should always be valid.
+         */
+        public int getDocument() {
+                return m_docHandle;
+        }
+
+        /**
+         * Given a node handle, find the owning document node.  This has the exact
+         * same semantics as the DOM Document method of the same name, in that if
+         * the nodeHandle is a document node, it will return NULL.
+         *
+         * <p>%REVIEW% Since this is DOM-specific, it may belong at the DOM
+         * binding layer. Included here as a convenience function and to
+         * aid porting of DOM code to DTM.</p>
+         *
+         * @param nodeHandle the id of the node.
+         * @return int Node handle of owning document, or NULL if the nodeHandle is
+         *             a document.
+         */
+        public int getOwnerDocument(int nodeHandle) {
+                // Assumption that Document Node is always in 0 slot
+                if ((nodeHandle & NODEHANDLE_MASK) == 0)
+                        return NULL;
+                return (nodeHandle & DOCHANDLE_MASK);
+        }
+
+        /**
+         * Given a node handle, find the owning document node.  This has the DTM
+         * semantics; a Document node is its own owner.
+         *
+         * <p>%REVIEW% Since this is DOM-specific, it may belong at the DOM
+         * binding layer. Included here as a convenience function and to
+         * aid porting of DOM code to DTM.</p>
+         *
+         * @param nodeHandle the id of the node.
+         * @return int Node handle of owning document, or NULL if the nodeHandle is
+         *             a document.
+         */
+        public int getDocumentRoot(int nodeHandle) {
+                // Assumption that Document Node is always in 0 slot
+                if ((nodeHandle & NODEHANDLE_MASK) == 0)
+                        return NULL;
+                return (nodeHandle & DOCHANDLE_MASK);
+        }
+
+        /**
+         * Get the string-value of a node as a String object
+         * (see http://www.w3.org/TR/xpath#data-model
+         * for the definition of a node's string-value).
+         *
+         * @param nodeHandle The node ID.
+         *
+         * @return A string object that represents the string-value of the given node.
+         */
+        public XMLString getStringValue(int nodeHandle) {
+        // ###zaj - researching
+        nodes.readSlot(nodeHandle, gotslot);
+        int nodetype=gotslot[0] & 0xFF;
+        String value=null;
+
+        switch (nodetype) {
+        case TEXT_NODE:
+        case COMMENT_NODE:
+        case CDATA_SECTION_NODE:
+                value= m_char.getString(gotslot[2], gotslot[3]);
+                break;
+        case PROCESSING_INSTRUCTION_NODE:
+        case ATTRIBUTE_NODE:
+        case ELEMENT_NODE:
+        case ENTITY_REFERENCE_NODE:
+        default:
+                break;
+        }
+        return m_xsf.newstr( value );
+
+        }
+
+        /**
+         * Get number of character array chunks in
+         * the string-value of a node.
+         * (see http://www.w3.org/TR/xpath#data-model
+         * for the definition of a node's string-value).
+         * Note that a single text node may have multiple text chunks.
+         *
+         * EXPLANATION: This method is an artifact of the fact that the
+         * underlying m_chars object may not store characters in a
+         * single contiguous array -- for example,the current
+         * FastStringBuffer may split a single node's text across
+         * multiple allocation units.  This call tells us how many
+         * separate accesses will be required to retrieve the entire
+         * content. PLEASE NOTE that this may not be the same as the
+         * number of SAX characters() events that caused the text node
+         * to be built in the first place, since m_chars buffering may
+         * be on different boundaries than the parser's buffers.
+         *
+         * @param nodeHandle The node ID.
+         *
+         * @return number of character array chunks in
+         *         the string-value of a node.
+         * */
+        //###zaj - tbd
+        public int getStringValueChunkCount(int nodeHandle)
+        {
+                //###zaj    return value
+                return 0;
+        }
+
+        /**
+         * Get a character array chunk in the string-value of a node.
+         * (see http://www.w3.org/TR/xpath#data-model
+         * for the definition of a node's string-value).
+         * Note that a single text node may have multiple text chunks.
+         *
+         * EXPLANATION: This method is an artifact of the fact that
+         * the underlying m_chars object may not store characters in a
+         * single contiguous array -- for example,the current
+         * FastStringBuffer may split a single node's text across
+         * multiple allocation units.  This call retrieves a single
+         * contiguous portion of the text -- as much as m-chars was
+         * able to store in a single allocation unit.  PLEASE NOTE
+         * that this may not be the same granularityas the SAX
+         * characters() events that caused the text node to be built
+         * in the first place, since m_chars buffering may be on
+         * different boundaries than the parser's buffers.
+         *
+         * @param nodeHandle The node ID.
+         * @param chunkIndex Which chunk to get.
+         * @param startAndLen An array of 2 where the start position and length of
+         *                    the chunk will be returned.
+         *
+         * @return The character array reference where the chunk occurs.  */
+        //###zaj - tbd
+        public char[] getStringValueChunk(int nodeHandle, int chunkIndex,
+                                                                                                                                                int[] startAndLen) {return new char[0];}
+
+        /**
+         * Given a node handle, return an ID that represents the node's expanded name.
+         *
+         * @param nodeHandle The handle to the node in question.
+         *
+         * @return the expanded-name id of the node.
+         */
+        public int getExpandedTypeID(int nodeHandle) {
+           nodes.readSlot(nodeHandle, gotslot);
+           String qName = m_localNames.indexToString(gotslot[3]);
+           // Remove prefix from qName
+           // %TBD% jjk This is assuming the elementName is the qName.
+           int colonpos = qName.indexOf(":");
+           String localName = qName.substring(colonpos+1);
+           // Get NS
+           String namespace = m_nsNames.indexToString(gotslot[0] << 16);
+           // Create expanded name
+           String expandedName = namespace + ":" + localName;
+           int expandedNameID = m_nsNames.stringToIndex(expandedName);
+
+        return expandedNameID;
+        }
+
+
+        /**
+         * Given an expanded name, return an ID.  If the expanded-name does not
+         * exist in the internal tables, the entry will be created, and the ID will
+         * be returned.  Any additional nodes that are created that have this
+         * expanded name will use this ID.
+         *
+         * @return the expanded-name id of the node.
+         */
+        public int getExpandedTypeID(String namespace, String localName, int type) {
+           // Create expanded name
+          // %TBD% jjk Expanded name is bitfield-encoded as
+          // typeID[6]nsuriID[10]localID[16]. Switch to that form, and to
+          // accessing the ns/local via their tables rather than confusing
+          // nsnames and expandednames.
+           String expandedName = namespace + ":" + localName;
+           int expandedNameID = m_nsNames.stringToIndex(expandedName);
+
+           return expandedNameID;
+        }
+
+
+        /**
+         * Given an expanded-name ID, return the local name part.
+         *
+         * @param ExpandedNameID an ID that represents an expanded-name.
+         * @return String Local name of this node.
+         */
+        public String getLocalNameFromExpandedNameID(int ExpandedNameID) {
+
+           // Get expanded name
+           String expandedName = m_localNames.indexToString(ExpandedNameID);
+           // Remove prefix from expanded name
+           int colonpos = expandedName.indexOf(":");
+           String localName = expandedName.substring(colonpos+1);
+           return localName;
+        }
+
+
+        /**
+         * Given an expanded-name ID, return the namespace URI part.
+         *
+         * @param ExpandedNameID an ID that represents an expanded-name.
+         * @return String URI value of this node's namespace, or null if no
+         * namespace was resolved.
+        */
+        public String getNamespaceFromExpandedNameID(int ExpandedNameID) {
+
+           String expandedName = m_localNames.indexToString(ExpandedNameID);
+           // Remove local name from expanded name
+           int colonpos = expandedName.indexOf(":");
+           String nsName = expandedName.substring(0, colonpos);
+
+        return nsName;
+        }
+
+
+        /**
+         * fixednames
+        */
+        private static final String[] fixednames=
+        {
+                null,null,							// nothing, Element
+                null,"#text",						// Attr, Text
+                "#cdata_section",null,	// CDATA, EntityReference
+                null,null,							// Entity, PI
+                "#comment","#document",	// Comment, Document
+                null,"#document-fragment", // Doctype, DocumentFragment
+                null};									// Notation
+
+        /**
+         * Given a node handle, return its DOM-style node name. This will
+         * include names such as #text or #document.
+         *
+         * @param nodeHandle the id of the node.
+         * @return String Name of this node, which may be an empty string.
+         * %REVIEW% Document when empty string is possible...
+         */
+        public String getNodeName(int nodeHandle) {
+                nodes.readSlot(nodeHandle, gotslot);
+                short type = (short) (gotslot[0] & 0xFFFF);
+                String name = fixednames[type];
+                if (null == name) {
+                  int i=gotslot[3];
+                  /**/System.out.println("got i="+i+" "+(i>>16)+"/"+(i&0xffff));
+
+                  name=m_localNames.indexToString(i & 0xFFFF);
+                  String prefix=m_prefixNames.indexToString(i >>16);
+                  if(prefix!=null && prefix.length()>0)
+                    name=prefix+":"+name;
+                }
+                return name;
+        }
+
+        /**
+         * Given a node handle, return the XPath node name.  This should be
+         * the name as described by the XPath data model, NOT the DOM-style
+         * name.
+         *
+         * @param nodeHandle the id of the node.
+         * @return String Name of this node.
+         */
+        public String getNodeNameX(int nodeHandle) {return null;}
+
+        /**
+         * Given a node handle, return its DOM-style localname.
+         * (As defined in Namespaces, this is the portion of the name after any
+         * colon character)
+         *
+         * %REVIEW% What's the local name of something other than Element/Attr?
+         * Should this be DOM-style (undefined unless namespaced), or other?
+         *
+         * @param nodeHandle the id of the node.
+         * @return String Local name of this node.
+         */
+        public String getLocalName(int nodeHandle) {
+                nodes.readSlot(nodeHandle, gotslot);
+                short type = (short) (gotslot[0] & 0xFFFF);
+                String name = "";
+                if ((type==ELEMENT_NODE) || (type==ATTRIBUTE_NODE)) {
+                  int i=gotslot[3];
+                  name=m_localNames.indexToString(i & 0xFFFF);
+                  if(name==null) name="";
+                }
+                return name;
+        }
+
+        /**
+         * Given a namespace handle, return the prefix that the namespace decl is
+         * mapping.
+         * Given a node handle, return the prefix used to map to the namespace.
+         *
+         * <p> %REVIEW% Are you sure you want "" for no prefix?  </p>
+         *
+         * %REVIEW%  Should this be DOM-style (undefined unless namespaced),
+         * or other?
+         *
+         * @param nodeHandle the id of the node.
+         * @return String prefix of this node's name, or "" if no explicit
+         * namespace prefix was given.
+         */
+        public String getPrefix(int nodeHandle) {
+                nodes.readSlot(nodeHandle, gotslot);
+                short type = (short) (gotslot[0] & 0xFFFF);
+                String name = "";
+                if((type==ELEMENT_NODE) || (type==ATTRIBUTE_NODE)) {
+                  int i=gotslot[3];
+                  name=m_prefixNames.indexToString(i >>16);
+                  if(name==null) name="";
+                }
+                return name;
+        }
+
+        /**
+         * Given a node handle, return its DOM-style namespace URI
+         * (As defined in Namespaces, this is the declared URI which this node's
+         * prefix -- or default in lieu thereof -- was mapped to.)
+         *
+         * @param nodeHandle the id of the node.
+         * @return String URI value of this node's namespace, or null if no
+         * namespace was resolved.
+         */
+        public String getNamespaceURI(int nodeHandle) {return null;}
+
+        /**
+         * Given a node handle, return its node value. This is mostly
+         * as defined by the DOM, but may ignore some conveniences.
+         * <p>
+         *
+         * @param nodeHandle The node id.
+         * @return String Value of this node, or null if not
+         * meaningful for this node type.
+         */
+        public String getNodeValue(int nodeHandle)
+        {
+                nodes.readSlot(nodeHandle, gotslot);
+                int nodetype=gotslot[0] & 0xFF;		// ###zaj use mask to get node type
+                String value=null;
+
+                switch (nodetype) {			// ###zaj todo - document nodetypes
+                case ATTRIBUTE_NODE:
+                        nodes.readSlot(nodeHandle+1, gotslot);
+                case TEXT_NODE:
+                case COMMENT_NODE:
+                case CDATA_SECTION_NODE:
+                        value=m_char.getString(gotslot[2], gotslot[3]);		//###zaj
+                        break;
+                case PROCESSING_INSTRUCTION_NODE:
+                case ELEMENT_NODE:
+                case ENTITY_REFERENCE_NODE:
+                default:
+                        break;
+                }
+                return value;
+        }
+
+        /**
+         * Given a node handle, return its DOM-style node type.
+         * <p>
+         * %REVIEW% Generally, returning short is false economy. Return int?
+         *
+         * @param nodeHandle The node id.
+         * @return int Node type, as per the DOM's Node._NODE constants.
+         */
+        public short getNodeType(int nodeHandle) {
+                return(short) (nodes.readEntry(nodeHandle, 0) & 0xFFFF);
+        }
+
+        /**
+         * Get the depth level of this node in the tree (equals 1 for
+         * a parentless node).
+         *
+         * @param nodeHandle The node id.
+         * @return the number of ancestors, plus one
+         * @xsl.usage internal
+         */
+        public short getLevel(int nodeHandle) {
+                short count = 0;
+                while (nodeHandle != 0) {
+                        count++;
+                        nodeHandle = nodes.readEntry(nodeHandle, 1);
+                }
+                return count;
+        }
+
+        // ============== Document query functions ==============
+
+        /**
+         * Tests whether DTM DOM implementation implements a specific feature and
+         * that feature is supported by this node.
+         *
+         * @param feature The name of the feature to test.
+         * @param version This is the version number of the feature to test.
+         *   If the version is not
+         *   specified, supporting any version of the feature will cause the
+         *   method to return <code>true</code>.
+         * @return Returns <code>true</code> if the specified feature is
+         *   supported on this node, <code>false</code> otherwise.
+         */
+        public boolean isSupported(String feature, String version) {return false;}
+
+        /**
+         * Return the base URI of the document entity. If it is not known
+         * (because the document was parsed from a socket connection or from
+         * standard input, for example), the value of this property is unknown.
+         *
+         * @return the document base URI String object or null if unknown.
+         */
+        public String getDocumentBaseURI()
+        {
+
+          return m_documentBaseURI;
+        }
+
+        /**
+         * Set the base URI of the document entity.
+         *
+         * @param baseURI the document base URI String object or null if unknown.
+         */
+        public void setDocumentBaseURI(String baseURI)
+        {
+
+          m_documentBaseURI = baseURI;
+        }
+
+        /**
+         * Return the system identifier of the document entity. If
+         * it is not known, the value of this property is unknown.
+         *
+         * @param nodeHandle The node id, which can be any valid node handle.
+         * @return the system identifier String object or null if unknown.
+         */
+        public String getDocumentSystemIdentifier(int nodeHandle) {return null;}
+
+        /**
+         * Return the name of the character encoding scheme
+         *        in which the document entity is expressed.
+         *
+         * @param nodeHandle The node id, which can be any valid node handle.
+         * @return the document encoding String object.
+         */
+        public String getDocumentEncoding(int nodeHandle) {return null;}
+
+        /**
+         * Return an indication of the standalone status of the document,
+         *        either "yes" or "no". This property is derived from the optional
+         *        standalone document declaration in the XML declaration at the
+         *        beginning of the document entity, and has no value if there is no
+         *        standalone document declaration.
+         *
+         * @param nodeHandle The node id, which can be any valid node handle.
+         * @return the document standalone String object, either "yes", "no", or null.
+         */
+        public String getDocumentStandalone(int nodeHandle) {return null;}
+
+        /**
+         * Return a string representing the XML version of the document. This
+         * property is derived from the XML declaration optionally present at the
+         * beginning of the document entity, and has no value if there is no XML
+         * declaration.
+         *
+         * @param documentHandle the document handle
+         *
+         * @return the document version String object
+         */
+        public String getDocumentVersion(int documentHandle) {return null;}
+
+        /**
+         * Return an indication of
+         * whether the processor has read the complete DTD. Its value is a
+         * boolean. If it is false, then certain properties (indicated in their
+         * descriptions below) may be unknown. If it is true, those properties
+         * are never unknown.
+         *
+         * @return <code>true</code> if all declarations were processed {};
+         *         <code>false</code> otherwise.
+         */
+        public boolean getDocumentAllDeclarationsProcessed() {return false;}
+
+        /**
+         *   A document type declaration information item has the following properties:
+         *
+         *     1. [system identifier] The system identifier of the external subset, if
+         *        it exists. Otherwise this property has no value.
+         *
+         * @return the system identifier String object, or null if there is none.
+         */
+        public String getDocumentTypeDeclarationSystemIdentifier() {return null;}
+
+        /**
+         * Return the public identifier of the external subset,
+         * normalized as described in 4.2.2 External Entities [XML]. If there is
+         * no external subset or if it has no public identifier, this property
+         * has no value.
+         *
+         * @return the public identifier String object, or null if there is none.
+         */
+        public String getDocumentTypeDeclarationPublicIdentifier() {return null;}
+
+        /**
+         * Returns the <code>Element</code> whose <code>ID</code> is given by
+         * <code>elementId</code>. If no such element exists, returns
+         * <code>DTM.NULL</code>. Behavior is not defined if more than one element
+         * has this <code>ID</code>. Attributes (including those
+         * with the name "ID") are not of type ID unless so defined by DTD/Schema
+         * information available to the DTM implementation.
+         * Implementations that do not know whether attributes are of type ID or
+         * not are expected to return <code>DTM.NULL</code>.
+         *
+         * <p>%REVIEW% Presumably IDs are still scoped to a single document,
+         * and this operation searches only within a single document, right?
+         * Wouldn't want collisions between DTMs in the same process.</p>
+         *
+         * @param elementId The unique <code>id</code> value for an element.
+         * @return The handle of the matching element.
+         */
+        public int getElementById(String elementId) {return 0;}
+
+        /**
+         * The getUnparsedEntityURI function returns the URI of the unparsed
+         * entity with the specified name in the same document as the context
+         * node (see [3.3 Unparsed Entities]). It returns the empty string if
+         * there is no such entity.
+         * <p>
+         * XML processors may choose to use the System Identifier (if one
+         * is provided) to resolve the entity, rather than the URI in the
+         * Public Identifier. The details are dependent on the processor, and
+         * we would have to support some form of plug-in resolver to handle
+         * this properly. Currently, we simply return the System Identifier if
+         * present, and hope that it a usable URI or that our caller can
+         * map it to one.
+         * TODO: Resolve Public Identifiers... or consider changing function name.
+         * <p>
+         * If we find a relative URI
+         * reference, XML expects it to be resolved in terms of the base URI
+         * of the document. The DOM doesn't do that for us, and it isn't
+         * entirely clear whether that should be done here; currently that's
+         * pushed up to a higher level of our application. (Note that DOM Level
+         * 1 didn't store the document's base URI.)
+         * TODO: Consider resolving Relative URIs.
+         * <p>
+         * (The DOM's statement that "An XML processor may choose to
+         * completely expand entities before the structure model is passed
+         * to the DOM" refers only to parsed entities, not unparsed, and hence
+         * doesn't affect this function.)
+         *
+         * @param name A string containing the Entity Name of the unparsed
+         * entity.
+         *
+         * @return String containing the URI of the Unparsed Entity, or an
+         * empty string if no such entity exists.
+         */
+        public String getUnparsedEntityURI(String name) {return null;}
+
+
+        // ============== Boolean methods ================
+
+        /**
+         * Return true if the xsl:strip-space or xsl:preserve-space was processed
+         * during construction of the DTM document.
+         *
+         * <p>%REVEIW% Presumes a 1:1 mapping from DTM to Document, since
+         * we aren't saying which Document to query...?</p>
+         */
+        public boolean supportsPreStripping() {return false;}
+
+        /**
+         * Figure out whether nodeHandle2 should be considered as being later
+         * in the document than nodeHandle1, in Document Order as defined
+         * by the XPath model. This may not agree with the ordering defined
+         * by other XML applications.
+         * <p>
+         * There are some cases where ordering isn't defined, and neither are
+         * the results of this function -- though we'll generally return true.
+         *
+         * TODO: Make sure this does the right thing with attribute nodes!!!
+         *
+         * @param nodeHandle1 DOM Node to perform position comparison on.
+         * @param nodeHandle2 DOM Node to perform position comparison on .
+         *
+         * @return false if node2 comes before node1, otherwise return true.
+         * You can think of this as
+         * <code>(node1.documentOrderPosition &lt;= node2.documentOrderPosition)</code>.
+         */
+        public boolean isNodeAfter(int nodeHandle1, int nodeHandle2) {return false;}
+
+        /**
+         *     2. [element content whitespace] A boolean indicating whether the
+         *        character is white space appearing within element content (see [XML],
+         *        2.10 "White Space Handling"). Note that validating XML processors are
+         *        required by XML 1.0 to provide this information. If there is no
+         *        declaration for the containing element, this property has no value for
+         *        white space characters. If no declaration has been read, but the [all
+         *        declarations processed] property of the document information item is
+         *        false (so there may be an unread declaration), then the value of this
+         *        property is unknown for white space characters. It is always false for
+         *        characters that are not white space.
+         *
+         * @param nodeHandle the node ID.
+         * @return <code>true</code> if the character data is whitespace;
+         *         <code>false</code> otherwise.
+         */
+        public boolean isCharacterElementContentWhitespace(int nodeHandle) {return false;}
+
+        /**
+         *    10. [all declarations processed] This property is not strictly speaking
+         *        part of the infoset of the document. Rather it is an indication of
+         *        whether the processor has read the complete DTD. Its value is a
+         *        boolean. If it is false, then certain properties (indicated in their
+         *        descriptions below) may be unknown. If it is true, those properties
+         *        are never unknown.
+         *
+         * @param documentHandle A node handle that must identify a document.
+         * @return <code>true</code> if all declarations were processed;
+         *         <code>false</code> otherwise.
+         */
+        public boolean isDocumentAllDeclarationsProcessed(int documentHandle) {return false;}
+
+        /**
+         *     5. [specified] A flag indicating whether this attribute was actually
+         *        specified in the start-tag of its element, or was defaulted from the
+         *        DTD.
+         *
+         * @param attributeHandle the attribute handle
+         * @return <code>true</code> if the attribute was specified;
+         *         <code>false</code> if it was defaulted.
+         */
+        public boolean isAttributeSpecified(int attributeHandle) {return false;}
+
+        // ========== Direct SAX Dispatch, for optimization purposes ========
+
+        /**
+         * Directly call the
+         * characters method on the passed ContentHandler for the
+         * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
+         * for the definition of a node's string-value). Multiple calls to the
+         * ContentHandler's characters methods may well occur for a single call to
+         * this method.
+         *
+         * @param nodeHandle The node ID.
+         * @param ch A non-null reference to a ContentHandler.
+         *
+         * @throws org.xml.sax.SAXException
+         */
+        public void dispatchCharactersEvents(
+                                                                                                                                                        int nodeHandle, org.xml.sax.ContentHandler ch, boolean normalize)
+        throws org.xml.sax.SAXException {}
+
+        /**
+         * Directly create SAX parser events from a subtree.
+         *
+         * @param nodeHandle The node ID.
+         * @param ch A non-null reference to a ContentHandler.
+         *
+         * @throws org.xml.sax.SAXException
+         */
+
+        public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch)
+        throws org.xml.sax.SAXException {}
+
+        /**
+         * Return an DOM node for the given node.
+         *
+         * @param nodeHandle The node ID.
+         *
+         * @return A node representation of the DTM node.
+         */
+        public org.w3c.dom.Node getNode(int nodeHandle)
+        {
+          return null;
+        }
+
+        // ==== Construction methods (may not be supported by some implementations!) =====
+        // %REVIEW% jjk: These probably aren't the right API. At the very least
+        // they need to deal with current-insertion-location and end-element
+        // issues.
+
+        /**
+         * Append a child to the end of the child list of the current node. Please note that the node
+         * is always cloned if it is owned by another document.
+         *
+         * <p>%REVIEW% "End of the document" needs to be defined more clearly.
+         * Does it become the last child of the Document? Of the root element?</p>
+         *
+         * @param newChild Must be a valid new node handle.
+         * @param clone true if the child should be cloned into the document.
+         * @param cloneDepth if the clone argument is true, specifies that the
+         *                   clone should include all it's children.
+         */
+        public void appendChild(int newChild, boolean clone, boolean cloneDepth) {
+                boolean sameDoc = ((newChild & DOCHANDLE_MASK) == m_docHandle);
+                if (clone || !sameDoc) {
+
+                } else {
+
+                }
+        }
+
+        /**
+         * Append a text node child that will be constructed from a string,
+         * to the end of the document.
+         *
+         * <p>%REVIEW% "End of the document" needs to be defined more clearly.
+         * Does it become the last child of the Document? Of the root element?</p>
+         *
+         * @param str Non-null reference to a string.
+         */
+        public void appendTextChild(String str) {
+                // ###shs Think more about how this differs from createTextNode
+          //%TBD%
+        }
+
+
+  //================================================================
+  // ==== BUILDER methods ====
+  // %TBD% jjk: SHOULD PROBABLY BE INLINED, unless we want to support
+  // both SAX1 and SAX2 and share this logic between them.
+
+  /** Append a text child at the current insertion point. Assumes that the
+   * actual content of the text has previously been appended to the m_char
+   * buffer (shared with the builder).
+   *
+   * @param m_char_current_start int Starting offset of node's content in m_char.
+   * @param contentLength int Length of node's content in m_char.
+   * */
+  void appendTextChild(int m_char_current_start,int contentLength)
+  {
+    // create a Text Node
+    // %TBD% may be possible to combine with appendNode()to replace the next chunk of code
+    int w0 = TEXT_NODE;
+    // W1: Parent
+    int w1 = currentParent;
+    // W2: Start position within m_char
+    int w2 = m_char_current_start;
+    // W3: Length of the full string
+    int w3 = contentLength;
+
+    int ourslot = appendNode(w0, w1, w2, w3);
+    previousSibling = ourslot;
+  }
+
+  /** Append a comment child at the current insertion point. Assumes that the
+   * actual content of the comment has previously been appended to the m_char
+   * buffer (shared with the builder).
+   *
+   * @param m_char_current_start int Starting offset of node's content in m_char.
+   * @param contentLength int Length of node's content in m_char.
+   * */
+  void appendComment(int m_char_current_start,int contentLength)
+  {
+    // create a Comment Node
+    // %TBD% may be possible to combine with appendNode()to replace the next chunk of code
+    int w0 = COMMENT_NODE;
+    // W1: Parent
+    int w1 = currentParent;
+    // W2: Start position within m_char
+    int w2 = m_char_current_start;
+    // W3: Length of the full string
+    int w3 = contentLength;
+
+    int ourslot = appendNode(w0, w1, w2, w3);
+    previousSibling = ourslot;
+  }
+
+
+  /** Append an Element child at the current insertion point. This
+   * Element then _becomes_ the insertion point; subsequent appends
+   * become its lastChild until an appendEndElement() call is made.
+   *
+   * Assumes that the symbols (local name, namespace URI and prefix)
+   * have already been added to the pools
+   *
+   * Note that this _only_ handles the Element node itself. Attrs and
+   * namespace nodes are unbundled in the ContentHandler layer
+   * and appended separately.
+   *
+   * @param namespaceIndex: Index within the namespaceURI string pool
+   * @param localNameIndex Index within the local name string pool
+   * @param prefixIndex: Index within the prefix string pool
+   * */
+  void appendStartElement(int namespaceIndex,int localNameIndex, int prefixIndex)
+  {
+                // do document root node creation here on the first element, create nodes for
+                // this element and its attributes, store the element, namespace, and attritute
+                // name indexes to the nodes array, keep track of the current node and parent
+                // element used
+
+                // W0  High:  Namespace  Low:  Node Type
+                int w0 = (namespaceIndex << 16) | ELEMENT_NODE;
+                // W1: Parent
+                int w1 = currentParent;
+                // W2: Next  (initialized as 0)
+                int w2 = 0;
+                // W3: Tagname high: prefix Low: local name
+                int w3 = localNameIndex | prefixIndex<<16;
+                /**/System.out.println("set w3="+w3+" "+(w3>>16)+"/"+(w3&0xffff));
+
+                //int ourslot = nodes.appendSlot(w0, w1, w2, w3);
+                int ourslot = appendNode(w0, w1, w2, w3);
+                currentParent = ourslot;
+                previousSibling = 0;
+
+                // set the root element pointer when creating the first element node
+                if (m_docElement == NULL)
+                        m_docElement = ourslot;
+  }
+
+  /** Append a Namespace Declaration child at the current insertion point.
+   * Assumes that the symbols (namespace URI and prefix) have already been
+   * added to the pools
+   *
+   * @param prefixIndex: Index within the prefix string pool
+   * @param namespaceIndex: Index within the namespaceURI string pool
+   * @param isID: If someone really insists on writing a bad DTD, it is
+   * theoretically possible for a namespace declaration to also be declared
+   * as being a node ID. I don't really want to support that stupidity,
+   * but I'm not sure we can refuse to accept it.
+   * */
+  void appendNSDeclaration(int prefixIndex, int namespaceIndex,
+                           boolean isID)
+  {
+    // %REVIEW% I'm assigning this node the "namespace for namespaces"
+    // which the DOM defined. It is expected that the Namespace spec will
+    // adopt this as official. It isn't strictly needed since it's implied
+    // by the nodetype, but for now...
+
+    // %REVIEW% Prefix need not be recorded; it's implied too. But
+    // recording it might simplify the design.
+
+    // %TBD% isID is not currently honored.
+
+    final int namespaceForNamespaces=m_nsNames.stringToIndex("http://www.w3.org/2000/xmlns/");
+
+    // W0  High:  Namespace  Low:  Node Type
+    int w0 = NAMESPACE_NODE | (m_nsNames.stringToIndex("http://www.w3.org/2000/xmlns/")<<16);
+
+    // W1:  Parent
+    int w1 = currentParent;
+    // W2:  CURRENTLY UNUSED -- It's next-sib in attrs, but we have no kids.
+    int w2 = 0;
+    // W3:  namespace name
+    int w3 = namespaceIndex;
+    // Add node
+    int ourslot = appendNode(w0, w1, w2, w3);
+    previousSibling = ourslot;	// Should attributes be previous siblings
+    previousSiblingWasParent = false;
+    return ;//(m_docHandle | ourslot);
+  }
+
+  /** Append an Attribute child at the current insertion
+   * point.  Assumes that the symbols (namespace URI, local name, and
+   * prefix) have already been added to the pools, and that the content has
+   * already been appended to m_char. Note that the attribute's content has
+   * been flattened into a single string; DTM does _NOT_ attempt to model
+   * the details of entity references within attribute values.
+   *
+   * @param namespaceIndex int Index within the namespaceURI string pool
+   * @param localNameIndex int Index within the local name string pool
+   * @param prefixIndex int Index within the prefix string pool
+   * @param isID boolean True if this attribute was declared as an ID
+   * (for use in supporting getElementByID).
+   * @param m_char_current_start int Starting offset of node's content in m_char.
+   * @param contentLength int Length of node's content in m_char.
+   * */
+  void appendAttribute(int namespaceIndex, int localNameIndex, int prefixIndex,
+                       boolean isID,
+                       int m_char_current_start, int contentLength)
+  {
+    // %TBD% isID is not currently honored.
+
+    // W0  High:  Namespace  Low:  Node Type
+    int w0 = ATTRIBUTE_NODE | namespaceIndex<<16;
+
+    // W1:  Parent
+    int w1 = currentParent;
+    // W2:  Next (not yet resolved)
+    int w2 = 0;
+    // W3:  Tagname high: prefix Low: local name
+    int w3 = localNameIndex | prefixIndex<<16;
+    /**/System.out.println("set w3="+w3+" "+(w3>>16)+"/"+(w3&0xffff));
+    // Add node
+    int ourslot = appendNode(w0, w1, w2, w3);
+    previousSibling = ourslot;	// Should attributes be previous siblings
+
+    // Attribute's content is currently appended as a Text Node
+
+    // W0: Node Type
+    w0 = TEXT_NODE;
+    // W1: Parent
+    w1 = ourslot;
+    // W2: Start Position within buffer
+    w2 = m_char_current_start;
+    // W3: Length
+    w3 = contentLength;
+    appendNode(w0, w1, w2, w3);
+
+    // Attrs are Parents
+    previousSiblingWasParent = true;
+    return ;//(m_docHandle | ourslot);
+  }
+
+  /**
+   * This returns a stateless "traverser", that can navigate over an
+   * XPath axis, though not in document order.
+   *
+   * @param axis One of Axes.ANCESTORORSELF, etc.
+   *
+   * @return A DTMAxisIterator, or null if the given axis isn't supported.
+   */
+  public DTMAxisTraverser getAxisTraverser(final int axis)
+  {
+    return null;
+  }
+
+  /**
+   * This is a shortcut to the iterators that implement the
+   * supported XPath axes (only namespace::) is not supported.
+   * Returns a bare-bones iterator that must be initialized
+   * with a start node (using iterator.setStartNode()).
+   *
+   * @param axis One of Axes.ANCESTORORSELF, etc.
+   *
+   * @return A DTMAxisIterator, or null if the given axis isn't supported.
+   */
+  public DTMAxisIterator getAxisIterator(final int axis)
+  {
+    // %TBD%
+    return null;
+  }
+
+  /**
+   * Get an iterator that can navigate over an XPath Axis, predicated by
+   * the extended type ID.
+   *
+   *
+   * @param axis
+   * @param type An extended type ID.
+   *
+   * @return A DTMAxisIterator, or null if the given axis isn't supported.
+   */
+  public DTMAxisIterator getTypedAxisIterator(final int axis, final int type)
+  {
+    // %TBD%
+    return null;
+  }
+
+
+  /** Terminate the element currently acting as an insertion point. Subsequent
+   * insertions will occur as the last child of this element's parent.
+   * */
+  void appendEndElement()
+  {
+    // pop up the stacks
+
+    if (previousSiblingWasParent)
+      nodes.writeEntry(previousSibling, 2, NULL);
+
+    // Pop parentage
+    previousSibling = currentParent;
+    nodes.readSlot(currentParent, gotslot);
+    currentParent = gotslot[1] & 0xFFFF;
+
+    // The element just being finished will be
+    // the previous sibling for the next operation
+    previousSiblingWasParent = true;
+
+    // Pop a level of namespace table
+    // namespaceTable.removeLastElem();
+  }
+
+  /**  Starting a new document. Perform any resets/initialization
+   * not already handled.
+   * */
+  void appendStartDocument()
+  {
+
+    // %TBD% reset slot 0 to indicate ChunkedIntArray reuse or wait for
+    //       the next initDocument().
+    m_docElement = NULL;	 // reset nodeHandle to the root of the actual dtm doc content
+    initDocument(0);
+  }
+
+  /**  All appends to this document have finished; do whatever final
+   * cleanup is needed.
+   * */
+  void appendEndDocument()
+  {
+    done = true;
+    // %TBD% may need to notice the last slot number and slot count to avoid
+    // residual data from provious use of this DTM
+  }
+
+  /**
+   * For the moment all the run time properties are ignored by this
+   * class.
+   *
+   * @param property a <code>String</code> value
+   * @param value an <code>Object</code> value
+   */
+  public void setProperty(String property, Object value)
+  {
+  }
+
+  /**
+   * Source information is not handled yet, so return
+   * <code>null</code> here.
+   *
+   * @param node an <code>int</code> value
+   * @return null
+   */
+  public SourceLocator getSourceLocatorFor(int node)
+  {
+    return null;
+  }
+
+
+  /**
+   * A dummy routine to satisify the abstract interface. If the DTM
+   * implememtation that extends the default base requires notification
+   * of registration, they can override this method.
+   */
+   public void documentRegistration()
+   {
+   }
+
+  /**
+   * A dummy routine to satisify the abstract interface. If the DTM
+   * implememtation that extends the default base requires notification
+   * when the document is being released, they can override this method
+   */
+   public void documentRelease()
+   {
+   }
+
+   /**
+    * Migrate a DTM built with an old DTMManager to a new DTMManager.
+    * After the migration, the new DTMManager will treat the DTM as
+    * one that is built by itself.
+    * This is used to support DTM sharing between multiple transformations.
+    * @param manager the DTMManager
+    */
+   public void migrateTo(DTMManager manager)
+   {
+   }
+
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMManagerDefault.java b/src/main/java/org/apache/xml/dtm/ref/DTMManagerDefault.java
new file mode 100644
index 0000000..5ba4151
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMManagerDefault.java
@@ -0,0 +1,859 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMManagerDefault.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMException;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.dtm.DTMWSFilter;
+import org.apache.xml.dtm.ref.dom2dtm.DOM2DTM;
+import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM;
+import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.SystemIDResolver;
+import org.apache.xml.utils.XMLReaderManager;
+import org.apache.xml.utils.XMLStringFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * The default implementation for the DTMManager.
+ *
+ * %REVIEW% There is currently a reentrancy issue, since the finalizer
+ * for XRTreeFrag (which runs in the GC thread) wants to call
+ * DTMManager.release(), and may do so at the same time that the main
+ * transformation thread is accessing the manager. Our current solution is
+ * to make most of the manager's methods <code>synchronized</code>.
+ * Early tests suggest that doing so is not causing a significant
+ * performance hit in Xalan. However, it should be noted that there
+ * is a possible alternative solution: rewrite release() so it merely
+ * posts a request for release onto a threadsafe queue, and explicitly
+ * process that queue on an infrequent basis during main-thread
+ * activity (eg, when getDTM() is invoked). The downside of that solution
+ * would be a greater delay before the DTM's storage is actually released
+ * for reuse.
+ * */
+public class DTMManagerDefault extends DTMManager
+{
+  //static final boolean JKESS_XNI_EXPERIMENT=true;
+
+  /** Set this to true if you want a dump of the DTM after creation. */
+  private static final boolean DUMPTREE = false;
+
+  /** Set this to true if you want a basic diagnostics. */
+  private static final boolean DEBUG = false;
+
+  /**
+   * Map from DTM identifier numbers to DTM objects that this manager manages.
+   * One DTM may have several prefix numbers, if extended node indexing
+   * is in use; in that case, m_dtm_offsets[] will used to control which
+   * prefix maps to which section of the DTM.
+   * 
+   * This array grows as necessary; see addDTM().
+   * 
+   * This array grows as necessary; see addDTM(). Growth is uncommon... but
+   * access needs to be blindingly fast since it's used in node addressing.
+   */
+  protected DTM m_dtms[] = new DTM[256];
+	
+  /** Map from DTM identifier numbers to offsets. For small DTMs with a 
+   * single identifier, this will always be 0. In overflow addressing, where
+   * additional identifiers are allocated to access nodes beyond the range of
+   * a single Node Handle, this table is used to map the handle's node field
+   * into the actual node identifier.
+   * 
+   * This array grows as necessary; see addDTM().
+   * 
+   * This array grows as necessary; see addDTM(). Growth is uncommon... but
+   * access needs to be blindingly fast since it's used in node addressing.
+   * (And at the moment, that includes accessing it from DTMDefaultBase,
+   * which is why this is not Protected or Private.)
+   */
+  int m_dtm_offsets[] = new int[256];
+
+  /**
+   * The cache for XMLReader objects to be used if the user did not
+   * supply an XMLReader for a SAXSource or supplied a StreamSource.
+   */
+  protected XMLReaderManager m_readerManager = null;
+  
+  /**
+   * The default implementation of ContentHandler, DTDHandler and ErrorHandler.
+   */
+  protected DefaultHandler m_defaultHandler = new DefaultHandler();
+
+  /**
+   * Add a DTM to the DTM table. This convenience call adds it as the 
+   * "base DTM ID", with offset 0. The other version of addDTM should 
+   * be used if you want to add "extended" DTM IDs with nonzero offsets.
+   *
+   * @param dtm Should be a valid reference to a DTM.
+   * @param id Integer DTM ID to be bound to this DTM
+   */
+  synchronized public void addDTM(DTM dtm, int id) {	addDTM(dtm,id,0); }
+
+	
+  /**
+   * Add a DTM to the DTM table.
+   *
+   * @param dtm Should be a valid reference to a DTM.
+   * @param id Integer DTM ID to be bound to this DTM.
+   * @param offset Integer addressing offset. The internal DTM Node ID is
+   * obtained by adding this offset to the node-number field of the 
+   * public DTM Handle. For the first DTM ID accessing each DTM, this is 0;
+   * for overflow addressing it will be a multiple of 1<<IDENT_DTM_NODE_BITS.
+   */
+  synchronized public void addDTM(DTM dtm, int id, int offset)
+  {
+		if(id>=IDENT_MAX_DTMS)
+		{
+			// TODO: %REVIEW% Not really the right error message.
+	    throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null)); //"No more DTM IDs are available!");			 
+		}
+		
+		// We used to just allocate the array size to IDENT_MAX_DTMS.
+		// But we expect to increase that to 16 bits, and I'm not willing
+		// to allocate that much space unless needed. We could use one of our
+		// handy-dandy Fast*Vectors, but this will do for now.
+		// %REVIEW%
+		int oldlen=m_dtms.length;
+		if(oldlen<=id)
+		{
+			// Various growth strategies are possible. I think we don't want 
+			// to over-allocate excessively, and I'm willing to reallocate
+			// more often to get that. See also Fast*Vector classes.
+			//
+			// %REVIEW% Should throw a more diagnostic error if we go over the max...
+			int newlen=Math.min((id+256),IDENT_MAX_DTMS);
+
+			DTM new_m_dtms[] = new DTM[newlen];
+			System.arraycopy(m_dtms,0,new_m_dtms,0,oldlen);
+			m_dtms=new_m_dtms;
+			int new_m_dtm_offsets[] = new int[newlen];
+			System.arraycopy(m_dtm_offsets,0,new_m_dtm_offsets,0,oldlen);
+			m_dtm_offsets=new_m_dtm_offsets;
+		}
+		
+    m_dtms[id] = dtm;
+		m_dtm_offsets[id]=offset;
+    dtm.documentRegistration();
+		// The DTM should have been told who its manager was when we created it.
+		// Do we need to allow for adopting DTMs _not_ created by this manager?
+  }
+
+  /**
+   * Get the first free DTM ID available. %OPT% Linear search is inefficient!
+   */
+  synchronized public int getFirstFreeDTMID()
+  {
+    int n = m_dtms.length;
+    for (int i = 1; i < n; i++)
+    {
+      if(null == m_dtms[i])
+      {
+        return i;
+      }
+    }
+		return n; // count on addDTM() to throw exception if out of range
+  }
+
+  /**
+   * The default table for exandedNameID lookups.
+   */
+  private ExpandedNameTable m_expandedNameTable =
+    new ExpandedNameTable();
+
+  /**
+   * Constructor DTMManagerDefault
+   *
+   */
+  public DTMManagerDefault(){}
+
+
+  /**
+   * Get an instance of a DTM, loaded with the content from the
+   * specified source.  If the unique flag is true, a new instance will
+   * always be returned.  Otherwise it is up to the DTMManager to return a
+   * new instance or an instance that it already created and may be being used
+   * by someone else.
+   * 
+   * A bit of magic in this implementation: If the source is null, unique is true,
+   * and incremental and doIndexing are both false, we return an instance of
+   * SAX2RTFDTM, which see.
+   * 
+   * (I think more parameters will need to be added for error handling, and entity
+   * resolution, and more explicit control of the RTF situation).
+   *
+   * @param source the specification of the source object.
+   * @param unique true if the returned DTM must be unique, probably because it
+   * is going to be mutated.
+   * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
+   *                         be null.
+   * @param incremental true if the DTM should be built incrementally, if
+   *                    possible.
+   * @param doIndexing true if the caller considers it worth it to use
+   *                   indexing schemes.
+   *
+   * @return a non-null DTM reference.
+   */
+  synchronized public DTM getDTM(Source source, boolean unique,
+                                 DTMWSFilter whiteSpaceFilter,
+                                 boolean incremental, boolean doIndexing)
+  {
+
+    if(DEBUG && null != source)
+      System.out.println("Starting "+
+                         (unique ? "UNIQUE" : "shared")+
+                         " source: "+source.getSystemId()
+                         );
+
+    XMLStringFactory xstringFactory = m_xsf;
+    int dtmPos = getFirstFreeDTMID();
+    int documentID = dtmPos << IDENT_DTM_NODE_BITS;
+
+    if ((null != source) && source instanceof DOMSource)
+    {
+      DOM2DTM dtm = new DOM2DTM(this, (DOMSource) source, documentID,
+                                whiteSpaceFilter, xstringFactory, doIndexing);
+
+      addDTM(dtm, dtmPos, 0);
+
+      //      if (DUMPTREE)
+      //      {
+      //        dtm.dumpDTM();
+      //      }
+
+      return dtm;
+    }
+    else
+    {
+      boolean isSAXSource = (null != source)
+        ? (source instanceof SAXSource) : true;
+      boolean isStreamSource = (null != source)
+        ? (source instanceof StreamSource) : false;
+
+      if (isSAXSource || isStreamSource) {
+        XMLReader reader = null;
+        SAX2DTM dtm;
+
+        try {
+          InputSource xmlSource;
+
+          if (null == source) {
+            xmlSource = null;
+          } else {
+            reader = getXMLReader(source);
+            xmlSource = SAXSource.sourceToInputSource(source);
+
+            String urlOfSource = xmlSource.getSystemId();
+
+            if (null != urlOfSource) {
+              try {
+                urlOfSource = SystemIDResolver.getAbsoluteURI(urlOfSource);
+              } catch (Exception e) {
+                // %REVIEW% Is there a better way to send a warning?
+                System.err.println("Can not absolutize URL: " + urlOfSource);
+              }
+
+              xmlSource.setSystemId(urlOfSource);
+            }
+          }
+
+          if (source==null && unique && !incremental && !doIndexing) {
+            // Special case to support RTF construction into shared DTM.
+            // It should actually still work for other uses,
+            // but may be slightly deoptimized relative to the base
+            // to allow it to deal with carrying multiple documents.
+            //
+            // %REVIEW% This is a sloppy way to request this mode;
+            // we need to consider architectural improvements.
+            dtm = new SAX2RTFDTM(this, source, documentID, whiteSpaceFilter,
+                                 xstringFactory, doIndexing);
+          }
+          /**************************************************************
+          // EXPERIMENTAL 3/22/02
+          else if(JKESS_XNI_EXPERIMENT && m_incremental) {        	
+            dtm = new XNI2DTM(this, source, documentID, whiteSpaceFilter,
+                              xstringFactory, doIndexing);
+          }
+          **************************************************************/
+          // Create the basic SAX2DTM.
+          else {
+            dtm = new SAX2DTM(this, source, documentID, whiteSpaceFilter,
+                              xstringFactory, doIndexing);
+          }
+
+          // Go ahead and add the DTM to the lookup table.  This needs to be
+          // done before any parsing occurs. Note offset 0, since we've just
+          // created a new DTM.
+          addDTM(dtm, dtmPos, 0);
+
+
+          boolean haveXercesParser =
+                     (null != reader)
+                     && (reader.getClass()
+                               .getName()
+                               .equals("org.apache.xerces.parsers.SAXParser") );
+        
+          if (haveXercesParser) {
+            incremental = true;  // No matter what.  %REVIEW%
+          }
+        
+          // If the reader is null, but they still requested an incremental
+          // build, then we still want to set up the IncrementalSAXSource stuff.
+          if (m_incremental && incremental
+               /* || ((null == reader) && incremental) */) {
+            IncrementalSAXSource coParser=null;
+
+            if (haveXercesParser) {
+              // IncrementalSAXSource_Xerces to avoid threading.
+              try {
+                coParser =(IncrementalSAXSource)
+                  Class.forName("org.apache.xml.dtm.ref.IncrementalSAXSource_Xerces").newInstance();  
+              }  catch( Exception ex ) {
+                ex.printStackTrace();
+                coParser=null;
+              }
+            }
+
+            if (coParser==null ) {
+              // Create a IncrementalSAXSource to run on the secondary thread.
+              if (null == reader) {
+                coParser = new IncrementalSAXSource_Filter();
+              } else {
+                IncrementalSAXSource_Filter filter =
+                         new IncrementalSAXSource_Filter();
+                filter.setXMLReader(reader);
+                coParser=filter;
+              }
+            }
+
+			
+            /**************************************************************
+            // EXPERIMENTAL 3/22/02
+            if (JKESS_XNI_EXPERIMENT && m_incremental &&
+                  dtm instanceof XNI2DTM && 
+                  coParser instanceof IncrementalSAXSource_Xerces) {
+                org.apache.xerces.xni.parser.XMLPullParserConfiguration xpc=
+                      ((IncrementalSAXSource_Xerces)coParser)
+                                           .getXNIParserConfiguration();
+              if (xpc!=null) {
+                // Bypass SAX; listen to the XNI stream
+                ((XNI2DTM)dtm).setIncrementalXNISource(xpc);
+              } else {
+                  // Listen to the SAX stream (will fail, diagnostically...)
+                dtm.setIncrementalSAXSource(coParser);
+              }
+            } else
+            ***************************************************************/
+          
+            // Have the DTM set itself up as IncrementalSAXSource's listener.
+            dtm.setIncrementalSAXSource(coParser);
+
+            if (null == xmlSource) {
+
+              // Then the user will construct it themselves.
+              return dtm;
+            }
+
+            if (null == reader.getErrorHandler()) {
+              reader.setErrorHandler(dtm);
+            }
+            reader.setDTDHandler(dtm);
+
+            try {
+              // Launch parsing coroutine.  Launches a second thread,
+              // if we're using IncrementalSAXSource.filter().
+
+              coParser.startParse(xmlSource);
+            } catch (RuntimeException re) {
+
+              dtm.clearCoRoutine();
+
+              throw re;
+            } catch (Exception e) {
+
+              dtm.clearCoRoutine();
+
+              throw new org.apache.xml.utils.WrappedRuntimeException(e);
+            }
+          } else {
+            if (null == reader) {
+
+              // Then the user will construct it themselves.
+              return dtm;
+            }
+
+            // not incremental
+            reader.setContentHandler(dtm);
+            reader.setDTDHandler(dtm);
+            if (null == reader.getErrorHandler()) {
+              reader.setErrorHandler(dtm);
+            }
+
+            try {
+              reader.setProperty(
+                               "http://xml.org/sax/properties/lexical-handler",
+                               dtm);
+            } catch (SAXNotRecognizedException e){}
+              catch (SAXNotSupportedException e){}
+
+            try {
+              reader.parse(xmlSource);
+            } catch (RuntimeException re) {
+              dtm.clearCoRoutine();
+
+              throw re;
+            } catch (Exception e) {
+              dtm.clearCoRoutine();
+
+              throw new org.apache.xml.utils.WrappedRuntimeException(e);
+            }
+          }
+
+          if (DUMPTREE) {
+            System.out.println("Dumping SAX2DOM");
+            dtm.dumpDTM(System.err);
+          }
+
+          return dtm;
+        } finally {
+          // Reset the ContentHandler, DTDHandler, ErrorHandler to the DefaultHandler
+          // after creating the DTM.
+          if (reader != null && !(m_incremental && incremental)) {
+            reader.setContentHandler(m_defaultHandler);
+            reader.setDTDHandler(m_defaultHandler);
+            reader.setErrorHandler(m_defaultHandler);
+            
+            // Reset the LexicalHandler to null after creating the DTM.
+            try {
+              reader.setProperty("http://xml.org/sax/properties/lexical-handler", null);
+            }
+            catch (Exception e) {}
+          }
+          releaseXMLReader(reader);
+        }
+      } else {
+
+        // It should have been handled by a derived class or the caller
+        // made a mistake.
+        throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NOT_SUPPORTED, new Object[]{source})); //"Not supported: " + source);
+      }
+    }
+  }
+
+  /**
+   * Given a W3C DOM node, try and return a DTM handle.
+   * Note: calling this may be non-optimal, and there is no guarantee that
+   * the node will be found in any particular DTM.
+   *
+   * @param node Non-null reference to a DOM node.
+   *
+   * @return a valid DTM handle.
+   */
+  synchronized public int getDTMHandleFromNode(org.w3c.dom.Node node)
+  {
+    if(null == node)
+      throw new IllegalArgumentException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NODE_NON_NULL, null)); //"node must be non-null for getDTMHandleFromNode!");
+
+    if (node instanceof org.apache.xml.dtm.ref.DTMNodeProxy)
+      return ((org.apache.xml.dtm.ref.DTMNodeProxy) node).getDTMNodeNumber();
+		
+    else
+    {
+      // Find the DOM2DTMs wrapped around this Document (if any)
+      // and check whether they contain the Node in question.
+      //
+      // NOTE that since a DOM2DTM may represent a subtree rather
+      // than a full document, we have to be prepared to check more
+      // than one -- and there is no guarantee that we will find
+      // one that contains ancestors or siblings of the node we're
+      // seeking.
+      //
+      // %REVIEW% We could search for the one which contains this
+      // node at the deepest level, and thus covers the widest
+      // subtree, but that's going to entail additional work
+      // checking more DTMs... and getHandleOfNode is not a
+      // cheap operation in most implementations.
+			//
+			// TODO: %REVIEW% If overflow addressing, we may recheck a DTM
+			// already examined. Ouch. But with the increased number of DTMs,
+			// scanning back to check this is painful. 
+			// POSSIBLE SOLUTIONS: 
+			//   Generate a list of _unique_ DTM objects?
+			//   Have each DTM cache last DOM node search?
+			int max = m_dtms.length;
+      for(int i = 0; i < max; i++)
+        {
+          DTM thisDTM=m_dtms[i];
+          if((null != thisDTM) && thisDTM instanceof DOM2DTM)
+          {
+            int handle=((DOM2DTM)thisDTM).getHandleOfNode(node);
+            if(handle!=DTM.NULL) return handle;
+          }
+         }
+
+			// Not found; generate a new DTM.
+			//
+			// %REVIEW% Is this really desirable, or should we return null
+			// and make folks explicitly instantiate from a DOMSource? The
+			// latter is more work but gives the caller the opportunity to
+			// explicitly add the DTM to a DTMManager... and thus to know when
+			// it can be discarded again, which is something we need to pay much
+			// more attention to. (Especially since only DTMs which are assigned
+			// to a manager can use the overflow addressing scheme.)
+			//
+			// %BUG% If the source node was a DOM2DTM$defaultNamespaceDeclarationNode
+			// and the DTM wasn't registered with this DTMManager, we will create
+			// a new DTM and _still_ not be able to find the node (since it will
+			// be resynthesized). Another reason to push hard on making all DTMs
+			// be managed DTMs.
+
+			// Since the real root of our tree may be a DocumentFragment, we need to
+      // use getParent to find the root, instead of getOwnerDocument.  Otherwise
+      // DOM2DTM#getHandleOfNode will be very unhappy.
+      Node root = node;
+      Node p = (root.getNodeType() == Node.ATTRIBUTE_NODE) ? ((org.w3c.dom.Attr)root).getOwnerElement() : root.getParentNode();
+      for (; p != null; p = p.getParentNode())
+      {
+        root = p;
+      }
+
+      DOM2DTM dtm = (DOM2DTM) getDTM(new javax.xml.transform.dom.DOMSource(root),
+																		 false, null, true, true);
+
+      int handle;
+      
+      if(node instanceof org.apache.xml.dtm.ref.dom2dtm.DOM2DTMdefaultNamespaceDeclarationNode)
+      {
+				// Can't return the same node since it's unique to a specific DTM, 
+				// but can return the equivalent node -- find the corresponding 
+				// Document Element, then ask it for the xml: namespace decl.
+				handle=dtm.getHandleOfNode(((org.w3c.dom.Attr)node).getOwnerElement());
+				handle=dtm.getAttributeNode(handle,node.getNamespaceURI(),node.getLocalName());
+      }
+      else
+				handle = ((DOM2DTM)dtm).getHandleOfNode(node);
+
+      if(DTM.NULL == handle)
+        throw new RuntimeException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COULD_NOT_RESOLVE_NODE, null)); //"Could not resolve the node to a handle!");
+
+      return handle;
+    }
+  }
+
+  /**
+   * This method returns the SAX2 parser to use with the InputSource
+   * obtained from this URI.
+   * It may return null if any SAX2-conformant XML parser can be used,
+   * or if getInputSource() will also return null. The parser must
+   * be free for use (i.e., not currently in use for another parse().
+   * After use of the parser is completed, the releaseXMLReader(XMLReader)
+   * must be called.
+   *
+   * @param inputSource The value returned from the URIResolver.
+   * @return  a SAX2 XMLReader to use to resolve the inputSource argument.
+   *
+   * @return non-null XMLReader reference ready to parse.
+   */
+  synchronized public XMLReader getXMLReader(Source inputSource)
+  {
+
+    try
+    {
+      XMLReader reader = (inputSource instanceof SAXSource)
+                         ? ((SAXSource) inputSource).getXMLReader() : null;
+
+      // If user did not supply a reader, ask for one from the reader manager
+      if (null == reader) {
+        if (m_readerManager == null) {
+            m_readerManager = XMLReaderManager.getInstance();
+        }
+
+        reader = m_readerManager.getXMLReader();
+      }
+
+      return reader;
+
+    } catch (SAXException se) {
+      throw new DTMException(se.getMessage(), se);
+    }
+  }
+
+  /**
+   * Indicates that the XMLReader object is no longer in use for the transform.
+   *
+   * Note that the getXMLReader method may return an XMLReader that was
+   * specified on the SAXSource object by the application code.  Such a
+   * reader should still be passed to releaseXMLReader, but the reader manager
+   * will only re-use XMLReaders that it created.
+   *
+   * @param reader The XMLReader to be released.
+   */
+  synchronized public void releaseXMLReader(XMLReader reader) {
+    if (m_readerManager != null) {
+      m_readerManager.releaseXMLReader(reader);
+    }
+  }
+
+  /**
+   * Return the DTM object containing a representation of this node.
+   *
+   * @param nodeHandle DTM Handle indicating which node to retrieve
+   *
+   * @return a reference to the DTM object containing this node.
+   */
+  synchronized public DTM getDTM(int nodeHandle)
+  {
+    try
+    {
+      // Performance critical function.
+      return m_dtms[nodeHandle >>> IDENT_DTM_NODE_BITS];
+    }
+    catch(java.lang.ArrayIndexOutOfBoundsException e)
+    {
+      if(nodeHandle==DTM.NULL)
+				return null;		// Accept as a special case.
+      else
+				throw e;		// Programming error; want to know about it.
+    }    
+  }
+
+  /**
+   * Given a DTM, find the ID number in the DTM tables which addresses
+   * the start of the document. If overflow addressing is in use, other
+   * DTM IDs may also be assigned to this DTM.
+   *
+   * @param dtm The DTM which (hopefully) contains this node.
+   *
+   * @return The DTM ID (as the high bits of a NodeHandle, not as our
+   * internal index), or -1 if the DTM doesn't belong to this manager.
+   */
+  synchronized public int getDTMIdentity(DTM dtm)
+  {
+	// Shortcut using DTMDefaultBase's extension hooks
+	// %REVIEW% Should the lookup be part of the basic DTM API?
+	if(dtm instanceof DTMDefaultBase)
+	{
+		DTMDefaultBase dtmdb=(DTMDefaultBase)dtm;
+		if(dtmdb.getManager()==this)
+			return dtmdb.getDTMIDs().elementAt(0);
+		else
+			return -1;
+	}
+				
+    int n = m_dtms.length;
+
+    for (int i = 0; i < n; i++)
+    {
+      DTM tdtm = m_dtms[i];
+
+      if (tdtm == dtm && m_dtm_offsets[i]==0)
+        return i << IDENT_DTM_NODE_BITS;
+    }
+
+    return -1;
+  }
+
+  /**
+   * Release the DTMManager's reference(s) to a DTM, making it unmanaged.
+   * This is typically done as part of returning the DTM to the heap after
+   * we're done with it.
+   *
+   * @param dtm the DTM to be released.
+   * 
+   * @param shouldHardDelete If false, this call is a suggestion rather than an
+   * order, and we may not actually release the DTM. This is intended to 
+   * support intelligent caching of documents... which is not implemented
+   * in this version of the DTM manager.
+   *
+   * @return true if the DTM was released, false if shouldHardDelete was set
+   * and we decided not to.
+   */
+  synchronized public boolean release(DTM dtm, boolean shouldHardDelete)
+  {
+    if(DEBUG)
+    {
+      System.out.println("Releasing "+
+			 (shouldHardDelete ? "HARD" : "soft")+
+			 " dtm="+
+			 // Following shouldn't need a nodeHandle, but does...
+			 // and doesn't seem to report the intended value
+			 dtm.getDocumentBaseURI()
+			 );
+    }
+
+    if (dtm instanceof SAX2DTM)
+    {
+      ((SAX2DTM) dtm).clearCoRoutine();
+    }
+
+		// Multiple DTM IDs may be assigned to a single DTM. 
+		// The Right Answer is to ask which (if it supports
+		// extension, the DTM will need a list anyway). The 
+		// Wrong Answer, applied if the DTM can't help us,
+		// is to linearly search them all; this may be very
+		// painful.
+		//
+		// %REVIEW% Should the lookup move up into the basic DTM API?
+		if(dtm instanceof DTMDefaultBase)
+		{
+			org.apache.xml.utils.SuballocatedIntVector ids=((DTMDefaultBase)dtm).getDTMIDs();
+			for(int i=ids.size()-1;i>=0;--i)
+				m_dtms[ids.elementAt(i)>>>DTMManager.IDENT_DTM_NODE_BITS]=null;
+		}
+		else
+		{
+			int i = getDTMIdentity(dtm);
+		    if (i >= 0)
+			{
+				m_dtms[i >>> DTMManager.IDENT_DTM_NODE_BITS] = null;
+			}
+		}
+
+    dtm.documentRelease();
+    return true;
+  }
+
+  /**
+   * Method createDocumentFragment
+   *
+   *
+   * NEEDSDOC (createDocumentFragment) @return
+   */
+  synchronized public DTM createDocumentFragment()
+  {
+
+    try
+    {
+      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+
+      dbf.setNamespaceAware(true);
+
+      DocumentBuilder db = dbf.newDocumentBuilder();
+      Document doc = db.newDocument();
+      Node df = doc.createDocumentFragment();
+
+      return getDTM(new DOMSource(df), true, null, false, false);
+    }
+    catch (Exception e)
+    {
+      throw new DTMException(e);
+    }
+  }
+
+  /**
+   * NEEDSDOC Method createDTMIterator
+   *
+   *
+   * NEEDSDOC @param whatToShow
+   * NEEDSDOC @param filter
+   * NEEDSDOC @param entityReferenceExpansion
+   *
+   * NEEDSDOC (createDTMIterator) @return
+   */
+  synchronized public DTMIterator createDTMIterator(int whatToShow, DTMFilter filter,
+                                       boolean entityReferenceExpansion)
+  {
+
+    /** @todo: implement this org.apache.xml.dtm.DTMManager abstract method */
+    return null;
+  }
+
+  /**
+   * NEEDSDOC Method createDTMIterator
+   *
+   *
+   * NEEDSDOC @param xpathString
+   * NEEDSDOC @param presolver
+   *
+   * NEEDSDOC (createDTMIterator) @return
+   */
+  synchronized public DTMIterator createDTMIterator(String xpathString,
+                                       PrefixResolver presolver)
+  {
+
+    /** @todo: implement this org.apache.xml.dtm.DTMManager abstract method */
+    return null;
+  }
+
+  /**
+   * NEEDSDOC Method createDTMIterator
+   *
+   *
+   * NEEDSDOC @param node
+   *
+   * NEEDSDOC (createDTMIterator) @return
+   */
+  synchronized public DTMIterator createDTMIterator(int node)
+  {
+
+    /** @todo: implement this org.apache.xml.dtm.DTMManager abstract method */
+    return null;
+  }
+
+  /**
+   * NEEDSDOC Method createDTMIterator
+   *
+   *
+   * NEEDSDOC @param xpathCompiler
+   * NEEDSDOC @param pos
+   *
+   * NEEDSDOC (createDTMIterator) @return
+   */
+  synchronized public DTMIterator createDTMIterator(Object xpathCompiler, int pos)
+  {
+
+    /** @todo: implement this org.apache.xml.dtm.DTMManager abstract method */
+    return null;
+  }
+
+  /**
+   * return the expanded name table.
+   *
+   * NEEDSDOC @param dtm
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public ExpandedNameTable getExpandedNameTable(DTM dtm)
+  {
+    return m_expandedNameTable;
+  }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMNamedNodeMap.java b/src/main/java/org/apache/xml/dtm/ref/DTMNamedNodeMap.java
new file mode 100644
index 0000000..4bb1322
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMNamedNodeMap.java
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMNamedNodeMap.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.DTM;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * DTMNamedNodeMap is a quickie (as opposed to quick) implementation of the DOM's
+ * NamedNodeMap interface, intended to support DTMProxy's getAttributes()
+ * call.
+ * <p>
+ * ***** Note: this does _not_ current attempt to cache any of the data;
+ * if you ask for attribute 27 and then 28, you'll have to rescan the first
+ * 27. It should probably at least keep track of the last one retrieved,
+ * and possibly buffer the whole array.
+ * <p>
+ * ***** Also note that there's no fastpath for the by-name query; we search
+ * linearly until we find it or fail to find it. Again, that could be
+ * optimized at some cost in object creation/storage.
+ * @xsl.usage internal
+ */
+public class DTMNamedNodeMap implements NamedNodeMap
+{
+
+  /** The DTM for this node. */
+  DTM dtm;
+
+  /** The DTM element handle. */
+  int element;
+
+  /** The number of nodes in this map. */
+  short m_count = -1;
+
+  /**
+   * Create a getAttributes NamedNodeMap for a given DTM element node
+   *
+   * @param dtm The DTM Reference, must be non-null.
+   * @param element The DTM element handle.
+   */
+  public DTMNamedNodeMap(DTM dtm, int element)
+  {
+    this.dtm = dtm;
+    this.element = element;
+  }
+
+  /**
+   * Return the number of Attributes on this Element
+   *
+   * @return The number of nodes in this map.
+   */
+  public int getLength()
+  {
+
+    if (m_count == -1)
+    {
+      short count = 0;
+
+      for (int n = dtm.getFirstAttribute(element); n != -1;
+              n = dtm.getNextAttribute(n))
+      {
+        ++count;
+      }
+
+      m_count = count;
+    }
+
+    return (int) m_count;
+  }
+
+  /**
+   * Retrieves a node specified by name.
+   * @param name The <code>nodeName</code> of a node to retrieve.
+   * @return A <code>Node</code> (of any type) with the specified
+   *   <code>nodeName</code>, or <code>null</code> if it does not identify
+   *   any node in this map.
+   */
+  public Node getNamedItem(String name)
+  {
+
+    for (int n = dtm.getFirstAttribute(element); n != DTM.NULL;
+            n = dtm.getNextAttribute(n))
+    {
+      if (dtm.getNodeName(n).equals(name))
+        return dtm.getNode(n);
+    }
+
+    return null;
+  }
+
+  /**
+   * Returns the <code>index</code>th item in the map. If <code>index</code>
+   * is greater than or equal to the number of nodes in this map, this
+   * returns <code>null</code>.
+   * @param i The index of the requested item.
+   * @return The node at the <code>index</code>th position in the map, or
+   *   <code>null</code> if that is not a valid index.
+   */
+  public Node item(int i)
+  {
+
+    int count = 0;
+
+    for (int n = dtm.getFirstAttribute(element); n != -1;
+            n = dtm.getNextAttribute(n))
+    {
+      if (count == i)
+        return dtm.getNode(n);
+      else
+        ++count;
+    }
+
+    return null;
+  }
+
+  /**
+   * Adds a node using its <code>nodeName</code> attribute. If a node with
+   * that name is already present in this map, it is replaced by the new
+   * one.
+   * <br>As the <code>nodeName</code> attribute is used to derive the name
+   * which the node must be stored under, multiple nodes of certain types
+   * (those that have a "special" string value) cannot be stored as the
+   * names would clash. This is seen as preferable to allowing nodes to be
+   * aliased.
+   * @param newNode node to store in this map. The node will later be
+   *   accessible using the value of its <code>nodeName</code> attribute.
+   *
+   * @return If the new <code>Node</code> replaces an existing node the
+   *   replaced <code>Node</code> is returned, otherwise <code>null</code>
+   *   is returned.
+   * @exception DOMException
+   *   WRONG_DOCUMENT_ERR: Raised if <code>arg</code> was created from a
+   *   different document than the one that created this map.
+   *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly.
+   *   <br>INUSE_ATTRIBUTE_ERR: Raised if <code>arg</code> is an
+   *   <code>Attr</code> that is already an attribute of another
+   *   <code>Element</code> object. The DOM user must explicitly clone
+   *   <code>Attr</code> nodes to re-use them in other elements.
+   */
+  public Node setNamedItem(Node newNode)
+  {
+    throw new DTMException(DTMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   * Removes a node specified by name. When this map contains the attributes
+   * attached to an element, if the removed attribute is known to have a
+   * default value, an attribute immediately appears containing the
+   * default value as well as the corresponding namespace URI, local name,
+   * and prefix when applicable.
+   * @param name The <code>nodeName</code> of the node to remove.
+   *
+   * @return The node removed from this map if a node with such a name
+   *   exists.
+   * @exception DOMException
+   *   NOT_FOUND_ERR: Raised if there is no node named <code>name</code> in
+   *   this map.
+   *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly.
+   */
+  public Node removeNamedItem(String name)
+  {
+    throw new DTMException(DTMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   * Retrieves a node specified by local name and namespace URI. HTML-only
+   * DOM implementations do not need to implement this method.
+   * @param namespaceURI The namespace URI of the node to retrieve.
+   * @param localName The local name of the node to retrieve.
+   *
+   * @return A <code>Node</code> (of any type) with the specified local
+   *   name and namespace URI, or <code>null</code> if they do not
+   *   identify any node in this map.
+   * @since DOM Level 2
+   */
+  public Node getNamedItemNS(String namespaceURI, String localName)
+  {
+       Node retNode = null;
+       for (int n = dtm.getFirstAttribute(element); n != DTM.NULL;
+                       n = dtm.getNextAttribute(n))
+       {
+         if (localName.equals(dtm.getLocalName(n)))
+         {
+           String nsURI = dtm.getNamespaceURI(n); 
+           if ((namespaceURI == null && nsURI == null)
+                  || (namespaceURI != null && namespaceURI.equals(nsURI)))
+           {
+             retNode = dtm.getNode(n);
+             break;
+           }
+         }
+       }
+       return retNode;
+  }
+
+  /**
+   * Adds a node using its <code>namespaceURI</code> and
+   * <code>localName</code>. If a node with that namespace URI and that
+   * local name is already present in this map, it is replaced by the new
+   * one.
+   * <br>HTML-only DOM implementations do not need to implement this method.
+   * @param arg A node to store in this map. The node will later be
+   *   accessible using the value of its <code>namespaceURI</code> and
+   *   <code>localName</code> attributes.
+   *
+   * @return If the new <code>Node</code> replaces an existing node the
+   *   replaced <code>Node</code> is returned, otherwise <code>null</code>
+   *   is returned.
+   * @exception DOMException
+   *   WRONG_DOCUMENT_ERR: Raised if <code>arg</code> was created from a
+   *   different document than the one that created this map.
+   *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly.
+   *   <br>INUSE_ATTRIBUTE_ERR: Raised if <code>arg</code> is an
+   *   <code>Attr</code> that is already an attribute of another
+   *   <code>Element</code> object. The DOM user must explicitly clone
+   *   <code>Attr</code> nodes to re-use them in other elements.
+   * @since DOM Level 2
+   */
+  public Node setNamedItemNS(Node arg) throws DOMException
+  {
+    throw new DTMException(DTMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   * Removes a node specified by local name and namespace URI. A removed
+   * attribute may be known to have a default value when this map contains
+   * the attributes attached to an element, as returned by the attributes
+   * attribute of the <code>Node</code> interface. If so, an attribute
+   * immediately appears containing the default value as well as the
+   * corresponding namespace URI, local name, and prefix when applicable.
+   * <br>HTML-only DOM implementations do not need to implement this method.
+   * 
+   * @param namespaceURI The namespace URI of the node to remove.
+   * @param localName The local name of the node to remove.
+   *
+   * @return The node removed from this map if a node with such a local
+   *   name and namespace URI exists.
+   * @exception DOMException
+   *   NOT_FOUND_ERR: Raised if there is no node with the specified
+   *   <code>namespaceURI</code> and <code>localName</code> in this map.
+   *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly.
+   * @since DOM Level 2
+   */
+  public Node removeNamedItemNS(String namespaceURI, String localName)
+          throws DOMException
+  {
+    throw new DTMException(DTMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   * Simple implementation of DOMException.
+   * @xsl.usage internal
+   */
+  public class DTMException extends org.w3c.dom.DOMException
+  {
+          static final long serialVersionUID = -8290238117162437678L;
+    /**
+     * Constructs a DOM/DTM exception.
+     *
+     * @param code
+     * @param message
+     */
+    public DTMException(short code, String message)
+    {
+      super(code, message);
+    }
+
+    /**
+     * Constructor DTMException
+     *
+     *
+     * @param code
+     */
+    public DTMException(short code)
+    {
+      super(code, "");
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMNodeIterator.java b/src/main/java/org/apache/xml/dtm/ref/DTMNodeIterator.java
new file mode 100644
index 0000000..a482757
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMNodeIterator.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMNodeIterator.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMDOMException;
+import org.apache.xml.dtm.DTMIterator;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeFilter;
+
+/**
+ * <code>DTMNodeIterator</code> gives us an implementation of the 
+ * DTMNodeIterator which returns DOM nodes.
+ *
+ * Please note that this is not necessarily equivlaent to a DOM
+ * NodeIterator operating over the same document. In particular:
+ * <ul>
+ *
+ * <li>If there are several Text nodes in logical succession (ie,
+ * across CDATASection and EntityReference boundaries), we will return
+ * only the first; the caller is responsible for stepping through
+ * them.
+ * (%REVIEW% Provide a convenience routine here to assist, pending
+ * proposed DOM Level 3 getAdjacentText() operation?) </li>
+ *
+ * <li>Since the whole XPath/XSLT architecture assumes that the source
+ * document is not altered while we're working with it, we do not
+ * promise to implement the DOM NodeIterator's "maintain current
+ * position" response to document mutation. </li>
+ *
+ * <li>Since our design for XPath NodeIterators builds a stateful
+ * filter directly into the traversal object, getNodeFilter() is not
+ * supported.</li>
+ *
+ * </ul>
+ *
+ * <p>State: In progress!!</p>
+ * */
+public class DTMNodeIterator implements org.w3c.dom.traversal.NodeIterator
+{
+  private DTMIterator dtm_iter;
+  private boolean valid=true;
+
+  //================================================================
+  // Methods unique to this class
+
+  /** Public constructor: Wrap a DTMNodeIterator around an existing
+   * and preconfigured DTMIterator
+   * */
+  public DTMNodeIterator(DTMIterator dtmIterator)
+    {
+      try
+      {
+        dtm_iter=(DTMIterator)dtmIterator.clone();
+      }
+      catch(CloneNotSupportedException cnse)
+      {
+        throw new org.apache.xml.utils.WrappedRuntimeException(cnse);
+      }
+    }
+
+  /** Access the wrapped DTMIterator. I'm not sure whether anyone will
+   * need this or not, but let's write it and think about it.
+   * */
+  public DTMIterator getDTMIterator()
+    {
+      return dtm_iter;
+    }
+  
+
+  //================================================================
+  // org.w3c.dom.traversal.NodeFilter API follows
+
+  /** Detaches the NodeIterator from the set which it iterated over,
+   * releasing any computational resources and placing the iterator in
+   * the INVALID state.
+   * */
+  public void detach() 
+    {
+      // Theoretically, we could release dtm_iter at this point. But
+      // some of the operations may still want to consult it even though
+      // navigation is now invalid.
+      valid=false;
+    }
+
+  /** The value of this flag determines whether the children
+   * of entity reference nodes are visible to the iterator.
+   *
+   * @return false, always (the DTM model flattens entity references)
+   * */
+  public boolean getExpandEntityReferences()
+    {
+      return false;
+    }
+  
+  /** Return a handle to the filter used to screen nodes.
+   *
+   * This is ill-defined in Xalan's usage of Nodeiterator, where we have
+   * built stateful XPath-based filtering directly into the traversal
+   * object. We could return something which supports the NodeFilter interface
+   * and allows querying whether a given node would be permitted if it appeared
+   * as our next node, but in the current implementation that would be very
+   * complex -- and just isn't all that useful.
+   * 
+   * @throws DOMException -- NOT_SUPPORTED_ERROR because I can't think
+   * of anything more useful to do in this case
+   * */
+  public NodeFilter getFilter() 
+    {
+      throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+    }
+  
+
+  /** @return The root node of the NodeIterator, as specified
+   * when it was created.
+   * */
+  public Node getRoot()
+    {
+      int handle=dtm_iter.getRoot();
+      return dtm_iter.getDTM(handle).getNode(handle);
+    }
+  
+
+  /** Return a mask describing which node types are presented via the
+   * iterator.
+   **/
+  public int getWhatToShow()
+    {
+      return dtm_iter.getWhatToShow();
+    }
+
+  /** @return the next node in the set and advance the position of the
+   * iterator in the set.
+   *
+   * @throws DOMException - INVALID_STATE_ERR Raised if this method is
+   * called after the detach method was invoked.
+   *  */
+  public Node nextNode() throws DOMException
+    {
+      if(!valid)
+        throw new DTMDOMException(DOMException.INVALID_STATE_ERR);
+      
+      int handle=dtm_iter.nextNode();
+      if (handle==DTM.NULL)
+        return null;
+      return dtm_iter.getDTM(handle).getNode(handle);
+    }
+  
+
+  /** @return the next previous in the set and advance the position of the
+   * iterator in the set.
+   *
+   * @throws DOMException - INVALID_STATE_ERR Raised if this method is
+   * called after the detach method was invoked.
+   *  */
+  public Node previousNode() 
+    {
+      if(!valid)
+        throw new DTMDOMException(DOMException.INVALID_STATE_ERR);
+      
+      int handle=dtm_iter.previousNode();
+      if (handle==DTM.NULL)
+        return null;      
+      return dtm_iter.getDTM(handle).getNode(handle);
+    }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMNodeList.java b/src/main/java/org/apache/xml/dtm/ref/DTMNodeList.java
new file mode 100644
index 0000000..f265080
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMNodeList.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMNodeList.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.w3c.dom.Node;
+
+/**
+ * <code>DTMNodeList</code> gives us an implementation of the DOM's
+ * NodeList interface wrapped around a DTM Iterator. The author
+ * considers this something of an abominations, since NodeList was not
+ * intended to be a general purpose "list of nodes" API and is
+ * generally considered by the DOM WG to have be a mistake... but I'm
+ * told that some of the XPath/XSLT folks say they must have this
+ * solution.
+ *
+ * Please note that this is not necessarily equivlaent to a DOM
+ * NodeList operating over the same document. In particular:
+ * <ul>
+ *
+ * <li>If there are several Text nodes in logical succession (ie,
+ * across CDATASection and EntityReference boundaries), we will return
+ * only the first; the caller is responsible for stepping through
+ * them.
+ * (%REVIEW% Provide a convenience routine here to assist, pending
+ * proposed DOM Level 3 getAdjacentText() operation?) </li>
+ *
+ * <li>Since the whole XPath/XSLT architecture assumes that the source
+ * document is not altered while we're working with it, we do not
+ * promise to implement the DOM NodeList's "live view" response to
+ * document mutation. </li>
+ *
+ * </ul>
+ *
+ * <p>State: In progress!!</p>
+ * */
+public class DTMNodeList extends DTMNodeListBase {
+    private DTMIterator m_iter;
+
+    //================================================================
+    // Methods unique to this class
+    private DTMNodeList() {
+    }
+
+    /**
+     * Public constructor: Wrap a DTMNodeList around an existing
+     * and preconfigured DTMIterator
+     *
+     * WARNING: THIS HAS THE SIDE EFFECT OF ISSUING setShouldCacheNodes(true)
+     * AGAINST THE DTMIterator.
+     *
+     */
+    public DTMNodeList(DTMIterator dtmIterator) {
+        if (dtmIterator != null) {
+            int pos = dtmIterator.getCurrentPos();
+            try {
+                m_iter=(DTMIterator)dtmIterator.cloneWithReset();
+            } catch(CloneNotSupportedException cnse) {
+                m_iter = dtmIterator;
+            }
+            m_iter.setShouldCacheNodes(true);
+            m_iter.runTo(-1);
+            m_iter.setCurrentPos(pos);
+        }
+    }
+
+    /**
+     * Access the wrapped DTMIterator. I'm not sure whether anyone will
+     * need this or not, but let's write it and think about it.
+     *
+     */
+    public DTMIterator getDTMIterator() {
+        return m_iter;
+    }
+
+    //================================================================
+    // org.w3c.dom.NodeList API follows
+
+    /**
+     * Returns the <code>index</code>th item in the collection. If 
+     * <code>index</code> is greater than or equal to the number of nodes in 
+     * the list, this returns <code>null</code>.
+     * @param index Index into the collection.
+     * @return The node at the <code>index</code>th position in the 
+     *   <code>NodeList</code>, or <code>null</code> if that is not a valid 
+     *   index.
+     */
+    public Node item(int index)
+    {
+        if (m_iter != null) {
+            int handle=m_iter.item(index);
+            if (handle == DTM.NULL) {
+                return null;
+            }
+            return m_iter.getDTM(handle).getNode(handle);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * The number of nodes in the list. The range of valid child node indices 
+     * is 0 to <code>length-1</code> inclusive. 
+     */
+    public int getLength() {
+        return (m_iter != null) ? m_iter.getLength() : 0;
+    }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMNodeListBase.java b/src/main/java/org/apache/xml/dtm/ref/DTMNodeListBase.java
new file mode 100644
index 0000000..60bac3d
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMNodeListBase.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMNodeListBase.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+import org.w3c.dom.Node;
+
+/**
+ * <code>DTMNodeList</code> gives us an implementation of the DOM's
+ * NodeList interface wrapped around a DTM Iterator. The author
+ * considers this something of an abominations, since NodeList was not
+ * intended to be a general purpose "list of nodes" API and is
+ * generally considered by the DOM WG to have be a mistake... but I'm
+ * told that some of the XPath/XSLT folks say they must have this
+ * solution.
+ *
+ * Please note that this is not necessarily equivlaent to a DOM
+ * NodeList operating over the same document. In particular:
+ * <ul>
+ *
+ * <li>If there are several Text nodes in logical succession (ie,
+ * across CDATASection and EntityReference boundaries), we will return
+ * only the first; the caller is responsible for stepping through
+ * them.
+ * (%REVIEW% Provide a convenience routine here to assist, pending
+ * proposed DOM Level 3 getAdjacentText() operation?) </li>
+ *
+ * <li>Since the whole XPath/XSLT architecture assumes that the source
+ * document is not altered while we're working with it, we do not
+ * promise to implement the DOM NodeList's "live view" response to
+ * document mutation. </li>
+ *
+ * </ul>
+ *
+ * <p>State: In progress!!</p>
+ *
+ */
+public class DTMNodeListBase implements org.w3c.dom.NodeList {
+    public DTMNodeListBase() {
+    }
+
+    //================================================================
+    // org.w3c.dom.NodeList API follows
+
+    /**
+     * Returns the <code>index</code>th item in the collection. If 
+     * <code>index</code> is greater than or equal to the number of nodes in 
+     * the list, this returns <code>null</code>.
+     * @param index Index into the collection.
+     * @return The node at the <code>index</code>th position in the 
+     *   <code>NodeList</code>, or <code>null</code> if that is not a valid 
+     *   index.
+     */
+    public Node item(int index) {
+        return null;
+    }
+
+    /**
+     * The number of nodes in the list. The range of valid child node indices 
+     * is 0 to <code>length-1</code> inclusive. 
+     */
+    public int getLength() {
+        return 0;
+    }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMNodeProxy.java b/src/main/java/org/apache/xml/dtm/ref/DTMNodeProxy.java
new file mode 100644
index 0000000..ebd578c
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMNodeProxy.java
@@ -0,0 +1,2218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMNodeProxy.java 889881 2009-12-12 03:47:15Z zongaro $
+ */
+package org.apache.xml.dtm.ref;
+
+import java.util.Vector;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMDOMException;
+import org.apache.xpath.NodeSet;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+import org.w3c.dom.UserDataHandler;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.TypeInfo;
+
+/**
+ * <code>DTMNodeProxy</code> presents a DOM Node API front-end to the DTM model.
+ * <p>
+ * It does _not_ attempt to address the "node identity" question; no effort
+ * is made to prevent the creation of multiple proxies referring to a single
+ * DTM node. Users can create a mechanism for managing this, or relinquish the
+ * use of "==" and use the .sameNodeAs() mechanism, which is under
+ * consideration for future versions of the DOM.
+ * <p>
+ * DTMNodeProxy may be subclassed further to present specific DOM node types.
+ *
+ * @see org.w3c.dom
+ * @xsl.usage internal
+ */
+public class DTMNodeProxy
+  implements Node, Document, Text, Element, Attr,
+                   ProcessingInstruction, Comment, DocumentFragment
+{
+
+  /** The DTM for this node. */
+  public DTM dtm;
+
+  /** The DTM node handle. */
+  int node;
+  
+  /** The return value as Empty String. */
+  private static final String EMPTYSTRING = "";
+          
+  /** The DOMImplementation object */
+  static final DOMImplementation implementation=new DTMNodeProxyImplementation();
+
+  /**
+   * Create a DTMNodeProxy Node representing a specific Node in a DTM
+   *
+   * @param dtm The DTM Reference, must be non-null.
+   * @param node The DTM node handle.
+   */
+  public DTMNodeProxy(DTM dtm, int node)
+  {
+    this.dtm = dtm;
+    this.node = node;
+  }
+
+  /**
+   * NON-DOM: Return the DTM model
+   *
+   * @return The DTM that this proxy is a representative for.
+   */
+  public final DTM getDTM()
+  {
+    return dtm;
+  }
+
+  /**
+   * NON-DOM: Return the DTM node number
+   *
+   * @return The DTM node handle.
+   */
+  public final int getDTMNodeNumber()
+  {
+    return node;
+  }
+
+  /**
+   * Test for equality based on node number.
+   *
+   * @param node A DTM node proxy reference.
+   *
+   * @return true if the given node has the same handle as this node.
+   */
+  public final boolean equals(Node node)
+  {
+
+    try
+    {
+      DTMNodeProxy dtmp = (DTMNodeProxy) node;
+
+      // return (dtmp.node == this.node);
+      // Patch attributed to Gary L Peskin <garyp@firstech.com>
+      return (dtmp.node == this.node) && (dtmp.dtm == this.dtm);
+    }
+    catch (ClassCastException cce)
+    {
+      return false;
+    }
+  }
+
+  /**
+   * Test for equality based on node number.
+   *
+   * @param node A DTM node proxy reference.
+   *
+   * @return true if the given node has the same handle as this node.
+   */
+  public final boolean equals(Object node)
+  {
+
+    try
+    {
+
+      // DTMNodeProxy dtmp = (DTMNodeProxy)node;
+      // return (dtmp.node == this.node);
+      // Patch attributed to Gary L Peskin <garyp@firstech.com>
+      return equals((Node) node);
+    }
+    catch (ClassCastException cce)
+    {
+      return false;
+    }
+  }
+
+  /**
+   * FUTURE DOM: Test node identity, in lieu of Node==Node
+   *
+   * @param other
+   *
+   * @return true if the given node has the same handle as this node.
+   */
+  public final boolean sameNodeAs(Node other)
+  {
+
+    if (!(other instanceof DTMNodeProxy))
+      return false;
+
+    DTMNodeProxy that = (DTMNodeProxy) other;
+
+    return this.dtm == that.dtm && this.node == that.node;
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final String getNodeName()
+  {
+    return dtm.getNodeName(node);
+  }
+
+  /**
+   * A PI's "target" states what processor channel the PI's data
+   * should be directed to. It is defined differently in HTML and XML.
+   * <p>
+   * In XML, a PI's "target" is the first (whitespace-delimited) token
+   * following the "<?" token that begins the PI.
+   * <p>
+   * In HTML, target is always null.
+   * <p>
+   * Note that getNodeName is aliased to getTarget.
+   *
+   *
+   */
+  public final String getTarget()
+  {
+    return dtm.getNodeName(node);
+  }  // getTarget():String
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node as of DOM Level 2
+   */
+  public final String getLocalName()
+  {
+    return dtm.getLocalName(node);
+  }
+
+  /**
+   * @return The prefix for this node.
+   * @see org.w3c.dom.Node as of DOM Level 2
+   */
+  public final String getPrefix()
+  {
+    return dtm.getPrefix(node);
+  }
+
+  /**
+   *
+   * @param prefix
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Node as of DOM Level 2 -- DTMNodeProxy is read-only
+   */
+  public final void setPrefix(String prefix) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node as of DOM Level 2
+   */
+  public final String getNamespaceURI()
+  {
+    return dtm.getNamespaceURI(node);
+  }
+
+  /** Ask whether we support a given DOM feature.
+   * In fact, we do not _fully_ support any DOM feature -- we're a
+   * read-only subset -- so arguably we should always return false.
+   * Or we could say that we support DOM Core Level 2 but all nodes
+   * are read-only. Unclear which answer is least misleading.
+   * 
+   * NON-DOM method. This was present in early drafts of DOM Level 2,
+   * but was renamed isSupported. It's present here only because it's
+   * cheap, harmless, and might help some poor fool who is still trying
+   * to use an early Working Draft of the DOM.
+   *
+   * @param feature
+   * @param version
+   *
+   * @return false
+   */
+  public final boolean supports(String feature, String version)
+  {
+    return implementation.hasFeature(feature,version);
+    //throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /** Ask whether we support a given DOM feature.
+   * In fact, we do not _fully_ support any DOM feature -- we're a
+   * read-only subset -- so arguably we should always return false.
+   *
+   * @param feature
+   * @param version
+   *
+   * @return false
+   * @see org.w3c.dom.Node as of DOM Level 2
+   */
+  public final boolean isSupported(String feature, String version)
+  {
+    return implementation.hasFeature(feature,version);
+    // throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Node
+   */
+  public final String getNodeValue() throws DOMException
+  {
+    return dtm.getNodeValue(node);
+  }
+  
+  /**
+   * @return The string value of the node
+   * 
+   * @throws DOMException
+   */
+  public final String getStringValue() throws DOMException
+  {
+  	return dtm.getStringValue(node).toString();
+  }
+
+  /**
+   *
+   * @param nodeValue
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Node -- DTMNodeProxy is read-only
+   */
+  public final void setNodeValue(String nodeValue) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final short getNodeType()
+  {
+    return (short) dtm.getNodeType(node);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final Node getParentNode()
+  {
+
+    if (getNodeType() == Node.ATTRIBUTE_NODE)
+      return null;
+
+    int newnode = dtm.getParent(node);
+
+    return (newnode == DTM.NULL) ? null : dtm.getNode(newnode);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final Node getOwnerNode()
+  {
+
+    int newnode = dtm.getParent(node);
+
+    return (newnode == DTM.NULL) ? null : dtm.getNode(newnode);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final NodeList getChildNodes()
+  {
+                
+    // Annoyingly, AxisIterators do not currently implement DTMIterator, so
+    // we can't just wap DTMNodeList around an Axis.CHILD iterator.
+    // Instead, we've created a special-case operating mode for that object.
+    return new DTMChildIterNodeList(dtm,node);
+
+    // throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final Node getFirstChild()
+  {
+
+    int newnode = dtm.getFirstChild(node);
+
+    return (newnode == DTM.NULL) ? null : dtm.getNode(newnode);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final Node getLastChild()
+  {
+
+    int newnode = dtm.getLastChild(node);
+
+    return (newnode == DTM.NULL) ? null : dtm.getNode(newnode);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final Node getPreviousSibling()
+  {
+
+    int newnode = dtm.getPreviousSibling(node);
+
+    return (newnode == DTM.NULL) ? null : dtm.getNode(newnode);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final Node getNextSibling()
+  {
+
+    // Attr's Next is defined at DTM level, but not at DOM level.
+    if (dtm.getNodeType(node) == Node.ATTRIBUTE_NODE)
+      return null;
+
+    int newnode = dtm.getNextSibling(node);
+
+    return (newnode == DTM.NULL) ? null : dtm.getNode(newnode);
+  }
+
+  // DTMNamedNodeMap m_attrs;
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final NamedNodeMap getAttributes()
+  {
+
+    return new DTMNamedNodeMap(dtm, node);
+  }
+
+  /**
+   * Method hasAttribute
+   *
+   *
+   * @param name
+   *
+   *
+   */
+  public boolean hasAttribute(String name)
+  {
+    return DTM.NULL != dtm.getAttributeNode(node,null,name);
+  }
+
+  /**
+   * Method hasAttributeNS
+   *
+   *
+   * @param namespaceURI
+   * @param localName
+   *
+   *
+   */
+  public boolean hasAttributeNS(String namespaceURI, String localName)
+  {
+    return DTM.NULL != dtm.getAttributeNode(node,namespaceURI,localName);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final Document getOwnerDocument()
+  {
+  	// Note that this uses the DOM-compatable version of the call
+	return (Document)(dtm.getNode(dtm.getOwnerDocument(node)));
+  }
+
+  /**
+   *
+   * @param newChild
+   * @param refChild
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Node -- DTMNodeProxy is read-only
+   */
+  public final Node insertBefore(Node newChild, Node refChild)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   *
+   * @param newChild
+   * @param oldChild
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Node -- DTMNodeProxy is read-only
+   */
+  public final Node replaceChild(Node newChild, Node oldChild)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   *
+   * @param oldChild
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Node -- DTMNodeProxy is read-only
+   */
+  public final Node removeChild(Node oldChild) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   *
+   * @param newChild
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Node -- DTMNodeProxy is read-only
+   */
+  public final Node appendChild(Node newChild) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Node
+   */
+  public final boolean hasChildNodes()
+  {
+    return (DTM.NULL != dtm.getFirstChild(node));
+  }
+
+  /**
+   *
+   * @param deep
+   *
+   *
+   * @see org.w3c.dom.Node -- DTMNodeProxy is read-only
+   */
+  public final Node cloneNode(boolean deep)
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Document
+   */
+  public final DocumentType getDoctype()
+  {
+    return null;
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Document
+   */
+  public final DOMImplementation getImplementation()
+  {
+    return implementation;
+  }
+
+  /** This is a bit of a problem in DTM, since a DTM may be a Document
+   * Fragment and hence not have a clear-cut Document Element. We can
+   * make it work in the well-formed cases but would that be confusing for others?
+   * 
+   *
+   * @see org.w3c.dom.Document
+   */
+  public final Element getDocumentElement()
+  {
+		int dochandle=dtm.getDocument();
+		int elementhandle=DTM.NULL;
+		for(int kidhandle=dtm.getFirstChild(dochandle);
+				kidhandle!=DTM.NULL;
+				kidhandle=dtm.getNextSibling(kidhandle))
+		{
+			switch(dtm.getNodeType(kidhandle))
+			{
+			case Node.ELEMENT_NODE:
+				if(elementhandle!=DTM.NULL) 
+				{
+					elementhandle=DTM.NULL; // More than one; ill-formed.
+					kidhandle=dtm.getLastChild(dochandle); // End loop
+				}
+				else
+					elementhandle=kidhandle;
+				break;
+				
+			// These are harmless; document is still wellformed
+			case Node.COMMENT_NODE:
+			case Node.PROCESSING_INSTRUCTION_NODE:
+			case Node.DOCUMENT_TYPE_NODE:
+				break;
+					
+			default:
+				elementhandle=DTM.NULL; // ill-formed
+				kidhandle=dtm.getLastChild(dochandle); // End loop
+				break;
+			}
+		}
+		if(elementhandle==DTM.NULL)
+			throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+		else
+			return (Element)(dtm.getNode(elementhandle));
+  }
+
+  /**
+   *
+   * @param tagName
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Document
+   */
+  public final Element createElement(String tagName) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Document
+   */
+  public final DocumentFragment createDocumentFragment()
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param data
+   *
+   *
+   * @see org.w3c.dom.Document
+   */
+  public final Text createTextNode(String data)
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param data
+   *
+   *
+   * @see org.w3c.dom.Document
+   */
+  public final Comment createComment(String data)
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param data
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Document
+   */
+  public final CDATASection createCDATASection(String data)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param target
+   * @param data
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Document
+   */
+  public final ProcessingInstruction createProcessingInstruction(
+                                                                 String target, String data) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param name
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Document
+   */
+  public final Attr createAttribute(String name) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param name
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Document
+   */
+  public final EntityReference createEntityReference(String name)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param tagname
+   *
+   *
+   * @see org.w3c.dom.Document
+   */
+  public final NodeList getElementsByTagName(String tagname) 
+  {
+       Vector listVector = new Vector();
+       Node retNode = dtm.getNode(node);
+       if (retNode != null) 
+       {
+         boolean isTagNameWildCard = "*".equals(tagname);
+         if (DTM.ELEMENT_NODE == retNode.getNodeType()) 
+         {
+           NodeList nodeList = retNode.getChildNodes();
+           for (int i = 0; i < nodeList.getLength(); i++) 
+           {
+             traverseChildren(listVector, nodeList.item(i), tagname,
+                              isTagNameWildCard);
+           }
+         } else if (DTM.DOCUMENT_NODE == retNode.getNodeType()) {
+           traverseChildren(listVector, dtm.getNode(node), tagname,
+                            isTagNameWildCard);
+         }
+       }
+       int size = listVector.size();
+       NodeSet nodeSet = new NodeSet(size);
+       for (int i = 0; i < size; i++) 
+       {
+         nodeSet.addNode((Node) listVector.elementAt(i));
+       }
+       return (NodeList) nodeSet;
+  }
+  /**
+   * 
+   * @param listVector
+   * @param tempNode
+   * @param tagname
+   * @param isTagNameWildCard
+   * 
+   * 
+   * Private method to be used for recursive iterations to obtain elements by tag name.
+   */
+  private final void traverseChildren
+  (
+    Vector listVector,
+    Node tempNode,
+    String tagname,
+    boolean isTagNameWildCard) {
+    if (tempNode == null) 
+    {
+      return;
+    } 
+    else
+    { 
+      if (tempNode.getNodeType() == DTM.ELEMENT_NODE
+            && (isTagNameWildCard || tempNode.getNodeName().equals(tagname)))
+      {
+        listVector.add(tempNode);
+      }
+      if(tempNode.hasChildNodes())
+      {
+        NodeList nodeList = tempNode.getChildNodes();
+        for (int i = 0; i < nodeList.getLength(); i++)
+        {
+          traverseChildren(listVector, nodeList.item(i), tagname,
+                           isTagNameWildCard);
+        }
+      }
+    }
+  }
+
+  /**
+   *
+   * @param importedNode
+   * @param deep
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Document as of DOM Level 2 -- DTMNodeProxy is read-only
+   */
+  public final Node importNode(Node importedNode, boolean deep)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
+  }
+
+  /**
+   *
+   * @param namespaceURI
+   * @param qualifiedName
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Document as of DOM Level 2
+   */
+  public final Element createElementNS(
+                                       String namespaceURI, String qualifiedName) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param namespaceURI
+   * @param qualifiedName
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Document as of DOM Level 2
+   */
+  public final Attr createAttributeNS(
+                                      String namespaceURI, String qualifiedName) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param namespaceURI
+   * @param localName
+   *
+   *
+   * @see org.w3c.dom.Document as of DOM Level 2
+   */
+  public final NodeList getElementsByTagNameNS(String namespaceURI,
+                                               String localName)
+  {
+    Vector listVector = new Vector();
+    Node retNode = dtm.getNode(node);
+    if (retNode != null)
+    {               
+      boolean isNamespaceURIWildCard = "*".equals(namespaceURI);
+      boolean isLocalNameWildCard    = "*".equals(localName);
+      if (DTM.ELEMENT_NODE == retNode.getNodeType())
+      {
+        NodeList nodeList = retNode.getChildNodes();                    
+        for(int i = 0; i < nodeList.getLength(); i++)
+        {
+          traverseChildren(listVector, nodeList.item(i), namespaceURI, localName, isNamespaceURIWildCard, isLocalNameWildCard);
+        }
+      }
+      else if(DTM.DOCUMENT_NODE == retNode.getNodeType())
+      {
+        traverseChildren(listVector, dtm.getNode(node), namespaceURI, localName, isNamespaceURIWildCard, isLocalNameWildCard);
+      }
+    }
+    int size = listVector.size();
+    NodeSet nodeSet = new NodeSet(size);
+    for (int i = 0; i < size; i++)
+    {
+      nodeSet.addNode((Node)listVector.elementAt(i));
+    }
+    return (NodeList) nodeSet;
+  }
+  /**
+   * 
+   * @param listVector
+   * @param tempNode
+   * @param namespaceURI
+   * @param localname
+   * @param isNamespaceURIWildCard
+   * @param isLocalNameWildCard
+   * 
+   * Private method to be used for recursive iterations to obtain elements by tag name 
+   * and namespaceURI.
+   */
+  private final void traverseChildren
+  (
+   Vector listVector, 
+   Node tempNode, 
+   String namespaceURI, 
+   String localname,
+   boolean isNamespaceURIWildCard,
+   boolean isLocalNameWildCard) 
+   {
+    if (tempNode == null)
+    {
+      return;
+    }
+    else 
+    {
+      if (tempNode.getNodeType() == DTM.ELEMENT_NODE
+              && (isLocalNameWildCard
+                      || tempNode.getLocalName().equals(localname)))
+      {         
+        String nsURI = tempNode.getNamespaceURI();
+        if ((namespaceURI == null && nsURI == null)
+               || isNamespaceURIWildCard
+               || (namespaceURI != null && namespaceURI.equals(nsURI)))
+        {     
+          listVector.add(tempNode); 
+        } 
+      }
+      if(tempNode.hasChildNodes())
+      {
+        NodeList nl = tempNode.getChildNodes();                 
+        for(int i = 0; i < nl.getLength(); i++)
+        {
+          traverseChildren(listVector, nl.item(i), namespaceURI, localname,
+                           isNamespaceURIWildCard, isLocalNameWildCard);
+        }
+      }
+    }
+  }
+  /**
+   *
+   * @param elementId
+   *
+   *
+   * @see org.w3c.dom.Document as of DOM Level 2
+   */
+  public final Element getElementById(String elementId)
+  {
+       return (Element) dtm.getNode(dtm.getElementById(elementId));
+  }
+
+  /**
+   *
+   * @param offset
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Text
+   */
+  public final Text splitText(int offset) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.CharacterData
+   */
+  public final String getData() throws DOMException
+  {
+    return dtm.getNodeValue(node);
+  }
+
+  /**
+   *
+   * @param data
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.CharacterData
+   */
+  public final void setData(String data) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.CharacterData
+   */
+  public final int getLength()
+  {
+    // %OPT% This should do something smarter?
+    return dtm.getNodeValue(node).length();
+  }
+
+  /**
+   *
+   * @param offset
+   * @param count
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.CharacterData
+   */
+  public final String substringData(int offset, int count) throws DOMException
+  {
+    return getData().substring(offset,offset+count);
+  }
+
+  /**
+   *
+   * @param arg
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.CharacterData
+   */
+  public final void appendData(String arg) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param offset
+   * @param arg
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.CharacterData
+   */
+  public final void insertData(int offset, String arg) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param offset
+   * @param count
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.CharacterData
+   */
+  public final void deleteData(int offset, int count) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param offset
+   * @param count
+   * @param arg
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.CharacterData
+   */
+  public final void replaceData(int offset, int count, String arg)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Element
+   */
+  public final String getTagName()
+  {
+    return dtm.getNodeName(node);
+  }
+
+  /**
+   *
+   * @param name
+   *
+   *
+   * @see org.w3c.dom.Element
+   */
+  public final String getAttribute(String name)
+  {
+
+    DTMNamedNodeMap  map = new DTMNamedNodeMap(dtm, node);
+    Node node = map.getNamedItem(name);
+    return (null == node) ? EMPTYSTRING : node.getNodeValue();
+  }
+
+  /**
+   *
+   * @param name
+   * @param value
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Element
+   */
+  public final void setAttribute(String name, String value)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param name
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Element
+   */
+  public final void removeAttribute(String name) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param name
+   *
+   *
+   * @see org.w3c.dom.Element
+   */
+  public final Attr getAttributeNode(String name)
+  {
+
+    DTMNamedNodeMap  map = new DTMNamedNodeMap(dtm, node);
+    return (Attr)map.getNamedItem(name);
+  }
+
+  /**
+   *
+   * @param newAttr
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Element
+   */
+  public final Attr setAttributeNode(Attr newAttr) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param oldAttr
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Element
+   */
+  public final Attr removeAttributeNode(Attr oldAttr) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   * Introduced in DOM Level 2.
+   *
+   *
+   */
+  public boolean hasAttributes()
+  {
+    return DTM.NULL != dtm.getFirstAttribute(node);
+  }
+
+  /** @see org.w3c.dom.Element */
+  public final void normalize()
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param namespaceURI
+   * @param localName
+   *
+   *
+   * @see org.w3c.dom.Element
+   */
+  public final String getAttributeNS(String namespaceURI, String localName)
+  {
+       Node retNode = null;
+       int n = dtm.getAttributeNode(node,namespaceURI,localName);
+       if(n != DTM.NULL)
+               retNode = dtm.getNode(n);
+       return (null == retNode) ? EMPTYSTRING : retNode.getNodeValue();
+  }
+
+  /**
+   *
+   * @param namespaceURI
+   * @param qualifiedName
+   * @param value
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Element
+   */
+  public final void setAttributeNS(
+                                   String namespaceURI, String qualifiedName, String value)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param namespaceURI
+   * @param localName
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Element
+   */
+  public final void removeAttributeNS(String namespaceURI, String localName)
+    throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   * @param namespaceURI
+   * @param localName
+   *
+   *
+   * @see org.w3c.dom.Element
+   */
+  public final Attr getAttributeNodeNS(String namespaceURI, String localName)
+  {
+       Attr retAttr = null;
+       int n = dtm.getAttributeNode(node,namespaceURI,localName);
+       if(n != DTM.NULL)
+               retAttr = (Attr) dtm.getNode(n);
+       return retAttr;
+  }
+
+  /**
+   *
+   * @param newAttr
+   *
+   *
+   *
+   * @throws DOMException
+   * @see org.w3c.dom.Element
+   */
+  public final Attr setAttributeNodeNS(Attr newAttr) throws DOMException
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Attr
+   */
+  public final String getName()
+  {
+    return dtm.getNodeName(node);
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Attr
+   */
+  public final boolean getSpecified()
+  {
+    // We really don't know which attributes might have come from the
+    // source document versus from the DTD. Treat them all as having
+    // been provided by the user.
+    // %REVIEW% if/when we become aware of DTDs/schemae.
+    return true;
+  }
+
+  /**
+   *
+   *
+   * @see org.w3c.dom.Attr
+   */
+  public final String getValue()
+  {
+    return dtm.getNodeValue(node);
+  }
+
+  /**
+   *
+   * @param value
+   * @see org.w3c.dom.Attr
+   */
+  public final void setValue(String value)
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   * Get the owner element of an attribute.
+   *
+   *
+   * @see org.w3c.dom.Attr as of DOM Level 2
+   */
+  public final Element getOwnerElement()
+  {
+    if (getNodeType() != Node.ATTRIBUTE_NODE)
+      return null;
+    // In XPath and DTM data models, unlike DOM, an Attr's parent is its
+    // owner element.
+    int newnode = dtm.getParent(node);
+    return (newnode == DTM.NULL) ? null : (Element)(dtm.getNode(newnode));
+  }
+
+  /**
+   * NEEDSDOC Method adoptNode 
+   *
+   *
+   * NEEDSDOC @param source
+   *
+   *
+   *
+   * @throws DOMException
+   */
+  public Node adoptNode(Node source) throws DOMException
+  {
+
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   * <p>Based on the <a
+   * href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document
+   * Object Model (DOM) Level 3 Core Specification of 07 April 2004.</a>.
+   * <p>
+   * An attribute specifying, as part of the XML declaration, the encoding
+   * of this document. This is <code>null</code> when unspecified.
+   * @since DOM Level 3
+   *
+   *
+   */
+  public String getInputEncoding()
+  {
+
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   * <p>Based on the <a
+   * href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document
+   * Object Model (DOM) Level 3 Core Specification of 07 April 2004.</a>.
+   * <p>
+   * An attribute specifying whether errors checking is enforced or not.
+   * When set to <code>false</code>, the implementation is free to not
+   * test every possible error case normally defined on DOM operations,
+   * and not raise any <code>DOMException</code>. In case of error, the
+   * behavior is undefined. This attribute is <code>true</code> by
+   * defaults.
+   * @since DOM Level 3
+   *
+   *
+   */
+  public boolean getStrictErrorChecking()
+  {
+
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }
+
+  /**
+   * <p>Based on the <a
+   * href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document
+   * Object Model (DOM) Level 3 Core Specification of 07 April 2004.</a>.
+   * <p>
+   * An attribute specifying whether errors checking is enforced or not.
+   * When set to <code>false</code>, the implementation is free to not
+   * test every possible error case normally defined on DOM operations,
+   * and not raise any <code>DOMException</code>. In case of error, the
+   * behavior is undefined. This attribute is <code>true</code> by
+   * defaults.
+   * @since DOM Level 3
+   *
+   * NEEDSDOC @param strictErrorChecking
+   */
+  public void setStrictErrorChecking(boolean strictErrorChecking)
+  {
+    throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+  }        
+        
+  /** Inner class to support getDOMImplementation.
+   */
+  static class DTMNodeProxyImplementation implements DOMImplementation
+  {
+    public DocumentType createDocumentType(String qualifiedName,String publicId, String systemId)
+    {
+      throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);
+    }
+    public Document createDocument(String namespaceURI,String qualfiedName,DocumentType doctype)                        
+    {
+      // Could create a DTM... but why, when it'd have to be permanantly empty?
+      throw new DTMDOMException(DOMException.NOT_SUPPORTED_ERR);        
+    }
+    /** Ask whether we support a given DOM feature.
+     * 
+     * In fact, we do not _fully_ support any DOM feature -- we're a
+     * read-only subset -- so arguably we should always return false.
+     * On the other hand, it may be more practically useful to return
+     * true and simply treat the whole DOM as read-only, failing on the
+     * methods we can't support. I'm not sure which would be more useful
+     * to the caller.
+     */
+    public boolean hasFeature(String feature,String version)
+    {
+      if( ("CORE".equals(feature.toUpperCase()) || "XML".equals(feature.toUpperCase())) 
+					&& 
+          ("1.0".equals(version) || "2.0".equals(version))
+          )
+        return true;
+      return false;
+    }
+
+    /**
+     *  This method returns a specialized object which implements the
+     * specialized APIs of the specified feature and version. The
+     * specialized object may also be obtained by using binding-specific
+     * casting methods but is not necessarily expected to, as discussed in Mixed DOM implementations
+.
+     * @param feature The name of the feature requested (case-insensitive).
+     * @param version  This is the version number of the feature to test. If
+     *   the version is <code>null</code> or the empty string, supporting
+     *   any version of the feature will cause the method to return an
+     *   object that supports at least one version of the feature.
+     * @return  Returns an object which implements the specialized APIs of
+     *   the specified feature and version, if any, or <code>null</code> if
+     *   there is no object which implements interfaces associated with that
+     *   feature. If the <code>DOMObject</code> returned by this method
+     *   implements the <code>Node</code> interface, it must delegate to the
+     *   primary core <code>Node</code> and not return results inconsistent
+     *   with the primary core <code>Node</code> such as attributes,
+     *   childNodes, etc.
+     * @since DOM Level 3
+     */
+    public Object getFeature(String feature, String version) {
+        // we don't have any alternate node, either this node does the job
+        // or we don't have anything that does
+        //return hasFeature(feature, version) ? this : null;
+        return null; //PENDING
+    }
+
+  }
+
+
+    //RAMESH : Pending proper implementation of DOM Level 3
+    
+    public Object setUserData(String key,
+                              Object data,
+                              UserDataHandler handler) {
+        return getOwnerDocument().setUserData( key, data, handler);
+    }
+
+    /**
+     * Retrieves the object associated to a key on a this node. The object
+     * must first have been set to this node by calling
+     * <code>setUserData</code> with the same key.
+     * @param key The key the object is associated to.
+     * @return Returns the <code>DOMObject</code> associated to the given key
+     *   on this node, or <code>null</code> if there was none.
+     * @since DOM Level 3
+     */
+    public Object getUserData(String key) {
+        return getOwnerDocument().getUserData( key);
+    } 
+
+    /**
+     *  This method returns a specialized object which implements the
+     * specialized APIs of the specified feature and version. The
+     * specialized object may also be obtained by using binding-specific
+     * casting methods but is not necessarily expected to, as discussed in Mixed DOM implementations.
+     * @param feature The name of the feature requested (case-insensitive).
+     * @param version  This is the version number of the feature to test. If
+     *   the version is <code>null</code> or the empty string, supporting
+     *   any version of the feature will cause the method to return an
+     *   object that supports at least one version of the feature.
+     * @return  Returns an object which implements the specialized APIs of
+     *   the specified feature and version, if any, or <code>null</code> if
+     *   there is no object which implements interfaces associated with that
+     *   feature. If the <code>DOMObject</code> returned by this method
+     *   implements the <code>Node</code> interface, it must delegate to the
+     *   primary core <code>Node</code> and not return results inconsistent
+     *   with the primary core <code>Node</code> such as attributes,
+     *   childNodes, etc.
+     * @since DOM Level 3
+     */
+    public Object getFeature(String feature, String version) {
+        // we don't have any alternate node, either this node does the job
+        // or we don't have anything that does
+        return isSupported(feature, version) ? this : null;
+    }
+
+    /**
+     * Tests whether two nodes are equal.
+     * <br>This method tests for equality of nodes, not sameness (i.e.,
+     * whether the two nodes are references to the same object) which can be
+     * tested with <code>Node.isSameNode</code>. All nodes that are the same
+     * will also be equal, though the reverse may not be true.
+     * <br>Two nodes are equal if and only if the following conditions are
+     * satisfied: The two nodes are of the same type.The following string
+     * attributes are equal: <code>nodeName</code>, <code>localName</code>,
+     * <code>namespaceURI</code>, <code>prefix</code>, <code>nodeValue</code>
+     * , <code>baseURI</code>. This is: they are both <code>null</code>, or
+     * they have the same length and are character for character identical.
+     * The <code>attributes</code> <code>NamedNodeMaps</code> are equal.
+     * This is: they are both <code>null</code>, or they have the same
+     * length and for each node that exists in one map there is a node that
+     * exists in the other map and is equal, although not necessarily at the
+     * same index.The <code>childNodes</code> <code>NodeLists</code> are
+     * equal. This is: they are both <code>null</code>, or they have the
+     * same length and contain equal nodes at the same index. This is true
+     * for <code>Attr</code> nodes as for any other type of node. Note that
+     * normalization can affect equality; to avoid this, nodes should be
+     * normalized before being compared.
+     * <br>For two <code>DocumentType</code> nodes to be equal, the following
+     * conditions must also be satisfied: The following string attributes
+     * are equal: <code>publicId</code>, <code>systemId</code>,
+     * <code>internalSubset</code>.The <code>entities</code>
+     * <code>NamedNodeMaps</code> are equal.The <code>notations</code>
+     * <code>NamedNodeMaps</code> are equal.
+     * <br>On the other hand, the following do not affect equality: the
+     * <code>ownerDocument</code> attribute, the <code>specified</code>
+     * attribute for <code>Attr</code> nodes, the
+     * <code>isWhitespaceInElementContent</code> attribute for
+     * <code>Text</code> nodes, as well as any user data or event listeners
+     * registered on the nodes.
+     * @param arg The node to compare equality with.
+     * @param deep If <code>true</code>, recursively compare the subtrees; if
+     *   <code>false</code>, compare only the nodes themselves (and its
+     *   attributes, if it is an <code>Element</code>).
+     * @return If the nodes, and possibly subtrees are equal,
+     *   <code>true</code> otherwise <code>false</code>.
+     * @since DOM Level 3
+     */
+    public boolean isEqualNode(Node arg) {
+        if (arg == this) {
+            return true;
+        }
+        if (arg.getNodeType() != getNodeType()) {
+            return false;
+        }
+        // in theory nodeName can't be null but better be careful
+        // who knows what other implementations may be doing?...
+        if (getNodeName() == null) {
+            if (arg.getNodeName() != null) {
+                return false;
+            }
+        }
+        else if (!getNodeName().equals(arg.getNodeName())) {
+            return false;
+        }
+
+        if (getLocalName() == null) {
+            if (arg.getLocalName() != null) {
+                return false;
+            }
+        }
+        else if (!getLocalName().equals(arg.getLocalName())) {
+            return false;
+        }
+
+        if (getNamespaceURI() == null) {
+            if (arg.getNamespaceURI() != null) {
+                return false;
+            }
+        }
+        else if (!getNamespaceURI().equals(arg.getNamespaceURI())) {
+            return false;
+        }
+
+        if (getPrefix() == null) {
+            if (arg.getPrefix() != null) {
+                return false;
+            }
+        }
+        else if (!getPrefix().equals(arg.getPrefix())) {
+            return false;
+        }
+
+        if (getNodeValue() == null) {
+            if (arg.getNodeValue() != null) {
+                return false;
+            }
+        }
+        else if (!getNodeValue().equals(arg.getNodeValue())) {
+            return false;
+        }
+    /*
+        if (getBaseURI() == null) {
+            if (((NodeImpl) arg).getBaseURI() != null) {
+                return false;
+            }
+        }
+        else if (!getBaseURI().equals(((NodeImpl) arg).getBaseURI())) {
+            return false;
+        }
+*/
+        return true;
+    }
+
+    /**
+     * DOM Level 3:
+     * Look up the namespace URI associated to the given prefix, starting from this node.
+     * Use lookupNamespaceURI(null) to lookup the default namespace
+     *
+     * @param namespaceURI
+     * @return th URI for the namespace
+     * @since DOM Level 3
+     */
+    public String lookupNamespaceURI(String specifiedPrefix) {
+        short type = this.getNodeType();
+        switch (type) {
+        case Node.ELEMENT_NODE : {
+
+                String namespace = this.getNamespaceURI();
+                String prefix = this.getPrefix();
+                if (namespace !=null) {
+                    // REVISIT: is it possible that prefix is empty string?
+                    if (specifiedPrefix== null && prefix==specifiedPrefix) {
+                        // looking for default namespace
+                        return namespace;
+                    } else if (prefix != null && prefix.equals(specifiedPrefix)) {
+                        // non default namespace
+                        return namespace;
+                    }
+                }
+                if (this.hasAttributes()) {
+                    NamedNodeMap map = this.getAttributes();
+                    int length = map.getLength();
+                    for (int i=0;i<length;i++) {
+                        Node attr = map.item(i);
+                        String attrPrefix = attr.getPrefix();
+                        String value = attr.getNodeValue();
+                        namespace = attr.getNamespaceURI();
+                        if (namespace !=null && namespace.equals("http://www.w3.org/2000/xmlns/")) {
+                            // at this point we are dealing with DOM Level 2 nodes only
+                            if (specifiedPrefix == null &&
+                                attr.getNodeName().equals("xmlns")) {
+                                // default namespace
+                                return value;
+                            } else if (attrPrefix !=null &&
+                                       attrPrefix.equals("xmlns") &&
+                                       attr.getLocalName().equals(specifiedPrefix)) {
+                 // non default namespace
+                                return value;
+                            }
+                        }
+                    }
+                }
+		/*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupNamespaceURI(specifiedPrefix);
+                }
+		*/
+                return null;
+            }
+/*
+        case Node.DOCUMENT_NODE : {
+                return((NodeImpl)((Document)this).getDocumentElement()).lookupNamespaceURI(specifiedPrefix) ;
+            }
+*/
+        case Node.ENTITY_NODE :
+        case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return null;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
+                    return getOwnerElement().lookupNamespaceURI(specifiedPrefix);
+                }
+                return null;
+            }
+        default:{
+	   /*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupNamespaceURI(specifiedPrefix);
+                }
+             */
+                return null;
+            }
+
+        }
+    }
+    
+    /**
+     *  DOM Level 3:
+     *  This method checks if the specified <code>namespaceURI</code> is the
+     *  default namespace or not.
+     *  @param namespaceURI The namespace URI to look for.
+     *  @return  <code>true</code> if the specified <code>namespaceURI</code>
+     *   is the default namespace, <code>false</code> otherwise.
+     * @since DOM Level 3
+     */
+    public boolean isDefaultNamespace(String namespaceURI){
+       /*
+        // REVISIT: remove casts when DOM L3 becomes REC.
+        short type = this.getNodeType();
+        switch (type) {
+        case Node.ELEMENT_NODE: {
+            String namespace = this.getNamespaceURI();
+            String prefix = this.getPrefix();
+
+            // REVISIT: is it possible that prefix is empty string?
+            if (prefix == null || prefix.length() == 0) {
+                if (namespaceURI == null) {
+                    return (namespace == namespaceURI);
+                }
+                return namespaceURI.equals(namespace);
+            }
+            if (this.hasAttributes()) {
+                ElementImpl elem = (ElementImpl)this;
+                NodeImpl attr = (NodeImpl)elem.getAttributeNodeNS("http://www.w3.org/2000/xmlns/", "xmlns");
+                if (attr != null) {
+                    String value = attr.getNodeValue();
+                    if (namespaceURI == null) {
+                        return (namespace == value);
+                    }
+                    return namespaceURI.equals(value);
+                }
+            }
+
+            NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+            if (ancestor != null) {
+                return ancestor.isDefaultNamespace(namespaceURI);
+            }
+            return false;
+        }
+        case Node.DOCUMENT_NODE:{
+                return((NodeImpl)((Document)this).getDocumentElement()).isDefaultNamespace(namespaceURI);
+            }
+
+        case Node.ENTITY_NODE :
+          case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return false;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.ownerNode.getNodeType() == Node.ELEMENT_NODE) {
+                    return ownerNode.isDefaultNamespace(namespaceURI);
+
+                }
+                return false;
+            }
+        default:{  
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.isDefaultNamespace(namespaceURI);
+                }
+                return false;
+            }
+
+        }
+*/
+        return false;
+    }
+
+    /**
+     * DOM Level 3:
+     * Look up the prefix associated to the given namespace URI, starting from this node.
+     *
+     * @param namespaceURI
+     * @return the prefix for the namespace
+     */
+    public String lookupPrefix(String namespaceURI){
+
+        // REVISIT: When Namespaces 1.1 comes out this may not be true
+        // Prefix can't be bound to null namespace
+        if (namespaceURI == null) {
+            return null;
+        }
+
+        short type = this.getNodeType();
+
+        switch (type) {
+/*
+        case Node.ELEMENT_NODE: {
+
+                String namespace = this.getNamespaceURI(); // to flip out children
+                return lookupNamespacePrefix(namespaceURI, (ElementImpl)this);
+            }
+
+        case Node.DOCUMENT_NODE:{
+                return((NodeImpl)((Document)this).getDocumentElement()).lookupPrefix(namespaceURI);
+            }
+*/
+        case Node.ENTITY_NODE :
+        case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return null;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
+                    return getOwnerElement().lookupPrefix(namespaceURI);
+
+                }
+                return null;
+            }
+        default:{ 
+/*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupPrefix(namespaceURI);
+                }
+*/
+                return null;
+            }
+         }
+    }
+
+    /**
+     * Returns whether this node is the same node as the given one.
+     * <br>This method provides a way to determine whether two
+     * <code>Node</code> references returned by the implementation reference
+     * the same object. When two <code>Node</code> references are references
+     * to the same object, even if through a proxy, the references may be
+     * used completely interchangably, such that all attributes have the
+     * same values and calling the same DOM method on either reference
+     * always has exactly the same effect.
+     * @param other The node to test against.
+     * @return Returns <code>true</code> if the nodes are the same,
+     *   <code>false</code> otherwise.
+     * @since DOM Level 3
+     */
+    public boolean isSameNode(Node other) {
+        // we do not use any wrapper so the answer is obvious
+        return this == other;
+    }
+
+    /**
+     * This attribute returns the text content of this node and its
+     * descendants. When it is defined to be null, setting it has no effect.
+     * When set, any possible children this node may have are removed and
+     * replaced by a single <code>Text</code> node containing the string
+     * this attribute is set to. On getting, no serialization is performed,
+     * the returned string does not contain any markup. No whitespace
+     * normalization is performed, the returned string does not contain the
+     * element content whitespaces . Similarly, on setting, no parsing is
+     * performed either, the input string is taken as pure textual content.
+     * <br>The string returned is made of the text content of this node
+     * depending on its type, as defined below:
+     * <table border='1'>
+     * <tr>
+     * <th>Node type</th>
+     * <th>Content</th>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
+     * DOCUMENT_FRAGMENT_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
+     * attribute value of every child node, excluding COMMENT_NODE and
+     * PROCESSING_INSTRUCTION_NODE nodes</td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
+     * CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * <code>nodeValue</code></td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * null</td>
+     * </tr>
+     * </table>
+     * @exception DOMException
+     *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+     * @exception DOMException
+     *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
+     *   fit in a <code>DOMString</code> variable on the implementation
+     *   platform.
+     * @since DOM Level 3
+     */
+    public void setTextContent(String textContent)
+        throws DOMException {
+        setNodeValue(textContent);
+    }
+    
+    /**
+     * This attribute returns the text content of this node and its
+     * descendants. When it is defined to be null, setting it has no effect.
+     * When set, any possible children this node may have are removed and
+     * replaced by a single <code>Text</code> node containing the string
+     * this attribute is set to. On getting, no serialization is performed,
+     * the returned string does not contain any markup. No whitespace
+     * normalization is performed, the returned string does not contain the
+     * element content whitespaces . Similarly, on setting, no parsing is
+     * performed either, the input string is taken as pure textual content.
+     * <br>The string returned is made of the text content of this node
+     * depending on its type, as defined below:
+     * <table border='1'>
+     * <tr>
+     * <th>Node type</th>
+     * <th>Content</th>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
+     * DOCUMENT_FRAGMENT_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
+     * attribute value of every child node, excluding COMMENT_NODE and
+     * PROCESSING_INSTRUCTION_NODE nodes</td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
+     * CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * <code>nodeValue</code></td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * null</td>
+     * </tr>
+     * </table>
+     * @exception DOMException
+     *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+     * @exception DOMException
+     *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
+     *   fit in a <code>DOMString</code> variable on the implementation
+     *   platform.
+     * @since DOM Level 3
+     */
+    public String getTextContent() throws DOMException {
+        return dtm.getStringValue(node).toString();
+    }
+
+    /**
+     * Compares a node with this node with regard to their position in the
+     * document.
+     * @param other The node to compare against this node.
+     * @return Returns how the given node is positioned relatively to this
+     *   node.
+     * @since DOM Level 3
+     */
+    public short compareDocumentPosition(Node other) throws DOMException {
+        return 0;
+    }
+
+    /**
+     * The absolute base URI of this node or <code>null</code> if undefined.
+     * This value is computed according to . However, when the
+     * <code>Document</code> supports the feature "HTML" , the base URI is
+     * computed using first the value of the href attribute of the HTML BASE
+     * element if any, and the value of the <code>documentURI</code>
+     * attribute from the <code>Document</code> interface otherwise.
+     * <br> When the node is an <code>Element</code>, a <code>Document</code>
+     * or a a <code>ProcessingInstruction</code>, this attribute represents
+     * the properties [base URI] defined in . When the node is a
+     * <code>Notation</code>, an <code>Entity</code>, or an
+     * <code>EntityReference</code>, this attribute represents the
+     * properties [declaration base URI] in the . How will this be affected
+     * by resolution of relative namespace URIs issue?It's not.Should this
+     * only be on Document, Element, ProcessingInstruction, Entity, and
+     * Notation nodes, according to the infoset? If not, what is it equal to
+     * on other nodes? Null? An empty string? I think it should be the
+     * parent's.No.Should this be read-only and computed or and actual
+     * read-write attribute?Read-only and computed (F2F 19 Jun 2000 and
+     * teleconference 30 May 2001).If the base HTML element is not yet
+     * attached to a document, does the insert change the Document.baseURI?
+     * Yes. (F2F 26 Sep 2001)
+     * @since DOM Level 3
+     */
+    public String getBaseURI() {
+        return null;
+    }
+
+    /**
+     * DOM Level 3
+     * Renaming node
+     */
+    public Node renameNode(Node n,
+                           String namespaceURI,
+                           String name)
+                           throws DOMException{
+        return n;
+    }
+    
+    /**
+     *  DOM Level 3
+     *  Normalize document.
+     */
+    public void normalizeDocument(){   
+
+    }
+    
+    /**
+     *  The configuration used when <code>Document.normalizeDocument</code> is
+     * invoked.
+     * @since DOM Level 3
+     */
+    public DOMConfiguration getDomConfig(){
+       return null;
+    }
+    
+    /** DOM Level 3 feature: documentURI */
+    protected String fDocumentURI;
+
+    /**
+     * DOM Level 3
+     */
+    public void setDocumentURI(String documentURI){
+        
+        fDocumentURI= documentURI;
+    }
+
+    /**
+     * DOM Level 3
+     * The location of the document or <code>null</code> if undefined.
+     * <br>Beware that when the <code>Document</code> supports the feature
+     * "HTML" , the href attribute of the HTML BASE element takes precedence
+     * over this attribute.
+     * @since DOM Level 3
+     */
+    public String getDocumentURI(){
+        return fDocumentURI;
+    }
+
+    /** DOM Level 3 feature: Document actualEncoding */
+    protected String actualEncoding;
+
+    /**
+     * DOM Level 3
+     * An attribute specifying the actual encoding of this document. This is
+     * <code>null</code> otherwise.
+     * <br> This attribute represents the property [character encoding scheme]
+     * defined in .
+     * @since DOM Level 3
+     */
+    public String getActualEncoding() {
+        return actualEncoding;
+    }
+
+    /**
+     * DOM Level 3
+     * An attribute specifying the actual encoding of this document. This is
+     * <code>null</code> otherwise.
+     * <br> This attribute represents the property [character encoding scheme]
+     * defined in .
+     * @since DOM Level 3
+     */
+    public void setActualEncoding(String value) {
+        actualEncoding = value;
+    }
+
+   /**
+    * DOM Level 3
+    */
+    public Text replaceWholeText(String content)
+                                 throws DOMException{
+/*
+
+        if (needsSyncData()) {
+            synchronizeData();
+        }
+
+        // make sure we can make the replacement
+        if (!canModify(nextSibling)) {
+            throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
+                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
+        }
+
+        Node parent = this.getParentNode();
+        if (content == null || content.length() == 0) {
+            // remove current node
+            if (parent !=null) { // check if node in the tree
+                parent.removeChild(this);
+                return null;
+            }
+        }
+        Text currentNode = null;
+        if (isReadOnly()){
+            Text newNode = this.ownerDocument().createTextNode(content);
+            if (parent !=null) { // check if node in the tree
+                parent.insertBefore(newNode, this);
+                parent.removeChild(this);
+                currentNode = newNode;
+            } else {
+                return newNode;
+            }
+        }  else {
+            this.setData(content);
+            currentNode = this;
+        }
+        Node sibling =  currentNode.getNextSibling();
+        while ( sibling !=null) {
+            parent.removeChild(sibling);
+            sibling = currentNode.getNextSibling();
+        }
+
+        return currentNode;
+*/
+        return null; //Pending
+    }
+
+    /**
+     * DOM Level 3
+     * Returns all text of <code>Text</code> nodes logically-adjacent text
+     * nodes to this node, concatenated in document order.
+     * @since DOM Level 3
+     */
+    public String getWholeText(){
+
+/*
+        if (needsSyncData()) {
+            synchronizeData();
+        }
+        if (nextSibling == null) {
+            return data;
+        }
+        StringBuffer buffer = new StringBuffer();
+        if (data != null && data.length() != 0) {
+            buffer.append(data);
+        }
+        getWholeText(nextSibling, buffer);
+        return buffer.toString();
+*/
+        return null; // PENDING
+    }
+
+    /**
+     * DOM Level 3
+     * Returns whether this text node contains whitespace in element content,
+     * often abusively called "ignorable whitespace".
+     */
+    public boolean isElementContentWhitespace(){
+        return false;
+    }
+
+    /**
+     * NON-DOM: set the type of this attribute to be ID type.
+     *
+     * @param id
+     */
+    public void setIdAttribute(boolean id){
+        //PENDING
+    }
+
+    /**
+     * DOM Level 3: register the given attribute node as an ID attribute
+     */
+    public void setIdAttribute(String name, boolean makeId) {
+        //PENDING
+    }
+
+       
+    /**
+     * DOM Level 3: register the given attribute node as an ID attribute
+     */
+    public void setIdAttributeNode(Attr at, boolean makeId) {
+        //PENDING
+    }
+
+    /**
+     * DOM Level 3: register the given attribute node as an ID attribute
+     */
+    public void setIdAttributeNS(String namespaceURI, String localName,
+                                    boolean makeId) {
+        //PENDING
+    }
+
+    public TypeInfo getSchemaTypeInfo(){
+      return null; //PENDING
+    }
+
+    public boolean isId() {
+        return false; //PENDING
+    }
+
+
+    private String xmlEncoding;
+    
+    public String getXmlEncoding( ) {
+        return xmlEncoding;
+    }
+    
+    public void setXmlEncoding( String xmlEncoding ) {
+        this.xmlEncoding = xmlEncoding;
+    }
+
+    private boolean xmlStandalone;
+    
+    public boolean getXmlStandalone() {
+        return xmlStandalone;
+    }
+
+    public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
+        this.xmlStandalone = xmlStandalone;
+    }
+
+    private String xmlVersion;
+    
+    public String getXmlVersion() {
+        return xmlVersion;
+    }
+
+    public void setXmlVersion(String xmlVersion) throws DOMException {
+        this.xmlVersion = xmlVersion;
+    }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMStringPool.java b/src/main/java/org/apache/xml/dtm/ref/DTMStringPool.java
new file mode 100644
index 0000000..a47e7bf
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMStringPool.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMStringPool.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+
+package org.apache.xml.dtm.ref;
+
+import java.util.Vector;
+
+import org.apache.xml.utils.IntVector;
+
+/** <p>DTMStringPool is an "interning" mechanism for strings. It will
+ * create a stable 1:1 mapping between a set of string values and a set of
+ * integer index values, so the integers can be used to reliably and
+ * uniquely identify (and when necessary retrieve) the strings.</p>
+ *
+ * <p>Design Priorities:
+ * <ul>
+ * <li>String-to-index lookup speed is critical.</li>
+ * <li>Index-to-String lookup speed is slightly less so.</li>
+ * <li>Threadsafety is not guaranteed at this level.
+ * Enforce that in the application if needed.</li>
+ * <li>Storage efficiency is an issue but not a huge one.
+ * It is expected that string pools won't exceed about 2000 entries.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>Implementation detail: A standard Hashtable is relatively
+ * inefficient when looking up primitive int values, especially when
+ * we're already maintaining an int-to-string vector.  So I'm
+ * maintaining a simple hash chain within this class.</p>
+ *
+ * <p>NOTE: There is nothing in the code that has a real dependency upon
+ * String. It would work with any object type that implements reliable
+ * .hashCode() and .equals() operations. The API enforces Strings because
+ * it's safer that way, but this could trivially be turned into a general
+ * ObjectPool if one was needed.</p>
+ *
+ * <p>Status: Passed basic test in main().</p>
+ * */
+public class DTMStringPool
+{
+  Vector m_intToString;
+  static final int HASHPRIME=101;
+  int[] m_hashStart=new int[HASHPRIME];
+  IntVector m_hashChain;
+  public static final int NULL=-1;
+
+  /**
+   * Create a DTMStringPool using the given chain size
+   * 
+   * @param chainSize The size of the hash chain vector
+   */
+  public DTMStringPool(int chainSize)
+    {
+      m_intToString=new Vector();
+      m_hashChain=new IntVector(chainSize);
+      removeAllElements();
+      
+      // -sb Add this to force empty strings to be index 0.
+      stringToIndex("");
+    }
+  
+  public DTMStringPool()
+    {
+      this(512);	
+    }
+    
+  public void removeAllElements()
+    {
+      m_intToString.removeAllElements();
+      for(int i=0;i<HASHPRIME;++i)
+        m_hashStart[i]=NULL;
+      m_hashChain.removeAllElements();
+    }
+
+  /** @return string whose value is uniquely identified by this integer index.
+   * @throws java.lang.ArrayIndexOutOfBoundsException
+   *  if index doesn't map to a string.
+   * */ 
+  public String indexToString(int i)
+    throws java.lang.ArrayIndexOutOfBoundsException
+    {
+      if(i==NULL) return null;
+      return (String) m_intToString.elementAt(i);
+    }
+
+  /** @return integer index uniquely identifying the value of this string. */ 
+  public int stringToIndex(String s)
+    {
+      if(s==null) return NULL;
+      
+      int hashslot=s.hashCode()%HASHPRIME;
+      if(hashslot<0) hashslot=-hashslot;
+
+      // Is it one we already know?
+      int hashlast=m_hashStart[hashslot];
+      int hashcandidate=hashlast;
+      while(hashcandidate!=NULL)
+        {
+          if(m_intToString.elementAt(hashcandidate).equals(s))
+            return hashcandidate;
+
+          hashlast=hashcandidate;
+          hashcandidate=m_hashChain.elementAt(hashcandidate);
+        }
+      
+      // New value. Add to tables.
+      int newIndex=m_intToString.size();
+      m_intToString.addElement(s);
+
+      m_hashChain.addElement(NULL);	// Initialize to no-following-same-hash
+      if(hashlast==NULL)  // First for this hash
+        m_hashStart[hashslot]=newIndex;
+      else // Link from previous with same hash
+        m_hashChain.setElementAt(newIndex,hashlast);
+
+      return newIndex;
+    }
+
+  /** Command-line unit test driver. This test relies on the fact that
+   * this version of the pool assigns indices consecutively, starting
+   * from zero, as new unique strings are encountered.
+   */
+  public static void main(String[] args)
+  {
+    String[] word={
+      "Zero","One","Two","Three","Four","Five",
+      "Six","Seven","Eight","Nine","Ten",
+      "Eleven","Twelve","Thirteen","Fourteen","Fifteen",
+      "Sixteen","Seventeen","Eighteen","Nineteen","Twenty",
+      "Twenty-One","Twenty-Two","Twenty-Three","Twenty-Four",
+      "Twenty-Five","Twenty-Six","Twenty-Seven","Twenty-Eight",
+      "Twenty-Nine","Thirty","Thirty-One","Thirty-Two",
+      "Thirty-Three","Thirty-Four","Thirty-Five","Thirty-Six",
+      "Thirty-Seven","Thirty-Eight","Thirty-Nine"};
+
+    DTMStringPool pool=new DTMStringPool();
+
+    System.out.println("If no complaints are printed below, we passed initial test.");
+
+    for(int pass=0;pass<=1;++pass)
+      {
+        int i;
+
+        for(i=0;i<word.length;++i)
+          {
+            int j=pool.stringToIndex(word[i]);
+            if(j!=i)
+              System.out.println("\tMismatch populating pool: assigned "+
+                                 j+" for create "+i);
+          }
+
+        for(i=0;i<word.length;++i)
+          {
+            int j=pool.stringToIndex(word[i]);
+            if(j!=i)
+              System.out.println("\tMismatch in stringToIndex: returned "+
+                                 j+" for lookup "+i);
+          }
+
+        for(i=0;i<word.length;++i)
+          {
+            String w=pool.indexToString(i);
+            if(!word[i].equals(w))
+              System.out.println("\tMismatch in indexToString: returned"+
+                                 w+" for lookup "+i);
+          }
+        
+        pool.removeAllElements();
+        
+        System.out.println("\nPass "+pass+" complete\n");
+      } // end pass loop
+  }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/DTMTreeWalker.java b/src/main/java/org/apache/xml/dtm/ref/DTMTreeWalker.java
new file mode 100644
index 0000000..73d2fc0
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/DTMTreeWalker.java
@@ -0,0 +1,404 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DTMTreeWalker.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.NodeConsumer;
+import org.apache.xml.utils.XMLString;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * This class does a pre-order walk of the DTM tree, calling a ContentHandler
+ * interface as it goes. As such, it's more like the Visitor design pattern
+ * than like the DOM's TreeWalker.
+ *
+ * I think normally this class should not be needed, because 
+ * of DTM#dispatchToEvents.
+ * @xsl.usage advanced
+ */
+public class DTMTreeWalker
+{
+
+  /** Local reference to a ContentHandler          */
+  private ContentHandler m_contentHandler = null;
+
+  /** DomHelper for this TreeWalker          */
+  protected DTM m_dtm;
+  
+  /**
+   * Set the DTM to be traversed.
+   * 
+   * @param dtm The Document Table Model to be used.
+   */
+  public void setDTM(DTM dtm)
+  {
+    m_dtm = dtm;
+  }
+
+  /**
+   * Get the ContentHandler used for the tree walk.
+   *
+   * @return the ContentHandler used for the tree walk
+   */
+  public ContentHandler getcontentHandler()
+  {
+    return m_contentHandler;
+  }
+  
+  /**
+   * Set the ContentHandler used for the tree walk.
+   *
+   * @param ch the ContentHandler to be the result of the tree walk.
+   */
+  public void setcontentHandler(ContentHandler ch)
+  {
+    m_contentHandler = ch;
+  }
+
+  
+  /**
+   * Constructor.
+   */
+  public DTMTreeWalker()
+  {
+  }
+  
+  /**
+   * Constructor.
+   * @param   contentHandler The implemention of the
+   * contentHandler operation (toXMLString, digest, ...)
+   */
+  public DTMTreeWalker(ContentHandler contentHandler, DTM dtm)
+  {
+    this.m_contentHandler = contentHandler;
+    m_dtm = dtm;
+  }
+  
+  /** Perform a non-recursive pre-order/post-order traversal,
+   * operating as a Visitor. startNode (preorder) and endNode
+   * (postorder) are invoked for each node as we traverse over them,
+   * with the result that the node is written out to m_contentHandler.
+   *
+   * @param pos Node in the tree at which to start (and end) traversal --
+   * in other words, the root of the subtree to traverse over.
+   *
+   * @throws TransformerException */
+  public void traverse(int pos) throws org.xml.sax.SAXException
+  {
+    // %REVIEW% Why isn't this just traverse(pos,pos)?
+
+    int top = pos;		// Remember the root of this subtree
+
+    while (DTM.NULL != pos)
+    {
+      startNode(pos);
+      int nextNode = m_dtm.getFirstChild(pos);
+      while (DTM.NULL == nextNode)
+      {
+        endNode(pos);
+
+        if (top == pos)
+          break;
+
+        nextNode = m_dtm.getNextSibling(pos);
+
+        if (DTM.NULL == nextNode)
+        {
+          pos = m_dtm.getParent(pos);
+
+          if ((DTM.NULL == pos) || (top == pos))
+          {
+            // %REVIEW% This condition isn't tested in traverse(pos,top)
+            // -- bug?
+            if (DTM.NULL != pos)
+              endNode(pos);
+
+            nextNode = DTM.NULL;
+
+            break;
+          }
+        }
+      }
+
+      pos = nextNode;
+    }
+  }
+
+  /** Perform a non-recursive pre-order/post-order traversal,
+   * operating as a Visitor. startNode (preorder) and endNode
+   * (postorder) are invoked for each node as we traverse over them,
+   * with the result that the node is written out to m_contentHandler.
+   *
+   * @param pos Node in the tree where to start traversal
+   * @param top Node in the tree where to end traversal.
+   * If top==DTM.NULL, run through end of document.
+   *
+   * @throws TransformerException
+   */
+  public void traverse(int pos, int top) throws org.xml.sax.SAXException
+  {
+    // %OPT% Can we simplify the loop conditionals by adding:
+    //		if(top==DTM.NULL) top=0
+    // -- or by simply ignoring this case and relying on the fact that
+    // pos will never equal DTM.NULL until we're ready to exit?
+
+    while (DTM.NULL != pos)
+    {
+      startNode(pos);
+      int nextNode = m_dtm.getFirstChild(pos);
+      while (DTM.NULL == nextNode)
+      {
+        endNode(pos);
+
+        if ((DTM.NULL != top) && top == pos)
+          break;
+
+        nextNode = m_dtm.getNextSibling(pos);
+
+        if (DTM.NULL == nextNode)
+        {
+          pos = m_dtm.getParent(pos);
+
+          if ((DTM.NULL == pos) || ((DTM.NULL != top) && (top == pos)))
+          {
+            nextNode = DTM.NULL;
+
+            break;
+          }
+        }
+      }
+
+      pos = nextNode;
+    }
+  }
+
+  /** Flag indicating whether following text to be processed is raw text          */
+  boolean nextIsRaw = false;
+  
+  /**
+   * Optimized dispatch of characters.
+   */
+  private final void dispatachChars(int node)
+     throws org.xml.sax.SAXException
+  {
+    m_dtm.dispatchCharactersEvents(node, m_contentHandler, false);
+  }
+
+  /**
+   * Start processing given node
+   *
+   *
+   * @param node Node to process
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  protected void startNode(int node) throws org.xml.sax.SAXException
+  {
+
+    if (m_contentHandler instanceof NodeConsumer)
+    {
+      // %TBD%
+//      ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
+    }
+
+    switch (m_dtm.getNodeType(node))
+    {
+    case DTM.COMMENT_NODE :
+    {
+      XMLString data = m_dtm.getStringValue(node);
+
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
+        data.dispatchAsComment(lh);
+      }
+    }
+    break;
+    case DTM.DOCUMENT_FRAGMENT_NODE :
+
+      // ??;
+      break;
+    case DTM.DOCUMENT_NODE :
+      this.m_contentHandler.startDocument();
+      break;
+    case DTM.ELEMENT_NODE :
+      DTM dtm = m_dtm;           
+
+      for (int nsn = dtm.getFirstNamespaceNode(node, true); DTM.NULL != nsn;
+           nsn = dtm.getNextNamespaceNode(node, nsn, true))
+      {
+        // String prefix = dtm.getPrefix(nsn);
+        String prefix = dtm.getNodeNameX(nsn);
+
+        this.m_contentHandler.startPrefixMapping(prefix, dtm.getNodeValue(nsn));
+        
+      }
+
+      // System.out.println("m_dh.getNamespaceOfNode(node): "+m_dh.getNamespaceOfNode(node));
+      // System.out.println("m_dh.getLocalNameOfNode(node): "+m_dh.getLocalNameOfNode(node));
+      String ns = dtm.getNamespaceURI(node);
+      if(null == ns)
+        ns = "";
+        
+      // %OPT% !!
+      org.xml.sax.helpers.AttributesImpl attrs = 
+                            new org.xml.sax.helpers.AttributesImpl();
+              
+      for (int i = dtm.getFirstAttribute(node); 
+           i != DTM.NULL; 
+           i = dtm.getNextAttribute(i)) 
+      {
+        attrs.addAttribute(dtm.getNamespaceURI(i), 
+                           dtm.getLocalName(i), 
+                           dtm.getNodeName(i), 
+                           "CDATA", 
+                           dtm.getNodeValue(i));
+      }
+      
+        
+      this.m_contentHandler.startElement(ns,
+                                         m_dtm.getLocalName(node),
+                                         m_dtm.getNodeName(node),
+                                         attrs);
+      break;
+    case DTM.PROCESSING_INSTRUCTION_NODE :
+    {
+      String name = m_dtm.getNodeName(node);
+
+      // String data = pi.getData();
+      if (name.equals("xslt-next-is-raw"))
+      {
+        nextIsRaw = true;
+      }
+      else
+      {
+        this.m_contentHandler.processingInstruction(name,
+                                                    m_dtm.getNodeValue(node));
+      }
+    }
+    break;
+    case DTM.CDATA_SECTION_NODE :
+    {
+      boolean isLexH = (m_contentHandler instanceof LexicalHandler);
+      LexicalHandler lh = isLexH
+                          ? ((LexicalHandler) this.m_contentHandler) : null;
+
+      if (isLexH)
+      {
+        lh.startCDATA();
+      }
+      
+      dispatachChars(node);
+
+      {
+        if (isLexH)
+        {
+          lh.endCDATA();
+        }
+      }
+    }
+    break;
+    case DTM.TEXT_NODE :
+    {
+      if (nextIsRaw)
+      {
+        nextIsRaw = false;
+
+        m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
+        dispatachChars(node);
+        m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
+      }
+      else
+      {
+        dispatachChars(node);
+      }
+    }
+    break;
+    case DTM.ENTITY_REFERENCE_NODE :
+    {
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        ((LexicalHandler) this.m_contentHandler).startEntity(
+          m_dtm.getNodeName(node));
+      }
+      else
+      {
+
+        // warning("Can not output entity to a pure SAX ContentHandler");
+      }
+    }
+    break;
+    default :
+    }
+  }
+
+  /**
+   * End processing of given node 
+   *
+   *
+   * @param node Node we just finished processing
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  protected void endNode(int node) throws org.xml.sax.SAXException
+  {
+
+    switch (m_dtm.getNodeType(node))
+    {
+    case DTM.DOCUMENT_NODE :
+      this.m_contentHandler.endDocument();
+      break;
+    case DTM.ELEMENT_NODE :
+      String ns = m_dtm.getNamespaceURI(node);
+      if(null == ns)
+        ns = "";
+      this.m_contentHandler.endElement(ns,
+                                         m_dtm.getLocalName(node),
+                                         m_dtm.getNodeName(node));
+
+      for (int nsn = m_dtm.getFirstNamespaceNode(node, true); DTM.NULL != nsn;
+           nsn = m_dtm.getNextNamespaceNode(node, nsn, true))
+      {
+        // String prefix = m_dtm.getPrefix(nsn);
+        String prefix = m_dtm.getNodeNameX(nsn);
+
+        this.m_contentHandler.endPrefixMapping(prefix);
+      }
+      break;
+    case DTM.CDATA_SECTION_NODE :
+      break;
+    case DTM.ENTITY_REFERENCE_NODE :
+    {
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
+
+        lh.endEntity(m_dtm.getNodeName(node));
+      }
+    }
+    break;
+    default :
+    }
+  }
+}  //TreeWalker
+
diff --git a/src/main/java/org/apache/xml/dtm/ref/ExpandedNameTable.java b/src/main/java/org/apache/xml/dtm/ref/ExpandedNameTable.java
new file mode 100644
index 0000000..9fcdbd6
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/ExpandedNameTable.java
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExpandedNameTable.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+import org.apache.xml.dtm.DTM;
+
+/**
+ * This is a default implementation of a table that manages mappings from
+ * expanded names to expandedNameIDs.
+ *
+ * %OPT% The performance of the getExpandedTypeID() method is very important 
+ * to DTM building. To get the best performance out of this class, we implement
+ * a simple hash algorithm directly into this class, instead of using the
+ * inefficient java.util.Hashtable. The code for the get and put operations
+ * are combined in getExpandedTypeID() method to share the same hash calculation
+ * code. We only need to implement the rehash() interface which is used to
+ * expand the hash table.
+ */
+public class ExpandedNameTable
+{
+
+  /** Array of extended types for this document   */
+  private ExtendedType[] m_extendedTypes;
+
+  /** The initial size of the m_extendedTypes array */
+  private static int m_initialSize = 128;
+  
+  /** Next available extended type   */
+  // %REVIEW% Since this is (should be) always equal 
+  // to the length of m_extendedTypes, do we need this? 
+  private int m_nextType;
+
+  // These are all the types prerotated, for caller convenience.
+  public static final int ELEMENT = ((int)DTM.ELEMENT_NODE) ;
+  public static final int ATTRIBUTE = ((int)DTM.ATTRIBUTE_NODE) ;
+  public static final int TEXT = ((int)DTM.TEXT_NODE) ;
+  public static final int CDATA_SECTION = ((int)DTM.CDATA_SECTION_NODE) ;
+  public static final int ENTITY_REFERENCE = ((int)DTM.ENTITY_REFERENCE_NODE) ;
+  public static final int ENTITY = ((int)DTM.ENTITY_NODE) ;
+  public static final int PROCESSING_INSTRUCTION = ((int)DTM.PROCESSING_INSTRUCTION_NODE) ;
+  public static final int COMMENT = ((int)DTM.COMMENT_NODE) ;
+  public static final int DOCUMENT = ((int)DTM.DOCUMENT_NODE) ;
+  public static final int DOCUMENT_TYPE = ((int)DTM.DOCUMENT_TYPE_NODE) ;
+  public static final int DOCUMENT_FRAGMENT =((int)DTM.DOCUMENT_FRAGMENT_NODE) ;
+  public static final int NOTATION = ((int)DTM.NOTATION_NODE) ;
+  public static final int NAMESPACE = ((int)DTM.NAMESPACE_NODE) ;
+
+  /** Workspace for lookup. NOT THREAD SAFE!
+   * */
+  ExtendedType hashET = new ExtendedType(-1, "", "");
+
+  /** The array to store the default extended types. */
+  private static ExtendedType[] m_defaultExtendedTypes;
+
+  /**
+   * The default load factor of the Hashtable.
+   * This is used to calcualte the threshold.
+   */
+  private static float m_loadFactor = 0.75f;
+    
+  /**
+   * The initial capacity of the hash table. Use a bigger number
+   * to avoid the cost of expanding the table.
+   */ 
+  private static int m_initialCapacity = 203;
+  
+  /**
+   * The capacity of the hash table, i.e. the size of the
+   * internal HashEntry array.
+   */ 
+  private int m_capacity;
+  
+  /** 
+   * The threshold of the hash table, which is equal to capacity * loadFactor.
+   * If the number of entries in the hash table is bigger than the threshold,
+   * the hash table needs to be expanded.
+   */
+  private int m_threshold;
+  
+  /**
+   * The internal array to store the hash entries.
+   * Each array member is a slot for a hash bucket.
+   */
+  private HashEntry[] m_table;
+
+  /**
+   * Init default values
+   */
+  static {
+    m_defaultExtendedTypes = new ExtendedType[DTM.NTYPES];
+
+    for (int i = 0; i < DTM.NTYPES; i++)
+    {
+      m_defaultExtendedTypes[i] = new ExtendedType(i, "", "");
+    }
+  }
+
+  /**
+   * Create an expanded name table.
+   */
+  public ExpandedNameTable()
+  {
+    m_capacity = m_initialCapacity;
+    m_threshold = (int)(m_capacity * m_loadFactor);
+    m_table = new HashEntry[m_capacity];
+    
+    initExtendedTypes();
+  }
+
+
+  /**
+   *  Initialize the vector of extended types with the
+   *  basic DOM node types.
+   */
+  private void initExtendedTypes()
+  {    
+    m_extendedTypes = new ExtendedType[m_initialSize];
+    for (int i = 0; i < DTM.NTYPES; i++) {
+        m_extendedTypes[i] = m_defaultExtendedTypes[i];
+        m_table[i] = new HashEntry(m_defaultExtendedTypes[i], i, i, null);
+    }
+    
+    m_nextType = DTM.NTYPES;
+  }
+
+  /**
+   * Given an expanded name represented by namespace, local name and node type,
+   * return an ID.  If the expanded-name does not exist in the internal tables,
+   * the entry will be created, and the ID will be returned.  Any additional 
+   * nodes that are created that have this expanded name will use this ID.
+   *
+   * @param namespace The namespace
+   * @param localName The local name
+   * @param type The node type
+   *
+   * @return the expanded-name id of the node.
+   */
+  public int getExpandedTypeID(String namespace, String localName, int type)
+  {
+    return getExpandedTypeID(namespace, localName, type, false);
+  }
+  
+  /**
+   * Given an expanded name represented by namespace, local name and node type,
+   * return an ID.  If the expanded-name does not exist in the internal tables,
+   * the entry will be created, and the ID will be returned.  Any additional 
+   * nodes that are created that have this expanded name will use this ID.
+   * <p>
+   * If searchOnly is true, we will return -1 if the name is not found in the 
+   * table, otherwise the name is added to the table and the expanded name id
+   * of the new entry is returned.
+   *
+   * @param namespace The namespace
+   * @param localName The local name
+   * @param type The node type
+   * @param searchOnly If it is true, we will only search for the expanded name.
+   * -1 is return is the name is not found.
+   *
+   * @return the expanded-name id of the node.
+   */
+  public int getExpandedTypeID(String namespace, String localName, int type, boolean searchOnly)
+  {
+    if (null == namespace)
+      namespace = "";
+    if (null == localName)
+      localName = "";
+    
+    // Calculate the hash code
+    int hash = type + namespace.hashCode() + localName.hashCode();
+    
+    // Redefine the hashET object to represent the new expanded name.
+    hashET.redefine(type, namespace, localName, hash);
+    
+    // Calculate the index into the HashEntry table.
+    int index = hash % m_capacity;
+    if (index < 0)
+      index = -index;
+
+    // Look up the expanded name in the hash table. Return the id if
+    // the expanded name is already in the hash table.
+    for (HashEntry e = m_table[index]; e != null; e = e.next)
+    {
+      if (e.hash == hash && e.key.equals(hashET))
+        return e.value;
+    }
+    
+    if (searchOnly)
+    {
+      return DTM.NULL;
+    }
+
+    // Expand the internal HashEntry array if necessary.
+    if (m_nextType > m_threshold) {
+      rehash();
+      index = hash % m_capacity;
+      if (index < 0)
+        index = -index;
+    }
+    
+    // Create a new ExtendedType object
+    ExtendedType newET = new ExtendedType(type, namespace, localName, hash);
+    
+    // Expand the m_extendedTypes array if necessary.
+    if (m_extendedTypes.length == m_nextType) {
+        ExtendedType[] newArray = new ExtendedType[m_extendedTypes.length * 2];
+        System.arraycopy(m_extendedTypes, 0, newArray, 0,
+                         m_extendedTypes.length);
+        m_extendedTypes = newArray;
+    }
+    
+    m_extendedTypes[m_nextType] = newET;
+    
+    // Create a new hash entry for the new ExtendedType and put it into 
+    // the table.
+    HashEntry entry = new HashEntry(newET, m_nextType, hash, m_table[index]);
+    m_table[index] = entry;
+
+    return m_nextType++;
+  }
+
+  /**
+   * Increases the capacity of and internally reorganizes the hashtable, 
+   * in order to accommodate and access its entries more efficiently. 
+   * This method is called when the number of keys in the hashtable exceeds
+   * this hashtable's capacity and load factor.
+   */
+  private void rehash()
+  {
+    int oldCapacity = m_capacity;
+    HashEntry[] oldTable = m_table;
+      
+    int newCapacity = 2 * oldCapacity + 1;
+    m_capacity = newCapacity;
+    m_threshold = (int)(newCapacity * m_loadFactor);
+      
+    m_table = new HashEntry[newCapacity];
+    for (int i = oldCapacity-1; i >=0 ; i--)
+    {
+      for (HashEntry old = oldTable[i]; old != null; )
+      {
+        HashEntry e = old;
+        old = old.next;
+          
+        int newIndex = e.hash % newCapacity;
+        if (newIndex < 0)
+          newIndex = -newIndex;
+          
+        e.next = m_table[newIndex];
+        m_table[newIndex] = e;
+      }
+    }
+  }
+
+  /**
+   * Given a type, return an expanded name ID.Any additional nodes that are
+   * created that have this expanded name will use this ID.
+   *
+   * @return the expanded-name id of the node.
+   */
+  public int getExpandedTypeID(int type)
+  {
+    return type;
+  }
+
+  /**
+   * Given an expanded-name ID, return the local name part.
+   *
+   * @param ExpandedNameID an ID that represents an expanded-name.
+   * @return String Local name of this node, or null if the node has no name.
+   */
+  public String getLocalName(int ExpandedNameID)
+  {
+    return m_extendedTypes[ExpandedNameID].getLocalName();
+  }
+
+  /**
+   * Given an expanded-name ID, return the local name ID.
+   *
+   * @param ExpandedNameID an ID that represents an expanded-name.
+   * @return The id of this local name.
+   */
+  public final int getLocalNameID(int ExpandedNameID)
+  {
+    // ExtendedType etype = m_extendedTypes[ExpandedNameID];
+    if (m_extendedTypes[ExpandedNameID].getLocalName().equals(""))
+      return 0;
+    else
+    return ExpandedNameID;
+  }
+
+
+  /**
+   * Given an expanded-name ID, return the namespace URI part.
+   *
+   * @param ExpandedNameID an ID that represents an expanded-name.
+   * @return String URI value of this node's namespace, or null if no
+   * namespace was resolved.
+   */
+  public String getNamespace(int ExpandedNameID)
+  {
+    String namespace = m_extendedTypes[ExpandedNameID].getNamespace();
+    return (namespace.equals("") ? null : namespace);
+  }
+
+  /**
+   * Given an expanded-name ID, return the namespace URI ID.
+   *
+   * @param ExpandedNameID an ID that represents an expanded-name.
+   * @return The id of this namespace.
+   */
+  public final int getNamespaceID(int ExpandedNameID)
+  {
+    //ExtendedType etype = m_extendedTypes[ExpandedNameID];
+    if (m_extendedTypes[ExpandedNameID].getNamespace().equals(""))
+      return 0;
+    else
+    return ExpandedNameID;
+  }
+
+  /**
+   * Given an expanded-name ID, return the local name ID.
+   *
+   * @param ExpandedNameID an ID that represents an expanded-name.
+   * @return The id of this local name.
+   */
+  public final short getType(int ExpandedNameID)
+  {
+    //ExtendedType etype = m_extendedTypes[ExpandedNameID];
+    return (short)m_extendedTypes[ExpandedNameID].getNodeType();
+  }
+  
+  /**
+   * Return the size of the ExpandedNameTable
+   *
+   * @return The size of the ExpandedNameTable
+   */
+  public int getSize()
+  {
+    return m_nextType;
+  }
+  
+  /**
+   * Return the array of extended types
+   *
+   * @return The array of extended types
+   */
+  public ExtendedType[] getExtendedTypes()
+  {
+    return m_extendedTypes;
+  }
+
+  /**
+   * Inner class which represents a hash table entry.
+   * The field next points to the next entry which is hashed into
+   * the same bucket in the case of "hash collision".
+   */
+  private static final class HashEntry
+  {
+    ExtendedType key;
+    int value;
+    int hash;
+    HashEntry next;
+      
+    protected HashEntry(ExtendedType key, int value, int hash, HashEntry next)
+    {
+      this.key = key;
+      this.value = value;
+      this.hash = hash;
+      this.next = next;
+    }
+  }
+  
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/ExtendedType.java b/src/main/java/org/apache/xml/dtm/ref/ExtendedType.java
new file mode 100644
index 0000000..fd4e1d0
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/ExtendedType.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExtendedType.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref;
+
+/**
+ * The class ExtendedType represents an extended type object used by
+ * ExpandedNameTable.
+ */
+public final class ExtendedType
+{
+    private int nodetype;
+    private String namespace;
+    private String localName;
+    private int hash;
+
+    /**
+     * Create an ExtendedType object from node type, namespace and local name.
+     * The hash code is calculated from the node type, namespace and local name.
+     * 
+     * @param nodetype Type of the node
+     * @param namespace Namespace of the node
+     * @param localName Local name of the node
+     */
+    public ExtendedType (int nodetype, String namespace, String localName)
+    {
+      this.nodetype = nodetype;
+      this.namespace = namespace;
+      this.localName = localName;
+      this.hash = nodetype + namespace.hashCode() + localName.hashCode();
+    }
+
+    /**
+     * Create an ExtendedType object from node type, namespace, local name
+     * and a given hash code.
+     * 
+     * @param nodetype Type of the node
+     * @param namespace Namespace of the node
+     * @param localName Local name of the node
+     * @param hash The given hash code
+     */
+    public ExtendedType (int nodetype, String namespace, String localName, int hash)
+    {
+      this.nodetype = nodetype;
+      this.namespace = namespace;
+      this.localName = localName;
+      this.hash = hash;
+    }
+
+    /** 
+     * Redefine this ExtendedType object to represent a different extended type.
+     * This is intended to be used ONLY on the hashET object. Using it elsewhere
+     * will mess up existing hashtable entries!
+     */
+    protected void redefine(int nodetype, String namespace, String localName)
+    {
+      this.nodetype = nodetype;
+      this.namespace = namespace;
+      this.localName = localName;
+      this.hash = nodetype + namespace.hashCode() + localName.hashCode();
+    }
+
+    /** 
+     * Redefine this ExtendedType object to represent a different extended type.
+     * This is intended to be used ONLY on the hashET object. Using it elsewhere
+     * will mess up existing hashtable entries!
+     */
+    protected void redefine(int nodetype, String namespace, String localName, int hash)
+    {
+      this.nodetype = nodetype;
+      this.namespace = namespace;
+      this.localName = localName;
+      this.hash = hash;
+    }
+
+    /**
+     * Override the hashCode() method in the Object class
+     */
+    public int hashCode()
+    {
+      return hash;
+    }
+
+    /**
+     * Test if this ExtendedType object is equal to the given ExtendedType.
+     * 
+     * @param other The other ExtendedType object to test for equality
+     * @return true if the two ExtendedType objects are equal.
+     */
+    public boolean equals(ExtendedType other)
+    {
+      try
+      {
+        return other.nodetype == this.nodetype &&
+                other.localName.equals(this.localName) &&
+                other.namespace.equals(this.namespace);
+      }
+      catch(NullPointerException e)
+      {
+        return false;
+      }
+    }
+    
+    /**
+     * Return the node type
+     */
+    public int getNodeType()
+    {
+      return nodetype;
+    }
+    
+    /**
+     * Return the local name
+     */
+    public String getLocalName()
+    {
+      return localName;
+    }
+    
+    /**
+     * Return the namespace
+     */
+    public String getNamespace()
+    {
+      return namespace;
+    }
+
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/IncrementalSAXSource.java b/src/main/java/org/apache/xml/dtm/ref/IncrementalSAXSource.java
new file mode 100644
index 0000000..5a12ff6
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/IncrementalSAXSource.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: IncrementalSAXSource.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+
+package org.apache.xml.dtm.ref;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/** <p>IncrementalSAXSource is an API that delivers a small number of
+ * SAX events each time a request is made from a "controller"
+ * coroutine.  See IncrementalSAXFilter and IncrementalSAXFilter_Xerces
+ * for examples.
+ * 
+ * Note that interaction is via the deliverMoreNodes
+ * method, and therefore coroutine support is not exposed
+ * here.</p>
+ * */
+public interface IncrementalSAXSource
+{
+  // ------------------------------------------------------------------
+  // SAX Output API
+  // ------------------------------------------------------------------
+
+  /** Register a SAX-style content handler for us to output to
+   */
+  public void setContentHandler(ContentHandler handler);
+
+  /**  Register a SAX-style lexical handler for us to output to
+   */
+  public void setLexicalHandler(org.xml.sax.ext.LexicalHandler handler);
+
+  /**  Register a SAX-style DTD handler for us to output to
+   */
+  public void setDTDHandler(org.xml.sax.DTDHandler handler);
+
+  // ------------------------------------------------------------------
+  // Command Input API
+  // ------------------------------------------------------------------
+
+  /** deliverMoreNodes() is a simple API which tells the thread in which the
+   * IncrementalSAXSource is running to deliver more events (true),
+   * or stop delivering events and close out its input (false).
+   *
+   * This is intended to be called from one of our partner coroutines,
+   * and serves to encapsulate the coroutine communication protocol.
+   *
+   * @param parsemore If true, tells the incremental SAX stream to deliver
+   * another chunk of events. If false, finishes out the stream.
+   *
+   * @return Boolean.TRUE if the IncrementalSAXSource believes more data
+   * may be available for further parsing. Boolean.FALSE if parsing
+   * ran to completion, or was ended by deliverMoreNodes(false).
+   * */
+  public Object deliverMoreNodes (boolean parsemore);
+
+  // ------------------------------------------------------------------
+  // Parse Thread Convenience API
+  // ------------------------------------------------------------------
+
+  /** Launch an XMLReader's parsing operation, feeding events to this
+   * IncrementalSAXSource. In some implementations, this may launch a
+   * thread which runs the previously supplied XMLReader's parse() operation.
+   * In others, it may do other forms of initialization.
+   *
+   * @throws SAXException is parse thread is already in progress
+   * or parsing can not be started.
+   * */
+  public void startParse(InputSource source) throws SAXException;
+    
+} // class IncrementalSAXSource
diff --git a/src/main/java/org/apache/xml/dtm/ref/IncrementalSAXSource_Filter.java b/src/main/java/org/apache/xml/dtm/ref/IncrementalSAXSource_Filter.java
new file mode 100644
index 0000000..7192094
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/IncrementalSAXSource_Filter.java
@@ -0,0 +1,807 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: IncrementalSAXSource_Filter.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+
+package org.apache.xml.dtm.ref;
+
+import java.io.IOException;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+import org.apache.xml.utils.ThreadControllerWrapper;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+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.ext.LexicalHandler;
+
+/** <p>IncrementalSAXSource_Filter implements IncrementalSAXSource, using a
+ * standard SAX2 event source as its input and parcelling out those
+ * events gradually in reponse to deliverMoreNodes() requests.  Output from the
+ * filter will be passed along to a SAX handler registered as our
+ * listener, but those callbacks will pass through a counting stage
+ * which periodically yields control back to the controller coroutine.
+ * </p>
+ *
+ * <p>%REVIEW%: This filter is not currenly intended to be reusable
+ * for parsing additional streams/documents. We may want to consider
+ * making it resettable at some point in the future. But it's a 
+ * small object, so that'd be mostly a convenience issue; the cost
+ * of allocating each time is trivial compared to the cost of processing
+ * any nontrival stream.</p>
+ *
+ * <p>For a brief usage example, see the unit-test main() method.</p>
+ *
+ * <p>This is a simplification of the old CoroutineSAXParser, focusing
+ * specifically on filtering. The resulting controller protocol is _far_
+ * simpler and less error-prone; the only controller operation is deliverMoreNodes(),
+ * and the only requirement is that deliverMoreNodes(false) be called if you want to
+ * discard the rest of the stream and the previous deliverMoreNodes() didn't return
+ * false.
+ * */
+public class IncrementalSAXSource_Filter
+implements IncrementalSAXSource, ContentHandler, DTDHandler, LexicalHandler, ErrorHandler, Runnable
+{
+  boolean DEBUG=false; //Internal status report
+
+  //
+  // Data
+  //
+  private CoroutineManager fCoroutineManager = null;
+  private int fControllerCoroutineID = -1;
+  private int fSourceCoroutineID = -1;
+
+  private ContentHandler clientContentHandler=null; // %REVIEW% support multiple?
+  private LexicalHandler clientLexicalHandler=null; // %REVIEW% support multiple?
+  private DTDHandler clientDTDHandler=null; // %REVIEW% support multiple?
+  private ErrorHandler clientErrorHandler=null; // %REVIEW% support multiple?
+  private int eventcounter;
+  private int frequency=5;
+
+  // Flag indicating that no more events should be delivered -- either
+  // because input stream ran to completion (endDocument), or because
+  // the user requested an early stop via deliverMoreNodes(false).
+  private boolean fNoMoreEvents=false;
+
+  // Support for startParse()
+  private XMLReader fXMLReader=null;
+  private InputSource fXMLReaderInputSource=null;
+
+  //
+  // Constructors
+  //
+
+  public IncrementalSAXSource_Filter() {
+    this.init( new CoroutineManager(), -1, -1);
+  }
+  
+  /** Create a IncrementalSAXSource_Filter which is not yet bound to a specific
+   * SAX event source.
+   * */
+  public IncrementalSAXSource_Filter(CoroutineManager co, int controllerCoroutineID)
+  {
+    this.init( co, controllerCoroutineID, -1 );
+  }
+
+  //
+  // Factories
+  //
+  static public IncrementalSAXSource createIncrementalSAXSource(CoroutineManager co, int controllerCoroutineID) {
+    return new IncrementalSAXSource_Filter(co, controllerCoroutineID);
+  }
+
+  //
+  // Public methods
+  //
+
+  public void init( CoroutineManager co, int controllerCoroutineID,
+                    int sourceCoroutineID)
+  {
+    if(co==null)
+      co = new CoroutineManager();
+    fCoroutineManager = co;
+    fControllerCoroutineID = co.co_joinCoroutineSet(controllerCoroutineID);
+    fSourceCoroutineID = co.co_joinCoroutineSet(sourceCoroutineID);
+    if (fControllerCoroutineID == -1 || fSourceCoroutineID == -1)
+      throw new RuntimeException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COJOINROUTINESET_FAILED, null)); //"co_joinCoroutineSet() failed");
+
+    fNoMoreEvents=false;
+    eventcounter=frequency;
+  }
+    
+  /** Bind our input streams to an XMLReader.
+   *
+   * Just a convenience routine; obviously you can explicitly register
+   * this as a listener with the same effect.
+   * */
+  public void setXMLReader(XMLReader eventsource)
+  {
+    fXMLReader=eventsource;
+    eventsource.setContentHandler(this);
+    eventsource.setDTDHandler(this);
+    eventsource.setErrorHandler(this); // to report fatal errors in filtering mode
+
+    // Not supported by all SAX2 filters:
+    try 
+    {
+      eventsource.
+        setProperty("http://xml.org/sax/properties/lexical-handler",
+                    this);
+    }
+    catch(SAXNotRecognizedException e)
+    {
+      // Nothing we can do about it
+    }
+    catch(SAXNotSupportedException e)
+    {
+      // Nothing we can do about it
+    }
+
+    // Should we also bind as other varieties of handler?
+    // (DTDHandler and so on)
+  }
+
+  // Register a content handler for us to output to
+  public void setContentHandler(ContentHandler handler)
+  {
+    clientContentHandler=handler;
+  }
+  // Register a DTD handler for us to output to
+  public void setDTDHandler(DTDHandler handler)
+  {
+    clientDTDHandler=handler;
+  }
+  // Register a lexical handler for us to output to
+  // Not all filters support this...
+  // ??? Should we register directly on the filter?
+  // NOTE NAME -- subclassing issue in the Xerces version
+  public void setLexicalHandler(LexicalHandler handler)
+  {
+    clientLexicalHandler=handler;
+  }
+  // Register an error handler for us to output to
+  // NOTE NAME -- subclassing issue in the Xerces version
+  public void setErrHandler(ErrorHandler handler)
+  {
+    clientErrorHandler=handler;
+  }
+
+  // Set the number of events between resumes of our coroutine
+  // Immediately resets number of events before _next_ resume as well.
+  public void setReturnFrequency(int events)
+  {
+    if(events<1) events=1;
+    frequency=eventcounter=events;
+  }
+  
+  //
+  // ContentHandler methods
+  // These  pass the data to our client ContentHandler...
+  // but they also count the number of events passing through,
+  // and resume our coroutine each time that counter hits zero and
+  // is reset.
+  //
+  // Note that for everything except endDocument and fatalError, we do the count-and-yield
+  // BEFORE passing the call along. I'm hoping that this will encourage JIT
+  // compilers to realize that these are tail-calls, reducing the expense of
+  // the additional layer of data flow.
+  //
+  // %REVIEW% Glenn suggests that pausing after endElement, endDocument,
+  // and characters may be sufficient. I actually may not want to
+  // stop after characters, since in our application these wind up being
+  // concatenated before they're processed... but that risks huge blocks of
+  // text causing greater than usual readahead. (Unlikely? Consider the
+  // possibility of a large base-64 block in a SOAP stream.)
+  //
+  public void characters(char[] ch, int start, int length)
+       throws org.xml.sax.SAXException
+  {
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.characters(ch,start,length);
+  }
+  public void endDocument() 
+       throws org.xml.sax.SAXException
+  {
+    // EXCEPTION: In this case we need to run the event BEFORE we yield.
+    if(clientContentHandler!=null)
+      clientContentHandler.endDocument();
+
+    eventcounter=0;     
+    co_yield(false);
+  }
+  public void endElement(java.lang.String namespaceURI, java.lang.String localName,
+      java.lang.String qName) 
+       throws org.xml.sax.SAXException
+  {
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.endElement(namespaceURI,localName,qName);
+  }
+  public void endPrefixMapping(java.lang.String prefix) 
+       throws org.xml.sax.SAXException
+  {
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.endPrefixMapping(prefix);
+  }
+  public void ignorableWhitespace(char[] ch, int start, int length) 
+       throws org.xml.sax.SAXException
+  {
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.ignorableWhitespace(ch,start,length);
+  }
+  public void processingInstruction(java.lang.String target, java.lang.String data) 
+       throws org.xml.sax.SAXException
+  {
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.processingInstruction(target,data);
+  }
+  public void setDocumentLocator(Locator locator) 
+  {
+    if(--eventcounter<=0)
+      {
+        // This can cause a hang.  -sb
+        // co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.setDocumentLocator(locator);
+  }
+  public void skippedEntity(java.lang.String name) 
+       throws org.xml.sax.SAXException
+  {
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.skippedEntity(name);
+  }
+  public void startDocument() 
+       throws org.xml.sax.SAXException
+  {
+    co_entry_pause();
+
+    // Otherwise, begin normal event delivery
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.startDocument();
+  }
+  public void startElement(java.lang.String namespaceURI, java.lang.String localName,
+      java.lang.String qName, Attributes atts) 
+       throws org.xml.sax.SAXException
+  {
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.startElement(namespaceURI, localName, qName, atts);
+  }
+  public void startPrefixMapping(java.lang.String prefix, java.lang.String uri) 
+       throws org.xml.sax.SAXException
+  {
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+    if(clientContentHandler!=null)
+      clientContentHandler.startPrefixMapping(prefix,uri);
+  }
+
+  //
+  // LexicalHandler support. Not all SAX2 filters support these events
+  // but we may want to pass them through when they exist...
+  //
+  // %REVIEW% These do NOT currently affect the eventcounter; I'm asserting
+  // that they're rare enough that it makes little or no sense to
+  // pause after them. As such, it may make more sense for folks who
+  // actually want to use them to register directly with the filter.
+  // But I want 'em here for now, to remind us to recheck this assertion!
+  //
+  public void comment(char[] ch, int start, int length) 
+       throws org.xml.sax.SAXException
+  {
+    if(null!=clientLexicalHandler)
+      clientLexicalHandler.comment(ch,start,length);
+  }
+  public void endCDATA() 
+       throws org.xml.sax.SAXException
+  {
+    if(null!=clientLexicalHandler)
+      clientLexicalHandler.endCDATA();
+  }
+  public void endDTD() 
+       throws org.xml.sax.SAXException
+  {
+    if(null!=clientLexicalHandler)
+      clientLexicalHandler.endDTD();
+  }
+  public void endEntity(java.lang.String name) 
+       throws org.xml.sax.SAXException
+  {
+    if(null!=clientLexicalHandler)
+      clientLexicalHandler.endEntity(name);
+  }
+  public void startCDATA() 
+       throws org.xml.sax.SAXException
+  {
+    if(null!=clientLexicalHandler)
+      clientLexicalHandler.startCDATA();
+  }
+  public void startDTD(java.lang.String name, java.lang.String publicId,
+      java.lang.String systemId) 
+       throws org.xml.sax.SAXException
+  {
+    if(null!=clientLexicalHandler)
+      clientLexicalHandler. startDTD(name, publicId, systemId);
+  }
+  public void startEntity(java.lang.String name) 
+       throws org.xml.sax.SAXException
+  {
+    if(null!=clientLexicalHandler)
+      clientLexicalHandler.startEntity(name);
+  }
+
+  //
+  // DTDHandler support.
+  
+  public void notationDecl(String a, String b, String c) throws SAXException
+  {
+  	if(null!=clientDTDHandler)
+	  	clientDTDHandler.notationDecl(a,b,c);
+  }
+  public void unparsedEntityDecl(String a, String b, String c, String d)  throws SAXException
+  {
+  	if(null!=clientDTDHandler)
+	  	clientDTDHandler.unparsedEntityDecl(a,b,c,d);
+  }
+  
+  //
+  // ErrorHandler support.
+  //
+  // PROBLEM: Xerces is apparently _not_ calling the ErrorHandler for
+  // exceptions thrown by the ContentHandler, which prevents us from
+  // handling this properly when running in filtering mode with Xerces
+  // as our event source.  It's unclear whether this is a Xerces bug
+  // or a SAX design flaw.
+  // 
+  // %REVIEW% Current solution: In filtering mode, it is REQUIRED that
+  // event source make sure this method is invoked if the event stream
+  // abends before endDocument is delivered. If that means explicitly calling
+  // us in the exception handling code because it won't be delivered as part
+  // of the normal SAX ErrorHandler stream, that's fine; Not Our Problem.
+  //
+  public void error(SAXParseException exception) throws SAXException
+  {
+    if(null!=clientErrorHandler)
+      clientErrorHandler.error(exception);
+  }
+  
+  public void fatalError(SAXParseException exception) throws SAXException
+  {
+    // EXCEPTION: In this case we need to run the event BEFORE we yield --
+    // just as with endDocument, this terminates the event stream.
+    if(null!=clientErrorHandler)
+      clientErrorHandler.error(exception);
+
+    eventcounter=0;     
+    co_yield(false);
+
+  }
+  
+  public void warning(SAXParseException exception) throws SAXException
+  {
+    if(null!=clientErrorHandler)
+      clientErrorHandler.error(exception);
+  }
+  
+
+  //
+  // coroutine support
+  //
+
+  public int getSourceCoroutineID() {
+    return fSourceCoroutineID;
+  }
+  public int getControllerCoroutineID() {
+    return fControllerCoroutineID;
+  }
+
+  /** @return the CoroutineManager this CoroutineFilter object is bound to.
+   * If you're using the do...() methods, applications should only
+   * need to talk to the CoroutineManager once, to obtain the
+   * application's Coroutine ID.
+   * */
+  public CoroutineManager getCoroutineManager()
+  {
+    return fCoroutineManager;
+  }
+
+  /** <p>In the SAX delegation code, I've inlined the count-down in
+   * the hope of encouraging compilers to deliver better
+   * performance. However, if we subclass (eg to directly connect the
+   * output to a DTM builder), that would require calling super in
+   * order to run that logic... which seems inelegant.  Hence this
+   * routine for the convenience of subclasses: every [frequency]
+   * invocations, issue a co_yield.</p>
+   *
+   * @param moreExepected Should always be true unless this is being called
+   * at the end of endDocument() handling.
+   * */
+  protected void count_and_yield(boolean moreExpected) throws SAXException
+  {
+    if(!moreExpected) eventcounter=0;
+    
+    if(--eventcounter<=0)
+      {
+        co_yield(true);
+        eventcounter=frequency;
+      }
+  }
+
+  /**
+   * co_entry_pause is called in startDocument() before anything else
+   * happens. It causes the filter to wait for a "go ahead" request
+   * from the controller before delivering any events. Note that
+   * the very first thing the controller tells us may be "I don't
+   * need events after all"!
+   */
+  private void co_entry_pause() throws SAXException
+  {
+    if(fCoroutineManager==null)
+    {
+      // Nobody called init()? Do it now...
+      init(null,-1,-1);
+    }
+
+    try
+    {
+      Object arg=fCoroutineManager.co_entry_pause(fSourceCoroutineID);
+      if(arg==Boolean.FALSE)
+        co_yield(false);
+    }
+    catch(NoSuchMethodException e)
+    {
+      // Coroutine system says we haven't registered. That's an
+      // application coding error, and is unrecoverable.
+      if(DEBUG) e.printStackTrace();
+      throw new SAXException(e);
+    }
+  }
+
+  /**
+   * Co_Yield handles coroutine interactions while a parse is in progress.
+   *
+   * When moreRemains==true, we are pausing after delivering events, to
+   * ask if more are needed. We will resume the controller thread with 
+   *   co_resume(Boolean.TRUE, ...)
+   * When control is passed back it may indicate
+   *      Boolean.TRUE    indication to continue delivering events
+   *      Boolean.FALSE   indication to discontinue events and shut down.
+   * 
+   * When moreRemains==false, we shut down immediately without asking the
+   * controller's permission. Normally this means end of document has been
+   * reached.
+   *
+   * Shutting down a IncrementalSAXSource_Filter requires terminating the incoming
+   * SAX event stream. If we are in control of that stream (if it came
+   * from an XMLReader passed to our startReader() method), we can do so
+   * very quickly by throwing a reserved exception to it. If the stream is
+   * coming from another source, we can't do that because its caller may
+   * not be prepared for this "normal abnormal exit", and instead we put
+   * ourselves in a "spin" mode where events are discarded.
+   */
+  private void co_yield(boolean moreRemains) throws SAXException
+  {
+    // Horrendous kluge to run filter to completion. See below.
+    if(fNoMoreEvents)
+      return;
+
+    try // Coroutine manager might throw no-such.
+    {
+      Object arg=Boolean.FALSE;
+      if(moreRemains)
+      {
+        // Yield control, resume parsing when done
+        arg = fCoroutineManager.co_resume(Boolean.TRUE, fSourceCoroutineID,
+                                          fControllerCoroutineID);
+        
+      }
+
+      // If we're at end of document or were told to stop early
+      if(arg==Boolean.FALSE)
+      {
+        fNoMoreEvents=true;
+        
+        if(fXMLReader!=null)    // Running under startParseThread()
+          throw new StopException(); // We'll co_exit from there.
+        
+        // Yield control. We do NOT expect anyone to ever ask us again.
+        fCoroutineManager.co_exit_to(Boolean.FALSE, fSourceCoroutineID,
+                                     fControllerCoroutineID);
+      }
+    }
+    catch(NoSuchMethodException e)
+    {
+      // Shouldn't happen unless we've miscoded our coroutine logic
+      // "Shut down the garbage smashers on the detention level!"
+      fNoMoreEvents=true;
+      fCoroutineManager.co_exit(fSourceCoroutineID);
+      throw new SAXException(e);
+    }
+  }
+
+  //
+  // Convenience: Run an XMLReader in a thread
+  //
+
+  /** Launch a thread that will run an XMLReader's parse() operation within
+   *  a thread, feeding events to this IncrementalSAXSource_Filter. Mostly a convenience
+   *  routine, but has the advantage that -- since we invoked parse() --
+   *  we can halt parsing quickly via a StopException rather than waiting
+   *  for the SAX stream to end by itself.
+   *
+   * @throws SAXException is parse thread is already in progress
+   * or parsing can not be started.
+   * */
+  public void startParse(InputSource source) throws SAXException
+  {
+    if(fNoMoreEvents)
+      throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_INCRSAXSRCFILTER_NOT_RESTARTABLE, null)); //"IncrmentalSAXSource_Filter not currently restartable.");
+    if(fXMLReader==null)
+      throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_XMLRDR_NOT_BEFORE_STARTPARSE, null)); //"XMLReader not before startParse request");
+
+    fXMLReaderInputSource=source;
+    
+    // Xalan thread pooling...
+    // org.apache.xalan.transformer.TransformerImpl.runTransformThread(this);
+    ThreadControllerWrapper.runThread(this, -1);
+  }
+  
+  /* Thread logic to support startParseThread()
+   */
+  public void run()
+  {
+    // Guard against direct invocation of start().
+    if(fXMLReader==null) return;
+
+    if(DEBUG)System.out.println("IncrementalSAXSource_Filter parse thread launched");
+
+    // Initially assume we'll run successfully.
+    Object arg=Boolean.FALSE;
+
+    // For the duration of this operation, all coroutine handshaking
+    // will occur in the co_yield method. That's the nice thing about
+    // coroutines; they give us a way to hand off control from the
+    // middle of a synchronous method.
+    try
+    {
+      fXMLReader.parse(fXMLReaderInputSource);
+    }
+    catch(IOException ex)
+    {
+      arg=ex;
+    }
+    catch(StopException ex)
+    {
+      // Expected and harmless
+      if(DEBUG)System.out.println("Active IncrementalSAXSource_Filter normal stop exception");
+    }
+    catch (SAXException ex)
+    {
+      Exception inner=ex.getException();
+      if(inner instanceof StopException){
+        // Expected and harmless
+        if(DEBUG)System.out.println("Active IncrementalSAXSource_Filter normal stop exception");
+      }
+      else
+      {
+        // Unexpected malfunction
+        if(DEBUG)
+        {
+          System.out.println("Active IncrementalSAXSource_Filter UNEXPECTED SAX exception: "+inner);
+          inner.printStackTrace();
+        }                
+        arg=ex;
+      }
+    } // end parse
+    
+    // Mark as no longer running in thread.
+    fXMLReader=null;
+
+    try
+    {
+      // Mark as done and yield control to the controller coroutine
+      fNoMoreEvents=true;
+      fCoroutineManager.co_exit_to(arg, fSourceCoroutineID,
+                                   fControllerCoroutineID);
+    }
+    catch(java.lang.NoSuchMethodException e)
+    {
+      // Shouldn't happen unless we've miscoded our coroutine logic
+      // "CPO, shut down the garbage smashers on the detention level!"
+      e.printStackTrace(System.err);
+      fCoroutineManager.co_exit(fSourceCoroutineID);
+    }
+  }
+
+  /** Used to quickly terminate parse when running under a
+      startParse() thread. Only its type is important. */
+  class StopException extends RuntimeException
+  {
+          static final long serialVersionUID = -1129245796185754956L;
+  }
+
+  /** deliverMoreNodes() is a simple API which tells the coroutine
+   * parser that we need more nodes.  This is intended to be called
+   * from one of our partner routines, and serves to encapsulate the
+   * details of how incremental parsing has been achieved.
+   *
+   * @param parsemore If true, tells the incremental filter to generate
+   * another chunk of output. If false, tells the filter that we're
+   * satisfied and it can terminate parsing of this document.
+   *
+   * @return Boolean.TRUE if there may be more events available by invoking
+   * deliverMoreNodes() again. Boolean.FALSE if parsing has run to completion (or been
+   * terminated by deliverMoreNodes(false). Or an exception object if something
+   * malfunctioned. %REVIEW% We _could_ actually throw the exception, but
+   * that would require runinng deliverMoreNodes() in a try/catch... and for many
+   * applications, exception will be simply be treated as "not TRUE" in
+   * any case.
+   * */
+  public Object deliverMoreNodes(boolean parsemore)
+  {
+    // If parsing is already done, we can immediately say so
+    if(fNoMoreEvents)
+      return Boolean.FALSE;
+
+    try 
+    {
+      Object result =
+        fCoroutineManager.co_resume(parsemore?Boolean.TRUE:Boolean.FALSE,
+                                    fControllerCoroutineID, fSourceCoroutineID);
+      if(result==Boolean.FALSE)
+        fCoroutineManager.co_exit(fControllerCoroutineID);
+
+      return result;
+    }
+      
+    // SHOULD NEVER OCCUR, since the coroutine number and coroutine manager
+    // are those previously established for this IncrementalSAXSource_Filter...
+    // So I'm just going to return it as a parsing exception, for now.
+    catch(NoSuchMethodException e)
+      {
+        return e;
+      }
+  }
+  
+
+  //================================================================
+  /** Simple unit test. Attempt coroutine parsing of document indicated
+   * by first argument (as a URI), report progress.
+   */
+    /*
+  public static void main(String args[])
+  {
+    System.out.println("Starting...");
+
+    org.xml.sax.XMLReader theSAXParser=
+      new org.apache.xerces.parsers.SAXParser();
+  
+
+    for(int arg=0;arg<args.length;++arg)
+    {
+      // The filter is not currently designed to be restartable
+      // after a parse has ended. Generate a new one each time.
+      IncrementalSAXSource_Filter filter=
+        new IncrementalSAXSource_Filter();
+      // Use a serializer as our sample output
+      org.apache.xml.serialize.XMLSerializer trace;
+      trace=new org.apache.xml.serialize.XMLSerializer(System.out,null);
+      filter.setContentHandler(trace);
+      filter.setLexicalHandler(trace);
+
+      try
+      {
+        InputSource source = new InputSource(args[arg]);
+        Object result=null;
+        boolean more=true;
+
+        // init not issued; we _should_ automagically Do The Right Thing
+
+        // Bind parser, kick off parsing in a thread
+        filter.setXMLReader(theSAXParser);
+        filter.startParse(source);
+      
+        for(result = filter.deliverMoreNodes(more);
+            (result instanceof Boolean && ((Boolean)result)==Boolean.TRUE);
+            result = filter.deliverMoreNodes(more))
+        {
+          System.out.println("\nSome parsing successful, trying more.\n");
+          
+          // Special test: Terminate parsing early.
+          if(arg+1<args.length && "!".equals(args[arg+1]))
+          {
+            ++arg;
+            more=false;
+          }
+          
+        }
+      
+        if (result instanceof Boolean && ((Boolean)result)==Boolean.FALSE)
+        {
+          System.out.println("\nFilter ended (EOF or on request).\n");
+        }
+        else if (result == null) {
+          System.out.println("\nUNEXPECTED: Filter says shut down prematurely.\n");
+        }
+        else if (result instanceof Exception) {
+          System.out.println("\nFilter threw exception:");
+          ((Exception)result).printStackTrace();
+        }
+      
+      }
+      catch(SAXException e)
+      {
+        e.printStackTrace();
+      }
+    } // end for
+  }
+    */  
+} // class IncrementalSAXSource_Filter
diff --git a/src/main/java/org/apache/xml/dtm/ref/NodeLocator.java b/src/main/java/org/apache/xml/dtm/ref/NodeLocator.java
new file mode 100644
index 0000000..c5fd89b
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/NodeLocator.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeLocator.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+
+package org.apache.xml.dtm.ref;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * <code>NodeLocator</code> maintains information on an XML source
+ * node.
+ *
+ * @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
+ * @since May 23, 2001
+ */
+public class NodeLocator implements SourceLocator
+{
+  protected String m_publicId;
+  protected String m_systemId;
+  protected int m_lineNumber;
+  protected int m_columnNumber;
+
+  /**
+   * Creates a new <code>NodeLocator</code> instance.
+   *
+   * @param publicId a <code>String</code> value
+   * @param systemId a <code>String</code> value
+   * @param lineNumber an <code>int</code> value
+   * @param columnNumber an <code>int</code> value
+   */
+  public NodeLocator(String publicId, String systemId,
+                     int lineNumber, int columnNumber)
+  {
+    this.m_publicId = publicId;
+    this.m_systemId = systemId;
+    this.m_lineNumber = lineNumber;
+    this.m_columnNumber = columnNumber;
+  }
+
+  /**
+   * <code>getPublicId</code> returns the public ID of the node.
+   *
+   * @return a <code>String</code> value
+   */
+  public String getPublicId()
+  {
+    return m_publicId;
+  }
+
+  /**
+   * <code>getSystemId</code> returns the system ID of the node.
+   *
+   * @return a <code>String</code> value
+   */
+  public String getSystemId()
+  {
+    return m_systemId;
+  }
+
+  /**
+   * <code>getLineNumber</code> returns the line number of the node.
+   *
+   * @return an <code>int</code> value
+   */
+  public int getLineNumber()
+  {
+    return m_lineNumber;
+  }
+
+  /**
+   * <code>getColumnNumber</code> returns the column number of the
+   * node.
+   *
+   * @return an <code>int</code> value
+   */
+  public int getColumnNumber()
+  {
+    return m_columnNumber;
+  }
+
+  /**
+   * <code>toString</code> returns a string representation of this
+   * NodeLocator instance.
+   *
+   * @return a <code>String</code> value
+   */
+  public String toString()
+  {
+    return "file '" + m_systemId
+      + "', line #" + m_lineNumber
+      + ", column #" + m_columnNumber;
+  }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/SecuritySupport.java b/src/main/java/org/apache/xml/dtm/ref/SecuritySupport.java
new file mode 100755
index 0000000..0dd7ac4
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/SecuritySupport.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SecuritySupport.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+
+package org.apache.xml.dtm.ref;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Base class with security related methods that work on JDK 1.1.
+ */
+class SecuritySupport {
+
+    /*
+     * Make this of type Object so that the verifier won't try to
+     * prove its type, thus possibly trying to load the SecuritySupport12
+     * class.
+     */
+    private static final Object securitySupport;
+
+    static {
+	SecuritySupport ss = null;
+	try {
+	    Class c = Class.forName("java.security.AccessController");
+	    // if that worked, we're on 1.2.
+	    /*
+	    // don't reference the class explicitly so it doesn't
+	    // get dragged in accidentally.
+	    c = Class.forName("javax.mail.SecuritySupport12");
+	    Constructor cons = c.getConstructor(new Class[] { });
+	    ss = (SecuritySupport)cons.newInstance(new Object[] { });
+	    */
+	    /*
+	     * Unfortunately, we can't load the class using reflection
+	     * because the class is package private.  And the class has
+	     * to be package private so the APIs aren't exposed to other
+	     * code that could use them to circumvent security.  Thus,
+	     * we accept the risk that the direct reference might fail
+	     * on some JDK 1.1 JVMs, even though we would never execute
+	     * this code in such a case.  Sigh...
+	     */
+	    ss = new SecuritySupport12();
+	} catch (Exception ex) {
+	    // ignore it
+	} finally {
+	    if (ss == null)
+		ss = new SecuritySupport();
+	    securitySupport = ss;
+	}
+    }
+
+    /**
+     * Return an appropriate instance of this class, depending on whether
+     * we're on a JDK 1.1 or J2SE 1.2 (or later) system.
+     */
+    static SecuritySupport getInstance() {
+	return (SecuritySupport)securitySupport;
+    }
+
+    ClassLoader getContextClassLoader() {
+	return null;
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return null;
+    }
+
+    ClassLoader getParentClassLoader(ClassLoader cl) {
+        return null;
+    }
+
+    String getSystemProperty(String propName) {
+        return System.getProperty(propName);
+    }
+
+    FileInputStream getFileInputStream(File file)
+        throws FileNotFoundException
+    {
+        return new FileInputStream(file);
+    }
+
+    InputStream getResourceAsStream(ClassLoader cl, String name) {
+        InputStream ris;
+        if (cl == null) {
+            ris = ClassLoader.getSystemResourceAsStream(name);
+        } else {
+            ris = cl.getResourceAsStream(name);
+        }
+        return ris;
+    }
+    
+    boolean getFileExists(File f) {
+        return f.exists();
+    }
+    
+    long getLastModified(File f) {
+        return f.lastModified();
+    }    
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/SecuritySupport12.java b/src/main/java/org/apache/xml/dtm/ref/SecuritySupport12.java
new file mode 100755
index 0000000..e70c91b
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/SecuritySupport12.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SecuritySupport12.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+
+package org.apache.xml.dtm.ref;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Security related methods that only work on J2SE 1.2 and newer.
+ */
+class SecuritySupport12 extends SecuritySupport {
+
+    ClassLoader getContextClassLoader() {
+        return (ClassLoader)
+                AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                ClassLoader cl = null;
+                try {
+                    cl = Thread.currentThread().getContextClassLoader();
+                } catch (SecurityException ex) { }
+                return cl;
+            }
+        });
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader cl = null;
+                    try {
+                        cl = ClassLoader.getSystemClassLoader();
+                    } catch (SecurityException ex) {}
+                    return cl;
+                }
+            });
+    }
+
+    ClassLoader getParentClassLoader(final ClassLoader cl) {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader parent = null;
+                    try {
+                        parent = cl.getParent();
+                    } catch (SecurityException ex) {}
+
+                    // eliminate loops in case of the boot
+                    // ClassLoader returning itself as a parent
+                    return (parent == cl) ? null : parent;
+                }
+            });
+    }
+
+    String getSystemProperty(final String propName) {
+        return (String)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return System.getProperty(propName);
+                }
+            });
+    }
+
+    FileInputStream getFileInputStream(final File file)
+        throws FileNotFoundException
+    {
+        try {
+            return (FileInputStream)
+                AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    public Object run() throws FileNotFoundException {
+                        return new FileInputStream(file);
+                    }
+                });
+        } catch (PrivilegedActionException e) {
+            throw (FileNotFoundException)e.getException();
+        }
+    }
+
+    InputStream getResourceAsStream(final ClassLoader cl,
+                                           final String name)
+    {
+        return (InputStream)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    InputStream ris;
+                    if (cl == null) {
+                        ris = ClassLoader.getSystemResourceAsStream(name);
+                    } else {
+                        ris = cl.getResourceAsStream(name);
+                    }
+                    return ris;
+                }
+            });
+    }
+    
+    boolean getFileExists(final File f) {
+    return ((Boolean)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Boolean(f.exists());
+                }
+            })).booleanValue();
+    }
+    
+    long getLastModified(final File f) {
+    return ((Long)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Long(f.lastModified());
+                }
+            })).longValue();
+    }
+        
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/dom2dtm/DOM2DTM.java b/src/main/java/org/apache/xml/dtm/ref/dom2dtm/DOM2DTM.java
new file mode 100644
index 0000000..2c376a0
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/dom2dtm/DOM2DTM.java
@@ -0,0 +1,1766 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOM2DTM.java 478671 2006-11-23 21:00:31Z minchau $
+ */
+package org.apache.xml.dtm.ref.dom2dtm;
+
+import java.util.Vector;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.dom.DOMSource;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.dtm.DTMWSFilter;
+import org.apache.xml.dtm.ref.DTMDefaultBaseIterators;
+import org.apache.xml.dtm.ref.DTMManagerDefault;
+import org.apache.xml.dtm.ref.ExpandedNameTable;
+import org.apache.xml.dtm.ref.IncrementalSAXSource;
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.StringBufferPool;
+import org.apache.xml.utils.TreeWalker;
+import org.apache.xml.utils.XMLCharacterRecognizer;
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Entity;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.ContentHandler;
+
+/** The <code>DOM2DTM</code> class serves up a DOM's contents via the
+ * DTM API.
+ *
+ * Note that it doesn't necessarily represent a full Document
+ * tree. You can wrap a DOM2DTM around a specific node and its subtree
+ * and the right things should happen. (I don't _think_ we currently
+ * support DocumentFrgment nodes as roots, though that might be worth
+ * considering.)
+ *
+ * Note too that we do not currently attempt to track document
+ * mutation. If you alter the DOM after wrapping DOM2DTM around it,
+ * all bets are off.
+ * */
+public class DOM2DTM extends DTMDefaultBaseIterators
+{
+  static final boolean JJK_DEBUG=false;
+  static final boolean JJK_NEWCODE=true;
+  
+  /** Manefest constant
+   */
+  static final String NAMESPACE_DECL_NS="http://www.w3.org/XML/1998/namespace";
+  
+  /** The current position in the DOM tree. Last node examined for
+   * possible copying to DTM. */
+  transient private Node m_pos;
+  /** The current position in the DTM tree. Who children get appended to. */
+  private int m_last_parent=0;
+  /** The current position in the DTM tree. Who children reference as their 
+   * previous sib. */
+  private int m_last_kid=NULL;
+
+  /** The top of the subtree.
+   * %REVIEW%: 'may not be the same as m_context if "//foo" pattern.'
+   * */
+  transient private Node m_root;
+
+  /** True iff the first element has been processed. This is used to control
+      synthesis of the implied xml: namespace declaration node. */
+  boolean m_processedFirstElement=false;
+        
+  /** true if ALL the nodes in the m_root subtree have been processed;
+   * false if our incremental build has not yet finished scanning the
+   * DOM tree.  */
+  transient private boolean m_nodesAreProcessed;
+
+  /** The node objects.  The instance part of the handle indexes
+   * directly into this vector.  Each DTM node may actually be
+   * composed of several DOM nodes (for example, if logically-adjacent
+   * Text/CDATASection nodes in the DOM have been coalesced into a
+   * single DTM Text node); this table points only to the first in
+   * that sequence. */
+  protected Vector m_nodes = new Vector();
+
+  /**
+   * Construct a DOM2DTM object from a DOM node.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param domSource the DOM source that this DTM will wrap.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may 
+   *                         be null.
+   * @param xstringfactory XMLString factory for creating character content.
+   * @param doIndexing true if the caller considers it worth it to use 
+   *                   indexing schemes.
+   */
+  public DOM2DTM(DTMManager mgr, DOMSource domSource, 
+                 int dtmIdentity, DTMWSFilter whiteSpaceFilter,
+                 XMLStringFactory xstringfactory,
+                 boolean doIndexing)
+  {
+    super(mgr, domSource, dtmIdentity, whiteSpaceFilter, 
+          xstringfactory, doIndexing);
+
+    // Initialize DOM navigation
+    m_pos=m_root = domSource.getNode();
+    // Initialize DTM navigation
+    m_last_parent=m_last_kid=NULL;
+    m_last_kid=addNode(m_root, m_last_parent,m_last_kid, NULL);
+
+    // Apparently the domSource root may not actually be the
+    // Document node. If it's an Element node, we need to immediately
+    // add its attributes. Adapted from nextNode().
+    // %REVIEW% Move this logic into addNode and recurse? Cleaner!
+    //
+    // (If it's an EntityReference node, we're probably in 
+    // seriously bad trouble. For now
+    // I'm just hoping nobody is ever quite that foolish... %REVIEW%)
+		//
+		// %ISSUE% What about inherited namespaces in this case?
+		// Do we need to special-case initialize them into the DTM model?
+    if(ELEMENT_NODE == m_root.getNodeType())
+    {
+      NamedNodeMap attrs=m_root.getAttributes();
+      int attrsize=(attrs==null) ? 0 : attrs.getLength();
+      if(attrsize>0)
+      {
+        int attrIndex=NULL; // start with no previous sib
+        for(int i=0;i<attrsize;++i)
+        {
+          // No need to force nodetype in this case;
+          // addNode() will take care of switching it from
+          // Attr to Namespace if necessary.
+          attrIndex=addNode(attrs.item(i),0,attrIndex,NULL);
+          m_firstch.setElementAt(DTM.NULL,attrIndex);
+        }
+        // Terminate list of attrs, and make sure they aren't
+        // considered children of the element
+        m_nextsib.setElementAt(DTM.NULL,attrIndex);
+
+        // IMPORTANT: This does NOT change m_last_parent or m_last_kid!
+      } // if attrs exist
+    } //if(ELEMENT_NODE)
+
+    // Initialize DTM-completed status 
+    m_nodesAreProcessed = false;
+  }
+
+  /**
+   * Construct the node map from the node.
+   *
+   * @param node The node that is to be added to the DTM.
+   * @param parentIndex The current parent index.
+   * @param previousSibling The previous sibling index.
+   * @param forceNodeType If not DTM.NULL, overrides the DOM node type.
+   *	Used to force nodes to Text rather than CDATASection when their
+   *	coalesced value includes ordinary Text nodes (current DTM behavior).
+   *
+   * @return The index identity of the node that was added.
+   */
+  protected int addNode(Node node, int parentIndex,
+                        int previousSibling, int forceNodeType)
+  {
+    int nodeIndex = m_nodes.size();
+
+    // Have we overflowed a DTM Identity's addressing range?
+    if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS))
+    {
+      try
+      {
+        if(m_mgr==null)
+          throw new ClassCastException();
+                                
+                                // Handle as Extended Addressing
+        DTMManagerDefault mgrD=(DTMManagerDefault)m_mgr;
+        int id=mgrD.getFirstFreeDTMID();
+        mgrD.addDTM(this,id,nodeIndex);
+        m_dtmIdent.addElement(id<<DTMManager.IDENT_DTM_NODE_BITS);
+      }
+      catch(ClassCastException e)
+      {
+        // %REVIEW% Wrong error message, but I've been told we're trying
+        // not to add messages right not for I18N reasons.
+        // %REVIEW% Should this be a Fatal Error?
+        error(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null));//"No more DTM IDs are available";
+      }
+    }
+
+    m_size++;
+    // ensureSize(nodeIndex);
+    
+    int type;
+    if(NULL==forceNodeType)
+        type = node.getNodeType();
+    else
+        type=forceNodeType;
+        
+    // %REVIEW% The Namespace Spec currently says that Namespaces are
+    // processed in a non-namespace-aware manner, by matching the
+    // QName, even though there is in fact a namespace assigned to
+    // these nodes in the DOM. If and when that changes, we will have
+    // to consider whether we check the namespace-for-namespaces
+    // rather than the node name.
+    //
+    // %TBD% Note that the DOM does not necessarily explicitly declare
+    // all the namespaces it uses. DOM Level 3 will introduce a
+    // namespace-normalization operation which reconciles that, and we
+    // can request that users invoke it or otherwise ensure that the
+    // tree is namespace-well-formed before passing the DOM to Xalan.
+    // But if they don't, what should we do about it? We probably
+    // don't want to alter the source DOM (and may not be able to do
+    // so if it's read-only). The best available answer might be to
+    // synthesize additional DTM Namespace Nodes that don't correspond
+    // to DOM Attr Nodes.
+    if (Node.ATTRIBUTE_NODE == type)
+    {
+      String name = node.getNodeName();
+
+      if (name.startsWith("xmlns:") || name.equals("xmlns"))
+      {
+        type = DTM.NAMESPACE_NODE;
+      }
+    }
+    
+    m_nodes.addElement(node);
+    
+    m_firstch.setElementAt(NOTPROCESSED,nodeIndex);
+    m_nextsib.setElementAt(NOTPROCESSED,nodeIndex);
+    m_prevsib.setElementAt(previousSibling,nodeIndex);
+    m_parent.setElementAt(parentIndex,nodeIndex);
+    
+    if(DTM.NULL != parentIndex && 
+       type != DTM.ATTRIBUTE_NODE && 
+       type != DTM.NAMESPACE_NODE)
+    {
+      // If the DTM parent had no children, this becomes its first child.
+      if(NOTPROCESSED == m_firstch.elementAt(parentIndex))
+        m_firstch.setElementAt(nodeIndex,parentIndex);
+    }
+    
+    String nsURI = node.getNamespaceURI();
+
+    // Deal with the difference between Namespace spec and XSLT
+    // definitions of local name. (The former says PIs don't have
+    // localnames; the latter says they do.)
+    String localName =  (type == Node.PROCESSING_INSTRUCTION_NODE) ? 
+                         node.getNodeName() :
+                         node.getLocalName();
+                         
+    // Hack to make DOM1 sort of work...
+    if(((type == Node.ELEMENT_NODE) || (type == Node.ATTRIBUTE_NODE)) 
+        && null == localName)
+      localName = node.getNodeName(); // -sb
+      
+    ExpandedNameTable exnt = m_expandedNameTable;
+
+    // %TBD% Nodes created with the old non-namespace-aware DOM
+    // calls createElement() and createAttribute() will never have a
+    // localname. That will cause their expandedNameID to be just the
+    // nodeType... which will keep them from being matched
+    // successfully by name. Since the DOM makes no promise that
+    // those will participate in namespace processing, this is
+    // officially accepted as Not Our Fault. But it might be nice to
+    // issue a diagnostic message!
+    if(node.getLocalName()==null &&
+       (type==Node.ELEMENT_NODE || type==Node.ATTRIBUTE_NODE))
+      {
+        // warning("DOM 'level 1' node "+node.getNodeName()+" won't be mapped properly in DOM2DTM.");
+      }
+    
+    int expandedNameID = (null != localName) 
+       ? exnt.getExpandedTypeID(nsURI, localName, type) :
+         exnt.getExpandedTypeID(type);
+
+    m_exptype.setElementAt(expandedNameID,nodeIndex);
+    
+    indexNode(expandedNameID, nodeIndex);
+
+    if (DTM.NULL != previousSibling)
+      m_nextsib.setElementAt(nodeIndex,previousSibling);
+
+    // This should be done after m_exptype has been set, and probably should
+    // always be the last thing we do
+    if (type == DTM.NAMESPACE_NODE)
+        declareNamespaceInContext(parentIndex,nodeIndex);
+
+    return nodeIndex;
+  }
+  
+  /**
+   * Get the number of nodes that have been added.
+   */
+  public int getNumberOfNodes()
+  {
+    return m_nodes.size();
+  }
+  
+ /**
+   * This method iterates to the next node that will be added to the table.
+   * Each call to this method adds a new node to the table, unless the end
+   * is reached, in which case it returns null.
+   *
+   * @return The true if a next node is found or false if 
+   *         there are no more nodes.
+   */
+  protected boolean nextNode()
+  {
+    // Non-recursive one-fetch-at-a-time depth-first traversal with 
+    // attribute/namespace nodes and white-space stripping.
+    // Navigating the DOM is simple, navigating the DTM is simple;
+    // keeping track of both at once is a trifle baroque but at least
+    // we've avoided most of the special cases.
+    if (m_nodesAreProcessed)
+      return false;
+        
+    // %REVIEW% Is this local copy Really Useful from a performance
+    // point of view?  Or is this a false microoptimization?
+    Node pos=m_pos; 
+    Node next=null;
+    int nexttype=NULL;
+
+    // Navigate DOM tree
+    do
+      {
+        // Look down to first child.
+        if (pos.hasChildNodes()) 
+          {
+            next = pos.getFirstChild();
+
+            // %REVIEW% There's probably a more elegant way to skip
+            // the doctype. (Just let it go and Suppress it?
+            if(next!=null && DOCUMENT_TYPE_NODE==next.getNodeType())
+              next=next.getNextSibling();
+
+            // Push DTM context -- except for children of Entity References, 
+            // which have no DTM equivalent and cause no DTM navigation.
+            if(ENTITY_REFERENCE_NODE!=pos.getNodeType())
+              {
+                m_last_parent=m_last_kid;
+                m_last_kid=NULL;
+                // Whitespace-handler context stacking
+                if(null != m_wsfilter)
+                {
+                  short wsv =
+                    m_wsfilter.getShouldStripSpace(makeNodeHandle(m_last_parent),this);
+                  boolean shouldStrip = (DTMWSFilter.INHERIT == wsv) 
+                    ? getShouldStripWhitespace() 
+                    : (DTMWSFilter.STRIP == wsv);
+                  pushShouldStripWhitespace(shouldStrip);
+                } // if(m_wsfilter)
+              }
+          }
+
+        // If that fails, look up and right (but not past root!)
+        else 
+          {
+            if(m_last_kid!=NULL)
+              {
+                // Last node posted at this level had no more children
+                // If it has _no_ children, we need to record that.
+                if(m_firstch.elementAt(m_last_kid)==NOTPROCESSED)
+                  m_firstch.setElementAt(NULL,m_last_kid);
+              }
+                        
+            while(m_last_parent != NULL)
+              {
+                // %REVIEW% There's probably a more elegant way to
+                // skip the doctype. (Just let it go and Suppress it?
+                next = pos.getNextSibling();
+                if(next!=null && DOCUMENT_TYPE_NODE==next.getNodeType())
+                  next=next.getNextSibling();
+
+                if(next!=null)
+                  break; // Found it!
+                
+                // No next-sibling found. Pop the DOM.
+                pos=pos.getParentNode();
+                if(pos==null)
+                  {
+                    // %TBD% Should never arise, but I want to be sure of that...
+                    if(JJK_DEBUG)
+                      {
+                        System.out.println("***** DOM2DTM Pop Control Flow problem");
+                        for(;;); // Freeze right here!
+                      }
+                  }
+                
+                // The only parents in the DTM are Elements.  However,
+                // the DOM could contain EntityReferences.  If we
+                // encounter one, pop it _without_ popping DTM.
+                if(pos!=null && ENTITY_REFERENCE_NODE == pos.getNodeType())
+                  {
+                    // Nothing needs doing
+                    if(JJK_DEBUG)
+                      System.out.println("***** DOM2DTM popping EntRef");
+                  }
+                else
+                  {
+                    popShouldStripWhitespace();
+                    // Fix and pop DTM
+                    if(m_last_kid==NULL)
+                      m_firstch.setElementAt(NULL,m_last_parent); // Popping from an element
+                    else
+                      m_nextsib.setElementAt(NULL,m_last_kid); // Popping from anything else
+                    m_last_parent=m_parent.elementAt(m_last_kid=m_last_parent);
+                  }
+              }
+            if(m_last_parent==NULL)
+              next=null;
+          }
+                
+        if(next!=null)
+          nexttype=next.getNodeType();
+                
+        // If it's an entity ref, advance past it.
+        //
+        // %REVIEW% Should we let this out the door and just suppress it?
+        // More work, but simpler code, more likely to be correct, and
+        // it doesn't happen very often. We'd get rid of the loop too.
+        if (ENTITY_REFERENCE_NODE == nexttype)
+          pos=next;
+      }
+    while (ENTITY_REFERENCE_NODE == nexttype); 
+        
+    // Did we run out of the tree?
+    if(next==null)
+      {
+        m_nextsib.setElementAt(NULL,0);
+        m_nodesAreProcessed = true;
+        m_pos=null;
+                
+        if(JJK_DEBUG)
+          {
+            System.out.println("***** DOM2DTM Crosscheck:");
+            for(int i=0;i<m_nodes.size();++i)
+              System.out.println(i+":\t"+m_firstch.elementAt(i)+"\t"+m_nextsib.elementAt(i));
+          }
+                
+        return false;
+      }
+
+    // Text needs some special handling:
+    //
+    // DTM may skip whitespace. This is handled by the suppressNode flag, which
+    // when true will keep the DTM node from being created.
+    //
+    // DTM only directly records the first DOM node of any logically-contiguous
+    // sequence. The lastTextNode value will be set to the last node in the 
+    // contiguous sequence, and -- AFTER the DTM addNode -- can be used to 
+    // advance next over this whole block. Should be simpler than special-casing
+    // the above loop for "Was the logically-preceeding sibling a text node".
+    // 
+    // Finally, a DTM node should be considered a CDATASection only if all the
+    // contiguous text it covers is CDATASections. The first Text should
+    // force DTM to Text.
+        
+    boolean suppressNode=false;
+    Node lastTextNode=null;
+
+    nexttype=next.getNodeType();
+        
+    // nexttype=pos.getNodeType();
+    if(TEXT_NODE == nexttype || CDATA_SECTION_NODE == nexttype)
+      {
+        // If filtering, initially assume we're going to suppress the node
+        suppressNode=((null != m_wsfilter) && getShouldStripWhitespace());
+
+        // Scan logically contiguous text (siblings, plus "flattening"
+        // of entity reference boundaries).
+        Node n=next;
+        while(n!=null)
+          {
+            lastTextNode=n;
+            // Any Text node means DTM considers it all Text
+            if(TEXT_NODE == n.getNodeType())
+              nexttype=TEXT_NODE;
+            // Any non-whitespace in this sequence blocks whitespace
+            // suppression
+            suppressNode &=
+              XMLCharacterRecognizer.isWhiteSpace(n.getNodeValue());
+                        
+            n=logicalNextDOMTextNode(n);
+          }
+      }
+        
+    // Special handling for PIs: Some DOMs represent the XML
+    // Declaration as a PI. This is officially incorrect, per the DOM
+    // spec, but is considered a "wrong but tolerable" temporary
+    // workaround pending proper handling of these fields in DOM Level
+    // 3. We want to recognize and reject that case.
+    else if(PROCESSING_INSTRUCTION_NODE==nexttype)
+      {
+        suppressNode = (pos.getNodeName().toLowerCase().equals("xml"));
+      }
+        
+        
+    if(!suppressNode)
+      {
+        // Inserting next. NOTE that we force the node type; for
+        // coalesced Text, this records CDATASections adjacent to
+        // ordinary Text as Text.
+        int nextindex=addNode(next,m_last_parent,m_last_kid,
+			      nexttype);
+	
+        m_last_kid=nextindex;
+
+        if(ELEMENT_NODE == nexttype)
+          {
+            int attrIndex=NULL; // start with no previous sib
+            // Process attributes _now_, rather than waiting.
+            // Simpler control flow, makes NS cache available immediately.
+            NamedNodeMap attrs=next.getAttributes();
+            int attrsize=(attrs==null) ? 0 : attrs.getLength();
+            if(attrsize>0)
+              {
+                for(int i=0;i<attrsize;++i)
+                  {
+                    // No need to force nodetype in this case;
+                    // addNode() will take care of switching it from
+                    // Attr to Namespace if necessary.
+                    attrIndex=addNode(attrs.item(i),
+                                      nextindex,attrIndex,NULL);
+                    m_firstch.setElementAt(DTM.NULL,attrIndex);
+
+                    // If the xml: prefix is explicitly declared
+                    // we don't need to synthesize one.
+		    //
+		    // NOTE that XML Namespaces were not originally
+		    // defined as being namespace-aware (grrr), and
+		    // while the W3C is planning to fix this it's
+		    // safer for now to test the QName and trust the
+		    // parsers to prevent anyone from redefining the
+		    // reserved xmlns: prefix
+                    if(!m_processedFirstElement
+                       && "xmlns:xml".equals(attrs.item(i).getNodeName()))
+                      m_processedFirstElement=true; 
+                  }
+                // Terminate list of attrs, and make sure they aren't
+                // considered children of the element
+              } // if attrs exist
+            if(!m_processedFirstElement)
+            {
+              // The DOM might not have an explicit declaration for the
+              // implicit "xml:" prefix, but the XPath data model
+              // requires that this appear as a Namespace Node so we
+              // have to synthesize one. You can think of this as
+              // being a default attribute defined by the XML
+              // Namespaces spec rather than by the DTD.
+              attrIndex=addNode(new DOM2DTMdefaultNamespaceDeclarationNode(
+																	(Element)next,"xml",NAMESPACE_DECL_NS,
+																	makeNodeHandle(((attrIndex==NULL)?nextindex:attrIndex)+1)
+																	),
+                                nextindex,attrIndex,NULL);      
+              m_firstch.setElementAt(DTM.NULL,attrIndex);
+              m_processedFirstElement=true;
+            }
+            if(attrIndex!=NULL)
+              m_nextsib.setElementAt(DTM.NULL,attrIndex);
+          } //if(ELEMENT_NODE)
+      } // (if !suppressNode)
+
+    // Text postprocessing: Act on values stored above
+    if(TEXT_NODE == nexttype || CDATA_SECTION_NODE == nexttype)
+      {
+        // %TBD% If nexttype was forced to TEXT, patch the DTM node
+                
+        next=lastTextNode;      // Advance the DOM cursor over contiguous text
+      }
+        
+    // Remember where we left off.
+    m_pos=next;
+    return true;
+  }  
+
+
+  /**
+   * Return an DOM node for the given node.
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A node representation of the DTM node.
+   */
+  public Node getNode(int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+
+    return (Node) m_nodes.elementAt(identity);
+  }
+
+  /**
+   * Get a Node from an identity index.
+   *
+   * NEEDSDOC @param nodeIdentity
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  protected Node lookupNode(int nodeIdentity)
+  {
+    return (Node) m_nodes.elementAt(nodeIdentity);
+  }
+
+  /**
+   * Get the next node identity value in the list, and call the iterator
+   * if it hasn't been added yet.
+   *
+   * @param identity The node identity (index).
+   * @return identity+1, or DTM.NULL.
+   */
+  protected int getNextNodeIdentity(int identity)
+  {
+
+    identity += 1;
+
+    if (identity >= m_nodes.size())
+    {
+      if (!nextNode())
+        identity = DTM.NULL;
+    }
+
+    return identity;
+  }
+
+  /**
+   * Get the handle from a Node.
+   * <p>%OPT% This will be pretty slow.</p>
+   *
+   * <p>%OPT% An XPath-like search (walk up DOM to root, tracking path;
+   * walk down DTM reconstructing path) might be considerably faster
+   * on later nodes in large documents. That might also imply improving
+   * this call to handle nodes which would be in this DTM but
+   * have not yet been built, which might or might not be a Good Thing.</p>
+   * 
+   * %REVIEW% This relies on being able to test node-identity via
+   * object-identity. DTM2DOM proxying is a great example of a case where
+   * that doesn't work. DOM Level 3 will provide the isSameNode() method
+   * to fix that, but until then this is going to be flaky.
+   *
+   * @param node A node, which may be null.
+   *
+   * @return The node handle or <code>DTM.NULL</code>.
+   */
+  private int getHandleFromNode(Node node)
+  {
+    if (null != node)
+    {
+      int len = m_nodes.size();        
+      boolean isMore;
+      int i = 0;
+      do
+      {          
+        for (; i < len; i++)
+        {
+          if (m_nodes.elementAt(i) == node)
+            return makeNodeHandle(i);
+        }
+
+        isMore = nextNode();
+  
+        len = m_nodes.size();
+            
+      } 
+      while(isMore || i < len);
+    }
+    
+    return DTM.NULL;
+  }
+
+  /** Get the handle from a Node. This is a more robust version of
+   * getHandleFromNode, intended to be usable by the public.
+   *
+   * <p>%OPT% This will be pretty slow.</p>
+   * 
+   * %REVIEW% This relies on being able to test node-identity via
+   * object-identity. DTM2DOM proxying is a great example of a case where
+   * that doesn't work. DOM Level 3 will provide the isSameNode() method
+   * to fix that, but until then this is going to be flaky.
+   *
+   * @param node A node, which may be null.
+   *
+   * @return The node handle or <code>DTM.NULL</code>.  */
+  public int getHandleOfNode(Node node)
+  {
+    if (null != node)
+    {
+      // Is Node actually within the same document? If not, don't search!
+      // This would be easier if m_root was always the Document node, but
+      // we decided to allow wrapping a DTM around a subtree.
+      if((m_root==node) ||
+         (m_root.getNodeType()==DOCUMENT_NODE &&
+          m_root==node.getOwnerDocument()) ||
+         (m_root.getNodeType()!=DOCUMENT_NODE &&
+          m_root.getOwnerDocument()==node.getOwnerDocument())
+         )
+        {
+          // If node _is_ in m_root's tree, find its handle
+          //
+          // %OPT% This check may be improved significantly when DOM
+          // Level 3 nodeKey and relative-order tests become
+          // available!
+          for(Node cursor=node;
+              cursor!=null;
+              cursor=
+                (cursor.getNodeType()!=ATTRIBUTE_NODE)
+                ? cursor.getParentNode()
+                : ((org.w3c.dom.Attr)cursor).getOwnerElement())
+            {
+              if(cursor==m_root)
+                // We know this node; find its handle.
+                return getHandleFromNode(node); 
+            } // for ancestors of node
+        } // if node and m_root in same Document
+    } // if node!=null
+
+    return DTM.NULL;
+  }
+
+  /**
+   * Retrieves an attribute node by by qualified name and namespace URI.
+   *
+   * @param nodeHandle int Handle of the node upon which to look up this attribute..
+   * @param namespaceURI The namespace URI of the attribute to
+   *   retrieve, or null.
+   * @param name The local name of the attribute to
+   *   retrieve.
+   * @return The attribute node handle with the specified name (
+   *   <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
+   *   attribute.
+   */
+  public int getAttributeNode(int nodeHandle, String namespaceURI,
+                              String name)
+  {
+
+    // %OPT% This is probably slower than it needs to be.
+    if (null == namespaceURI)
+      namespaceURI = "";
+
+    int type = getNodeType(nodeHandle);
+
+    if (DTM.ELEMENT_NODE == type)
+    {
+
+      // Assume that attributes immediately follow the element.
+      int identity = makeNodeIdentity(nodeHandle);
+
+      while (DTM.NULL != (identity = getNextNodeIdentity(identity)))
+      {
+        // Assume this can not be null.
+        type = _type(identity);
+
+				// %REVIEW%
+				// Should namespace nodes be retrievable DOM-style as attrs?
+				// If not we need a separate function... which may be desirable
+				// architecturally, but which is ugly from a code point of view.
+				// (If we REALLY insist on it, this code should become a subroutine
+				// of both -- retrieve the node, then test if the type matches
+				// what you're looking for.)
+        if (type == DTM.ATTRIBUTE_NODE || type==DTM.NAMESPACE_NODE)
+        {
+          Node node = lookupNode(identity);
+          String nodeuri = node.getNamespaceURI();
+
+          if (null == nodeuri)
+            nodeuri = "";
+
+          String nodelocalname = node.getLocalName();
+
+          if (nodeuri.equals(namespaceURI) && name.equals(nodelocalname))
+            return makeNodeHandle(identity);
+        }
+				
+        else // if (DTM.NAMESPACE_NODE != type)
+        {
+          break;
+        }
+      }
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * Get the string-value of a node as a String object
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A string object that represents the string-value of the given node.
+   */
+  public XMLString getStringValue(int nodeHandle)
+  {
+
+    int type = getNodeType(nodeHandle);
+    Node node = getNode(nodeHandle);
+    // %TBD% If an element only has one text node, we should just use it 
+    // directly.
+    if(DTM.ELEMENT_NODE == type || DTM.DOCUMENT_NODE == type 
+    || DTM.DOCUMENT_FRAGMENT_NODE == type)
+    {
+      FastStringBuffer buf = StringBufferPool.get();
+      String s;
+  
+      try
+      {
+        getNodeData(node, buf);
+  
+        s = (buf.length() > 0) ? buf.toString() : "";
+      }
+      finally
+      {
+        StringBufferPool.free(buf);
+      }
+  
+      return m_xstrf.newstr( s );
+    }
+    else if(TEXT_NODE == type || CDATA_SECTION_NODE == type)
+    {
+      // If this is a DTM text node, it may be made of multiple DOM text
+      // nodes -- including navigating into Entity References. DOM2DTM
+      // records the first node in the sequence and requires that we
+      // pick up the others when we retrieve the DTM node's value.
+      //
+      // %REVIEW% DOM Level 3 is expected to add a "whole text"
+      // retrieval method which performs this function for us.
+      FastStringBuffer buf = StringBufferPool.get();
+      while(node!=null)
+      {
+        buf.append(node.getNodeValue());
+        node=logicalNextDOMTextNode(node);
+      }
+      String s=(buf.length() > 0) ? buf.toString() : "";
+      StringBufferPool.free(buf);
+      return m_xstrf.newstr( s );
+    }
+    else
+      return m_xstrf.newstr( node.getNodeValue() );
+  }
+  
+  /**
+   * Determine if the string-value of a node is whitespace
+   *
+   * @param nodeHandle The node Handle.
+   *
+   * @return Return true if the given node is whitespace.
+   */
+  public boolean isWhitespace(int nodeHandle)
+  {
+  	int type = getNodeType(nodeHandle);
+    Node node = getNode(nodeHandle);
+  	if(TEXT_NODE == type || CDATA_SECTION_NODE == type)
+    {
+      // If this is a DTM text node, it may be made of multiple DOM text
+      // nodes -- including navigating into Entity References. DOM2DTM
+      // records the first node in the sequence and requires that we
+      // pick up the others when we retrieve the DTM node's value.
+      //
+      // %REVIEW% DOM Level 3 is expected to add a "whole text"
+      // retrieval method which performs this function for us.
+      FastStringBuffer buf = StringBufferPool.get();
+      while(node!=null)
+      {
+        buf.append(node.getNodeValue());
+        node=logicalNextDOMTextNode(node);
+      }
+     boolean b = buf.isWhitespace(0, buf.length());
+      StringBufferPool.free(buf);
+     return b;
+    }
+    return false;
+  }
+  
+  /**
+   * Retrieve the text content of a DOM subtree, appending it into a
+   * user-supplied FastStringBuffer object. Note that attributes are
+   * not considered part of the content of an element.
+   * <p>
+   * There are open questions regarding whitespace stripping. 
+   * Currently we make no special effort in that regard, since the standard
+   * DOM doesn't yet provide DTD-based information to distinguish
+   * whitespace-in-element-context from genuine #PCDATA. Note that we
+   * should probably also consider xml:space if/when we address this.
+   * DOM Level 3 may solve the problem for us.
+   * <p>
+   * %REVIEW% Actually, since this method operates on the DOM side of the
+   * fence rather than the DTM side, it SHOULDN'T do
+   * any special handling. The DOM does what the DOM does; if you want
+   * DTM-level abstractions, use DTM-level methods.
+   *
+   * @param node Node whose subtree is to be walked, gathering the
+   * contents of all Text or CDATASection nodes.
+   * @param buf FastStringBuffer into which the contents of the text
+   * nodes are to be concatenated.
+   */
+  protected static void getNodeData(Node node, FastStringBuffer buf)
+  {
+
+    switch (node.getNodeType())
+    {
+    case Node.DOCUMENT_FRAGMENT_NODE :
+    case Node.DOCUMENT_NODE :
+    case Node.ELEMENT_NODE :
+    {
+      for (Node child = node.getFirstChild(); null != child;
+              child = child.getNextSibling())
+      {
+        getNodeData(child, buf);
+      }
+    }
+    break;
+    case Node.TEXT_NODE :
+    case Node.CDATA_SECTION_NODE :
+    case Node.ATTRIBUTE_NODE :	// Never a child but might be our starting node
+      buf.append(node.getNodeValue());
+      break;
+    case Node.PROCESSING_INSTRUCTION_NODE :
+      // warning(XPATHErrorResources.WG_PARSING_AND_PREPARING);        
+      break;
+    default :
+      // ignore
+      break;
+    }
+  }
+
+  /**
+   * Given a node handle, return its DOM-style node name. This will
+   * include names such as #text or #document.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   * %REVIEW% Document when empty string is possible...
+   * %REVIEW-COMMENT% It should never be empty, should it?
+   */
+  public String getNodeName(int nodeHandle)
+  {
+
+    Node node = getNode(nodeHandle);
+
+    // Assume non-null.
+    return node.getNodeName();
+  }
+
+  /**
+   * Given a node handle, return the XPath node name.  This should be
+   * the name as described by the XPath data model, NOT the DOM-style
+   * name.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   */
+  public String getNodeNameX(int nodeHandle)
+  {
+
+    String name;
+    short type = getNodeType(nodeHandle);
+
+    switch (type)
+    {
+    case DTM.NAMESPACE_NODE :
+    {
+      Node node = getNode(nodeHandle);
+
+      // assume not null.
+      name = node.getNodeName();
+      if(name.startsWith("xmlns:"))
+      {
+        name = QName.getLocalPart(name);
+      }
+      else if(name.equals("xmlns"))
+      {
+        name = "";
+      }
+    }
+    break;
+    case DTM.ATTRIBUTE_NODE :
+    case DTM.ELEMENT_NODE :
+    case DTM.ENTITY_REFERENCE_NODE :
+    case DTM.PROCESSING_INSTRUCTION_NODE :
+    {
+      Node node = getNode(nodeHandle);
+
+      // assume not null.
+      name = node.getNodeName();
+    }
+    break;
+    default :
+      name = "";
+    }
+
+    return name;
+  }
+
+  /**
+   * Given a node handle, return its XPath-style localname.
+   * (As defined in Namespaces, this is the portion of the name after any
+   * colon character).
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Local name of this node.
+   */
+  public String getLocalName(int nodeHandle)
+  {
+    if(JJK_NEWCODE)
+    {
+      int id=makeNodeIdentity(nodeHandle);
+      if(NULL==id) return null;
+      Node newnode=(Node)m_nodes.elementAt(id);
+      String newname=newnode.getLocalName();
+      if (null == newname)
+      {
+	// XSLT treats PIs, and possibly other things, as having QNames.
+	String qname = newnode.getNodeName();
+	if('#'==qname.charAt(0))
+	{
+	  //  Match old default for this function
+	  // This conversion may or may not be necessary
+	  newname="";
+	}
+	else
+	{
+	  int index = qname.indexOf(':');
+	  newname = (index < 0) ? qname : qname.substring(index + 1);
+	}
+      }
+      return newname;
+    }
+    else
+    {
+      String name;
+      short type = getNodeType(nodeHandle);
+      switch (type)
+      {
+      case DTM.ATTRIBUTE_NODE :
+      case DTM.ELEMENT_NODE :
+      case DTM.ENTITY_REFERENCE_NODE :
+      case DTM.NAMESPACE_NODE :
+      case DTM.PROCESSING_INSTRUCTION_NODE :
+	{
+	  Node node = getNode(nodeHandle);
+	  
+	  // assume not null.
+	  name = node.getLocalName();
+	  
+	  if (null == name)
+	  {
+	    String qname = node.getNodeName();
+	    int index = qname.indexOf(':');
+	    
+	    name = (index < 0) ? qname : qname.substring(index + 1);
+	  }
+	}
+	break;
+      default :
+	name = "";
+      }
+      return name;
+    }
+  }
+
+  /**
+   * Given a namespace handle, return the prefix that the namespace decl is
+   * mapping.
+   * Given a node handle, return the prefix used to map to the namespace.
+   *
+   * <p> %REVIEW% Are you sure you want "" for no prefix?  </p>
+   * <p> %REVIEW-COMMENT% I think so... not totally sure. -sb  </p>
+   *
+   * @param nodeHandle the id of the node.
+   * @return String prefix of this node's name, or "" if no explicit
+   * namespace prefix was given.
+   */
+  public String getPrefix(int nodeHandle)
+  {
+
+    String prefix;
+    short type = getNodeType(nodeHandle);
+
+    switch (type)
+    {
+    case DTM.NAMESPACE_NODE :
+    {
+      Node node = getNode(nodeHandle);
+
+      // assume not null.
+      String qname = node.getNodeName();
+      int index = qname.indexOf(':');
+
+      prefix = (index < 0) ? "" : qname.substring(index + 1);
+    }
+    break;
+    case DTM.ATTRIBUTE_NODE :
+    case DTM.ELEMENT_NODE :
+    {
+      Node node = getNode(nodeHandle);
+
+      // assume not null.
+      String qname = node.getNodeName();
+      int index = qname.indexOf(':');
+
+      prefix = (index < 0) ? "" : qname.substring(0, index);
+    }
+    break;
+    default :
+      prefix = "";
+    }
+
+    return prefix;
+  }
+
+  /**
+   * Given a node handle, return its DOM-style namespace URI
+   * (As defined in Namespaces, this is the declared URI which this node's
+   * prefix -- or default in lieu thereof -- was mapped to.)
+   *
+   * <p>%REVIEW% Null or ""? -sb</p>
+   *
+   * @param nodeHandle the id of the node.
+   * @return String URI value of this node's namespace, or null if no
+   * namespace was resolved.
+   */
+  public String getNamespaceURI(int nodeHandle)
+  {
+    if(JJK_NEWCODE)
+    {
+      int id=makeNodeIdentity(nodeHandle);
+      if(id==NULL) return null;
+      Node node=(Node)m_nodes.elementAt(id);
+      return node.getNamespaceURI();
+    }
+    else
+    {
+      String nsuri;
+      short type = getNodeType(nodeHandle);
+      
+      switch (type)
+      {
+      case DTM.ATTRIBUTE_NODE :
+      case DTM.ELEMENT_NODE :
+      case DTM.ENTITY_REFERENCE_NODE :
+      case DTM.NAMESPACE_NODE :
+      case DTM.PROCESSING_INSTRUCTION_NODE :
+	{
+	  Node node = getNode(nodeHandle);
+	  
+	  // assume not null.
+	  nsuri = node.getNamespaceURI();
+	  
+	  // %TBD% Handle DOM1?
+	}
+	break;
+      default :
+	nsuri = null;
+      }
+
+      return nsuri;
+    }
+    
+  }
+  
+  /** Utility function: Given a DOM Text node, determine whether it is
+   * logically followed by another Text or CDATASection node. This may
+   * involve traversing into Entity References.
+   * 
+   * %REVIEW% DOM Level 3 is expected to add functionality which may 
+   * allow us to retire this.
+   */
+  private Node logicalNextDOMTextNode(Node n)
+  {
+        Node p=n.getNextSibling();
+        if(p==null)
+        {
+                // Walk out of any EntityReferenceNodes that ended with text
+                for(n=n.getParentNode();
+                        n!=null && ENTITY_REFERENCE_NODE == n.getNodeType();
+                        n=n.getParentNode())
+                {
+                        p=n.getNextSibling();
+                        if(p!=null)
+                                break;
+                }
+        }
+        n=p;
+        while(n!=null && ENTITY_REFERENCE_NODE == n.getNodeType())
+        {
+                // Walk into any EntityReferenceNodes that start with text
+                if(n.hasChildNodes())
+                        n=n.getFirstChild();
+                else
+                        n=n.getNextSibling();
+        }
+        if(n!=null)
+        {
+                // Found a logical next sibling. Is it text?
+                int ntype=n.getNodeType();
+                if(TEXT_NODE != ntype && CDATA_SECTION_NODE != ntype)
+                        n=null;
+        }
+        return n;
+  }
+
+  /**
+   * Given a node handle, return its node value. This is mostly
+   * as defined by the DOM, but may ignore some conveniences.
+   * <p>
+   *
+   * @param nodeHandle The node id.
+   * @return String Value of this node, or null if not
+   * meaningful for this node type.
+   */
+  public String getNodeValue(int nodeHandle)
+  {
+    // The _type(nodeHandle) call was taking the lion's share of our
+    // time, and was wrong anyway since it wasn't coverting handle to
+    // identity. Inlined it.
+    int type = _exptype(makeNodeIdentity(nodeHandle));
+    type=(NULL != type) ? getNodeType(nodeHandle) : NULL;
+    
+    if(TEXT_NODE!=type && CDATA_SECTION_NODE!=type)
+      return getNode(nodeHandle).getNodeValue();
+    
+    // If this is a DTM text node, it may be made of multiple DOM text
+    // nodes -- including navigating into Entity References. DOM2DTM
+    // records the first node in the sequence and requires that we
+    // pick up the others when we retrieve the DTM node's value.
+    //
+    // %REVIEW% DOM Level 3 is expected to add a "whole text"
+    // retrieval method which performs this function for us.
+    Node node = getNode(nodeHandle);
+    Node n=logicalNextDOMTextNode(node);
+    if(n==null)
+      return node.getNodeValue();
+    
+    FastStringBuffer buf = StringBufferPool.get();
+        buf.append(node.getNodeValue());
+    while(n!=null)
+    {
+      buf.append(n.getNodeValue());
+      n=logicalNextDOMTextNode(n);
+    }
+    String s = (buf.length() > 0) ? buf.toString() : "";
+    StringBufferPool.free(buf);
+    return s;
+  }
+
+  /**
+   *   A document type declaration information item has the following properties:
+   *
+   *     1. [system identifier] The system identifier of the external subset, if
+   *        it exists. Otherwise this property has no value.
+   *
+   * @return the system identifier String object, or null if there is none.
+   */
+  public String getDocumentTypeDeclarationSystemIdentifier()
+  {
+
+    Document doc;
+
+    if (m_root.getNodeType() == Node.DOCUMENT_NODE)
+      doc = (Document) m_root;
+    else
+      doc = m_root.getOwnerDocument();
+
+    if (null != doc)
+    {
+      DocumentType dtd = doc.getDoctype();
+
+      if (null != dtd)
+      {
+        return dtd.getSystemId();
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Return the public identifier of the external subset,
+   * normalized as described in 4.2.2 External Entities [XML]. If there is
+   * no external subset or if it has no public identifier, this property
+   * has no value.
+   *
+   * @return the public identifier String object, or null if there is none.
+   */
+  public String getDocumentTypeDeclarationPublicIdentifier()
+  {
+
+    Document doc;
+
+    if (m_root.getNodeType() == Node.DOCUMENT_NODE)
+      doc = (Document) m_root;
+    else
+      doc = m_root.getOwnerDocument();
+
+    if (null != doc)
+    {
+      DocumentType dtd = doc.getDoctype();
+
+      if (null != dtd)
+      {
+        return dtd.getPublicId();
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Returns the <code>Element</code> whose <code>ID</code> is given by
+   * <code>elementId</code>. If no such element exists, returns
+   * <code>DTM.NULL</code>. Behavior is not defined if more than one element
+   * has this <code>ID</code>. Attributes (including those
+   * with the name "ID") are not of type ID unless so defined by DTD/Schema
+   * information available to the DTM implementation.
+   * Implementations that do not know whether attributes are of type ID or
+   * not are expected to return <code>DTM.NULL</code>.
+   *
+   * <p>%REVIEW% Presumably IDs are still scoped to a single document,
+   * and this operation searches only within a single document, right?
+   * Wouldn't want collisions between DTMs in the same process.</p>
+   *
+   * @param elementId The unique <code>id</code> value for an element.
+   * @return The handle of the matching element.
+   */
+  public int getElementById(String elementId)
+  {
+
+    Document doc = (m_root.getNodeType() == Node.DOCUMENT_NODE) 
+        ? (Document) m_root : m_root.getOwnerDocument();
+        
+    if(null != doc)
+    {
+      Node elem = doc.getElementById(elementId);
+      if(null != elem)
+      {
+        int elemHandle = getHandleFromNode(elem);
+        
+        if(DTM.NULL == elemHandle)
+        {
+          int identity = m_nodes.size()-1;
+          while (DTM.NULL != (identity = getNextNodeIdentity(identity)))
+          {
+            Node node = getNode(identity);
+            if(node == elem)
+            {
+              elemHandle = getHandleFromNode(elem);
+              break;
+            }
+           }
+        }
+        
+        return elemHandle;
+      }
+    
+    }
+    return DTM.NULL;
+  }
+
+  /**
+   * The getUnparsedEntityURI function returns the URI of the unparsed
+   * entity with the specified name in the same document as the context
+   * node (see [3.3 Unparsed Entities]). It returns the empty string if
+   * there is no such entity.
+   * <p>
+   * XML processors may choose to use the System Identifier (if one
+   * is provided) to resolve the entity, rather than the URI in the
+   * Public Identifier. The details are dependent on the processor, and
+   * we would have to support some form of plug-in resolver to handle
+   * this properly. Currently, we simply return the System Identifier if
+   * present, and hope that it a usable URI or that our caller can
+   * map it to one.
+   * TODO: Resolve Public Identifiers... or consider changing function name.
+   * <p>
+   * If we find a relative URI
+   * reference, XML expects it to be resolved in terms of the base URI
+   * of the document. The DOM doesn't do that for us, and it isn't
+   * entirely clear whether that should be done here; currently that's
+   * pushed up to a higher level of our application. (Note that DOM Level
+   * 1 didn't store the document's base URI.)
+   * TODO: Consider resolving Relative URIs.
+   * <p>
+   * (The DOM's statement that "An XML processor may choose to
+   * completely expand entities before the structure model is passed
+   * to the DOM" refers only to parsed entities, not unparsed, and hence
+   * doesn't affect this function.)
+   *
+   * @param name A string containing the Entity Name of the unparsed
+   * entity.
+   *
+   * @return String containing the URI of the Unparsed Entity, or an
+   * empty string if no such entity exists.
+   */
+  public String getUnparsedEntityURI(String name)
+  {
+
+    String url = "";
+    Document doc = (m_root.getNodeType() == Node.DOCUMENT_NODE) 
+        ? (Document) m_root : m_root.getOwnerDocument();
+
+    if (null != doc)
+    {
+      DocumentType doctype = doc.getDoctype();
+  
+      if (null != doctype)
+      {
+        NamedNodeMap entities = doctype.getEntities();
+        if(null == entities)
+          return url;
+        Entity entity = (Entity) entities.getNamedItem(name);
+        if(null == entity)
+          return url;
+        
+        String notationName = entity.getNotationName();
+  
+        if (null != notationName)  // then it's unparsed
+        {
+          // The draft says: "The XSLT processor may use the public 
+          // identifier to generate a URI for the entity instead of the URI 
+          // specified in the system identifier. If the XSLT processor does 
+          // not use the public identifier to generate the URI, it must use 
+          // the system identifier; if the system identifier is a relative 
+          // URI, it must be resolved into an absolute URI using the URI of 
+          // the resource containing the entity declaration as the base 
+          // URI [RFC2396]."
+          // So I'm falling a bit short here.
+          url = entity.getSystemId();
+  
+          if (null == url)
+          {
+            url = entity.getPublicId();
+          }
+          else
+          {
+            // This should be resolved to an absolute URL, but that's hard 
+            // to do from here.
+          }        
+        }
+      }
+    }
+
+    return url;
+  }
+
+  /**
+   *     5. [specified] A flag indicating whether this attribute was actually
+   *        specified in the start-tag of its element, or was defaulted from the
+   *        DTD.
+   *
+   * @param attributeHandle the attribute handle
+   * @return <code>true</code> if the attribute was specified;
+   *         <code>false</code> if it was defaulted.
+   */
+  public boolean isAttributeSpecified(int attributeHandle)
+  {
+    int type = getNodeType(attributeHandle);
+
+    if (DTM.ATTRIBUTE_NODE == type)
+    {
+      Attr attr = (Attr)getNode(attributeHandle);
+      return attr.getSpecified();
+    }
+    return false;
+  }
+
+  /** Bind an IncrementalSAXSource to this DTM. NOT RELEVANT for DOM2DTM, since
+   * we're wrapped around an existing DOM.
+   *
+   * @param source The IncrementalSAXSource that we want to recieve events from
+   * on demand.
+   */
+  public void setIncrementalSAXSource(IncrementalSAXSource source)
+  {
+  }
+  
+  /** getContentHandler returns "our SAX builder" -- the thing that
+   * someone else should send SAX events to in order to extend this
+   * DTM model.
+   *
+   * @return null if this model doesn't respond to SAX events,
+   * "this" if the DTM object has a built-in SAX ContentHandler,
+   * the IncrmentalSAXSource if we're bound to one and should receive
+   * the SAX stream via it for incremental build purposes...
+   * */
+  public org.xml.sax.ContentHandler getContentHandler()
+  {
+      return null;
+  }
+  
+  /**
+   * Return this DTM's lexical handler.
+   *
+   * %REVIEW% Should this return null if constrution already done/begun?
+   *
+   * @return null if this model doesn't respond to lexical SAX events,
+   * "this" if the DTM object has a built-in SAX ContentHandler,
+   * the IncrementalSAXSource if we're bound to one and should receive
+   * the SAX stream via it for incremental build purposes...
+   */
+  public org.xml.sax.ext.LexicalHandler getLexicalHandler()
+  {
+
+    return null;
+  }
+
+  
+  /**
+   * Return this DTM's EntityResolver.
+   *
+   * @return null if this model doesn't respond to SAX entity ref events.
+   */
+  public org.xml.sax.EntityResolver getEntityResolver()
+  {
+
+    return null;
+  }
+  
+  /**
+   * Return this DTM's DTDHandler.
+   *
+   * @return null if this model doesn't respond to SAX dtd events.
+   */
+  public org.xml.sax.DTDHandler getDTDHandler()
+  {
+
+    return null;
+  }
+
+  /**
+   * Return this DTM's ErrorHandler.
+   *
+   * @return null if this model doesn't respond to SAX error events.
+   */
+  public org.xml.sax.ErrorHandler getErrorHandler()
+  {
+
+    return null;
+  }
+  
+  /**
+   * Return this DTM's DeclHandler.
+   *
+   * @return null if this model doesn't respond to SAX Decl events.
+   */
+  public org.xml.sax.ext.DeclHandler getDeclHandler()
+  {
+
+    return null;
+  }  
+
+  /** @return true iff we're building this model incrementally (eg
+   * we're partnered with a IncrementalSAXSource) and thus require that the
+   * transformation and the parse run simultaneously. Guidance to the
+   * DTMManager.
+   * */
+  public boolean needsTwoThreads()
+  {
+    return false;
+  }
+
+  // ========== Direct SAX Dispatch, for optimization purposes ========
+  
+  /**
+   * Returns whether the specified <var>ch</var> conforms to the XML 1.0 definition
+   * of whitespace.  Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S">
+   * the definition of <CODE>S</CODE></A> for details.
+   * @param   ch      Character to check as XML whitespace.
+   * @return          =true if <var>ch</var> is XML whitespace; otherwise =false.
+   */
+  private static boolean isSpace(char ch)
+  {
+    return XMLCharacterRecognizer.isWhiteSpace(ch);  // Take the easy way out for now.
+  }
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value). Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchCharactersEvents(
+          int nodeHandle, org.xml.sax.ContentHandler ch, 
+          boolean normalize)
+            throws org.xml.sax.SAXException
+  {
+    if(normalize)
+    {
+      XMLString str = getStringValue(nodeHandle);
+      str = str.fixWhiteSpace(true, true, false);
+      str.dispatchCharactersEvents(ch);
+    }
+    else
+    {
+      int type = getNodeType(nodeHandle);
+      Node node = getNode(nodeHandle);
+      dispatchNodeData(node, ch, 0);
+          // Text coalition -- a DTM text node may represent multiple
+          // DOM nodes.
+          if(TEXT_NODE == type || CDATA_SECTION_NODE == type)
+          {
+                  while( null != (node=logicalNextDOMTextNode(node)) )
+                  {
+                      dispatchNodeData(node, ch, 0);
+                  }
+          }
+    }
+  }
+  
+  /**
+   * Retrieve the text content of a DOM subtree, appending it into a
+   * user-supplied FastStringBuffer object. Note that attributes are
+   * not considered part of the content of an element.
+   * <p>
+   * There are open questions regarding whitespace stripping. 
+   * Currently we make no special effort in that regard, since the standard
+   * DOM doesn't yet provide DTD-based information to distinguish
+   * whitespace-in-element-context from genuine #PCDATA. Note that we
+   * should probably also consider xml:space if/when we address this.
+   * DOM Level 3 may solve the problem for us.
+   * <p>
+   * %REVIEW% Note that as a DOM-level operation, it can be argued that this
+   * routine _shouldn't_ perform any processing beyond what the DOM already
+   * does, and that whitespace stripping and so on belong at the DTM level.
+   * If you want a stripped DOM view, wrap DTM2DOM around DOM2DTM.
+   *
+   * @param node Node whose subtree is to be walked, gathering the
+   * contents of all Text or CDATASection nodes.
+   */
+  protected static void dispatchNodeData(Node node, 
+                                         org.xml.sax.ContentHandler ch, 
+                                         int depth)
+            throws org.xml.sax.SAXException
+  {
+
+    switch (node.getNodeType())
+    {
+    case Node.DOCUMENT_FRAGMENT_NODE :
+    case Node.DOCUMENT_NODE :
+    case Node.ELEMENT_NODE :
+    {
+      for (Node child = node.getFirstChild(); null != child;
+              child = child.getNextSibling())
+      {
+        dispatchNodeData(child, ch, depth+1);
+      }
+    }
+    break;
+    case Node.PROCESSING_INSTRUCTION_NODE : // %REVIEW%
+    case Node.COMMENT_NODE :
+      if(0 != depth)
+        break;
+        // NOTE: Because this operation works in the DOM space, it does _not_ attempt
+        // to perform Text Coalition. That should only be done in DTM space. 
+    case Node.TEXT_NODE :
+    case Node.CDATA_SECTION_NODE :
+    case Node.ATTRIBUTE_NODE :
+      String str = node.getNodeValue();
+      if(ch instanceof CharacterNodeHandler)
+      {
+        ((CharacterNodeHandler)ch).characters(node);
+      }
+      else
+      {
+        ch.characters(str.toCharArray(), 0, str.length());
+      }
+      break;
+//    /* case Node.PROCESSING_INSTRUCTION_NODE :
+//      // warning(XPATHErrorResources.WG_PARSING_AND_PREPARING);        
+//      break; */
+    default :
+      // ignore
+      break;
+    }
+  }
+  
+  TreeWalker m_walker = new TreeWalker(null);
+  
+  /**
+   * Directly create SAX parser events from a subtree.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch)
+          throws org.xml.sax.SAXException
+  {
+    TreeWalker treeWalker = m_walker;
+    ContentHandler prevCH = treeWalker.getContentHandler();
+    
+    if(null != prevCH)
+    {
+      treeWalker = new TreeWalker(null);
+    }
+    treeWalker.setContentHandler(ch);
+    
+    try
+    {
+      Node node = getNode(nodeHandle);
+      treeWalker.traverseFragment(node);
+    }
+    finally
+    {
+      treeWalker.setContentHandler(null);
+    }
+  }
+  
+  public interface CharacterNodeHandler
+  {
+    public void characters(Node node)
+            throws org.xml.sax.SAXException;
+  }
+
+  /**
+   * For the moment all the run time properties are ignored by this
+   * class.
+   *
+   * @param property a <code>String</code> value
+   * @param value an <code>Object</code> value
+   */
+  public void setProperty(String property, Object value)
+  {
+  }
+  
+  /**
+   * No source information is available for DOM2DTM, so return
+   * <code>null</code> here.
+   *
+   * @param node an <code>int</code> value
+   * @return null
+   */
+  public SourceLocator getSourceLocatorFor(int node)
+  {
+    return null;
+  }
+
+}
+
+
diff --git a/src/main/java/org/apache/xml/dtm/ref/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java b/src/main/java/org/apache/xml/dtm/ref/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java
new file mode 100644
index 0000000..1629326
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java
@@ -0,0 +1,679 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOM2DTMdefaultNamespaceDeclarationNode.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+
+package org.apache.xml.dtm.ref.dom2dtm;
+
+import org.apache.xml.dtm.DTMException;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.TypeInfo;
+import org.w3c.dom.UserDataHandler;
+import org.w3c.dom.DOMException;
+
+/** This is a kluge to let us shove a declaration for xml: into the
+ * DOM2DTM model.  Basically, it creates a proxy node in DOM space to
+ * carry the additional information. This is _NOT_ a full DOM
+ * implementation, and shouldn't be one since it sits alongside the
+ * DOM rather than becoming part of the DOM model.
+ * 
+ * (This used to be an internal class within DOM2DTM. Moved out because
+ * I need to perform an instanceof operation on it to support a temporary
+ * workaround in DTMManagerDefault.)
+ * 
+ * %REVIEW% What if the DOM2DTM was built around a DocumentFragment and
+ * there isn't a single root element? I think this fails that case...
+ * 
+ * %REVIEW% An alternative solution would be to create the node _only_
+ * in DTM space, but given how DOM2DTM is currently written I think
+ * this is simplest.
+ * */
+public class DOM2DTMdefaultNamespaceDeclarationNode implements Attr,TypeInfo
+{
+  final String NOT_SUPPORTED_ERR="Unsupported operation on pseudonode";
+  
+  Element pseudoparent;
+  String prefix,uri,nodename;
+  int handle;
+  DOM2DTMdefaultNamespaceDeclarationNode(Element pseudoparent,String prefix,String uri,int handle)
+  {
+    this.pseudoparent=pseudoparent;
+    this.prefix=prefix;
+    this.uri=uri;
+    this.handle=handle;
+    this.nodename="xmlns:"+prefix;
+  }
+  public String getNodeName() {return nodename;}
+  public String getName() {return nodename;}
+  public String getNamespaceURI() {return "http://www.w3.org/2000/xmlns/";}
+  public String getPrefix() {return prefix;}
+  public String getLocalName() {return prefix;}
+  public String getNodeValue() {return uri;}
+  public String getValue() {return uri;}
+  public Element getOwnerElement() {return pseudoparent;}
+  
+  public boolean isSupported(String feature, String version) {return false;}
+  public boolean hasChildNodes() {return false;}
+  public boolean hasAttributes() {return false;}
+  public Node getParentNode() {return null;}
+  public Node getFirstChild() {return null;}
+  public Node getLastChild() {return null;}
+  public Node getPreviousSibling() {return null;}
+  public Node getNextSibling() {return null;}
+  public boolean getSpecified() {return false;}
+  public void normalize() {return;}
+  public NodeList getChildNodes() {return null;}
+  public NamedNodeMap getAttributes() {return null;}
+  public short getNodeType() {return Node.ATTRIBUTE_NODE;}
+  public void setNodeValue(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public void setValue(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public void setPrefix(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Node insertBefore(Node a, Node b) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Node replaceChild(Node a, Node b) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Node appendChild(Node a) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Node removeChild(Node a) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Document getOwnerDocument() {return pseudoparent.getOwnerDocument();}
+  public Node cloneNode(boolean deep) {throw new DTMException(NOT_SUPPORTED_ERR);}
+	
+    /** Non-DOM method, part of the temporary kluge
+     * %REVIEW% This would be a pruning problem, but since it will always be
+     * added to the root element and we prune on elements, we shouldn't have 
+     * to worry.
+     */
+    public int getHandleOfNode()		
+    {
+        return handle;
+    }
+
+    //RAMESH: PENDING=> Add proper implementation for the below DOM L3 additions
+
+    /**
+     * @see org.w3c.dom.TypeInfo#getTypeName()
+     */
+    public String getTypeName() {return null; }
+     
+    /**
+     * @see org.w3c.dom.TypeInfo#getTypeNamespace()
+     */
+    public String getTypeNamespace() { return null;}
+    
+    /**
+     * @see or.gw3c.dom.TypeInfo#isDerivedFrom(String,String,int)
+     */
+    public boolean isDerivedFrom( String ns, String localName, int derivationMethod ) {
+        return false;
+    }
+
+    public TypeInfo getSchemaTypeInfo() { return this; }
+    
+    public boolean isId( ) { return false; }
+
+    /**
+     * Associate an object to a key on this node. The object can later be
+     * retrieved from this node by calling <code>getUserData</code> with the
+     * same key.
+     * @param key The key to associate the object to.
+     * @param data The object to associate to the given key, or
+     *   <code>null</code> to remove any existing association to that key.
+     * @param handler The handler to associate to that key, or
+     *   <code>null</code>.
+     * @return Returns the <code>DOMObject</code> previously associated to
+     *   the given key on this node, or <code>null</code> if there was none.
+     * @since DOM Level 3
+     */
+    public Object setUserData(String key,
+                              Object data,
+                              UserDataHandler handler) {
+        return getOwnerDocument().setUserData( key, data, handler);
+    }
+
+    /**
+     * Retrieves the object associated to a key on a this node. The object
+     * must first have been set to this node by calling
+     * <code>setUserData</code> with the same key.
+     * @param key The key the object is associated to.
+     * @return Returns the <code>DOMObject</code> associated to the given key
+     *   on this node, or <code>null</code> if there was none.
+     * @since DOM Level 3
+     */
+    public Object getUserData(String key) {
+        return getOwnerDocument().getUserData( key);
+    } 
+
+    /**
+     *  This method returns a specialized object which implements the
+     * specialized APIs of the specified feature and version. The
+     * specialized object may also be obtained by using binding-specific
+     * casting methods but is not necessarily expected to, as discussed in Mixed DOM implementations.
+     * @param feature The name of the feature requested (case-insensitive).
+     * @param version  This is the version number of the feature to test. If
+     *   the version is <code>null</code> or the empty string, supporting
+     *   any version of the feature will cause the method to return an
+     *   object that supports at least one version of the feature.
+     * @return  Returns an object which implements the specialized APIs of
+     *   the specified feature and version, if any, or <code>null</code> if
+     *   there is no object which implements interfaces associated with that
+     *   feature. If the <code>DOMObject</code> returned by this method
+     *   implements the <code>Node</code> interface, it must delegate to the
+     *   primary core <code>Node</code> and not return results inconsistent
+     *   with the primary core <code>Node</code> such as attributes,
+     *   childNodes, etc.
+     * @since DOM Level 3
+     */
+    public Object getFeature(String feature, String version) {
+        // we don't have any alternate node, either this node does the job
+        // or we don't have anything that does
+        return isSupported(feature, version) ? this : null;
+    }
+
+    /**
+     * Tests whether two nodes are equal.
+     * <br>This method tests for equality of nodes, not sameness (i.e.,
+     * whether the two nodes are references to the same object) which can be
+     * tested with <code>Node.isSameNode</code>. All nodes that are the same
+     * will also be equal, though the reverse may not be true.
+     * <br>Two nodes are equal if and only if the following conditions are
+     * satisfied: The two nodes are of the same type.The following string
+     * attributes are equal: <code>nodeName</code>, <code>localName</code>,
+     * <code>namespaceURI</code>, <code>prefix</code>, <code>nodeValue</code>
+     * , <code>baseURI</code>. This is: they are both <code>null</code>, or
+     * they have the same length and are character for character identical.
+     * The <code>attributes</code> <code>NamedNodeMaps</code> are equal.
+     * This is: they are both <code>null</code>, or they have the same
+     * length and for each node that exists in one map there is a node that
+     * exists in the other map and is equal, although not necessarily at the
+     * same index.The <code>childNodes</code> <code>NodeLists</code> are
+     * equal. This is: they are both <code>null</code>, or they have the
+     * same length and contain equal nodes at the same index. This is true
+     * for <code>Attr</code> nodes as for any other type of node. Note that
+     * normalization can affect equality; to avoid this, nodes should be
+     * normalized before being compared.
+     * <br>For two <code>DocumentType</code> nodes to be equal, the following
+     * conditions must also be satisfied: The following string attributes
+     * are equal: <code>publicId</code>, <code>systemId</code>,
+     * <code>internalSubset</code>.The <code>entities</code>
+     * <code>NamedNodeMaps</code> are equal.The <code>notations</code>
+     * <code>NamedNodeMaps</code> are equal.
+     * <br>On the other hand, the following do not affect equality: the
+     * <code>ownerDocument</code> attribute, the <code>specified</code>
+     * attribute for <code>Attr</code> nodes, the
+     * <code>isWhitespaceInElementContent</code> attribute for
+     * <code>Text</code> nodes, as well as any user data or event listeners
+     * registered on the nodes.
+     * @param arg The node to compare equality with.
+     * @param deep If <code>true</code>, recursively compare the subtrees; if
+     *   <code>false</code>, compare only the nodes themselves (and its
+     *   attributes, if it is an <code>Element</code>).
+     * @return If the nodes, and possibly subtrees are equal,
+     *   <code>true</code> otherwise <code>false</code>.
+     * @since DOM Level 3
+     */
+    public boolean isEqualNode(Node arg) {
+        if (arg == this) {
+            return true;
+        }
+        if (arg.getNodeType() != getNodeType()) {
+            return false;
+        }
+        // in theory nodeName can't be null but better be careful
+        // who knows what other implementations may be doing?...
+        if (getNodeName() == null) {
+            if (arg.getNodeName() != null) {
+                return false;
+            }
+        }
+        else if (!getNodeName().equals(arg.getNodeName())) {
+            return false;
+        }
+
+        if (getLocalName() == null) {
+            if (arg.getLocalName() != null) {
+                return false;
+            }
+        }
+        else if (!getLocalName().equals(arg.getLocalName())) {
+            return false;
+        }
+
+        if (getNamespaceURI() == null) {
+            if (arg.getNamespaceURI() != null) {
+                return false;
+            }
+        }
+        else if (!getNamespaceURI().equals(arg.getNamespaceURI())) {
+            return false;
+        }
+
+        if (getPrefix() == null) {
+            if (arg.getPrefix() != null) {
+                return false;
+            }
+        }
+        else if (!getPrefix().equals(arg.getPrefix())) {
+            return false;
+        }
+
+        if (getNodeValue() == null) {
+            if (arg.getNodeValue() != null) {
+                return false;
+            }
+        }
+        else if (!getNodeValue().equals(arg.getNodeValue())) {
+            return false;
+        }
+    /*
+        if (getBaseURI() == null) {
+            if (((NodeImpl) arg).getBaseURI() != null) {
+                return false;
+            }
+        }
+        else if (!getBaseURI().equals(((NodeImpl) arg).getBaseURI())) {
+            return false;
+        }
+*/
+
+             return true;
+    }
+
+    /**
+     * DOM Level 3 - Experimental:
+     * Look up the namespace URI associated to the given prefix, starting from this node.
+     * Use lookupNamespaceURI(null) to lookup the default namespace
+     *
+     * @param namespaceURI
+     * @return th URI for the namespace
+     * @since DOM Level 3
+     */
+    public String lookupNamespaceURI(String specifiedPrefix) {
+        short type = this.getNodeType();
+        switch (type) {
+        case Node.ELEMENT_NODE : {
+
+                String namespace = this.getNamespaceURI();
+                String prefix = this.getPrefix();
+                if (namespace !=null) {
+                    // REVISIT: is it possible that prefix is empty string?
+                    if (specifiedPrefix== null && prefix==specifiedPrefix) {
+                        // looking for default namespace
+                        return namespace;
+                    } else if (prefix != null && prefix.equals(specifiedPrefix)) {
+                        // non default namespace
+                        return namespace;
+                    }
+                }
+                if (this.hasAttributes()) {
+                    NamedNodeMap map = this.getAttributes();
+                    int length = map.getLength();
+                    for (int i=0;i<length;i++) {
+                        Node attr = map.item(i);
+                        String attrPrefix = attr.getPrefix();
+                        String value = attr.getNodeValue();
+                        namespace = attr.getNamespaceURI();
+                        if (namespace !=null && namespace.equals("http://www.w3.org/2000/xmlns/")) {
+                            // at this point we are dealing with DOM Level 2 nodes only
+                            if (specifiedPrefix == null &&
+                                attr.getNodeName().equals("xmlns")) {
+                                // default namespace
+                                return value;
+                            } else if (attrPrefix !=null &&
+                                       attrPrefix.equals("xmlns") &&
+                                       attr.getLocalName().equals(specifiedPrefix)) {
+                 // non default namespace
+                                return value;
+                            }
+                        }
+                    }
+                }
+		/*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupNamespaceURI(specifiedPrefix);
+                }
+		*/
+
+                return null;
+
+
+            }
+/*
+        case Node.DOCUMENT_NODE : {
+                return((NodeImpl)((Document)this).getDocumentElement()).lookupNamespaceURI(specifiedPrefix) ;
+            }
+*/
+        case Node.ENTITY_NODE :
+        case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return null;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
+                    return getOwnerElement().lookupNamespaceURI(specifiedPrefix);
+
+                }
+                return null;
+            }
+        default:{
+	   /*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupNamespaceURI(specifiedPrefix);
+                }
+             */
+                return null;
+            }
+
+        }
+    }
+    
+    /**
+     *  DOM Level 3: Experimental
+     *  This method checks if the specified <code>namespaceURI</code> is the
+     *  default namespace or not.
+     *  @param namespaceURI The namespace URI to look for.
+     *  @return  <code>true</code> if the specified <code>namespaceURI</code>
+     *   is the default namespace, <code>false</code> otherwise.
+     * @since DOM Level 3
+     */
+    public boolean isDefaultNamespace(String namespaceURI){
+       /*
+        // REVISIT: remove casts when DOM L3 becomes REC.
+        short type = this.getNodeType();
+        switch (type) {
+        case Node.ELEMENT_NODE: {
+            String namespace = this.getNamespaceURI();
+            String prefix = this.getPrefix();
+
+            // REVISIT: is it possible that prefix is empty string?
+            if (prefix == null || prefix.length() == 0) {
+                if (namespaceURI == null) {
+                    return (namespace == namespaceURI);
+                }
+                return namespaceURI.equals(namespace);
+            }
+            if (this.hasAttributes()) {
+                ElementImpl elem = (ElementImpl)this;
+                NodeImpl attr = (NodeImpl)elem.getAttributeNodeNS("http://www.w3.org/2000/xmlns/", "xmlns");
+                if (attr != null) {
+                    String value = attr.getNodeValue();
+                    if (namespaceURI == null) {
+                        return (namespace == value);
+                    }
+                    return namespaceURI.equals(value);
+                }
+            }
+
+            NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+            if (ancestor != null) {
+                return ancestor.isDefaultNamespace(namespaceURI);
+            }
+            return false;
+        }
+        case Node.DOCUMENT_NODE:{
+                return((NodeImpl)((Document)this).getDocumentElement()).isDefaultNamespace(namespaceURI);
+            }
+
+        case Node.ENTITY_NODE :
+          case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return false;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.ownerNode.getNodeType() == Node.ELEMENT_NODE) {
+                    return ownerNode.isDefaultNamespace(namespaceURI);
+
+                }
+                return false;
+            }
+        default:{  
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.isDefaultNamespace(namespaceURI);
+                }
+                return false;
+            }
+
+        }
+*/
+        return false;
+
+
+    }
+
+    /**
+     *
+     * DOM Level 3 - Experimental:
+     * Look up the prefix associated to the given namespace URI, starting from this node.
+     *
+     * @param namespaceURI
+     * @return the prefix for the namespace
+     */
+    public String lookupPrefix(String namespaceURI){
+
+        // REVISIT: When Namespaces 1.1 comes out this may not be true
+        // Prefix can't be bound to null namespace
+        if (namespaceURI == null) {
+            return null;
+        }
+
+        short type = this.getNodeType();
+
+        switch (type) {
+/*
+        case Node.ELEMENT_NODE: {
+
+                String namespace = this.getNamespaceURI(); // to flip out children
+                return lookupNamespacePrefix(namespaceURI, (ElementImpl)this);
+            }
+
+        case Node.DOCUMENT_NODE:{
+                return((NodeImpl)((Document)this).getDocumentElement()).lookupPrefix(namespaceURI);
+            }
+*/
+        case Node.ENTITY_NODE :
+        case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return null;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
+                    return getOwnerElement().lookupPrefix(namespaceURI);
+
+                }
+                return null;
+            }
+        default:{ 
+/*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupPrefix(namespaceURI);
+                }
+*/
+                return null;
+            }
+         }
+    }
+
+    /**
+     * Returns whether this node is the same node as the given one.
+     * <br>This method provides a way to determine whether two
+     * <code>Node</code> references returned by the implementation reference
+     * the same object. When two <code>Node</code> references are references
+     * to the same object, even if through a proxy, the references may be
+     * used completely interchangably, such that all attributes have the
+     * same values and calling the same DOM method on either reference
+     * always has exactly the same effect.
+     * @param other The node to test against.
+     * @return Returns <code>true</code> if the nodes are the same,
+     *   <code>false</code> otherwise.
+     * @since DOM Level 3
+     */
+    public boolean isSameNode(Node other) {
+        // we do not use any wrapper so the answer is obvious
+        return this == other;
+    }
+
+    /**
+     * This attribute returns the text content of this node and its
+     * descendants. When it is defined to be null, setting it has no effect.
+     * When set, any possible children this node may have are removed and
+     * replaced by a single <code>Text</code> node containing the string
+     * this attribute is set to. On getting, no serialization is performed,
+     * the returned string does not contain any markup. No whitespace
+     * normalization is performed, the returned string does not contain the
+     * element content whitespaces . Similarly, on setting, no parsing is
+     * performed either, the input string is taken as pure textual content.
+     * <br>The string returned is made of the text content of this node
+     * depending on its type, as defined below:
+     * <table border='1'>
+     * <tr>
+     * <th>Node type</th>
+     * <th>Content</th>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
+     * DOCUMENT_FRAGMENT_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
+     * attribute value of every child node, excluding COMMENT_NODE and
+     * PROCESSING_INSTRUCTION_NODE nodes</td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
+     * CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * <code>nodeValue</code></td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * null</td>
+     * </tr>
+     * </table>
+     * @exception DOMException
+     *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+     * @exception DOMException
+     *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
+     *   fit in a <code>DOMString</code> variable on the implementation
+     *   platform.
+     * @since DOM Level 3
+     */
+    public void setTextContent(String textContent)
+        throws DOMException {
+        setNodeValue(textContent);
+    }
+    
+    /**
+     * This attribute returns the text content of this node and its
+     * descendants. When it is defined to be null, setting it has no effect.
+     * When set, any possible children this node may have are removed and
+     * replaced by a single <code>Text</code> node containing the string
+     * this attribute is set to. On getting, no serialization is performed,
+     * the returned string does not contain any markup. No whitespace
+     * normalization is performed, the returned string does not contain the
+     * element content whitespaces . Similarly, on setting, no parsing is
+     * performed either, the input string is taken as pure textual content.
+     * <br>The string returned is made of the text content of this node
+     * depending on its type, as defined below:
+     * <table border='1'>
+     * <tr>
+     * <th>Node type</th>
+     * <th>Content</th>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
+     * DOCUMENT_FRAGMENT_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
+     * attribute value of every child node, excluding COMMENT_NODE and
+     * PROCESSING_INSTRUCTION_NODE nodes</td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
+     * CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * <code>nodeValue</code></td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * null</td>
+     * </tr>
+     * </table>
+     * @exception DOMException
+     *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+     * @exception DOMException
+     *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
+     *   fit in a <code>DOMString</code> variable on the implementation
+     *   platform.
+     * @since DOM Level 3
+     */
+    public String getTextContent() throws DOMException {
+        return getNodeValue();  // overriden in some subclasses
+    }
+
+    /**
+     * Compares a node with this node with regard to their position in the
+     * document.
+     * @param other The node to compare against this node.
+     * @return Returns how the given node is positioned relatively to this
+     *   node.
+     * @since DOM Level 3
+     */
+    public short compareDocumentPosition(Node other) throws DOMException {
+        return 0;
+    }
+
+    /**
+     * The absolute base URI of this node or <code>null</code> if undefined.
+     * This value is computed according to . However, when the
+     * <code>Document</code> supports the feature "HTML" , the base URI is
+     * computed using first the value of the href attribute of the HTML BASE
+     * element if any, and the value of the <code>documentURI</code>
+     * attribute from the <code>Document</code> interface otherwise.
+     * <br> When the node is an <code>Element</code>, a <code>Document</code>
+     * or a a <code>ProcessingInstruction</code>, this attribute represents
+     * the properties [base URI] defined in . When the node is a
+     * <code>Notation</code>, an <code>Entity</code>, or an
+     * <code>EntityReference</code>, this attribute represents the
+     * properties [declaration base URI] in the . How will this be affected
+     * by resolution of relative namespace URIs issue?It's not.Should this
+     * only be on Document, Element, ProcessingInstruction, Entity, and
+     * Notation nodes, according to the infoset? If not, what is it equal to
+     * on other nodes? Null? An empty string? I think it should be the
+     * parent's.No.Should this be read-only and computed or and actual
+     * read-write attribute?Read-only and computed (F2F 19 Jun 2000 and
+     * teleconference 30 May 2001).If the base HTML element is not yet
+     * attached to a document, does the insert change the Document.baseURI?
+     * Yes. (F2F 26 Sep 2001)
+     * @since DOM Level 3
+     */
+    public String getBaseURI() {
+        return null;
+    }
+}
+
diff --git a/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2DTM.java b/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2DTM.java
new file mode 100644
index 0000000..8033787
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2DTM.java
@@ -0,0 +1,2513 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SAX2DTM.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref.sax2dtm;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+
+import org.apache.xml.dtm.*;
+import org.apache.xml.dtm.ref.*;
+import org.apache.xml.utils.StringVector;
+import org.apache.xml.utils.IntVector;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.IntStack;
+import org.apache.xml.utils.SuballocatedIntVector;
+import org.apache.xml.utils.SystemIDResolver;
+import org.apache.xml.utils.WrappedRuntimeException;
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringFactory;
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+import org.xml.sax.*;
+import org.xml.sax.ext.*;
+
+/**
+ * This class implements a DTM that tends to be optimized more for speed than
+ * for compactness, that is constructed via SAX2 ContentHandler events.
+ */
+public class SAX2DTM extends DTMDefaultBaseIterators
+        implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler,
+                   DeclHandler, LexicalHandler
+{
+  /** Set true to monitor SAX events and similar diagnostic info. */
+  private static final boolean DEBUG = false;
+
+  /**
+   * If we're building the model incrementally on demand, we need to
+   * be able to tell the source when to send us more data.
+   *
+   * Note that if this has not been set, and you attempt to read ahead
+   * of the current build point, we'll probably throw a null-pointer
+   * exception. We could try to wait-and-retry instead, as a very poor
+   * fallback, but that has all the known problems with multithreading
+   * on multiprocessors and we Don't Want to Go There.
+   *
+   * @see setIncrementalSAXSource
+   */
+  private IncrementalSAXSource m_incrementalSAXSource = null;
+
+  /**
+   * All the character content, including attribute values, are stored in
+   * this buffer.
+   *
+   * %REVIEW% Should this have an option of being shared across DTMs?
+   * Sequentially only; not threadsafe... Currently, I think not.
+   *
+   * %REVIEW% Initial size was pushed way down to reduce weight of RTFs.
+   * pending reduction in number of RTF DTMs. Now that we're sharing a DTM
+   * between RTFs, and tail-pruning... consider going back to the larger/faster.
+   *
+   * Made protected rather than private so SAX2RTFDTM can access it.
+   */
+  //private FastStringBuffer m_chars = new FastStringBuffer(13, 13);
+  protected FastStringBuffer m_chars;
+
+  /** This vector holds offset and length data.
+   */
+  protected SuballocatedIntVector m_data;
+
+  /** The parent stack, needed only for construction.
+   * Made protected rather than private so SAX2RTFDTM can access it.
+   */
+  transient protected IntStack m_parents;
+
+  /** The current previous node, needed only for construction time.
+   * Made protected rather than private so SAX2RTFDTM can access it.
+   */
+  transient protected int m_previous = 0;
+
+  /** Namespace support, only relevent at construction time.
+   * Made protected rather than private so SAX2RTFDTM can access it.
+   */
+  transient protected java.util.Vector m_prefixMappings =
+    new java.util.Vector();
+
+  /** Namespace support, only relevent at construction time.
+   * Made protected rather than private so SAX2RTFDTM can access it.
+   */
+  transient protected IntStack m_contextIndexes;
+
+  /** Type of next characters() event within text block in prgress. */
+  transient protected int m_textType = DTM.TEXT_NODE;
+
+  /**
+   * Type of coalesced text block. See logic in the characters()
+   * method.
+   */
+  transient protected int m_coalescedTextType = DTM.TEXT_NODE;
+
+  /** The SAX Document locator */
+  transient protected Locator m_locator = null;
+
+  /** The SAX Document system-id */
+  transient private String m_systemId = null;
+
+  /** We are inside the DTD.  This is used for ignoring comments.  */
+  transient protected boolean m_insideDTD = false;
+
+  /** Tree Walker for dispatchToEvents. */
+  protected DTMTreeWalker m_walker = new DTMTreeWalker();
+
+  /** pool of string values that come as strings. */
+  protected DTMStringPool m_valuesOrPrefixes;
+
+  /** End document has been reached.
+   * Made protected rather than private so SAX2RTFDTM can access it.
+   */
+  protected boolean m_endDocumentOccured = false;
+
+  /** Data or qualified name values, one array element for each node. */
+  protected SuballocatedIntVector m_dataOrQName;
+
+  /**
+   * This table holds the ID string to node associations, for
+   * XML IDs.
+   */
+  protected Hashtable m_idAttributes = new Hashtable();
+
+  /**
+   * fixed dom-style names.
+   */
+  private static final String[] m_fixednames = { null, 
+                    null,  // nothing, Element
+                    null, "#text",  // Attr, Text
+                    "#cdata_section", null,  // CDATA, EntityReference
+                    null, null,  // Entity, PI
+                    "#comment", "#document",  // Comment, Document
+                    null, "#document-fragment",  // Doctype, DocumentFragment
+                    null };  // Notation
+
+  /**
+   * Vector of entities.  Each record is composed of four Strings:
+   *  publicId, systemID, notationName, and name.
+   */
+  private Vector m_entities = null;
+
+  /** m_entities public ID offset. */
+  private static final int ENTITY_FIELD_PUBLICID = 0;
+
+  /** m_entities system ID offset. */
+  private static final int ENTITY_FIELD_SYSTEMID = 1;
+
+  /** m_entities notation name offset. */
+  private static final int ENTITY_FIELD_NOTATIONNAME = 2;
+
+  /** m_entities name offset. */
+  private static final int ENTITY_FIELD_NAME = 3;
+
+  /** Number of entries per record for m_entities. */
+  private static final int ENTITY_FIELDS_PER = 4;
+
+  /**
+   * The starting offset within m_chars for the text or
+   * CDATA_SECTION node currently being acumulated,
+   * or -1 if there is no text node in progress
+   */
+  protected int m_textPendingStart = -1;
+
+  /**
+   * Describes whether information about document source location
+   * should be maintained or not.
+   * 
+   * Made protected for access by SAX2RTFDTM.
+   */
+  protected boolean m_useSourceLocationProperty = false;
+
+   /** Made protected for access by SAX2RTFDTM.
+   */
+  protected StringVector m_sourceSystemId;
+   /** Made protected for access by SAX2RTFDTM.
+   */
+  protected IntVector m_sourceLine;
+   /** Made protected for access by SAX2RTFDTM.
+   */
+  protected IntVector m_sourceColumn;
+  
+  /**
+   * Construct a SAX2DTM object using the default block size.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param source the JAXP 1.1 Source object for this DTM.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may
+   *                         be null.
+   * @param xstringfactory XMLString factory for creating character content.
+   * @param doIndexing true if the caller considers it worth it to use
+   *                   indexing schemes.
+   */
+  public SAX2DTM(DTMManager mgr, Source source, int dtmIdentity,
+                 DTMWSFilter whiteSpaceFilter,
+                 XMLStringFactory xstringfactory,
+                 boolean doIndexing)
+  {
+
+    this(mgr, source, dtmIdentity, whiteSpaceFilter,
+          xstringfactory, doIndexing, DEFAULT_BLOCKSIZE, true, false);
+  }
+  
+  /**
+   * Construct a SAX2DTM object ready to be constructed from SAX2
+   * ContentHandler events.
+   *
+   * @param mgr The DTMManager who owns this DTM.
+   * @param source the JAXP 1.1 Source object for this DTM.
+   * @param dtmIdentity The DTM identity ID for this DTM.
+   * @param whiteSpaceFilter The white space filter for this DTM, which may
+   *                         be null.
+   * @param xstringfactory XMLString factory for creating character content.
+   * @param doIndexing true if the caller considers it worth it to use
+   *                   indexing schemes.
+   * @param blocksize The block size of the DTM.
+   * @param usePrevsib true if we want to build the previous sibling node array.
+   * @param newNameTable true if we want to use a new ExpandedNameTable for this DTM.
+   */
+  public SAX2DTM(DTMManager mgr, Source source, int dtmIdentity,
+                 DTMWSFilter whiteSpaceFilter,
+                 XMLStringFactory xstringfactory,
+                 boolean doIndexing,
+                 int blocksize,
+                 boolean usePrevsib,
+                 boolean newNameTable)
+  {
+
+    super(mgr, source, dtmIdentity, whiteSpaceFilter,
+          xstringfactory, doIndexing, blocksize, usePrevsib, newNameTable);
+
+    // %OPT% Use smaller sizes for all internal storage units when
+    // the blocksize is small. This reduces the cost of creating an RTF.
+    if (blocksize <= 64) 
+    {
+      m_data = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS_SMALL);
+      m_dataOrQName = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS_SMALL);
+      m_valuesOrPrefixes = new DTMStringPool(16);
+      m_chars = new FastStringBuffer(7, 10);
+      m_contextIndexes = new IntStack(4);
+      m_parents = new IntStack(4);
+    }
+    else
+    {
+      m_data = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS);
+      m_dataOrQName = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS);
+      m_valuesOrPrefixes = new DTMStringPool();
+      m_chars = new FastStringBuffer(10, 13);
+      m_contextIndexes = new IntStack();
+      m_parents = new IntStack();
+    }
+         
+    // %REVIEW%  Initial size pushed way down to reduce weight of RTFs
+    // (I'm not entirely sure 0 would work, so I'm playing it safe for now.)
+    //m_data = new SuballocatedIntVector(doIndexing ? (1024*2) : 512, 1024);
+    //m_data = new SuballocatedIntVector(blocksize);
+
+    m_data.addElement(0);   // Need placeholder in case index into here must be <0.
+
+    //m_dataOrQName = new SuballocatedIntVector(blocksize);
+    
+    // m_useSourceLocationProperty=org.apache.xalan.processor.TransformerFactoryImpl.m_source_location;
+    m_useSourceLocationProperty = mgr.getSource_location();
+    m_sourceSystemId = (m_useSourceLocationProperty) ? new StringVector() : null;
+ 	m_sourceLine = (m_useSourceLocationProperty) ?  new IntVector() : null;
+    m_sourceColumn = (m_useSourceLocationProperty) ?  new IntVector() : null; 
+  }
+
+  /**
+   * Set whether information about document source location
+   * should be maintained or not. 
+   */
+  public void setUseSourceLocation(boolean useSourceLocation)
+  {
+    m_useSourceLocationProperty = useSourceLocation;
+  }
+
+  /**
+   * Get the data or qualified name for the given node identity.
+   *
+   * @param identity The node identity.
+   *
+   * @return The data or qualified name, or DTM.NULL.
+   */
+  protected int _dataOrQName(int identity)
+  {
+
+    if (identity < m_size)
+      return m_dataOrQName.elementAt(identity);
+
+    // Check to see if the information requested has been processed, and,
+    // if not, advance the iterator until we the information has been
+    // processed.
+    while (true)
+    {
+      boolean isMore = nextNode();
+
+      if (!isMore)
+        return NULL;
+      else if (identity < m_size)
+        return m_dataOrQName.elementAt(identity);
+    }
+  }
+
+  /**
+   * Ask the CoRoutine parser to doTerminate and clear the reference.
+   */
+  public void clearCoRoutine()
+  {
+    clearCoRoutine(true);
+  }
+
+  /**
+   * Ask the CoRoutine parser to doTerminate and clear the reference. If
+   * the CoRoutine parser has already been cleared, this will have no effect.
+   *
+   * @param callDoTerminate true of doTerminate should be called on the
+   * coRoutine parser.
+   */
+  public void clearCoRoutine(boolean callDoTerminate)
+  {
+
+    if (null != m_incrementalSAXSource)
+    {
+      if (callDoTerminate)
+        m_incrementalSAXSource.deliverMoreNodes(false);
+
+      m_incrementalSAXSource = null;
+    }
+  }
+
+  /**
+   * Bind a IncrementalSAXSource to this DTM. If we discover we need nodes
+   * that have not yet been built, we will ask this object to send us more
+   * events, and it will manage interactions with its data sources.
+   *
+   * Note that we do not actually build the IncrementalSAXSource, since we don't
+   * know what source it's reading from, what thread that source will run in,
+   * or when it will run.
+   *
+   * @param incrementalSAXSource The parser that we want to recieve events from
+   * on demand.
+   */
+  public void setIncrementalSAXSource(IncrementalSAXSource incrementalSAXSource)
+  {
+
+    // Establish coroutine link so we can request more data
+    //
+    // Note: It's possible that some versions of IncrementalSAXSource may
+    // not actually use a CoroutineManager, and hence may not require
+    // that we obtain an Application Coroutine ID. (This relies on the
+    // coroutine transaction details having been encapsulated in the
+    // IncrementalSAXSource.do...() methods.)
+    m_incrementalSAXSource = incrementalSAXSource;
+
+    // Establish SAX-stream link so we can receive the requested data
+    incrementalSAXSource.setContentHandler(this);
+    incrementalSAXSource.setLexicalHandler(this);
+    incrementalSAXSource.setDTDHandler(this);
+
+    // Are the following really needed? incrementalSAXSource doesn't yet
+    // support them, and they're mostly no-ops here...
+    //incrementalSAXSource.setErrorHandler(this);
+    //incrementalSAXSource.setDeclHandler(this);
+  }
+
+  /**
+   * getContentHandler returns "our SAX builder" -- the thing that
+   * someone else should send SAX events to in order to extend this
+   * DTM model.
+   *
+   * %REVIEW% Should this return null if constrution already done/begun?
+   *
+   * @return null if this model doesn't respond to SAX events,
+   * "this" if the DTM object has a built-in SAX ContentHandler,
+   * the IncrementalSAXSource if we're bound to one and should receive
+   * the SAX stream via it for incremental build purposes...
+   */
+  public ContentHandler getContentHandler()
+  {
+
+    if (m_incrementalSAXSource instanceof IncrementalSAXSource_Filter)
+      return (ContentHandler) m_incrementalSAXSource;
+    else
+      return this;
+  }
+
+  /**
+   * Return this DTM's lexical handler.
+   *
+   * %REVIEW% Should this return null if constrution already done/begun?
+   *
+   * @return null if this model doesn't respond to lexical SAX events,
+   * "this" if the DTM object has a built-in SAX ContentHandler,
+   * the IncrementalSAXSource if we're bound to one and should receive
+   * the SAX stream via it for incremental build purposes...
+   */
+  public LexicalHandler getLexicalHandler()
+  {
+
+    if (m_incrementalSAXSource instanceof IncrementalSAXSource_Filter)
+      return (LexicalHandler) m_incrementalSAXSource;
+    else
+      return this;
+  }
+
+  /**
+   * Return this DTM's EntityResolver.
+   *
+   * @return null if this model doesn't respond to SAX entity ref events.
+   */
+  public EntityResolver getEntityResolver()
+  {
+    return this;
+  }
+
+  /**
+   * Return this DTM's DTDHandler.
+   *
+   * @return null if this model doesn't respond to SAX dtd events.
+   */
+  public DTDHandler getDTDHandler()
+  {
+    return this;
+  }
+
+  /**
+   * Return this DTM's ErrorHandler.
+   *
+   * @return null if this model doesn't respond to SAX error events.
+   */
+  public ErrorHandler getErrorHandler()
+  {
+    return this;
+  }
+
+  /**
+   * Return this DTM's DeclHandler.
+   *
+   * @return null if this model doesn't respond to SAX Decl events.
+   */
+  public DeclHandler getDeclHandler()
+  {
+    return this;
+  }
+
+  /**
+   * @return true iff we're building this model incrementally (eg
+   * we're partnered with a IncrementalSAXSource) and thus require that the
+   * transformation and the parse run simultaneously. Guidance to the
+   * DTMManager.
+   */
+  public boolean needsTwoThreads()
+  {
+    return null != m_incrementalSAXSource;
+  }
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value). Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   * @param normalize true if the content should be normalized according to
+   * the rules for the XPath
+   * <a href="http://www.w3.org/TR/xpath#function-normalize-space">normalize-space</a>
+   * function.
+   *
+   * @throws SAXException
+   */
+  public void dispatchCharactersEvents(int nodeHandle, ContentHandler ch,
+                                       boolean normalize)
+          throws SAXException
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+    
+    if (identity == DTM.NULL)
+      return;
+    
+    int type = _type(identity);
+
+    if (isTextType(type))
+    {
+      int dataIndex = m_dataOrQName.elementAt(identity);
+      int offset = m_data.elementAt(dataIndex);
+      int length = m_data.elementAt(dataIndex + 1);
+
+      if(normalize)
+        m_chars.sendNormalizedSAXcharacters(ch, offset, length);
+      else
+        m_chars.sendSAXcharacters(ch, offset, length);
+    }
+    else
+    {
+      int firstChild = _firstch(identity);
+
+      if (DTM.NULL != firstChild)
+      {
+        int offset = -1;
+        int length = 0;
+        int startNode = identity;
+
+        identity = firstChild;
+
+        do {
+          type = _type(identity);
+
+          if (isTextType(type))
+          {
+            int dataIndex = _dataOrQName(identity);
+
+            if (-1 == offset)
+            {
+              offset = m_data.elementAt(dataIndex);
+            }
+
+            length += m_data.elementAt(dataIndex + 1);
+          }
+
+          identity = getNextNodeIdentity(identity);
+        } while (DTM.NULL != identity && (_parent(identity) >= startNode));
+
+        if (length > 0)
+        {
+          if(normalize)
+            m_chars.sendNormalizedSAXcharacters(ch, offset, length);
+          else
+            m_chars.sendSAXcharacters(ch, offset, length);
+        }
+      }
+      else if(type != DTM.ELEMENT_NODE)
+      {
+        int dataIndex = _dataOrQName(identity);
+
+        if (dataIndex < 0)
+        {
+          dataIndex = -dataIndex;
+          dataIndex = m_data.elementAt(dataIndex + 1);
+        }
+
+        String str = m_valuesOrPrefixes.indexToString(dataIndex);
+
+          if(normalize)
+            FastStringBuffer.sendNormalizedSAXcharacters(str.toCharArray(),
+                                                         0, str.length(), ch);
+          else
+            ch.characters(str.toCharArray(), 0, str.length());
+      }
+    }
+  }
+
+
+  /**
+   * Given a node handle, return its DOM-style node name. This will
+   * include names such as #text or #document.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   * %REVIEW% Document when empty string is possible...
+   * %REVIEW-COMMENT% It should never be empty, should it?
+   */
+  public String getNodeName(int nodeHandle)
+  {
+
+    int expandedTypeID = getExpandedTypeID(nodeHandle);
+    // If just testing nonzero, no need to shift...
+    int namespaceID = m_expandedNameTable.getNamespaceID(expandedTypeID);                     
+
+    if (0 == namespaceID)
+    {
+      // Don't retrieve name until/unless needed
+      // String name = m_expandedNameTable.getLocalName(expandedTypeID);
+      int type = getNodeType(nodeHandle);
+
+      if (type == DTM.NAMESPACE_NODE)
+      {
+        if (null == m_expandedNameTable.getLocalName(expandedTypeID))
+          return "xmlns";
+        else
+          return "xmlns:" + m_expandedNameTable.getLocalName(expandedTypeID);
+      }
+      else if (0 == m_expandedNameTable.getLocalNameID(expandedTypeID))
+      {
+        return m_fixednames[type];
+      }
+      else
+        return m_expandedNameTable.getLocalName(expandedTypeID);
+    }
+    else
+    {
+      int qnameIndex = m_dataOrQName.elementAt(makeNodeIdentity(nodeHandle));
+
+      if (qnameIndex < 0)
+      {
+        qnameIndex = -qnameIndex;
+        qnameIndex = m_data.elementAt(qnameIndex);
+      }
+
+      return m_valuesOrPrefixes.indexToString(qnameIndex);
+    }
+  }
+
+  /**
+   * Given a node handle, return the XPath node name.  This should be
+   * the name as described by the XPath data model, NOT the DOM-style
+   * name.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   */
+  public String getNodeNameX(int nodeHandle)
+  {
+
+    int expandedTypeID = getExpandedTypeID(nodeHandle);    
+    int namespaceID = m_expandedNameTable.getNamespaceID(expandedTypeID);                      
+
+    if (0 == namespaceID)
+    {
+      String name = m_expandedNameTable.getLocalName(expandedTypeID);
+
+      if (name == null)
+        return "";
+      else
+        return name;
+    }
+    else
+    {
+      int qnameIndex = m_dataOrQName.elementAt(makeNodeIdentity(nodeHandle));
+
+      if (qnameIndex < 0)
+      {
+        qnameIndex = -qnameIndex;
+        qnameIndex = m_data.elementAt(qnameIndex);
+      }
+
+      return m_valuesOrPrefixes.indexToString(qnameIndex);
+    }
+  }
+
+  /**
+   *     5. [specified] A flag indicating whether this attribute was actually
+   *        specified in the start-tag of its element, or was defaulted from the
+   *        DTD.
+   *
+   * @param attributeHandle Must be a valid handle to an attribute node.
+   * @return <code>true</code> if the attribute was specified;
+   *         <code>false</code> if it was defaulted.
+   */
+  public boolean isAttributeSpecified(int attributeHandle)
+  {
+
+    // I'm not sure if I want to do anything with this...
+    return true;  // ??
+  }
+
+  /**
+   *   A document type declaration information item has the following properties:
+   *
+   *     1. [system identifier] The system identifier of the external subset, if
+   *        it exists. Otherwise this property has no value.
+   *
+   * @return the system identifier String object, or null if there is none.
+   */
+  public String getDocumentTypeDeclarationSystemIdentifier()
+  {
+
+    /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */
+    error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!");
+
+    return null;
+  }
+
+  /**
+   * Get the next node identity value in the list, and call the iterator
+   * if it hasn't been added yet.
+   *
+   * @param identity The node identity (index).
+   * @return identity+1, or DTM.NULL.
+   */
+  protected int getNextNodeIdentity(int identity)
+  {
+
+    identity += 1;
+
+    while (identity >= m_size)
+    {
+      if (null == m_incrementalSAXSource)
+        return DTM.NULL;
+
+      nextNode();
+    }
+
+    return identity;
+  }
+
+  /**
+   * Directly create SAX parser events from a subtree.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch)
+          throws org.xml.sax.SAXException
+  {
+
+    DTMTreeWalker treeWalker = m_walker;
+    ContentHandler prevCH = treeWalker.getcontentHandler();
+
+    if (null != prevCH)
+    {
+      treeWalker = new DTMTreeWalker();
+    }
+
+    treeWalker.setcontentHandler(ch);
+    treeWalker.setDTM(this);
+
+    try
+    {
+      treeWalker.traverse(nodeHandle);
+    }
+    finally
+    {
+      treeWalker.setcontentHandler(null);
+    }
+  }
+
+  /**
+   * Get the number of nodes that have been added.
+   *
+   * @return The number of that are currently in the tree.
+   */
+  public int getNumberOfNodes()
+  {
+    return m_size;
+  }
+
+  /**
+   * This method should try and build one or more nodes in the table.
+   *
+   * @return The true if a next node is found or false if
+   *         there are no more nodes.
+   */
+  protected boolean nextNode()
+  {
+
+    if (null == m_incrementalSAXSource)
+      return false;
+
+    if (m_endDocumentOccured)
+    {
+      clearCoRoutine();
+
+      return false;
+    }
+
+    Object gotMore = m_incrementalSAXSource.deliverMoreNodes(true);
+
+    // gotMore may be a Boolean (TRUE if still parsing, FALSE if
+    // EOF) or an exception if IncrementalSAXSource malfunctioned
+    // (code error rather than user error).
+    //
+    // %REVIEW% Currently the ErrorHandlers sketched herein are
+    // no-ops, so I'm going to initially leave this also as a
+    // no-op.
+    if (!(gotMore instanceof Boolean))
+    {
+      if(gotMore instanceof RuntimeException)
+      {
+        throw (RuntimeException)gotMore;
+      }
+      else if(gotMore instanceof Exception)
+      {
+        throw new WrappedRuntimeException((Exception)gotMore);
+      }
+      // for now...
+      clearCoRoutine();
+
+      return false;
+
+      // %TBD%
+    }
+
+    if (gotMore != Boolean.TRUE)
+    {
+
+      // EOF reached without satisfying the request
+      clearCoRoutine();  // Drop connection, stop trying
+
+      // %TBD% deregister as its listener?
+    }
+
+    return true;
+  }
+
+  /**
+   * Bottleneck determination of text type.
+   *
+   * @param type oneof DTM.XXX_NODE.
+   *
+   * @return true if this is a text or cdata section.
+   */
+  private final boolean isTextType(int type)
+  {
+    return (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type);
+  }
+
+//    /**
+//     * Ensure that the size of the information arrays can hold another entry
+//     * at the given index.
+//     *
+//     * @param on exit from this function, the information arrays sizes must be
+//     * at least index+1.
+//     *
+//     * NEEDSDOC @param index
+//     */
+//    protected void ensureSize(int index)
+//    {
+//          // dataOrQName is an SuballocatedIntVector and hence self-sizing.
+//          // But DTMDefaultBase may need fixup.
+//        super.ensureSize(index);
+//    }
+
+  /**
+   * Construct the node map from the node.
+   *
+   * @param type raw type ID, one of DTM.XXX_NODE.
+   * @param expandedTypeID The expended type ID.
+   * @param parentIndex The current parent index.
+   * @param previousSibling The previous sibling index.
+   * @param dataOrPrefix index into m_data table, or string handle.
+   * @param canHaveFirstChild true if the node can have a first child, false
+   *                          if it is atomic.
+   *
+   * @return The index identity of the node that was added.
+   */
+  protected int addNode(int type, int expandedTypeID,
+                        int parentIndex, int previousSibling,
+                        int dataOrPrefix, boolean canHaveFirstChild)
+  {
+    // Common to all nodes:
+    int nodeIndex = m_size++;
+
+    // Have we overflowed a DTM Identity's addressing range?
+    if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS))
+    {
+      addNewDTMID(nodeIndex);
+    }
+
+    m_firstch.addElement(canHaveFirstChild ? NOTPROCESSED : DTM.NULL);
+    m_nextsib.addElement(NOTPROCESSED);
+    m_parent.addElement(parentIndex);
+    m_exptype.addElement(expandedTypeID);
+    m_dataOrQName.addElement(dataOrPrefix);
+
+    if (m_prevsib != null) {
+      m_prevsib.addElement(previousSibling);
+    }
+
+    if (DTM.NULL != previousSibling) {
+      m_nextsib.setElementAt(nodeIndex,previousSibling);
+    }
+
+    if (m_locator != null && m_useSourceLocationProperty) {
+      setSourceLocation();
+    }
+
+    // Note that nextSibling is not processed until charactersFlush()
+    // is called, to handle successive characters() events.
+
+    // Special handling by type: Declare namespaces, attach first child
+    switch(type)
+    {
+    case DTM.NAMESPACE_NODE:
+      declareNamespaceInContext(parentIndex,nodeIndex);
+      break;
+    case DTM.ATTRIBUTE_NODE:
+      break;
+    default:
+      if (DTM.NULL == previousSibling && DTM.NULL != parentIndex) {
+        m_firstch.setElementAt(nodeIndex,parentIndex);
+      }
+      break;
+    }
+
+    return nodeIndex;
+  }
+
+  /**
+   * Get a new DTM ID beginning at the specified node index.
+   * @param  nodeIndex The node identity at which the new DTM ID will begin
+   * addressing.
+   */
+  protected void addNewDTMID(int nodeIndex) {
+    try
+    {
+      if(m_mgr==null)
+        throw new ClassCastException();
+                              
+                              // Handle as Extended Addressing
+      DTMManagerDefault mgrD=(DTMManagerDefault)m_mgr;
+      int id=mgrD.getFirstFreeDTMID();
+      mgrD.addDTM(this,id,nodeIndex);
+      m_dtmIdent.addElement(id<<DTMManager.IDENT_DTM_NODE_BITS);
+    }
+    catch(ClassCastException e)
+    {
+      // %REVIEW% Wrong error message, but I've been told we're trying
+      // not to add messages right not for I18N reasons.
+      // %REVIEW% Should this be a Fatal Error?
+      error(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null));//"No more DTM IDs are available";
+    }
+  }
+
+  /**
+    * Migrate a DTM built with an old DTMManager to a new DTMManager.
+    * After the migration, the new DTMManager will treat the DTM as
+    * one that is built by itself.
+    * This is used to support DTM sharing between multiple transformations.
+    * @param manager the DTMManager
+    */
+  public void migrateTo(DTMManager manager) {
+    super.migrateTo(manager);
+    
+    // We have to reset the information in m_dtmIdent and
+    // register the DTM with the new manager. 
+    int numDTMs = m_dtmIdent.size();
+    int dtmId = m_mgrDefault.getFirstFreeDTMID();
+    int nodeIndex = 0;
+    for (int i = 0; i < numDTMs; i++)
+    {     
+      m_dtmIdent.setElementAt(dtmId << DTMManager.IDENT_DTM_NODE_BITS, i);
+      m_mgrDefault.addDTM(this, dtmId, nodeIndex);
+      dtmId++;
+      nodeIndex += (1 << DTMManager.IDENT_DTM_NODE_BITS);
+    }
+  }
+
+  /**
+   * Store the source location of the current node.  This method must be called
+   * as every node is added to the DTM or for no node.
+   */
+  protected void setSourceLocation() {
+    m_sourceSystemId.addElement(m_locator.getSystemId());
+    m_sourceLine.addElement(m_locator.getLineNumber());
+    m_sourceColumn.addElement(m_locator.getColumnNumber());
+
+    //%REVIEW% %BUG% Prevent this from arising in the first place
+    // by not allowing the enabling conditions to change after we start
+    // building the document.
+    if (m_sourceSystemId.size() != m_size) {
+        String msg = "CODING ERROR in Source Location: " + m_size + " != "
+                    + m_sourceSystemId.size();
+        System.err.println(msg);
+        throw new RuntimeException(msg);
+    }
+  }
+
+  /**
+   * Given a node handle, return its node value. This is mostly
+   * as defined by the DOM, but may ignore some conveniences.
+   * <p>
+   *
+   * @param nodeHandle The node id.
+   * @return String Value of this node, or null if not
+   * meaningful for this node type.
+   */
+  public String getNodeValue(int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+    int type = _type(identity);
+
+    if (isTextType(type))
+    {
+      int dataIndex = _dataOrQName(identity);
+      int offset = m_data.elementAt(dataIndex);
+      int length = m_data.elementAt(dataIndex + 1);
+
+      // %OPT% We should cache this, I guess.
+      return m_chars.getString(offset, length);
+    }
+    else if (DTM.ELEMENT_NODE == type || DTM.DOCUMENT_FRAGMENT_NODE == type
+             || DTM.DOCUMENT_NODE == type)
+    {
+      return null;
+    }
+    else
+    {
+      int dataIndex = _dataOrQName(identity);
+
+      if (dataIndex < 0)
+      {
+        dataIndex = -dataIndex;
+        dataIndex = m_data.elementAt(dataIndex + 1);
+      }
+
+      return m_valuesOrPrefixes.indexToString(dataIndex);
+    }
+  }
+
+  /**
+   * Given a node handle, return its XPath-style localname.
+   * (As defined in Namespaces, this is the portion of the name after any
+   * colon character).
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Local name of this node.
+   */
+  public String getLocalName(int nodeHandle)
+  {
+    return m_expandedNameTable.getLocalName(_exptype(makeNodeIdentity(nodeHandle)));
+  }
+
+  /**
+   * The getUnparsedEntityURI function returns the URI of the unparsed
+   * entity with the specified name in the same document as the context
+   * node (see [3.3 Unparsed Entities]). It returns the empty string if
+   * there is no such entity.
+   * <p>
+   * XML processors may choose to use the System Identifier (if one
+   * is provided) to resolve the entity, rather than the URI in the
+   * Public Identifier. The details are dependent on the processor, and
+   * we would have to support some form of plug-in resolver to handle
+   * this properly. Currently, we simply return the System Identifier if
+   * present, and hope that it a usable URI or that our caller can
+   * map it to one.
+   * TODO: Resolve Public Identifiers... or consider changing function name.
+   * <p>
+   * If we find a relative URI
+   * reference, XML expects it to be resolved in terms of the base URI
+   * of the document. The DOM doesn't do that for us, and it isn't
+   * entirely clear whether that should be done here; currently that's
+   * pushed up to a higher level of our application. (Note that DOM Level
+   * 1 didn't store the document's base URI.)
+   * TODO: Consider resolving Relative URIs.
+   * <p>
+   * (The DOM's statement that "An XML processor may choose to
+   * completely expand entities before the structure model is passed
+   * to the DOM" refers only to parsed entities, not unparsed, and hence
+   * doesn't affect this function.)
+   *
+   * @param name A string containing the Entity Name of the unparsed
+   * entity.
+   *
+   * @return String containing the URI of the Unparsed Entity, or an
+   * empty string if no such entity exists.
+   */
+  public String getUnparsedEntityURI(String name)
+  {
+
+    String url = "";
+
+    if (null == m_entities)
+      return url;
+
+    int n = m_entities.size();
+
+    for (int i = 0; i < n; i += ENTITY_FIELDS_PER)
+    {
+      String ename = (String) m_entities.elementAt(i + ENTITY_FIELD_NAME);
+
+      if (null != ename && ename.equals(name))
+      {
+        String nname = (String) m_entities.elementAt(i
+                         + ENTITY_FIELD_NOTATIONNAME);
+
+        if (null != nname)
+        {
+
+          // The draft says: "The XSLT processor may use the public
+          // identifier to generate a URI for the entity instead of the URI
+          // specified in the system identifier. If the XSLT processor does
+          // not use the public identifier to generate the URI, it must use
+          // the system identifier; if the system identifier is a relative
+          // URI, it must be resolved into an absolute URI using the URI of
+          // the resource containing the entity declaration as the base
+          // URI [RFC2396]."
+          // So I'm falling a bit short here.
+          url = (String) m_entities.elementAt(i + ENTITY_FIELD_SYSTEMID);
+
+          if (null == url)
+          {
+            url = (String) m_entities.elementAt(i + ENTITY_FIELD_PUBLICID);
+          }
+        }
+
+        break;
+      }
+    }
+
+    return url;
+  }
+
+  /**
+   * Given a namespace handle, return the prefix that the namespace decl is
+   * mapping.
+   * Given a node handle, return the prefix used to map to the namespace.
+   *
+   * <p> %REVIEW% Are you sure you want "" for no prefix?  </p>
+   * <p> %REVIEW-COMMENT% I think so... not totally sure. -sb  </p>
+   *
+   * @param nodeHandle the id of the node.
+   * @return String prefix of this node's name, or "" if no explicit
+   * namespace prefix was given.
+   */
+  public String getPrefix(int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+    int type = _type(identity);
+
+    if (DTM.ELEMENT_NODE == type)
+    {
+      int prefixIndex = _dataOrQName(identity);
+
+      if (0 == prefixIndex)
+        return "";
+      else
+      {
+        String qname = m_valuesOrPrefixes.indexToString(prefixIndex);
+
+        return getPrefix(qname, null);
+      }
+    }
+    else if (DTM.ATTRIBUTE_NODE == type)
+    {
+      int prefixIndex = _dataOrQName(identity);
+
+      if (prefixIndex < 0)
+      {
+        prefixIndex = m_data.elementAt(-prefixIndex);
+
+        String qname = m_valuesOrPrefixes.indexToString(prefixIndex);
+
+        return getPrefix(qname, null);
+      }
+    }
+
+    return "";
+  }
+
+  /**
+   * Retrieves an attribute node by by qualified name and namespace URI.
+   *
+   * @param nodeHandle int Handle of the node upon which to look up this attribute..
+   * @param namespaceURI The namespace URI of the attribute to
+   *   retrieve, or null.
+   * @param name The local name of the attribute to
+   *   retrieve.
+   * @return The attribute node handle with the specified name (
+   *   <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
+   *   attribute.
+   */
+  public int getAttributeNode(int nodeHandle, String namespaceURI,
+                              String name)
+  {
+
+    for (int attrH = getFirstAttribute(nodeHandle); DTM.NULL != attrH;
+            attrH = getNextAttribute(attrH))
+    {
+      String attrNS = getNamespaceURI(attrH);
+      String attrName = getLocalName(attrH);
+      boolean nsMatch = namespaceURI == attrNS
+                        || (namespaceURI != null
+                            && namespaceURI.equals(attrNS));
+
+      if (nsMatch && name.equals(attrName))
+        return attrH;
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * Return the public identifier of the external subset,
+   * normalized as described in 4.2.2 External Entities [XML]. If there is
+   * no external subset or if it has no public identifier, this property
+   * has no value.
+   *
+   * @return the public identifier String object, or null if there is none.
+   */
+  public String getDocumentTypeDeclarationPublicIdentifier()
+  {
+
+    /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */
+    error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!");
+
+    return null;
+  }
+
+  /**
+   * Given a node handle, return its DOM-style namespace URI
+   * (As defined in Namespaces, this is the declared URI which this node's
+   * prefix -- or default in lieu thereof -- was mapped to.)
+   *
+   * <p>%REVIEW% Null or ""? -sb</p>
+   *
+   * @param nodeHandle the id of the node.
+   * @return String URI value of this node's namespace, or null if no
+   * namespace was resolved.
+   */
+  public String getNamespaceURI(int nodeHandle)
+  {
+
+    return m_expandedNameTable.getNamespace(_exptype(makeNodeIdentity(nodeHandle)));
+  }
+
+  /**
+   * Get the string-value of a node as a String object
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A string object that represents the string-value of the given node.
+   */
+  public XMLString getStringValue(int nodeHandle)
+  {
+    int identity = makeNodeIdentity(nodeHandle);
+    int type;
+    if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint it
+      type = DTM.NULL;
+    else
+      type= _type(identity);
+
+    if (isTextType(type))
+    {
+      int dataIndex = _dataOrQName(identity);
+      int offset = m_data.elementAt(dataIndex);
+      int length = m_data.elementAt(dataIndex + 1);
+
+      return m_xstrf.newstr(m_chars, offset, length);
+    }
+    else
+    {
+      int firstChild = _firstch(identity);
+
+      if (DTM.NULL != firstChild)
+      {
+        int offset = -1;
+        int length = 0;
+        int startNode = identity;
+
+        identity = firstChild;
+
+        do {
+          type = _type(identity);
+
+          if (isTextType(type))
+          {
+            int dataIndex = _dataOrQName(identity);
+
+            if (-1 == offset)
+            {
+              offset = m_data.elementAt(dataIndex);
+            }
+
+            length += m_data.elementAt(dataIndex + 1);
+          }
+
+          identity = getNextNodeIdentity(identity);
+        } while (DTM.NULL != identity && (_parent(identity) >= startNode));
+
+        if (length > 0)
+        {
+          return m_xstrf.newstr(m_chars, offset, length);
+        }
+      }
+      else if(type != DTM.ELEMENT_NODE)
+      {
+        int dataIndex = _dataOrQName(identity);
+
+        if (dataIndex < 0)
+        {
+          dataIndex = -dataIndex;
+          dataIndex = m_data.elementAt(dataIndex + 1);
+        }
+        return m_xstrf.newstr(m_valuesOrPrefixes.indexToString(dataIndex));
+      }
+    }
+
+    return m_xstrf.emptystr();
+  }
+  
+  /**
+   * Determine if the string-value of a node is whitespace
+   *
+   * @param nodeHandle The node Handle.
+   *
+   * @return Return true if the given node is whitespace.
+   */
+  public boolean isWhitespace(int nodeHandle)
+  {
+    int identity = makeNodeIdentity(nodeHandle);
+    int type;
+    if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint it
+      type = DTM.NULL;
+    else
+      type= _type(identity);
+
+    if (isTextType(type))
+    {
+      int dataIndex = _dataOrQName(identity);
+      int offset = m_data.elementAt(dataIndex);
+      int length = m_data.elementAt(dataIndex + 1);
+
+      return m_chars.isWhitespace(offset, length);
+    }
+    return false;
+  }
+
+  /**
+   * Returns the <code>Element</code> whose <code>ID</code> is given by
+   * <code>elementId</code>. If no such element exists, returns
+   * <code>DTM.NULL</code>. Behavior is not defined if more than one element
+   * has this <code>ID</code>. Attributes (including those
+   * with the name "ID") are not of type ID unless so defined by DTD/Schema
+   * information available to the DTM implementation.
+   * Implementations that do not know whether attributes are of type ID or
+   * not are expected to return <code>DTM.NULL</code>.
+   *
+   * <p>%REVIEW% Presumably IDs are still scoped to a single document,
+   * and this operation searches only within a single document, right?
+   * Wouldn't want collisions between DTMs in the same process.</p>
+   *
+   * @param elementId The unique <code>id</code> value for an element.
+   * @return The handle of the matching element.
+   */
+  public int getElementById(String elementId)
+  {
+
+    Integer intObj;
+    boolean isMore = true;
+
+    do
+    {
+      intObj = (Integer) m_idAttributes.get(elementId);
+
+      if (null != intObj)
+        return makeNodeHandle(intObj.intValue());
+
+      if (!isMore || m_endDocumentOccured)
+        break;
+
+      isMore = nextNode();
+    }
+    while (null == intObj);
+
+    return DTM.NULL;
+  }
+
+  /**
+   * Get a prefix either from the qname or from the uri mapping, or just make
+   * one up!
+   *
+   * @param qname The qualified name, which may be null.
+   * @param uri The namespace URI, which may be null.
+   *
+   * @return The prefix if there is one, or null.
+   */
+  public String getPrefix(String qname, String uri)
+  {
+
+    String prefix;
+    int uriIndex = -1;
+
+    if (null != uri && uri.length() > 0)
+    {
+
+      do
+      {
+        uriIndex = m_prefixMappings.indexOf(uri, ++uriIndex);
+      } while ( (uriIndex & 0x01) == 0);
+
+      if (uriIndex >= 0)
+      {
+        prefix = (String) m_prefixMappings.elementAt(uriIndex - 1);
+      }
+      else if (null != qname)
+      {
+        int indexOfNSSep = qname.indexOf(':');
+
+        if (qname.equals("xmlns"))
+          prefix = "";
+        else if (qname.startsWith("xmlns:"))
+          prefix = qname.substring(indexOfNSSep + 1);
+        else
+          prefix = (indexOfNSSep > 0)
+                   ? qname.substring(0, indexOfNSSep) : null;
+      }
+      else
+      {
+        prefix = null;
+      }
+    }
+    else if (null != qname)
+    {
+      int indexOfNSSep = qname.indexOf(':');
+
+      if (indexOfNSSep > 0)
+      {
+        if (qname.startsWith("xmlns:"))
+          prefix = qname.substring(indexOfNSSep + 1);
+        else
+          prefix = qname.substring(0, indexOfNSSep);	
+      }
+      else
+      {
+      	if (qname.equals("xmlns"))
+      	  prefix = "";
+      	else
+      	  prefix = null;
+      }
+    }
+    else
+    {
+      prefix = null;
+    }
+
+    return prefix;
+  }
+  
+  /**
+   * Get a prefix either from the uri mapping, or just make
+   * one up!
+   *
+   * @param uri The namespace URI, which may be null.
+   *
+   * @return The prefix if there is one, or null.
+   */
+  public int getIdForNamespace(String uri)
+  {
+
+     return m_valuesOrPrefixes.stringToIndex(uri);
+    
+  }
+
+    /**
+   * Get a prefix either from the qname or from the uri mapping, or just make
+   * one up!
+   *
+   * @return The prefix if there is one, or null.
+   */
+  public String getNamespaceURI(String prefix)
+  {
+
+    String uri = "";
+    int prefixIndex = m_contextIndexes.peek() - 1 ;
+
+    if(null == prefix)
+      prefix = "";
+
+      do
+      {
+        prefixIndex = m_prefixMappings.indexOf(prefix, ++prefixIndex);
+      } while ( (prefixIndex >= 0) && (prefixIndex & 0x01) == 0x01);
+
+      if (prefixIndex > -1)
+      {
+        uri = (String) m_prefixMappings.elementAt(prefixIndex + 1);
+      }
+
+
+    return uri;
+  }
+
+  /**
+   * Set an ID string to node association in the ID table.
+   *
+   * @param id The ID string.
+   * @param elem The associated element handle.
+   */
+  public void setIDAttribute(String id, int elem)
+  {
+    m_idAttributes.put(id, new Integer(elem));
+  }
+
+  /**
+   * Check whether accumulated text should be stripped; if not,
+   * append the appropriate flavor of text/cdata node.
+   */
+  protected void charactersFlush()
+  {
+
+    if (m_textPendingStart >= 0)  // -1 indicates no-text-in-progress
+    {
+      int length = m_chars.size() - m_textPendingStart;
+      boolean doStrip = false;
+
+      if (getShouldStripWhitespace())
+      {
+        doStrip = m_chars.isWhitespace(m_textPendingStart, length);
+      }
+
+      if (doStrip) {
+        m_chars.setLength(m_textPendingStart);  // Discard accumulated text
+      } else {
+        // Guard against characters/ignorableWhitespace events that
+        // contained no characters.  They should not result in a node.
+        if (length > 0) {
+          int exName = m_expandedNameTable.getExpandedTypeID(DTM.TEXT_NODE);
+          int dataIndex = m_data.size();
+
+          m_previous = addNode(m_coalescedTextType, exName,
+                               m_parents.peek(), m_previous, dataIndex, false);
+
+          m_data.addElement(m_textPendingStart);
+          m_data.addElement(length);
+        }
+      }
+
+      // Reset for next text block
+      m_textPendingStart = -1;
+      m_textType = m_coalescedTextType = DTM.TEXT_NODE;
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of the EntityResolver interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Resolve an external entity.
+   *
+   * <p>Always return null, so that the parser will use the system
+   * identifier provided in the XML document.  This method implements
+   * the SAX default behaviour: application writers can override it
+   * in a subclass to do special translations such as catalog lookups
+   * or URI redirection.</p>
+   *
+   * @param publicId The public identifer, or null if none is
+   *                 available.
+   * @param systemId The system identifier provided in the XML
+   *                 document.
+   * @return The new input source, or null to require the
+   *         default behaviour.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.EntityResolver#resolveEntity
+   *
+   * @throws SAXException
+   */
+  public InputSource resolveEntity(String publicId, String systemId)
+          throws SAXException
+  {
+    return null;
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of DTDHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Receive notification of a notation declaration.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass if they wish to keep track of the notations
+   * declared in a document.</p>
+   *
+   * @param name The notation name.
+   * @param publicId The notation public identifier, or null if not
+   *                 available.
+   * @param systemId The notation system identifier.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.DTDHandler#notationDecl
+   *
+   * @throws SAXException
+   */
+  public void notationDecl(String name, String publicId, String systemId)
+          throws SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Receive notification of an unparsed entity declaration.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to keep track of the unparsed entities
+   * declared in a document.</p>
+   *
+   * @param name The entity name.
+   * @param publicId The entity public identifier, or null if not
+   *                 available.
+   * @param systemId The entity system identifier.
+   * @param notationName The name of the associated notation.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   *
+   * @throws SAXException
+   */
+  public void unparsedEntityDecl(
+          String name, String publicId, String systemId, String notationName)
+            throws SAXException
+  {
+
+    if (null == m_entities)
+    {
+      m_entities = new Vector();
+    }
+
+    try
+    {
+      systemId = SystemIDResolver.getAbsoluteURI(systemId,
+                                                 getDocumentBaseURI());
+    }
+    catch (Exception e)
+    {
+      throw new org.xml.sax.SAXException(e);
+    }
+
+    //  private static final int ENTITY_FIELD_PUBLICID = 0;
+    m_entities.addElement(publicId);
+
+    //  private static final int ENTITY_FIELD_SYSTEMID = 1;
+    m_entities.addElement(systemId);
+
+    //  private static final int ENTITY_FIELD_NOTATIONNAME = 2;
+    m_entities.addElement(notationName);
+
+    //  private static final int ENTITY_FIELD_NAME = 3;
+    m_entities.addElement(name);
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of ContentHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Receive a Locator object for document events.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass if they wish to store the locator for use
+   * with other document events.</p>
+   *
+   * @param locator A locator for all SAX document events.
+   * @see org.xml.sax.ContentHandler#setDocumentLocator
+   * @see org.xml.sax.Locator
+   */
+  public void setDocumentLocator(Locator locator)
+  {
+    m_locator = locator;
+    m_systemId = locator.getSystemId();
+  }
+
+  /**
+   * Receive notification of the beginning of the document.
+   *
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startDocument
+   */
+  public void startDocument() throws SAXException
+  {
+    if (DEBUG)
+      System.out.println("startDocument");
+
+		
+    int doc = addNode(DTM.DOCUMENT_NODE,
+                      m_expandedNameTable.getExpandedTypeID(DTM.DOCUMENT_NODE),
+                      DTM.NULL, DTM.NULL, 0, true);
+
+    m_parents.push(doc);
+    m_previous = DTM.NULL;
+
+    m_contextIndexes.push(m_prefixMappings.size());  // for the next element.
+  }
+
+  /**
+   * Receive notification of the end of the document.
+   *
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endDocument
+   */
+  public void endDocument() throws SAXException
+  {
+    if (DEBUG)
+      System.out.println("endDocument");
+
+		charactersFlush();
+
+    m_nextsib.setElementAt(NULL,0);
+
+    if (m_firstch.elementAt(0) == NOTPROCESSED)
+      m_firstch.setElementAt(NULL,0);
+
+    if (DTM.NULL != m_previous)
+      m_nextsib.setElementAt(DTM.NULL,m_previous);
+
+    m_parents = null;
+    m_prefixMappings = null;
+    m_contextIndexes = null;
+
+    m_endDocumentOccured = true;
+    
+    // Bugzilla 4858: throw away m_locator. we cache m_systemId
+    m_locator = null;
+  }
+
+  /**
+   * Receive notification of the start of a Namespace mapping.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the start of
+   * each Namespace prefix scope (such as storing the prefix mapping).</p>
+   *
+   * @param prefix The Namespace prefix being declared.
+   * @param uri The Namespace URI mapped to the prefix.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startPrefixMapping
+   */
+  public void startPrefixMapping(String prefix, String uri)
+          throws SAXException
+  {
+
+    if (DEBUG)
+      System.out.println("startPrefixMapping: prefix: " + prefix + ", uri: "
+                         + uri);
+
+    if(null == prefix)
+      prefix = "";
+    m_prefixMappings.addElement(prefix);  // JDK 1.1.x compat -sc
+    m_prefixMappings.addElement(uri);  // JDK 1.1.x compat -sc
+  }
+
+  /**
+   * Receive notification of the end of a Namespace mapping.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the end of
+   * each prefix mapping.</p>
+   *
+   * @param prefix The Namespace prefix being declared.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endPrefixMapping
+   */
+  public void endPrefixMapping(String prefix) throws SAXException
+  {
+    if (DEBUG)
+      System.out.println("endPrefixMapping: prefix: " + prefix);
+
+    if(null == prefix)
+      prefix = "";
+
+    int index = m_contextIndexes.peek() - 1;
+
+    do
+    {
+      index = m_prefixMappings.indexOf(prefix, ++index);
+    } while ( (index >= 0) && ((index & 0x01) == 0x01) );
+
+
+    if (index > -1)
+    {
+      m_prefixMappings.setElementAt("%@$#^@#", index);
+      m_prefixMappings.setElementAt("%@$#^@#", index + 1);
+    }
+
+    // no op
+  }
+
+  /**
+   * Check if a declaration has already been made for a given prefix.
+   *
+   * @param prefix non-null prefix string.
+   *
+   * @return true if the declaration has already been declared in the
+   *         current context.
+   */
+  protected boolean declAlreadyDeclared(String prefix)
+  {
+
+    int startDecls = m_contextIndexes.peek();
+    java.util.Vector prefixMappings = m_prefixMappings;
+    int nDecls = prefixMappings.size();
+
+    for (int i = startDecls; i < nDecls; i += 2)
+    {
+      String prefixDecl = (String) prefixMappings.elementAt(i);
+
+      if (prefixDecl == null)
+        continue;
+
+      if (prefixDecl.equals(prefix))
+        return true;
+    }
+
+    return false;
+  }
+
+	boolean m_pastFirstElement=false;
+
+  /**
+   * Receive notification of the start of an element.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the start of
+   * each element (such as allocating a new tree node or writing
+   * output to a file).</p>
+   *
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param qName The qualified name (with prefix), or the
+   *        empty string if qualified names are not available.
+   * @param attributes The specified or defaulted attributes.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startElement
+   */
+  public void startElement(
+          String uri, String localName, String qName, Attributes attributes)
+            throws SAXException
+  {
+   if (DEBUG)
+	 {
+      System.out.println("startElement: uri: " + uri + ", localname: "
+												 + localName + ", qname: "+qName+", atts: " + attributes);
+
+			boolean DEBUG_ATTRS=true;
+			if(DEBUG_ATTRS & attributes!=null)
+			{
+				int n = attributes.getLength();
+				if(n==0)
+					System.out.println("\tempty attribute list");
+				else for (int i = 0; i < n; i++)
+					System.out.println("\t attr: uri: " + attributes.getURI(i) +
+														 ", localname: " + attributes.getLocalName(i) +
+														 ", qname: " + attributes.getQName(i) +
+														 ", type: " + attributes.getType(i) +
+														 ", value: " + attributes.getValue(i)
+														 );
+			}
+	 }
+		
+    charactersFlush();
+
+    int exName = m_expandedNameTable.getExpandedTypeID(uri, localName, DTM.ELEMENT_NODE);
+    String prefix = getPrefix(qName, uri);
+    int prefixIndex = (null != prefix)
+                      ? m_valuesOrPrefixes.stringToIndex(qName) : 0;
+
+    int elemNode = addNode(DTM.ELEMENT_NODE, exName,
+                           m_parents.peek(), m_previous, prefixIndex, true);
+
+    if(m_indexing)
+      indexNode(exName, elemNode);
+    
+
+    m_parents.push(elemNode);
+
+    int startDecls = m_contextIndexes.peek();
+    int nDecls = m_prefixMappings.size();
+    int prev = DTM.NULL;
+
+    if(!m_pastFirstElement)
+    {
+      // SPECIAL CASE: Implied declaration at root element
+      prefix="xml";
+      String declURL = "http://www.w3.org/XML/1998/namespace";
+      exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE);
+      int val = m_valuesOrPrefixes.stringToIndex(declURL);
+      prev = addNode(DTM.NAMESPACE_NODE, exName, elemNode,
+                     prev, val, false);
+      m_pastFirstElement=true;
+    }
+
+    for (int i = startDecls; i < nDecls; i += 2)
+    {
+      prefix = (String) m_prefixMappings.elementAt(i);
+
+      if (prefix == null)
+        continue;
+
+      String declURL = (String) m_prefixMappings.elementAt(i + 1);
+
+      exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE);
+
+      int val = m_valuesOrPrefixes.stringToIndex(declURL);
+
+      prev = addNode(DTM.NAMESPACE_NODE, exName, elemNode,
+                     prev, val, false);
+    }
+
+    int n = attributes.getLength();
+
+    for (int i = 0; i < n; i++)
+    {
+      String attrUri = attributes.getURI(i);
+      String attrQName = attributes.getQName(i);
+      String valString = attributes.getValue(i);
+
+      prefix = getPrefix(attrQName, attrUri);
+
+      int nodeType;
+      
+       String attrLocalName = attributes.getLocalName(i);
+
+      if ((null != attrQName)
+              && (attrQName.equals("xmlns")
+                  || attrQName.startsWith("xmlns:")))
+      {
+        if (declAlreadyDeclared(prefix))
+          continue;  // go to the next attribute.
+
+        nodeType = DTM.NAMESPACE_NODE;
+      }
+      else
+      {
+        nodeType = DTM.ATTRIBUTE_NODE;
+
+        if (attributes.getType(i).equalsIgnoreCase("ID"))
+          setIDAttribute(valString, elemNode);
+      }
+
+      // Bit of a hack... if somehow valString is null, stringToIndex will
+      // return -1, which will make things very unhappy.
+      if(null == valString)
+        valString = "";
+
+      int val = m_valuesOrPrefixes.stringToIndex(valString);
+      //String attrLocalName = attributes.getLocalName(i);
+
+      if (null != prefix)
+      {
+
+        prefixIndex = m_valuesOrPrefixes.stringToIndex(attrQName);
+
+        int dataIndex = m_data.size();
+
+        m_data.addElement(prefixIndex);
+        m_data.addElement(val);
+
+        val = -dataIndex;
+      }
+
+      exName = m_expandedNameTable.getExpandedTypeID(attrUri, attrLocalName, nodeType);
+      prev = addNode(nodeType, exName, elemNode, prev, val,
+                     false);
+    }
+
+    if (DTM.NULL != prev)
+      m_nextsib.setElementAt(DTM.NULL,prev);
+
+    if (null != m_wsfilter)
+    {
+      short wsv = m_wsfilter.getShouldStripSpace(makeNodeHandle(elemNode), this);
+      boolean shouldStrip = (DTMWSFilter.INHERIT == wsv)
+                            ? getShouldStripWhitespace()
+                            : (DTMWSFilter.STRIP == wsv);
+
+      pushShouldStripWhitespace(shouldStrip);
+    }
+
+    m_previous = DTM.NULL;
+
+    m_contextIndexes.push(m_prefixMappings.size());  // for the children.
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the end of
+   * each element (such as finalising a tree node or writing
+   * output to a file).</p>
+   *
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param qName The qualified XML 1.0 name (with prefix), or the
+   *        empty string if qualified names are not available.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endElement
+   */
+  public void endElement(String uri, String localName, String qName)
+          throws SAXException
+  {
+   if (DEBUG)
+      System.out.println("endElement: uri: " + uri + ", localname: "
+												 + localName + ", qname: "+qName);
+
+    charactersFlush();
+
+    // If no one noticed, startPrefixMapping is a drag.
+    // Pop the context for the last child (the one pushed by startElement)
+    m_contextIndexes.quickPop(1);
+
+    // Do it again for this one (the one pushed by the last endElement).
+    int topContextIndex = m_contextIndexes.peek();
+    if (topContextIndex != m_prefixMappings.size()) {
+      m_prefixMappings.setSize(topContextIndex);
+    }
+
+    int lastNode = m_previous;
+
+    m_previous = m_parents.pop();
+
+    // If lastNode is still DTM.NULL, this element had no children
+    if (DTM.NULL == lastNode)
+      m_firstch.setElementAt(DTM.NULL,m_previous);
+    else
+      m_nextsib.setElementAt(DTM.NULL,lastNode);
+
+    popShouldStripWhitespace();
+  }
+
+  /**
+   * Receive notification of character data inside an element.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method to take specific actions for each chunk of character data
+   * (such as adding the data to a node or buffer, or printing it to
+   * a file).</p>
+   *
+   * @param ch The characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#characters
+   */
+  public void characters(char ch[], int start, int length) throws SAXException
+  {
+    if (m_textPendingStart == -1)  // First one in this block
+    {
+      m_textPendingStart = m_chars.size();
+      m_coalescedTextType = m_textType;
+    }
+    // Type logic: If all adjacent text is CDATASections, the
+    // concatentated text is treated as a single CDATASection (see
+    // initialization above).  If any were ordinary Text, the whole
+    // thing is treated as Text. This may be worth %REVIEW%ing.
+    else if (m_textType == DTM.TEXT_NODE)
+    {
+      m_coalescedTextType = DTM.TEXT_NODE;
+    }
+
+    m_chars.append(ch, start, length);
+  }
+
+  /**
+   * Receive notification of ignorable whitespace in element content.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method to take specific actions for each chunk of ignorable
+   * whitespace (such as adding data to a node or buffer, or printing
+   * it to a file).</p>
+   *
+   * @param ch The whitespace characters.
+   * @param start The start position in the character array.
+   * @param length The number of characters to use from the
+   *               character array.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#ignorableWhitespace
+   */
+  public void ignorableWhitespace(char ch[], int start, int length)
+          throws SAXException
+  {
+
+    // %OPT% We can probably take advantage of the fact that we know this 
+    // is whitespace.
+    characters(ch, start, length);
+  }
+
+  /**
+   * Receive notification of a processing instruction.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions for each
+   * processing instruction, such as setting status variables or
+   * invoking other methods.</p>
+   *
+   * @param target The processing instruction target.
+   * @param data The processing instruction data, or null if
+   *             none is supplied.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   */
+  public void processingInstruction(String target, String data)
+          throws SAXException
+  {
+    if (DEBUG)
+		 System.out.println("processingInstruction: target: " + target +", data: "+data);
+
+    charactersFlush();
+
+    int exName = m_expandedNameTable.getExpandedTypeID(null, target,
+                                         DTM.PROCESSING_INSTRUCTION_NODE);
+    int dataIndex = m_valuesOrPrefixes.stringToIndex(data);
+
+    m_previous = addNode(DTM.PROCESSING_INSTRUCTION_NODE, exName,
+                         m_parents.peek(), m_previous,
+                         dataIndex, false);
+  }
+
+  /**
+   * Receive notification of a skipped entity.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions for each
+   * processing instruction, such as setting status variables or
+   * invoking other methods.</p>
+   *
+   * @param name The name of the skipped entity.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   */
+  public void skippedEntity(String name) throws SAXException
+  {
+
+    // %REVIEW% What should be done here?
+    // no op
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of the ErrorHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Receive notification of a parser warning.
+   *
+   * <p>The default implementation does nothing.  Application writers
+   * may override this method in a subclass to take specific actions
+   * for each warning, such as inserting the message in a log file or
+   * printing it to the console.</p>
+   *
+   * @param e The warning information encoded as an exception.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ErrorHandler#warning
+   * @see org.xml.sax.SAXParseException
+   */
+  public void warning(SAXParseException e) throws SAXException
+  {
+
+    // %REVIEW% Is there anyway to get the JAXP error listener here?
+    System.err.println(e.getMessage());
+  }
+
+  /**
+   * Receive notification of a recoverable parser error.
+   *
+   * <p>The default implementation does nothing.  Application writers
+   * may override this method in a subclass to take specific actions
+   * for each error, such as inserting the message in a log file or
+   * printing it to the console.</p>
+   *
+   * @param e The warning information encoded as an exception.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ErrorHandler#warning
+   * @see org.xml.sax.SAXParseException
+   */
+  public void error(SAXParseException e) throws SAXException
+  {
+    throw e;
+  }
+
+  /**
+   * Report a fatal XML parsing error.
+   *
+   * <p>The default implementation throws a SAXParseException.
+   * Application writers may override this method in a subclass if
+   * they need to take specific actions for each fatal error (such as
+   * collecting all of the errors into a single report): in any case,
+   * the application must stop all regular processing when this
+   * method is invoked, since the document is no longer reliable, and
+   * the parser may no longer report parsing events.</p>
+   *
+   * @param e The error information encoded as an exception.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ErrorHandler#fatalError
+   * @see org.xml.sax.SAXParseException
+   */
+  public void fatalError(SAXParseException e) throws SAXException
+  {
+    throw e;
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of the DeclHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Report an element type declaration.
+   *
+   * <p>The content model will consist of the string "EMPTY", the
+   * string "ANY", or a parenthesised group, optionally followed
+   * by an occurrence indicator.  The model will be normalized so
+   * that all whitespace is removed,and will include the enclosing
+   * parentheses.</p>
+   *
+   * @param name The element type name.
+   * @param model The content model as a normalized string.
+   * @throws SAXException The application may raise an exception.
+   */
+  public void elementDecl(String name, String model) throws SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Report an attribute type declaration.
+   *
+   * <p>Only the effective (first) declaration for an attribute will
+   * be reported.  The type will be one of the strings "CDATA",
+   * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
+   * "ENTITIES", or "NOTATION", or a parenthesized token group with
+   * the separator "|" and all whitespace removed.</p>
+   *
+   * @param eName The name of the associated element.
+   * @param aName The name of the attribute.
+   * @param type A string representing the attribute type.
+   * @param valueDefault A string representing the attribute default
+   *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
+   *        none of these applies.
+   * @param value A string representing the attribute's default value,
+   *        or null if there is none.
+   * @throws SAXException The application may raise an exception.
+   */
+  public void attributeDecl(
+          String eName, String aName, String type, String valueDefault, String value)
+            throws SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Report an internal entity declaration.
+   *
+   * <p>Only the effective (first) declaration for each entity
+   * will be reported.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @param value The replacement text of the entity.
+   * @throws SAXException The application may raise an exception.
+   * @see #externalEntityDecl
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void internalEntityDecl(String name, String value)
+          throws SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Report a parsed external entity declaration.
+   *
+   * <p>Only the effective (first) declaration for each entity
+   * will be reported.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @param publicId The declared public identifier of the entity, or
+   *        null if none was declared.
+   * @param systemId The declared system identifier of the entity.
+   * @throws SAXException The application may raise an exception.
+   * @see #internalEntityDecl
+   * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+   */
+  public void externalEntityDecl(
+          String name, String publicId, String systemId) throws SAXException
+  {
+
+    // no op
+  }
+
+  ////////////////////////////////////////////////////////////////////
+  // Implementation of the LexicalHandler interface.
+  ////////////////////////////////////////////////////////////////////
+
+  /**
+   * Report the start of DTD declarations, if any.
+   *
+   * <p>Any declarations are assumed to be in the internal subset
+   * unless otherwise indicated by a {@link #startEntity startEntity}
+   * event.</p>
+   *
+   * <p>Note that the start/endDTD events will appear within
+   * the start/endDocument events from ContentHandler and
+   * before the first startElement event.</p>
+   *
+   * @param name The document type name.
+   * @param publicId The declared public identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @param systemId The declared system identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @throws SAXException The application may raise an
+   *            exception.
+   * @see #endDTD
+   * @see #startEntity
+   */
+  public void startDTD(String name, String publicId, String systemId)
+          throws SAXException
+  {
+
+    m_insideDTD = true;
+  }
+
+  /**
+   * Report the end of DTD declarations.
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #startDTD
+   */
+  public void endDTD() throws SAXException
+  {
+
+    m_insideDTD = false;
+  }
+
+  /**
+   * Report the beginning of an entity in content.
+   *
+   * <p><strong>NOTE:</entity> entity references in attribute
+   * values -- and the start and end of the document entity --
+   * are never reported.</p>
+   *
+   * <p>The start and end of the external DTD subset are reported
+   * using the pseudo-name "[dtd]".  All other events must be
+   * properly nested within start/end entity events.</p>
+   *
+   * <p>Note that skipped entities will be reported through the
+   * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
+   * event, which is part of the ContentHandler interface.</p>
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @throws SAXException The application may raise an exception.
+   * @see #endEntity
+   * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
+   * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
+   */
+  public void startEntity(String name) throws SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Report the end of an entity.
+   *
+   * @param name The name of the entity that is ending.
+   * @throws SAXException The application may raise an exception.
+   * @see #startEntity
+   */
+  public void endEntity(String name) throws SAXException
+  {
+
+    // no op
+  }
+
+  /**
+   * Report the start of a CDATA section.
+   *
+   * <p>The contents of the CDATA section will be reported through
+   * the regular {@link org.xml.sax.ContentHandler#characters
+   * characters} event.</p>
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #endCDATA
+   */
+  public void startCDATA() throws SAXException
+  {
+    m_textType = DTM.CDATA_SECTION_NODE;
+  }
+
+  /**
+   * Report the end of a CDATA section.
+   *
+   * @throws SAXException The application may raise an exception.
+   * @see #startCDATA
+   */
+  public void endCDATA() throws SAXException
+  {
+    m_textType = DTM.TEXT_NODE;
+  }
+
+  /**
+   * Report an XML comment anywhere in the document.
+   *
+   * <p>This callback will be used for comments inside or outside the
+   * document element, including comments in the external DTD
+   * subset (if read).</p>
+   *
+   * @param ch An array holding the characters in the comment.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   * @throws SAXException The application may raise an exception.
+   */
+  public void comment(char ch[], int start, int length) throws SAXException
+  {
+
+    if (m_insideDTD)      // ignore comments if we're inside the DTD
+      return;
+
+    charactersFlush();
+
+    int exName = m_expandedNameTable.getExpandedTypeID(DTM.COMMENT_NODE);
+
+    // For now, treat comments as strings...  I guess we should do a
+    // seperate FSB buffer instead.
+    int dataIndex = m_valuesOrPrefixes.stringToIndex(new String(ch, start,
+                      length));
+
+
+    m_previous = addNode(DTM.COMMENT_NODE, exName,
+                         m_parents.peek(), m_previous, dataIndex, false);
+  }
+
+  /**
+   * Set a run time property for this DTM instance.
+   * 
+   * %REVIEW% Now that we no longer use this method to support
+   * getSourceLocatorFor, can we remove it?
+   *
+   * @param property a <code>String</code> value
+   * @param value an <code>Object</code> value
+   */
+  public void setProperty(String property, Object value)
+  {
+  }
+
+  /** Retrieve the SourceLocator associated with a specific node.
+   * This is only meaningful if the XalanProperties.SOURCE_LOCATION flag was
+   * set True using setProperty; if it was never set, or was set false, we
+   * will return null. 
+   * 
+   * (We _could_ return a locator with the document's base URI and bogus 
+   * line/column information. Trying that; see the else clause.)
+   * */
+  public SourceLocator getSourceLocatorFor(int node)
+  {
+    if (m_useSourceLocationProperty)
+    {
+
+      node = makeNodeIdentity(node);
+      
+
+      return new NodeLocator(null,
+                             m_sourceSystemId.elementAt(node),
+                             m_sourceLine.elementAt(node),
+                             m_sourceColumn.elementAt(node));
+    }
+    else if(m_locator!=null)
+    {
+    	return new NodeLocator(null,m_locator.getSystemId(),-1,-1);
+    }
+    else if(m_systemId!=null)
+    {
+    	return new NodeLocator(null,m_systemId,-1,-1);
+    }
+    return null;
+  }
+  
+  public String getFixedNames(int type){
+    return m_fixednames[type];
+  }
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2DTM2.java b/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2DTM2.java
new file mode 100644
index 0000000..be2941a
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2DTM2.java
@@ -0,0 +1,3383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SAX2DTM2.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref.sax2dtm;
+
+import org.apache.xml.dtm.*;
+import org.apache.xml.dtm.ref.*;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringDefault;
+import org.apache.xml.utils.XMLStringFactory;
+import org.apache.xml.res.XMLMessages;
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.serializer.SerializationHandler;
+
+import javax.xml.transform.Source;
+import java.util.Vector;
+import org.apache.xml.utils.SuballocatedIntVector;
+import org.xml.sax.*;
+
+/**
+ * SAX2DTM2 is an optimized version of SAX2DTM which is used in non-incremental situation.
+ * It is used as the super class of the XSLTC SAXImpl. Many of the interfaces in SAX2DTM
+ * and DTMDefaultBase are overridden in SAX2DTM2 in order to allow fast, efficient
+ * access to the DTM model. Some nested iterators in DTMDefaultBaseIterators
+ * are also overridden in SAX2DTM2 for performance reasons.
+ * <p>
+ * Performance is the biggest consideration in the design of SAX2DTM2. To make the code most
+ * efficient, the incremental support is dropped in SAX2DTM2, which means that you should not
+ * use it in incremental situation. To reduce the overhead of pulling data from the DTM model,
+ * a few core interfaces in SAX2DTM2 have direct access to the internal arrays of the
+ * SuballocatedIntVectors.
+ * <p>
+ * The design of SAX2DTM2 may limit its extensibilty. If you have a reason to extend the
+ * SAX2DTM model, please extend from SAX2DTM instead of this class.
+ * <p>
+ * TODO: This class is currently only used by XSLTC. We need to investigate the possibility
+ * of also using it in Xalan-J Interpretive. Xalan's performance is likely to get an instant
+ * boost if we use SAX2DTM2 instead of SAX2DTM in non-incremental case.
+ * <p>
+ * %MK% The code in this class is critical to the XSLTC_DTM performance. Be very careful
+ * when making changes here!
+ */
+public class SAX2DTM2 extends SAX2DTM
+{
+
+  /****************************************************************
+   *       Optimized version of the nested iterators
+   ****************************************************************/
+
+  /**
+   * Iterator that returns all immediate children of a given node
+   */
+  public final class ChildrenIterator extends InternalAxisIteratorBase
+  {
+
+    /**
+     * Setting start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     * <p>
+     * If the iterator is not restartable, this has no effect.
+     * %REVIEW% Should it return/throw something in that case,
+     * or set current node to END, to indicate request-not-honored?
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = (node == DTM.NULL) ? DTM.NULL
+                                          : _firstch2(makeNodeIdentity(node));
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END if no more
+     * are available.
+     */
+    public int next()
+    {
+      if (_currentNode != NULL) {
+        int node = _currentNode;
+        _currentNode = _nextsib2(node);
+        return returnNode(makeNodeHandle(node));
+      }
+
+      return END;
+    }
+  }  // end of ChildrenIterator
+
+  /**
+   * Iterator that returns the parent of a given node. Note that
+   * this delivers only a single node; if you want all the ancestors,
+   * see AncestorIterator.
+   */
+  public final class ParentIterator extends InternalAxisIteratorBase
+  {
+
+    /** The extended type ID that was requested. */
+    private int _nodeType = DTM.NULL;
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+
+        if (node != DTM.NULL)
+          _currentNode = _parent2(makeNodeIdentity(node));
+        else
+          _currentNode = DTM.NULL;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Set the node type of the parent that we're looking for.
+     * Note that this does _not_ mean "find the nearest ancestor of
+     * this type", but "yield the parent if it is of this type".
+     *
+     *
+     * @param type extended type ID.
+     *
+     * @return ParentIterator configured with the type filter set.
+     */
+    public DTMAxisIterator setNodeType(final int type)
+    {
+
+      _nodeType = type;
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration. In this case, we return
+     * only the immediate parent, _if_ it matches the requested nodeType.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int result = _currentNode;
+      if (result == END)
+        return DTM.NULL;
+
+      // %OPT% The most common case is handled first.
+      if (_nodeType == NULL) {
+        _currentNode = END;
+        return returnNode(makeNodeHandle(result));
+      }
+      else if (_nodeType >= DTM.NTYPES) {
+        if (_nodeType == _exptype2(result)) {
+          _currentNode = END;
+	  return returnNode(makeNodeHandle(result));
+        }
+      }
+      else {
+        if (_nodeType == _type2(result)) {
+	  _currentNode = END;
+	  return returnNode(makeNodeHandle(result));
+        }
+      }
+
+      return DTM.NULL;
+    }
+  }  // end of ParentIterator
+
+  /**
+   * Iterator that returns children of a given type for a given node.
+   * The functionality chould be achieved by putting a filter on top
+   * of a basic child iterator, but a specialised iterator is used
+   * for efficiency (both speed and size of translet).
+   */
+  public final class TypedChildrenIterator extends InternalAxisIteratorBase
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedChildrenIterator
+     *
+     *
+     * @param nodeType The extended type ID being requested.
+     */
+    public TypedChildrenIterator(int nodeType)
+    {
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = (node == DTM.NULL)
+                                   ? DTM.NULL
+                                   : _firstch2(makeNodeIdentity(_startNode));
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int node = _currentNode;
+      if (node == DTM.NULL)
+        return DTM.NULL;
+
+      final int nodeType = _nodeType;
+
+      if (nodeType != DTM.ELEMENT_NODE) {
+        while (node != DTM.NULL && _exptype2(node) != nodeType) {
+          node = _nextsib2(node);
+        }
+      }
+      // %OPT% If the nodeType is element (matching child::*), we only
+      // need to compare the expType with DTM.NTYPES. A child node of
+      // an element can be either an element, text, comment or
+      // processing instruction node. Only element node has an extended
+      // type greater than or equal to DTM.NTYPES.
+      else {
+      	int eType;
+      	while (node != DTM.NULL) {
+      	  eType = _exptype2(node);
+      	  if (eType >= DTM.NTYPES)
+      	    break;
+      	  else
+      	    node = _nextsib2(node);
+      	}
+      }
+
+      if (node == DTM.NULL) {
+        _currentNode = DTM.NULL;
+        return DTM.NULL;
+      } else {
+        _currentNode = _nextsib2(node);
+        return returnNode(makeNodeHandle(node));
+      }
+
+    }
+
+    /**
+     * Return the node at the given position.
+     */
+    public int getNodeByPosition(int position)
+    {
+      if (position <= 0)
+        return DTM.NULL;
+
+      int node = _currentNode;
+      int pos = 0;
+
+      final int nodeType = _nodeType;
+      if (nodeType != DTM.ELEMENT_NODE) {
+        while (node != DTM.NULL) {
+          if (_exptype2(node) == nodeType) {
+            pos++;
+            if (pos == position)
+              return makeNodeHandle(node);
+          }
+
+          node = _nextsib2(node);
+        }
+        return NULL;
+      }
+      else {
+      	while (node != DTM.NULL) {
+      	  if (_exptype2(node) >= DTM.NTYPES) {
+      	    pos++;
+      	    if (pos == position)
+      	      return makeNodeHandle(node);
+      	  }
+      	  node = _nextsib2(node);
+      	}
+      	return NULL;
+      }
+    }
+
+  }  // end of TypedChildrenIterator
+
+  /**
+   * Iterator that returns the namespace nodes as defined by the XPath data model
+   * for a given node, filtered by extended type ID.
+   */
+  public class TypedRootIterator extends RootIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedRootIterator
+     *
+     * @param nodeType The extended type ID being requested.
+     */
+    public TypedRootIterator(int nodeType)
+    {
+      super();
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      if(_startNode == _currentNode)
+        return NULL;
+
+      final int node = _startNode;
+      int expType = _exptype2(makeNodeIdentity(node));
+
+      _currentNode = node;
+
+      if (_nodeType >= DTM.NTYPES) {
+        if (_nodeType == expType) {
+          return returnNode(node);
+        }
+      }
+      else {
+        if (expType < DTM.NTYPES) {
+          if (expType == _nodeType) {
+            return returnNode(node);
+          }
+        }
+        else {
+          if (m_extendedTypes[expType].getNodeType() == _nodeType) {
+            return returnNode(node);
+          }
+        }
+      }
+
+      return NULL;
+    }
+  }  // end of TypedRootIterator
+
+  /**
+   * Iterator that returns all siblings of a given node.
+   */
+  public class FollowingSiblingIterator extends InternalAxisIteratorBase
+  {
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = makeNodeIdentity(node);
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      _currentNode = (_currentNode == DTM.NULL) ? DTM.NULL
+                                                : _nextsib2(_currentNode);
+      return returnNode(makeNodeHandle(_currentNode));
+    }
+  }  // end of FollowingSiblingIterator
+
+  /**
+   * Iterator that returns all following siblings of a given node.
+   */
+  public final class TypedFollowingSiblingIterator
+          extends FollowingSiblingIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedFollowingSiblingIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedFollowingSiblingIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      if (_currentNode == DTM.NULL) {
+        return DTM.NULL;
+      }
+
+      int node = _currentNode;
+      final int nodeType = _nodeType;
+
+      if (nodeType != DTM.ELEMENT_NODE) {
+        while ((node = _nextsib2(node)) != DTM.NULL && _exptype2(node) != nodeType) {}
+      }
+      else {
+        while ((node = _nextsib2(node)) != DTM.NULL && _exptype2(node) < DTM.NTYPES) {}
+      }
+
+      _currentNode = node;
+
+      return (node == DTM.NULL)
+                      ? DTM.NULL
+                      : returnNode(makeNodeHandle(node));
+    }
+
+  }  // end of TypedFollowingSiblingIterator
+
+  /**
+   * Iterator that returns attribute nodes (of what nodes?)
+   */
+  public final class AttributeIterator extends InternalAxisIteratorBase
+  {
+
+    // assumes caller will pass element nodes
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        _currentNode = getFirstAttributeIdentity(makeNodeIdentity(node));
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      final int node = _currentNode;
+
+      if (node != NULL) {
+        _currentNode = getNextAttributeIdentity(node);
+        return returnNode(makeNodeHandle(node));
+      }
+
+      return NULL;
+    }
+  }  // end of AttributeIterator
+
+  /**
+   * Iterator that returns attribute nodes of a given type
+   */
+  public final class TypedAttributeIterator extends InternalAxisIteratorBase
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedAttributeIterator
+     *
+     *
+     * @param nodeType The extended type ID that is requested.
+     */
+    public TypedAttributeIterator(int nodeType)
+    {
+      _nodeType = nodeType;
+    }
+
+    // assumes caller will pass element nodes
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+      if (_isRestartable)
+      {
+        _startNode = node;
+
+        _currentNode = getTypedAttribute(node, _nodeType);
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      final int node = _currentNode;
+
+      // singleton iterator, since there can only be one attribute of
+      // a given type.
+      _currentNode = NULL;
+
+      return returnNode(node);
+    }
+  }  // end of TypedAttributeIterator
+
+  /**
+   * Iterator that returns preceding siblings of a given node
+   */
+  public class PrecedingSiblingIterator extends InternalAxisIteratorBase
+  {
+
+    /**
+     * The node identity of _startNode for this iterator
+     */
+    protected int _startNodeID;
+
+    /**
+     * True if this iterator has a reversed axis.
+     *
+     * @return true.
+     */
+    public boolean isReverse()
+    {
+      return true;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+        node = _startNodeID = makeNodeIdentity(node);
+
+        if(node == NULL)
+        {
+          _currentNode = node;
+          return resetPosition();
+        }
+
+        int type = _type2(node);
+        if(ExpandedNameTable.ATTRIBUTE == type
+           || ExpandedNameTable.NAMESPACE == type )
+        {
+          _currentNode = node;
+        }
+        else
+        {
+          // Be careful to handle the Document node properly
+          _currentNode = _parent2(node);
+          if(NULL!=_currentNode)
+            _currentNode = _firstch2(_currentNode);
+          else
+            _currentNode = node;
+        }
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      if (_currentNode == _startNodeID || _currentNode == DTM.NULL)
+      {
+        return NULL;
+      }
+      else
+      {
+        final int node = _currentNode;
+        _currentNode = _nextsib2(node);
+
+        return returnNode(makeNodeHandle(node));
+      }
+    }
+  }  // end of PrecedingSiblingIterator
+
+  /**
+   * Iterator that returns preceding siblings of a given type for
+   * a given node
+   */
+  public final class TypedPrecedingSiblingIterator
+          extends PrecedingSiblingIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedPrecedingSiblingIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedPrecedingSiblingIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int node = _currentNode;
+
+      final int nodeType = _nodeType;
+      final int startNodeID = _startNodeID;
+
+      if (nodeType != DTM.ELEMENT_NODE) {
+        while (node != NULL && node != startNodeID && _exptype2(node) != nodeType) {
+          node = _nextsib2(node);
+        }
+      }
+      else {
+        while (node != NULL && node != startNodeID && _exptype2(node) < DTM.NTYPES) {
+          node = _nextsib2(node);
+        }
+      }
+
+      if (node == DTM.NULL || node == startNodeID) {
+        _currentNode = NULL;
+        return NULL;
+      }
+      else {
+        _currentNode = _nextsib2(node);
+        return returnNode(makeNodeHandle(node));
+      }
+    }
+
+    /**
+     * Return the index of the last node in this iterator.
+     */
+    public int getLast()
+    {
+      if (_last != -1)
+        return _last;
+
+      setMark();
+
+      int node = _currentNode;
+      final int nodeType = _nodeType;
+      final int startNodeID = _startNodeID;
+
+      int last = 0;
+      if (nodeType != DTM.ELEMENT_NODE) {
+        while (node != NULL && node != startNodeID) {
+          if (_exptype2(node) == nodeType) {
+            last++;
+          }
+          node = _nextsib2(node);
+        }
+      }
+      else {
+        while (node != NULL && node != startNodeID) {
+          if (_exptype2(node) >= DTM.NTYPES) {
+            last++;
+          }
+          node = _nextsib2(node);
+        }
+      }
+
+      gotoMark();
+
+      return (_last = last);
+    }
+  }  // end of TypedPrecedingSiblingIterator
+
+  /**
+   * Iterator that returns preceding nodes of a given node.
+   * This includes the node set {root+1, start-1}, but excludes
+   * all ancestors, attributes, and namespace nodes.
+   */
+  public class PrecedingIterator extends InternalAxisIteratorBase
+  {
+
+    /** The max ancestors, but it can grow... */
+    private final int _maxAncestors = 8;
+
+    /**
+     * The stack of start node + ancestors up to the root of the tree,
+     *  which we must avoid.
+     */
+    protected int[] _stack = new int[_maxAncestors];
+
+    /** (not sure yet... -sb) */
+    protected int _sp, _oldsp;
+
+    protected int _markedsp, _markedNode, _markedDescendant;
+
+    /* _currentNode precedes candidates.  This is the identity, not the handle! */
+
+    /**
+     * True if this iterator has a reversed axis.
+     *
+     * @return true since this iterator is a reversed axis.
+     */
+    public boolean isReverse()
+    {
+      return true;
+    }
+
+    /**
+     * Returns a deep copy of this iterator.   The cloned iterator is not reset.
+     *
+     * @return a deep copy of this iterator.
+     */
+    public DTMAxisIterator cloneIterator()
+    {
+      _isRestartable = false;
+
+      try
+      {
+        final PrecedingIterator clone = (PrecedingIterator) super.clone();
+        final int[] stackCopy = new int[_stack.length];
+        System.arraycopy(_stack, 0, stackCopy, 0, _stack.length);
+
+        clone._stack = stackCopy;
+
+        // return clone.reset();
+        return clone;
+      }
+      catch (CloneNotSupportedException e)
+      {
+        throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_ITERATOR_CLONE_NOT_SUPPORTED, null)); //"Iterator clone not supported.");
+      }
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        node = makeNodeIdentity(node);
+
+        // iterator is not a clone
+        int parent, index;
+
+       if (_type2(node) == DTM.ATTRIBUTE_NODE)
+         node = _parent2(node);
+
+        _startNode = node;
+        _stack[index = 0] = node;
+
+       	parent=node;
+	while ((parent = _parent2(parent)) != NULL)
+	{
+	  if (++index == _stack.length)
+	  {
+	    final int[] stack = new int[index*2];
+	    System.arraycopy(_stack, 0, stack, 0, index);
+	    _stack = stack;
+	  }
+	  _stack[index] = parent;
+        }
+
+        if(index>0)
+	  --index; // Pop actual root node (if not start) back off the stack
+
+        _currentNode=_stack[index]; // Last parent before root node
+
+        _oldsp = _sp = index;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+    	// Bugzilla 8324: We were forgetting to skip Attrs and NS nodes.
+    	// Also recoded the loop controls for clarity and to flatten out
+    	// the tail-recursion.
+   	for(++_currentNode; _sp>=0; ++_currentNode)
+   	{
+   	  if(_currentNode < _stack[_sp])
+   	  {
+   	    int type = _type2(_currentNode);
+   	    if(type != ATTRIBUTE_NODE && type != NAMESPACE_NODE)
+   	      return returnNode(makeNodeHandle(_currentNode));
+   	  }
+   	  else
+   	    --_sp;
+   	}
+   	return NULL;
+    }
+
+    // redefine DTMAxisIteratorBase's reset
+
+    /**
+     * Resets the iterator to the last start node.
+     *
+     * @return A DTMAxisIterator, which may or may not be the same as this
+     *         iterator.
+     */
+    public DTMAxisIterator reset()
+    {
+
+      _sp = _oldsp;
+
+      return resetPosition();
+    }
+
+    public void setMark() {
+        _markedsp = _sp;
+        _markedNode = _currentNode;
+        _markedDescendant = _stack[0];
+    }
+
+    public void gotoMark() {
+        _sp = _markedsp;
+        _currentNode = _markedNode;
+    }
+  }  // end of PrecedingIterator
+
+  /**
+   * Iterator that returns preceding nodes of agiven type for a
+   * given node. This includes the node set {root+1, start-1}, but
+   * excludes all ancestors.
+   */
+  public final class TypedPrecedingIterator extends PrecedingIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedPrecedingIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedPrecedingIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int node = _currentNode;
+      final int nodeType = _nodeType;
+
+      if (nodeType >= DTM.NTYPES) {
+        while (true) {
+          node++;
+
+          if (_sp < 0) {
+            node = NULL;
+            break;
+          }
+          else if (node >= _stack[_sp]) {
+            if (--_sp < 0) {
+              node = NULL;
+              break;
+            }
+          }
+          else if (_exptype2(node) == nodeType) {
+            break;
+          }
+        }
+      }
+      else {
+        int expType;
+
+        while (true) {
+          node++;
+
+          if (_sp < 0) {
+            node = NULL;
+            break;
+          }
+          else if (node >= _stack[_sp]) {
+            if (--_sp < 0) {
+              node = NULL;
+              break;
+            }
+          }
+          else {
+            expType = _exptype2(node);
+            if (expType < DTM.NTYPES) {
+              if (expType == nodeType) {
+                break;
+              }
+            }
+            else {
+              if (m_extendedTypes[expType].getNodeType() == nodeType) {
+                break;
+              }
+            }
+          }
+        }
+      }
+
+      _currentNode = node;
+
+      return (node == NULL) ? NULL : returnNode(makeNodeHandle(node));
+    }
+  }  // end of TypedPrecedingIterator
+
+  /**
+   * Iterator that returns following nodes of for a given node.
+   */
+  public class FollowingIterator extends InternalAxisIteratorBase
+  {
+    //DTMAxisTraverser m_traverser; // easier for now
+
+    public FollowingIterator()
+    {
+      //m_traverser = getAxisTraverser(Axis.FOLLOWING);
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        _startNode = node;
+
+        //_currentNode = m_traverser.first(node);
+
+        node = makeNodeIdentity(node);
+
+        int first;
+        int type = _type2(node);
+
+        if ((DTM.ATTRIBUTE_NODE == type) || (DTM.NAMESPACE_NODE == type))
+        {
+          node = _parent2(node);
+          first = _firstch2(node);
+
+          if (NULL != first) {
+            _currentNode = makeNodeHandle(first);
+            return resetPosition();
+          }
+        }
+
+        do
+        {
+          first = _nextsib2(node);
+
+          if (NULL == first)
+            node = _parent2(node);
+        }
+        while (NULL == first && NULL != node);
+
+        _currentNode = makeNodeHandle(first);
+
+        // _currentNode precedes possible following(node) nodes
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      int node = _currentNode;
+
+      //_currentNode = m_traverser.next(_startNode, _currentNode);
+      int current = makeNodeIdentity(node);
+
+      while (true)
+      {
+        current++;
+
+        int type = _type2(current);
+        if (NULL == type) {
+          _currentNode = NULL;
+          return returnNode(node);
+        }
+
+        if (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type)
+          continue;
+
+        _currentNode = makeNodeHandle(current);
+        return returnNode(node);
+      }
+    }
+
+  }  // end of FollowingIterator
+
+  /**
+   * Iterator that returns following nodes of a given type for a given node.
+   */
+  public final class TypedFollowingIterator extends FollowingIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedFollowingIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedFollowingIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      int current;
+      int node;
+      int type;
+
+      final int nodeType = _nodeType;
+      int currentNodeID = makeNodeIdentity(_currentNode);
+
+      if (nodeType >= DTM.NTYPES) {
+        do {
+          node = currentNodeID;
+	  current = node;
+
+          do {
+            current++;
+            type = _type2(current);
+          }
+          while (type != NULL && (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type));
+
+          currentNodeID = (type != NULL) ? current : NULL;
+        }
+        while (node != DTM.NULL && _exptype2(node) != nodeType);
+      }
+      else {
+        do {
+          node = currentNodeID;
+	  current = node;
+
+          do {
+            current++;
+            type = _type2(current);
+          }
+          while (type != NULL && (ATTRIBUTE_NODE == type || NAMESPACE_NODE == type));
+
+          currentNodeID = (type != NULL) ? current : NULL;
+        }
+        while (node != DTM.NULL
+               && (_exptype2(node) != nodeType && _type2(node) != nodeType));
+      }
+
+      _currentNode = makeNodeHandle(currentNodeID);
+      return (node == DTM.NULL ? DTM.NULL :returnNode(makeNodeHandle(node)));
+    }
+  }  // end of TypedFollowingIterator
+
+  /**
+   * Iterator that returns the ancestors of a given node in document
+   * order.  (NOTE!  This was changed from the XSLTC code!)
+   */
+  public class AncestorIterator extends InternalAxisIteratorBase
+  {
+    // The initial size of the ancestor array
+    private static final int m_blocksize = 32;
+
+    // The array for ancestor nodes. This array will grow dynamically.
+    int[] m_ancestors = new int[m_blocksize];
+
+    // Number of ancestor nodes in the array
+    int m_size = 0;
+
+    int m_ancestorsPos;
+
+    int m_markedPos;
+
+    /** The real start node for this axes, since _startNode will be adjusted. */
+    int m_realStartNode;
+
+    /**
+     * Get start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @return The root node of the iteration.
+     */
+    public int getStartNode()
+    {
+      return m_realStartNode;
+    }
+
+    /**
+     * True if this iterator has a reversed axis.
+     *
+     * @return true since this iterator is a reversed axis.
+     */
+    public final boolean isReverse()
+    {
+      return true;
+    }
+
+    /**
+     * Returns a deep copy of this iterator.  The cloned iterator is not reset.
+     *
+     * @return a deep copy of this iterator.
+     */
+    public DTMAxisIterator cloneIterator()
+    {
+      _isRestartable = false;  // must set to false for any clone
+
+      try
+      {
+        final AncestorIterator clone = (AncestorIterator) super.clone();
+
+        clone._startNode = _startNode;
+
+        // return clone.reset();
+        return clone;
+      }
+      catch (CloneNotSupportedException e)
+      {
+        throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_ITERATOR_CLONE_NOT_SUPPORTED, null)); //"Iterator clone not supported.");
+      }
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      m_realStartNode = node;
+
+      if (_isRestartable)
+      {
+        int nodeID = makeNodeIdentity(node);
+        m_size = 0;
+
+        if (nodeID == DTM.NULL) {
+          _currentNode = DTM.NULL;
+          m_ancestorsPos = 0;
+          return this;
+        }
+
+        // Start from the current node's parent if
+        // _includeSelf is false.
+        if (!_includeSelf) {
+          nodeID = _parent2(nodeID);
+          node = makeNodeHandle(nodeID);
+        }
+
+        _startNode = node;
+
+        while (nodeID != END) {
+          //m_ancestors.addElement(node);
+          if (m_size >= m_ancestors.length)
+          {
+            int[] newAncestors = new int[m_size * 2];
+            System.arraycopy(m_ancestors, 0, newAncestors, 0, m_ancestors.length);
+            m_ancestors = newAncestors;
+          }
+
+          m_ancestors[m_size++] = node;
+          nodeID = _parent2(nodeID);
+          node = makeNodeHandle(nodeID);
+        }
+
+        m_ancestorsPos = m_size - 1;
+
+        _currentNode = (m_ancestorsPos>=0)
+                               ? m_ancestors[m_ancestorsPos]
+                               : DTM.NULL;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Resets the iterator to the last start node.
+     *
+     * @return A DTMAxisIterator, which may or may not be the same as this
+     *         iterator.
+     */
+    public DTMAxisIterator reset()
+    {
+
+      m_ancestorsPos = m_size - 1;
+
+      _currentNode = (m_ancestorsPos >= 0) ? m_ancestors[m_ancestorsPos]
+                                         : DTM.NULL;
+
+      return resetPosition();
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      int next = _currentNode;
+
+      int pos = --m_ancestorsPos;
+
+      _currentNode = (pos >= 0) ? m_ancestors[m_ancestorsPos]
+                                : DTM.NULL;
+
+      return returnNode(next);
+    }
+
+    public void setMark() {
+        m_markedPos = m_ancestorsPos;
+    }
+
+    public void gotoMark() {
+        m_ancestorsPos = m_markedPos;
+        _currentNode = m_ancestorsPos>=0 ? m_ancestors[m_ancestorsPos]
+                                         : DTM.NULL;
+    }
+  }  // end of AncestorIterator
+
+  /**
+   * Typed iterator that returns the ancestors of a given node.
+   */
+  public final class TypedAncestorIterator extends AncestorIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedAncestorIterator
+     *
+     *
+     * @param type The extended type ID being requested.
+     */
+    public TypedAncestorIterator(int type)
+    {
+      _nodeType = type;
+    }
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      m_realStartNode = node;
+
+      if (_isRestartable)
+      {
+        int nodeID = makeNodeIdentity(node);
+        m_size = 0;
+
+        if (nodeID == DTM.NULL) {
+          _currentNode = DTM.NULL;
+          m_ancestorsPos = 0;
+          return this;
+        }
+
+        final int nodeType = _nodeType;
+
+        if (!_includeSelf) {
+          nodeID = _parent2(nodeID);
+          node = makeNodeHandle(nodeID);
+        }
+
+        _startNode = node;
+
+        if (nodeType >= DTM.NTYPES) {
+          while (nodeID != END) {
+            int eType = _exptype2(nodeID);
+
+            if (eType == nodeType) {
+              if (m_size >= m_ancestors.length)
+              {
+              	int[] newAncestors = new int[m_size * 2];
+              	System.arraycopy(m_ancestors, 0, newAncestors, 0, m_ancestors.length);
+              	m_ancestors = newAncestors;
+              }
+              m_ancestors[m_size++] = makeNodeHandle(nodeID);
+            }
+            nodeID = _parent2(nodeID);
+          }
+        }
+        else {
+          while (nodeID != END) {
+            int eType = _exptype2(nodeID);
+
+            if ((eType < DTM.NTYPES && eType == nodeType)
+                || (eType >= DTM.NTYPES
+                    && m_extendedTypes[eType].getNodeType() == nodeType)) {
+              if (m_size >= m_ancestors.length)
+              {
+              	int[] newAncestors = new int[m_size * 2];
+              	System.arraycopy(m_ancestors, 0, newAncestors, 0, m_ancestors.length);
+              	m_ancestors = newAncestors;
+              }
+              m_ancestors[m_size++] = makeNodeHandle(nodeID);
+            }
+            nodeID = _parent2(nodeID);
+          }
+        }
+        m_ancestorsPos = m_size - 1;
+
+        _currentNode = (m_ancestorsPos>=0)
+                               ? m_ancestors[m_ancestorsPos]
+                               : DTM.NULL;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Return the node at the given position.
+     */
+    public int getNodeByPosition(int position)
+    {
+      if (position > 0 && position <= m_size) {
+        return m_ancestors[position-1];
+      }
+      else
+        return DTM.NULL;
+    }
+
+    /**
+     * Returns the position of the last node within the iteration, as
+     * defined by XPath.
+     */
+    public int getLast() {
+      return m_size;
+    }
+  }  // end of TypedAncestorIterator
+
+  /**
+   * Iterator that returns the descendants of a given node.
+   */
+  public class DescendantIterator extends InternalAxisIteratorBase
+  {
+
+    /**
+     * Set start to END should 'close' the iterator,
+     * i.e. subsequent call to next() should return END.
+     *
+     * @param node Sets the root of the iteration.
+     *
+     * @return A DTMAxisIterator set to the start of the iteration.
+     */
+    public DTMAxisIterator setStartNode(int node)
+    {
+//%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
+      if (node == DTMDefaultBase.ROOTNODE)
+        node = getDocument();
+      if (_isRestartable)
+      {
+        node = makeNodeIdentity(node);
+        _startNode = node;
+
+        if (_includeSelf)
+          node--;
+
+        _currentNode = node;
+
+        return resetPosition();
+      }
+
+      return this;
+    }
+
+    /**
+     * Tell if this node identity is a descendant.  Assumes that
+     * the node info for the element has already been obtained.
+     *
+     * This one-sided test works only if the parent has been
+     * previously tested and is known to be a descendent. It fails if
+     * the parent is the _startNode's next sibling, or indeed any node
+     * that follows _startNode in document order.  That may suffice
+     * for this iterator, but it's not really an isDescendent() test.
+     * %REVIEW% rename?
+     *
+     * @param identity The index number of the node in question.
+     * @return true if the index is a descendant of _startNode.
+     */
+    protected final boolean isDescendant(int identity)
+    {
+      return (_parent2(identity) >= _startNode) || (_startNode == identity);
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      final int startNode = _startNode;
+      if (startNode == NULL) {
+        return NULL;
+      }
+
+      if (_includeSelf && (_currentNode + 1) == startNode)
+          return returnNode(makeNodeHandle(++_currentNode)); // | m_dtmIdent);
+
+      int node = _currentNode;
+      int type;
+
+      // %OPT% If the startNode is the root node, do not need
+      // to do the isDescendant() check.
+      if (startNode == ROOTNODE) {
+        int eType;
+        do {
+          node++;
+          eType = _exptype2(node);
+
+          if (NULL == eType) {
+            _currentNode = NULL;
+            return END;
+          }
+        } while (eType == TEXT_NODE
+                 || (type = m_extendedTypes[eType].getNodeType()) == ATTRIBUTE_NODE
+                 || type == NAMESPACE_NODE);
+      }
+      else {
+        do {
+          node++;
+          type = _type2(node);
+
+          if (NULL == type ||!isDescendant(node)) {
+            _currentNode = NULL;
+            return END;
+          }
+        } while(ATTRIBUTE_NODE == type || TEXT_NODE == type
+                 || NAMESPACE_NODE == type);
+      }
+
+      _currentNode = node;
+      return returnNode(makeNodeHandle(node));  // make handle.
+    }
+
+    /**
+     * Reset.
+     *
+     */
+  public DTMAxisIterator reset()
+  {
+
+    final boolean temp = _isRestartable;
+
+    _isRestartable = true;
+
+    setStartNode(makeNodeHandle(_startNode));
+
+    _isRestartable = temp;
+
+    return this;
+  }
+
+  }  // end of DescendantIterator
+
+  /**
+   * Typed iterator that returns the descendants of a given node.
+   */
+  public final class TypedDescendantIterator extends DescendantIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedDescendantIterator
+     *
+     *
+     * @param nodeType Extended type ID being requested.
+     */
+    public TypedDescendantIterator(int nodeType)
+    {
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+      final int startNode = _startNode;
+      if (_startNode == NULL) {
+        return NULL;
+      }
+
+      int node = _currentNode;
+
+      int expType;
+      final int nodeType = _nodeType;
+
+      if (nodeType != DTM.ELEMENT_NODE)
+      {
+        do
+        {
+          node++;
+	  expType = _exptype2(node);
+
+          if (NULL == expType || _parent2(node) < startNode && startNode != node) {
+            _currentNode = NULL;
+            return END;
+          }
+        }
+        while (expType != nodeType);
+      }
+      // %OPT% If the start node is root (e.g. in the case of //node),
+      // we can save the isDescendant() check, because all nodes are
+      // descendants of root.
+      else if (startNode == DTMDefaultBase.ROOTNODE)
+      {
+	do
+	{
+	  node++;
+	  expType = _exptype2(node);
+
+	  if (NULL == expType) {
+	    _currentNode = NULL;
+	    return END;
+	  }
+	} while (expType < DTM.NTYPES
+	        || m_extendedTypes[expType].getNodeType() != DTM.ELEMENT_NODE);
+      }
+      else
+      {
+        do
+        {
+          node++;
+	  expType = _exptype2(node);
+
+          if (NULL == expType || _parent2(node) < startNode && startNode != node) {
+            _currentNode = NULL;
+            return END;
+          }
+        }
+        while (expType < DTM.NTYPES
+	       || m_extendedTypes[expType].getNodeType() != DTM.ELEMENT_NODE);
+      }
+
+      _currentNode = node;
+      return returnNode(makeNodeHandle(node));
+    }
+  }  // end of TypedDescendantIterator
+
+  /**
+   * Iterator that returns a given node only if it is of a given type.
+   */
+  public final class TypedSingletonIterator extends SingletonIterator
+  {
+
+    /** The extended type ID that was requested. */
+    private final int _nodeType;
+
+    /**
+     * Constructor TypedSingletonIterator
+     *
+     *
+     * @param nodeType The extended type ID being requested.
+     */
+    public TypedSingletonIterator(int nodeType)
+    {
+      _nodeType = nodeType;
+    }
+
+    /**
+     * Get the next node in the iteration.
+     *
+     * @return The next node handle in the iteration, or END.
+     */
+    public int next()
+    {
+
+      final int result = _currentNode;
+      if (result == END)
+        return DTM.NULL;
+
+      _currentNode = END;
+
+      if (_nodeType >= DTM.NTYPES) {
+        if (_exptype2(makeNodeIdentity(result)) == _nodeType) {
+          return returnNode(result);
+        }
+      }
+      else {
+        if (_type2(makeNodeIdentity(result)) == _nodeType) {
+          return returnNode(result);
+        }
+      }
+
+      return NULL;
+    }
+  }  // end of TypedSingletonIterator
+
+  /*******************************************************************
+   *                End of nested iterators
+   *******************************************************************/
+
+
+  // %OPT% Array references which are used to cache the map0 arrays in
+  // SuballocatedIntVectors. Using the cached arrays reduces the level
+  // of indirection and results in better performance than just calling
+  // SuballocatedIntVector.elementAt().
+  private int[] m_exptype_map0;
+  private int[] m_nextsib_map0;
+  private int[] m_firstch_map0;
+  private int[] m_parent_map0;
+
+  // Double array references to the map arrays in SuballocatedIntVectors.
+  private int[][] m_exptype_map;
+  private int[][] m_nextsib_map;
+  private int[][] m_firstch_map;
+  private int[][] m_parent_map;
+
+  // %OPT% Cache the array of extended types in this class
+  protected ExtendedType[] m_extendedTypes;
+
+  // A Vector which is used to store the values of attribute, namespace,
+  // comment and PI nodes.
+  //
+  // %OPT% These values are unlikely to be equal. Storing
+  // them in a plain Vector is more efficient than storing in the
+  // DTMStringPool because we can save the cost for hash calculation.
+  //
+  // %REVISIT% Do we need a custom class (e.g. StringVector) here?
+  protected Vector m_values;
+
+  // The current index into the m_values Vector.
+  private int m_valueIndex = 0;
+
+  // The maximum value of the current node index.
+  private int m_maxNodeIndex;
+
+  // Cache the shift and mask values for the SuballocatedIntVectors.
+  protected int m_SHIFT;
+  protected int m_MASK;
+  protected int m_blocksize;
+
+  /** %OPT% If the offset and length of a Text node are within certain limits,
+   * we store a bitwise encoded value into an int, using 10 bits (max. 1024)
+   * for length and 21 bits for offset. We can save two SuballocatedIntVector
+   * calls for each getStringValueX() and dispatchCharacterEvents() call by
+   * doing this.
+   */
+  // The number of bits for the length of a Text node.
+  protected final static int TEXT_LENGTH_BITS = 10;
+
+  // The number of bits for the offset of a Text node.
+  protected final static int TEXT_OFFSET_BITS = 21;
+
+  // The maximum length value
+  protected final static int TEXT_LENGTH_MAX = (1<<TEXT_LENGTH_BITS) - 1;
+
+  // The maximum offset value
+  protected final static int TEXT_OFFSET_MAX = (1<<TEXT_OFFSET_BITS) - 1;
+
+  // True if we want to build the ID index table.
+  protected boolean m_buildIdIndex = true;
+
+  // Constant for empty String
+  private static final String EMPTY_STR = "";
+
+  // Constant for empty XMLString
+  private static final XMLString EMPTY_XML_STR = new XMLStringDefault("");
+
+  /**
+   * Construct a SAX2DTM2 object using the default block size.
+   */
+  public SAX2DTM2(DTMManager mgr, Source source, int dtmIdentity,
+                 DTMWSFilter whiteSpaceFilter,
+                 XMLStringFactory xstringfactory,
+                 boolean doIndexing)
+  {
+
+    this(mgr, source, dtmIdentity, whiteSpaceFilter,
+          xstringfactory, doIndexing, DEFAULT_BLOCKSIZE, true, true, false);
+  }
+
+  /**
+   * Construct a SAX2DTM2 object using the given block size.
+   */
+  public SAX2DTM2(DTMManager mgr, Source source, int dtmIdentity,
+                 DTMWSFilter whiteSpaceFilter,
+                 XMLStringFactory xstringfactory,
+                 boolean doIndexing,
+                 int blocksize,
+                 boolean usePrevsib,
+                 boolean buildIdIndex,
+                 boolean newNameTable)
+  {
+
+    super(mgr, source, dtmIdentity, whiteSpaceFilter,
+          xstringfactory, doIndexing, blocksize, usePrevsib, newNameTable);
+
+    // Initialize the values of m_SHIFT and m_MASK.
+    int shift;
+    for(shift=0; (blocksize>>>=1) != 0; ++shift);
+
+    m_blocksize = 1<<shift;
+    m_SHIFT = shift;
+    m_MASK = m_blocksize - 1;
+
+    m_buildIdIndex = buildIdIndex;
+
+    // Some documents do not have attribute nodes. That is why
+    // we set the initial size of this Vector to be small and set
+    // the increment to a bigger number.
+    m_values = new Vector(32, 512);
+
+    m_maxNodeIndex = 1 << DTMManager.IDENT_DTM_NODE_BITS;
+
+    // Set the map0 values in the constructor.
+    m_exptype_map0 = m_exptype.getMap0();
+    m_nextsib_map0 = m_nextsib.getMap0();
+    m_firstch_map0 = m_firstch.getMap0();
+    m_parent_map0  = m_parent.getMap0();
+  }
+
+  /**
+   * Override DTMDefaultBase._exptype() by dropping the incremental code.
+   *
+   * <p>This one is less efficient than _exptype2. It is only used during
+   * DTM building. _exptype2 is used after the document is fully built.
+   */
+  public final int _exptype(int identity)
+  {
+    return m_exptype.elementAt(identity);
+  }
+
+  /************************************************************************
+   *             DTM base accessor interfaces
+   *
+   * %OPT% The code in the following interfaces (e.g. _exptype2, etc.) are
+   * very important to the DTM performance. To have the best performace,
+   * these several interfaces have direct access to the internal arrays of
+   * the SuballocatedIntVectors. The final modifier also has a noticeable
+   * impact on performance.
+   ***********************************************************************/
+
+  /**
+   * The optimized version of DTMDefaultBase._exptype().
+   *
+   * @param identity A node identity, which <em>must not</em> be equal to
+   *        <code>DTM.NULL</code>
+   */
+  public final int _exptype2(int identity)
+  {
+    //return m_exptype.elementAt(identity);
+
+    if (identity < m_blocksize)
+      return m_exptype_map0[identity];
+    else
+      return m_exptype_map[identity>>>m_SHIFT][identity&m_MASK];
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase._nextsib().
+   *
+   * @param identity A node identity, which <em>must not</em> be equal to
+   *        <code>DTM.NULL</code>
+   */
+  public final int _nextsib2(int identity)
+  {
+    //return m_nextsib.elementAt(identity);
+
+    if (identity < m_blocksize)
+      return m_nextsib_map0[identity];
+    else
+      return m_nextsib_map[identity>>>m_SHIFT][identity&m_MASK];
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase._firstch().
+   *
+   * @param identity A node identity, which <em>must not</em> be equal to
+   *        <code>DTM.NULL</code>
+   */
+  public final int _firstch2(int identity)
+  {
+    //return m_firstch.elementAt(identity);
+
+    if (identity < m_blocksize)
+      return m_firstch_map0[identity];
+    else
+      return m_firstch_map[identity>>>m_SHIFT][identity&m_MASK];
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase._parent().
+   *
+   * @param identity A node identity, which <em>must not</em> be equal to
+   *        <code>DTM.NULL</code>
+   */
+  public final int _parent2(int identity)
+  {
+    //return m_parent.elementAt(identity);
+
+    if (identity < m_blocksize)
+      return m_parent_map0[identity];
+    else
+      return m_parent_map[identity>>>m_SHIFT][identity&m_MASK];
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase._type().
+   *
+   * @param identity A node identity, which <em>must not</em> be equal to
+   *        <code>DTM.NULL</code>
+   */
+  public final int _type2(int identity)
+  {
+    //int eType = _exptype2(identity);
+    int eType;
+    if (identity < m_blocksize)
+      eType = m_exptype_map0[identity];
+    else
+      eType = m_exptype_map[identity>>>m_SHIFT][identity&m_MASK];
+
+    if (NULL != eType)
+      return m_extendedTypes[eType].getNodeType();
+    else
+      return NULL;
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase.getExpandedTypeID(int).
+   *
+   * <p>This one is only used by DOMAdapter.getExpandedTypeID(int), which
+   * is mostly called from the compiled translets.
+   */
+  public final int getExpandedTypeID2(int nodeHandle)
+  {
+    int nodeID = makeNodeIdentity(nodeHandle);
+
+    //return (nodeID != NULL) ? _exptype2(nodeID) : NULL;
+
+    if (nodeID != NULL) {
+      if (nodeID < m_blocksize)
+        return m_exptype_map0[nodeID];
+      else
+        return m_exptype_map[nodeID>>>m_SHIFT][nodeID&m_MASK];
+    }
+    else
+      return NULL;
+  }
+
+  /*************************************************************************
+   *                 END of DTM base accessor interfaces
+   *************************************************************************/
+
+
+  /**
+   * Return the node type from the expanded type
+   */
+  public final int _exptype2Type(int exptype)
+  {
+    if (NULL != exptype)
+      return m_extendedTypes[exptype].getNodeType();
+    else
+      return NULL;
+  }
+
+  /**
+   * Get a prefix either from the uri mapping, or just make
+   * one up!
+   *
+   * @param uri The namespace URI, which may be null.
+   *
+   * @return The prefix if there is one, or null.
+   */
+  public int getIdForNamespace(String uri)
+  {
+     int index = m_values.indexOf(uri);
+     if (index < 0)
+     {
+       m_values.addElement(uri);
+       return m_valueIndex++;
+     }
+     else
+       return index;
+  }
+
+  /**
+   * Override SAX2DTM.startElement()
+   *
+   * <p>Receive notification of the start of an element.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the start of
+   * each element (such as allocating a new tree node or writing
+   * output to a file).</p>
+   *
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param qName The qualified name (with prefix), or the
+   *        empty string if qualified names are not available.
+   * @param attributes The specified or defaulted attributes.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startElement
+   */
+  public void startElement(String uri, String localName, String qName, Attributes attributes)
+      throws SAXException
+  {
+
+    charactersFlush();
+
+    int exName = m_expandedNameTable.getExpandedTypeID(uri, localName, DTM.ELEMENT_NODE);
+
+    int prefixIndex = (qName.length() != localName.length())
+                      ? m_valuesOrPrefixes.stringToIndex(qName) : 0;
+
+    int elemNode = addNode(DTM.ELEMENT_NODE, exName,
+                           m_parents.peek(), m_previous, prefixIndex, true);
+
+    if(m_indexing)
+      indexNode(exName, elemNode);
+
+    m_parents.push(elemNode);
+
+    int startDecls = m_contextIndexes.peek();
+    int nDecls = m_prefixMappings.size();
+    String prefix;
+
+    if(!m_pastFirstElement)
+    {
+      // SPECIAL CASE: Implied declaration at root element
+      prefix="xml";
+      String declURL = "http://www.w3.org/XML/1998/namespace";
+      exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE);
+      m_values.addElement(declURL);
+      int val = m_valueIndex++;
+      addNode(DTM.NAMESPACE_NODE, exName, elemNode,
+                     DTM.NULL, val, false);
+      m_pastFirstElement=true;
+    }
+
+    for (int i = startDecls; i < nDecls; i += 2)
+    {
+      prefix = (String) m_prefixMappings.elementAt(i);
+
+      if (prefix == null)
+        continue;
+
+      String declURL = (String) m_prefixMappings.elementAt(i + 1);
+
+      exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE);
+
+      m_values.addElement(declURL);
+      int val = m_valueIndex++;
+
+      addNode(DTM.NAMESPACE_NODE, exName, elemNode, DTM.NULL, val, false);
+    }
+
+    int n = attributes.getLength();
+
+    for (int i = 0; i < n; i++)
+    {
+      String attrUri = attributes.getURI(i);
+      String attrQName = attributes.getQName(i);
+      String valString = attributes.getValue(i);
+
+      int nodeType;
+
+      String attrLocalName = attributes.getLocalName(i);
+
+      if ((null != attrQName)
+              && (attrQName.equals("xmlns")
+                  || attrQName.startsWith("xmlns:")))
+      {
+        prefix = getPrefix(attrQName, attrUri);
+        if (declAlreadyDeclared(prefix))
+          continue;  // go to the next attribute.
+
+        nodeType = DTM.NAMESPACE_NODE;
+      }
+      else
+      {
+        nodeType = DTM.ATTRIBUTE_NODE;
+
+        if (m_buildIdIndex && attributes.getType(i).equalsIgnoreCase("ID"))
+          setIDAttribute(valString, elemNode);
+      }
+
+      // Bit of a hack... if somehow valString is null, stringToIndex will
+      // return -1, which will make things very unhappy.
+      if(null == valString)
+        valString = "";
+
+      m_values.addElement(valString);
+      int val = m_valueIndex++;
+
+      if (attrLocalName.length() != attrQName.length())
+      {
+
+        prefixIndex = m_valuesOrPrefixes.stringToIndex(attrQName);
+
+        int dataIndex = m_data.size();
+
+        m_data.addElement(prefixIndex);
+        m_data.addElement(val);
+
+        val = -dataIndex;
+      }
+
+      exName = m_expandedNameTable.getExpandedTypeID(attrUri, attrLocalName, nodeType);
+      addNode(nodeType, exName, elemNode, DTM.NULL, val,
+                     false);
+    }
+
+    if (null != m_wsfilter)
+    {
+      short wsv = m_wsfilter.getShouldStripSpace(makeNodeHandle(elemNode), this);
+      boolean shouldStrip = (DTMWSFilter.INHERIT == wsv)
+                            ? getShouldStripWhitespace()
+                            : (DTMWSFilter.STRIP == wsv);
+
+      pushShouldStripWhitespace(shouldStrip);
+    }
+
+    m_previous = DTM.NULL;
+
+    m_contextIndexes.push(m_prefixMappings.size());  // for the children.
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * <p>By default, do nothing.  Application writers may override this
+   * method in a subclass to take specific actions at the end of
+   * each element (such as finalising a tree node or writing
+   * output to a file).</p>
+   *
+   * @param uri The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param qName The qualified XML 1.0 name (with prefix), or the
+   *        empty string if qualified names are not available.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endElement
+   */
+  public void endElement(String uri, String localName, String qName)
+          throws SAXException
+  {
+    charactersFlush();
+
+    // If no one noticed, startPrefixMapping is a drag.
+    // Pop the context for the last child (the one pushed by startElement)
+    m_contextIndexes.quickPop(1);
+
+    // Do it again for this one (the one pushed by the last endElement).
+    int topContextIndex = m_contextIndexes.peek();
+    if (topContextIndex != m_prefixMappings.size()) {
+      m_prefixMappings.setSize(topContextIndex);
+    }
+
+    m_previous = m_parents.pop();
+
+    popShouldStripWhitespace();
+  }
+
+  /**
+   * Report an XML comment anywhere in the document.
+   *
+   * <p>This callback will be used for comments inside or outside the
+   * document element, including comments in the external DTD
+   * subset (if read).</p>
+   *
+   * @param ch An array holding the characters in the comment.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   * @throws SAXException The application may raise an exception.
+   */
+  public void comment(char ch[], int start, int length) throws SAXException
+  {
+
+    if (m_insideDTD)      // ignore comments if we're inside the DTD
+      return;
+
+    charactersFlush();
+
+    // %OPT% Saving the comment string in a Vector has a lower cost than
+    // saving it in DTMStringPool.
+    m_values.addElement(new String(ch, start, length));
+    int dataIndex = m_valueIndex++;
+
+    m_previous = addNode(DTM.COMMENT_NODE, DTM.COMMENT_NODE,
+                         m_parents.peek(), m_previous, dataIndex, false);
+  }
+
+  /**
+   * Receive notification of the beginning of the document.
+   *
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startDocument
+   */
+  public void startDocument() throws SAXException
+  {
+
+    int doc = addNode(DTM.DOCUMENT_NODE,
+                      DTM.DOCUMENT_NODE,
+                      DTM.NULL, DTM.NULL, 0, true);
+
+    m_parents.push(doc);
+    m_previous = DTM.NULL;
+
+    m_contextIndexes.push(m_prefixMappings.size());  // for the next element.
+  }
+
+  /**
+   * Receive notification of the end of the document.
+   *
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endDocument
+   */
+  public void endDocument() throws SAXException
+  {
+    super.endDocument();
+
+    // Add a NULL entry to the end of the node arrays as
+    // the end indication.
+    m_exptype.addElement(NULL);
+    m_parent.addElement(NULL);
+    m_nextsib.addElement(NULL);
+    m_firstch.addElement(NULL);
+
+    // Set the cached references after the document is built.
+    m_extendedTypes = m_expandedNameTable.getExtendedTypes();
+    m_exptype_map = m_exptype.getMap();
+    m_nextsib_map = m_nextsib.getMap();
+    m_firstch_map = m_firstch.getMap();
+    m_parent_map  = m_parent.getMap();
+  }
+
+  /**
+   * Construct the node map from the node.
+   *
+   * @param type raw type ID, one of DTM.XXX_NODE.
+   * @param expandedTypeID The expended type ID.
+   * @param parentIndex The current parent index.
+   * @param previousSibling The previous sibling index.
+   * @param dataOrPrefix index into m_data table, or string handle.
+   * @param canHaveFirstChild true if the node can have a first child, false
+   *                          if it is atomic.
+   *
+   * @return The index identity of the node that was added.
+   */
+  protected final int addNode(int type, int expandedTypeID,
+                        int parentIndex, int previousSibling,
+                        int dataOrPrefix, boolean canHaveFirstChild)
+  {
+    // Common to all nodes:
+    int nodeIndex = m_size++;
+
+    // Have we overflowed a DTM Identity's addressing range?
+    //if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS))
+    if (nodeIndex == m_maxNodeIndex)
+    {
+      addNewDTMID(nodeIndex);
+      m_maxNodeIndex += (1 << DTMManager.IDENT_DTM_NODE_BITS);
+    }
+
+    m_firstch.addElement(DTM.NULL);
+    m_nextsib.addElement(DTM.NULL);
+    m_parent.addElement(parentIndex);
+    m_exptype.addElement(expandedTypeID);
+    m_dataOrQName.addElement(dataOrPrefix);
+
+    if (m_prevsib != null) {
+      m_prevsib.addElement(previousSibling);
+    }
+
+    if (m_locator != null && m_useSourceLocationProperty) {
+      setSourceLocation();
+    }
+
+    // Note that nextSibling is not processed until charactersFlush()
+    // is called, to handle successive characters() events.
+
+    // Special handling by type: Declare namespaces, attach first child
+    switch(type)
+    {
+    case DTM.NAMESPACE_NODE:
+      declareNamespaceInContext(parentIndex,nodeIndex);
+      break;
+    case DTM.ATTRIBUTE_NODE:
+      break;
+    default:
+      if (DTM.NULL != previousSibling) {
+        m_nextsib.setElementAt(nodeIndex,previousSibling);
+      }
+      else if (DTM.NULL != parentIndex) {
+        m_firstch.setElementAt(nodeIndex,parentIndex);
+      }
+      break;
+    }
+
+    return nodeIndex;
+  }
+
+  /**
+   * Check whether accumulated text should be stripped; if not,
+   * append the appropriate flavor of text/cdata node.
+   */
+  protected final void charactersFlush()
+  {
+
+    if (m_textPendingStart >= 0)  // -1 indicates no-text-in-progress
+    {
+      int length = m_chars.size() - m_textPendingStart;
+      boolean doStrip = false;
+
+      if (getShouldStripWhitespace())
+      {
+        doStrip = m_chars.isWhitespace(m_textPendingStart, length);
+      }
+
+      if (doStrip) {
+        m_chars.setLength(m_textPendingStart);  // Discard accumulated text
+      } else {
+        // Guard against characters/ignorableWhitespace events that
+        // contained no characters.  They should not result in a node.
+        if (length > 0) {
+          // If the offset and length do not exceed the given limits
+          // (offset < 2^21 and length < 2^10), then save both the offset
+          // and length in a bitwise encoded value.
+          if (length <= TEXT_LENGTH_MAX
+                  && m_textPendingStart <= TEXT_OFFSET_MAX) {
+            m_previous = addNode(m_coalescedTextType, DTM.TEXT_NODE,
+                             m_parents.peek(), m_previous,
+                             length + (m_textPendingStart << TEXT_LENGTH_BITS),
+                             false);
+
+          } else {
+            // Store offset and length in the m_data array if one exceeds 
+            // the given limits. Use a negative dataIndex as an indication.
+            int dataIndex = m_data.size();
+            m_previous = addNode(m_coalescedTextType, DTM.TEXT_NODE,
+                               m_parents.peek(), m_previous, -dataIndex, false);
+
+            m_data.addElement(m_textPendingStart);
+            m_data.addElement(length);
+          }
+        }
+      }
+
+      // Reset for next text block
+      m_textPendingStart = -1;
+      m_textType = m_coalescedTextType = DTM.TEXT_NODE;
+    }
+  }
+
+  /**
+   * Override the processingInstruction() interface in SAX2DTM2.
+   * <p>
+   * %OPT% This one is different from SAX2DTM.processingInstruction()
+   * in that we do not use extended types for PI nodes. The name of
+   * the PI is saved in the DTMStringPool.
+   *
+   * Receive notification of a processing instruction.
+   *
+   * @param target The processing instruction target.
+   * @param data The processing instruction data, or null if
+   *             none is supplied.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   */
+  public void processingInstruction(String target, String data)
+	  throws SAXException
+  {
+
+    charactersFlush();
+
+    int dataIndex = m_data.size();
+    m_previous = addNode(DTM.PROCESSING_INSTRUCTION_NODE,
+			 DTM.PROCESSING_INSTRUCTION_NODE,
+			 m_parents.peek(), m_previous,
+			 -dataIndex, false);
+
+    m_data.addElement(m_valuesOrPrefixes.stringToIndex(target));
+    m_values.addElement(data);
+    m_data.addElement(m_valueIndex++);
+
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase.getFirstAttribute().
+   * <p>
+   * Given a node handle, get the index of the node's first attribute.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @return Handle of first attribute, or DTM.NULL to indicate none exists.
+   */
+  public final int getFirstAttribute(int nodeHandle)
+  {
+    int nodeID = makeNodeIdentity(nodeHandle);
+
+    if (nodeID == DTM.NULL)
+      return DTM.NULL;
+
+    int type = _type2(nodeID);
+
+    if (DTM.ELEMENT_NODE == type)
+    {
+      // Assume that attributes and namespaces immediately follow the element.
+      while (true)
+      {
+        nodeID++;
+	// Assume this can not be null.
+	type = _type2(nodeID);
+
+	if (type == DTM.ATTRIBUTE_NODE)
+	{
+	  return makeNodeHandle(nodeID);
+	}
+	else if (DTM.NAMESPACE_NODE != type)
+	{
+	  break;
+	}
+      }
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase.getFirstAttributeIdentity(int).
+   * <p>
+   * Given a node identity, get the index of the node's first attribute.
+   *
+   * @param identity int identity of the node.
+   * @return Identity of first attribute, or DTM.NULL to indicate none exists.
+   */
+  protected int getFirstAttributeIdentity(int identity) {
+    if (identity == NULL) {
+        return NULL;
+    }
+    int type = _type2(identity);
+
+    if (DTM.ELEMENT_NODE == type)
+    {
+      // Assume that attributes and namespaces immediately follow the element.
+      while (true)
+      {
+        identity++;
+
+        // Assume this can not be null.
+        type = _type2(identity);
+
+        if (type == DTM.ATTRIBUTE_NODE)
+        {
+          return identity;
+        }
+        else if (DTM.NAMESPACE_NODE != type)
+        {
+          break;
+        }
+      }
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase.getNextAttributeIdentity(int).
+   * <p>
+   * Given a node identity for an attribute, advance to the next attribute.
+   *
+   * @param identity int identity of the attribute node.  This
+   * <strong>must</strong> be an attribute node.
+   *
+   * @return int DTM node-identity of the resolved attr,
+   * or DTM.NULL to indicate none exists.
+   *
+   */
+  protected int getNextAttributeIdentity(int identity) {
+    // Assume that attributes and namespace nodes immediately follow the element
+    while (true) {
+      identity++;
+      int type = _type2(identity);
+
+      if (type == DTM.ATTRIBUTE_NODE) {
+        return identity;
+      } else if (type != DTM.NAMESPACE_NODE) {
+        break;
+      }
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * The optimized version of DTMDefaultBase.getTypedAttribute(int, int).
+   * <p>
+   * Given a node handle and an expanded type ID, get the index of the node's
+   * attribute of that type, if any.
+   *
+   * @param nodeHandle int Handle of the node.
+   * @param attType int expanded type ID of the required attribute.
+   * @return Handle of attribute of the required type, or DTM.NULL to indicate
+   * none exists.
+   */
+  protected final int getTypedAttribute(int nodeHandle, int attType)
+  {
+
+    int nodeID = makeNodeIdentity(nodeHandle);
+
+    if (nodeID == DTM.NULL)
+      return DTM.NULL;
+
+    int type = _type2(nodeID);
+
+    if (DTM.ELEMENT_NODE == type)
+    {
+      int expType;
+      while (true)
+      {
+	nodeID++;
+	expType = _exptype2(nodeID);
+
+	if (expType != DTM.NULL)
+	  type = m_extendedTypes[expType].getNodeType();
+	else
+	  return DTM.NULL;
+
+	if (type == DTM.ATTRIBUTE_NODE)
+	{
+	  if (expType == attType) return makeNodeHandle(nodeID);
+	}
+	else if (DTM.NAMESPACE_NODE != type)
+	{
+	  break;
+	}
+      }
+    }
+
+    return DTM.NULL;
+  }
+
+  /**
+   * Override SAX2DTM.getLocalName() in SAX2DTM2.
+   * <p>Processing for PIs is different.
+   *
+   * Given a node handle, return its XPath- style localname. (As defined in
+   * Namespaces, this is the portion of the name after any colon character).
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Local name of this node.
+   */
+  public String getLocalName(int nodeHandle)
+  {
+    int expType = _exptype(makeNodeIdentity(nodeHandle));
+
+    if (expType == DTM.PROCESSING_INSTRUCTION_NODE)
+    {
+      int dataIndex = _dataOrQName(makeNodeIdentity(nodeHandle));
+      dataIndex = m_data.elementAt(-dataIndex);
+      return m_valuesOrPrefixes.indexToString(dataIndex);
+    }
+    else
+      return m_expandedNameTable.getLocalName(expType);
+  }
+
+  /**
+   * The optimized version of SAX2DTM.getNodeNameX().
+   * <p>
+   * Given a node handle, return the XPath node name. This should be the name
+   * as described by the XPath data model, NOT the DOM- style name.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   */
+  public final String getNodeNameX(int nodeHandle)
+  {
+
+    int nodeID = makeNodeIdentity(nodeHandle);
+    int eType = _exptype2(nodeID);
+
+    if (eType == DTM.PROCESSING_INSTRUCTION_NODE)
+    {
+      int dataIndex = _dataOrQName(nodeID);
+      dataIndex = m_data.elementAt(-dataIndex);
+      return m_valuesOrPrefixes.indexToString(dataIndex);
+    }
+
+    final ExtendedType extType = m_extendedTypes[eType];
+
+    if (extType.getNamespace().length() == 0)
+    {
+      return extType.getLocalName();
+    }
+    else
+    {
+      int qnameIndex = m_dataOrQName.elementAt(nodeID);
+
+      if (qnameIndex == 0)
+        return extType.getLocalName();
+
+      if (qnameIndex < 0)
+      {
+	qnameIndex = -qnameIndex;
+	qnameIndex = m_data.elementAt(qnameIndex);
+      }
+
+      return m_valuesOrPrefixes.indexToString(qnameIndex);
+    }
+  }
+
+  /**
+   * The optimized version of SAX2DTM.getNodeName().
+   * <p>
+   * Given a node handle, return its DOM-style node name. This will include
+   * names such as #text or #document.
+   *
+   * @param nodeHandle the id of the node.
+   * @return String Name of this node, which may be an empty string.
+   * %REVIEW% Document when empty string is possible...
+   * %REVIEW-COMMENT% It should never be empty, should it?
+   */
+  public String getNodeName(int nodeHandle)
+  {
+
+    int nodeID = makeNodeIdentity(nodeHandle);
+    int eType = _exptype2(nodeID);
+
+    final ExtendedType extType = m_extendedTypes[eType];
+    if (extType.getNamespace().length() == 0)
+    {
+      int type = extType.getNodeType();
+
+      String localName = extType.getLocalName();
+      if (type == DTM.NAMESPACE_NODE)
+      {
+	if (localName.length() == 0)
+	  return "xmlns";
+	else
+	  return "xmlns:" + localName;
+      }
+      else if (type == DTM.PROCESSING_INSTRUCTION_NODE)
+      {
+	int dataIndex = _dataOrQName(nodeID);
+	dataIndex = m_data.elementAt(-dataIndex);
+	return m_valuesOrPrefixes.indexToString(dataIndex);
+      }
+      else if (localName.length() == 0)
+      {
+        return getFixedNames(type);
+      }
+      else
+	return localName;
+    }
+    else
+    {
+      int qnameIndex = m_dataOrQName.elementAt(nodeID);
+
+      if (qnameIndex == 0)
+        return extType.getLocalName();
+
+      if (qnameIndex < 0)
+      {
+	qnameIndex = -qnameIndex;
+	qnameIndex = m_data.elementAt(qnameIndex);
+      }
+
+      return m_valuesOrPrefixes.indexToString(qnameIndex);
+    }
+  }
+
+  /**
+   * Override SAX2DTM.getStringValue(int)
+   * <p>
+   * This method is only used by Xalan-J Interpretive. It is not used by XSLTC.
+   * <p>
+   * If the caller supplies an XMLStringFactory, the getStringValue() interface
+   * in SAX2DTM will be called. Otherwise just calls getStringValueX() and
+   * wraps the returned String in an XMLString.
+   *
+   * Get the string-value of a node as a String object
+   * (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value).
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A string object that represents the string-value of the given node.
+   */
+  public XMLString getStringValue(int nodeHandle)
+  {
+    int identity = makeNodeIdentity(nodeHandle);
+    if (identity == DTM.NULL)
+      return EMPTY_XML_STR;
+
+    int type= _type2(identity);
+
+    if (type == DTM.ELEMENT_NODE || type == DTM.DOCUMENT_NODE)
+    {
+      int startNode = identity;
+      identity = _firstch2(identity);
+      if (DTM.NULL != identity)
+      {
+	int offset = -1;
+	int length = 0;
+
+	do
+	{
+	  type = _exptype2(identity);
+
+	  if (type == DTM.TEXT_NODE || type == DTM.CDATA_SECTION_NODE)
+	  {
+	    int dataIndex = m_dataOrQName.elementAt(identity);
+	    if (dataIndex >= 0)
+	    {
+	      if (-1 == offset)
+	      {
+                offset = dataIndex >>> TEXT_LENGTH_BITS;
+	      }
+
+	      length += dataIndex & TEXT_LENGTH_MAX;
+	    }
+	    else
+	    {
+	      if (-1 == offset)
+	      {
+                offset = m_data.elementAt(-dataIndex);
+	      }
+
+	      length += m_data.elementAt(-dataIndex + 1);
+	    }
+	  }
+
+	  identity++;
+	} while (_parent2(identity) >= startNode);
+
+	if (length > 0)
+	{
+	  if (m_xstrf != null)
+	    return m_xstrf.newstr(m_chars, offset, length);
+	  else
+	    return new XMLStringDefault(m_chars.getString(offset, length));
+	}
+	else
+	  return EMPTY_XML_STR;
+      }
+      else
+        return EMPTY_XML_STR;
+    }
+    else if (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type)
+    {
+      int dataIndex = m_dataOrQName.elementAt(identity);
+      if (dataIndex >= 0)
+      {
+      	if (m_xstrf != null)
+      	  return m_xstrf.newstr(m_chars, dataIndex >>> TEXT_LENGTH_BITS,
+      	                 dataIndex & TEXT_LENGTH_MAX);
+      	else
+      	  return new XMLStringDefault(m_chars.getString(dataIndex >>> TEXT_LENGTH_BITS,
+      	                              dataIndex & TEXT_LENGTH_MAX));
+      }
+      else
+      {
+        if (m_xstrf != null)
+          return m_xstrf.newstr(m_chars, m_data.elementAt(-dataIndex),
+                                m_data.elementAt(-dataIndex+1));
+        else
+          return new XMLStringDefault(m_chars.getString(m_data.elementAt(-dataIndex),
+                                   m_data.elementAt(-dataIndex+1)));
+      }
+    }
+    else
+    {
+      int dataIndex = m_dataOrQName.elementAt(identity);
+
+      if (dataIndex < 0)
+      {
+        dataIndex = -dataIndex;
+        dataIndex = m_data.elementAt(dataIndex + 1);
+      }
+
+      if (m_xstrf != null)
+        return m_xstrf.newstr((String)m_values.elementAt(dataIndex));
+      else
+        return new XMLStringDefault((String)m_values.elementAt(dataIndex));
+    }
+  }
+
+  /**
+   * The optimized version of SAX2DTM.getStringValue(int).
+   * <p>
+   * %OPT% This is one of the most often used interfaces. Performance is
+   * critical here. This one is different from SAX2DTM.getStringValue(int) in
+   * that it returns a String instead of a XMLString.
+   *
+   * Get the string- value of a node as a String object (see http: //www. w3.
+   * org/TR/xpath#data- model for the definition of a node's string- value).
+   *
+   * @param nodeHandle The node ID.
+   *
+   * @return A string object that represents the string-value of the given node.
+   */
+  public final String getStringValueX(final int nodeHandle)
+  {
+    int identity = makeNodeIdentity(nodeHandle);
+    if (identity == DTM.NULL)
+      return EMPTY_STR;
+
+    int type= _type2(identity);
+
+    if (type == DTM.ELEMENT_NODE || type == DTM.DOCUMENT_NODE)
+    {
+      int startNode = identity;
+      identity = _firstch2(identity);
+      if (DTM.NULL != identity)
+      {
+	int offset = -1;
+	int length = 0;
+
+	do
+	{
+	  type = _exptype2(identity);
+
+	  if (type == DTM.TEXT_NODE || type == DTM.CDATA_SECTION_NODE)
+	  {
+	    int dataIndex = m_dataOrQName.elementAt(identity);
+	    if (dataIndex >= 0)
+	    {
+	      if (-1 == offset)
+	      {
+                offset = dataIndex >>> TEXT_LENGTH_BITS;
+	      }
+
+	      length += dataIndex & TEXT_LENGTH_MAX;
+	    }
+	    else
+	    {
+	      if (-1 == offset)
+	      {
+                offset = m_data.elementAt(-dataIndex);
+	      }
+
+	      length += m_data.elementAt(-dataIndex + 1);
+	    }
+	  }
+
+	  identity++;
+	} while (_parent2(identity) >= startNode);
+
+	if (length > 0)
+	{
+	  return m_chars.getString(offset, length);
+	}
+	else
+	  return EMPTY_STR;
+      }
+      else
+        return EMPTY_STR;
+    }
+    else if (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type)
+    {
+      int dataIndex = m_dataOrQName.elementAt(identity);
+      if (dataIndex >= 0)
+      {
+      	return m_chars.getString(dataIndex >>> TEXT_LENGTH_BITS,
+      	                          dataIndex & TEXT_LENGTH_MAX);
+      }
+      else
+      {
+        return m_chars.getString(m_data.elementAt(-dataIndex),
+                                  m_data.elementAt(-dataIndex+1));
+      }
+    }
+    else
+    {
+      int dataIndex = m_dataOrQName.elementAt(identity);
+
+      if (dataIndex < 0)
+      {
+        dataIndex = -dataIndex;
+        dataIndex = m_data.elementAt(dataIndex + 1);
+      }
+
+      return (String)m_values.elementAt(dataIndex);
+    }
+  }
+
+  /**
+   * Returns the string value of the entire tree
+   */
+  public String getStringValue()
+  {
+    int child = _firstch2(ROOTNODE);
+    if (child == DTM.NULL) return EMPTY_STR;
+
+    // optimization: only create StringBuffer if > 1 child
+    if ((_exptype2(child) == DTM.TEXT_NODE) && (_nextsib2(child) == DTM.NULL))
+    {
+      int dataIndex = m_dataOrQName.elementAt(child);
+      if (dataIndex >= 0)
+        return m_chars.getString(dataIndex >>> TEXT_LENGTH_BITS, dataIndex & TEXT_LENGTH_MAX);
+      else
+        return m_chars.getString(m_data.elementAt(-dataIndex),
+                                  m_data.elementAt(-dataIndex + 1));
+    }
+    else
+      return getStringValueX(getDocument());
+
+  }
+
+  /**
+   * The optimized version of SAX2DTM.dispatchCharactersEvents(int, ContentHandler, boolean).
+   * <p>
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
+   * for the definition of a node's string-value). Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param nodeHandle The node ID.
+   * @param ch A non-null reference to a ContentHandler.
+   * @param normalize true if the content should be normalized according to
+   * the rules for the XPath
+   * <a href="http://www.w3.org/TR/xpath#function-normalize-space">normalize-space</a>
+   * function.
+   *
+   * @throws SAXException
+   */
+  public final void dispatchCharactersEvents(int nodeHandle, ContentHandler ch,
+                                             boolean normalize)
+          throws SAXException
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+
+    if (identity == DTM.NULL)
+      return;
+
+    int type = _type2(identity);
+
+    if (type == DTM.ELEMENT_NODE || type == DTM.DOCUMENT_NODE)
+    {
+      int startNode = identity;
+      identity = _firstch2(identity);
+      if (DTM.NULL != identity)
+      {
+	int offset = -1;
+	int length = 0;
+
+	do
+	{
+	  type = _exptype2(identity);
+
+	  if (type == DTM.TEXT_NODE || type == DTM.CDATA_SECTION_NODE)
+	  {
+	    int dataIndex = m_dataOrQName.elementAt(identity);
+
+	    if (dataIndex >= 0)
+	    {
+	      if (-1 == offset)
+	      {
+                offset = dataIndex >>> TEXT_LENGTH_BITS;
+	      }
+
+	      length += dataIndex & TEXT_LENGTH_MAX;
+	    }
+	    else
+	    {
+	      if (-1 == offset)
+	      {
+                offset = m_data.elementAt(-dataIndex);
+	      }
+
+	      length += m_data.elementAt(-dataIndex + 1);
+	    }
+	  }
+
+	  identity++;
+	} while (_parent2(identity) >= startNode);
+
+	if (length > 0)
+	{
+          if(normalize)
+            m_chars.sendNormalizedSAXcharacters(ch, offset, length);
+          else
+	    m_chars.sendSAXcharacters(ch, offset, length);
+	}
+      }
+    }
+    else if (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type)
+    {
+      int dataIndex = m_dataOrQName.elementAt(identity);
+
+      if (dataIndex >= 0)
+      {
+      	if (normalize)
+      	  m_chars.sendNormalizedSAXcharacters(ch, dataIndex >>> TEXT_LENGTH_BITS,
+      	                                      dataIndex & TEXT_LENGTH_MAX);
+      	else
+      	  m_chars.sendSAXcharacters(ch, dataIndex >>> TEXT_LENGTH_BITS,
+      	                            dataIndex & TEXT_LENGTH_MAX);
+      }
+      else
+      {
+        if (normalize)
+          m_chars.sendNormalizedSAXcharacters(ch, m_data.elementAt(-dataIndex),
+                                              m_data.elementAt(-dataIndex+1));
+        else
+          m_chars.sendSAXcharacters(ch, m_data.elementAt(-dataIndex),
+                                    m_data.elementAt(-dataIndex+1));
+      }
+    }
+    else
+    {
+      int dataIndex = m_dataOrQName.elementAt(identity);
+
+      if (dataIndex < 0)
+      {
+        dataIndex = -dataIndex;
+        dataIndex = m_data.elementAt(dataIndex + 1);
+      }
+
+      String str = (String)m_values.elementAt(dataIndex);
+
+      if(normalize)
+        FastStringBuffer.sendNormalizedSAXcharacters(str.toCharArray(),
+                                                     0, str.length(), ch);
+      else
+        ch.characters(str.toCharArray(), 0, str.length());
+    }
+  }
+
+  /**
+   * Given a node handle, return its node value. This is mostly
+   * as defined by the DOM, but may ignore some conveniences.
+   * <p>
+   *
+   * @param nodeHandle The node id.
+   * @return String Value of this node, or null if not
+   * meaningful for this node type.
+   */
+  public String getNodeValue(int nodeHandle)
+  {
+
+    int identity = makeNodeIdentity(nodeHandle);
+    int type = _type2(identity);
+
+    if (type == DTM.TEXT_NODE || type == DTM.CDATA_SECTION_NODE)
+    {
+      int dataIndex = _dataOrQName(identity);
+      if (dataIndex > 0)
+      {
+      	return m_chars.getString(dataIndex >>> TEXT_LENGTH_BITS,
+      	                          dataIndex & TEXT_LENGTH_MAX);
+      }
+      else
+      {
+        return m_chars.getString(m_data.elementAt(-dataIndex),
+                                  m_data.elementAt(-dataIndex+1));
+      }
+    }
+    else if (DTM.ELEMENT_NODE == type || DTM.DOCUMENT_FRAGMENT_NODE == type
+             || DTM.DOCUMENT_NODE == type)
+    {
+      return null;
+    }
+    else
+    {
+      int dataIndex = m_dataOrQName.elementAt(identity);
+
+      if (dataIndex < 0)
+      {
+        dataIndex = -dataIndex;
+        dataIndex = m_data.elementAt(dataIndex + 1);
+      }
+
+      return (String)m_values.elementAt(dataIndex);
+    }
+  }
+
+    /**
+     * Copy the String value of a Text node to a SerializationHandler
+     */
+    protected final void copyTextNode(final int nodeID, SerializationHandler handler)
+        throws SAXException
+    {
+        if (nodeID != DTM.NULL) {
+      	    int dataIndex = m_dataOrQName.elementAt(nodeID);
+            if (dataIndex >= 0) {
+                m_chars.sendSAXcharacters(handler,
+                                          dataIndex >>> TEXT_LENGTH_BITS,
+                                          dataIndex & TEXT_LENGTH_MAX);
+            } else {
+                m_chars.sendSAXcharacters(handler, m_data.elementAt(-dataIndex),
+                                          m_data.elementAt(-dataIndex+1));
+            }
+        }
+    }
+
+    /**
+     * Copy an Element node to a SerializationHandler.
+     *
+     * @param nodeID The node identity
+     * @param exptype The expanded type of the Element node
+     * @param handler The SerializationHandler
+     * @return The qualified name of the Element node.
+     */
+    protected final String copyElement(int nodeID, int exptype,
+                               SerializationHandler handler)
+        throws SAXException
+    {
+        final ExtendedType extType = m_extendedTypes[exptype];
+        String uri = extType.getNamespace();
+        String name = extType.getLocalName();
+
+        if (uri.length() == 0) {
+            handler.startElement(name);
+            return name;
+        }
+        else {
+            int qnameIndex = m_dataOrQName.elementAt(nodeID);
+
+            if (qnameIndex == 0) {
+                handler.startElement(name);
+                handler.namespaceAfterStartElement(EMPTY_STR, uri);
+                return name;
+            }
+
+            if (qnameIndex < 0) {
+    	        qnameIndex = -qnameIndex;
+    	        qnameIndex = m_data.elementAt(qnameIndex);
+            }
+
+            String qName = m_valuesOrPrefixes.indexToString(qnameIndex);
+            handler.startElement(qName);
+            int prefixIndex = qName.indexOf(':');
+            String prefix;
+            if (prefixIndex > 0) {
+                prefix = qName.substring(0, prefixIndex);
+            }
+            else {
+                prefix = null;
+            }
+            handler.namespaceAfterStartElement(prefix, uri);
+            return qName;
+        }
+
+    }
+
+    /**
+     * Copy  namespace nodes.
+     *
+     * @param nodeID The Element node identity
+     * @param handler The SerializationHandler
+     * @param inScope  true if all namespaces in scope should be copied,
+     *  false if only the namespace declarations should be copied.
+     */
+    protected final void copyNS(final int nodeID, SerializationHandler handler, boolean inScope)
+        throws SAXException
+    {
+        // %OPT% Optimization for documents which does not have any explicit
+        // namespace nodes. For these documents, there is an implicit
+        // namespace node (xmlns:xml="http://www.w3.org/XML/1998/namespace")
+        // declared on the root element node. In this case, there is no
+        // need to do namespace copying. We can safely return without
+        // doing anything.
+        if (m_namespaceDeclSetElements != null &&
+            m_namespaceDeclSetElements.size() == 1 &&
+            m_namespaceDeclSets != null &&
+            ((SuballocatedIntVector)m_namespaceDeclSets.elementAt(0))
+            .size() == 1)
+            return;
+
+        SuballocatedIntVector nsContext = null;
+        int nextNSNode;
+
+        // Find the first namespace node
+        if (inScope) {
+            nsContext = findNamespaceContext(nodeID);
+            if (nsContext == null || nsContext.size() < 1)
+                return;
+            else
+                nextNSNode = makeNodeIdentity(nsContext.elementAt(0));
+        }
+        else
+            nextNSNode = getNextNamespaceNode2(nodeID);
+
+        int nsIndex = 1;
+        while (nextNSNode != DTM.NULL) {
+            // Retrieve the name of the namespace node
+            int eType = _exptype2(nextNSNode);
+            String nodeName = m_extendedTypes[eType].getLocalName();
+
+            // Retrieve the node value of the namespace node
+            int dataIndex = m_dataOrQName.elementAt(nextNSNode);
+
+            if (dataIndex < 0) {
+                dataIndex = -dataIndex;
+                dataIndex = m_data.elementAt(dataIndex + 1);
+            }
+
+            String nodeValue = (String)m_values.elementAt(dataIndex);
+
+            handler.namespaceAfterStartElement(nodeName, nodeValue);
+
+            if (inScope) {
+                if (nsIndex < nsContext.size()) {
+                    nextNSNode = makeNodeIdentity(nsContext.elementAt(nsIndex));
+                    nsIndex++;
+                }
+                else
+                    return;
+            }
+            else
+                nextNSNode = getNextNamespaceNode2(nextNSNode);
+        }
+    }
+
+    /**
+     * Return the next namespace node following the given base node.
+     *
+     * @baseID The node identity of the base node, which can be an
+     * element, attribute or namespace node.
+     * @return The namespace node immediately following the base node.
+     */
+    protected final int getNextNamespaceNode2(int baseID) {
+        int type;
+        while ((type = _type2(++baseID)) == DTM.ATTRIBUTE_NODE);
+
+        if (type == DTM.NAMESPACE_NODE)
+            return baseID;
+        else
+            return NULL;
+    }
+
+    /**
+     * Copy  attribute nodes from an element .
+     *
+     * @param nodeID The Element node identity
+     * @param handler The SerializationHandler
+     */
+    protected final void copyAttributes(final int nodeID, SerializationHandler handler)
+        throws SAXException{
+
+       for(int current = getFirstAttributeIdentity(nodeID); current != DTM.NULL; current = getNextAttributeIdentity(current)){
+            int eType = _exptype2(current);
+            copyAttribute(current, eType, handler);
+       }
+    }
+
+
+
+    /**
+     * Copy an Attribute node to a SerializationHandler
+     *
+     * @param nodeID The node identity
+     * @param exptype The expanded type of the Element node
+     * @param handler The SerializationHandler
+     */
+    protected final void copyAttribute(int nodeID, int exptype,
+        SerializationHandler handler)
+        throws SAXException
+    {
+        /*
+            final String uri = getNamespaceName(node);
+            if (uri.length() != 0) {
+                final String prefix = getPrefix(node);
+                handler.namespaceAfterStartElement(prefix, uri);
+            }
+            handler.addAttribute(getNodeName(node), getNodeValue(node));
+        */
+        final ExtendedType extType = m_extendedTypes[exptype];
+        final String uri = extType.getNamespace();
+        final String localName = extType.getLocalName();
+
+        String prefix = null;
+        String qname = null;
+        int dataIndex = _dataOrQName(nodeID);
+        int valueIndex = dataIndex;
+            if (dataIndex <= 0) {
+                int prefixIndex = m_data.elementAt(-dataIndex);
+                valueIndex = m_data.elementAt(-dataIndex+1);
+                qname = m_valuesOrPrefixes.indexToString(prefixIndex);
+                int colonIndex = qname.indexOf(':');
+                if (colonIndex > 0) {
+                    prefix = qname.substring(0, colonIndex);
+                }
+            }
+            if (uri.length() != 0) {
+                handler.namespaceAfterStartElement(prefix, uri);
+            }
+
+        String nodeName = (prefix != null) ? qname : localName;
+        String nodeValue = (String)m_values.elementAt(valueIndex);
+
+        handler.addAttribute(nodeName, nodeValue);
+    }
+
+}
diff --git a/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2RTFDTM.java b/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2RTFDTM.java
new file mode 100644
index 0000000..da04a06
--- /dev/null
+++ b/src/main/java/org/apache/xml/dtm/ref/sax2dtm/SAX2RTFDTM.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SAX2RTFDTM.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.dtm.ref.sax2dtm;
+
+import javax.xml.transform.Source;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.dtm.DTMWSFilter;
+import org.apache.xml.utils.IntStack;
+import org.apache.xml.utils.IntVector;
+import org.apache.xml.utils.StringVector;
+import org.apache.xml.utils.XMLStringFactory;
+
+import org.xml.sax.SAXException;
+
+/**
+ * This is a subclass of SAX2DTM which has been modified to meet the needs of
+ * Result Tree Frameworks (RTFs). The differences are:
+ *
+ * 1) Multiple XML trees may be appended to the single DTM. This means
+ * that the root node of each document is _not_ node 0. Some code has
+ * had to be deoptimized to support this mode of operation, and an
+ * explicit mechanism for obtaining the Node Handle of the root node
+ * has been provided.
+ *
+ * 2) A stack of these documents is maintained, allowing us to "tail-prune" the
+ * most recently added trees off the end of the DTM as stylesheet elements 
+ * (and thus variable contexts) are exited.
+ *
+ * PLEASE NOTE that this class may be _heavily_ dependent upon the
+ * internals of the SAX2DTM superclass, and must be maintained in
+ * parallel with that code.  Arguably, they should be conditionals
+ * within a single class... but they have deen separated for
+ * performance reasons. (In fact, one could even argue about which is
+ * the superclass and which is the subclass; the current arrangement
+ * is as much about preserving stability of existing code during
+ * development as anything else.)
+ * 
+ * %REVIEW% In fact, since the differences are so minor, I think it
+ * may be possible/practical to fold them back into the base
+ * SAX2DTM. Consider that as a future code-size optimization.
+ * */
+public class SAX2RTFDTM extends SAX2DTM
+{
+  /** Set true to monitor SAX events and similar diagnostic info. */
+  private static final boolean DEBUG = false;
+  
+  /** Most recently started Document, or null if the DTM is empty.  */
+  private int m_currentDocumentNode=NULL;
+  
+  /** Tail-pruning mark: Number of nodes in use */
+  IntStack mark_size=new IntStack();
+  /** Tail-pruning mark: Number of data items in use */
+  IntStack mark_data_size=new IntStack();
+  /** Tail-pruning mark: Number of size-of-data fields in use */
+  IntStack mark_char_size=new IntStack();
+  /** Tail-pruning mark: Number of dataOrQName slots in use */
+  IntStack mark_doq_size=new IntStack();
+  /** Tail-pruning mark: Number of namespace declaration sets in use
+   * %REVIEW% I don't think number of NS sets is ever different from number
+   * of NS elements. We can probabably reduce these to a single stack and save
+   * some storage.
+   * */
+  IntStack mark_nsdeclset_size=new IntStack();
+  /** Tail-pruning mark: Number of naespace declaration elements in use
+   * %REVIEW% I don't think number of NS sets is ever different from number
+   * of NS elements. We can probabably reduce these to a single stack and save
+   * some storage.
+   */
+  IntStack mark_nsdeclelem_size=new IntStack();
+
+  /**
+   * Tail-pruning mark:  initial number of nodes in use
+   */
+  int m_emptyNodeCount;
+
+  /**
+   * Tail-pruning mark:  initial number of namespace declaration sets
+   */
+  int m_emptyNSDeclSetCount;
+
+  /**
+   * Tail-pruning mark:  initial number of namespace declaration elements
+   */
+  int m_emptyNSDeclSetElemsCount;
+
+  /**
+   * Tail-pruning mark:  initial number of data items in use
+   */
+  int m_emptyDataCount;
+
+  /**
+   * Tail-pruning mark:  initial number of characters in use
+   */
+  int m_emptyCharsCount;
+
+  /**
+   * Tail-pruning mark:  default initial number of dataOrQName slots in use
+   */
+  int m_emptyDataQNCount;
+  
+  public SAX2RTFDTM(DTMManager mgr, Source source, int dtmIdentity,
+                 DTMWSFilter whiteSpaceFilter,
+                 XMLStringFactory xstringfactory,
+                 boolean doIndexing)
+  {
+    super(mgr, source, dtmIdentity, whiteSpaceFilter, 
+          xstringfactory, doIndexing);
+          
+    // NEVER track source locators for RTFs; they aren't meaningful. I think.
+    // (If we did track them, we'd need to tail-prune these too.)
+    //org.apache.xalan.processor.TransformerFactoryImpl.m_source_location;
+    m_useSourceLocationProperty=false;
+    m_sourceSystemId = (m_useSourceLocationProperty) ? new StringVector()
+                                                     : null;
+    m_sourceLine = (m_useSourceLocationProperty) ? new IntVector() : null;
+    m_sourceColumn = (m_useSourceLocationProperty) ? new IntVector() : null;
+
+    // Record initial sizes of fields that are pushed and restored
+    // for RTF tail-pruning.  More entries can be popped than pushed, so
+    // we need this to mark the primordial state of the DTM.
+    m_emptyNodeCount = m_size;
+    m_emptyNSDeclSetCount = (m_namespaceDeclSets == null)
+                                 ? 0 : m_namespaceDeclSets.size();
+    m_emptyNSDeclSetElemsCount = (m_namespaceDeclSetElements == null)
+                                      ? 0 : m_namespaceDeclSetElements.size();
+    m_emptyDataCount = m_data.size();
+    m_emptyCharsCount = m_chars.size();
+    m_emptyDataQNCount = m_dataOrQName.size();
+  }
+  
+  /**
+   * Given a DTM, find the owning document node. In the case of
+   * SAX2RTFDTM, which may contain multiple documents, this returns
+   * the <b>most recently started</b> document, or null if the DTM is
+   * empty or no document is currently under construction.
+   *
+   * %REVIEW% Should we continue to report the most recent after
+   * construction has ended? I think not, given that it may have been
+   * tail-pruned.
+   *
+   *  @return int Node handle of Document node, or null if this DTM does not
+   *  contain an "active" document.
+   * */
+  public int getDocument()
+  {
+    return makeNodeHandle(m_currentDocumentNode);
+  }
+
+  /**
+   * Given a node handle, find the owning document node, using DTM semantics
+   * (Document owns itself) rather than DOM semantics (Document has no owner).
+   *
+   * (I'm counting on the fact that getOwnerDocument() is implemented on top
+   * of this call, in the superclass, to avoid having to rewrite that one.
+   * Be careful if that code changes!)
+   *
+   * @param nodeHandle the id of the node.
+   * @return int Node handle of owning document
+   */
+  public int getDocumentRoot(int nodeHandle)
+  {
+    for (int id=makeNodeIdentity(nodeHandle); id!=NULL; id=_parent(id)) {
+      if (_type(id)==DTM.DOCUMENT_NODE) {
+        return makeNodeHandle(id);
+      }
+    }
+
+    return DTM.NULL; // Safety net; should never happen
+  }
+  
+  /**
+   * Given a node identifier, find the owning document node.  Unlike the DOM,
+   * this considers the owningDocument of a Document to be itself. Note that
+   * in shared DTMs this may not be zero.
+   *
+   * @param nodeIdentifier the id of the starting node.
+   * @return int Node identifier of the root of this DTM tree
+   */
+  protected int _documentRoot(int nodeIdentifier)
+  {
+    if(nodeIdentifier==NULL) return NULL;
+
+    for (int parent=_parent(nodeIdentifier);
+         parent!=NULL;
+         nodeIdentifier=parent,parent=_parent(nodeIdentifier))
+      ;
+   
+    return nodeIdentifier;
+  }
+
+  /**
+   * Receive notification of the beginning of a new RTF document.
+   *
+   * %REVIEW% Y'know, this isn't all that much of a deoptimization. We
+   * might want to consider folding the start/endDocument changes back
+   * into the main SAX2DTM so we don't have to expose so many fields
+   * (even as Protected) and carry the additional code.
+   *
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#startDocument
+   * */
+  public void startDocument() throws SAXException
+  {
+    // Re-initialize the tree append process
+    m_endDocumentOccured = false;
+    m_prefixMappings = new java.util.Vector();
+    m_contextIndexes = new IntStack();
+    m_parents = new IntStack();
+   
+    m_currentDocumentNode=m_size;
+    super.startDocument();
+  }
+ 
+  /**
+   * Receive notification of the end of the document.
+   *
+   * %REVIEW% Y'know, this isn't all that much of a deoptimization. We
+   * might want to consider folding the start/endDocument changes back
+   * into the main SAX2DTM so we don't have to expose so many fields
+   * (even as Protected).
+   *
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#endDocument
+   * */
+  public void endDocument() throws SAXException
+  {
+    charactersFlush();
+
+    m_nextsib.setElementAt(NULL,m_currentDocumentNode);
+
+    if (m_firstch.elementAt(m_currentDocumentNode) == NOTPROCESSED)
+      m_firstch.setElementAt(NULL,m_currentDocumentNode);
+
+    if (DTM.NULL != m_previous)
+      m_nextsib.setElementAt(DTM.NULL,m_previous);
+
+    m_parents = null;
+    m_prefixMappings = null;
+    m_contextIndexes = null;
+
+    m_currentDocumentNode= NULL; // no longer open
+    m_endDocumentOccured = true;
+  }
+ 
+
+  /** "Tail-pruning" support for RTFs.
+   *
+   * This function pushes information about the current size of the
+   * DTM's data structures onto a stack, for use by popRewindMark()
+   * (which see).
+   *
+   * %REVIEW% I have no idea how to rewind m_elemIndexes. However,
+   * RTFs will not be indexed, so I can simply panic if that case
+   * arises. Hey, it works...
+   * */
+  public void pushRewindMark()
+  {
+    if(m_indexing || m_elemIndexes!=null)
+      throw new java.lang.NullPointerException("Coding error; Don't try to mark/rewind an indexed DTM");
+
+    // Values from DTMDefaultBase
+    // %REVIEW% Can the namespace stack sizes ever differ? If not, save space!
+    mark_size.push(m_size);
+    mark_nsdeclset_size.push((m_namespaceDeclSets==null)
+                                   ? 0
+                                   : m_namespaceDeclSets.size());
+    mark_nsdeclelem_size.push((m_namespaceDeclSetElements==null)
+                                   ? 0
+                                   : m_namespaceDeclSetElements.size());
+   
+    // Values from SAX2DTM
+    mark_data_size.push(m_data.size());
+    mark_char_size.push(m_chars.size());
+    mark_doq_size.push(m_dataOrQName.size());
+  }
+ 
+  /** "Tail-pruning" support for RTFs.
+   *
+   * This function pops the information previously saved by
+   * pushRewindMark (which see) and uses it to discard all nodes added
+   * to the DTM after that time. We expect that this will allow us to
+   * reuse storage more effectively.
+   *
+   * This is _not_ intended to be called while a document is still being
+   * constructed -- only between endDocument and the next startDocument
+   *
+   * %REVIEW% WARNING: This is the first use of some of the truncation
+   * methods.  If Xalan blows up after this is called, that's a likely
+   * place to check.
+   *
+   * %REVIEW% Our original design for DTMs permitted them to share
+   * string pools.  If there any risk that this might be happening, we
+   * can _not_ rewind and recover the string storage. One solution
+   * might to assert that DTMs used for RTFs Must Not take advantage
+   * of that feature, but this seems excessively fragile. Another, much
+   * less attractive, would be to just let them leak... Nah.
+   *
+   * @return true if and only if the pop completely emptied the
+   * RTF. That response is used when determining how to unspool
+   * RTF-started-while-RTF-open situations.
+   * */
+  public boolean popRewindMark()
+  {
+    boolean top=mark_size.empty();
+   
+    m_size=top ? m_emptyNodeCount : mark_size.pop();
+    m_exptype.setSize(m_size);
+    m_firstch.setSize(m_size);
+    m_nextsib.setSize(m_size);
+    m_prevsib.setSize(m_size);
+    m_parent.setSize(m_size);
+
+    m_elemIndexes=null;
+
+    int ds= top ? m_emptyNSDeclSetCount : mark_nsdeclset_size.pop();
+    if (m_namespaceDeclSets!=null) {
+      m_namespaceDeclSets.setSize(ds);
+    }
+
+    int ds1= top ? m_emptyNSDeclSetElemsCount : mark_nsdeclelem_size.pop();
+    if (m_namespaceDeclSetElements!=null) {
+      m_namespaceDeclSetElements.setSize(ds1);
+    }
+ 
+    // Values from SAX2DTM - m_data always has a reserved entry
+    m_data.setSize(top ? m_emptyDataCount : mark_data_size.pop());
+    m_chars.setLength(top ? m_emptyCharsCount : mark_char_size.pop());
+    m_dataOrQName.setSize(top ? m_emptyDataQNCount : mark_doq_size.pop());
+
+    // Return true iff DTM now empty
+    return m_size==0;
+  }
+ 
+  /** @return true if a DTM tree is currently under construction.
+   * */
+  public boolean isTreeIncomplete()
+  {
+    return !m_endDocumentOccured;
+  }
+}
diff --git a/src/main/java/org/apache/xml/res/XMLErrorResources.java b/src/main/java/org/apache/xml/res/XMLErrorResources.java
new file mode 100644
index 0000000..ef036f2
--- /dev/null
+++ b/src/main/java/org/apache/xml/res/XMLErrorResources.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLErrorResources.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.res;
+
+
+import java.util.ListResourceBundle;
+
+/**
+ * Set up error messages.
+ * We build a two dimensional array of message keys and
+ * message strings. In order to add a new message here,
+ * you need to first add a String constant. And you need
+ * to enter key, value pair as part of the contents
+ * array. You also need to update MAX_CODE for error strings
+ * and MAX_WARNING for warnings ( Needed for only information
+ * purpose )
+ */
+public class XMLErrorResources extends ListResourceBundle
+{
+
+/*
+ * This file contains error and warning messages related to Xalan Error
+ * Handling.
+ *
+ *  General notes to translators:
+ *
+ *  1) Xalan (or more properly, Xalan-interpretive) and XSLTC are names of
+ *     components.
+ *     XSLT is an acronym for "XML Stylesheet Language: Transformations".
+ *     XSLTC is an acronym for XSLT Compiler.
+ *
+ *  2) A stylesheet is a description of how to transform an input XML document
+ *     into a resultant XML document (or HTML document or text).  The
+ *     stylesheet itself is described in the form of an XML document.
+ *
+ *  3) A template is a component of a stylesheet that is used to match a
+ *     particular portion of an input document and specifies the form of the
+ *     corresponding portion of the output document.
+ *
+ *  4) An element is a mark-up tag in an XML document; an attribute is a
+ *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+ *     "elem" is an element name, "attr" and "attr2" are attribute names with
+ *     the values "val" and "val2", respectively.
+ *
+ *  5) A namespace declaration is a special attribute that is used to associate
+ *     a prefix with a URI (the namespace).  The meanings of element names and
+ *     attribute names that use that prefix are defined with respect to that
+ *     namespace.
+ *
+ *  6) "Translet" is an invented term that describes the class file that
+ *     results from compiling an XML stylesheet into a Java class.
+ *
+ *  7) XPath is a specification that describes a notation for identifying
+ *     nodes in a tree-structured representation of an XML document.  An
+ *     instance of that notation is referred to as an XPath expression.
+ *
+ */
+
+  /* 
+   * Message keys
+   */
+  public static final String ER_FUNCTION_NOT_SUPPORTED = "ER_FUNCTION_NOT_SUPPORTED";
+  public static final String ER_CANNOT_OVERWRITE_CAUSE = "ER_CANNOT_OVERWRITE_CAUSE";
+  public static final String ER_NO_DEFAULT_IMPL = "ER_NO_DEFAULT_IMPL";
+  public static final String ER_CHUNKEDINTARRAY_NOT_SUPPORTED = "ER_CHUNKEDINTARRAY_NOT_SUPPORTED";
+  public static final String ER_OFFSET_BIGGER_THAN_SLOT = "ER_OFFSET_BIGGER_THAN_SLOT";
+  public static final String ER_COROUTINE_NOT_AVAIL = "ER_COROUTINE_NOT_AVAIL";
+  public static final String ER_COROUTINE_CO_EXIT = "ER_COROUTINE_CO_EXIT";
+  public static final String ER_COJOINROUTINESET_FAILED = "ER_COJOINROUTINESET_FAILED";
+  public static final String ER_COROUTINE_PARAM = "ER_COROUTINE_PARAM";
+  public static final String ER_PARSER_DOTERMINATE_ANSWERS = "ER_PARSER_DOTERMINATE_ANSWERS";
+  public static final String ER_NO_PARSE_CALL_WHILE_PARSING = "ER_NO_PARSE_CALL_WHILE_PARSING";
+  public static final String ER_TYPED_ITERATOR_AXIS_NOT_IMPLEMENTED = "ER_TYPED_ITERATOR_AXIS_NOT_IMPLEMENTED";
+  public static final String ER_ITERATOR_AXIS_NOT_IMPLEMENTED = "ER_ITERATOR_AXIS_NOT_IMPLEMENTED";
+  public static final String ER_ITERATOR_CLONE_NOT_SUPPORTED = "ER_ITERATOR_CLONE_NOT_SUPPORTED";
+  public static final String ER_UNKNOWN_AXIS_TYPE = "ER_UNKNOWN_AXIS_TYPE";
+  public static final String ER_AXIS_NOT_SUPPORTED = "ER_AXIS_NOT_SUPPORTED";
+  public static final String ER_NO_DTMIDS_AVAIL = "ER_NO_DTMIDS_AVAIL";
+  public static final String ER_NOT_SUPPORTED = "ER_NOT_SUPPORTED";
+  public static final String ER_NODE_NON_NULL = "ER_NODE_NON_NULL";
+  public static final String ER_COULD_NOT_RESOLVE_NODE = "ER_COULD_NOT_RESOLVE_NODE";
+  public static final String ER_STARTPARSE_WHILE_PARSING = "ER_STARTPARSE_WHILE_PARSING";
+  public static final String ER_STARTPARSE_NEEDS_SAXPARSER = "ER_STARTPARSE_NEEDS_SAXPARSER";
+  public static final String ER_COULD_NOT_INIT_PARSER = "ER_COULD_NOT_INIT_PARSER";
+  public static final String ER_EXCEPTION_CREATING_POOL = "ER_EXCEPTION_CREATING_POOL";
+  public static final String ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE = "ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE";
+  public static final String ER_SCHEME_REQUIRED = "ER_SCHEME_REQUIRED";
+  public static final String ER_NO_SCHEME_IN_URI = "ER_NO_SCHEME_IN_URI";
+  public static final String ER_NO_SCHEME_INURI = "ER_NO_SCHEME_INURI";
+  public static final String ER_PATH_INVALID_CHAR = "ER_PATH_INVALID_CHAR";
+  public static final String ER_SCHEME_FROM_NULL_STRING = "ER_SCHEME_FROM_NULL_STRING";
+  public static final String ER_SCHEME_NOT_CONFORMANT = "ER_SCHEME_NOT_CONFORMANT";
+  public static final String ER_HOST_ADDRESS_NOT_WELLFORMED = "ER_HOST_ADDRESS_NOT_WELLFORMED";
+  public static final String ER_PORT_WHEN_HOST_NULL = "ER_PORT_WHEN_HOST_NULL";
+  public static final String ER_INVALID_PORT = "ER_INVALID_PORT";
+  public static final String ER_FRAG_FOR_GENERIC_URI ="ER_FRAG_FOR_GENERIC_URI";
+  public static final String ER_FRAG_WHEN_PATH_NULL = "ER_FRAG_WHEN_PATH_NULL";
+  public static final String ER_FRAG_INVALID_CHAR = "ER_FRAG_INVALID_CHAR";
+  public static final String ER_PARSER_IN_USE = "ER_PARSER_IN_USE";
+  public static final String ER_CANNOT_CHANGE_WHILE_PARSING = "ER_CANNOT_CHANGE_WHILE_PARSING";
+  public static final String ER_SELF_CAUSATION_NOT_PERMITTED = "ER_SELF_CAUSATION_NOT_PERMITTED";
+  public static final String ER_NO_USERINFO_IF_NO_HOST = "ER_NO_USERINFO_IF_NO_HOST";
+  public static final String ER_NO_PORT_IF_NO_HOST = "ER_NO_PORT_IF_NO_HOST";
+  public static final String ER_NO_QUERY_STRING_IN_PATH = "ER_NO_QUERY_STRING_IN_PATH";
+  public static final String ER_NO_FRAGMENT_STRING_IN_PATH = "ER_NO_FRAGMENT_STRING_IN_PATH";
+  public static final String ER_CANNOT_INIT_URI_EMPTY_PARMS = "ER_CANNOT_INIT_URI_EMPTY_PARMS";
+  public static final String ER_METHOD_NOT_SUPPORTED ="ER_METHOD_NOT_SUPPORTED";
+  public static final String ER_INCRSAXSRCFILTER_NOT_RESTARTABLE = "ER_INCRSAXSRCFILTER_NOT_RESTARTABLE";
+  public static final String ER_XMLRDR_NOT_BEFORE_STARTPARSE = "ER_XMLRDR_NOT_BEFORE_STARTPARSE";
+  public static final String ER_AXIS_TRAVERSER_NOT_SUPPORTED = "ER_AXIS_TRAVERSER_NOT_SUPPORTED";
+  public static final String ER_ERRORHANDLER_CREATED_WITH_NULL_PRINTWRITER = "ER_ERRORHANDLER_CREATED_WITH_NULL_PRINTWRITER";
+  public static final String ER_SYSTEMID_UNKNOWN = "ER_SYSTEMID_UNKNOWN";
+  public static final String ER_LOCATION_UNKNOWN = "ER_LOCATION_UNKNOWN";
+  public static final String ER_PREFIX_MUST_RESOLVE = "ER_PREFIX_MUST_RESOLVE";
+  public static final String ER_CREATEDOCUMENT_NOT_SUPPORTED = "ER_CREATEDOCUMENT_NOT_SUPPORTED";  
+  public static final String ER_CHILD_HAS_NO_OWNER_DOCUMENT = "ER_CHILD_HAS_NO_OWNER_DOCUMENT";
+  public static final String ER_CHILD_HAS_NO_OWNER_DOCUMENT_ELEMENT = "ER_CHILD_HAS_NO_OWNER_DOCUMENT_ELEMENT";  
+  public static final String ER_CANT_OUTPUT_TEXT_BEFORE_DOC = "ER_CANT_OUTPUT_TEXT_BEFORE_DOC";  
+  public static final String ER_CANT_HAVE_MORE_THAN_ONE_ROOT = "ER_CANT_HAVE_MORE_THAN_ONE_ROOT";  
+  public static final String ER_ARG_LOCALNAME_NULL = "ER_ARG_LOCALNAME_NULL";  
+  public static final String ER_ARG_LOCALNAME_INVALID = "ER_ARG_LOCALNAME_INVALID";  
+  public static final String ER_ARG_PREFIX_INVALID = "ER_ARG_PREFIX_INVALID";  
+  public static final String ER_NAME_CANT_START_WITH_COLON = "ER_NAME_CANT_START_WITH_COLON";
+
+  /*
+   * Now fill in the message text.
+   * Then fill in the message text for that message code in the
+   * array. Use the new error code as the index into the array.
+   */
+
+  // Error messages...
+
+  /**
+   * Get the lookup table for error messages
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][] {
+
+  /** Error message ID that has a null message, but takes in a single object.    */
+    {"ER0000" , "{0}" },
+ 
+    { ER_FUNCTION_NOT_SUPPORTED, 
+      "Function not supported!"},
+
+    { ER_CANNOT_OVERWRITE_CAUSE,
+      "Cannot overwrite cause"},
+
+    { ER_NO_DEFAULT_IMPL,
+      "No default implementation found "},
+
+    { ER_CHUNKEDINTARRAY_NOT_SUPPORTED,
+      "ChunkedIntArray({0}) not currently supported"},
+
+    { ER_OFFSET_BIGGER_THAN_SLOT,
+      "Offset bigger than slot"},
+
+    { ER_COROUTINE_NOT_AVAIL,
+      "Coroutine not available, id={0}"},
+    
+    { ER_COROUTINE_CO_EXIT,
+      "CoroutineManager received co_exit() request"},
+
+    { ER_COJOINROUTINESET_FAILED,
+      "co_joinCoroutineSet() failed"},
+
+    { ER_COROUTINE_PARAM,
+      "Coroutine parameter error ({0})"},
+
+    { ER_PARSER_DOTERMINATE_ANSWERS,
+      "\nUNEXPECTED: Parser doTerminate answers {0}"},
+
+    { ER_NO_PARSE_CALL_WHILE_PARSING,
+      "parse may not be called while parsing"},
+
+    { ER_TYPED_ITERATOR_AXIS_NOT_IMPLEMENTED,
+      "Error: typed iterator for axis  {0} not implemented"},
+
+    { ER_ITERATOR_AXIS_NOT_IMPLEMENTED,
+      "Error: iterator for axis {0} not implemented "},
+
+    { ER_ITERATOR_CLONE_NOT_SUPPORTED,
+      "Iterator clone not supported"},
+
+    { ER_UNKNOWN_AXIS_TYPE,
+      "Unknown axis traversal type: {0}"},
+
+    { ER_AXIS_NOT_SUPPORTED,
+      "Axis traverser not supported: {0}"},
+
+    { ER_NO_DTMIDS_AVAIL,
+      "No more DTM IDs are available"},
+
+    { ER_NOT_SUPPORTED,
+      "Not supported: {0}"},
+    
+    { ER_NODE_NON_NULL,
+      "Node must be non-null for getDTMHandleFromNode"},
+
+    { ER_COULD_NOT_RESOLVE_NODE,
+      "Could not resolve the node to a handle"},
+
+    { ER_STARTPARSE_WHILE_PARSING,
+       "startParse may not be called while parsing"},
+
+    { ER_STARTPARSE_NEEDS_SAXPARSER,
+       "startParse needs a non-null SAXParser"},
+
+    { ER_COULD_NOT_INIT_PARSER,
+       "could not initialize parser with"},
+
+    { ER_EXCEPTION_CREATING_POOL,
+       "exception creating new instance for pool"},
+
+    { ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+       "Path contains invalid escape sequence"},
+
+    { ER_SCHEME_REQUIRED,
+       "Scheme is required!"},
+    
+    { ER_NO_SCHEME_IN_URI,
+       "No scheme found in URI: {0}"},
+
+    { ER_NO_SCHEME_INURI,
+       "No scheme found in URI"},
+
+    { ER_PATH_INVALID_CHAR,
+       "Path contains invalid character: {0}"},
+
+    { ER_SCHEME_FROM_NULL_STRING,
+       "Cannot set scheme from null string"},
+
+    { ER_SCHEME_NOT_CONFORMANT,
+       "The scheme is not conformant."},
+
+    { ER_HOST_ADDRESS_NOT_WELLFORMED,
+       "Host is not a well formed address"},
+
+    { ER_PORT_WHEN_HOST_NULL,
+       "Port cannot be set when host is null"},
+
+    { ER_INVALID_PORT,
+       "Invalid port number"},
+
+    { ER_FRAG_FOR_GENERIC_URI,
+       "Fragment can only be set for a generic URI"},
+
+    { ER_FRAG_WHEN_PATH_NULL,
+       "Fragment cannot be set when path is null"},
+
+    { ER_FRAG_INVALID_CHAR,
+       "Fragment contains invalid character"},
+
+    { ER_PARSER_IN_USE,
+      "Parser is already in use"},
+
+    { ER_CANNOT_CHANGE_WHILE_PARSING,
+      "Cannot change {0} {1} while parsing"},
+   
+    { ER_SELF_CAUSATION_NOT_PERMITTED,
+      "Self-causation not permitted"},
+
+    { ER_NO_USERINFO_IF_NO_HOST,
+      "Userinfo may not be specified if host is not specified"},
+
+    { ER_NO_PORT_IF_NO_HOST,
+      "Port may not be specified if host is not specified"},
+
+    { ER_NO_QUERY_STRING_IN_PATH, 
+      "Query string cannot be specified in path and query string"},
+
+    { ER_NO_FRAGMENT_STRING_IN_PATH,
+      "Fragment cannot be specified in both the path and fragment"},
+
+    { ER_CANNOT_INIT_URI_EMPTY_PARMS, 
+      "Cannot initialize URI with empty parameters"},
+
+    { ER_METHOD_NOT_SUPPORTED,
+      "Method not yet supported "},
+    
+    { ER_INCRSAXSRCFILTER_NOT_RESTARTABLE,
+      "IncrementalSAXSource_Filter not currently restartable"},
+
+    { ER_XMLRDR_NOT_BEFORE_STARTPARSE,
+      "XMLReader not before startParse request"},
+
+    { ER_AXIS_TRAVERSER_NOT_SUPPORTED,
+      "Axis traverser not supported: {0}"},
+
+    { ER_ERRORHANDLER_CREATED_WITH_NULL_PRINTWRITER,
+      "ListingErrorHandler created with null PrintWriter!"},
+
+    { ER_SYSTEMID_UNKNOWN,
+      "SystemId Unknown"},
+
+    { ER_LOCATION_UNKNOWN,
+      "Location of error unknown"},
+
+    { ER_PREFIX_MUST_RESOLVE,
+      "Prefix must resolve to a namespace: {0}"},
+
+    { ER_CREATEDOCUMENT_NOT_SUPPORTED,
+      "createDocument() not supported in XPathContext!"},
+
+    { ER_CHILD_HAS_NO_OWNER_DOCUMENT,
+      "Attribute child does not have an owner document!"},
+
+    { ER_CHILD_HAS_NO_OWNER_DOCUMENT_ELEMENT,
+      "Attribute child does not have an owner document element!"},
+
+    { ER_CANT_OUTPUT_TEXT_BEFORE_DOC,
+      "Warning: can't output text before document element!  Ignoring..."},
+
+    { ER_CANT_HAVE_MORE_THAN_ONE_ROOT,
+      "Can't have more than one root on a DOM!"},
+
+    { ER_ARG_LOCALNAME_NULL,
+       "Argument 'localName' is null"},
+ 
+    // Note to translators:  A QNAME has the syntactic form [NCName:]NCName
+    // The localname is the portion after the optional colon; the message indicates
+    // that there is a problem with that part of the QNAME.
+    { ER_ARG_LOCALNAME_INVALID,
+       "Localname in QNAME should be a valid NCName"},
+
+    // Note to translators:  A QNAME has the syntactic form [NCName:]NCName
+    // The prefix is the portion before the optional colon; the message indicates
+    // that there is a problem with that part of the QNAME.
+    { ER_ARG_PREFIX_INVALID,
+       "Prefix in QNAME should be a valid NCName"},
+       
+    { ER_NAME_CANT_START_WITH_COLON,
+      "Name cannot start with a colon"},
+       
+    { "BAD_CODE", "Parameter to createMessage was out of bounds"},
+    { "FORMAT_FAILED", "Exception thrown during messageFormat call"},
+    { "line", "Line #"},
+    { "column","Column #"}
+  
+  
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/res/XMLMessages.java b/src/main/java/org/apache/xml/res/XMLMessages.java
new file mode 100644
index 0000000..9a2412e
--- /dev/null
+++ b/src/main/java/org/apache/xml/res/XMLMessages.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLMessages.java 468653 2006-10-28 07:07:05Z minchau $
+ */
+package org.apache.xml.res;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * A utility class for issuing XML error messages.
+ * @xsl.usage internal
+ */
+public class XMLMessages
+{
+
+  /** The local object to use.  */
+  protected Locale fLocale = Locale.getDefault();
+
+  /** The language specific resource object for XML messages.  */
+  private static ListResourceBundle XMLBundle = new XMLErrorResources(); // android-changed
+
+  /** String to use if a bad message code is used. */
+  protected static final String BAD_CODE = "BAD_CODE";
+
+  /** String to use if the message format operation failed.  */
+  protected static final String FORMAT_FAILED = "FORMAT_FAILED";
+    
+  /**
+   * Set the Locale object to use.
+   * 
+   * @param locale non-null reference to Locale object.
+   */
+   public void setLocale(Locale locale)
+  {
+    fLocale = locale;
+  }
+
+  /**
+   * Get the Locale object that is being used.
+   * 
+   * @return non-null reference to Locale object.
+   */
+  public Locale getLocale()
+  {
+    return fLocale;
+  }
+    
+  /**
+   * Creates a message from the specified key and replacement
+   * arguments, localized to the given locale.
+   *
+   * @param msgKey    The key for the message text.
+   * @param args      The arguments to be used as replacement text
+   *                  in the message created.
+   *
+   * @return The formatted message string.
+   */
+  public static final String createXMLMessage(String msgKey, Object args[])
+  {
+      // BEGIN android-changed
+      //     don't localize exceptions
+      return createMsg(XMLBundle, msgKey, args);
+      // END android-changed
+  }
+
+  /**
+   * Creates a message from the specified key and replacement
+   * arguments, localized to the given locale.
+   *
+   * @param fResourceBundle The resource bundle to use.
+   * @param msgKey  The message key to use.
+   * @param args      The arguments to be used as replacement text
+   *                  in the message created.
+   *
+   * @return The formatted message string.
+   */
+  public static final String createMsg(ListResourceBundle fResourceBundle,
+	String msgKey, Object args[])  //throws Exception
+  {
+
+    String fmsg = null;
+    boolean throwex = false;
+    String msg = null;
+
+    if (msgKey != null)
+      msg = fResourceBundle.getString(msgKey);
+
+    if (msg == null)
+    {
+      msg = fResourceBundle.getString(BAD_CODE);
+      throwex = true;
+    }
+
+    if (args != null)
+    {
+      try
+      {
+
+        // Do this to keep format from crying.
+        // This is better than making a bunch of conditional
+        // code all over the place.
+        int n = args.length;
+
+        for (int i = 0; i < n; i++)
+        {
+          if (null == args[i])
+            args[i] = "";
+        }
+
+        fmsg = java.text.MessageFormat.format(msg, args);
+      }
+      catch (Exception e)
+      {
+        fmsg = fResourceBundle.getString(FORMAT_FAILED);
+        fmsg += " " + msg;
+      }
+    }
+    else
+      fmsg = msg;
+
+    if (throwex)
+    {
+      throw new RuntimeException(fmsg);
+    }
+
+    return fmsg;
+  }
+}
diff --git a/src/main/java/org/apache/xml/serializer/AttributesImplSerializer.java b/src/main/java/org/apache/xml/serializer/AttributesImplSerializer.java
new file mode 100644
index 0000000..440d4c9
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/AttributesImplSerializer.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AttributesImplSerializer.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+
+package org.apache.xml.serializer;
+
+import java.util.Hashtable;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * This class extends org.xml.sax.helpers.AttributesImpl which implements org.
+ * xml.sax.Attributes. But for optimization this class adds a Hashtable for
+ * faster lookup of an index by qName, which is commonly done in the stream
+ * serializer.
+ * 
+ * @see org.xml.sax.Attributes
+ * 
+ * @xsl.usage internal
+ */
+public final class AttributesImplSerializer extends AttributesImpl
+{
+    /**
+     * Hash table of qName/index values to quickly lookup the index
+     * of an attributes qName.  qNames are in uppercase in the hash table
+     * to make the search case insensitive.
+     * 
+     * The keys to the hashtable to find the index are either
+     * "prefix:localName"  or "{uri}localName".
+     */
+    private final Hashtable m_indexFromQName = new Hashtable();
+    
+    private final StringBuffer m_buff = new StringBuffer();
+    
+    /**
+     * This is the number of attributes before switching to the hash table,
+     * and can be tuned, but 12 seems good for now - Brian M.
+     */
+    private static final int MAX = 12;
+    
+    /**
+     * One less than the number of attributes before switching to
+     * the Hashtable.
+     */
+    private static final int MAXMinus1 = MAX - 1;
+
+    /**
+     * This method gets the index of an attribute given its qName.
+     * @param qname the qualified name of the attribute, e.g. "prefix1:locName1"
+     * @return the integer index of the attribute.
+     * @see org.xml.sax.Attributes#getIndex(String)
+     */
+    public final int getIndex(String qname)
+    {
+        int index;
+
+        if (super.getLength() < MAX)
+        {
+            // if we haven't got too many attributes let the
+            // super class look it up
+            index = super.getIndex(qname);
+            return index;
+        }
+        // we have too many attributes and the super class is slow
+        // so find it quickly using our Hashtable.
+        Integer i = (Integer)m_indexFromQName.get(qname);
+        if (i == null)
+            index = -1;
+        else
+            index = i.intValue();
+        return index;
+    }
+    /**
+     * This method adds the attribute, but also records its qName/index pair in
+     * the hashtable for fast lookup by getIndex(qName).
+     * @param uri the URI of the attribute
+     * @param local the local name of the attribute
+     * @param qname the qualified name of the attribute
+     * @param type the type of the attribute
+     * @param val the value of the attribute
+     *
+     * @see org.xml.sax.helpers.AttributesImpl#addAttribute(String, String, String, String, String)
+     * @see #getIndex(String)
+     */
+    public final void addAttribute(
+        String uri,
+        String local,
+        String qname,
+        String type,
+        String val)
+    {
+        int index = super.getLength();
+        super.addAttribute(uri, local, qname, type, val);
+        // (index + 1) is now the number of attributes
+        // so either compare (index+1) to MAX, or compare index to (MAX-1)
+
+        if (index < MAXMinus1)
+        {
+            return;
+        }
+        else if (index == MAXMinus1)
+        {
+            switchOverToHash(MAX);
+        }
+        else
+        {
+            /* add the key with the format of "prefix:localName" */
+            /* we have just added the attibute, its index is the old length */
+            Integer i = new Integer(index);
+            m_indexFromQName.put(qname, i);
+            
+            /* now add with key of the format "{uri}localName" */
+            m_buff.setLength(0);
+            m_buff.append('{').append(uri).append('}').append(local);
+            String key = m_buff.toString();
+            m_indexFromQName.put(key, i);
+        }
+        return;
+    }
+
+    /**
+     * We are switching over to having a hash table for quick look
+     * up of attributes, but up until now we haven't kept any
+     * information in the Hashtable, so we now update the Hashtable.
+     * Future additional attributes will update the Hashtable as
+     * they are added.
+     * @param numAtts
+     */
+    private void switchOverToHash(int numAtts)
+    {
+        for (int index = 0; index < numAtts; index++)
+        {
+            String qName = super.getQName(index);
+            Integer i = new Integer(index);
+            m_indexFromQName.put(qName, i);
+            
+            // Add quick look-up to find with uri/local name pair
+            String uri = super.getURI(index);
+            String local = super.getLocalName(index);
+            m_buff.setLength(0);
+            m_buff.append('{').append(uri).append('}').append(local);
+            String key = m_buff.toString();
+            m_indexFromQName.put(key, i);
+        }
+    }
+
+    /**
+     * This method clears the accumulated attributes.
+     *
+     * @see org.xml.sax.helpers.AttributesImpl#clear()
+     */
+    public final void clear()
+    {
+
+        int len = super.getLength();
+        super.clear();
+        if (MAX <= len)
+        {
+            // if we have had enough attributes and are
+            // using the Hashtable, then clear the Hashtable too.
+            m_indexFromQName.clear();
+        }
+
+    }
+
+    /**
+     * This method sets the attributes, previous attributes are cleared,
+     * it also keeps the hashtable up to date for quick lookup via
+     * getIndex(qName).
+     * @param atts the attributes to copy into these attributes.
+     * @see org.xml.sax.helpers.AttributesImpl#setAttributes(Attributes)
+     * @see #getIndex(String)
+     */
+    public final void setAttributes(Attributes atts)
+    {
+
+        super.setAttributes(atts);
+
+        // we've let the super class add the attributes, but
+        // we need to keep the hash table up to date ourselves for the
+        // potentially new qName/index pairs for quick lookup. 
+        int numAtts = atts.getLength();
+        if (MAX <= numAtts)
+            switchOverToHash(numAtts);
+
+    }
+    
+    /**
+     * This method gets the index of an attribute given its uri and locanName.
+     * @param uri the URI of the attribute name.
+     * @param localName the local namer (after the ':' ) of the attribute name.
+     * @return the integer index of the attribute.
+     * @see org.xml.sax.Attributes#getIndex(String)
+     */
+    public final int getIndex(String uri, String localName)
+    {
+        int index;
+
+        if (super.getLength() < MAX)
+        {
+            // if we haven't got too many attributes let the
+            // super class look it up
+            index = super.getIndex(uri,localName);
+            return index;
+        }
+        // we have too many attributes and the super class is slow
+        // so find it quickly using our Hashtable.
+        // Form the key of format "{uri}localName"
+        m_buff.setLength(0);
+        m_buff.append('{').append(uri).append('}').append(localName);
+        String key = m_buff.toString();
+        Integer i = (Integer)m_indexFromQName.get(key);
+        if (i == null)
+            index = -1;
+        else
+            index = i.intValue();
+        return index;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/CharInfo.java b/src/main/java/org/apache/xml/serializer/CharInfo.java
new file mode 100644
index 0000000..ef5e449
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/CharInfo.java
@@ -0,0 +1,807 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: CharInfo.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.PropertyResourceBundle;
+import java.util.ResourceBundle;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.serializer.utils.MsgKey;
+import org.apache.xml.serializer.utils.SystemIDResolver;
+import org.apache.xml.serializer.utils.Utils;
+import org.apache.xml.serializer.utils.WrappedRuntimeException;
+
+/**
+ * This class provides services that tell if a character should have
+ * special treatement, such as entity reference substitution or normalization
+ * of a newline character.  It also provides character to entity reference
+ * lookup.
+ *
+ * DEVELOPERS: See Known Issue in the constructor.
+ * 
+ * @xsl.usage internal
+ */
+final class CharInfo
+{
+    /** Given a character, lookup a String to output (e.g. a decorated entity reference). */
+    private HashMap m_charToString;
+
+    /**
+     * The name of the HTML entities file.
+     * If specified, the file will be resource loaded with the default class loader.
+     */
+    public static final String HTML_ENTITIES_RESOURCE = 
+                SerializerBase.PKG_NAME+".HTMLEntities";
+
+    /**
+     * The name of the XML entities file.
+     * If specified, the file will be resource loaded with the default class loader.
+     */
+    public static final String XML_ENTITIES_RESOURCE = 
+                SerializerBase.PKG_NAME+".XMLEntities";
+
+    /** The horizontal tab character, which the parser should always normalize. */
+    static final char S_HORIZONAL_TAB = 0x09;
+
+    /** The linefeed character, which the parser should always normalize. */
+    static final char S_LINEFEED = 0x0A;
+
+    /** The carriage return character, which the parser should always normalize. */
+    static final char S_CARRIAGERETURN = 0x0D;
+    static final char S_SPACE = 0x20;
+    static final char S_QUOTE = 0x22;
+    static final char S_LT = 0x3C;
+    static final char S_GT = 0x3E;
+    static final char S_NEL = 0x85;    
+    static final char S_LINE_SEPARATOR = 0x2028;
+    
+    /** This flag is an optimization for HTML entities. It false if entities 
+     * other than quot (34), amp (38), lt (60) and gt (62) are defined
+     * in the range 0 to 127.
+     * @xsl.usage internal
+     */    
+    boolean onlyQuotAmpLtGt;
+    
+    /** Copy the first 0,1 ... ASCII_MAX values into an array */
+    static final int ASCII_MAX = 128;
+    
+    /** Array of values is faster access than a set of bits 
+     * to quickly check ASCII characters in attribute values,
+     * the value is true if the character in an attribute value
+     * should be mapped to a String. 
+     */
+    private final boolean[] shouldMapAttrChar_ASCII;
+    
+    /** Array of values is faster access than a set of bits 
+     * to quickly check ASCII characters in text nodes, 
+     * the value is true if the character in a text node
+     * should be mapped to a String. 
+     */
+    private final boolean[] shouldMapTextChar_ASCII;
+
+    /** An array of bits to record if the character is in the set.
+     * Although information in this array is complete, the
+     * isSpecialAttrASCII array is used first because access to its values
+     * is common and faster.
+     */   
+    private final int array_of_bits[];
+     
+    
+    // 5 for 32 bit words,  6 for 64 bit words ...
+    /*
+     * This constant is used to shift an integer to quickly
+     * calculate which element its bit is stored in.
+     * 5 for 32 bit words (int) ,  6 for 64 bit words (long)
+     */
+    private static final int SHIFT_PER_WORD = 5;
+    
+    /*
+     * A mask to get the low order bits which are used to
+     * calculate the value of the bit within a given word,
+     * that will represent the presence of the integer in the 
+     * set.
+     * 
+     * 0x1F for 32 bit words (int),
+     * or 0x3F for 64 bit words (long) 
+     */
+    private static final int LOW_ORDER_BITMASK = 0x1f;
+    
+    /*
+     * This is used for optimizing the lookup of bits representing
+     * the integers in the set. It is the index of the first element
+     * in the array array_of_bits[] that is not used.
+     */
+    private int firstWordNotUsed;
+
+
+    /**
+     * A base constructor just to explicitly create the fields,
+     * with the exception of m_charToString which is handled
+     * by the constructor that delegates base construction to this one.
+     * <p>
+     * m_charToString is not created here only for performance reasons,
+     * to avoid creating a Hashtable that will be replaced when
+     * making a mutable copy, {@link #mutableCopyOf(CharInfo)}. 
+     *
+     */
+    private CharInfo() 
+    {
+    	this.array_of_bits = createEmptySetOfIntegers(65535);
+    	this.firstWordNotUsed = 0;
+    	this.shouldMapAttrChar_ASCII = new boolean[ASCII_MAX];
+    	this.shouldMapTextChar_ASCII = new boolean[ASCII_MAX];
+    	this.m_charKey = new CharKey();
+    	
+    	// Not set here, but in a constructor that uses this one
+    	// this.m_charToString =  new Hashtable();  
+    	
+    	this.onlyQuotAmpLtGt = true;
+    	
+
+    	return;
+    }
+    
+    private CharInfo(String entitiesResource, String method, boolean internal)
+    {
+    	// call the default constructor to create the fields
+    	this();
+    	m_charToString = new HashMap();
+
+        ResourceBundle entities = null;
+        boolean noExtraEntities = true;
+
+        // Make various attempts to interpret the parameter as a properties
+        // file or resource file, as follows:
+        //
+        //   1) attempt to load .properties file using ResourceBundle
+        //   2) try using the class loader to find the specified file a resource
+        //      file
+        //   3) try treating the resource a URI
+
+        if (internal) { 
+            try {
+                // Load entity property files by using PropertyResourceBundle,
+                // cause of security issure for applets
+                entities = PropertyResourceBundle.getBundle(entitiesResource);
+            } catch (Exception e) {}
+        }
+
+        if (entities != null) {
+            Enumeration keys = entities.getKeys();
+            while (keys.hasMoreElements()){
+                String name = (String) keys.nextElement();
+                String value = entities.getString(name);
+                int code = Integer.parseInt(value);
+                boolean extra = defineEntity(name, (char) code);
+                if (extra)
+                    noExtraEntities = false;
+            }
+        } else {
+            InputStream is = null;
+
+            // Load user specified resource file by using URL loading, it
+            // requires a valid URI as parameter
+            try {
+                if (internal) {
+                    is = CharInfo.class.getResourceAsStream(entitiesResource);
+                } else {
+                    ClassLoader cl = ObjectFactory.findClassLoader();
+                    if (cl == null) {
+                        is = ClassLoader.getSystemResourceAsStream(entitiesResource);
+                    } else {
+                        is = cl.getResourceAsStream(entitiesResource);
+                    }
+
+                    if (is == null) {
+                        try {
+                            URL url = new URL(entitiesResource);
+                            is = url.openStream();
+                        } catch (Exception e) {}
+                    }
+                }
+
+                if (is == null) {
+                    throw new RuntimeException(
+                        Utils.messages.createMessage(
+                            MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                            new Object[] {entitiesResource, entitiesResource}));
+                }
+
+                // Fix Bugzilla#4000: force reading in UTF-8
+                //  This creates the de facto standard that Xalan's resource 
+                //  files must be encoded in UTF-8. This should work in all
+                // JVMs.
+                //
+                // %REVIEW% KNOWN ISSUE: IT FAILS IN MICROSOFT VJ++, which
+                // didn't implement the UTF-8 encoding. Theoretically, we should
+                // simply let it fail in that case, since the JVM is obviously
+                // broken if it doesn't support such a basic standard.  But
+                // since there are still some users attempting to use VJ++ for
+                // development, we have dropped in a fallback which makes a
+                // second attempt using the platform's default encoding. In VJ++
+                // this is apparently ASCII, which is subset of UTF-8... and
+                // since the strings we'll be reading here are also primarily
+                // limited to the 7-bit ASCII range (at least, in English
+                // versions of Xalan), this should work well enough to keep us
+                // on the air until we're ready to officially decommit from
+                // VJ++.
+
+                BufferedReader reader;
+                try {
+                    reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+                } catch (UnsupportedEncodingException e) {
+                    reader = new BufferedReader(new InputStreamReader(is));
+                }
+
+                String line = reader.readLine();
+
+                while (line != null) {
+                    if (line.length() == 0 || line.charAt(0) == '#') {
+                        line = reader.readLine();
+
+                        continue;
+                    }
+
+                    int index = line.indexOf(' ');
+
+                    if (index > 1) {
+                        String name = line.substring(0, index);
+
+                        ++index;
+
+                        if (index < line.length()) {
+                            String value = line.substring(index);
+                            index = value.indexOf(' ');
+
+                            if (index > 0) {
+                                value = value.substring(0, index);
+                            }
+
+                            int code = Integer.parseInt(value);
+
+                            boolean extra = defineEntity(name, (char) code);
+                            if (extra)
+                                noExtraEntities = false;
+                        }
+                    }
+
+                    line = reader.readLine();
+                }
+
+                is.close();
+            } catch (Exception e) {
+                throw new RuntimeException(
+                    Utils.messages.createMessage(
+                        MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                        new Object[] { entitiesResource,
+                                       e.toString(),
+                                       entitiesResource,
+                                       e.toString()}));
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (Exception except) {}
+                }
+            }
+        }
+
+        onlyQuotAmpLtGt = noExtraEntities;
+            
+        /* Now that we've used get(ch) just above to initialize the
+         * two arrays we will change by adding a tab to the set of 
+         * special chars for XML (but not HTML!).
+         * We do this because a tab is always a
+         * special character in an XML attribute, 
+         * but only a special character in XML text 
+         * if it has an entity defined for it.
+         * This is the reason for this delay.
+         */
+        if (Method.XML.equals(method)) 
+        {       
+            // We choose not to escape the quotation mark as &quot; in text nodes
+            shouldMapTextChar_ASCII[S_QUOTE] = false;
+        }
+        
+        if (Method.HTML.equals(method)) {
+        	// The XSLT 1.0 recommendation says 
+        	// "The html output method should not escape < characters occurring in attribute values."
+        	// So we don't escape '<' in an attribute for HTML
+        	shouldMapAttrChar_ASCII['<'] = false;    
+        	
+        	// We choose not to escape the quotation mark as &quot; in text nodes.
+            shouldMapTextChar_ASCII[S_QUOTE] = false;
+        }
+    }
+
+    /**
+     * Defines a new character reference. The reference's name and value are
+     * supplied. Nothing happens if the character reference is already defined.
+     * <p>Unlike internal entities, character references are a string to single
+     * character mapping. They are used to map non-ASCII characters both on
+     * parsing and printing, primarily for HTML documents. '&amp;lt;' is an
+     * example of a character reference.</p>
+     *
+     * @param name The entity's name
+     * @param value The entity's value
+     * @return true if the mapping is not one of:
+     * <ul>
+     * <li> '<' to "&lt;"
+     * <li> '>' to "&gt;"
+     * <li> '&' to "&amp;"
+     * <li> '"' to "&quot;"
+     * </ul>
+     */
+    private boolean defineEntity(String name, char value)
+    {
+        StringBuffer sb = new StringBuffer("&");
+        sb.append(name);
+        sb.append(';');
+        String entityString = sb.toString();
+        
+        boolean extra = defineChar2StringMapping(entityString, value);
+        return extra;
+    }
+
+    /**
+     * A utility object, just used to map characters to output Strings,
+     * needed because a HashMap needs to map an object as a key, not a 
+     * Java primitive type, like a char, so this object gets around that
+     * and it is reusable.
+     */
+    private final CharKey m_charKey;
+
+    /**
+     * Map a character to a String. For example given
+     * the character '>' this method would return the fully decorated
+     * entity name "&lt;".
+     * Strings for entity references are loaded from a properties file,
+     * but additional mappings defined through calls to defineChar2String()
+     * are possible. Such entity reference mappings could be over-ridden.
+     *
+     * This is reusing a stored key object, in an effort to avoid
+     * heap activity. Unfortunately, that introduces a threading risk.
+     * Simplest fix for now is to make it a synchronized method, or to give
+     * up the reuse; I see very little performance difference between them.
+     * Long-term solution would be to replace the hashtable with a sparse array
+     * keyed directly from the character's integer value; see DTM's
+     * string pool for a related solution.
+     *
+     * @param value The character that should be resolved to
+     * a String, e.g. resolve '>' to  "&lt;".
+     *
+     * @return The String that the character is mapped to, or null if not found.
+     * @xsl.usage internal
+     */
+    String getOutputStringForChar(char value)
+    {
+        // CharKey m_charKey = new CharKey(); //Alternative to synchronized
+        m_charKey.setChar(value);
+        return (String) m_charToString.get(m_charKey);
+    }
+    
+    /**
+     * Tell if the character argument that is from
+     * an attribute value has a mapping to a String.
+     * 
+     * @param value the value of a character that is in an attribute value
+     * @return true if the character should have any special treatment, 
+     * such as when writing out entity references.
+     * @xsl.usage internal
+     */
+    final boolean shouldMapAttrChar(int value)
+    {
+        // for performance try the values in the boolean array first,
+        // this is faster access than the BitSet for common ASCII values
+
+        if (value < ASCII_MAX)
+            return shouldMapAttrChar_ASCII[value];
+
+        // rather than java.util.BitSet, our private
+        // implementation is faster (and less general).
+        return get(value);
+    }    
+
+    /**
+     * Tell if the character argument that is from a 
+     * text node has a mapping to a String, for example
+     * to map '<' to "&lt;".
+     * 
+     * @param value the value of a character that is in a text node
+     * @return true if the character has a mapping to a String, 
+     * such as when writing out entity references.
+     * @xsl.usage internal
+     */
+    final boolean shouldMapTextChar(int value)
+    {
+        // for performance try the values in the boolean array first,
+        // this is faster access than the BitSet for common ASCII values
+
+        if (value < ASCII_MAX)
+            return shouldMapTextChar_ASCII[value];
+
+        // rather than java.util.BitSet, our private
+        // implementation is faster (and less general).
+        return get(value);
+    }
+    
+
+     
+    private static CharInfo getCharInfoBasedOnPrivilege(
+        final String entitiesFileName, final String method, 
+        final boolean internal){
+            return (CharInfo) AccessController.doPrivileged(
+                new PrivilegedAction() {
+                        public Object run() {
+                            return new CharInfo(entitiesFileName, 
+                              method, internal);}
+            });            
+    }
+     
+    /**
+     * Factory that reads in a resource file that describes the mapping of
+     * characters to entity references.
+     *
+     * Resource files must be encoded in UTF-8 and have a format like:
+     * <pre>
+     * # First char # is a comment
+     * Entity numericValue
+     * quot 34
+     * amp 38
+     * </pre>
+     * (Note: Why don't we just switch to .properties files? Oct-01 -sc)
+     *
+     * @param entitiesResource Name of entities resource file that should
+     * be loaded, which describes that mapping of characters to entity references.
+     * @param method the output method type, which should be one of "xml", "html", "text"...
+     * 
+     * @xsl.usage internal
+     */
+    static CharInfo getCharInfo(String entitiesFileName, String method)
+    {
+        CharInfo charInfo = (CharInfo) m_getCharInfoCache.get(entitiesFileName);
+        if (charInfo != null) {
+        	return mutableCopyOf(charInfo);
+        }
+
+        // try to load it internally - cache
+        try {
+            charInfo = getCharInfoBasedOnPrivilege(entitiesFileName, 
+                                        method, true);
+            // Put the common copy of charInfo in the cache, but return
+            // a copy of it.
+            m_getCharInfoCache.put(entitiesFileName, charInfo);
+            return mutableCopyOf(charInfo);
+        } catch (Exception e) {}
+
+        // try to load it externally - do not cache
+        try {
+            return getCharInfoBasedOnPrivilege(entitiesFileName, 
+                                method, false);
+        } catch (Exception e) {}
+
+        String absoluteEntitiesFileName;
+
+        if (entitiesFileName.indexOf(':') < 0) {
+            absoluteEntitiesFileName =
+                SystemIDResolver.getAbsoluteURIFromRelative(entitiesFileName);
+        } else {
+            try {
+                absoluteEntitiesFileName =
+                    SystemIDResolver.getAbsoluteURI(entitiesFileName, null);
+            } catch (TransformerException te) {
+                throw new WrappedRuntimeException(te);
+            }
+        }
+
+        return getCharInfoBasedOnPrivilege(entitiesFileName, 
+                                method, false);
+    }
+
+    /**
+     * Create a mutable copy of the cached one.
+     * @param charInfo The cached one.
+     * @return
+     */
+    private static CharInfo mutableCopyOf(CharInfo charInfo) {
+    	CharInfo copy = new CharInfo();
+    	
+    	int max = charInfo.array_of_bits.length;
+    	System.arraycopy(charInfo.array_of_bits,0,copy.array_of_bits,0,max);
+    	
+    	copy.firstWordNotUsed = charInfo.firstWordNotUsed;
+    	
+    	max = charInfo.shouldMapAttrChar_ASCII.length;
+    	System.arraycopy(charInfo.shouldMapAttrChar_ASCII,0,copy.shouldMapAttrChar_ASCII,0,max);
+    	
+    	max = charInfo.shouldMapTextChar_ASCII.length;
+    	System.arraycopy(charInfo.shouldMapTextChar_ASCII,0,copy.shouldMapTextChar_ASCII,0,max);
+    	
+    	// utility field copy.m_charKey is already created in the default constructor 
+    	
+    	copy.m_charToString = (HashMap) charInfo.m_charToString.clone();
+    	
+    	copy.onlyQuotAmpLtGt = charInfo.onlyQuotAmpLtGt;
+    	    	
+		return copy;
+	}
+
+	/** 
+	 * Table of user-specified char infos.
+	 * The table maps entify file names (the name of the
+	 * property file without the .properties extension)
+	 * to CharInfo objects populated with entities defined in 
+	 * corresponding property file.  
+	 */
+    private static Hashtable m_getCharInfoCache = new Hashtable();
+
+    /**
+     * Returns the array element holding the bit value for the
+     * given integer
+     * @param i the integer that might be in the set of integers
+     * 
+     */
+    private static int arrayIndex(int i) {
+        return (i >> SHIFT_PER_WORD);
+    }
+
+    /**
+     * For a given integer in the set it returns the single bit
+     * value used within a given word that represents whether
+     * the integer is in the set or not.
+     */
+    private static int bit(int i) {
+        int ret = (1 << (i & LOW_ORDER_BITMASK));
+        return ret;
+    }
+
+    /**
+     * Creates a new empty set of integers (characters)
+     * @param max the maximum integer to be in the set.
+     */
+    private int[] createEmptySetOfIntegers(int max) {
+        firstWordNotUsed = 0; // an optimization 
+
+        int[] arr = new int[arrayIndex(max - 1) + 1];
+            return arr;
+ 
+    }
+
+    /**
+     * Adds the integer (character) to the set of integers.
+     * @param i the integer to add to the set, valid values are 
+     * 0, 1, 2 ... up to the maximum that was specified at
+     * the creation of the set.
+     */
+    private final void set(int i) {   
+        setASCIItextDirty(i);
+        setASCIIattrDirty(i); 
+             
+        int j = (i >> SHIFT_PER_WORD); // this word is used
+        int k = j + 1;       
+        
+        if(firstWordNotUsed < k) // for optimization purposes.
+            firstWordNotUsed = k;
+            
+        array_of_bits[j] |= (1 << (i & LOW_ORDER_BITMASK));
+    }
+
+
+    /**
+     * Return true if the integer (character)is in the set of integers.
+     * 
+     * This implementation uses an array of integers with 32 bits per
+     * integer.  If a bit is set to 1 the corresponding integer is 
+     * in the set of integers.
+     * 
+     * @param i an integer that is tested to see if it is the
+     * set of integers, or not.
+     */
+    private final boolean get(int i) {
+
+        boolean in_the_set = false;
+        int j = (i >> SHIFT_PER_WORD); // wordIndex(i)
+        // an optimization here, ... a quick test to see
+        // if this integer is beyond any of the words in use
+        if(j < firstWordNotUsed)
+            in_the_set = (array_of_bits[j] & 
+                          (1 << (i & LOW_ORDER_BITMASK))
+            ) != 0;  // 0L for 64 bit words
+        return in_the_set;
+    }
+    
+    /**
+     * This method returns true if there are some non-standard mappings to
+     * entities other than quot, amp, lt, gt, and its only purpose is for
+     * performance.
+     * @param charToMap The value of the character that is mapped to a String
+     * @param outputString The String to which the character is mapped, usually
+     * an entity reference such as "&lt;".
+     * @return true if the mapping is not one of:
+     * <ul>
+     * <li> '<' to "&lt;"
+     * <li> '>' to "&gt;"
+     * <li> '&' to "&amp;"
+     * <li> '"' to "&quot;"
+     * </ul>
+     */
+    private boolean extraEntity(String outputString, int charToMap)
+    {
+        boolean extra = false;
+        if (charToMap < ASCII_MAX)
+        {
+            switch (charToMap)
+            {
+                case '"' : // quot
+                	if (!outputString.equals("&quot;"))
+                		extra = true;  
+                	break;
+                case '&' : // amp
+                	if (!outputString.equals("&amp;"))
+                		extra = true;
+                	break;
+                case '<' : // lt
+                	if (!outputString.equals("&lt;"))
+                		extra = true;
+                	break;
+                case '>' : // gt
+                	if (!outputString.equals("&gt;"))
+                		extra = true;
+                    break;
+                default : // other entity in range 0 to 127  
+                    extra = true;
+            }
+        }
+        return extra;
+    }    
+    
+    /**
+     * If the character is in the ASCII range then
+     * mark it as needing replacement with
+     * a String on output if it occurs in a text node.
+     * @param ch
+     */
+    private void setASCIItextDirty(int j) 
+    {
+        if (0 <= j && j < ASCII_MAX) 
+        {
+            shouldMapTextChar_ASCII[j] = true;
+        } 
+    }
+    
+    /**
+     * If the character is in the ASCII range then
+     * mark it as needing replacement with
+     * a String on output if it occurs in a attribute value.
+     * @param ch
+     */
+    private void setASCIIattrDirty(int j) 
+    {
+        if (0 <= j && j < ASCII_MAX) 
+        {
+            shouldMapAttrChar_ASCII[j] = true;
+        } 
+    }
+
+    
+    /**
+     * Call this method to register a char to String mapping, for example
+     * to map '<' to "&lt;".
+     * @param outputString The String to map to.
+     * @param inputChar The char to map from.
+     * @return true if the mapping is not one of:
+     * <ul>
+     * <li> '<' to "&lt;"
+     * <li> '>' to "&gt;"
+     * <li> '&' to "&amp;"
+     * <li> '"' to "&quot;"
+     * </ul>
+     */
+    boolean defineChar2StringMapping(String outputString, char inputChar) 
+    {
+        CharKey character = new CharKey(inputChar);
+        m_charToString.put(character, outputString);
+        set(inputChar);  // mark the character has having a mapping to a String
+        
+        boolean extraMapping = extraEntity(outputString, inputChar);
+        return extraMapping;
+        	
+    }
+
+    /**
+     * Simple class for fast lookup of char values, when used with
+     * hashtables.  You can set the char, then use it as a key.
+     *  
+     * @xsl.usage internal
+     */
+    private static class CharKey extends Object
+    {
+
+      /** String value          */
+      private char m_char;
+
+      /**
+       * Constructor CharKey
+       *
+       * @param key char value of this object.
+       */
+      public CharKey(char key)
+      {
+        m_char = key;
+      }
+  
+      /**
+       * Default constructor for a CharKey.
+       *
+       * @param key char value of this object.
+       */
+      public CharKey()
+      {
+      }
+  
+      /**
+       * Get the hash value of the character.  
+       *
+       * @return hash value of the character.
+       */
+      public final void setChar(char c)
+      {
+        m_char = c;
+      }
+
+
+
+      /**
+       * Get the hash value of the character.  
+       *
+       * @return hash value of the character.
+       */
+      public final int hashCode()
+      {
+        return (int)m_char;
+      }
+
+      /**
+       * Override of equals() for this object 
+       *
+       * @param obj to compare to
+       *
+       * @return True if this object equals this string value 
+       */
+      public final boolean equals(Object obj)
+      {
+        return ((CharKey)obj).m_char == m_char;
+      }
+    }
+   
+
+}
diff --git a/src/main/java/org/apache/xml/serializer/DOM3Serializer.java b/src/main/java/org/apache/xml/serializer/DOM3Serializer.java
new file mode 100644
index 0000000..df0e1fd
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/DOM3Serializer.java
@@ -0,0 +1,170 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id$

+ */

+

+package org.apache.xml.serializer;

+

+import java.io.IOException;

+

+import org.w3c.dom.DOMErrorHandler;

+import org.w3c.dom.Node;

+import org.w3c.dom.ls.LSSerializerFilter;

+

+/**

+ * This interface is not intended to be used

+ * by an end user, but rather by an XML parser that is implementing the DOM 

+ * Level 3 Load and Save APIs.

+ * <p>

+ * 

+ * See the DOM Level 3 Load and Save interface at <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html#LS-LSSerializer">LSSeializer</a>.

+ * 

+ * For a list of configuration parameters for DOM Level 3 see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#DOMConfiguration">DOMConfiguration</a>.

+ * For additional configuration parameters available with the DOM Level 3 Load and Save API LSSerializer see

+ * <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html#LS-LSSerializer-config">LSerializer config</a>.

+ * <p>

+ * The following example uses a DOM3Serializer indirectly, through an an XML

+ * parser that uses this class as part of its implementation of the DOM Level 3

+ * Load and Save APIs, and is the prefered way to serialize with DOM Level 3 APIs.

+ * <p>

+ * Example:

+ * <pre>

+ *    public class TestDOM3 {

+ *

+ *    public static void main(String args[]) throws Exception {

+ *        // Get document to serialize

+ *        TestDOM3 test = new TestDOM3();

+ *        

+ *        // Serialize using standard DOM Level 3 Load/Save APIs        

+ *        System.out.println(test.testDOM3LS());

+ *    }

+ *

+ *    public org.w3c.dom.Document getDocument() throws Exception {

+ *        // Create a simple DOM Document.

+ *        javax.xml.parsers.DocumentBuilderFactory factory = 

+ *            javax.xml.parsers.DocumentBuilderFactory.newInstance();

+ *        javax.xml.parsers.DocumentBuilder builder = factory.newDocumentBuilder();

+ *        byte[] bytes = "<parent><child/></parent>".getBytes();

+ *        java.io.InputStream is = new java.io.ByteArrayInputStream(bytes);

+ *        org.w3c.dom.Document doc = builder.parse(is);

+ *        return doc;

+ *    }

+ *    

+ *    //

+ *    // This method uses standard DOM Level 3 Load Save APIs:

+ *    //   org.w3c.dom.bootstrap.DOMImplementationRegistry

+ *    //   org.w3c.dom.ls.DOMImplementationLS

+ *    //   org.w3c.dom.ls.DOMImplementationLS

+ *    //   org.w3c.dom.ls.LSSerializer

+ *    //   org.w3c.dom.DOMConfiguration

+ *    //   

+ *    // The only thing non-standard in this method is the value set for the

+ *    // name of the class implementing the DOM Level 3 Load Save APIs,

+ *    // which in this case is:

+ *    //   org.apache.xerces.dom.DOMImplementationSourceImpl

+ *    //

+ *

+ *    public String testDOM3LS() throws Exception {

+ *        

+ *        // Get a simple DOM Document that will be serialized.

+ *        org.w3c.dom.Document docToSerialize = getDocument();

+ *

+ *        // Get a factory (DOMImplementationLS) for creating a Load and Save object.

+ *        org.w3c.dom.ls.DOMImplementationLS impl = 

+ *            (org.w3c.dom.ls.DOMImplementationLS) 

+ *            org.w3c.dom.bootstrap.DOMImplementationRegistry.newInstance().getDOMImplementation("LS");

+ *

+ *        // Use the factory to create an object (LSSerializer) used to 

+ *        // write out or save the document.

+ *        org.w3c.dom.ls.LSSerializer writer = impl.createLSSerializer();

+ *        org.w3c.dom.DOMConfiguration config = writer.getDomConfig();

+ *        config.setParameter("format-pretty-print", Boolean.TRUE);

+ *        

+ *        // Use the LSSerializer to write out or serialize the document to a String.

+ *        String serializedXML = writer.writeToString(docToSerialize);

+ *        return serializedXML;

+ *    }

+ *    

+ *    }  // end of class TestDOM3

+ * </pre>

+ * 

+ * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#DOMConfiguration">DOMConfiguration</a>

+ * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html#LS-LSSerializer-config">LSSerializer</a>

+ * @see org.apache.xml.serializer.Serializer

+ * @see org.apache.xml.serializer.DOMSerializer

+ * 

+ * @xsl.usage advanced

+ *

+ */

+public interface DOM3Serializer {

+    /**

+     * Serializes the Level 3 DOM node. Throws an exception only if an I/O

+     * exception occured while serializing.

+     * 

+     * This interface is a public API.

+     *

+     * @param node the Level 3 DOM node to serialize

+     * @throws IOException if an I/O exception occured while serializing

+     */

+    public void serializeDOM3(Node node) throws IOException;

+

+    /**

+     * Sets a DOMErrorHandler on the DOM Level 3 Serializer.

+     * 

+     * This interface is a public API.

+     *

+     * @param handler the Level 3 DOMErrorHandler

+     */

+    public void setErrorHandler(DOMErrorHandler handler);

+

+    /**

+     * Returns a DOMErrorHandler set on the DOM Level 3 Serializer.

+     * 

+     * This interface is a public API.

+     *

+     * @return A Level 3 DOMErrorHandler

+     */

+    public DOMErrorHandler getErrorHandler();

+

+    /**

+     * Sets a LSSerializerFilter on the DOM Level 3 Serializer to filter nodes

+     * during serialization.

+     * 

+     * This interface is a public API.

+     *

+     * @param filter the Level 3 LSSerializerFilter

+     */

+    public void setNodeFilter(LSSerializerFilter filter);

+

+    /**

+     * Returns a LSSerializerFilter set on the DOM Level 3 Serializer to filter nodes

+     * during serialization.

+     * 

+     * This interface is a public API.

+     *

+     * @return The Level 3 LSSerializerFilter

+     */

+    public LSSerializerFilter getNodeFilter();

+

+    /**

+     * Sets the end-of-line sequence of characters to be used during serialization

+     * @param newLine The end-of-line sequence of characters to be used during serialization

+     */

+    public void setNewLine(char[] newLine);

+}

diff --git a/src/main/java/org/apache/xml/serializer/DOMSerializer.java b/src/main/java/org/apache/xml/serializer/DOMSerializer.java
new file mode 100644
index 0000000..86752b6
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/DOMSerializer.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOMSerializer.java 475350 2006-11-15 18:39:15Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+
+import org.w3c.dom.Node;
+
+/**
+ * Interface for a DOM serializer implementation.
+ * <p>
+ * The DOMSerializer is a facet of a serializer and is obtained from the
+ * asDOMSerializer() method of the ({@link Serializer}) interface. 
+ * A serializer may or may not support a DOM serializer, if it does not then the 
+ * return value from asDOMSerializer() is null.
+ * <p>
+ * Example:
+ * <pre>
+ * // Create a document to be serialized
+ * org.w3c.dom.Document doc = ...;
+ * 
+ * // Create a Serializer that will be used
+ * // to serialize the document  
+ * org.apache.xml.serializer.Serializer ser = ...;
+ *
+ * // Set the Writer to write output to, and 
+ * // serialize the DOM using that Serializer
+ * java.io.StringWriter sw = new java.io.StringWriter();
+ * ser.setWriter(sw);
+ * DOMSerialzier dser = ser.asDOMSerializer();
+ * dser.serialize(doc);
+ * 
+ * // Write out the serialized XML in the String.
+ * System.out.println(sw.toString());
+ * </pre>
+ * 
+ * @see OutputPropertiesFactory
+ * @see SerializerFactory
+ * @see Serializer
+ * 
+ * @xsl.usage general
+ *
+ */
+public interface DOMSerializer
+{
+    /**
+     * Serializes the DOM node. Throws an exception only if an I/O
+     * exception occured while serializing.
+     * 
+     * This interface is a public API.
+     *
+     * @param node the DOM node to serialize
+     * @throws IOException if an I/O exception occured while serializing
+     */
+    public void serialize(Node node) throws IOException;
+}
diff --git a/src/main/java/org/apache/xml/serializer/ElemContext.java b/src/main/java/org/apache/xml/serializer/ElemContext.java
new file mode 100644
index 0000000..0461645
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ElemContext.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemContext.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+/**
+ * This class is a stack frame that consists of 
+ * information about the element currently being processed 
+ * by a serializer. Consider this example:
+ * <pre>
+ *   <A>
+ *     <B1>
+ *     </B1>
+ *     <B2>
+ *     </B2>
+ *   <A>
+ * </pre> 
+ * 
+ * A stack frame will be pushed for "A" at depth 1, 
+ * then another one for "B1" at depth 2.
+ * Then "B1" stackframe is popped.  When the stack frame for "B2" is 
+ * pushed, this implementation re-uses the old stack fram object used
+ * by "B1" to be efficient at not creating too many of these object.
+ * 
+ * This is by no means a public class, and neither are its fields or methods,
+ * they are all helper fields for a serializer.
+ * 
+ * The purpose of this class is to be more consistent with pushing information
+ * when a new element is being serialized and more quickly restoring the old
+ * information about the parent element with a simple pop() when the
+ * child element is done.  Previously there was some redundant and error-prone
+ * calculations going on to retore information.
+ * 
+ * @xsl.usage internal
+ */
+final class ElemContext
+{
+    // Fields that form the context of the element
+
+    /**
+     * The nesting depth of the element inside other elements.
+     */
+    final int m_currentElemDepth;
+
+    /** HTML field, the element description of the HTML element */
+    ElemDesc m_elementDesc = null;
+
+    /**
+     * The local name of the element.
+     */
+    String m_elementLocalName = null;
+
+    /**
+     * The fully qualified name of the element (with prefix, if any).
+     */
+    String m_elementName = null;
+
+    /**
+     * The URI of the element.
+     * If this value is null it means that the URI is not yet determined
+     * for the element. Valid values are the empty string "", meaning
+     * that it is in no namespace, or a string of non-zero length.
+     */
+    String m_elementURI = null;
+
+    /** If the element is in the cdata-section-names list
+     * then the value is true. If it is true the text children of the element
+     * should be output in CDATA section blocks. 
+     */
+    boolean m_isCdataSection;
+
+    /** True if the current element has output escaping disabled.
+     * This is true for SCRIPT and STYLE elements. 
+     */
+    boolean m_isRaw = false;
+
+    /** The next element "stack frame". This value will only be
+     * set once as deeper stack frames are not deleted when popped off,
+     * but are rather re-used when a push is required.
+     * 
+     * This makes for very fast pushing and popping of stack frames 
+     * because very few stack frame objects are ever created, they are
+     * mostly re-used.  This re-use saves object creation but it also means
+     * that connections between the frames via m_next and m_prev
+     * never changes either. Just the contents of the frames change
+     * as they are re-used. Only the reference to the current stack frame, which
+     * is held by the serializer is changed via a quick pop() or push().
+     */
+    private ElemContext m_next;
+
+    /** The previous element "stack frame". */
+    final ElemContext m_prev;
+
+    /**
+     * Set to true when a start tag is started, or open, but not all the
+     * attributes or namespace information is yet collected.
+     */
+    boolean m_startTagOpen = false;
+
+    /**
+     * Constructor to create the root of the element contexts. 
+     *
+     */
+    ElemContext()
+    {
+        // this assignment means can never pop this context off
+        m_prev = this;
+        // depth 0 because it doesn't correspond to any element
+        m_currentElemDepth = 0;
+    }
+
+    /**
+     * Constructor to create the "stack frame" for a given element depth.
+     * 
+     * This implementation will re-use the context at each depth. If
+     * a documents deepest element depth is N then there will be (N+1)
+     * such objects created, no more than that.
+     * 
+     * @param previous The "stack frame" corresponding to the new
+     * elements parent element.
+     */
+    private ElemContext(final ElemContext previous)
+    {
+        m_prev = previous;
+        m_currentElemDepth = previous.m_currentElemDepth + 1;
+    }
+
+    /**
+     * Pop the current "stack frame".
+     * @return Returns the parent "stack frame" of the one popped.
+     */
+    final ElemContext pop()
+    {
+        /* a very simple pop.  No clean up is done of the deeper
+         * stack frame.  All deeper stack frames are still attached
+         * but dormant, just waiting to be re-used.
+         */
+        return this.m_prev;
+    }
+
+    /**
+     * This method pushes an element "stack frame" 
+     * but with no initialization of values in that frame.
+     * This method is used for optimization purposes, like when pushing
+     * a stack frame for an HTML "IMG" tag which has no children and
+     * the stack frame will almost immediately be popped.
+     */
+    final ElemContext push()
+    {
+        ElemContext frame = this.m_next;
+        if (frame == null)
+        {
+            /* We have never been at this depth yet, and there is no
+             * stack frame to re-use, so we now make a new one.
+             */
+            frame = new ElemContext(this);
+            this.m_next = frame;
+        }
+        /*
+         * We shouldn't need to set this true because we should just
+         * be pushing a dummy stack frame that will be instantly popped.
+         * Yet we need to be ready in case this element does have
+         * unexpected children.
+         */
+        frame.m_startTagOpen = true;
+        return frame;
+    }
+    
+    /**
+     * Push an element context on the stack. This context keeps track of
+     * information gathered about the element.
+     * @param uri The URI for the namespace for the element name, 
+     * can be null if it is not yet known.
+     * @param localName The local name of the element (no prefix),  
+     * can be null.
+     * @param qName The qualified name (with prefix, if any) 
+     * of the element, this parameter is required.
+     */
+    final ElemContext push(
+        final String uri,
+        final String localName,
+        final String qName)
+    {
+        ElemContext frame = this.m_next;
+        if (frame == null)
+        {
+            /* We have never been at this depth yet, and there is no
+             * stack frame to re-use, so we now make a new one.
+             */
+            frame = new ElemContext(this);
+            this.m_next = frame;
+        }
+
+        // Initialize, or reset values in the new or re-used stack frame.
+        frame.m_elementName = qName;
+        frame.m_elementLocalName = localName;
+        frame.m_elementURI = uri;
+        frame.m_isCdataSection = false;
+        frame.m_startTagOpen = true;
+
+        // is_Raw is already set in the HTML startElement() method
+        // frame.m_isRaw = false; 
+        return frame;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/ElemDesc.java b/src/main/java/org/apache/xml/serializer/ElemDesc.java
new file mode 100644
index 0000000..f551210
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ElemDesc.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ElemDesc.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import org.apache.xml.serializer.utils.StringToIntTable;
+
+/**
+ * This class has a series of flags (bit values) that describe an HTML element
+ * <p>
+ * This class is not a public API.
+ * It is public because it is used outside of this package.
+ * 
+ * @xsl.usage internal
+ */
+public final class ElemDesc
+{
+    /** Bit flags to tell about this element type. */
+    private int m_flags;
+
+    /**
+     * Table of attribute names to integers, which contain bit flags telling about
+     *  the attributes.
+     */
+    private StringToIntTable m_attrs = null;
+
+    /** Bit position if this element type is empty. */
+    static final int EMPTY = (1 << 1);
+
+    /** Bit position if this element type is a flow. */
+    private static final int FLOW = (1 << 2);
+
+    /** Bit position if this element type is a block. */
+    static final int BLOCK = (1 << 3);
+
+    /** Bit position if this element type is a block form. */
+    static final int BLOCKFORM = (1 << 4);
+
+    /** Bit position if this element type is a block form field set. */
+    static final int BLOCKFORMFIELDSET = (1 << 5);
+
+    /** Bit position if this element type is CDATA. */
+    private static final int CDATA = (1 << 6);
+
+    /** Bit position if this element type is PCDATA. */
+    private static final int PCDATA = (1 << 7);
+
+    /** Bit position if this element type is should be raw characters. */
+    static final int RAW = (1 << 8);
+
+    /** Bit position if this element type should be inlined. */
+    private static final int INLINE = (1 << 9);
+
+    /** Bit position if this element type is INLINEA. */
+    private static final int INLINEA = (1 << 10);
+
+    /** Bit position if this element type is an inline label. */
+    static final int INLINELABEL = (1 << 11);
+
+    /** Bit position if this element type is a font style. */
+    static final int FONTSTYLE = (1 << 12);
+
+    /** Bit position if this element type is a phrase. */
+    static final int PHRASE = (1 << 13);
+
+    /** Bit position if this element type is a form control. */
+    static final int FORMCTRL = (1 << 14);
+
+    /** Bit position if this element type is ???. */
+    static final int SPECIAL = (1 << 15);
+
+    /** Bit position if this element type is ???. */
+    static final int ASPECIAL = (1 << 16);
+
+    /** Bit position if this element type is an odd header element. */
+    static final int HEADMISC = (1 << 17);
+
+    /** Bit position if this element type is a head element (i.e. H1, H2, etc.) */
+    static final int HEAD = (1 << 18);
+
+    /** Bit position if this element type is a list. */
+    static final int LIST = (1 << 19);
+
+    /** Bit position if this element type is a preformatted type. */
+    static final int PREFORMATTED = (1 << 20);
+
+    /** Bit position if this element type is whitespace sensitive. */
+    static final int WHITESPACESENSITIVE = (1 << 21);
+
+    /** Bit position if this element type is a header element (i.e. HEAD). */
+    static final int HEADELEM = (1 << 22);
+    
+    /** Bit position if this element is the "HTML" element */
+    static final int HTMLELEM = (1 << 23);
+
+    /** Bit position if this attribute type is a URL. */
+    public static final int ATTRURL = (1 << 1);
+
+    /** Bit position if this attribute type is an empty type. */
+    public static final int ATTREMPTY = (1 << 2);
+
+    /**
+     * Construct an ElemDesc from a set of bit flags.
+     *
+     *
+     * @param flags Bit flags that describe the basic properties of this element type.
+     */
+    ElemDesc(int flags)
+    {
+        m_flags = flags;
+    }
+
+    /**
+     * Tell if this element type has the basic bit properties that are passed
+     * as an argument.
+     *
+     * @param flags Bit flags that describe the basic properties of interest.
+     *
+     * @return true if any of the flag bits are true.
+     */
+    private boolean is(int flags)
+    {
+
+        // int which = (m_flags & flags);
+        return (m_flags & flags) != 0;
+    }
+
+    int getFlags() {
+        return m_flags;
+    }
+
+    /**
+     * Set an attribute name and it's bit properties.
+     *
+     *
+     * @param name non-null name of attribute, in upper case.
+     * @param flags flag bits.
+     */
+    void setAttr(String name, int flags)
+    {
+
+        if (null == m_attrs)
+            m_attrs = new StringToIntTable();
+
+        m_attrs.put(name, flags);
+    }
+
+    /**
+     * Tell if any of the bits of interest are set for a named attribute type.
+     *
+     * @param name non-null reference to attribute name, in any case.
+     * @param flags flag mask.
+     *
+     * @return true if any of the flags are set for the named attribute.
+     */
+    public boolean isAttrFlagSet(String name, int flags)
+    {
+        return (null != m_attrs)
+            ? ((m_attrs.getIgnoreCase(name) & flags) != 0)
+            : false;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/EncodingInfo.java b/src/main/java/org/apache/xml/serializer/EncodingInfo.java
new file mode 100644
index 0000000..60465d4
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/EncodingInfo.java
@@ -0,0 +1,562 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: EncodingInfo.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+
+/**
+ * Holds information about a given encoding, which is the Java name for the
+ * encoding, the equivalent ISO name.
+ * <p>
+ * An object of this type has two useful methods
+ * <pre>
+ * isInEncoding(char ch);
+ * </pre>
+ * which can be called if the character is not the high one in
+ * a surrogate pair and:
+ * <pre>
+ * isInEncoding(char high, char low);
+ * </pre>
+ * which can be called if the two characters from a high/low surrogate pair.
+ * <p>
+ * An EncodingInfo object is a node in a binary search tree. Such a node
+ * will answer if a character is in the encoding, and do so for a given
+ * range of unicode values (<code>m_first</code> to
+ * <code>m_last</code>). It will handle a certain range of values
+ * explicitly (<code>m_explFirst</code> to <code>m_explLast</code>).
+ * If the unicode point is before that explicit range, that is it
+ * is in the range <code>m_first <= value < m_explFirst</code>, then it will delegate to another EncodingInfo object for The root
+ * of such a tree, m_before.  Likewise for values in the range 
+ * <code>m_explLast < value <= m_last</code>, but delgating to <code>m_after</code>
+ * <p>
+ * Actually figuring out if a code point is in the encoding is expensive. So the
+ * purpose of this tree is to cache such determinations, and not to build the
+ * entire tree of information at the start, but only build up as much of the 
+ * tree as is used during the transformation.
+ * <p>
+ * This Class is not a public API, and should only be used internally within
+ * the serializer.
+ * <p>
+ * This class is not a public API.
+ * @xsl.usage internal
+ */
+public final class EncodingInfo extends Object
+{
+
+    /**
+     * Not all characters in an encoding are in on contiguous group,
+     * however there is a lowest contiguous group starting at '\u0001'
+     * and working up to m_highCharInContiguousGroup.
+     * <p>
+     * This is the char for which chars at or below this value are 
+     * definately in the encoding, although for chars
+     * above this point they might be in the encoding.
+     * This exists for performance, especially for ASCII characters
+     * because for ASCII all chars in the range '\u0001' to '\u007F' 
+     * are in the encoding.
+     * 
+     */
+    private final char m_highCharInContiguousGroup;
+
+    /**
+     * The ISO encoding name.
+     */
+    final String name;
+
+    /**
+     * The name used by the Java convertor.
+     */
+    final String javaName;
+    
+    /**
+     * A helper object that we can ask if a
+     * single char, or a surrogate UTF-16 pair
+     * of chars that form a single character,
+     * is in this encoding.
+     */
+    private InEncoding m_encoding;
+    
+    /**
+     * This is not a public API. It returns true if the
+     * char in question is in the encoding.
+     * @param ch the char in question.
+     * <p>
+     * This method is not a public API.
+     * @xsl.usage internal
+     */
+    public boolean isInEncoding(char ch) {
+        if (m_encoding == null) {
+            m_encoding = new EncodingImpl();
+            
+            // One could put alternate logic in here to
+            // instantiate another object that implements the
+            // InEncoding interface. For example if the JRE is 1.4 or up
+            // we could have an object that uses JRE 1.4 methods
+        }
+        return m_encoding.isInEncoding(ch); 
+    }
+    
+    /**
+     * This is not a public API. It returns true if the
+     * character formed by the high/low pair is in the encoding.
+     * @param high a char that the a high char of a high/low surrogate pair.
+     * @param low a char that is the low char of a high/low surrogate pair.
+     * <p>
+     * This method is not a public API.
+     * @xsl.usage internal
+     */
+    public boolean isInEncoding(char high, char low) {
+        if (m_encoding == null) {
+            m_encoding = new EncodingImpl();
+            
+            // One could put alternate logic in here to
+            // instantiate another object that implements the
+            // InEncoding interface. For example if the JRE is 1.4 or up
+            // we could have an object that uses JRE 1.4 methods
+        }
+        return m_encoding.isInEncoding(high, low); 
+    }
+
+    /**
+     * Create an EncodingInfo object based on the ISO name and Java name.
+     * If both parameters are null any character will be considered to
+     * be in the encoding. This is useful for when the serializer is in
+     * temporary output state, and has no assciated encoding.
+     *
+     * @param name reference to the ISO name.
+     * @param javaName reference to the Java encoding name.
+     * @param highChar The char for which characters at or below this value are 
+     * definately in the
+     * encoding, although for characters above this point they might be in the encoding.
+     */
+    public EncodingInfo(String name, String javaName, char highChar)
+    {
+
+        this.name = name;
+        this.javaName = javaName;
+        this.m_highCharInContiguousGroup = highChar;
+    }
+    
+    
+    
+    /**
+     * A simple interface to isolate the implementation.
+     * We could also use some new JRE 1.4 methods in another implementation
+     * provided we use reflection with them.
+     * <p>
+     * This interface is not a public API,
+     * and should only be used internally within the serializer. 
+     * @xsl.usage internal
+     */
+    private interface InEncoding {
+        /**
+         * Returns true if the char is in the encoding
+         */
+        public boolean isInEncoding(char ch);
+        /**
+         * Returns true if the high/low surrogate pair forms
+         * a character that is in the encoding.
+         */
+        public boolean isInEncoding(char high, char low);
+    }
+
+    /**
+     * This class implements the 
+     */
+    private class EncodingImpl implements InEncoding {
+        
+
+
+        public boolean isInEncoding(char ch1) {
+            final boolean ret;
+            int codePoint = Encodings.toCodePoint(ch1);
+            if (codePoint < m_explFirst) {
+                // The unicode value is before the range
+                // that we explictly manage, so we delegate the answer.
+                
+                // If we don't have an m_before object to delegate to, make one.
+                if (m_before == null)
+                    m_before =
+                        new EncodingImpl(
+                            m_encoding,
+                            m_first,
+                            m_explFirst - 1,
+                            codePoint);
+                ret = m_before.isInEncoding(ch1);
+            } else if (m_explLast < codePoint) {
+                // The unicode value is after the range
+                // that we explictly manage, so we delegate the answer.
+                
+                // If we don't have an m_after object to delegate to, make one.
+                if (m_after == null)
+                    m_after =
+                        new EncodingImpl(
+                            m_encoding,
+                            m_explLast + 1,
+                            m_last,
+                            codePoint);
+                ret = m_after.isInEncoding(ch1);
+            } else {
+                // The unicode value is in the range we explitly handle
+                final int idx = codePoint - m_explFirst;
+                
+                // If we already know the answer, just return it.
+                if (m_alreadyKnown[idx])
+                    ret = m_isInEncoding[idx];
+                else {
+                    // We don't know the answer, so find out,
+                    // which may be expensive, then cache the answer 
+                    ret = inEncoding(ch1, m_encoding);
+                    m_alreadyKnown[idx] = true;
+                    m_isInEncoding[idx] = ret;
+                }
+            }
+            return ret;
+        }
+
+        public boolean isInEncoding(char high, char low) {
+            final boolean ret;
+            int codePoint = Encodings.toCodePoint(high,low);
+            if (codePoint < m_explFirst) {
+                // The unicode value is before the range
+                // that we explictly manage, so we delegate the answer.
+                
+                // If we don't have an m_before object to delegate to, make one.
+                if (m_before == null)
+                    m_before =
+                        new EncodingImpl(
+                            m_encoding,
+                            m_first,
+                            m_explFirst - 1,
+                            codePoint);
+                ret = m_before.isInEncoding(high,low);
+            } else if (m_explLast < codePoint) {
+                // The unicode value is after the range
+                // that we explictly manage, so we delegate the answer.
+                
+                // If we don't have an m_after object to delegate to, make one.
+                if (m_after == null)
+                    m_after =
+                        new EncodingImpl(
+                            m_encoding,
+                            m_explLast + 1,
+                            m_last,
+                            codePoint);
+                ret = m_after.isInEncoding(high,low);
+            } else {
+                // The unicode value is in the range we explitly handle
+                final int idx = codePoint - m_explFirst;
+                
+                // If we already know the answer, just return it.
+                if (m_alreadyKnown[idx])
+                    ret = m_isInEncoding[idx];
+                else {
+                    // We don't know the answer, so find out,
+                    // which may be expensive, then cache the answer 
+                    ret = inEncoding(high, low, m_encoding);
+                    m_alreadyKnown[idx] = true;
+                    m_isInEncoding[idx] = ret;
+                }
+            }
+            return ret;
+        }
+
+        /**
+         * The encoding.
+         */
+        final private String m_encoding;
+        /**
+         * m_first through m_last is the range of unicode
+         * values that this object will return an answer on.
+         * It may delegate to a similar object with a different
+         * range
+         */
+        final private int m_first;
+        
+        /**
+         * m_explFirst through m_explLast is the range of unicode
+         * value that this object handles explicitly and does not
+         * delegate to a similar object.
+         */
+        final private int m_explFirst;
+        final private int m_explLast;
+        final private int m_last;
+
+        /**
+         * The object, of the same type as this one,
+         * that handles unicode values in a range before
+         * the range explictly handled by this object, and
+         * to which this object may delegate.
+         */
+        private InEncoding m_before;
+        /**
+         * The object, of the same type as this one,
+         * that handles unicode values in a range after
+         * the range explictly handled by this object, and
+         * to which this object may delegate.
+         */
+        private InEncoding m_after;
+        
+        /**
+         * The number of unicode values explicitly handled
+         * by a single EncodingInfo object. This value is 
+         * tuneable, but is set to 128 because that covers the
+         * entire low range of ASCII type chars within a single
+         * object.
+         */
+        private static final int RANGE = 128;
+
+        /**
+         * A flag to record if we already know the answer
+         * for the given unicode value.
+         */
+        final private boolean m_alreadyKnown[] = new boolean[RANGE];
+        /**
+         * A table holding the answer on whether the given unicode
+         * value is in the encoding.
+         */
+        final private boolean m_isInEncoding[] = new boolean[RANGE];
+        
+        private EncodingImpl() {
+            // This object will answer whether any unicode value
+            // is in the encoding, it handles values 0 through Integer.MAX_VALUE
+            this(javaName, 0, Integer.MAX_VALUE, (char) 0);
+        }
+
+        private EncodingImpl(String encoding, int first, int last, int codePoint) {
+            // Set the range of unicode values that this object manages
+            // either explicitly or implicitly.
+            m_first = first;
+            m_last = last;  
+                      
+            // Set the range of unicode values that this object 
+            // explicitly manages
+            m_explFirst = codePoint;
+            m_explLast = codePoint + (RANGE-1);  
+            
+            m_encoding = encoding;
+            
+            if (javaName != null)
+            {
+                // Some optimization.
+                if (0 <= m_explFirst && m_explFirst <= 127) {
+                    // This particular EncodingImpl explicitly handles
+                    // characters in the low range.
+                    if ("UTF8".equals(javaName)
+                        || "UTF-16".equals(javaName)
+                        || "ASCII".equals(javaName)
+                        || "US-ASCII".equals(javaName)
+                        || "Unicode".equals(javaName)
+                        || "UNICODE".equals(javaName)
+                        || javaName.startsWith("ISO8859")) {
+                        
+                        // Not only does this EncodingImpl object explicitly
+                        // handle chracters in the low range, it is
+                        // also one that we know something about, without
+                        // needing to call inEncoding(char ch, String encoding)
+                        // for this low range
+                        //
+                        // By initializing the table ahead of time
+                        // for these low values, we prevent the expensive
+                        // inEncoding(char ch, String encoding)
+                        // from being called, at least for these common
+                        // encodings.
+                        for (int unicode = 1; unicode < 127; unicode++) {
+                            final int idx = unicode - m_explFirst;
+                            if (0 <= idx && idx < RANGE) {
+                                m_alreadyKnown[idx] = true;
+                                m_isInEncoding[idx] = true;
+                            }
+                        }
+                    }
+                }
+
+                /* A little bit more than optimization.
+                 * 
+                 * We will say that any character is in the encoding if
+                 * we don't have an encoding.
+                 * This is meaningful when the serializer is being used
+                 * in temporary output state, where we are not writing to
+                 * the final output tree.  It is when writing to the
+                 * final output tree that we need to worry about the output
+                 * encoding
+                 */
+                if (javaName == null) {
+                    for (int idx = 0; idx < m_alreadyKnown.length; idx++) {
+                        m_alreadyKnown[idx] = true;
+                        m_isInEncoding[idx] = true;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * This is heart of the code that determines if a given character
+     * is in the given encoding. This method is probably expensive,
+     * and the answer should be cached.
+     * <p>
+     * This method is not a public API,
+     * and should only be used internally within the serializer.
+     * @param ch the char in question, that is not a high char of
+     * a high/low surrogate pair.
+     * @param encoding the Java name of the enocding.
+     * 
+     * @xsl.usage internal
+     * 
+     */
+    private static boolean inEncoding(char ch, String encoding) {
+        boolean isInEncoding;
+        try {
+            char cArray[] = new char[1];
+            cArray[0] = ch;
+            // Construct a String from the char 
+            String s = new String(cArray);
+            // Encode the String into a sequence of bytes 
+            // using the given, named charset. 
+            byte[] bArray = s.getBytes(encoding);
+            isInEncoding = inEncoding(ch, bArray);
+
+        } catch (Exception e) {
+            isInEncoding = false;
+            
+            // If for some reason the encoding is null, e.g.
+            // for a temporary result tree, we should just
+            // say that every character is in the encoding.
+            if (encoding == null)
+            	isInEncoding = true;
+        }
+        return isInEncoding;
+    }
+    
+    /**
+     * This is heart of the code that determines if a given high/low
+     * surrogate pair forms a character that is in the given encoding.
+     * This method is probably expensive, and the answer should be cached. 
+     * <p>
+     * This method is not a public API,
+     * and should only be used internally within the serializer.
+     * @param high the high char of
+     * a high/low surrogate pair.
+     * @param low the low char of a high/low surrogate pair.
+     * @param encoding the Java name of the encoding.
+     * 
+     * @xsl.usage internal
+     * 
+     */ 
+    private static boolean inEncoding(char high, char low, String encoding) {
+        boolean isInEncoding;
+        try {
+            char cArray[] = new char[2];
+            cArray[0] = high;
+            cArray[1] = low;
+            // Construct a String from the char 
+            String s = new String(cArray);
+            // Encode the String into a sequence of bytes 
+            // using the given, named charset. 
+            byte[] bArray = s.getBytes(encoding);
+            isInEncoding = inEncoding(high,bArray);
+        } catch (Exception e) {
+            isInEncoding = false;
+        }
+        
+        return isInEncoding;
+    } 
+    
+    /**
+     * This method is the core of determining if character
+     * is in the encoding. The method is not foolproof, because
+     * s.getBytes(encoding) has specified behavior only if the
+     * characters are in the specified encoding. However this
+     * method tries it's best.
+     * @param ch the char that was converted using getBytes, or
+     * the first char of a high/low pair that was converted.
+     * @param data the bytes written out by the call to s.getBytes(encoding);
+     * @return true if the character is in the encoding.
+     */
+    private static boolean inEncoding(char ch, byte[] data) {
+        final boolean isInEncoding;
+        // If the string written out as data is not in the encoding,
+        // the output is not specified according to the documentation
+        // on the String.getBytes(encoding) method,
+        // but we do our best here.        
+        if (data==null || data.length == 0) {
+            isInEncoding = false;
+        }
+        else {
+            if (data[0] == 0)
+                isInEncoding = false;
+            else if (data[0] == '?' && ch != '?')
+                isInEncoding = false;
+            /*
+             * else if (isJapanese) {
+             *   // isJapanese is really 
+             *   //   (    "EUC-JP".equals(javaName) 
+             *   //    ||  "EUC_JP".equals(javaName)
+             *  //     ||  "SJIS".equals(javaName)   )
+             * 
+             *   // Work around some bugs in JRE for Japanese
+             *   if(data[0] == 0x21)
+             *     isInEncoding = false;
+             *   else if (ch == 0xA5)
+             *     isInEncoding = false;
+             *   else
+             *     isInEncoding = true;
+             * }
+             */ 
+                
+            else {
+                // We don't know for sure, but it looks like it is in the encoding
+                isInEncoding = true; 
+            }
+        }
+        return isInEncoding;
+    }
+    
+    /**
+     * This method exists for performance reasons.
+     * <p>
+     * Except for '\u0000', if a char is less than or equal to the value
+     * returned by this method then it in the encoding.
+     * <p>
+     * The characters in an encoding are not contiguous, however
+     * there is a lowest group of chars starting at '\u0001' upto and
+     * including the char returned by this method that are all in the encoding.
+     * So the char returned by this method essentially defines the lowest
+     * contiguous group.
+     * <p>
+     * chars above the value returned might be in the encoding, but 
+     * chars at or below the value returned are definately in the encoding.
+     * <p>
+     * In any case however, the isInEncoding(char) method can be used
+     * regardless of the value of the char returned by this method.
+     * <p>
+     * If the value returned is '\u0000' it means that every character must be tested
+     * with an isInEncoding method {@link #isInEncoding(char)} or {@link #isInEncoding(char, char)} 
+     * for surrogate pairs.
+     * <p>
+     * This method is not a public API.
+     * @xsl.usage internal
+     */
+    public final char getHighChar() {
+        return m_highCharInContiguousGroup;
+    }
+
+}
diff --git a/src/main/java/org/apache/xml/serializer/Encodings.java b/src/main/java/org/apache/xml/serializer/Encodings.java
new file mode 100644
index 0000000..0831ef5
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/Encodings.java
@@ -0,0 +1,497 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Encodings.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+
+/**
+ * Provides information about encodings. Depends on the Java runtime
+ * to provides writers for the different encodings.
+ * <p>
+ * This class is not a public API. It is only public because it
+ * is used outside of this package.
+ * 
+ * @xsl.usage internal
+ */
+
+public final class Encodings extends Object
+{
+    /**
+     * Standard filename for properties file with encodings data.
+     */
+    private static final String ENCODINGS_FILE = SerializerBase.PKG_PATH+"/Encodings.properties";
+
+    /**
+     * Returns a writer for the specified encoding based on
+     * an output stream.
+     * <p>
+     * This is not a public API.
+     * @param output The output stream
+     * @param encoding The encoding MIME name, not a Java name for the encoding.
+     * @return A suitable writer
+     * @throws UnsupportedEncodingException There is no convertor
+     *  to support this encoding
+     * @xsl.usage internal
+     */
+    static Writer getWriter(OutputStream output, String encoding)
+        throws UnsupportedEncodingException
+    {
+
+        for (int i = 0; i < _encodings.length; ++i)
+        {
+            if (_encodings[i].name.equalsIgnoreCase(encoding))
+            {
+                try
+                {
+                    String javaName = _encodings[i].javaName;
+                	OutputStreamWriter osw = new OutputStreamWriter(output,javaName);
+                    return osw; 
+                }
+                catch (java.lang.IllegalArgumentException iae) // java 1.1.8
+                {
+                    // keep trying
+                }
+                catch (UnsupportedEncodingException usee)
+                {
+
+                    // keep trying
+                }
+            }
+        }
+
+        try
+        {
+            return new OutputStreamWriter(output, encoding);
+        }
+        catch (java.lang.IllegalArgumentException iae) // java 1.1.8
+        {
+            throw new UnsupportedEncodingException(encoding);
+        }
+    }
+
+    /**
+     * Returns the EncodingInfo object for the specified
+     * encoding, never null, although the encoding name 
+     * inside the returned EncodingInfo object will be if
+     * we can't find a "real" EncodingInfo for the encoding.
+     * <p>
+     * This is not a public API.
+     *
+     * @param encoding The encoding
+     * @return The object that is used to determine if 
+     * characters are in the given encoding.
+     * @xsl.usage internal
+     */
+    static EncodingInfo getEncodingInfo(String encoding)
+    {
+        EncodingInfo ei;
+
+        String normalizedEncoding = toUpperCaseFast(encoding);
+        ei = (EncodingInfo) _encodingTableKeyJava.get(normalizedEncoding);
+        if (ei == null)
+            ei = (EncodingInfo) _encodingTableKeyMime.get(normalizedEncoding);
+        if (ei == null) {
+            // We shouldn't have to do this, but just in case.
+            ei = new EncodingInfo(null,null, '\u0000');
+        }
+
+        return ei;
+    }
+ 
+    /**
+     * Determines if the encoding specified was recognized by the
+     * serializer or not.
+     *
+     * @param encoding The encoding
+     * @return boolean - true if the encoding was recognized else false
+     */
+    public static boolean isRecognizedEncoding(String encoding)
+    {
+        EncodingInfo ei;
+
+        String normalizedEncoding = encoding.toUpperCase();
+        ei = (EncodingInfo) _encodingTableKeyJava.get(normalizedEncoding);
+        if (ei == null)
+            ei = (EncodingInfo) _encodingTableKeyMime.get(normalizedEncoding);
+        if (ei != null)
+            return true;
+        return false;
+    }
+    
+    /**
+     * A fast and cheap way to uppercase a String that is
+     * only made of printable ASCII characters.
+     * <p>
+     * This is not a public API.
+     * @param s a String of ASCII characters
+     * @return an uppercased version of the input String,
+     * possibly the same String.
+     * @xsl.usage internal
+     */
+    static private String toUpperCaseFast(final String s) {
+
+    	boolean different = false;
+    	final int mx = s.length();
+		char[] chars = new char[mx];
+    	for (int i=0; i < mx; i++) {
+    		char ch = s.charAt(i);
+            // is the character a lower case ASCII one?
+    		if ('a' <= ch && ch <= 'z') {
+                // a cheap and fast way to uppercase that is good enough
+    			ch = (char) (ch + ('A' - 'a'));
+    			different = true; // the uppercased String is different
+    		}
+    		chars[i] = ch;
+    	}
+    	
+    	// A little optimization, don't call String.valueOf() if
+    	// the uppercased string is the same as the input string.
+    	final String upper;
+    	if (different) 
+    		upper = String.valueOf(chars);
+    	else
+    		upper = s;
+    		
+    	return upper;
+    }
+
+    /** The default encoding, ISO style, ISO style.   */
+    static final String DEFAULT_MIME_ENCODING = "UTF-8";
+
+    /**
+     * Get the proper mime encoding.  From the XSLT recommendation: "The encoding
+     * attribute specifies the preferred encoding to use for outputting the result
+     * tree. XSLT processors are required to respect values of UTF-8 and UTF-16.
+     * For other values, if the XSLT processor does not support the specified
+     * encoding it may signal an error; if it does not signal an error it should
+     * use UTF-8 or UTF-16 instead. The XSLT processor must not use an encoding
+     * whose name does not match the EncName production of the XML Recommendation
+     * [XML]. If no encoding attribute is specified, then the XSLT processor should
+     * use either UTF-8 or UTF-16."
+     * <p>
+     * This is not a public API.
+     *
+     * @param encoding Reference to java-style encoding string, which may be null,
+     * in which case a default will be found.
+     *
+     * @return The ISO-style encoding string, or null if failure.
+     * @xsl.usage internal
+     */
+    static String getMimeEncoding(String encoding)
+    {
+
+        if (null == encoding)
+        {
+            try
+            {
+
+                // Get the default system character encoding.  This may be
+                // incorrect if they passed in a writer, but right now there
+                // seems to be no way to get the encoding from a writer.
+                encoding = System.getProperty("file.encoding", "UTF8");
+
+                if (null != encoding)
+                {
+
+                    /*
+                    * See if the mime type is equal to UTF8.  If you don't
+                    * do that, then  convertJava2MimeEncoding will convert
+                    * 8859_1 to "ISO-8859-1", which is not what we want,
+                    * I think, and I don't think I want to alter the tables
+                    * to convert everything to UTF-8.
+                    */
+                    String jencoding =
+                        (encoding.equalsIgnoreCase("Cp1252")
+                            || encoding.equalsIgnoreCase("ISO8859_1")
+                            || encoding.equalsIgnoreCase("8859_1")
+                            || encoding.equalsIgnoreCase("UTF8"))
+                            ? DEFAULT_MIME_ENCODING
+                            : convertJava2MimeEncoding(encoding);
+
+                    encoding =
+                        (null != jencoding) ? jencoding : DEFAULT_MIME_ENCODING;
+                }
+                else
+                {
+                    encoding = DEFAULT_MIME_ENCODING;
+                }
+            }
+            catch (SecurityException se)
+            {
+                encoding = DEFAULT_MIME_ENCODING;
+            }
+        }
+        else
+        {
+            encoding = convertJava2MimeEncoding(encoding);
+        }
+
+        return encoding;
+    }
+
+    /**
+     * Try the best we can to convert a Java encoding to a XML-style encoding.
+     * <p>
+     * This is not a public API.
+     * @param encoding non-null reference to encoding string, java style.
+     *
+     * @return ISO-style encoding string.
+     * @xsl.usage internal
+     */
+    private static String convertJava2MimeEncoding(String encoding)
+    {
+        EncodingInfo enc =
+            (EncodingInfo) _encodingTableKeyJava.get(toUpperCaseFast(encoding));
+        if (null != enc)
+            return enc.name;
+        return encoding;
+    }
+
+    /**
+     * Try the best we can to convert a Java encoding to a XML-style encoding.
+     * <p>
+     * This is not a public API.
+     *
+     * @param encoding non-null reference to encoding string, java style.
+     *
+     * @return ISO-style encoding string.
+     * <p>
+     * This method is not a public API.
+     * @xsl.usage internal
+     */
+    public static String convertMime2JavaEncoding(String encoding)
+    {
+
+        for (int i = 0; i < _encodings.length; ++i)
+        {
+            if (_encodings[i].name.equalsIgnoreCase(encoding))
+            {
+                return _encodings[i].javaName;
+            }
+        }
+
+        return encoding;
+    }
+
+    /**
+     * Load a list of all the supported encodings.
+     *
+     * System property "encodings" formatted using URL syntax may define an
+     * external encodings list. Thanks to Sergey Ushakov for the code
+     * contribution!
+     * @xsl.usage internal
+     */
+    private static EncodingInfo[] loadEncodingInfo()
+    {
+        try
+        {
+            final InputStream is; 
+                
+            SecuritySupport ss = SecuritySupport.getInstance();
+            is = ss.getResourceAsStream(ObjectFactory.findClassLoader(),
+                                            ENCODINGS_FILE);
+
+            Properties props = new Properties();
+            if (is != null) {
+                props.load(is);
+                is.close();
+            } else {
+                // Seems to be no real need to force failure here, let the
+                // system do its best... The issue is not really very critical,
+                // and the output will be in any case _correct_ though maybe not
+                // always human-friendly... :)
+                // But maybe report/log the resource problem?
+                // Any standard ways to report/log errors (in static context)?
+            }
+
+            int totalEntries = props.size();
+
+            List encodingInfo_list = new ArrayList();
+            Enumeration keys = props.keys();
+            for (int i = 0; i < totalEntries; ++i)
+            {
+                String javaName = (String) keys.nextElement();
+                String val = props.getProperty(javaName);
+                int len = lengthOfMimeNames(val);
+
+                String mimeName;
+                char highChar;
+                if (len == 0)
+                {
+                    // There is no property value, only the javaName, so try and recover
+                    mimeName = javaName;
+                    highChar = '\u0000'; // don't know the high code point, will need to test every character
+                }
+                else
+                {
+                    try {
+                        // Get the substring after the Mime names
+                        final String highVal = val.substring(len).trim();
+                        highChar = (char) Integer.decode(highVal).intValue();
+                    }
+                    catch( NumberFormatException e) {
+                        highChar = 0;
+                    }
+                    String mimeNames = val.substring(0, len);
+                    StringTokenizer st =
+                        new StringTokenizer(mimeNames, ",");
+                    for (boolean first = true;
+                        st.hasMoreTokens();
+                        first = false)
+                    {
+                        mimeName = st.nextToken();
+                        EncodingInfo ei = new EncodingInfo(mimeName, javaName, highChar);
+                        encodingInfo_list.add(ei);
+                        _encodingTableKeyMime.put(mimeName.toUpperCase(), ei);
+                        if (first)
+                            _encodingTableKeyJava.put(javaName.toUpperCase(), ei);
+                    }
+                }
+            }
+            // Convert the Vector of EncodingInfo objects into an array of them,
+            // as that is the kind of thing this method returns.
+            EncodingInfo[] ret_ei = new EncodingInfo[encodingInfo_list.size()];
+            encodingInfo_list.toArray(ret_ei);
+            return ret_ei;
+        }
+        catch (java.net.MalformedURLException mue)
+        {
+            throw new org.apache.xml.serializer.utils.WrappedRuntimeException(mue);
+        }
+        catch (java.io.IOException ioe)
+        {
+            throw new org.apache.xml.serializer.utils.WrappedRuntimeException(ioe);
+        }
+    }
+    
+    /**
+     * Get the length of the Mime names within the property value
+     * @param val The value of the property, which should contain a comma
+     * separated list of Mime names, followed optionally by a space and the
+     * high char value
+     * @return
+     */
+    private static int lengthOfMimeNames(String val) {
+        // look for the space preceding the optional high char
+        int len = val.indexOf(' ');
+        // If len is zero it means the optional part is not there, so
+        // the value must be all Mime names, so set the length appropriately
+        if (len < 0)  
+            len = val.length();
+        
+        return len;
+    }
+
+    /**
+     * Return true if the character is the high member of a surrogate pair.
+     * <p>
+     * This is not a public API.
+     * @param ch the character to test
+     * @xsl.usage internal
+     */
+    static boolean isHighUTF16Surrogate(char ch) {
+        return ('\uD800' <= ch && ch <= '\uDBFF');
+    }
+    /**
+     * Return true if the character is the low member of a surrogate pair.
+     * <p>
+     * This is not a public API.
+     * @param ch the character to test
+     * @xsl.usage internal
+     */
+    static boolean isLowUTF16Surrogate(char ch) {
+        return ('\uDC00' <= ch && ch <= '\uDFFF');
+    }
+    /**
+     * Return the unicode code point represented by the high/low surrogate pair.
+     * <p>
+     * This is not a public API.
+     * @param highSurrogate the high char of the high/low pair
+     * @param lowSurrogate the low char of the high/low pair
+     * @xsl.usage internal
+     */
+    static int toCodePoint(char highSurrogate, char lowSurrogate) {
+        int codePoint =
+            ((highSurrogate - 0xd800) << 10)
+                + (lowSurrogate - 0xdc00)
+                + 0x10000;
+        return codePoint;
+    }
+    /**
+     * Return the unicode code point represented by the char.
+     * A bit of a dummy method, since all it does is return the char,
+     * but as an int value.
+     * <p>
+     * This is not a public API.
+     * @param ch the char.
+     * @xsl.usage internal
+     */
+    static int toCodePoint(char ch) {
+        int codePoint = ch;
+        return codePoint;
+    }
+    
+    /**
+     * Characters with values at or below the high code point are
+     * in the encoding. Code point values above this one may or may
+     * not be in the encoding, but lower ones certainly are.
+     * <p>
+     * This is for performance.
+     *
+     * @param encoding The encoding
+     * @return The code point for which characters at or below this code point
+     * are in the encoding. Characters with higher code point may or may not be
+     * in the encoding. A value of zero is returned if the high code point is unknown.
+     * <p>
+     * This method is not a public API.
+     * @xsl.usage internal
+     */
+    static public char getHighChar(String encoding)
+    {
+        final char highCodePoint;
+        EncodingInfo ei;
+
+        String normalizedEncoding = toUpperCaseFast(encoding);
+        ei = (EncodingInfo) _encodingTableKeyJava.get(normalizedEncoding);
+        if (ei == null)
+            ei = (EncodingInfo) _encodingTableKeyMime.get(normalizedEncoding);
+        if (ei != null)
+            highCodePoint =  ei.getHighChar();
+        else
+            highCodePoint = 0;
+        return highCodePoint;
+    }
+
+    private static final Hashtable _encodingTableKeyJava = new Hashtable();
+    private static final Hashtable _encodingTableKeyMime = new Hashtable();
+    private static final EncodingInfo[] _encodings = loadEncodingInfo();
+}
diff --git a/src/main/java/org/apache/xml/serializer/Encodings.properties b/src/main/java/org/apache/xml/serializer/Encodings.properties
new file mode 100644
index 0000000..71ebde8
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/Encodings.properties
@@ -0,0 +1,260 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the  "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+#
+# $Id: Encodings.properties 468654 2006-10-28 07:09:23Z minchau $
+#
+# Each entry in this properties file is:
+# 1) The Java name for the encoding
+# 2) A comma separated list of the MIME names for the encoding,
+#    with the first one being the preferred MIME name.
+# 3) An optional high char. Characters at or below this value are
+#    definately in the encoding, but characters above it may or may not be.
+#    This value is given only for performance reasons.
+#    A value of zero is the same as no value at all.
+#
+# For example this line in this file:
+#              ASCII ASCII,US-ASCII 0x007F
+# Means the Java name for the encoding is "ASCII". The MIME names for this
+# encoding which may appear in a stylesheet are "ASCII" or "US-ASCII"
+# and the optional high code point value is given, and it is 0X007F
+# which means that the contiguous block of chars from
+# 0x0001 to 0x007F ( 127 in base 10) are all in the encoding.
+# Higher values above this char might be in the encoding, although in the 
+# case of this particular encoding there are no higher chars.
+#
+#
+# <JAVA name encoding>, <PREFERRED name MIME>
+#
+#
+ASCII      ASCII,US-ASCII                         0x007F
+#
+# Big5, Traditional Chinese 
+Big5       BIG5,csBig5                            0x007F
+#Big5 with Hong Kong extensions, Traditional Chinese (incorporating 2001 revision) 
+Big5_HKSCS BIG5-HKSCS                             0x007F
+# USA, Canada (Bilingual, French), Netherlands, Portugal, Brazil, Australia
+Cp037      EBCDIC-CP-US,EBCDIC-CP-CA,EBCDIC-CP-WT,EBCDIC-CP-NL,IBM037 0x0019
+# IBM Austria, Germany 
+Cp273      IBM273,csIBM273                        0x0019
+Cp274      csIBM274,EBCDIC-BE
+Cp275      csIBM275,EBCDIC-BR
+# IBM Denmark, Norway 
+Cp277      EBCDIC-CP-DK,EBCDIC-CP-NO,IBM277,csIBM277    0x0019
+# IBM Finland, Sweden 
+Cp278      EBCDIC-CP-FI,EBCDIC-CP-SE,IBM278,csIBM278    0x0019
+# IBM Italy
+Cp280      EBCDIC-CP-IT,IBM280,csIBM280           0x0019
+Cp281      EBCDIC-JP-E,csIBM281
+# IBM Catalan/Spain, Spanish Latin America 
+Cp284      EBCDIC-CP-ES,IBM284,csIBM284           0x0019
+# IBM United Kingdom, Ireland
+Cp285      EBCDIC-CP-GB,IBM284,csIBM285           0x0019
+Cp290      EBCDIC-JP-kana,IBM290,csIBM290         0x0019
+# IBM France
+Cp297      EBCDIC-CP-FR,IBM297,csIBM297           0x0019
+# IBM Arabic
+Cp420      EBCDIC-CP-AR1,IBM420,csIBM420          0x0019
+Cp423      EBCDIC-CP-GR,IBM423,csIBM423
+# IBM Hebrew
+Cp424      EBCDIC-CP-HE,IBM424,csIBM424           0x0019
+Cp437      437,IBM437,csPC8CodePage437            0x007F
+# EBCDIC 500V1
+Cp500      EBCDIC-CP-CH,EBCDIC-CP-BE,IBM500,csIBM500    0x0019
+# PC Baltic
+Cp775      IBM775,csPC775Baltic                   0x007F
+# IBM Thailand extended SBCS 
+Cp838      IBM-Thai,838,csIBMThai                 0x0019
+# MS-DOS Latin-1
+Cp850      850,csPC850Multilingual,IBM850         0x007F
+Cp851      851,IBM851,csIBM851
+# MS-DOS Latin-2
+Cp852      IBM852,852,csPCp852                    0x007F
+# IBM Cyrillic
+Cp855      IBM855,855,csIBM855                    0x007F
+# IBM Turkish
+Cp857      IBM857,857,csIBM857                    0x007F
+# Variant of Cp850 with Euro character 
+Cp858      IBM00858                               0x007F
+# MS-DOS Portuguese
+Cp860      860,csIBM860,IBM860                    0x007F
+# MS-DOS Icelandic
+Cp861      IBM861,861,csIBM861,cp-is              0x007F
+#
+Cp862      IBM862,862,csPCi62LatinHebrew          0x007F
+# MS-DOS Canadian French
+Cp863      IBM863,863,csIBM863                    0x007F
+# PC Arabic 
+Cp864      IBM864,864,csIBM864                    0x007F
+# MS-DOS Nordic 
+Cp865      IBM865,865,csIBM865                    0x007F
+# MS-DOS Russian 
+Cp866      IBM866,866,csIBM866                    0x007F
+# MS-DOS Pakistan 
+Cp868      IBM868,cp-ar,csIBM868                  0x007F
+# IBM Modern Greek 
+Cp869      IBM869,869,cp-gr,csIBM869              0x007F
+# IBM Multilingual Latin-2 
+Cp870      EBCDIC-CP-ROECE,EBCDIC-CP-YU,IBM870,csIBM870 0x0019
+# IBM Iceland 
+Cp871      EBCDIC-CP-IS,IBM871,csIBM871           0x0019
+Cp880      EBCDIC-Cyrillic,IBM880,csIBM880
+Cp891      IBM891,csIBM891
+Cp903      IBM903,csIBM903
+Cp904      IBM904,csIBM904
+Cp905      IBM905,csIBM905,EBCDIC-CP-TR
+# IBM Pakistan (Urdu)
+Cp918      EBCDIC-CP-AR2,IBM918,csIBM918          0x0019
+# GBK, Simplified Chinese 
+Cp936      GBK,MS936,WINDOWS-936
+# IBM Latin-5, Turkey 
+Cp1026     IBM1026,csIBM1026                      0x0019
+# Latin-1 character set for EBCDIC hosts 
+Cp1047     IBM1047,IBM-1047                       0x0019
+# Variant of Cp037 with Euro character 
+Cp1140     IBM01140                               0x0019
+# Variant of Cp273 with Euro character 
+Cp1141     IBM01141                               0x0019
+# Variant of Cp277 with Euro character 
+Cp1142     IBM01142                               0x0019
+# Variant of Cp278 with Euro character 
+Cp1143     IBM01143                               0x0019
+# Variant of Cp280 with Euro character 
+Cp1144     IBM01144                               0x0019
+# Variant of Cp284 with Euro character 
+Cp1145     IBM01145                               0x0019
+# Variant of Cp285 with Euro character 
+Cp1146     IBM01146                               0x0019
+# Variant of Cp297 with Euro character 
+Cp1147     IBM01147                               0x0019
+# Variant of Cp500 with Euro character 
+Cp1148     IBM01148                               0x0019
+# Variant of Cp871 with Euro character 
+Cp1149     IBM01149                               0x0019
+Cp1250     WINDOWS-1250                           0x007F
+Cp1251     WINDOWS-1251                           0x007F
+Cp1252     WINDOWS-1252                           0x007F
+Cp1253     WINDOWS-1253                           0x007F
+Cp1254     WINDOWS-1254                           0x007F
+# Windows Hebrew 
+Cp1255     WINDOWS-1255                           0x007F
+# Windows Arabic
+Cp1256     WINDOWS-1256                           0x007F
+Cp1257     WINDOWS-1257                           0x007F
+# Windows Vietnamese
+Cp1258     WINDOWS-1258                           0x007F
+EUC-CN     EUC-CN                                 0x007F
+EUC_CN     EUC-CN                                 0x007F
+#
+#JISX 0201, 0208 and 0212, EUC encoding Japanese
+EUC-JP     EUC-JP                                 0x007F
+EUC_JP     EUC-JP                                 0x007F
+# KS C 5601, EUC encoding, Korean 
+EUC-KR     EUC-KR                                 0x007F
+EUC_KR     EUC-KR                                 0x007F
+# CNS11643 (Plane 1-7,15), EUC encoding, Traditional Chinese
+EUC-TW     EUC-TW                                 0x007F
+EUC_TW     EUC-TW,x-EUC-TW                        0x007F
+EUCJIS     EUC-JP                                 0x007F
+#
+# GB2312, EUC encoding, Simplified Chinese 
+GB2312     GB2312                                 0x007F
+
+# GB2312 and CNS11643 in ISO 2022 CN form, Simplified and Traditional Chinese (conversion to Unicode only) 
+ISO2022CN  ISO-2022-CN
+# JIS X 0201, 0208, in ISO 2022 form, Japanese 
+ISO2022JP  ISO-2022-JP
+# ISO 2022 KR, Korean 
+ISO2022KR  ISO-2022-KR                            0x007F
+#
+#
+ISO8859-1  ISO-8859-1                             0x00FF
+ISO8859_1  ISO-8859-1                             0x00FF
+8859-1     ISO-8859-1                             0x00FF
+8859_1     ISO-8859-1                             0x00FF
+#
+ISO8859-2  ISO-8859-2                             0x00A0
+ISO8859_2  ISO-8859-2                             0x00A0
+8859-2     ISO-8859-2                             0x00A0
+8859_2     ISO-8859-2                             0x00A0
+#
+# Latin Alphabet No. 3 
+ISO8859-3  ISO-8859-3                             0x00A0
+ISO8859_3  ISO-8859-3                             0x00A0
+8859-3     ISO-8859-3                             0x00A0
+8859_3     ISO-8859-3                             0x00A0
+#
+ISO8859-4  ISO-8859-4                             0x00A0
+ISO8859_4  ISO-8859-4                             0x00A0
+8859-4     ISO-8859-4                             0x00A0
+8859_4     ISO-8859-4                             0x00A0
+#
+ISO8859-5  ISO-8859-5                             0x00A0
+ISO8859_5  ISO-8859-5                             0x00A0
+8859-5     ISO-8859-5                             0x00A0
+8859_5     ISO-8859-5                             0x00A0
+#
+# Latin/Arabic Alphabet 
+ISO8859-6  ISO-8859-6                             0x00A0
+ISO8859_6  ISO-8859-6                             0x00A0
+8859-6     ISO-8859-6                             0x00A0
+8859_6     ISO-8859-6                             0x00A0
+#
+ISO8859-7  ISO-8859-7                             0x00A0
+ISO8859_7  ISO-8859-7                             0x00A0
+8859-7     ISO-8859-7                             0x00A0
+8859_7     ISO-8859-7                             0x00A0
+#
+ISO8859-8  ISO-8859-8                             0x00A0
+ISO8859_8  ISO-8859-8                             0x00A0
+8859-8     ISO-8859-8                             0x00A0
+8859_8     ISO-8859-8                             0x00A0
+#
+ISO8859-9  ISO-8859-9                             0x00CF
+ISO8859_9  ISO-8859-9                             0x00CF
+8859-9     ISO-8859-9                             0x00CF
+8859_9     ISO-8859-9                             0x00CF
+#
+ISO8859-10 ISO-8859-10                            0x007E
+ISO8859_10 ISO-8859-10                            0x007E
+ISO8859-11 ISO-8859-11                            0x007E
+ISO8859_11 ISO-8859-11                            0x007E
+ISO8859-12 ISO-8859-12                            0x007F
+ISO8859_12 ISO-8859-12                            0x007F
+ISO8859-13 ISO-8859-13                            0x00A0
+ISO8859_13 ISO-8859-13                            0x00A0
+ISO8859-14 ISO-8859-14                            0x007E
+ISO8859_14 ISO-8859-14                            0x007E
+ISO8859-15 ISO-8859-15                            0x00A3
+ISO8859_15 ISO-8859-15                            0x00A3
+JIS        ISO-2022-JP                            0x007F
+KOI8_R     KOI8-R                                 0x007F
+KSC5601    EUC-KR                                 0x007F
+KS_C_5601-1987 KS_C_5601-1987,iso-ir-149,KS_C_5601-1989,KSC_5601,csKSC56011987  0x007F
+MacTEC     MacRoman
+# Windows Japanese
+MS932      windows-31j
+# Shift-JIS, Japanese 
+SJIS       SHIFT_JIS                              0x007F
+# TIS620, Thai
+TIS620     TIS-620
+UTF8       UTF-8                                  0xFFFF
+Unicode    UNICODE,UTF-16                         0xFFFF
+
+# note that more character set names and their aliases
+# can be found at http://www.iana.org/assignments/character-sets
+
diff --git a/src/main/java/org/apache/xml/serializer/ExtendedContentHandler.java b/src/main/java/org/apache/xml/serializer/ExtendedContentHandler.java
new file mode 100644
index 0000000..a811228
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ExtendedContentHandler.java
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExtendedContentHandler.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import javax.xml.transform.SourceLocator;
+
+import org.xml.sax.SAXException;
+
+/**
+ * This interface describes extensions to the SAX ContentHandler interface.
+ * It is intended to be used by a serializer. The methods on this interface will
+ * implement SAX- like behavior. This allows the gradual collection of
+ * information rather than having it all up front. For example the call
+ * <pre>
+ * startElement(namespaceURI,localName,qName,atts)
+ * </pre>
+ * could be replaced with the calls
+ * <pre>
+ * startElement(namespaceURI,localName,qName)
+ * addAttributes(atts)
+ * </pre>
+ * If there are no attributes the second call can be dropped. If attributes are
+ * to be added one at a time with calls to
+ * <pre>
+ * addAttribute(namespaceURI, localName, qName, type, value)
+ * </pre>
+ * @xsl.usage internal
+ */
+public interface ExtendedContentHandler extends org.xml.sax.ContentHandler
+{
+    /**
+     * Add at attribute to the current element
+     * @param uri the namespace URI of the attribute name
+     * @param localName the local name of the attribute (without prefix)
+     * @param rawName the qualified name of the attribute
+     * @param type the attribute type typically character data (CDATA)
+     * @param value the value of the attribute
+     * @param XSLAttribute true if the added attribute is coming from an xsl:attribute element
+     * @throws SAXException
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean XSLAttribute)
+        throws SAXException;
+    /**
+     * Add attributes to the current element
+     * @param atts the attributes to add.
+     * @throws SAXException
+     */
+    public void addAttributes(org.xml.sax.Attributes atts)
+        throws org.xml.sax.SAXException;
+    /**
+     * Add an attribute to the current element. The namespace URI of the
+     * attribute will be calculated from the prefix of qName. The local name
+     * will be derived from qName and the type will be assumed to be "CDATA".
+     * @param qName
+     * @param value
+     */
+    public void addAttribute(String qName, String value);
+    
+    /**
+     * This method is used to notify of a character event, but passing the data
+     * as a character String rather than the standard character array.
+     * @param chars the character data
+     * @throws SAXException
+     */
+    public void characters(String chars) throws SAXException;
+    
+    /**
+     * This method is used to notify of a character event, but passing the data
+     * as a DOM Node rather than the standard character array.
+     * @param node a DOM Node containing text.
+     * @throws SAXException
+     */    
+    public void characters(org.w3c.dom.Node node) throws org.xml.sax.SAXException;
+    /**
+     * This method is used to notify that an element has ended. Unlike the
+     * standard SAX method
+     * <pre>
+     * endElement(namespaceURI,localName,qName)
+     * </pre>
+     * only the last parameter is passed. If needed the serializer can derive
+     * the localName from the qualified name and derive the namespaceURI from
+     * its implementation.
+     * @param elemName the fully qualified element name.
+     * @throws SAXException
+     */
+    public void endElement(String elemName) throws SAXException;
+
+    /**
+     * This method is used to notify that an element is starting.
+     * This method is just like the standard SAX method
+     * <pre>
+     * startElement(uri,localName,qname,atts)
+     * </pre>
+     * but without the attributes.
+     * @param uri the namespace URI of the element
+     * @param localName the local name (without prefix) of the element
+     * @param qName the qualified name of the element
+     * 
+     * @throws SAXException
+     */
+    public void startElement(String uri, String localName, String qName)
+        throws org.xml.sax.SAXException;
+
+    /**
+     * This method is used to notify of the start of an element
+     * @param qName the fully qualified name of the element
+     * @throws SAXException
+     */
+    public void startElement(String qName) throws SAXException;
+    /**
+     * This method is used to notify that a prefix mapping is to start, but
+     * after an element is started. The SAX method call
+     * <pre>
+     * startPrefixMapping(prefix,uri)
+     * </pre>
+     * is used just before an element starts and applies to the element to come,
+     * not to the current element.  This method applies to the current element.
+     * For example one could make the calls in this order:
+     * <pre>
+     * startElement("prfx8:elem9")
+     * namespaceAfterStartElement("http://namespace8","prfx8")
+     * </pre>
+     * 
+     * @param uri the namespace URI being declared
+     * @param prefix the prefix that maps to the given namespace
+     * @throws SAXException
+     */
+    public void namespaceAfterStartElement(String uri, String prefix)
+        throws SAXException;
+
+    /**
+     * This method is used to notify that a prefix maping is to start, which can
+     * be for the current element, or for the one to come.
+     * @param prefix the prefix that maps to the given URI
+     * @param uri the namespace URI of the given prefix
+     * @param shouldFlush if true this call is like the SAX
+     * startPrefixMapping(prefix,uri) call and the mapping applies to the
+     * element to come.  If false the mapping applies to the current element.
+     * @return boolean false if the prefix mapping was already in effect (in
+     * other words we are just re-declaring), true if this is a new, never
+     * before seen mapping for the element.
+     * @throws SAXException
+     */
+    public boolean startPrefixMapping(
+        String prefix,
+        String uri,
+        boolean shouldFlush)
+        throws SAXException;
+    /**
+     * Notify of an entity reference.
+     * @param entityName the name of the entity
+     * @throws SAXException
+     */
+    public void entityReference(String entityName) throws SAXException;
+
+    /**
+     * This method returns an object that has the current namespace mappings in
+     * effect.
+     * 
+     * @return NamespaceMappings an object that has the current namespace
+     * mappings in effect.
+     */
+    public NamespaceMappings getNamespaceMappings();
+    /**
+     * This method returns the prefix that currently maps to the given namespace
+     * URI.
+     * @param uri the namespace URI
+     * @return String the prefix that currently maps to the given URI.
+     */
+    public String getPrefix(String uri);
+    /**
+     * This method gets the prefix associated with a current element or
+     * attribute name.
+     * @param name the qualified name of an element, or attribute
+     * @param isElement true if it is an element name, false if it is an
+     * atttribute name
+     * @return String the namespace URI associated with the element or
+     * attribute.
+     */
+    public String getNamespaceURI(String name, boolean isElement);
+    /**
+     * This method returns the namespace URI currently associated with the
+     * prefix.
+     * @param prefix a prefix of an element or attribute.
+     * @return String the namespace URI currently associated with the prefix.
+     */
+    public String getNamespaceURIFromPrefix(String prefix);
+
+    /**
+     * This method is used to set the source locator, which might be used to
+     * generated an error message.
+     * @param locator the source locator
+     */
+    public void setSourceLocator(SourceLocator locator);
+
+    // Bit constants for addUniqueAttribute().
+    
+    // The attribute value contains no bad characters. A "bad" character is one which
+    // is greater than 126 or it is one of '<', '>', '&' or '"'.
+    public static final int NO_BAD_CHARS = 0x1;
+    
+    // An HTML empty attribute (e.g. <OPTION selected>).
+    public static final int HTML_ATTREMPTY = 0x2;
+    
+    // An HTML URL attribute
+    public static final int HTML_ATTRURL = 0x4;
+
+    /**
+     * Add a unique attribute to the current element.
+     * The attribute is guaranteed to be unique here. The serializer can write
+     * it out immediately without saving it in a table first. The integer
+     * flag contains information about the attribute, which helps the serializer
+     * to decide whether a particular processing is needed.
+     *
+     * @param qName the fully qualified attribute name.
+     * @param value the attribute value
+     * @param flags a bitwise flag
+     */
+    public void addUniqueAttribute(String qName, String value, int flags)
+        throws SAXException;
+    
+    /**
+     * Add an attribute from an xsl:attribute element.
+     * @param qName the qualified attribute name (prefix:localName)
+     * @param value the attributes value
+     * @param uri the uri that the prefix of the qName is mapped to.
+     */    
+    public void addXSLAttribute(String qName, final String value, final String uri);
+    
+    /**
+     * Add at attribute to the current element, not from an xsl:attribute
+     * element.
+     * @param uri the namespace URI of the attribute name
+     * @param localName the local name of the attribute (without prefix)
+     * @param rawName the qualified name of the attribute
+     * @param type the attribute type typically character data (CDATA)
+     * @param value the value of the attribute
+     * @throws SAXException
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value)
+        throws SAXException;
+}
diff --git a/src/main/java/org/apache/xml/serializer/ExtendedLexicalHandler.java b/src/main/java/org/apache/xml/serializer/ExtendedLexicalHandler.java
new file mode 100644
index 0000000..a825604
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ExtendedLexicalHandler.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExtendedLexicalHandler.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import org.xml.sax.SAXException;
+
+/**
+ * This interface has extensions to the standard SAX LexicalHandler interface.
+ * This interface is intended to be used by a serializer.
+ * @xsl.usage internal
+ */
+public interface ExtendedLexicalHandler extends org.xml.sax.ext.LexicalHandler
+{
+    /**
+     * This method is used to notify of a comment
+     * @param comment the comment, but unlike the SAX comment() method this
+     * method takes a String rather than a character array.
+     * @throws SAXException
+     */
+    public void comment(String comment) throws SAXException;
+}
diff --git a/src/main/java/org/apache/xml/serializer/HTMLEntities.properties b/src/main/java/org/apache/xml/serializer/HTMLEntities.properties
new file mode 100644
index 0000000..2de4df6
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/HTMLEntities.properties
@@ -0,0 +1,311 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the  "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+#
+# $Id: HTMLEntities.properties 468654 2006-10-28 07:09:23Z minchau $
+#
+# @version $Revision: 468654 $ $Date: 2006-10-28 00:09:23 -0700 (Sat, 28 Oct 2006) $
+# This file must be encoded in UTF-8; see CharInfo.java
+#
+# Character entity references for markup-significant
+#
+quot=34
+amp=38
+lt=60
+gt=62
+nbsp=160
+#
+# Character entity references for ISO 8859-1 characters
+#
+iexcl=161
+cent=162
+pound=163
+curren=164
+yen=165
+brvbar=166
+sect=167
+uml=168
+copy=169
+ordf=170
+laquo=171
+not=172
+shy=173
+reg=174
+macr=175
+deg=176
+plusmn=177
+sup2=178
+sup3=179
+acute=180
+micro=181
+para=182
+middot=183
+cedil=184
+sup1=185
+ordm=186
+raquo=187
+frac14=188
+frac12=189
+frac34=190
+iquest=191
+Agrave=192
+Aacute=193
+Acirc=194
+Atilde=195
+Auml=196
+Aring=197
+AElig=198
+Ccedil=199
+Egrave=200
+Eacute=201
+Ecirc=202
+Euml=203
+Igrave=204
+Iacute=205
+Icirc=206
+Iuml=207
+ETH=208
+Ntilde=209
+Ograve=210
+Oacute=211
+Ocirc=212
+Otilde=213
+Ouml=214
+times=215
+Oslash=216
+Ugrave=217
+Uacute=218
+Ucirc=219
+Uuml=220
+Yacute=221
+THORN=222
+szlig=223
+agrave=224
+aacute=225
+acirc=226
+atilde=227
+auml=228
+aring=229
+aelig=230
+ccedil=231
+egrave=232
+eacute=233
+ecirc=234
+euml=235
+igrave=236
+iacute=237
+icirc=238
+iuml=239
+eth=240
+ntilde=241
+ograve=242
+oacute=243
+ocirc=244
+otilde=245
+ouml=246
+divide=247
+oslash=248
+ugrave=249
+uacute=250
+ucirc=251
+uuml=252
+yacute=253
+thorn=254
+yuml=255
+#
+# Character entity references for symbols, mathematical symbols, and Greek letters
+#
+# Latin Extended -- Netscape can't handle
+# fnof 402
+#
+# Greek - Netscape can't handle these
+# Alpha 913
+# Beta 914
+# Gamma 915
+# Delta 916
+# Epsilon 917
+# Zeta 918
+# Eta 919
+# Theta 920
+# Iota 921
+# Kappa 922
+# Lambda 923
+# Mu 924
+# Nu 925
+# Xi 926
+# Omicron 927
+# Pi 928
+# Rho 929
+# Sigma 931
+# Tau 932
+# Upsilon 933
+# Phi 934
+# Chi 935
+# Psi 936
+# Omega 937
+# alpha 945
+# beta 946
+# gamma 947
+# delta 948
+# epsilon 949
+# zeta 950
+# eta 951
+# theta 952
+# iota 953
+# kappa 954
+# lambda 955
+# mu 956
+# nu 957
+# xi 958
+# omicron 959
+# pi 960
+# rho 961
+# sigmaf 962
+# sigma 963
+# tau 964
+# upsilon 965
+# phi 966
+# chi 967
+# psi 968
+# omega 969
+# thetasym 977
+# upsih 978
+# piv 982
+#
+# General Punctuation
+bull=8226
+hellip=8230
+prime=8242
+Prime=8243
+oline=8254
+frasl=8260
+#
+# Letterlike Symbols
+weierp=8472
+image=8465
+real=8476
+trade=8482
+alefsym=8501
+#
+# Arrows
+larr=8592
+uarr=8593
+rarr=8594
+darr=8595
+harr=8596
+crarr=8629
+lArr=8656
+uArr=8657
+rArr=8658
+dArr=8659
+hArr=8660
+#
+# Mathematical Operators
+forall=8704
+part=8706
+exist=8707
+empty=8709
+nabla=8711
+isin=8712
+notin=8713
+ni=8715
+prod=8719
+sum=8721
+minus=8722
+lowast=8727
+radic=8730
+prop=8733
+infin=8734
+ang=8736
+and=8743
+or=8744
+cap=8745
+cup=8746
+int=8747
+there4=8756
+sim=8764
+cong=8773
+asymp=8776
+ne=8800
+equiv=8801
+le=8804
+ge=8805
+sub=8834
+sup=8835
+nsub=8836
+sube=8838
+supe=8839
+oplus=8853
+otimes=8855
+perp=8869
+sdot=8901
+#
+# Miscellaneous Technical
+lceil=8968
+rceil=8969
+lfloor=8970
+rfloor=8971
+lang=9001
+rang=9002
+#
+# Geometric Shapes
+loz=9674
+#
+# Miscellaneous Symbols
+spades=9824
+clubs=9827
+hearts=9829
+diams=9830
+#
+# Character entity references for internationalization characters
+#
+# Latin Extended-A
+# Netscape can't handle!
+# OElig 338
+# oelig 339
+
+#-- NN 4.7 does not seem to support these, so they might ought to be commented.
+# Scaron 352
+# scaron 353
+# Yuml 376
+#
+# Spacing Modifier Letters -- Netscape can't handle
+# circ 710
+# tilde 732
+#
+# General Punctuation
+ensp=8194
+emsp=8195
+thinsp=8201
+zwnj=8204
+zwj=8205
+lrm=8206
+rlm=8207
+ndash=8211
+mdash=8212
+lsquo=8216
+rsquo=8217
+sbquo=8218
+ldquo=8220
+rdquo=8221
+bdquo=8222
+dagger=8224
+Dagger=8225
+permil=8240
+lsaquo=8249
+rsaquo=8250
+euro=8364
diff --git a/src/main/java/org/apache/xml/serializer/Method.java b/src/main/java/org/apache/xml/serializer/Method.java
new file mode 100644
index 0000000..0fb66f0
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/Method.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Method.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+/**
+ * This class defines the constants which are the names of the four default
+ * output methods.
+ * <p>
+ * The default output methods defined are:
+ * <ul>
+ * <li>XML
+ * <li>TEXT
+ * <li>HTML
+ * </ul>
+ * These constants can be used as an argument to the
+ * OutputPropertiesFactory.getDefaultMethodProperties() method to get
+ * the properties to create a serializer.
+ * 
+ * This class is a public API.
+ * 
+ * @see OutputPropertiesFactory
+ * @see Serializer
+ * 
+ * @xsl.usage general
+ */
+public final class Method
+{
+    /**
+     * A private constructor to prevent the creation of such a class.
+     */
+    private Method() {
+        
+    }
+
+  /**
+   * The output method type for XML documents: <tt>xml</tt>.
+   */
+  public static final String XML = "xml";
+
+  /**
+   * The output method type for HTML documents: <tt>html</tt>.
+   */
+  public static final String HTML = "html";
+
+  /**
+   * The output method for XHTML documents: <tt>xhtml</tt>.
+   * <p>
+   * This method type is not currently supported.
+   */
+  public static final String XHTML = "xhtml";
+
+  /**
+   * The output method type for text documents: <tt>text</tt>.
+   */
+  public static final String TEXT = "text";
+  
+  /**
+   * The "internal" method, just used when no method is 
+   * specified in the style sheet, and a serializer of this type wraps either an
+   * XML or HTML type (depending on the first tag in the output being html or
+   * not)
+   */  
+  public static final String UNKNOWN = "";
+}
diff --git a/src/main/java/org/apache/xml/serializer/NamespaceMappings.java b/src/main/java/org/apache/xml/serializer/NamespaceMappings.java
new file mode 100644
index 0000000..3c67d80
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/NamespaceMappings.java
@@ -0,0 +1,500 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NamespaceMappings.java 469648 2006-10-31 20:52:27Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * This class keeps track of the currently defined namespaces. Conceptually the
+ * prefix/uri/depth triplets are pushed on a stack pushed on a stack. The depth
+ * indicates the nesting depth of the element for which the mapping was made.
+ * 
+ * <p>For example:
+ * <pre>
+ * <chapter xmlns:p1="def">
+ *   <paragraph xmlns:p2="ghi">
+ *      <sentance xmlns:p3="jkl">
+ *      </sentance>
+ *    </paragraph>
+ *    <paragraph xlmns:p4="mno">
+ *    </paragraph>
+ * </chapter>
+ * </pre>
+ * 
+ * When the <chapter> element is encounted the prefix "p1" associated with uri
+ * "def" is pushed on the stack with depth 1.
+ * When the first <paragraph> is encountered "p2" and "ghi" are pushed with
+ * depth 2.
+ * When the <sentance> is encountered "p3" and "jkl" are pushed with depth 3.
+ * When </sentance> occurs the popNamespaces(3) will pop "p3"/"jkl" off the
+ * stack.  Of course popNamespaces(2) would pop anything with depth 2 or
+ * greater.
+ * 
+ * So prefix/uri pairs are pushed and poped off the stack as elements are
+ * processed.  At any given moment of processing the currently visible prefixes
+ * are on the stack and a prefix can be found given a uri, or a uri can be found
+ * given a prefix.
+ *
+ * This class is intended for internal use only.  However, it is made public because
+ * other packages require it. 
+ * @xsl.usage internal
+ */
+public class NamespaceMappings
+{
+    /**
+     * This member is continually incremented when new prefixes need to be
+     * generated. ("ns0"  "ns1" ...)
+     */
+    private int count = 0;
+
+    /**
+     * Each entry (prefix) in this hashtable points to a Stack of URIs
+     * This table maps a prefix (String) to a Stack of NamespaceNodes.
+     * All Namespace nodes in that retrieved stack have the same prefix,
+     * though possibly different URI's or depths. Such a stack must have
+     * mappings at deeper depths push later on such a stack.  Mappings pushed
+     * earlier on the stack will have smaller values for MappingRecord.m_declarationDepth.
+     */
+    private Hashtable m_namespaces = new Hashtable();
+
+    /** 
+     * This stack is used as a convenience.
+     * It contains the pushed NamespaceNodes (shallowest
+     * to deepest) and is used to delete NamespaceNodes 
+     * when leaving the current element depth 
+     * to returning to the parent. The mappings of the deepest
+     * depth can be popped of the top and the same node
+     * can be removed from the appropriate prefix stack.
+     * 
+     * All prefixes pushed at the current depth can be 
+     * removed at the same time by using this stack to
+     * ensure prefix/uri map scopes are closed correctly.
+     */
+    private Stack m_nodeStack = new Stack();
+
+    private static final String EMPTYSTRING = "";
+    private static final String XML_PREFIX = "xml"; // was "xmlns"
+
+    /**
+     * Default constructor
+     * @see java.lang.Object#Object()
+     */
+    public NamespaceMappings()
+    {
+        initNamespaces();
+    }
+
+    /**
+     * This method initializes the namespace object with appropriate stacks
+     * and predefines a few prefix/uri pairs which always exist.
+     */
+    private void initNamespaces()
+    {
+        // The initial prefix mappings will never be deleted because they are at element depth -1 
+        // (a kludge)
+        
+        // Define the default namespace (initially maps to "" uri)
+        Stack stack;
+        MappingRecord nn;
+        nn = new MappingRecord(EMPTYSTRING, EMPTYSTRING, -1);
+        stack = createPrefixStack(EMPTYSTRING);
+        stack.push(nn);
+
+        // define "xml" namespace
+        nn = new MappingRecord(XML_PREFIX, "http://www.w3.org/XML/1998/namespace", -1);
+        stack = createPrefixStack(XML_PREFIX);
+        stack.push(nn);
+    }
+
+    /**
+     * Use a namespace prefix to lookup a namespace URI.
+     * 
+     * @param prefix String the prefix of the namespace
+     * @return the URI corresponding to the prefix, returns ""
+     * if there is no visible mapping.
+     */
+    public String lookupNamespace(String prefix)
+    {
+        String uri = null;
+        final Stack stack = getPrefixStack(prefix);
+        if (stack != null && !stack.isEmpty()) {
+            uri = ((MappingRecord) stack.peek()).m_uri;
+        }
+        if (uri == null)
+            uri = EMPTYSTRING;
+        return uri;
+    }
+  
+    
+    MappingRecord getMappingFromPrefix(String prefix) {
+        final Stack stack = (Stack) m_namespaces.get(prefix);
+        return stack != null && !stack.isEmpty() ? 
+            ((MappingRecord) stack.peek()) : null;
+    }
+
+    /**
+     * Given a namespace uri, and the namespaces mappings for the 
+     * current element, return the current prefix for that uri.
+     * 
+     * @param uri the namespace URI to be search for
+     * @return an existing prefix that maps to the given URI, null if no prefix
+     * maps to the given namespace URI.
+     */
+    public String lookupPrefix(String uri)
+    {
+        String foundPrefix = null;
+        Enumeration prefixes = m_namespaces.keys();
+        while (prefixes.hasMoreElements())
+        {
+            String prefix = (String) prefixes.nextElement();
+            String uri2 = lookupNamespace(prefix);
+            if (uri2 != null && uri2.equals(uri))
+            {
+                foundPrefix = prefix;
+                break;
+            }
+        }
+        return foundPrefix;
+    }
+    
+    MappingRecord getMappingFromURI(String uri)
+    {
+        MappingRecord foundMap = null;
+        Enumeration prefixes = m_namespaces.keys();
+        while (prefixes.hasMoreElements())
+        {
+            String prefix = (String) prefixes.nextElement();
+            MappingRecord map2 = getMappingFromPrefix(prefix);
+            if (map2 != null && (map2.m_uri).equals(uri))
+            {
+                foundMap = map2;
+                break;
+            }
+        }
+        return foundMap;
+    }
+
+    /**
+     * Undeclare the namespace that is currently pointed to by a given prefix
+     */
+    boolean popNamespace(String prefix)
+    {
+        // Prefixes "xml" and "xmlns" cannot be redefined
+        if (prefix.startsWith(XML_PREFIX))
+        {
+            return false;
+        }
+
+        Stack stack;
+        if ((stack = getPrefixStack(prefix)) != null)
+        {
+            stack.pop();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Declare a mapping of a prefix to namespace URI at the given element depth.
+     * @param prefix a String with the prefix for a qualified name
+     * @param uri a String with the uri to which the prefix is to map
+     * @param elemDepth the depth of current declaration
+     */
+    public boolean pushNamespace(String prefix, String uri, int elemDepth)
+    {
+        // Prefixes "xml" and "xmlns" cannot be redefined
+        if (prefix.startsWith(XML_PREFIX))
+        {
+            return false;
+        }
+
+        Stack stack;
+        // Get the stack that contains URIs for the specified prefix
+        if ((stack = (Stack) m_namespaces.get(prefix)) == null)
+        {
+            m_namespaces.put(prefix, stack = new Stack());
+        }
+
+        if (!stack.empty())
+        {
+            MappingRecord mr = (MappingRecord)stack.peek();
+            if (uri.equals(mr.m_uri) || elemDepth == mr.m_declarationDepth) {
+                // If the same prefix/uri mapping is already on the stack
+                // don't push this one.
+                // Or if we have a mapping at the same depth
+                // don't replace by pushing this one. 
+                return false;
+            }
+        }
+        MappingRecord map = new MappingRecord(prefix,uri,elemDepth);
+        stack.push(map);
+        m_nodeStack.push(map);
+        return true;
+    }
+
+    /**
+     * Pop, or undeclare all namespace definitions that are currently
+     * declared at the given element depth, or deepter.
+     * @param elemDepth the element depth for which mappings declared at this
+     * depth or deeper will no longer be valid
+     * @param saxHandler The ContentHandler to notify of any endPrefixMapping()
+     * calls.  This parameter can be null.
+     */
+    void popNamespaces(int elemDepth, ContentHandler saxHandler)
+    {
+        while (true)
+        {
+            if (m_nodeStack.isEmpty())
+                return;
+            MappingRecord map = (MappingRecord) (m_nodeStack.peek());
+            int depth = map.m_declarationDepth;
+            if (elemDepth < 1 || map.m_declarationDepth < elemDepth)
+                break;
+            /* the depth of the declared mapping is elemDepth or deeper
+             * so get rid of it
+             */
+
+            MappingRecord nm1 = (MappingRecord) m_nodeStack.pop();
+            // pop the node from the stack
+            String prefix = map.m_prefix;
+
+            Stack prefixStack = getPrefixStack(prefix);
+            MappingRecord nm2 = (MappingRecord) prefixStack.peek();
+            if (nm1 == nm2)
+            {
+                // It would be nice to always pop() but we
+                // need to check that the prefix stack still has
+                // the node we want to get rid of. This is because
+                // the optimization of essentially this situation:
+                // <a xmlns:x="abc"><b xmlns:x="" xmlns:x="abc" /></a>
+                // will remove both mappings in <b> because the
+                // new mapping is the same as the masked one and we get
+                // <a xmlns:x="abc"><b/></a>
+                // So we are only removing xmlns:x="" or
+                // xmlns:x="abc" from the depth of element <b>
+                // when going back to <a> if in fact they have
+                // not been optimized away.
+                // 
+                prefixStack.pop();
+                if (saxHandler != null)
+                {
+                    try
+                    {
+                        saxHandler.endPrefixMapping(prefix);
+                    }
+                    catch (SAXException e)
+                    {
+                        // not much we can do if they aren't willing to listen
+                    }
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Generate a new namespace prefix ( ns0, ns1 ...) not used before
+     * @return String a new namespace prefix ( ns0, ns1, ns2 ...)
+     */
+    public String generateNextPrefix()
+    {
+        return "ns" + (count++);
+    }
+
+ 
+    /**
+     * This method makes a clone of this object.
+     *
+     */
+    public Object clone() throws CloneNotSupportedException {
+        NamespaceMappings clone = new NamespaceMappings();
+        clone.m_nodeStack = (NamespaceMappings.Stack) m_nodeStack.clone();        
+        clone.count = this.count;
+        clone.m_namespaces = (Hashtable) m_namespaces.clone();
+        
+        clone.count = count;
+        return clone;
+        
+    }
+    
+    final void reset()
+    {
+        this.count = 0;
+        this.m_namespaces.clear();
+        this.m_nodeStack.clear();        
+        
+        initNamespaces();
+    }
+    
+    /**
+     * Just a little class that ties the 3 fields together
+     * into one object, and this simplifies the pushing
+     * and popping of namespaces to one push or one pop on
+     * one stack rather than on 3 separate stacks.
+     */
+    class MappingRecord {
+        final String m_prefix;  // the prefix
+        final String m_uri;     // the uri, possibly "" but never null
+        // the depth of the element where declartion was made
+        final int m_declarationDepth;
+        MappingRecord(String prefix, String uri, int depth) {
+            m_prefix = prefix;
+            m_uri = (uri==null)? EMPTYSTRING : uri;
+            m_declarationDepth = depth;
+        }
+    }    
+    
+    /**
+     * Rather than using java.util.Stack, this private class
+     * provides a minimal subset of methods and is faster
+     * because it is not thread-safe.
+     */
+    private class Stack {
+        private int top = -1;
+        private int max = 20;
+        Object[] m_stack = new Object[max];
+        
+        public Object clone() throws CloneNotSupportedException {
+            NamespaceMappings.Stack clone = new NamespaceMappings.Stack();  
+            clone.max = this.max;
+            clone.top = this.top;
+            clone.m_stack = new Object[clone.max];
+            for (int i=0; i <= top; i++) {
+            	// We are just copying references to immutable MappingRecord objects here
+            	// so it is OK if the clone has references to these.
+            	clone.m_stack[i] = this.m_stack[i];
+            }
+            return clone;            
+        }
+        
+        public Stack()
+        {
+        }
+        
+        public Object push(Object o) {
+            top++;
+            if (max <= top) {
+                int newMax = 2*max + 1;
+                Object[] newArray = new Object[newMax];
+                System.arraycopy(m_stack,0, newArray, 0, max);
+                max = newMax;
+                m_stack = newArray;
+            }
+            m_stack[top] = o;
+            return o;
+        }
+        
+        public Object pop() {
+            Object o;
+            if (0 <= top) {
+                o = m_stack[top];
+                // m_stack[top] = null;  do we really care?
+                top--;
+            }
+            else
+                o = null;
+            return o;
+        }
+        
+        public Object peek() {
+            Object o;
+            if (0 <= top) {
+                o = m_stack[top];
+            }
+            else
+                o = null;
+            return o;
+        }
+        
+        public Object peek(int idx) {
+            return m_stack[idx];
+        }
+        
+        public boolean isEmpty() {
+            return (top < 0);
+        }
+        public boolean empty() {
+            return (top < 0);
+        }
+        
+        public void clear() {
+            for (int i=0; i<= top; i++)
+                m_stack[i] = null;
+            top = -1;
+        }  
+        
+        public Object getElement(int index) {
+            return m_stack[index];      
+        }
+    }
+    /**
+     * A more type-safe way to get a stack of prefix mappings
+     * from the Hashtable m_namespaces
+     * (this is the only method that does the type cast).
+     */
+
+    private Stack getPrefixStack(String prefix) {
+        Stack fs = (Stack) m_namespaces.get(prefix);
+        return fs;
+    }
+    
+    /**
+     * A more type-safe way of saving stacks under the
+     * m_namespaces Hashtable.
+     */
+    private Stack createPrefixStack(String prefix)
+    {
+        Stack fs = new Stack();
+        m_namespaces.put(prefix, fs);
+        return fs;
+    }
+    
+    /**
+     * Given a namespace uri, get all prefixes bound to the Namespace URI in the current scope. 
+     * 
+     * @param uri the namespace URI to be search for
+     * @return An array of Strings which are
+     * all prefixes bound to the namespace URI in the current scope.
+     * An array of zero elements is returned if no prefixes map to the given
+     * namespace URI.
+     */
+    public String[] lookupAllPrefixes(String uri)
+    {
+        java.util.ArrayList foundPrefixes = new java.util.ArrayList();
+        Enumeration prefixes = m_namespaces.keys();
+        while (prefixes.hasMoreElements())
+        {
+            String prefix = (String) prefixes.nextElement();
+            String uri2 = lookupNamespace(prefix);
+            if (uri2 != null && uri2.equals(uri))
+            {
+                foundPrefixes.add(prefix);
+            }
+        }
+        String[] prefixArray = new String[foundPrefixes.size()];
+        foundPrefixes.toArray(prefixArray);
+        return prefixArray;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/ObjectFactory.java b/src/main/java/org/apache/xml/serializer/ObjectFactory.java
new file mode 100755
index 0000000..560d984
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ObjectFactory.java
@@ -0,0 +1,660 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ObjectFactory.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+
+package org.apache.xml.serializer;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each JAXP subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the JAXP
+ * API.
+ * <p>
+ * This code is designed to implement the JAXP 1.1 spec pluggability
+ * feature and is designed to run on JDK version 1.1 and
+ * later, and to compile on JDK 1.2 and onward.  
+ * The code also runs both as part of an unbundled jar file and
+ * when bundled as part of the JDK.
+ * <p>
+ * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
+ * class and modified to be used as a general utility for creating objects 
+ * dynamically.
+ *
+ * @xsl.usage internal
+ */
+class ObjectFactory {
+
+    //
+    // Constants
+    //
+
+    // name of default properties file to look for in JDK's jre/lib directory
+    private static final String DEFAULT_PROPERTIES_FILENAME =
+                                                     "xalan.properties";
+
+    private static final String SERVICES_PATH = "META-INF/services/";
+
+    /** Set to true for debugging */
+    private static final boolean DEBUG = false;
+
+    /** cache the contents of the xalan.properties file.
+     *  Until an attempt has been made to read this file, this will
+     * be null; if the file does not exist or we encounter some other error
+     * during the read, this will be empty.
+     */
+    private static Properties fXalanProperties = null;
+
+    /***
+     * Cache the time stamp of the xalan.properties file so
+     * that we know if it's been modified and can invalidate
+     * the cache when necessary.
+     */
+    private static long fLastModified = -1;
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, String fallbackClassName)
+        throws ConfigurationError {
+        return createObject(factoryId, null, fallbackClassName);
+    } // createObject(String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, 
+                                      String propertiesFilename,
+                                      String fallbackClassName)
+        throws ConfigurationError
+    {
+        Class factoryClass = lookUpFactoryClass(factoryId,
+                                                propertiesFilename,
+                                                fallbackClassName);
+
+        if (factoryClass == null) {
+            throw new ConfigurationError(
+                "Provider for " + factoryId + " cannot be found", null);
+        }
+
+        try{
+            Object instance = factoryClass.newInstance();
+            debugPrintln("created new instance of factory " + factoryId);
+            return instance;
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider for factory " + factoryId
+                    + " could not be instantiated: " + x, x);
+        }
+    } // createObject(String,String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId) 
+        throws ConfigurationError
+    {
+        return lookUpFactoryClass(factoryId, null, null);
+    } // lookUpFactoryClass(String):Class
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId,
+                                           String propertiesFilename,
+                                           String fallbackClassName)
+        throws ConfigurationError
+    {
+        String factoryClassName = lookUpFactoryClassName(factoryId,
+                                                         propertiesFilename,
+                                                         fallbackClassName);
+        ClassLoader cl = findClassLoader();
+
+        if (factoryClassName == null) {
+            factoryClassName = fallbackClassName;
+        }
+
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(factoryClassName,
+                                                    cl,
+                                                    true);
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return providerClass;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + factoryClassName + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider "+factoryClassName+" could not be instantiated: "+x,
+                x);
+        }
+    } // lookUpFactoryClass(String,String,String):Class
+
+    /**
+     * Finds the name of the required implementation class in the specified
+     * order.  The specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return name of class that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static String lookUpFactoryClassName(String factoryId,
+                                                String propertiesFilename,
+                                                String fallbackClassName)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Use the system property first
+        try {
+            String systemProp = ss.getSystemProperty(factoryId);
+            if (systemProp != null) {
+                debugPrintln("found system property, value=" + systemProp);
+                return systemProp;
+            }
+        } catch (SecurityException se) {
+            // Ignore and continue w/ next location
+        }
+
+        // Try to read from propertiesFilename, or
+        // $java.home/lib/xalan.properties
+        String factoryClassName = null;
+        // no properties file name specified; use
+        // $JAVA_HOME/lib/xalan.properties:
+        if (propertiesFilename == null) {
+            File propertiesFile = null;
+            boolean propertiesFileExists = false;
+            try {
+                String javah = ss.getSystemProperty("java.home");
+                propertiesFilename = javah + File.separator +
+                    "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
+                propertiesFile = new File(propertiesFilename);
+                propertiesFileExists = ss.getFileExists(propertiesFile);
+            } catch (SecurityException e) {
+                // try again...
+                fLastModified = -1;
+                fXalanProperties = null;
+            }
+
+            synchronized (ObjectFactory.class) {
+                boolean loadProperties = false;
+                FileInputStream fis = null;
+                try {
+                    // file existed last time
+                    if(fLastModified >= 0) {
+                        if(propertiesFileExists &&
+                                (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
+                            loadProperties = true;
+                        } else {
+                            // file has stopped existing...
+                            if(!propertiesFileExists) {
+                                fLastModified = -1;
+                                fXalanProperties = null;
+                            } // else, file wasn't modified!
+                        }
+                    } else {
+                        // file has started to exist:
+                        if(propertiesFileExists) {
+                            loadProperties = true;
+                            fLastModified = ss.getLastModified(propertiesFile);
+                        } // else, nothing's changed
+                    }
+                    if(loadProperties) {
+                        // must never have attempted to read xalan.properties
+                        // before (or it's outdeated)
+                        fXalanProperties = new Properties();
+                        fis = ss.getFileInputStream(propertiesFile);
+                        fXalanProperties.load(fis);
+                    }
+	        } catch (Exception x) {
+	            fXalanProperties = null;
+	            fLastModified = -1;
+                    // assert(x instanceof FileNotFoundException
+	            //        || x instanceof SecurityException)
+	            // In both cases, ignore and continue w/ next location
+	        }
+                finally {
+                    // try to close the input stream if one was opened.
+                    if (fis != null) {
+                        try {
+                            fis.close();
+                        }
+                        // Ignore the exception.
+                        catch (IOException exc) {}
+                    }
+                }	            
+            }
+            if(fXalanProperties != null) {
+                factoryClassName = fXalanProperties.getProperty(factoryId);
+            }
+        } else {
+            FileInputStream fis = null;
+            try {
+                fis = ss.getFileInputStream(new File(propertiesFilename));
+                Properties props = new Properties();
+                props.load(fis);
+                factoryClassName = props.getProperty(factoryId);
+            } catch (Exception x) {
+                // assert(x instanceof FileNotFoundException
+                //        || x instanceof SecurityException)
+                // In both cases, ignore and continue w/ next location
+            }
+            finally {
+                // try to close the input stream if one was opened.
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    }
+                    // Ignore the exception.
+                    catch (IOException exc) {}
+                }
+            }               
+        }
+        if (factoryClassName != null) {
+            debugPrintln("found in " + propertiesFilename + ", value="
+                          + factoryClassName);
+            return factoryClassName;
+        }
+
+        // Try Jar Service Provider Mechanism
+        return findJarServiceProviderName(factoryId);
+    } // lookUpFactoryClass(String,String):String
+
+    //
+    // Private static methods
+    //
+
+    /** Prints a message to standard error if debugging is enabled. */
+    private static void debugPrintln(String msg) {
+        if (DEBUG) {
+            System.err.println("JAXP: " + msg);
+        }
+    } // debugPrintln(String)
+
+    /**
+     * Figure out which ClassLoader to use.  For JDK 1.2 and later use
+     * the context ClassLoader.
+     */
+    static ClassLoader findClassLoader()
+        throws ConfigurationError
+    { 
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Figure out which ClassLoader to use for loading the provider
+        // class.  If there is a Context ClassLoader then use it.
+        ClassLoader context = ss.getContextClassLoader();
+        ClassLoader system = ss.getSystemClassLoader();
+
+        ClassLoader chain = system;
+        while (true) {
+            if (context == chain) {
+                // Assert: we are on JDK 1.1 or we have no Context ClassLoader
+                // or any Context ClassLoader in chain of system classloader
+                // (including extension ClassLoader) so extend to widest
+                // ClassLoader (always look in system ClassLoader if Xalan
+                // is in boot/extension/system classpath and in current
+                // ClassLoader otherwise); normal classloaders delegate
+                // back to system ClassLoader first so this widening doesn't
+                // change the fact that context ClassLoader will be consulted
+                ClassLoader current = ObjectFactory.class.getClassLoader();
+
+                chain = system;
+                while (true) {
+                    if (current == chain) {
+                        // Assert: Current ClassLoader in chain of
+                        // boot/extension/system ClassLoaders
+                        return system;
+                    }
+                    if (chain == null) {
+                        break;
+                    }
+                    chain = ss.getParentClassLoader(chain);
+                }
+
+                // Assert: Current ClassLoader not in chain of
+                // boot/extension/system ClassLoaders
+                return current;
+            }
+
+            if (chain == null) {
+                // boot ClassLoader reached
+                break;
+            }
+
+            // Check for any extension ClassLoaders in chain up to
+            // boot ClassLoader
+            chain = ss.getParentClassLoader(chain);
+        };
+
+        // Assert: Context ClassLoader not in chain of
+        // boot/extension/system ClassLoaders
+        return context;
+    } // findClassLoader():ClassLoader
+
+    /**
+     * Create an instance of a class using the specified ClassLoader
+     */ 
+    static Object newInstance(String className, ClassLoader cl,
+                                      boolean doFallback)
+        throws ConfigurationError
+    {
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(className, cl, doFallback);
+            Object instance = providerClass.newInstance();
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return instance;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + className + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider " + className + " could not be instantiated: " + x,
+                x);
+        }
+    }
+
+    /**
+     * Find a Class using the specified ClassLoader
+     */ 
+    static Class findProviderClass(String className, ClassLoader cl,
+                                           boolean doFallback)
+        throws ClassNotFoundException, ConfigurationError
+    {   
+        //throw security exception if the calling thread is not allowed to access the
+        //class. Restrict the access to the package classes as specified in java.security policy.
+        SecurityManager security = System.getSecurityManager();
+        try{
+                if (security != null){
+                    final int lastDot = className.lastIndexOf(".");
+                    String packageName = className;
+                    if (lastDot != -1) packageName = className.substring(0, lastDot);
+                    security.checkPackageAccess(packageName);
+                 }   
+        }catch(SecurityException e){
+            throw e;
+        }
+        
+        Class providerClass;
+        if (cl == null) {
+            // XXX Use the bootstrap ClassLoader.  There is no way to
+            // load a class using the bootstrap ClassLoader that works
+            // in both JDK 1.1 and Java 2.  However, this should still
+            // work b/c the following should be true:
+            //
+            // (cl == null) iff current ClassLoader == null
+            //
+            // Thus Class.forName(String) will use the current
+            // ClassLoader which will be the bootstrap ClassLoader.
+            providerClass = Class.forName(className);
+        } else {
+            try {
+                providerClass = cl.loadClass(className);
+            } catch (ClassNotFoundException x) {
+                if (doFallback) {
+                    // Fall back to current classloader
+                    ClassLoader current = ObjectFactory.class.getClassLoader();
+                    if (current == null) {
+                        providerClass = Class.forName(className);
+                    } else if (cl != current) {
+                        cl = current;
+                        providerClass = cl.loadClass(className);
+                    } else {
+                        throw x;
+                    }
+                } else {
+                    throw x;
+                }
+            }
+        }
+
+        return providerClass;
+    }
+
+    /**
+     * Find the name of service provider using Jar Service Provider Mechanism
+     *
+     * @return instance of provider class if found or null
+     */
+    private static String findJarServiceProviderName(String factoryId)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+        String serviceId = SERVICES_PATH + factoryId;
+        InputStream is = null;
+
+        // First try the Context ClassLoader
+        ClassLoader cl = findClassLoader();
+
+        is = ss.getResourceAsStream(cl, serviceId);
+
+        // If no provider found then try the current ClassLoader
+        if (is == null) {
+            ClassLoader current = ObjectFactory.class.getClassLoader();
+            if (cl != current) {
+                cl = current;
+                is = ss.getResourceAsStream(cl, serviceId);
+            }
+        }
+
+        if (is == null) {
+            // No provider found
+            return null;
+        }
+
+        debugPrintln("found jar resource=" + serviceId +
+               " using ClassLoader: " + cl);
+
+        // Read the service provider name in UTF-8 as specified in
+        // the jar spec.  Unfortunately this fails in Microsoft
+        // VJ++, which does not implement the UTF-8
+        // encoding. Theoretically, we should simply let it fail in
+        // that case, since the JVM is obviously broken if it
+        // doesn't support such a basic standard.  But since there
+        // are still some users attempting to use VJ++ for
+        // development, we have dropped in a fallback which makes a
+        // second attempt using the platform's default encoding. In
+        // VJ++ this is apparently ASCII, which is a subset of
+        // UTF-8... and since the strings we'll be reading here are
+        // also primarily limited to the 7-bit ASCII range (at
+        // least, in English versions), this should work well
+        // enough to keep us on the air until we're ready to
+        // officially decommit from VJ++. [Edited comment from
+        // jkesselm]
+        BufferedReader rd;
+        try {
+            rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+        } catch (java.io.UnsupportedEncodingException e) {
+            rd = new BufferedReader(new InputStreamReader(is));
+        }
+        
+        String factoryClassName = null;
+        try {
+            // XXX Does not handle all possible input as specified by the
+            // Jar Service Provider specification
+            factoryClassName = rd.readLine();
+        } catch (IOException x) {
+            // No provider found
+            return null;
+        }
+        finally {
+            try {
+                // try to close the reader.
+                rd.close();
+            }
+            // Ignore the exception.
+            catch (IOException exc) {}
+        }          
+
+        if (factoryClassName != null &&
+            ! "".equals(factoryClassName)) {
+            debugPrintln("found in resource, value="
+                   + factoryClassName);
+
+            // Note: here we do not want to fall back to the current
+            // ClassLoader because we want to avoid the case where the
+            // resource file was found using one ClassLoader and the
+            // provider class was instantiated using a different one.
+            return factoryClassName;
+        }
+
+        // No provider found
+        return null;
+    }
+
+    //
+    // Classes
+    //
+
+    /**
+     * A configuration error.
+     */
+    static class ConfigurationError 
+        extends Error {
+                static final long serialVersionUID = 8859254254255146542L;
+        //
+        // Data
+        //
+
+        /** Exception. */
+        private Exception exception;
+
+        //
+        // Constructors
+        //
+
+        /**
+         * Construct a new instance with the specified detail string and
+         * exception.
+         */
+        ConfigurationError(String msg, Exception x) {
+            super(msg);
+            this.exception = x;
+        } // <init>(String,Exception)
+
+        //
+        // Public methods
+        //
+
+        /** Returns the exception associated to this error. */
+        Exception getException() {
+            return exception;
+        } // getException():Exception
+
+    } // class ConfigurationError
+
+} // class ObjectFactory
diff --git a/src/main/java/org/apache/xml/serializer/OutputPropertiesFactory.java b/src/main/java/org/apache/xml/serializer/OutputPropertiesFactory.java
new file mode 100644
index 0000000..cb41927
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/OutputPropertiesFactory.java
@@ -0,0 +1,516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: OutputPropertiesFactory.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import javax.xml.transform.OutputKeys;
+
+import org.apache.xml.serializer.utils.MsgKey;
+import org.apache.xml.serializer.utils.Utils;
+import org.apache.xml.serializer.utils.WrappedRuntimeException;
+
+/**
+ * This class is a factory to generate a set of default properties
+ * of key/value pairs that are used to create a serializer through the
+ * factory {@link SerializerFactory SerilizerFactory}. 
+ * The properties generated by this factory
+ * may be modified to non-default values before the SerializerFactory is used to
+ * create a Serializer.
+ * <p>
+ * The given output types supported are "xml", "text", and "html". 
+ * These type strings can be obtained from the 
+ * {@link Method Method} class in this package.
+ * <p>
+ * Other constants defined in this class are the non-standard property keys
+ * that can be used to set non-standard property values on a java.util.Properties object
+ * that is used to create or configure a serializer. Here are the non-standard keys:
+ * <ul>
+ * <li> <b>S_KEY_INDENT_AMOUNT </b> -
+ * The non-standard property key to use to set the indentation amount.
+ * The "indent" key needs to have a value of "yes", and this
+ * properties value is a the number of whitespaces to indent by per
+ * indentation level.
+ * 
+ * <li> <b>S_KEY_CONTENT_HANDLER </b> -
+ * This non-standard property key is used to set the name of the fully qualified 
+ * Java class that implements the ContentHandler interface. 
+ * The output of the serializer will be SAX events sent to this an
+ * object of this class.
+ * 
+ * <li> <b>S_KEY_ENTITIES </b> -
+ * This non-standard property key is used to specify the name of the property file
+ * that specifies character to entity reference mappings. A line in such a
+ * file is has the name of the entity and the numeric (base 10) value
+ * of the corresponding character, like this one: <br> quot=34 <br>
+ * 
+ * <li> <b>S_USE_URL_ESCAPING </b> -
+ * This non-standard property key is used to set a value of "yes" if the href values for HTML serialization should
+ *  use %xx escaping.
+ * 
+ * <li> <b>S_OMIT_META_TAG </b> -
+ * This non-standard property key is used to set a value of "yes" if the META tag should be omitted where it would
+ *  otherwise be supplied.
+ * </ul>
+ * 
+ * @see SerializerFactory
+ * @see Method
+ * @see Serializer
+ */
+public final class OutputPropertiesFactory
+{
+    /** S_BUILTIN_EXTENSIONS_URL is a mnemonic for the XML Namespace 
+     *(http://xml.apache.org/xalan) predefined to signify Xalan's
+     * built-in XSLT Extensions. When used in stylesheets, this is often 
+     * bound to the "xalan:" prefix.
+     */
+    private static final String 
+      S_BUILTIN_EXTENSIONS_URL = "http://xml.apache.org/xalan"; 
+
+    /**
+     * The old built-in extension url. It is still supported for
+     * backward compatibility.
+     */
+    private static final String 
+      S_BUILTIN_OLD_EXTENSIONS_URL = "http://xml.apache.org/xslt"; 
+      
+    //************************************************************
+    //*  PUBLIC CONSTANTS
+    //************************************************************
+    /** 
+     * This is not a public API.
+     * This is the built-in extensions namespace, 
+     * reexpressed in {namespaceURI} syntax
+     * suitable for prepending to a localname to produce a "universal
+     * name".
+     */
+    public static final String S_BUILTIN_EXTENSIONS_UNIVERSAL =
+        "{" + S_BUILTIN_EXTENSIONS_URL + "}";
+
+    // Some special Xalan keys.
+
+    /** 
+     * The non-standard property key to use to set the
+     * number of whitepaces to indent by, per indentation level,
+     * if indent="yes".
+     */
+    public static final String S_KEY_INDENT_AMOUNT =
+        S_BUILTIN_EXTENSIONS_UNIVERSAL + "indent-amount";
+        
+    /** 
+     * The non-standard property key to use to set the
+     * characters to write out as at the end of a line,
+     * rather than the default ones from the runtime.
+     */
+    public static final String S_KEY_LINE_SEPARATOR =
+        S_BUILTIN_EXTENSIONS_UNIVERSAL + "line-separator";        
+
+    /** This non-standard property key is used to set the name of the fully qualified 
+     * Java class that implements the ContentHandler interface. 
+     * Fully qualified name of class with a default constructor that
+     *  implements the ContentHandler interface, where the result tree events
+     *  will be sent to.
+     */
+
+    public static final String S_KEY_CONTENT_HANDLER =
+        S_BUILTIN_EXTENSIONS_UNIVERSAL + "content-handler";
+
+    /**
+     * This non-standard property key is used to specify the name of the property file
+     * that specifies character to entity reference mappings.
+     */
+    public static final String S_KEY_ENTITIES =
+        S_BUILTIN_EXTENSIONS_UNIVERSAL + "entities";
+
+    /** 
+     * This non-standard property key is used to set a value of "yes" if the href values for HTML serialization should
+     *  use %xx escaping. */
+    public static final String S_USE_URL_ESCAPING =
+        S_BUILTIN_EXTENSIONS_UNIVERSAL + "use-url-escaping";
+
+    /** 
+     * This non-standard property key is used to set a value of "yes" if the META tag should be omitted where it would
+     *  otherwise be supplied.
+     */
+    public static final String S_OMIT_META_TAG =
+        S_BUILTIN_EXTENSIONS_UNIVERSAL + "omit-meta-tag";
+
+    /**
+     * The old built-in extension namespace, this is not a public API.
+     */
+    public static final String S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL =
+        "{" + S_BUILTIN_OLD_EXTENSIONS_URL + "}";
+
+    /**
+     * This is not a public API, it is only public because it is used
+     * by outside of this package,
+     * it is the length of the old built-in extension namespace.
+     */
+    public static final int S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL_LEN =
+        S_BUILTIN_OLD_EXTENSIONS_UNIVERSAL.length();
+
+    //************************************************************
+    //*  PRIVATE CONSTANTS
+    //************************************************************
+
+    private static final String S_XSLT_PREFIX = "xslt.output.";
+    private static final int S_XSLT_PREFIX_LEN = S_XSLT_PREFIX.length();
+    private static final String S_XALAN_PREFIX = "org.apache.xslt.";
+    private static final int S_XALAN_PREFIX_LEN = S_XALAN_PREFIX.length();
+
+    /** Synchronization object for lazy initialization of the above tables. */
+    private static Integer m_synch_object = new Integer(1);
+
+    /** the directory in which the various method property files are located */
+    private static final String PROP_DIR = SerializerBase.PKG_PATH+'/';
+    /** property file for default XML properties */
+    private static final String PROP_FILE_XML = "output_xml.properties";
+    /** property file for default TEXT properties */
+    private static final String PROP_FILE_TEXT = "output_text.properties";
+    /** property file for default HTML properties */
+    private static final String PROP_FILE_HTML = "output_html.properties";
+    /** property file for default UNKNOWN (Either XML or HTML, to be determined later) properties */
+    private static final String PROP_FILE_UNKNOWN = "output_unknown.properties";
+
+    //************************************************************
+    //*  PRIVATE STATIC FIELDS
+    //************************************************************
+
+    /** The default properties of all output files. */
+    private static Properties m_xml_properties = null;
+
+    /** The default properties when method="html". */
+    private static Properties m_html_properties = null;
+
+    /** The default properties when method="text". */
+    private static Properties m_text_properties = null;
+
+    /** The properties when method="" for the "unknown" wrapper */
+    private static Properties m_unknown_properties = null;
+
+    private static final Class
+        ACCESS_CONTROLLER_CLASS = findAccessControllerClass();
+
+    private static Class findAccessControllerClass() {
+        try
+        {
+            // This Class was introduced in JDK 1.2. With the re-architecture of
+            // security mechanism ( starting in JDK 1.2 ), we have option of
+            // giving privileges to certain part of code using doPrivileged block.
+            // In JDK1.1.X applications won't be having security manager and if
+            // there is security manager ( in applets ), code need to be signed
+            // and trusted for having access to resources.
+
+            return Class.forName("java.security.AccessController");
+        }
+        catch (Exception e)
+        {
+            //User may be using older JDK ( JDK <1.2 ). Allow him/her to use it.
+            // But don't try to use doPrivileged
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates an empty OutputProperties with the property key/value defaults specified by
+     * a property file.  The method argument is used to construct a string of
+     * the form output_[method].properties (for instance, output_html.properties).
+     * The output_xml.properties file is always used as the base.
+     * 
+     * <p>Anything other than 'text', 'xml', and 'html', will
+     * use the output_xml.properties file.</p>
+     *
+     * @param   method non-null reference to method name.
+     *
+     * @return Properties object that holds the defaults for the given method.
+     */
+    static public final Properties getDefaultMethodProperties(String method)
+    {
+        String fileName = null;
+        Properties defaultProperties = null;
+        // According to this article : Double-check locking does not work
+        // http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-toolbox.html
+        try
+        {
+            synchronized (m_synch_object)
+            {
+                if (null == m_xml_properties) // double check
+                {
+                    fileName = PROP_FILE_XML;
+                    m_xml_properties = loadPropertiesFile(fileName, null);
+                }
+            }
+
+            if (method.equals(Method.XML))
+            {
+                defaultProperties = m_xml_properties;
+            }
+            else if (method.equals(Method.HTML))
+            {
+                if (null == m_html_properties) // double check
+                {
+                    fileName = PROP_FILE_HTML;
+                    m_html_properties =
+                        loadPropertiesFile(fileName, m_xml_properties);
+                }
+
+                defaultProperties = m_html_properties;
+            }
+            else if (method.equals(Method.TEXT))
+            {
+                if (null == m_text_properties) // double check
+                {
+                    fileName = PROP_FILE_TEXT;
+                    m_text_properties =
+                        loadPropertiesFile(fileName, m_xml_properties);
+                    if (null
+                        == m_text_properties.getProperty(OutputKeys.ENCODING))
+                    {
+                        String mimeEncoding = Encodings.getMimeEncoding(null);
+                        m_text_properties.put(
+                            OutputKeys.ENCODING,
+                            mimeEncoding);
+                    }
+                }
+
+                defaultProperties = m_text_properties;
+            }
+            else if (method.equals(Method.UNKNOWN))
+            {
+                if (null == m_unknown_properties) // double check
+                {
+                    fileName = PROP_FILE_UNKNOWN;
+                    m_unknown_properties =
+                        loadPropertiesFile(fileName, m_xml_properties);
+                }
+
+                defaultProperties = m_unknown_properties;
+            }
+            else
+            {
+                // TODO: Calculate res file from name.
+                defaultProperties = m_xml_properties;
+            }
+        }
+        catch (IOException ioe)
+        {
+            throw new WrappedRuntimeException(
+                Utils.messages.createMessage(
+                    MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                    new Object[] { fileName, method }),
+                ioe);
+        }
+        // wrap these cached defaultProperties in a new Property object just so
+        // that the caller of this method can't modify the default values
+        return new Properties(defaultProperties);
+    }
+
+    /**
+     * Load the properties file from a resource stream.  If a
+     * key name such as "org.apache.xslt.xxx", fix up the start of
+     * string to be a curly namespace.  If a key name starts with
+     * "xslt.output.xxx", clip off "xslt.output.".  If a key name *or* a
+     * key value is discovered, check for \u003a in the text, and
+     * fix it up to be ":", since earlier versions of the JDK do not
+     * handle the escape sequence (at least in key names).
+     *
+     * @param resourceName non-null reference to resource name.
+     * @param defaults Default properties, which may be null.
+     */
+    static private Properties loadPropertiesFile(
+        final String resourceName,
+        Properties defaults)
+        throws IOException
+    {
+
+        // This static method should eventually be moved to a thread-specific class
+        // so that we can cache the ContextClassLoader and bottleneck all properties file
+        // loading throughout Xalan.
+
+        Properties props = new Properties(defaults);
+
+        InputStream is = null;
+        BufferedInputStream bis = null;
+
+        try
+        {
+            if (ACCESS_CONTROLLER_CLASS != null)
+            {
+                is = (InputStream) AccessController
+                    .doPrivileged(new PrivilegedAction() {
+                        public Object run()
+                        {
+                            return OutputPropertiesFactory.class
+                                .getResourceAsStream(resourceName);
+                        }
+                    });
+            }
+            else
+            {
+                // User may be using older JDK ( JDK < 1.2 )
+                is = OutputPropertiesFactory.class
+                    .getResourceAsStream(resourceName);
+            }
+
+            bis = new BufferedInputStream(is);
+            props.load(bis);
+        }
+        catch (IOException ioe)
+        {
+            if (defaults == null)
+            {
+                throw ioe;
+            }
+            else
+            {
+                throw new WrappedRuntimeException(
+                    Utils.messages.createMessage(
+                        MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                        new Object[] { resourceName }),
+                    ioe);
+                //"Could not load '"+resourceName+"' (check CLASSPATH), now using just the defaults ", ioe);
+            }
+        }
+        catch (SecurityException se)
+        {
+            // Repeat IOException handling for sandbox/applet case -sc
+            if (defaults == null)
+            {
+                throw se;
+            }
+            else
+            {
+                throw new WrappedRuntimeException(
+                    Utils.messages.createMessage(
+                        MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                        new Object[] { resourceName }),
+                    se);
+                //"Could not load '"+resourceName+"' (check CLASSPATH, applet security), now using just the defaults ", se);
+            }
+        }
+        finally
+        {
+            if (bis != null)
+            {
+                bis.close();
+            }
+            if (is != null)
+            {
+                is.close();
+            }
+        }
+
+        // Note that we're working at the HashTable level here,
+        // and not at the Properties level!  This is important
+        // because we don't want to modify the default properties.
+        // NB: If fixupPropertyString ends up changing the property
+        // name or value, we need to remove the old key and re-add
+        // with the new key and value.  However, then our Enumeration
+        // could lose its place in the HashTable.  So, we first
+        // clone the HashTable and enumerate over that since the
+        // clone will not change.  When we migrate to Collections,
+        // this code should be revisited and cleaned up to use
+        // an Iterator which may (or may not) alleviate the need for
+        // the clone.  Many thanks to Padraig O'hIceadha
+        // <padraig@gradient.ie> for finding this problem.  Bugzilla 2000.
+
+        Enumeration keys = ((Properties) props.clone()).keys();
+        while (keys.hasMoreElements())
+        {
+            String key = (String) keys.nextElement();
+            // Now check if the given key was specified as a
+            // System property. If so, the system property
+            // overides the default value in the propery file.
+            String value = null;
+            try
+            {
+                value = System.getProperty(key);
+            }
+            catch (SecurityException se)
+            {
+                // No-op for sandbox/applet case, leave null -sc
+            }
+            if (value == null)
+                value = (String) props.get(key);
+
+            String newKey = fixupPropertyString(key, true);
+            String newValue = null;
+            try
+            {
+                newValue = System.getProperty(newKey);
+            }
+            catch (SecurityException se)
+            {
+                // No-op for sandbox/applet case, leave null -sc
+            }
+            if (newValue == null)
+                newValue = fixupPropertyString(value, false);
+            else
+                newValue = fixupPropertyString(newValue, false);
+
+            if (key != newKey || value != newValue)
+            {
+                props.remove(key);
+                props.put(newKey, newValue);
+            }
+
+        }
+
+        return props;
+    }
+
+    /**
+     * Fix up a string in an output properties file according to
+     * the rules of {@link #loadPropertiesFile}.
+     *
+     * @param s non-null reference to string that may need to be fixed up.
+     * @return A new string if fixup occured, otherwise the s argument.
+     */
+    static private String fixupPropertyString(String s, boolean doClipping)
+    {
+        int index;
+        if (doClipping && s.startsWith(S_XSLT_PREFIX))
+        {
+            s = s.substring(S_XSLT_PREFIX_LEN);
+        }
+        if (s.startsWith(S_XALAN_PREFIX))
+        {
+            s =
+                S_BUILTIN_EXTENSIONS_UNIVERSAL
+                    + s.substring(S_XALAN_PREFIX_LEN);
+        }
+        if ((index = s.indexOf("\\u003a")) > 0)
+        {
+            String temp = s.substring(index + 6);
+            s = s.substring(0, index) + ":" + temp;
+
+        }
+        return s;
+    }
+
+}
diff --git a/src/main/java/org/apache/xml/serializer/OutputPropertyUtils.java b/src/main/java/org/apache/xml/serializer/OutputPropertyUtils.java
new file mode 100644
index 0000000..98b67ba
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/OutputPropertyUtils.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: OutputPropertyUtils.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.util.Properties;
+
+/**
+ * This class contains some static methods that act as helpers when parsing a
+ * Java Property object.
+ * 
+ * This class is not a public API. 
+ * It is only public because it is used outside of this package.
+ * 
+ * @see java.util.Properties
+ * @xsl.usage internal
+ */
+public final class OutputPropertyUtils
+{
+    /**
+      * Searches for the boolean property with the specified key in the property list.
+      * If the key is not found in this property list, the default property list,
+      * and its defaults, recursively, are then checked. The method returns
+      * <code>false</code> if the property is not found, or if the value is other
+      * than "yes".
+      *
+      * @param   key   the property key.
+      * @param   props   the list of properties that will be searched.
+      * @return  the value in this property list as a boolean value, or false
+      * if null or not "yes".
+      */
+    public static boolean getBooleanProperty(String key, Properties props)
+    {
+
+        String s = props.getProperty(key);
+
+        if (null == s || !s.equals("yes"))
+            return false;
+        else
+            return true;
+    }
+
+    /**
+     * Searches for the int property with the specified key in the property list.
+     * If the key is not found in this property list, the default property list,
+     * and its defaults, recursively, are then checked. The method returns
+     * <code>false</code> if the property is not found, or if the value is other
+     * than "yes".
+     *
+     * @param   key   the property key.
+     * @param   props   the list of properties that will be searched.
+     * @return  the value in this property list as a int value, or 0
+     * if null or not a number.
+     */
+    public static int getIntProperty(String key, Properties props)
+    {
+
+        String s = props.getProperty(key);
+
+        if (null == s)
+            return 0;
+        else
+            return Integer.parseInt(s);
+    }
+
+}
diff --git a/src/main/java/org/apache/xml/serializer/SecuritySupport.java b/src/main/java/org/apache/xml/serializer/SecuritySupport.java
new file mode 100644
index 0000000..2779261
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/SecuritySupport.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SecuritySupport.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+
+package org.apache.xml.serializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Base class with security related methods that work on JDK 1.1.
+ */
+class SecuritySupport {
+
+    /*
+     * Make this of type Object so that the verifier won't try to
+     * prove its type, thus possibly trying to load the SecuritySupport12
+     * class.
+     */
+    private static final Object securitySupport;
+
+    static {
+	SecuritySupport ss = null;
+	try {
+	    Class c = Class.forName("java.security.AccessController");
+	    // if that worked, we're on 1.2.
+	    /*
+	    // don't reference the class explicitly so it doesn't
+	    // get dragged in accidentally.
+	    c = Class.forName("javax.mail.SecuritySupport12");
+	    Constructor cons = c.getConstructor(new Class[] { });
+	    ss = (SecuritySupport)cons.newInstance(new Object[] { });
+	    */
+	    /*
+	     * Unfortunately, we can't load the class using reflection
+	     * because the class is package private.  And the class has
+	     * to be package private so the APIs aren't exposed to other
+	     * code that could use them to circumvent security.  Thus,
+	     * we accept the risk that the direct reference might fail
+	     * on some JDK 1.1 JVMs, even though we would never execute
+	     * this code in such a case.  Sigh...
+	     */
+	    ss = new SecuritySupport12();
+	} catch (Exception ex) {
+	    // ignore it
+	} finally {
+	    if (ss == null)
+		ss = new SecuritySupport();
+	    securitySupport = ss;
+	}
+    }
+
+    /**
+     * Return an appropriate instance of this class, depending on whether
+     * we're on a JDK 1.1 or J2SE 1.2 (or later) system.
+     */
+    static SecuritySupport getInstance() {
+	return (SecuritySupport)securitySupport;
+    }
+
+    ClassLoader getContextClassLoader() {
+	return null;
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return null;
+    }
+
+    ClassLoader getParentClassLoader(ClassLoader cl) {
+        return null;
+    }
+
+    String getSystemProperty(String propName) {
+        return System.getProperty(propName);
+    }
+
+    FileInputStream getFileInputStream(File file)
+        throws FileNotFoundException
+    {
+        return new FileInputStream(file);
+    }
+
+    InputStream getResourceAsStream(ClassLoader cl, String name) {
+        InputStream ris;
+        if (cl == null) {
+            ris = ClassLoader.getSystemResourceAsStream(name);
+        } else {
+            ris = cl.getResourceAsStream(name);
+        }
+        return ris;
+    }
+    
+    boolean getFileExists(File f) {
+        return f.exists();
+    }
+    
+    long getLastModified(File f) {
+        return f.lastModified();
+    }    
+}
diff --git a/src/main/java/org/apache/xml/serializer/SecuritySupport12.java b/src/main/java/org/apache/xml/serializer/SecuritySupport12.java
new file mode 100644
index 0000000..a5e7fad
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/SecuritySupport12.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SecuritySupport12.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+
+package org.apache.xml.serializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Security related methods that only work on J2SE 1.2 and newer.
+ */
+class SecuritySupport12 extends SecuritySupport {
+
+    ClassLoader getContextClassLoader() {
+        return (ClassLoader)
+                AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                ClassLoader cl = null;
+                try {
+                    cl = Thread.currentThread().getContextClassLoader();
+                } catch (SecurityException ex) { }
+                return cl;
+            }
+        });
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader cl = null;
+                    try {
+                        cl = ClassLoader.getSystemClassLoader();
+                    } catch (SecurityException ex) {}
+                    return cl;
+                }
+            });
+    }
+
+    ClassLoader getParentClassLoader(final ClassLoader cl) {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader parent = null;
+                    try {
+                        parent = cl.getParent();
+                    } catch (SecurityException ex) {}
+
+                    // eliminate loops in case of the boot
+                    // ClassLoader returning itself as a parent
+                    return (parent == cl) ? null : parent;
+                }
+            });
+    }
+
+    String getSystemProperty(final String propName) {
+        return (String)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return System.getProperty(propName);
+                }
+            });
+    }
+
+    FileInputStream getFileInputStream(final File file)
+        throws FileNotFoundException
+    {
+        try {
+            return (FileInputStream)
+                AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    public Object run() throws FileNotFoundException {
+                        return new FileInputStream(file);
+                    }
+                });
+        } catch (PrivilegedActionException e) {
+            throw (FileNotFoundException)e.getException();
+        }
+    }
+
+    InputStream getResourceAsStream(final ClassLoader cl,
+                                           final String name)
+    {
+        return (InputStream)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    InputStream ris;
+                    if (cl == null) {
+                        ris = ClassLoader.getSystemResourceAsStream(name);
+                    } else {
+                        ris = cl.getResourceAsStream(name);
+                    }
+                    return ris;
+                }
+            });
+    }
+    
+    boolean getFileExists(final File f) {
+    return ((Boolean)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Boolean(f.exists());
+                }
+            })).booleanValue();
+    }
+    
+    long getLastModified(final File f) {
+    return ((Long)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Long(f.lastModified());
+                }
+            })).longValue();
+    }
+        
+}
diff --git a/src/main/java/org/apache/xml/serializer/SerializationHandler.java b/src/main/java/org/apache/xml/serializer/SerializationHandler.java
new file mode 100644
index 0000000..57029e0
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/SerializationHandler.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializationHandler.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+
+import javax.xml.transform.Transformer;
+
+import org.w3c.dom.Node;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.DeclHandler;
+
+/**
+ * This interface is the one that a serializer implements. It is a group of
+ * other interfaces, such as ExtendedContentHandler, ExtendedLexicalHandler etc.
+ * In addition there are other methods, such as reset().
+ * 
+ * This class is public only because it is used in another package,
+ * it is not a public API.
+ * 
+ * @xsl.usage internal
+ */
+public interface SerializationHandler
+    extends
+        ExtendedContentHandler,
+        ExtendedLexicalHandler,
+        XSLOutputAttributes,
+        DeclHandler,
+        org.xml.sax.DTDHandler,
+        ErrorHandler,
+        DOMSerializer,
+        Serializer
+{
+    /**
+     * Set the SAX Content handler that the serializer sends its output to. This
+     * method only applies to a ToSAXHandler, not to a ToStream serializer.
+     * 
+     * @see Serializer#asContentHandler()
+     * @see ToSAXHandler
+     */
+    public void setContentHandler(ContentHandler ch);
+    
+    public void close();
+
+    /**
+     * Notify that the serializer should take this DOM node as input to be
+     * serialized.
+     * 
+     * @param node the DOM node to be serialized.
+     * @throws IOException
+     */
+    public void serialize(Node node) throws IOException;
+    /**
+     * Turns special character escaping on/off. 
+     * 
+     * Note that characters will
+     * never, even if this option is set to 'true', be escaped within
+     * CDATA sections in output XML documents.
+     * 
+     * @param escape true if escaping is to be set on.
+     */
+    public boolean setEscaping(boolean escape) throws SAXException;
+
+    /**
+     * Set the number of spaces to indent for each indentation level.
+     * @param spaces the number of spaces to indent for each indentation level.
+     */
+    public void setIndentAmount(int spaces);
+
+    /**
+     * Set the transformer associated with the serializer.
+     * @param transformer the transformer associated with the serializer.
+     */
+    public void setTransformer(Transformer transformer);
+    
+    /**
+     * Get the transformer associated with the serializer.
+     * @return Transformer the transformer associated with the serializer.
+     */
+    public Transformer getTransformer();
+
+    /** 
+     * Used only by TransformerSnapshotImpl to restore the serialization 
+     * to a previous state. 
+     * 
+     * @param mappings NamespaceMappings
+     */
+    public void setNamespaceMappings(NamespaceMappings mappings);
+
+    /**
+     * A SerializationHandler accepts SAX-like events, so
+     * it can accumulate attributes or namespace nodes after
+     * a startElement().
+     * <p>
+     * If the SerializationHandler has a Writer or OutputStream, 
+     * a call to this method will flush such accumulated 
+     * events as a closed start tag for an element.
+     * <p>
+     * If the SerializationHandler wraps a ContentHandler,
+     * a call to this method will flush such accumulated
+     * events as a SAX (not SAX-like) calls to
+     * startPrefixMapping() and startElement().
+     * <p>
+     * If one calls endDocument() then one need not call
+     * this method since a call to endDocument() will
+     * do what this method does. However, in some
+     * circumstances, such as with document fragments,
+     * endDocument() is not called and it may be
+     * necessary to call this method to flush
+     * any pending events.
+     * <p> 
+     * For performance reasons this method should not be called
+     * very often. 
+     */
+    public void flushPending() throws SAXException;
+    
+    /**
+     * Default behavior is to expand DTD entities,
+     * that is the initall default value is true.
+     * @param expand true if DTD entities are to be expanded,
+     * false if they are to be left as DTD entity references. 
+     */
+    public void setDTDEntityExpansion(boolean expand);
+    
+}
diff --git a/src/main/java/org/apache/xml/serializer/Serializer.java b/src/main/java/org/apache/xml/serializer/Serializer.java
new file mode 100644
index 0000000..849d39a
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/Serializer.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Serializer.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+package org.apache.xml.serializer;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Properties;
+
+import org.xml.sax.ContentHandler;
+
+/**
+ * The Serializer interface is implemented by a serializer to enable users to:
+ * <ul>
+ * <li>get and set streams or writers
+ * <li>configure the serializer with key/value properties
+ * <li>get an org.xml.sax.ContentHandler or a DOMSerializer to provide input to
+ * </ul>
+ *
+ * <p>
+ * Here is an example using the asContentHandler() method:
+ * <pre>
+ * java.util.Properties props = 
+ *   OutputPropertiesFactory.getDefaultMethodProperties(Method.TEXT);
+ * Serializer ser = SerializerFactory.getSerializer(props);
+ * java.io.PrintStream ostream = System.out; 
+ * ser.setOutputStream(ostream);
+ * 
+ * // Provide the SAX input events
+ * ContentHandler handler = ser.asContentHandler();
+ * handler.startDocument();
+ * char[] chars = { 'a', 'b', 'c' };
+ * handler.characters(chars, 0, chars.length);
+ * handler.endDocument();
+ * 
+ * ser.reset(); // get ready to use the serializer for another document
+ *              // of the same output method (TEXT).
+ * </pre>
+ * 
+ * <p>
+ * As an alternate to supplying a series of SAX events as input through the 
+ * ContentHandler interface, the input to serialize may be given as a DOM. 
+ * <p>
+ * For example:
+ * <pre>
+ * org.w3c.dom.Document     inputDoc;
+ * org.apache.xml.serializer.Serializer   ser;
+ * java.io.Writer owriter;
+ * 
+ * java.util.Properties props = 
+ *   OutputPropertiesFactory.getDefaultMethodProperties(Method.XML);
+ * Serializer ser = SerializerFactory.getSerializer(props);
+ * owriter = ...;  // create a writer to serialize the document to
+ * ser.setWriter( owriter );
+ * 
+ * inputDoc = ...; // create the DOM document to be serialized
+ * DOMSerializer dser = ser.asDOMSerializer(); // a DOM will be serialized
+ * dser.serialize(inputDoc); // serialize the DOM, sending output to owriter
+ * 
+ * ser.reset(); // get ready to use the serializer for another document
+ *              // of the same output method.
+ * </pre>
+ * 
+ * This interface is a public API.
+ * 
+ * @see Method
+ * @see OutputPropertiesFactory
+ * @see SerializerFactory
+ * @see DOMSerializer
+ * @see ContentHandler
+ * 
+ * @xsl.usage general
+ */
+public interface Serializer {
+
+    /**
+     * Specifies an output stream to which the document should be
+     * serialized. This method should not be called while the
+     * serializer is in the process of serializing a document.
+     * <p>
+     * The encoding specified in the output {@link Properties} is used, or
+     * if no encoding was specified, the default for the selected
+     * output method.
+     * <p>
+     * Only one of setWriter() or setOutputStream() should be called.
+     *
+     * @param output The output stream
+     */
+    public void setOutputStream(OutputStream output);
+
+    /**
+     * Get the output stream where the events will be serialized to.
+     *
+     * @return reference to the result stream, or null if only a writer was
+     * set.
+     */
+    public OutputStream getOutputStream();
+
+    /**
+     * Specifies a writer to which the document should be serialized.
+     * This method should not be called while the serializer is in
+     * the process of serializing a document.
+     * <p>
+     * The encoding specified for the output {@link Properties} must be
+     * identical to the output format used with the writer.
+     * 
+     * <p>
+     * Only one of setWriter() or setOutputStream() should be called.
+     *
+     * @param writer The output writer stream
+     */
+    public void setWriter(Writer writer);
+
+    /**
+     * Get the character stream where the events will be serialized to.
+     *
+     * @return Reference to the result Writer, or null.
+     */
+    public Writer getWriter();
+
+    /**
+     * Specifies an output format for this serializer. It the
+     * serializer has already been associated with an output format,
+     * it will switch to the new format. This method should not be
+     * called while the serializer is in the process of serializing
+     * a document.
+     * <p>
+     * The standard property keys supported are: "method", "version", "encoding",
+     * "omit-xml-declaration", "standalone", doctype-public",
+     * "doctype-system", "cdata-section-elements", "indent", "media-type". 
+     * These property keys and their values are described in the XSLT recommendation,
+     * see {@link <a href="http://www.w3.org/TR/1999/REC-xslt-19991116"> XSLT 1.0 recommendation</a>}
+     * <p>
+     * The non-standard property keys supported are defined in {@link OutputPropertiesFactory}.
+     *
+     * <p>
+     * This method can be called multiple times before a document is serialized. Each 
+     * time it is called more, or over-riding property values, can be specified. One 
+     * property value that can not be changed is that of the "method" property key.
+     * <p>
+     * The value of the "cdata-section-elements" property key is a whitespace
+     * separated list of elements. If the element is in a namespace then 
+     * value is passed in this format: {uri}localName 
+     * <p>
+     * If the "cdata-section-elements" key is specified on multiple calls
+     * to this method the set of elements specified in the value
+     * is not replaced from one call to the
+     * next, but it is cumulative across the calls.
+     *
+     * @param format The output format to use, as a set of key/value pairs.
+     */
+    public void setOutputFormat(Properties format);
+
+    /**
+     * Returns the output format properties for this serializer.
+     *
+     * @return The output format key/value pairs in use.
+     */
+    public Properties getOutputFormat();
+
+    /**
+     * Return a {@link ContentHandler} interface to provide SAX input to.
+     * Through the returned object the document to be serailized,
+     * as a series of SAX events, can be provided to the serialzier.
+     * If the serializer does not support the {@link ContentHandler}
+     * interface, it will return null.
+     * <p>
+     * In principle only one of asDOMSerializer() or asContentHander() 
+     * should be called.
+     *
+     * @return A {@link ContentHandler} interface into this serializer,
+     *  or null if the serializer is not SAX 2 capable
+     * @throws IOException An I/O exception occured
+     */
+    public ContentHandler asContentHandler() throws IOException;
+
+    /**
+     * Return a {@link DOMSerializer} interface into this serializer.
+     * Through the returned object the document to be serialized,
+     * a DOM, can be provided to the serializer.
+     * If the serializer does not support the {@link DOMSerializer}
+     * interface, it should return null.
+     * <p>
+     * In principle only one of asDOMSerializer() or asContentHander() 
+     * should be called.
+     *
+     * @return A {@link DOMSerializer} interface into this serializer,
+     *  or null if the serializer is not DOM capable
+     * @throws IOException An I/O exception occured
+     */
+    public DOMSerializer asDOMSerializer() throws IOException;
+
+    /**
+     * This method resets the serializer. 
+     * If this method returns true, the
+     * serializer may be used for subsequent serialization of new
+     * documents. It is possible to change the output format and
+     * output stream prior to serializing, or to reuse the existing
+     * output format and output stream or writer.
+     *
+     * @return True if serializer has been reset and can be reused
+     */
+    public boolean reset();
+
+    /**
+     * Return an Object into this serializer to be cast to a DOM3Serializer.
+     * Through the returned object the document to be serialized,
+     * a DOM (Level 3), can be provided to the serializer.
+     * If the serializer does not support casting to a {@link DOM3Serializer}
+     * interface, it should return null.
+     * <p>
+     * In principle only one of asDOM3Serializer() or asContentHander() 
+     * should be called.
+     *
+     * @return An Object to be cast to a DOM3Serializer interface into this serializer,
+     *  or null if the serializer is not DOM capable
+     * @throws IOException An I/O exception occured
+     */
+    public Object asDOM3Serializer() throws IOException;
+}
+
diff --git a/src/main/java/org/apache/xml/serializer/SerializerBase.java b/src/main/java/org/apache/xml/serializer/SerializerBase.java
new file mode 100644
index 0000000..8e7d38b
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/SerializerBase.java
@@ -0,0 +1,1699 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerBase.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Set;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.Transformer;
+
+import org.apache.xml.serializer.utils.MsgKey;
+import org.apache.xml.serializer.utils.Utils;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+
+/**
+ * This class acts as a base class for the XML "serializers"
+ * and the stream serializers.
+ * It contains a number of common fields and methods.
+ * 
+ * @xsl.usage internal
+ */
+public abstract class SerializerBase
+    implements SerializationHandler, SerializerConstants
+{
+    SerializerBase() {
+        return;
+    }
+    
+    /**
+     * The name of the package that this class is in.
+     * <p>
+     * Not a public API.
+     */
+    public static final String PKG_NAME;
+
+    /**
+     * The same as the name of the package that this class is in
+     * except that '.' are replaced with '/'.
+     * <p>
+     * Not a public API.
+     */
+    public static final String PKG_PATH;
+
+    static {
+        String fullyQualifiedName = SerializerBase.class.getName();
+        int lastDot = fullyQualifiedName.lastIndexOf('.');
+        if (lastDot < 0) {
+            PKG_NAME = "";
+        } else {
+            PKG_NAME = fullyQualifiedName.substring(0, lastDot);
+        }
+
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < PKG_NAME.length(); i++) {
+            char ch = PKG_NAME.charAt(i);
+            if (ch == '.')
+                sb.append('/');
+            else
+                sb.append(ch);
+        }
+        PKG_PATH = sb.toString();
+    }
+
+    
+
+    /**
+     * To fire off the end element trace event
+     * @param name Name of element
+     */
+    protected void fireEndElem(String name)
+        throws org.xml.sax.SAXException
+    {
+        if (m_tracer != null)
+        {
+            flushMyWriter();
+            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null);
+        }     	        	    	
+    }
+
+    /**
+     * Report the characters trace event
+     * @param chars  content of characters
+     * @param start  starting index of characters to output
+     * @param length  number of characters to output
+     */
+    protected void fireCharEvent(char[] chars, int start, int length)
+        throws org.xml.sax.SAXException
+    {
+        if (m_tracer != null)
+        {
+            flushMyWriter();
+            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length);
+        }     	        	    	
+    }
+
+    /**
+     * true if we still need to call startDocumentInternal() 
+	 */
+    protected boolean m_needToCallStartDocument = true; 
+
+    /** True if a trailing "]]>" still needs to be written to be
+     * written out. Used to merge adjacent CDATA sections
+     */
+    protected boolean m_cdataTagOpen = false;
+
+    /**
+     * All the attributes of the current element, collected from
+     * startPrefixMapping() calls, or addAddtribute() calls, or 
+     * from the SAX attributes in a startElement() call.
+     */
+    protected AttributesImplSerializer m_attributes = new AttributesImplSerializer();
+
+    /**
+     * Tells if we're in an EntityRef event.
+     */
+    protected boolean m_inEntityRef = false;
+
+    /** This flag is set while receiving events from the external DTD */
+    protected boolean m_inExternalDTD = false;
+
+    /**
+     * The System ID for the doc type.
+     */
+    protected String m_doctypeSystem;
+
+    /**
+     * The public ID for the doc type.
+     */
+    protected String m_doctypePublic;
+
+    /**
+     * Flag to tell that we need to add the doctype decl, which we can't do
+     * until the first element is encountered.
+     */
+    boolean m_needToOutputDocTypeDecl = true;
+
+    /**
+     * Tells if we should write the XML declaration.
+     */
+    protected boolean m_shouldNotWriteXMLHeader = false;
+
+    /**
+     * The standalone value for the doctype.
+     */
+    private String m_standalone;
+
+    /**
+     * True if standalone was specified.
+     */
+    protected boolean m_standaloneWasSpecified = false;
+
+    /**
+     * Flag to tell if indenting (pretty-printing) is on.
+     */
+    protected boolean m_doIndent = false;
+    /**
+     * Amount to indent.
+     */
+    protected int m_indentAmount = 0;
+
+    /**
+     * Tells the XML version, for writing out to the XML decl.
+     */
+    protected String m_version = null;
+
+    /**
+     * The mediatype.  Not used right now.
+     */
+    protected String m_mediatype;
+
+    /**
+     * The transformer that was around when this output handler was created (if
+     * any).
+     */
+    private Transformer m_transformer;
+
+    /**
+     * Namespace support, that keeps track of currently defined 
+     * prefix/uri mappings. As processed elements come and go, so do
+     * the associated mappings for that element.
+     */
+    protected NamespaceMappings m_prefixMap;
+    
+    /**
+     * Handle for firing generate events.  This interface may be implemented
+     * by the referenced transformer object.
+     */
+    protected SerializerTrace m_tracer;
+    
+    protected SourceLocator m_sourceLocator;
+    
+
+    /**
+     * The writer to send output to. This field is only used in the ToStream
+     * serializers, but exists here just so that the fireStartDoc() and
+     * other fire... methods can flush this writer when tracing.
+     */
+    protected java.io.Writer m_writer = null;
+    
+    /**
+     * A reference to "stack frame" corresponding to
+     * the current element. Such a frame is pushed at a startElement()
+     * and popped at an endElement(). This frame contains information about
+     * the element, such as its namespace URI. 
+     */
+    protected ElemContext m_elemContext = new ElemContext();
+    
+    /**
+     * A utility buffer for converting Strings passed to
+     * character() methods to character arrays.
+     * Reusing this buffer means not creating a new character array
+     * everytime and it runs faster.
+     */
+    protected char[] m_charsBuff = new char[60];
+    
+    /**
+     * A utility buffer for converting Strings passed to
+     * attribute methods to character arrays.
+     * Reusing this buffer means not creating a new character array
+     * everytime and it runs faster.
+     */
+    protected char[] m_attrBuff = new char[30];    
+
+    /**
+     * Receive notification of a comment.
+     * 
+     * @see ExtendedLexicalHandler#comment(String)
+     */
+    public void comment(String data) throws SAXException
+    {
+        m_docIsEmpty = false;
+        
+        final int length = data.length();
+        if (length > m_charsBuff.length)
+        {
+            m_charsBuff = new char[length * 2 + 1];
+        }
+        data.getChars(0, length, m_charsBuff, 0);
+        comment(m_charsBuff, 0, length);
+    }
+
+    /**
+     * If at runtime, when the qname of the attribute is
+     * known, another prefix is specified for the attribute, then we can
+     * patch or hack the name with this method. For
+     * a qname of the form "ns?:otherprefix:name", this function patches the
+     * qname by simply ignoring "otherprefix".
+     * TODO: This method is a HACK! We do not have access to the
+     * XML file, it sometimes generates a NS prefix of the form "ns?" for
+     * an attribute.
+     */
+    protected String patchName(String qname)
+    {
+
+        
+        final int lastColon = qname.lastIndexOf(':');
+
+        if (lastColon > 0) {
+            final int firstColon = qname.indexOf(':');
+            final String prefix = qname.substring(0, firstColon);
+            final String localName = qname.substring(lastColon + 1);
+
+        // If uri is "" then ignore prefix
+            final String uri = m_prefixMap.lookupNamespace(prefix);
+            if (uri != null && uri.length() == 0) {
+                return localName;
+            }
+            else if (firstColon != lastColon) {
+                return prefix + ':' + localName;
+            }
+        }
+        return qname;        
+    }
+
+    /**
+     * Returns the local name of a qualified name. If the name has no prefix,
+     * then it works as the identity (SAX2).
+     * @param qname the qualified name 
+     * @return the name, but excluding any prefix and colon.
+     */
+    protected static String getLocalName(String qname)
+    {
+        final int col = qname.lastIndexOf(':');
+        return (col > 0) ? qname.substring(col + 1) : qname;
+    }
+
+    /**
+     * Receive an object for locating the origin of SAX document events.
+     *
+     * @param locator An object that can return the location of any SAX document
+     * event.
+     * 
+     * Receive an object for locating the origin of SAX document events.
+     *
+     * <p>SAX parsers are strongly encouraged (though not absolutely
+     * required) to supply a locator: if it does so, it must supply
+     * the locator to the application by invoking this method before
+     * invoking any of the other methods in the DocumentHandler
+     * interface.</p>
+     *
+     * <p>The locator allows the application to determine the end
+     * position of any document-related event, even if the parser is
+     * not reporting an error.  Typically, the application will
+     * use this information for reporting its own errors (such as
+     * character content that does not match an application's
+     * business rules).  The information returned by the locator
+     * is probably not sufficient for use with a search engine.</p>
+     *
+     * <p>Note that the locator will return correct information only
+     * during the invocation of the events in this interface.  The
+     * application should not attempt to use it at any other time.</p>
+     */
+    public void setDocumentLocator(Locator locator)
+    {
+        return;
+
+        // I don't do anything with this yet.
+    }
+
+    /**
+     * Adds the given attribute to the set of collected attributes , but only if
+     * there is a currently open element.
+     * 
+     * An element is currently open if a startElement() notification has
+     * occured but the start of the element has not yet been written to the
+     * output.  In the stream case this means that we have not yet been forced
+     * to close the elements opening tag by another notification, such as a
+     * character notification.
+     * 
+     * @param uri the URI of the attribute
+     * @param localName the local name of the attribute
+     * @param rawName    the qualified name of the attribute
+     * @param type the type of the attribute (probably CDATA)
+     * @param value the value of the attribute
+     * @param XSLAttribute true if this attribute is coming from an xsl:attriute element
+     * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean XSLAttribute)
+        throws SAXException
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+            addAttributeAlways(uri, localName, rawName, type, value, XSLAttribute);
+        }
+
+    }
+    
+    /**
+     * Adds the given attribute to the set of attributes, even if there is
+     * no currently open element. This is useful if a SAX startPrefixMapping()
+     * should need to add an attribute before the element name is seen.
+     * 
+     * @param uri the URI of the attribute
+     * @param localName the local name of the attribute
+     * @param rawName   the qualified name of the attribute
+     * @param type the type of the attribute (probably CDATA)
+     * @param value the value of the attribute
+     * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
+     * @return true if the attribute was added, 
+     * false if an existing value was replaced.
+     */
+    public boolean addAttributeAlways(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean XSLAttribute)
+    {
+        boolean was_added;
+//            final int index =
+//                (localName == null || uri == null) ?
+//                m_attributes.getIndex(rawName):m_attributes.getIndex(uri, localName);        
+            int index;
+//            if (localName == null || uri == null){
+//                index = m_attributes.getIndex(rawName);
+//            }
+//            else {
+//                index = m_attributes.getIndex(uri, localName);
+//            }
+            if (localName == null || uri == null || uri.length() == 0)
+                index = m_attributes.getIndex(rawName);
+            else {
+                index = m_attributes.getIndex(uri,localName);
+            }
+            if (index >= 0)
+            {
+                /* We've seen the attribute before.
+                 * We may have a null uri or localName, but all
+                 * we really want to re-set is the value anyway.
+                 */
+                m_attributes.setValue(index,value);
+                was_added = false;
+            }
+            else
+            {
+                // the attribute doesn't exist yet, create it
+                m_attributes.addAttribute(uri, localName, rawName, type, value);
+                was_added = true;
+            }
+            return was_added;
+        
+    }
+  
+
+    /**
+     *  Adds  the given attribute to the set of collected attributes, 
+     * but only if there is a currently open element.
+     *
+     * @param name the attribute's qualified name
+     * @param value the value of the attribute
+     */
+    public void addAttribute(String name, final String value)
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+            final String patchedName = patchName(name);
+            final String localName = getLocalName(patchedName);
+            final String uri = getNamespaceURI(patchedName, false);
+
+            addAttributeAlways(uri,localName, patchedName, "CDATA", value, false);
+         }
+    }    
+
+    /**
+     * Adds the given xsl:attribute to the set of collected attributes, 
+     * but only if there is a currently open element.
+     *
+     * @param name the attribute's qualified name (prefix:localName)
+     * @param value the value of the attribute
+     * @param uri the URI that the prefix of the name points to
+     */
+    public void addXSLAttribute(String name, final String value, final String uri)
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+            final String patchedName = patchName(name);
+            final String localName = getLocalName(patchedName);
+
+            addAttributeAlways(uri,localName, patchedName, "CDATA", value, true);
+         }
+    } 
+
+    /**
+     * Add the given attributes to the currently collected ones. These
+     * attributes are always added, regardless of whether on not an element
+     * is currently open.
+     * @param atts List of attributes to add to this list
+     */
+    public void addAttributes(Attributes atts) throws SAXException
+    {
+
+        int nAtts = atts.getLength();
+
+        for (int i = 0; i < nAtts; i++)
+        {
+            String uri = atts.getURI(i);
+
+            if (null == uri)
+                uri = "";
+
+            addAttributeAlways(
+                uri,
+                atts.getLocalName(i),
+                atts.getQName(i),
+                atts.getType(i),
+                atts.getValue(i),
+                false);
+
+        }
+    }
+
+    /**
+     * Return a {@link ContentHandler} interface into this serializer.
+     * If the serializer does not support the {@link ContentHandler}
+     * interface, it should return null.
+     *
+     * @return A {@link ContentHandler} interface into this serializer,
+     *  or null if the serializer is not SAX 2 capable
+     * @throws IOException An I/O exception occured
+     */
+    public ContentHandler asContentHandler() throws IOException
+    {
+        return this;
+    }
+
+    /**
+     * Report the end of an entity.
+     *
+     * @param name The name of the entity that is ending.
+     * @throws org.xml.sax.SAXException The application may raise an exception.
+     * @see #startEntity
+     */
+    public void endEntity(String name) throws org.xml.sax.SAXException
+    {
+        if (name.equals("[dtd]"))
+            m_inExternalDTD = false;
+        m_inEntityRef = false;
+
+        if (m_tracer != null)
+            this.fireEndEntity(name);        
+    }
+
+    /**
+     * Flush and close the underlying java.io.Writer. This method applies to
+     * ToStream serializers, not ToSAXHandler serializers.
+     * @see ToStream
+     */
+    public void close()
+    {
+        // do nothing (base behavior)
+    }
+
+    /**
+     * Initialize global variables
+     */
+    protected void initCDATA()
+    {
+        // CDATA stack
+        //        _cdataStack = new Stack();
+        //        _cdataStack.push(new Integer(-1)); // push dummy value
+    }
+
+    /**
+     * Returns the character encoding to be used in the output document.
+     * @return the character encoding to be used in the output document.
+     */
+    public String getEncoding()
+    {
+        return getOutputProperty(OutputKeys.ENCODING);
+    }
+
+   /**
+     * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
+     * @param m_encoding the character encoding
+     */
+    public void setEncoding(String encoding)
+    {
+        setOutputProperty(OutputKeys.ENCODING,encoding);
+    }
+
+    /**
+     * Sets the value coming from the xsl:output omit-xml-declaration stylesheet attribute
+     * @param b true if the XML declaration is to be omitted from the output
+     * document.
+     */
+    public void setOmitXMLDeclaration(boolean b)
+    {
+        String val = b ? "yes":"no";
+        setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,val);
+    }
+
+
+    /**
+     * @return true if the XML declaration is to be omitted from the output
+     * document.
+     */
+    public boolean getOmitXMLDeclaration()
+    {
+        return m_shouldNotWriteXMLHeader;
+    }
+
+    /**
+     * Returns the previously set value of the value to be used as the public
+     * identifier in the document type declaration (DTD).
+     * 
+     *@return the public identifier to be used in the DOCTYPE declaration in the
+     * output document.
+     */    
+    public String getDoctypePublic()
+    {
+        return m_doctypePublic;
+    }
+
+    /** Set the value coming from the xsl:output doctype-public stylesheet attribute.
+      * @param doctypePublic the public identifier to be used in the DOCTYPE
+      * declaration in the output document.
+      */
+    public void setDoctypePublic(String doctypePublic)
+    {
+        setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctypePublic);
+    }
+
+
+    /**
+     * Returns the previously set value of the value to be used
+     * as the system identifier in the document type declaration (DTD).
+	 * @return the system identifier to be used in the DOCTYPE declaration in
+	 * the output document.
+     *
+     */    
+    public String getDoctypeSystem()
+    {
+        return m_doctypeSystem;
+    }
+
+    /** Set the value coming from the xsl:output doctype-system stylesheet attribute.
+      * @param doctypeSystem the system identifier to be used in the DOCTYPE
+      * declaration in the output document.
+      */
+    public void setDoctypeSystem(String doctypeSystem)
+    {
+        setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem);
+    }
+
+    /** Set the value coming from the xsl:output doctype-public and doctype-system stylesheet properties
+     * @param doctypeSystem the system identifier to be used in the DOCTYPE
+     * declaration in the output document.
+     * @param doctypePublic the public identifier to be used in the DOCTYPE
+     * declaration in the output document.
+     */
+    public void setDoctype(String doctypeSystem, String doctypePublic)
+    {
+        setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem);
+        setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctypePublic);
+    }
+
+    /**
+     * Sets the value coming from the xsl:output standalone stylesheet attribute.
+     * @param standalone a value of "yes" indicates that the
+     * <code>standalone</code> delaration is to be included in the output
+     * document. This method remembers if the value was explicitly set using
+     * this method, verses if the value is the default value.
+     */
+    public void setStandalone(String standalone)
+    {
+        setOutputProperty(OutputKeys.STANDALONE, standalone);
+    }
+    /**
+     * Sets the XSL standalone attribute, but does not remember if this is a
+     * default or explicite setting.
+     * @param standalone "yes" | "no"
+     */    
+    protected void setStandaloneInternal(String standalone)
+    {
+        if ("yes".equals(standalone))
+            m_standalone = "yes";
+        else
+            m_standalone = "no";
+        
+    }
+
+    /**
+     * Gets the XSL standalone attribute
+     * @return a value of "yes" if the <code>standalone</code> delaration is to
+     * be included in the output document.
+     *  @see XSLOutputAttributes#getStandalone()
+     */
+    public String getStandalone()
+    {
+        return m_standalone;
+    }
+
+    /**
+     * @return true if the output document should be indented to visually
+     * indicate its structure.
+     */
+    public boolean getIndent()
+    {
+        return m_doIndent;
+    }
+    /**
+     * Gets the mediatype the media-type or MIME type associated with the output
+     * document.
+     * @return the mediatype the media-type or MIME type associated with the
+     * output document.
+     */
+    public String getMediaType()
+    {
+        return m_mediatype;
+    }
+
+    /**
+     * Gets the version of the output format.
+     * @return the version of the output format.
+     */
+    public String getVersion()
+    {
+        return m_version;
+    }
+
+    /**
+     * Sets the value coming from the xsl:output version attribute.
+     * @param version the version of the output format.
+     * @see SerializationHandler#setVersion(String)
+     */
+    public void setVersion(String version)
+    {
+        setOutputProperty(OutputKeys.VERSION, version);
+    }
+
+    /**
+     * Sets the value coming from the xsl:output media-type stylesheet attribute.
+     * @param mediaType the non-null media-type or MIME type associated with the
+     * output document.
+     * @see javax.xml.transform.OutputKeys#MEDIA_TYPE
+     * @see SerializationHandler#setMediaType(String)
+     */
+    public void setMediaType(String mediaType)
+    {
+        setOutputProperty(OutputKeys.MEDIA_TYPE,mediaType);
+    }
+
+    /**
+     * @return the number of spaces to indent for each indentation level.
+     */
+    public int getIndentAmount()
+    {
+        return m_indentAmount;
+    }
+
+    /**
+     * Sets the indentation amount.
+     * @param m_indentAmount The m_indentAmount to set
+     */
+    public void setIndentAmount(int m_indentAmount)
+    {
+        this.m_indentAmount = m_indentAmount;
+    }
+
+    /**
+     * Sets the value coming from the xsl:output indent stylesheet
+     * attribute.
+     * @param doIndent true if the output document should be indented to
+     * visually indicate its structure.
+     * @see XSLOutputAttributes#setIndent(boolean)
+     */
+    public void setIndent(boolean doIndent)
+    {
+        String val = doIndent ? "yes":"no";
+        setOutputProperty(OutputKeys.INDENT,val);
+    }
+
+    /**
+     * This method is used when a prefix/uri namespace mapping
+     * is indicated after the element was started with a 
+     * startElement() and before and endElement().
+     * startPrefixMapping(prefix,uri) would be used before the
+     * startElement() call.
+     * @param uri the URI of the namespace
+     * @param prefix the prefix associated with the given URI.
+     * 
+     * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
+     */
+    public void namespaceAfterStartElement(String uri, String prefix)
+        throws SAXException
+    {
+        // default behavior is to do nothing
+    }
+
+    /**
+     * Return a {@link DOMSerializer} interface into this serializer. If the
+     * serializer does not support the {@link DOMSerializer} interface, it should
+     * return null.
+     *
+     * @return A {@link DOMSerializer} interface into this serializer,  or null
+     * if the serializer is not DOM capable
+     * @throws IOException An I/O exception occured
+     * @see Serializer#asDOMSerializer()
+     */
+    public DOMSerializer asDOMSerializer() throws IOException
+    { 
+        return this;
+    }
+
+    /**
+     * Tell if two strings are equal, without worry if the first string is null.
+     *
+     * @param p String reference, which may be null.
+     * @param t String reference, which may be null.
+     *
+     * @return true if strings are equal.
+     */
+    private static final boolean subPartMatch(String p, String t)
+    {
+        return (p == t) || ((null != p) && (p.equals(t)));
+    }
+
+    /**
+     * Returns the local name of a qualified name. 
+     * If the name has no prefix,
+     * then it works as the identity (SAX2). 
+     * 
+     * @param qname a qualified name
+     * @return returns the prefix of the qualified name,
+     * or null if there is no prefix.
+     */
+    protected static final String getPrefixPart(String qname)
+    {
+        final int col = qname.indexOf(':');
+        return (col > 0) ? qname.substring(0, col) : null;
+        //return (col > 0) ? qname.substring(0,col) : "";
+    }
+
+    /**
+     * Some users of the serializer may need the current namespace mappings
+     * @return the current namespace mappings (prefix/uri)
+     * @see ExtendedContentHandler#getNamespaceMappings()
+     */
+    public NamespaceMappings getNamespaceMappings()
+    {
+        return m_prefixMap;
+    }
+
+    /**
+     * Returns the prefix currently pointing to the given URI (if any).
+     * @param namespaceURI the uri of the namespace in question
+     * @return a prefix pointing to the given URI (if any).
+     * @see ExtendedContentHandler#getPrefix(String)
+     */
+    public String getPrefix(String namespaceURI)
+    {
+        String prefix = m_prefixMap.lookupPrefix(namespaceURI);
+        return prefix;
+    }
+
+    /**
+     * Returns the URI of an element or attribute. Note that default namespaces
+     * do not apply directly to attributes.
+     * @param qname a qualified name
+     * @param isElement true if the qualified name is the name of 
+     * an element.
+     * @return returns the namespace URI associated with the qualified name.
+     */
+    public String getNamespaceURI(String qname, boolean isElement)
+    {
+        String uri = EMPTYSTRING;
+        int col = qname.lastIndexOf(':');
+        final String prefix = (col > 0) ? qname.substring(0, col) : EMPTYSTRING;
+
+        if (!EMPTYSTRING.equals(prefix) || isElement)
+        {
+            if (m_prefixMap != null)
+            {
+                uri = m_prefixMap.lookupNamespace(prefix);
+                if (uri == null && !prefix.equals(XMLNS_PREFIX))
+                {
+                    throw new RuntimeException(
+                        Utils.messages.createMessage(
+                            MsgKey.ER_NAMESPACE_PREFIX,
+                            new Object[] { qname.substring(0, col) }  ));
+                }
+            }
+        }
+        return uri;
+    }
+
+    /**
+     * Returns the URI of prefix (if any)
+     * 
+	 * @param prefix the prefix whose URI is searched for
+     * @return the namespace URI currently associated with the
+     * prefix, null if the prefix is undefined.
+     */
+    public String getNamespaceURIFromPrefix(String prefix)
+    {
+        String uri = null;
+        if (m_prefixMap != null)
+            uri = m_prefixMap.lookupNamespace(prefix);
+        return uri;
+    }
+
+    /**
+     * Entity reference event.
+     *
+     * @param name Name of entity
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void entityReference(String name) throws org.xml.sax.SAXException
+    {
+
+        flushPending();
+
+        startEntity(name);
+        endEntity(name);
+
+        if (m_tracer != null)
+		    fireEntityReference(name);
+    }
+
+    /**
+     * Sets the transformer associated with this serializer
+     * @param t the transformer associated with this serializer.
+     * @see SerializationHandler#setTransformer(Transformer)
+     */
+    public void setTransformer(Transformer t)
+    {
+        m_transformer = t;
+        
+        // If this transformer object implements the SerializerTrace interface
+        // then assign m_tracer to the transformer object so it can be used
+        // to fire trace events.
+        if ((m_transformer instanceof SerializerTrace) &&
+            (((SerializerTrace) m_transformer).hasTraceListeners())) {
+           m_tracer = (SerializerTrace) m_transformer;
+        } else {
+           m_tracer = null;
+        }
+    }
+    /**
+     * Gets the transformer associated with this serializer
+     * @return returns the transformer associated with this serializer.
+     * @see SerializationHandler#getTransformer()
+     */
+    public Transformer getTransformer()
+    {
+        return m_transformer;
+    }
+    
+    /**
+     * This method gets the nodes value as a String and uses that String as if
+     * it were an input character notification.
+     * @param node the Node to serialize
+     * @throws org.xml.sax.SAXException
+     */
+    public void characters(org.w3c.dom.Node node)
+        throws org.xml.sax.SAXException
+    {
+        flushPending();
+        String data = node.getNodeValue();
+        if (data != null)
+        {
+            final int length = data.length();
+            if (length > m_charsBuff.length)
+            {
+                m_charsBuff = new char[length * 2 + 1];
+            }
+            data.getChars(0, length, m_charsBuff, 0);
+            characters(m_charsBuff, 0, length);
+        }
+    }
+    
+
+    /**
+     * @see org.xml.sax.ErrorHandler#error(SAXParseException)
+     */
+    public void error(SAXParseException exc) throws SAXException {
+    }
+
+    /**
+     * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
+     */
+    public void fatalError(SAXParseException exc) throws SAXException {
+        
+      m_elemContext.m_startTagOpen = false;
+
+    }
+
+    /**
+     * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
+     */
+    public void warning(SAXParseException exc) throws SAXException 
+    {
+    }
+
+    /**
+     * To fire off start entity trace event
+     * @param name Name of entity
+     */
+    protected void fireStartEntity(String name)
+        throws org.xml.sax.SAXException
+    {        
+        if (m_tracer != null)
+        {
+            flushMyWriter();
+            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF, name);
+        }     	        	    	
+    }
+
+    /**
+     * Report the characters event
+     * @param chars  content of characters
+     * @param start  starting index of characters to output
+     * @param length  number of characters to output
+     */
+//    protected void fireCharEvent(char[] chars, int start, int length)
+//        throws org.xml.sax.SAXException
+//    {
+//        if (m_tracer != null)
+//            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length);     	        	    	
+//    }
+//        
+
+    /**
+     * This method is only used internally when flushing the writer from the
+     * various fire...() trace events.  Due to the writer being wrapped with 
+     * SerializerTraceWriter it may cause the flush of these trace events:
+     * EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS 
+     * EVENTTYPE_OUTPUT_CHARACTERS
+     * which trace the output written to the output stream.
+     * 
+     */
+    private void flushMyWriter()
+    {
+        if (m_writer != null)
+        {
+            try
+            {
+                m_writer.flush();
+            }
+            catch(IOException ioe)
+            {
+            
+            }
+        }
+    }
+    /**
+     * Report the CDATA trace event
+     * @param chars  content of CDATA
+     * @param start  starting index of characters to output
+     * @param length  number of characters to output
+     */
+    protected void fireCDATAEvent(char[] chars, int start, int length)
+        throws org.xml.sax.SAXException
+    {
+		if (m_tracer != null)
+        {
+            flushMyWriter();
+			m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CDATA, chars, start,length);
+        }     	        	    	
+    }
+
+    /**
+     * Report the comment trace event
+     * @param chars  content of comment
+     * @param start  starting index of comment to output
+     * @param length  number of characters to output
+     */
+    protected void fireCommentEvent(char[] chars, int start, int length)
+        throws org.xml.sax.SAXException
+    {
+		if (m_tracer != null)
+        {
+            flushMyWriter();
+			m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_COMMENT, new String(chars, start, length));
+        }     	        	    	
+    }
+
+
+    /**
+     * To fire off end entity trace event
+     * @param name Name of entity
+     */
+    public void fireEndEntity(String name)
+        throws org.xml.sax.SAXException
+    {
+        if (m_tracer != null)
+            flushMyWriter();
+    	// we do not need to handle this.
+    }    
+
+    /**
+     * To fire off start document trace  event
+     */
+     protected void fireStartDoc()
+        throws org.xml.sax.SAXException
+    {
+        if (m_tracer != null)
+        {
+            flushMyWriter();
+            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTDOCUMENT);
+        }     	    
+    }    
+
+
+    /**
+     * To fire off end document trace event
+     */
+    protected void fireEndDoc()
+        throws org.xml.sax.SAXException
+    {
+        if (m_tracer != null)
+        {
+            flushMyWriter();
+            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDDOCUMENT);
+        }     	        	
+    }    
+    
+    /**
+     * Report the start element trace event. This trace method needs to be
+     * called just before the attributes are cleared.
+     * 
+     * @param elemName the qualified name of the element
+     * 
+     */
+    protected void fireStartElem(String elemName)
+        throws org.xml.sax.SAXException
+    {        
+        if (m_tracer != null)
+        {
+            flushMyWriter();
+            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTELEMENT,
+                elemName, m_attributes);     	 
+        }       	
+    }    
+
+
+    /**
+     * To fire off the end element event
+     * @param name Name of element
+     */
+//    protected void fireEndElem(String name)
+//        throws org.xml.sax.SAXException
+//    {
+//        if (m_tracer != null)
+//            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null);     	        	    	
+//    }    
+
+
+    /**
+     * To fire off the PI trace event
+     * @param name Name of PI
+     */
+    protected void fireEscapingEvent(String name, String data)
+        throws org.xml.sax.SAXException
+    {
+
+        if (m_tracer != null)
+        {
+            flushMyWriter();
+            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_PI,name, data);
+        }     	        	    	
+    }    
+
+
+    /**
+     * To fire off the entity reference trace event
+     * @param name Name of entity reference
+     */
+    protected void fireEntityReference(String name)
+        throws org.xml.sax.SAXException
+    {
+        if (m_tracer != null)
+        {
+            flushMyWriter();
+            m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF,name, (Attributes)null);
+        }     	        	    	
+    }    
+
+    /**
+     * Receive notification of the beginning of a document.
+     * This method is never a self generated call, 
+     * but only called externally.
+     *
+     * <p>The SAX parser will invoke this method only once, before any
+     * other methods in this interface or in DTDHandler (except for
+     * setDocumentLocator).</p>
+     *
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void startDocument() throws org.xml.sax.SAXException
+    {
+
+        // if we do get called with startDocument(), handle it right away       
+        startDocumentInternal();
+        m_needToCallStartDocument = false;
+        return;
+    }   
+    
+    /**
+     * This method handles what needs to be done at a startDocument() call,
+     * whether from an external caller, or internally called in the 
+     * serializer.  For historical reasons the serializer is flexible to
+     * startDocument() not always being called.
+     * Even if no external call is
+     * made into startDocument() this method will always be called as a self
+     * generated internal startDocument, it handles what needs to be done at a
+     * startDocument() call.
+     * 
+     * This method exists just to make sure that startDocument() is only ever
+     * called from an external caller, which in principle is just a matter of
+     * style.
+     * 
+     * @throws SAXException
+     */
+    protected void startDocumentInternal() throws org.xml.sax.SAXException
+    {
+        if (m_tracer != null)
+            this.fireStartDoc();
+    } 
+    /**
+     * This method is used to set the source locator, which might be used to
+     * generated an error message.
+     * @param locator the source locator
+     *
+     * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator)
+     */
+    public void setSourceLocator(SourceLocator locator)
+    {
+        m_sourceLocator = locator;    
+    }
+
+    
+    /** 
+     * Used only by TransformerSnapshotImpl to restore the serialization 
+     * to a previous state. 
+     * 
+     * @param mappings NamespaceMappings
+     */
+    public void setNamespaceMappings(NamespaceMappings mappings) {
+        m_prefixMap = mappings;
+    }
+    
+    public boolean reset()
+    {
+    	resetSerializerBase();
+    	return true;
+    }
+    
+    /**
+     * Reset all of the fields owned by SerializerBase
+     *
+     */
+    private void resetSerializerBase()
+    {
+    	this.m_attributes.clear();
+        this.m_CdataElems = null;
+        this.m_cdataTagOpen = false;
+        this.m_docIsEmpty = true;
+    	this.m_doctypePublic = null;
+    	this.m_doctypeSystem = null;
+    	this.m_doIndent = false;
+        this.m_elemContext = new ElemContext();
+    	this.m_indentAmount = 0;
+    	this.m_inEntityRef = false;
+    	this.m_inExternalDTD = false;
+    	this.m_mediatype = null;
+    	this.m_needToCallStartDocument = true;
+    	this.m_needToOutputDocTypeDecl = false;
+        if (m_OutputProps != null)
+            this.m_OutputProps.clear();
+        if (m_OutputPropsDefault != null)
+            this.m_OutputPropsDefault.clear();
+        if (this.m_prefixMap != null)
+    	    this.m_prefixMap.reset();
+    	this.m_shouldNotWriteXMLHeader = false;
+    	this.m_sourceLocator = null;
+    	this.m_standalone = null;
+    	this.m_standaloneWasSpecified = false;
+        this.m_StringOfCDATASections = null;
+    	this.m_tracer = null;
+    	this.m_transformer = null;
+    	this.m_version = null;
+    	// don't set writer to null, so that it might be re-used
+    	//this.m_writer = null;
+    }
+    
+    /**
+     * Returns true if the serializer is used for temporary output rather than
+     * final output.
+     * 
+     * This concept is made clear in the XSLT 2.0 draft.
+     */
+    final boolean inTemporaryOutputState() 
+    {
+        /* This is a hack. We should really be letting the serializer know
+         * that it is in temporary output state with an explicit call, but
+         * from a pragmatic point of view (for now anyways) having no output
+         * encoding at all, not even the default UTF-8 indicates that the serializer
+         * is being used for temporary RTF.
+         */ 
+        return (getEncoding() == null);
+        
+    }
+    
+    /**
+     * This method adds an attribute the the current element,
+     * but should not be used for an xsl:attribute child.
+     * @see ExtendedContentHandler#addAttribute(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+     */
+    public void addAttribute(String uri, String localName, String rawName, String type, String value) throws SAXException 
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+            addAttributeAlways(uri, localName, rawName, type, value, false);
+        }
+    }
+    
+    /**
+     * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
+     */
+    public void notationDecl(String arg0, String arg1, String arg2)
+        throws SAXException {
+        // This method just provides a definition to satisfy the interface
+        // A particular sub-class of SerializerBase provides the implementation (if desired)        
+    }
+
+    /**
+     * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+     */
+    public void unparsedEntityDecl(
+        String arg0,
+        String arg1,
+        String arg2,
+        String arg3)
+        throws SAXException {
+        // This method just provides a definition to satisfy the interface
+        // A particular sub-class of SerializerBase provides the implementation (if desired)        
+    }
+
+    /**
+     * If set to false the serializer does not expand DTD entities,
+     * but leaves them as is, the default value is true.
+     */
+    public void setDTDEntityExpansion(boolean expand) {
+        // This method just provides a definition to satisfy the interface
+        // A particular sub-class of SerializerBase provides the implementation (if desired)        
+    }
+ 
+
+    /** 
+     * The CDATA section names stored in a whitespace separateed list with
+     * each element being a word of the form "{uri}localName" This list
+     * comes from the cdata-section-elements attribute.
+     * 
+     * This field replaces m_cdataSectionElements Vector.
+     */
+    protected String m_StringOfCDATASections = null; 
+    
+    boolean m_docIsEmpty = true;
+    void initCdataElems(String s)
+    {
+        if (s != null)
+        {            
+            int max = s.length();
+
+            // true if we are in the middle of a pair of curly braces that delimit a URI
+            boolean inCurly = false;
+
+            // true if we found a URI but haven't yet processed the local name 
+            boolean foundURI = false;
+
+            StringBuffer buf = new StringBuffer();
+            String uri = null;
+            String localName = null;
+
+            // parse through string, breaking on whitespaces.  I do this instead
+            // of a tokenizer so I can track whitespace inside of curly brackets,
+            // which theoretically shouldn't happen if they contain legal URLs.
+
+
+            for (int i = 0; i < max; i++)
+            {
+
+                char c = s.charAt(i);
+
+                if (Character.isWhitespace(c))
+                {
+                    if (!inCurly)
+                    {
+                        if (buf.length() > 0)
+                        {
+                            localName = buf.toString();
+                            if (!foundURI)
+                                uri = "";
+                            addCDATAElement(uri,localName);
+                            buf.setLength(0);
+                            foundURI = false;
+                        }
+                        continue;
+                    }
+                    else
+                        buf.append(c); // add whitespace to the URI
+                }
+                else if ('{' == c) // starting a URI
+                    inCurly = true;
+                else if ('}' == c)
+                {
+                    // we just ended a URI, add the URI to the vector
+                    foundURI = true;
+                    uri = buf.toString();
+                    buf.setLength(0);
+                    inCurly = false;
+                }
+                else
+                {
+                    // append non-whitespace, non-curly to current URI or localName being gathered.                    
+                    buf.append(c);
+                }
+
+            }
+
+            if (buf.length() > 0)
+            {
+                // We have one last localName to process.
+                localName = buf.toString();
+                if (!foundURI)
+                    uri = "";
+                addCDATAElement(uri,localName);
+            }
+        }
+    }
+    protected java.util.Hashtable m_CdataElems = null;
+    private void addCDATAElement(String uri, String localName) 
+    {
+        if (m_CdataElems == null) {
+            m_CdataElems = new java.util.Hashtable();
+        }
+        
+        java.util.Hashtable h = (java.util.Hashtable) m_CdataElems.get(localName);
+        if (h == null) {
+            h = new java.util.Hashtable();
+            m_CdataElems.put(localName,h);
+        }
+        h.put(uri,uri);
+        
+    }
+    
+    
+    /**
+     * Return true if nothing has been sent to this result tree yet.
+     * <p>
+     * This is not a public API.
+     * 
+     * @xsl.usage internal
+     */
+    public boolean documentIsEmpty() {
+        // If we haven't called startDocument() yet, then this document is empty
+        return m_docIsEmpty && (m_elemContext.m_currentElemDepth == 0);
+    }    
+    
+    /**
+     * Return true if the current element in m_elemContext
+     * is a CDATA section.
+     * CDATA sections are specified in the <xsl:output> attribute
+     * cdata-section-names or in the JAXP equivalent property.
+     * In any case the format of the value of such a property is:
+     * <pre>
+     * "{uri1}localName1 {uri2}localName2 . . . "
+     * </pre>
+     * 
+     * <p>
+     * This method is not a public API, but is only used internally by the serializer.
+     */
+    protected boolean isCdataSection()
+    {
+
+        boolean b = false;
+
+        if (null != m_StringOfCDATASections)
+        {
+            if (m_elemContext.m_elementLocalName == null) 
+            {
+                String localName =  getLocalName(m_elemContext.m_elementName); 
+                m_elemContext.m_elementLocalName = localName;                   
+            }
+            
+            if ( m_elemContext.m_elementURI == null) {
+                
+                m_elemContext.m_elementURI = getElementURI();
+            }
+            else if ( m_elemContext.m_elementURI.length() == 0) {
+                if ( m_elemContext.m_elementName == null) {
+                    m_elemContext.m_elementName = m_elemContext.m_elementLocalName;    
+                    // leave URI as "", meaning in no namespace
+                }
+                else if (m_elemContext.m_elementLocalName.length() < m_elemContext.m_elementName.length()){
+                    // We were told the URI was "", yet the name has a prefix since the name is longer than the localname.
+                    // So we will fix that incorrect information here.
+                    m_elemContext.m_elementURI = getElementURI();  
+                }
+            }
+
+            java.util.Hashtable h = (java.util.Hashtable) m_CdataElems.get(m_elemContext.m_elementLocalName);
+            if (h != null) 
+            {
+                Object obj = h.get(m_elemContext.m_elementURI);
+                if (obj != null)
+                    b = true; 
+            }
+
+        }
+        return b;
+    }
+    
+    /**
+     * Before this call m_elementContext.m_elementURI is null,
+     * which means it is not yet known. After this call it
+     * is non-null, but possibly "" meaning that it is in the
+     * default namespace.
+     * 
+     * @return The URI of the element, never null, but possibly "".
+     */
+    private String getElementURI() {
+        String uri = null;
+        // At this point in processing we have received all the
+        // namespace mappings
+        // As we still don't know the elements namespace,
+        // we now figure it out.
+
+        String prefix = getPrefixPart(m_elemContext.m_elementName);
+
+        if (prefix == null) {
+            // no prefix so lookup the URI of the default namespace
+            uri = m_prefixMap.lookupNamespace("");
+        } else {
+            uri = m_prefixMap.lookupNamespace(prefix);
+        }
+        if (uri == null) {
+            // We didn't find the namespace for the
+            // prefix ... ouch, that shouldn't happen.
+            // This is a hack, we really don't know
+            // the namespace
+            uri = EMPTYSTRING;
+        }
+
+        return uri;
+    }
+    
+
+    /**
+     * Get the value of an output property,
+     * the explicit value, if any, otherwise the
+     * default value, if any, otherwise null.
+     */
+    public String getOutputProperty(String name) {
+        String val = getOutputPropertyNonDefault(name);
+        // If no explicit value, try to get the default value
+        if (val == null)
+            val = getOutputPropertyDefault(name);
+        return val;
+        
+    }
+    /**
+     * Get the value of an output property, 
+     * not the default value. If there is a default
+     * value, but no non-default value this method
+     * will return null.
+     * <p>
+     * 
+     */
+    public String getOutputPropertyNonDefault(String name )
+    {
+        return getProp(name,false);
+    }
+
+    /**
+     * Return a {@link DOM3Serializer} interface into this serializer. If the
+     * serializer does not support the {@link DOM3Serializer} interface, it should
+     * return null.
+     *
+     * @return A {@link DOM3Serializer} interface into this serializer,  or null
+     * if the serializer is not DOM capable
+     * @throws IOException An I/O exception occured
+     * @see org.apache.xml.serializer.Serializer#asDOM3Serializer()
+     */
+    public Object asDOM3Serializer() throws IOException
+    {
+        return new org.apache.xml.serializer.dom3.DOM3SerializerImpl(this);
+    }
+    /**
+     * Get the default value of an xsl:output property,
+     * which would be null only if no default value exists
+     * for the property.
+     */
+    public String getOutputPropertyDefault(String name) {
+        return getProp(name, true);
+    } 
+    
+    /**
+     * Set the value for the output property, typically from
+     * an xsl:output element, but this does not change what
+     * the default value is.
+     */
+    public void   setOutputProperty(String name, String val) {
+        setProp(name,val,false);
+        
+    }
+    
+    /**
+     * Set the default value for an output property, but this does
+     * not impact any explicitly set value.
+     */
+    public void   setOutputPropertyDefault(String name, String val) {
+        setProp(name,val,true);
+        
+    }
+    
+    /**
+     * A mapping of keys to explicitly set values, for example if 
+     * and <xsl:output/> has an "encoding" attribute, this
+     * map will have what that attribute maps to.
+     */
+    private HashMap m_OutputProps;
+    /**
+     * A mapping of keys to default values, for example if
+     * the default value of the encoding is "UTF-8" then this
+     * map will have that "encoding" maps to "UTF-8".
+     */
+    private HashMap m_OutputPropsDefault;
+    
+    Set getOutputPropDefaultKeys() {
+        return m_OutputPropsDefault.keySet();
+    }
+    Set getOutputPropKeys() {
+        return m_OutputProps.keySet();
+    }
+    
+    private String getProp(String name, boolean defaultVal) {
+        if (m_OutputProps == null) {
+            m_OutputProps = new HashMap();
+            m_OutputPropsDefault = new HashMap();
+        }
+        
+        String val;
+        if (defaultVal)
+            val = (String) m_OutputPropsDefault.get(name);
+        else
+            val = (String) m_OutputProps.get(name);
+        
+        return val;
+        
+    }
+    /**
+     * 
+     * @param name The name of the property, e.g. "{http://myprop}indent-tabs" or "indent".
+     * @param val The value of the property, e.g. "4"
+     * @param defaultVal true if this is a default value being set for the property as 
+     * opposed to a user define on, set say explicitly in the stylesheet or via JAXP
+     */
+    void setProp(String name, String val, boolean defaultVal) {
+        if (m_OutputProps == null) {
+            m_OutputProps = new HashMap();
+            m_OutputPropsDefault = new HashMap();
+        }
+        
+        if (defaultVal)
+            m_OutputPropsDefault.put(name,val);
+        else {
+            if (OutputKeys.CDATA_SECTION_ELEMENTS.equals(name) && val != null) {
+                initCdataElems(val);
+                String oldVal = (String) m_OutputProps.get(name);
+                String newVal;
+                if (oldVal == null)
+                    newVal = oldVal + ' ' + val;
+                else
+                    newVal = val;
+                m_OutputProps.put(name,newVal);
+            }
+            else {
+                m_OutputProps.put(name,val);
+            }
+        }
+        
+
+    }
+
+    /**
+     * Get the first char of the local name
+     * @param name Either a local name, or a local name
+     * preceeded by a uri enclosed in curly braces.
+     */
+    static char getFirstCharLocName(String name) {
+        final char first;
+        int i = name.indexOf('}');
+        if (i < 0)
+            first = name.charAt(0);
+        else
+            first = name.charAt(i+1);
+        return first;
+    }
+}
+    
+
diff --git a/src/main/java/org/apache/xml/serializer/SerializerConstants.java b/src/main/java/org/apache/xml/serializer/SerializerConstants.java
new file mode 100644
index 0000000..87f5052
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/SerializerConstants.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerConstants.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+/**
+ * Constants used in serialization, such as the string "xmlns"
+ * @xsl.usage internal
+ */
+interface SerializerConstants
+{
+
+    /** To insert ]]> in a CDATA section by ending the last CDATA section with
+     * ]] and starting the next CDATA section with >
+     */
+    static final String CDATA_CONTINUE = "]]]]><![CDATA[>";
+    /**
+     * The constant "]]>"
+     */
+    static final String CDATA_DELIMITER_CLOSE = "]]>";
+    static final String CDATA_DELIMITER_OPEN = "<![CDATA[";
+
+    static final String EMPTYSTRING = "";
+
+    static final String ENTITY_AMP = "&amp;";
+    static final String ENTITY_CRLF = "&#xA;";
+    static final String ENTITY_GT = "&gt;";
+    static final String ENTITY_LT = "&lt;";
+    static final String ENTITY_QUOT = "&quot;";
+
+    static final String XML_PREFIX = "xml";
+    static final String XMLNS_PREFIX = "xmlns";
+    static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
+   
+    public static final String DEFAULT_SAX_SERIALIZER=SerializerBase.PKG_NAME+".ToXMLSAXHandler";
+    
+    /**
+     * Define the XML version.
+     */
+    static final String XMLVERSION11 = "1.1";
+    static final String XMLVERSION10 = "1.0";
+}
diff --git a/src/main/java/org/apache/xml/serializer/SerializerFactory.java b/src/main/java/org/apache/xml/serializer/SerializerFactory.java
new file mode 100644
index 0000000..f4e5986
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/SerializerFactory.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerFactory.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.util.Hashtable;
+import java.util.Properties;
+
+import javax.xml.transform.OutputKeys;
+
+import org.apache.xml.serializer.utils.MsgKey;
+import org.apache.xml.serializer.utils.Utils;
+import org.xml.sax.ContentHandler;
+
+/**
+ * This class is a public API, it is a factory for creating serializers.
+   * 
+   * The properties object passed to the getSerializer() method should be created by
+   * the OutputPropertiesFactory. Although the properties object
+   * used to create a serializer does not need to be obtained 
+   * from OutputPropertiesFactory,
+   * using this factory ensures that the default key/value properties
+   * are set for the given output "method".
+   * 
+   * <p>
+   * The standard property keys supported are: "method", "version", "encoding",
+   * "omit-xml-declaration", "standalone", doctype-public",
+   * "doctype-system", "cdata-section-elements", "indent", "media-type". 
+   * These property keys and their values are described in the XSLT recommendation,
+   * see {@link <a href="http://www.w3.org/TR/1999/REC-xslt-19991116"> XSLT 1.0 recommendation</a>}
+   * 
+   * <p>
+   * The value of the "cdata-section-elements" property key is a whitespace
+   * separated list of elements. If the element is in a namespace then 
+   * value is passed in this format: {uri}localName 
+   *
+   * <p>
+   * The non-standard property keys supported are defined in {@link OutputPropertiesFactory}.
+   *
+   * @see OutputPropertiesFactory
+   * @see Method
+   * @see Serializer
+   */
+public final class SerializerFactory
+{
+  /**
+   * This constructor is private just to prevent the creation of such an object.
+   */
+
+  private SerializerFactory() {
+ 
+  }
+  /**
+   * Associates output methods to default output formats.
+   */
+  private static Hashtable m_formats = new Hashtable();
+
+  /**
+   * Returns a serializer for the specified output method. The output method
+   * is specified by the value of the property associated with the "method" key.
+   * If no implementation exists that supports the specified output method
+   * an exception of some type will be thrown.
+   * For a list of the output "method" key values see {@link Method}.
+   *
+   * @param format The output format, minimally the "method" property must be set.
+   * @return A suitable serializer.
+   * @throws IllegalArgumentException if method is
+   * null or an appropriate serializer can't be found
+   * @throws Exception if the class for the serializer is found but does not
+   * implement ContentHandler.
+   * @throws WrappedRuntimeException if an exception is thrown while trying to find serializer
+   */
+  public static Serializer getSerializer(Properties format)
+  {
+      Serializer ser;
+
+      try
+      {
+        String method = format.getProperty(OutputKeys.METHOD);
+
+        if (method == null) {
+            String msg = Utils.messages.createMessage(
+                MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                new Object[] { OutputKeys.METHOD});
+            throw new IllegalArgumentException(msg);
+        }
+
+        String className =
+            format.getProperty(OutputPropertiesFactory.S_KEY_CONTENT_HANDLER);
+
+
+        if (null == className)
+        {
+            // Missing Content Handler property, load default using OutputPropertiesFactory
+            Properties methodDefaults =
+                OutputPropertiesFactory.getDefaultMethodProperties(method);
+            className = 
+            methodDefaults.getProperty(OutputPropertiesFactory.S_KEY_CONTENT_HANDLER);
+            if (null == className) {
+                String msg = Utils.messages.createMessage(
+                    MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                    new Object[] { OutputPropertiesFactory.S_KEY_CONTENT_HANDLER});
+                throw new IllegalArgumentException(msg);
+            }
+
+        }
+
+
+
+        ClassLoader loader = ObjectFactory.findClassLoader();
+
+        Class cls = ObjectFactory.findProviderClass(className, loader, true);
+
+        // _serializers.put(method, cls);
+
+        Object obj = cls.newInstance();
+
+        if (obj instanceof SerializationHandler)
+        {
+              // this is one of the supplied serializers
+            ser = (Serializer) cls.newInstance();
+            ser.setOutputFormat(format);
+        }
+        else
+        {
+              /*
+               *  This  must be a user defined Serializer.
+               *  It had better implement ContentHandler.
+               */
+               if (obj instanceof ContentHandler)
+               {
+
+                  /*
+                   * The user defined serializer defines ContentHandler,
+                   * but we need to wrap it with ToXMLSAXHandler which
+                   * will collect SAX-like events and emit true
+                   * SAX ContentHandler events to the users handler.
+                   */
+                  className = SerializerConstants.DEFAULT_SAX_SERIALIZER;
+                  cls = ObjectFactory.findProviderClass(className, loader, true);
+                  SerializationHandler sh =
+                      (SerializationHandler) cls.newInstance();
+                  sh.setContentHandler( (ContentHandler) obj);
+                  sh.setOutputFormat(format);
+
+                  ser = sh;
+               }
+               else
+               {
+                  // user defined serializer does not implement
+                  // ContentHandler, ... very bad
+                   throw new Exception(
+                       Utils.messages.createMessage(
+                           MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                               new Object[] { className}));
+               }
+
+        }
+      }
+      catch (Exception e)
+      {
+        throw new org.apache.xml.serializer.utils.WrappedRuntimeException(e);
+      }
+
+      // If we make it to here ser is not null.
+      return ser;
+  }
+}
diff --git a/src/main/java/org/apache/xml/serializer/SerializerTrace.java b/src/main/java/org/apache/xml/serializer/SerializerTrace.java
new file mode 100644
index 0000000..df612f5
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/SerializerTrace.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerTrace.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import org.xml.sax.Attributes;
+
+/**
+ * This interface defines a set of integer constants that identify trace event
+ * types.
+ * 
+ * @xsl.usage internal
+ */
+
+public interface SerializerTrace {
+    
+  /**
+   * Event type generated when a document begins.
+   *
+   */
+  public static final int EVENTTYPE_STARTDOCUMENT = 1;
+
+  /**
+   * Event type generated when a document ends.
+   */
+  public static final int EVENTTYPE_ENDDOCUMENT = 2;
+
+  /**
+   * Event type generated when an element begins (after the attributes have been processed but before the children have been added).
+   */
+  public static final int EVENTTYPE_STARTELEMENT = 3;
+
+  /**
+   * Event type generated when an element ends, after it's children have been added.
+   */
+  public static final int EVENTTYPE_ENDELEMENT = 4;
+
+  /**
+   * Event type generated for character data (CDATA and Ignorable Whitespace have their own events).
+   */
+  public static final int EVENTTYPE_CHARACTERS = 5;
+
+  /**
+   * Event type generated for ignorable whitespace (I'm not sure how much this is actually called.
+   */
+  public static final int EVENTTYPE_IGNORABLEWHITESPACE = 6;
+
+  /**
+   * Event type generated for processing instructions.
+   */
+  public static final int EVENTTYPE_PI = 7;
+
+  /**
+   * Event type generated after a comment has been added.
+   */
+  public static final int EVENTTYPE_COMMENT = 8;
+
+  /**
+   * Event type generate after an entity ref is created.
+   */
+  public static final int EVENTTYPE_ENTITYREF = 9;
+
+  /**
+   * Event type generated after CDATA is generated.
+   */
+  public static final int EVENTTYPE_CDATA = 10;
+  
+  /**
+   * Event type generated when characters might be written to an output stream,
+   *  but  these characters never are. They will ultimately be written out via
+   * EVENTTYPE_OUTPUT_CHARACTERS. This type is used as attributes are collected.
+   * Whenever the attributes change this event type is fired. At the very end
+   * however, when the attributes do not change anymore and are going to be
+   * ouput to the document the real characters will be written out using the
+   * EVENTTYPE_OUTPUT_CHARACTERS.
+   */
+  public static final int EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS = 11;
+  
+  /**
+   * Event type generated when characters are written to an output stream.
+   */
+  public static final int EVENTTYPE_OUTPUT_CHARACTERS = 12;
+    
+
+  /**
+   * Tell if trace listeners are present.
+   *
+   * @return True if there are trace listeners
+   */
+  public boolean hasTraceListeners();
+  
+  /**
+   * Fire startDocument, endDocument events.
+   *
+   * @param eventType One of the EVENTTYPE_XXX constants.
+   */
+  public void fireGenerateEvent(int eventType);
+  
+  /**
+   * Fire startElement, endElement events.
+   *
+   * @param eventType One of the EVENTTYPE_XXX constants.
+   * @param name The name of the element.
+   * @param atts The SAX attribute list.
+   */
+  public void fireGenerateEvent(int eventType, String name, Attributes atts);
+  
+  /**
+   * Fire characters, cdata events.
+   *
+   * @param eventType One of the EVENTTYPE_XXX constants.
+   * @param ch The char array from the SAX event.
+   * @param start The start offset to be used in the char array.
+   * @param length The end offset to be used in the chara array.
+   */
+  public void fireGenerateEvent(int eventType, char ch[], int start, int length);
+  
+  /**
+   * Fire processingInstruction events.
+   *
+   * @param eventType One of the EVENTTYPE_XXX constants.
+   * @param name The name of the processing instruction.
+   * @param data The processing instruction data.
+   */
+  public void fireGenerateEvent(int eventType, String name, String data);
+  
+
+  /**
+   * Fire comment and entity ref events.
+   *
+   * @param eventType One of the EVENTTYPE_XXX constants.
+   * @param data The comment or entity ref data.
+   */
+  public void fireGenerateEvent(int eventType, String data);
+  
+}
diff --git a/src/main/java/org/apache/xml/serializer/SerializerTraceWriter.java b/src/main/java/org/apache/xml/serializer/SerializerTraceWriter.java
new file mode 100644
index 0000000..5255dba
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/SerializerTraceWriter.java
@@ -0,0 +1,341 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerTraceWriter.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+/**
+ * This class wraps the real writer, it only purpose is to send
+ * CHARACTERTOSTREAM events to the trace listener. 
+ * Each method immediately sends the call to the wrapped writer unchanged, but
+ * in addition it collects characters to be issued to a trace listener.
+ * 
+ * In this way the trace
+ * listener knows what characters have been written to the output Writer.
+ * 
+ * There may still be differences in what the trace events say is going to the
+ * output writer and what is really going there. These differences will be due
+ * to the fact that this class is UTF-8 encoding before emiting the trace event
+ * and the underlying writer may not be UTF-8 encoding. There may also be
+ * encoding differences.  So the main pupose of this class is to provide a
+ * resonable facsimile of the true output.
+ * 
+ * @xsl.usage internal
+ */
+final class SerializerTraceWriter extends Writer implements WriterChain
+{
+
+    /** The real writer to immediately write to.
+     * This reference may be null, in which case nothing is written out, but
+     * only the trace events are fired for output.
+     */
+    private final java.io.Writer m_writer;
+
+    /** The tracer to send events to */
+    private final SerializerTrace m_tracer;
+
+    /** The size of the internal buffer, just to keep too many
+     * events from being sent to the tracer
+     */
+    private int buf_length;
+
+    /**
+     * Internal buffer to collect the characters to go to the trace listener.
+     * 
+     */
+    private byte buf[];
+
+    /**
+     * How many bytes have been collected and still need to go to trace
+     * listener.
+     */
+    private int count;
+
+    /**
+     * Creates or replaces the internal buffer, and makes sure it has a few
+     * extra bytes slight overflow of the last UTF8 encoded character.
+     * @param size
+     */
+    private void setBufferSize(int size)
+    {
+        buf = new byte[size + 3];
+        buf_length = size;
+        count = 0;
+    }
+
+    /**
+     * Constructor.
+     * If the writer passed in is null, then this SerializerTraceWriter will
+     * only signal trace events of what would have been written to that writer.
+     * If the writer passed in is not null then the trace events will mirror
+     * what is going to that writer. In this way tools, such as a debugger, can
+     * gather information on what is being written out.
+     * 
+     * @param out the Writer to write to (possibly null)
+     * @param tracer the tracer to inform that characters are being written
+     */
+    public SerializerTraceWriter(Writer out, SerializerTrace tracer)
+    {
+        m_writer = out;
+        m_tracer = tracer;
+        setBufferSize(1024);
+    }
+
+    /**
+     * Flush out the collected characters by sending them to the trace
+     * listener.  These characters are never written to the real writer
+     * (m_writer) because that has already happened with every method
+     * call. This method simple informs the listener of what has already
+     * happened.
+     * @throws IOException
+     */
+    private void flushBuffer() throws IOException
+    {
+
+        // Just for tracing purposes
+        if (count > 0)
+        {
+            char[] chars = new char[count];
+            for(int i=0; i<count; i++)
+                chars[i] = (char) buf[i];
+
+            if (m_tracer != null)
+                m_tracer.fireGenerateEvent(
+                    SerializerTrace.EVENTTYPE_OUTPUT_CHARACTERS,
+                    chars,
+                    0,
+                    chars.length);
+
+            count = 0;
+        }
+    }
+
+    /**
+     * Flush the internal buffer and flush the Writer
+     * @see java.io.Writer#flush()
+     */
+    public void flush() throws java.io.IOException
+    {
+        // send to the real writer
+        if (m_writer != null)
+            m_writer.flush();
+
+        // from here on just for tracing purposes
+        flushBuffer();
+    }
+
+    /**
+     * Flush the internal buffer and close the Writer
+     * @see java.io.Writer#close()
+     */
+    public void close() throws java.io.IOException
+    {
+        // send to the real writer
+        if (m_writer != null)   
+            m_writer.close();
+
+        // from here on just for tracing purposes
+        flushBuffer();
+    }
+
+
+    /**
+     * Write a single character.  The character to be written is contained in
+     * the 16 low-order bits of the given integer value; the 16 high-order bits
+     * are ignored.
+     *
+     * <p> Subclasses that intend to support efficient single-character output
+     * should override this method.
+     *
+     * @param c  int specifying a character to be written.
+     * @exception  IOException  If an I/O error occurs
+     */
+    public void write(final int c) throws IOException
+    {
+        // send to the real writer
+        if (m_writer != null)
+            m_writer.write(c);
+
+        // ---------- from here on just collect for tracing purposes
+
+        /* If we are close to the end of the buffer then flush it.
+         * Remember the buffer can hold a few more characters than buf_length
+         */
+        if (count >= buf_length)
+            flushBuffer();
+
+        if (c < 0x80)
+        {
+            buf[count++] = (byte) (c);
+        }
+        else if (c < 0x800)
+        {
+            buf[count++] = (byte) (0xc0 + (c >> 6));
+            buf[count++] = (byte) (0x80 + (c & 0x3f));
+        }
+        else
+        {
+            buf[count++] = (byte) (0xe0 + (c >> 12));
+            buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
+            buf[count++] = (byte) (0x80 + (c & 0x3f));
+        }
+    }
+
+    /**
+     * Write a portion of an array of characters.
+     *
+     * @param  chars  Array of characters
+     * @param  start   Offset from which to start writing characters
+     * @param  length   Number of characters to write
+     *
+     * @exception  IOException  If an I/O error occurs
+     *
+     * @throws java.io.IOException
+     */
+    public void write(final char chars[], final int start, final int length)
+        throws java.io.IOException
+    {
+        // send to the real writer
+        if (m_writer != null)
+            m_writer.write(chars, start, length);
+
+        // from here on just collect for tracing purposes
+        int lengthx3 = (length << 1) + length;
+
+        if (lengthx3 >= buf_length)
+        {
+
+            /* If the request length exceeds the size of the output buffer,
+              * flush the output buffer and make the buffer bigger to handle.
+              */
+
+            flushBuffer();
+            setBufferSize(2 * lengthx3);
+
+        }
+
+        if (lengthx3 > buf_length - count)
+        {
+            flushBuffer();
+        }
+
+        final int n = length + start;
+        for (int i = start; i < n; i++)
+        {
+            final char c = chars[i];
+
+            if (c < 0x80)
+                buf[count++] = (byte) (c);
+            else if (c < 0x800)
+            {
+                buf[count++] = (byte) (0xc0 + (c >> 6));
+                buf[count++] = (byte) (0x80 + (c & 0x3f));
+            }
+            else
+            {
+                buf[count++] = (byte) (0xe0 + (c >> 12));
+                buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
+                buf[count++] = (byte) (0x80 + (c & 0x3f));
+            }
+        }
+
+    }
+
+    /**
+     * Write a string.
+     *
+     * @param  s  String to be written
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public void write(final String s) throws IOException
+    {
+        // send to the real writer
+        if (m_writer != null)
+            m_writer.write(s);
+
+        // from here on just collect for tracing purposes
+        final int length = s.length();
+
+        // We multiply the length by three since this is the maximum length
+        // of the characters that we can put into the buffer.  It is possible
+        // for each Unicode character to expand to three bytes.
+
+        int lengthx3 = (length << 1) + length;
+
+        if (lengthx3 >= buf_length)
+        {
+
+            /* If the request length exceeds the size of the output buffer,
+              * flush the output buffer and make the buffer bigger to handle.
+              */
+
+            flushBuffer();
+            setBufferSize(2 * lengthx3);
+        }
+
+        if (lengthx3 > buf_length - count)
+        {
+            flushBuffer();
+        }
+
+        for (int i = 0; i < length; i++)
+        {
+            final char c = s.charAt(i);
+
+            if (c < 0x80)
+                buf[count++] = (byte) (c);
+            else if (c < 0x800)
+            {
+                buf[count++] = (byte) (0xc0 + (c >> 6));
+                buf[count++] = (byte) (0x80 + (c & 0x3f));
+            }
+            else
+            {
+                buf[count++] = (byte) (0xe0 + (c >> 12));
+                buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
+                buf[count++] = (byte) (0x80 + (c & 0x3f));
+            }
+        }
+    }
+
+    /**
+     * Get the writer that this one directly wraps.
+     */
+    public Writer getWriter()
+    {
+        return m_writer;
+    }
+
+    /**
+     * Get the OutputStream that is the at the end of the
+     * chain of writers.
+     */
+    public OutputStream getOutputStream()
+    {
+        OutputStream retval = null;
+        if (m_writer instanceof WriterChain)
+            retval = ((WriterChain) m_writer).getOutputStream();
+        return retval;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/ToHTMLStream.java b/src/main/java/org/apache/xml/serializer/ToHTMLStream.java
new file mode 100644
index 0000000..4f2927a
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ToHTMLStream.java
@@ -0,0 +1,2350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ToHTMLStream.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.xml.transform.Result;
+
+import org.apache.xml.serializer.utils.MsgKey;
+import org.apache.xml.serializer.utils.Utils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * This serializer takes a series of SAX or
+ * SAX-like events and writes its output
+ * to the given stream.
+ * 
+ * This class is not a public API, it is public
+ * because it is used from another package.
+ * 
+ * @xsl.usage internal
+ */
+public class ToHTMLStream extends ToStream 
+{
+
+    /** This flag is set while receiving events from the DTD */
+    protected boolean m_inDTD = false;
+
+    /** True if the current element is a block element.  (seems like 
+     *  this needs to be a stack. -sb). */
+    private boolean m_inBlockElem = false;
+
+    /**
+     * Map that tells which XML characters should have special treatment, and it
+     *  provides character to entity name lookup.
+     */
+    private final CharInfo m_htmlcharInfo =
+//        new CharInfo(CharInfo.HTML_ENTITIES_RESOURCE);
+        CharInfo.getCharInfo(CharInfo.HTML_ENTITIES_RESOURCE, Method.HTML);
+
+    /** A digital search trie for fast, case insensitive lookup of ElemDesc objects. */
+    static final Trie m_elementFlags = new Trie();
+
+    static {
+        initTagReference(m_elementFlags);
+    }
+    static void initTagReference(Trie m_elementFlags) {
+
+        // HTML 4.0 loose DTD
+        m_elementFlags.put("BASEFONT", new ElemDesc(0 | ElemDesc.EMPTY));
+        m_elementFlags.put(
+            "FRAME",
+            new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
+        m_elementFlags.put("FRAMESET", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("NOFRAMES", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "ISINDEX",
+            new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "APPLET",
+            new ElemDesc(0 | ElemDesc.WHITESPACESENSITIVE));
+        m_elementFlags.put("CENTER", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("DIR", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("MENU", new ElemDesc(0 | ElemDesc.BLOCK));
+
+        // HTML 4.0 strict DTD
+        m_elementFlags.put("TT", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+        m_elementFlags.put("I", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+        m_elementFlags.put("B", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+        m_elementFlags.put("BIG", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+        m_elementFlags.put("SMALL", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+        m_elementFlags.put("EM", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("STRONG", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("DFN", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("CODE", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("SAMP", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("KBD", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("VAR", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("CITE", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("ABBR", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put("ACRONYM", new ElemDesc(0 | ElemDesc.PHRASE));
+        m_elementFlags.put(
+            "SUP",
+            new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
+        m_elementFlags.put(
+            "SUB",
+            new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
+        m_elementFlags.put(
+            "SPAN",
+            new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
+        m_elementFlags.put(
+            "BDO",
+            new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
+        m_elementFlags.put(
+            "BR",
+            new ElemDesc(
+                0
+                    | ElemDesc.SPECIAL
+                    | ElemDesc.ASPECIAL
+                    | ElemDesc.EMPTY
+                    | ElemDesc.BLOCK));
+        m_elementFlags.put("BODY", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "ADDRESS",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+        m_elementFlags.put(
+            "DIV",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+        m_elementFlags.put("A", new ElemDesc(0 | ElemDesc.SPECIAL));
+        m_elementFlags.put(
+            "MAP",
+            new ElemDesc(
+                0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "AREA",
+            new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "LINK",
+            new ElemDesc(
+                0 | ElemDesc.HEADMISC | ElemDesc.EMPTY | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "IMG",
+            new ElemDesc(
+                0
+                    | ElemDesc.SPECIAL
+                    | ElemDesc.ASPECIAL
+                    | ElemDesc.EMPTY
+                    | ElemDesc.WHITESPACESENSITIVE));
+        m_elementFlags.put(
+            "OBJECT",
+            new ElemDesc(
+                0
+                    | ElemDesc.SPECIAL
+                    | ElemDesc.ASPECIAL
+                    | ElemDesc.HEADMISC
+                    | ElemDesc.WHITESPACESENSITIVE));
+        m_elementFlags.put("PARAM", new ElemDesc(0 | ElemDesc.EMPTY));
+        m_elementFlags.put(
+            "HR",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET
+                    | ElemDesc.EMPTY));
+        m_elementFlags.put(
+            "P",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+        m_elementFlags.put(
+            "H1",
+            new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "H2",
+            new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "H3",
+            new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "H4",
+            new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "H5",
+            new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "H6",
+            new ElemDesc(0 | ElemDesc.HEAD | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "PRE",
+            new ElemDesc(0 | ElemDesc.PREFORMATTED | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "Q",
+            new ElemDesc(0 | ElemDesc.SPECIAL | ElemDesc.ASPECIAL));
+        m_elementFlags.put(
+            "BLOCKQUOTE",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+        m_elementFlags.put("INS", new ElemDesc(0));
+        m_elementFlags.put("DEL", new ElemDesc(0));
+        m_elementFlags.put(
+            "DL",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+        m_elementFlags.put("DT", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("DD", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "OL",
+            new ElemDesc(0 | ElemDesc.LIST | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "UL",
+            new ElemDesc(0 | ElemDesc.LIST | ElemDesc.BLOCK));
+        m_elementFlags.put("LI", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("FORM", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("LABEL", new ElemDesc(0 | ElemDesc.FORMCTRL));
+        m_elementFlags.put(
+            "INPUT",
+            new ElemDesc(
+                0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL | ElemDesc.EMPTY));
+        m_elementFlags.put(
+            "SELECT",
+            new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
+        m_elementFlags.put("OPTGROUP", new ElemDesc(0));
+        m_elementFlags.put("OPTION", new ElemDesc(0));
+        m_elementFlags.put(
+            "TEXTAREA",
+            new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
+        m_elementFlags.put(
+            "FIELDSET",
+            new ElemDesc(0 | ElemDesc.BLOCK | ElemDesc.BLOCKFORM));
+        m_elementFlags.put("LEGEND", new ElemDesc(0));
+        m_elementFlags.put(
+            "BUTTON",
+            new ElemDesc(0 | ElemDesc.FORMCTRL | ElemDesc.INLINELABEL));
+        m_elementFlags.put(
+            "TABLE",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+        m_elementFlags.put("CAPTION", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("THEAD", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("TFOOT", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("TBODY", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("COLGROUP", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "COL",
+            new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
+        m_elementFlags.put("TR", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put("TH", new ElemDesc(0));
+        m_elementFlags.put("TD", new ElemDesc(0));
+        m_elementFlags.put(
+            "HEAD",
+            new ElemDesc(0 | ElemDesc.BLOCK | ElemDesc.HEADELEM));
+        m_elementFlags.put("TITLE", new ElemDesc(0 | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "BASE",
+            new ElemDesc(0 | ElemDesc.EMPTY | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "META",
+            new ElemDesc(
+                0 | ElemDesc.HEADMISC | ElemDesc.EMPTY | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "STYLE",
+            new ElemDesc(
+                0 | ElemDesc.HEADMISC | ElemDesc.RAW | ElemDesc.BLOCK));
+        m_elementFlags.put(
+            "SCRIPT",
+            new ElemDesc(
+                0
+                    | ElemDesc.SPECIAL
+                    | ElemDesc.ASPECIAL
+                    | ElemDesc.HEADMISC
+                    | ElemDesc.RAW));
+        m_elementFlags.put(
+            "NOSCRIPT",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+        m_elementFlags.put("HTML", new ElemDesc(0 | ElemDesc.BLOCK | ElemDesc.HTMLELEM));
+
+        // From "John Ky" <hand@syd.speednet.com.au
+        // Transitional Document Type Definition ()
+        // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/sgml/loosedtd.html#basefont
+        m_elementFlags.put("FONT", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+
+        // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/present/graphics.html#edef-STRIKE
+        m_elementFlags.put("S", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+        m_elementFlags.put("STRIKE", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+
+        // file:///C:/Documents%20and%20Settings/sboag.BOAG600E/My%20Documents/html/present/graphics.html#edef-U
+        m_elementFlags.put("U", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+
+        // From "John Ky" <hand@syd.speednet.com.au
+        m_elementFlags.put("NOBR", new ElemDesc(0 | ElemDesc.FONTSTYLE));
+
+        // HTML 4.0, section 16.5
+        m_elementFlags.put(
+            "IFRAME",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+                    
+        // Netscape 4 extension
+        m_elementFlags.put(
+            "LAYER",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+        // Netscape 4 extension                    
+        m_elementFlags.put(
+            "ILAYER",
+            new ElemDesc(
+                0
+                    | ElemDesc.BLOCK
+                    | ElemDesc.BLOCKFORM
+                    | ElemDesc.BLOCKFORMFIELDSET));
+
+        // NOW FOR ATTRIBUTE INFORMATION . . .
+        ElemDesc elemDesc;
+
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("a");
+        elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
+        elemDesc.setAttr("NAME", ElemDesc.ATTRURL);
+        
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("area");
+
+        elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
+        elemDesc.setAttr("NOHREF", ElemDesc.ATTREMPTY);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("base");
+
+        elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("button");
+        elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("blockquote");
+
+        elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("del");
+        elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("dir");
+        elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
+           
+        // ----------------------------------------------
+        
+        elemDesc = (ElemDesc) m_elementFlags.get("div");
+        elemDesc.setAttr("SRC", ElemDesc.ATTRURL); // Netscape 4 extension
+        elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY); // Internet-Explorer extension
+   
+        // ----------------------------------------------        
+        elemDesc = (ElemDesc) m_elementFlags.get("dl");
+        elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
+           
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("form");
+        elemDesc.setAttr("ACTION", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        // Attribution to: "Voytenko, Dimitry" <DVoytenko@SECTORBASE.COM>
+        elemDesc = (ElemDesc) m_elementFlags.get("frame");
+        elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
+        elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
+        elemDesc.setAttr("NORESIZE",ElemDesc.ATTREMPTY);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("head");
+        elemDesc.setAttr("PROFILE", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------        
+        elemDesc = (ElemDesc) m_elementFlags.get("hr");
+        elemDesc.setAttr("NOSHADE", ElemDesc.ATTREMPTY);
+        
+        // ----------------------------------------------
+        // HTML 4.0, section 16.5
+        elemDesc = (ElemDesc) m_elementFlags.get("iframe");
+        elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
+        elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        // Netscape 4 extension
+        elemDesc = (ElemDesc) m_elementFlags.get("ilayer");
+        elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("img");
+        elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
+        elemDesc.setAttr("LONGDESC", ElemDesc.ATTRURL);
+        elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
+        elemDesc.setAttr("ISMAP", ElemDesc.ATTREMPTY);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("input");
+
+        elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
+        elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
+        elemDesc.setAttr("CHECKED", ElemDesc.ATTREMPTY);
+        elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
+        elemDesc.setAttr("ISMAP", ElemDesc.ATTREMPTY);
+        elemDesc.setAttr("READONLY", ElemDesc.ATTREMPTY);
+        
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("ins");
+        elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        // Netscape 4 extension
+        elemDesc = (ElemDesc) m_elementFlags.get("layer");
+        elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("link");
+        elemDesc.setAttr("HREF", ElemDesc.ATTRURL);
+       
+        // ----------------------------------------------       
+        elemDesc = (ElemDesc) m_elementFlags.get("menu");
+        elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
+        
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("object");
+
+        elemDesc.setAttr("CLASSID", ElemDesc.ATTRURL);
+        elemDesc.setAttr("CODEBASE", ElemDesc.ATTRURL);
+        elemDesc.setAttr("DATA", ElemDesc.ATTRURL);
+        elemDesc.setAttr("ARCHIVE", ElemDesc.ATTRURL);
+        elemDesc.setAttr("USEMAP", ElemDesc.ATTRURL);
+        elemDesc.setAttr("DECLARE", ElemDesc.ATTREMPTY);
+        
+        // ----------------------------------------------        
+        elemDesc = (ElemDesc) m_elementFlags.get("ol");
+        elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
+        
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("optgroup");
+        elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("option");
+        elemDesc.setAttr("SELECTED", ElemDesc.ATTREMPTY);
+        elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
+        
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("q");
+        elemDesc.setAttr("CITE", ElemDesc.ATTRURL);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("script");
+        elemDesc.setAttr("SRC", ElemDesc.ATTRURL);
+        elemDesc.setAttr("FOR", ElemDesc.ATTRURL);
+        elemDesc.setAttr("DEFER", ElemDesc.ATTREMPTY);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("select");
+        elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
+        elemDesc.setAttr("MULTIPLE", ElemDesc.ATTREMPTY);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("table");
+        elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY); // Internet-Explorer extension
+        
+        // ----------------------------------------------        
+        elemDesc = (ElemDesc) m_elementFlags.get("td");
+        elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
+
+        // ----------------------------------------------
+        elemDesc = (ElemDesc) m_elementFlags.get("textarea");
+        elemDesc.setAttr("DISABLED", ElemDesc.ATTREMPTY);
+        elemDesc.setAttr("READONLY", ElemDesc.ATTREMPTY);
+       
+        // ----------------------------------------------                
+        elemDesc = (ElemDesc) m_elementFlags.get("th");
+        elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
+        
+        // ----------------------------------------------
+        // The nowrap attribute of a tr element is both
+        // a Netscape and Internet-Explorer extension                
+        elemDesc = (ElemDesc) m_elementFlags.get("tr");
+        elemDesc.setAttr("NOWRAP", ElemDesc.ATTREMPTY);
+        
+        // ----------------------------------------------        
+        elemDesc = (ElemDesc) m_elementFlags.get("ul");
+        elemDesc.setAttr("COMPACT", ElemDesc.ATTREMPTY);
+    }
+
+    /**
+     * Dummy element for elements not found.
+     */
+    static private final ElemDesc m_dummy = new ElemDesc(0 | ElemDesc.BLOCK);
+
+    /** True if URLs should be specially escaped with the %xx form. */
+    private boolean m_specialEscapeURLs = true;
+
+    /** True if the META tag should be omitted. */
+    private boolean m_omitMetaTag = false;
+
+    /**
+     * Tells if the formatter should use special URL escaping.
+     *
+     * @param bool True if URLs should be specially escaped with the %xx form.
+     */
+    public void setSpecialEscapeURLs(boolean bool)
+    {
+        m_specialEscapeURLs = bool;
+    }
+
+    /**
+     * Tells if the formatter should omit the META tag.
+     *
+     * @param bool True if the META tag should be omitted.
+     */
+    public void setOmitMetaTag(boolean bool)
+    {
+        m_omitMetaTag = bool;
+    }
+
+    /**
+     * Specifies an output format for this serializer. It the
+     * serializer has already been associated with an output format,
+     * it will switch to the new format. This method should not be
+     * called while the serializer is in the process of serializing
+     * a document.
+     * 
+     * This method can be called multiple times before starting
+     * the serialization of a particular result-tree. In principle
+     * all serialization parameters can be changed, with the exception
+     * of method="html" (it must be method="html" otherwise we
+     * shouldn't even have a ToHTMLStream object here!) 
+     *
+     * @param format The output format or serialzation parameters
+     * to use.
+     */
+    public void setOutputFormat(Properties format)
+    {
+        /*
+         * If "format" does not contain the property
+         * S_USE_URL_ESCAPING, then don't set this value at all,
+         * just leave as-is rather than explicitly setting it.
+         */
+        String value; 
+        value = format.getProperty(OutputPropertiesFactory.S_USE_URL_ESCAPING);
+        if (value != null) {
+            m_specialEscapeURLs =
+                OutputPropertyUtils.getBooleanProperty(
+                    OutputPropertiesFactory.S_USE_URL_ESCAPING,
+                    format);
+        }
+
+        /*
+         * If "format" does not contain the property
+         * S_OMIT_META_TAG, then don't set this value at all,
+         * just leave as-is rather than explicitly setting it.
+         */
+        value = format.getProperty(OutputPropertiesFactory.S_OMIT_META_TAG);
+        if (value != null) {
+           m_omitMetaTag =
+                OutputPropertyUtils.getBooleanProperty(
+                    OutputPropertiesFactory.S_OMIT_META_TAG,
+                    format);
+        }
+
+        super.setOutputFormat(format);
+    }
+
+    /**
+     * Tells if the formatter should use special URL escaping.
+     *
+     * @return True if URLs should be specially escaped with the %xx form.
+     */
+    private final boolean getSpecialEscapeURLs()
+    {
+        return m_specialEscapeURLs;
+    }
+
+    /**
+     * Tells if the formatter should omit the META tag.
+     *
+     * @return True if the META tag should be omitted.
+     */
+    private final boolean getOmitMetaTag()
+    {
+        return m_omitMetaTag;
+    }
+
+    /**
+     * Get a description of the given element.
+     *
+     * @param name non-null name of element, case insensitive.
+     *
+     * @return non-null reference to ElemDesc, which may be m_dummy if no 
+     *         element description matches the given name.
+     */
+    public static final ElemDesc getElemDesc(String name)
+    {
+        /* this method used to return m_dummy  when name was null
+         * but now it doesn't check and and requires non-null name.
+         */
+        Object obj = m_elementFlags.get(name);
+        if (null != obj)
+            return (ElemDesc)obj;
+        return m_dummy;
+    }
+    
+    
+    /**
+     * A Trie that is just a copy of the "static" one.
+     * We need this one to be able to use the faster, but not thread-safe
+     * method Trie.get2(name)
+     */
+    private Trie m_htmlInfo = new Trie(m_elementFlags);
+    /**
+     * Calls to this method could be replaced with calls to
+     * getElemDesc(name), but this one should be faster.
+     */
+    private ElemDesc getElemDesc2(String name)
+    {
+        Object obj = m_htmlInfo.get2(name);
+        if (null != obj)
+            return (ElemDesc)obj;
+        return m_dummy;
+    }
+
+    /**
+     * Default constructor.
+     */
+    public ToHTMLStream()
+    {
+
+        super();
+        // we are just constructing this thing, no output properties
+        // have been used, so we will set the right default for
+        // indenting anyways
+        m_doIndent = true; 
+        m_charInfo = m_htmlcharInfo;
+        // initialize namespaces
+        m_prefixMap = new NamespaceMappings();
+
+    }
+
+    /** The name of the current element. */
+//    private String m_currentElementName = null;
+
+    /**
+     * Receive notification of the beginning of a document.
+     *
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    protected void startDocumentInternal() throws org.xml.sax.SAXException
+    {
+        super.startDocumentInternal();
+
+        m_needToCallStartDocument = false; 
+        m_needToOutputDocTypeDecl = true;
+        m_startNewLine = false;
+        setOmitXMLDeclaration(true);
+    }
+
+    /**
+     * This method should only get called once.
+     * If a DOCTYPE declaration needs to get written out, it will
+     * be written out. If it doesn't need to be written out, then
+     * the call to this method has no effect.
+     */
+    private void outputDocTypeDecl(String name) throws SAXException {
+        if (true == m_needToOutputDocTypeDecl)
+        {
+            String doctypeSystem = getDoctypeSystem();
+            String doctypePublic = getDoctypePublic();
+            if ((null != doctypeSystem) || (null != doctypePublic))
+            {
+                final java.io.Writer writer = m_writer;
+                try
+                {
+                writer.write("<!DOCTYPE ");
+                writer.write(name);
+
+                if (null != doctypePublic)
+                {
+                    writer.write(" PUBLIC \"");
+                    writer.write(doctypePublic);
+                    writer.write('"');
+                }
+
+                if (null != doctypeSystem)
+                {
+                    if (null == doctypePublic)
+                        writer.write(" SYSTEM \"");
+                    else
+                        writer.write(" \"");
+
+                    writer.write(doctypeSystem);
+                    writer.write('"');
+                }
+
+                writer.write('>');
+                outputLineSep();
+                }
+                catch(IOException e)
+                {
+                    throw new SAXException(e);
+                }
+            }
+        }
+
+        m_needToOutputDocTypeDecl = false;
+    }
+
+    /**
+     * Receive notification of the end of a document. 
+     *
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public final void endDocument() throws org.xml.sax.SAXException
+    {
+        
+        flushPending();
+        if (m_doIndent && !m_isprevtext)
+        {
+            try
+            {
+            outputLineSep();
+            }
+            catch(IOException e)
+            {
+                throw new SAXException(e);
+            }
+        }
+
+        flushWriter();
+        if (m_tracer != null)
+            super.fireEndDoc();
+    }
+
+    /**
+     *  Receive notification of the beginning of an element.
+     *
+     *
+     *  @param namespaceURI
+     *  @param localName
+     *  @param name The element type name.
+     *  @param atts The attributes attached to the element, if any.
+     *  @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *             wrapping another exception.
+     *  @see #endElement
+     *  @see org.xml.sax.AttributeList
+     */
+    public void startElement(
+        String namespaceURI,
+        String localName,
+        String name,
+        Attributes atts)
+        throws org.xml.sax.SAXException
+    {
+
+        ElemContext elemContext = m_elemContext;
+
+        // clean up any pending things first
+        if (elemContext.m_startTagOpen)
+        {
+            closeStartTag();
+            elemContext.m_startTagOpen = false;
+        }
+        else if (m_cdataTagOpen)
+        {
+            closeCDATA();
+            m_cdataTagOpen = false;
+        }
+        else if (m_needToCallStartDocument)
+        {
+            startDocumentInternal();
+            m_needToCallStartDocument = false;
+        }
+        
+        if (m_needToOutputDocTypeDecl) {            
+            String n = name;
+            if (n == null || n.length() == 0) {
+                // If the lexical QName is not given
+                // use the localName in the DOCTYPE
+                n = localName;
+            }
+            outputDocTypeDecl(n);
+        }
+
+
+        // if this element has a namespace then treat it like XML
+        if (null != namespaceURI && namespaceURI.length() > 0)
+        {
+            super.startElement(namespaceURI, localName, name, atts);
+
+            return;
+        }
+        
+        try
+        {
+            // getElemDesc2(name) is faster than getElemDesc(name)
+            ElemDesc elemDesc = getElemDesc2(name);
+            int elemFlags = elemDesc.getFlags();
+
+            // deal with indentation issues first
+            if (m_doIndent)
+            {
+
+                boolean isBlockElement = (elemFlags & ElemDesc.BLOCK) != 0;
+                if (m_ispreserve)
+                    m_ispreserve = false;
+                else if (
+                    (null != elemContext.m_elementName)
+                    && (!m_inBlockElem
+                        || isBlockElement) /* && !isWhiteSpaceSensitive */
+                    )
+                {
+                    m_startNewLine = true;
+
+                    indent();
+
+                }
+                m_inBlockElem = !isBlockElement;
+            }
+
+            // save any attributes for later processing
+            if (atts != null)
+                addAttributes(atts);            
+
+            m_isprevtext = false;
+            final java.io.Writer writer = m_writer;
+            writer.write('<');
+            writer.write(name);
+
+
+
+            if (m_tracer != null)
+                firePseudoAttributes();
+            
+            if ((elemFlags & ElemDesc.EMPTY) != 0)  
+            {
+                // an optimization for elements which are expected
+                // to be empty.
+                m_elemContext = elemContext.push();
+                /* XSLTC sometimes calls namespaceAfterStartElement()
+                 * so we need to remember the name
+                 */
+                m_elemContext.m_elementName = name;
+                m_elemContext.m_elementDesc = elemDesc;
+                return;                
+            } 
+            else
+            {
+                elemContext = elemContext.push(namespaceURI,localName,name);
+                m_elemContext = elemContext;
+                elemContext.m_elementDesc = elemDesc;
+                elemContext.m_isRaw = (elemFlags & ElemDesc.RAW) != 0;
+            }
+            
+
+            if ((elemFlags & ElemDesc.HEADELEM) != 0)
+            {
+                // This is the <HEAD> element, do some special processing
+                closeStartTag();
+                elemContext.m_startTagOpen = false;
+                if (!m_omitMetaTag)
+                {
+                    if (m_doIndent)
+                        indent();
+                    writer.write(
+                        "<META http-equiv=\"Content-Type\" content=\"text/html; charset=");
+                    String encoding = getEncoding();
+                    String encode = Encodings.getMimeEncoding(encoding);
+                    writer.write(encode);
+                    writer.write("\">");
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+    }
+
+    /**
+     *  Receive notification of the end of an element.
+     *
+     *
+     *  @param namespaceURI
+     *  @param localName
+     *  @param name The element type name
+     *  @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *             wrapping another exception.
+     */
+    public final void endElement(
+        final String namespaceURI,
+        final String localName,
+        final String name)
+        throws org.xml.sax.SAXException
+    {
+        // deal with any pending issues
+        if (m_cdataTagOpen)
+            closeCDATA();
+
+        // if the element has a namespace, treat it like XML, not HTML
+        if (null != namespaceURI && namespaceURI.length() > 0)
+        {
+            super.endElement(namespaceURI, localName, name);
+
+            return;
+        }
+
+        try
+        {
+
+            ElemContext elemContext = m_elemContext;
+            final ElemDesc elemDesc = elemContext.m_elementDesc;
+            final int elemFlags = elemDesc.getFlags();
+            final boolean elemEmpty = (elemFlags & ElemDesc.EMPTY) != 0;
+
+            // deal with any indentation issues
+            if (m_doIndent)
+            {
+                final boolean isBlockElement = (elemFlags&ElemDesc.BLOCK) != 0;
+                boolean shouldIndent = false;
+
+                if (m_ispreserve)
+                {
+                    m_ispreserve = false;
+                }
+                else if (m_doIndent && (!m_inBlockElem || isBlockElement))
+                {
+                    m_startNewLine = true;
+                    shouldIndent = true;
+                }
+                if (!elemContext.m_startTagOpen && shouldIndent)
+                    indent(elemContext.m_currentElemDepth - 1);
+                m_inBlockElem = !isBlockElement;
+            }
+
+            final java.io.Writer writer = m_writer;
+            if (!elemContext.m_startTagOpen)
+            {
+                writer.write("</");
+                writer.write(name);
+                writer.write('>');
+            }
+            else
+            {
+                // the start-tag open when this method was called,
+                // so we need to process it now.
+                
+                if (m_tracer != null)
+                    super.fireStartElem(name);
+
+                // the starting tag was still open when we received this endElement() call
+                // so we need to process any gathered attributes NOW, before they go away.
+                int nAttrs = m_attributes.getLength();
+                if (nAttrs > 0)
+                {
+                    processAttributes(m_writer, nAttrs);
+                    // clear attributes object for re-use with next element
+                    m_attributes.clear();
+                }
+                if (!elemEmpty)
+                {
+                    // As per Dave/Paul recommendation 12/06/2000
+                    // if (shouldIndent)
+                    // writer.write('>');
+                    //  indent(m_currentIndent);
+
+                    writer.write("></");
+                    writer.write(name);
+                    writer.write('>');
+                }
+                else
+                {
+                    writer.write('>');
+                }
+            }
+            
+            // clean up because the element has ended
+            if ((elemFlags & ElemDesc.WHITESPACESENSITIVE) != 0)
+                m_ispreserve = true;
+            m_isprevtext = false;
+
+            // fire off the end element event
+            if (m_tracer != null)
+                super.fireEndElem(name);            
+                           
+            // OPTIMIZE-EMPTY                
+            if (elemEmpty)
+            {
+                // a quick exit if the HTML element had no children.
+                // This block of code can be removed if the corresponding block of code
+                // in startElement() also labeled with "OPTIMIZE-EMPTY" is also removed
+                m_elemContext = elemContext.m_prev;
+                return;
+            }
+
+            // some more clean because the element has ended. 
+            if (!elemContext.m_startTagOpen)
+            {
+                if (m_doIndent && !m_preserves.isEmpty())
+                    m_preserves.pop();
+            }
+            m_elemContext = elemContext.m_prev;
+//            m_isRawStack.pop();
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+    }
+
+    /**
+     * Process an attribute.
+     * @param   writer The writer to write the processed output to.
+     * @param   name   The name of the attribute.
+     * @param   value   The value of the attribute.
+     * @param   elemDesc The description of the HTML element 
+     *           that has this attribute.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    protected void processAttribute(
+        java.io.Writer writer,
+        String name,
+        String value,
+        ElemDesc elemDesc)
+        throws IOException
+    {
+        writer.write(' ');
+
+        if (   ((value.length() == 0) || value.equalsIgnoreCase(name))
+            && elemDesc != null 
+            && elemDesc.isAttrFlagSet(name, ElemDesc.ATTREMPTY))
+        {
+            writer.write(name);
+        }
+        else
+        {
+            // %REVIEW% %OPT%
+            // Two calls to single-char write may NOT
+            // be more efficient than one to string-write...
+            writer.write(name);
+            writer.write("=\"");
+            if (   elemDesc != null
+                && elemDesc.isAttrFlagSet(name, ElemDesc.ATTRURL))
+                writeAttrURI(writer, value, m_specialEscapeURLs);
+            else
+                writeAttrString(writer, value, this.getEncoding());
+            writer.write('"');
+
+        }
+    }
+
+    /**
+     * Tell if a character is an ASCII digit.
+     */
+    private boolean isASCIIDigit(char c)
+    {
+        return (c >= '0' && c <= '9');
+    }
+
+    /**
+     * Make an integer into an HH hex value.
+     * Does no checking on the size of the input, since this 
+     * is only meant to be used locally by writeAttrURI.
+     * 
+     * @param i must be a value less than 255.
+     * 
+     * @return should be a two character string.
+     */
+    private static String makeHHString(int i)
+    {
+        String s = Integer.toHexString(i).toUpperCase();
+        if (s.length() == 1)
+        {
+            s = "0" + s;
+        }
+        return s;
+    }
+
+    /**
+    * Dmitri Ilyin: Makes sure if the String is HH encoded sign.
+    * @param str must be 2 characters long
+    *
+    * @return true or false
+    */
+    private boolean isHHSign(String str)
+    {
+        boolean sign = true;
+        try
+        {
+            char r = (char) Integer.parseInt(str, 16);
+        }
+        catch (NumberFormatException e)
+        {
+            sign = false;
+        }
+        return sign;
+    }
+
+    /**
+     * Write the specified <var>string</var> after substituting non ASCII characters,
+     * with <CODE>%HH</CODE>, where HH is the hex of the byte value.
+     *
+     * @param   string      String to convert to XML format.
+     * @param doURLEscaping True if we should try to encode as 
+     *                      per http://www.ietf.org/rfc/rfc2396.txt.
+     *
+     * @throws org.xml.sax.SAXException if a bad surrogate pair is detected.
+     */
+    public void writeAttrURI(
+        final java.io.Writer writer, String string, boolean doURLEscaping)
+        throws IOException
+    {
+        // http://www.ietf.org/rfc/rfc2396.txt says:
+        // A URI is always in an "escaped" form, since escaping or unescaping a
+        // completed URI might change its semantics.  Normally, the only time
+        // escape encodings can safely be made is when the URI is being created
+        // from its component parts; each component may have its own set of
+        // characters that are reserved, so only the mechanism responsible for
+        // generating or interpreting that component can determine whether or
+        // not escaping a character will change its semantics. Likewise, a URI
+        // must be separated into its components before the escaped characters
+        // within those components can be safely decoded.
+        //
+        // ...So we do our best to do limited escaping of the URL, without 
+        // causing damage.  If the URL is already properly escaped, in theory, this 
+        // function should not change the string value.
+
+        final int end = string.length();
+        if (end > m_attrBuff.length)
+        {
+           m_attrBuff = new char[end*2 + 1];               
+        }
+        string.getChars(0,end, m_attrBuff, 0); 
+        final char[] chars = m_attrBuff;
+
+        int cleanStart = 0;
+        int cleanLength = 0;
+        
+        
+        char ch = 0;
+        for (int i = 0; i < end; i++)
+        {
+            ch = chars[i];
+
+            if ((ch < 32) || (ch > 126))
+            {
+                if (cleanLength > 0)
+                {
+                    writer.write(chars, cleanStart, cleanLength);
+                    cleanLength = 0;
+                }
+                if (doURLEscaping)
+                {
+                    // Encode UTF16 to UTF8.
+                    // Reference is Unicode, A Primer, by Tony Graham.
+                    // Page 92.
+
+                    // Note that Kay doesn't escape 0x20...
+                    //  if(ch == 0x20) // Not sure about this... -sb
+                    //  {
+                    //    writer.write(ch);
+                    //  }
+                    //  else 
+                    if (ch <= 0x7F)
+                    {
+                        writer.write('%');
+                        writer.write(makeHHString(ch));
+                    }
+                    else if (ch <= 0x7FF)
+                    {
+                        // Clear low 6 bits before rotate, put high 4 bits in low byte, 
+                        // and set two high bits.
+                        int high = (ch >> 6) | 0xC0;
+                        int low = (ch & 0x3F) | 0x80;
+                        // First 6 bits, + high bit
+                        writer.write('%');
+                        writer.write(makeHHString(high));
+                        writer.write('%');
+                        writer.write(makeHHString(low));
+                    }
+                    else if (Encodings.isHighUTF16Surrogate(ch)) // high surrogate
+                    {
+                        // I'm sure this can be done in 3 instructions, but I choose 
+                        // to try and do it exactly like it is done in the book, at least 
+                        // until we are sure this is totally clean.  I don't think performance 
+                        // is a big issue with this particular function, though I could be 
+                        // wrong.  Also, the stuff below clearly does more masking than 
+                        // it needs to do.
+
+                        // Clear high 6 bits.
+                        int highSurrogate = ((int) ch) & 0x03FF;
+
+                        // Middle 4 bits (wwww) + 1
+                        // "Note that the value of wwww from the high surrogate bit pattern
+                        // is incremented to make the uuuuu bit pattern in the scalar value 
+                        // so the surrogate pair don't address the BMP."
+                        int wwww = ((highSurrogate & 0x03C0) >> 6);
+                        int uuuuu = wwww + 1;
+
+                        // next 4 bits
+                        int zzzz = (highSurrogate & 0x003C) >> 2;
+
+                        // low 2 bits
+                        int yyyyyy = ((highSurrogate & 0x0003) << 4) & 0x30;
+
+                        // Get low surrogate character.
+                        ch = chars[++i];
+
+                        // Clear high 6 bits.
+                        int lowSurrogate = ((int) ch) & 0x03FF;
+
+                        // put the middle 4 bits into the bottom of yyyyyy (byte 3)
+                        yyyyyy = yyyyyy | ((lowSurrogate & 0x03C0) >> 6);
+
+                        // bottom 6 bits.
+                        int xxxxxx = (lowSurrogate & 0x003F);
+
+                        int byte1 = 0xF0 | (uuuuu >> 2); // top 3 bits of uuuuu
+                        int byte2 =
+                            0x80 | (((uuuuu & 0x03) << 4) & 0x30) | zzzz;
+                        int byte3 = 0x80 | yyyyyy;
+                        int byte4 = 0x80 | xxxxxx;
+
+                        writer.write('%');
+                        writer.write(makeHHString(byte1));
+                        writer.write('%');
+                        writer.write(makeHHString(byte2));
+                        writer.write('%');
+                        writer.write(makeHHString(byte3));
+                        writer.write('%');
+                        writer.write(makeHHString(byte4));
+                    }
+                    else
+                    {
+                        int high = (ch >> 12) | 0xE0; // top 4 bits
+                        int middle = ((ch & 0x0FC0) >> 6) | 0x80;
+                        // middle 6 bits
+                        int low = (ch & 0x3F) | 0x80;
+                        // First 6 bits, + high bit
+                        writer.write('%');
+                        writer.write(makeHHString(high));
+                        writer.write('%');
+                        writer.write(makeHHString(middle));
+                        writer.write('%');
+                        writer.write(makeHHString(low));
+                    }
+
+                }
+                else if (escapingNotNeeded(ch))
+                {
+                    writer.write(ch);
+                }
+                else
+                {
+                    writer.write("&#");
+                    writer.write(Integer.toString(ch));
+                    writer.write(';');
+                }
+                // In this character range we have first written out any previously accumulated 
+                // "clean" characters, then processed the current more complicated character,
+                // which may have incremented "i".
+                // We now we reset the next possible clean character.
+                cleanStart = i + 1;
+            }
+            // Since http://www.ietf.org/rfc/rfc2396.txt refers to the URI grammar as
+            // not allowing quotes in the URI proper syntax, nor in the fragment 
+            // identifier, we believe that it's OK to double escape quotes.
+            else if (ch == '"')
+            {
+                // If the character is a '%' number number, try to avoid double-escaping.
+                // There is a question if this is legal behavior.
+
+                // Dmitri Ilyin: to check if '%' number number is invalid. It must be checked if %xx is a sign, that would be encoded
+                // The encoded signes are in Hex form. So %xx my be in form %3C that is "<" sign. I will try to change here a little.
+
+                //        if( ((i+2) < len) && isASCIIDigit(stringArray[i+1]) && isASCIIDigit(stringArray[i+2]) )
+
+                // We are no longer escaping '%'
+
+                if (cleanLength > 0)
+                {
+                    writer.write(chars, cleanStart, cleanLength);
+                    cleanLength = 0;
+                }   
+                
+                
+                // Mike Kay encodes this as &#34;, so he may know something I don't?
+                if (doURLEscaping)
+                    writer.write("%22");
+                else
+                    writer.write("&quot;"); // we have to escape this, I guess.
+
+                // We have written out any clean characters, then the escaped '%' and now we
+                // We now we reset the next possible clean character.
+                cleanStart = i + 1;    
+            }
+            else if (ch == '&')
+            {
+                // HTML 4.01 reads, "Authors should use "&amp;" (ASCII decimal 38) 
+                // instead of "&" to avoid confusion with the beginning of a character 
+                // reference (entity reference open delimiter). 
+                if (cleanLength > 0)
+                {
+                    writer.write(chars, cleanStart, cleanLength);
+                    cleanLength = 0;
+                } 
+                writer.write("&amp;");
+                cleanStart = i + 1; 
+            }
+            else
+            {
+                // no processing for this character, just count how
+                // many characters in a row that we have that need no processing
+                cleanLength++;
+            }
+        }
+        
+        // are there any clean characters at the end of the array
+        // that we haven't processed yet?
+        if (cleanLength > 1)
+        {
+            // if the whole string can be written out as-is do so
+            // otherwise write out the clean chars at the end of the
+            // array
+            if (cleanStart == 0)
+                writer.write(string);
+            else
+                writer.write(chars, cleanStart, cleanLength);
+        }
+        else if (cleanLength == 1)
+        {
+            // a little optimization for 1 clean character
+            // (we could have let the previous if(...) handle them all)
+            writer.write(ch);
+        }
+    }
+
+    /**
+     * Writes the specified <var>string</var> after substituting <VAR>specials</VAR>,
+     * and UTF-16 surrogates for character references <CODE>&amp;#xnn</CODE>.
+     *
+     * @param   string      String to convert to XML format.
+     * @param   encoding    CURRENTLY NOT IMPLEMENTED.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void writeAttrString(
+        final java.io.Writer writer, String string, String encoding)
+        throws IOException
+    {
+        final int end = string.length();
+        if (end > m_attrBuff.length)
+        {
+            m_attrBuff = new char[end * 2 + 1];
+        }
+        string.getChars(0, end, m_attrBuff, 0);
+        final char[] chars = m_attrBuff;
+
+        
+
+        int cleanStart = 0;
+        int cleanLength = 0;
+
+        char ch = 0;
+        for (int i = 0; i < end; i++)
+        {
+            ch = chars[i];
+
+            // System.out.println("SPECIALSSIZE: "+SPECIALSSIZE);
+            // System.out.println("ch: "+(int)ch);
+            // System.out.println("m_maxCharacter: "+(int)m_maxCharacter);
+            // System.out.println("m_attrCharsMap[ch]: "+(int)m_attrCharsMap[ch]);
+            if (escapingNotNeeded(ch) && (!m_charInfo.shouldMapAttrChar(ch)))
+            {
+                cleanLength++;
+            }
+            else if ('<' == ch || '>' == ch)
+            {
+                cleanLength++; // no escaping in this case, as specified in 15.2
+            }
+            else if (
+                ('&' == ch) && ((i + 1) < end) && ('{' == chars[i + 1]))
+            {
+                cleanLength++; // no escaping in this case, as specified in 15.2
+            }
+            else
+            {
+                if (cleanLength > 0)
+                {
+                    writer.write(chars,cleanStart,cleanLength);
+                    cleanLength = 0;
+                }
+                int pos = accumDefaultEntity(writer, ch, i, chars, end, false, true);
+
+                if (i != pos)
+                {
+                    i = pos - 1;
+                }
+                else
+                {
+                    if (Encodings.isHighUTF16Surrogate(ch))
+                    {
+ 
+                            writeUTF16Surrogate(ch, chars, i, end);
+                            i++; // two input characters processed
+                                 // this increments by one and the for()
+                                 // loop itself increments by another one.
+                    }
+
+                    // The next is kind of a hack to keep from escaping in the case 
+                    // of Shift_JIS and the like.
+
+                    /*
+                    else if ((ch < m_maxCharacter) && (m_maxCharacter == 0xFFFF)
+                    && (ch != 160))
+                    {
+                    writer.write(ch);  // no escaping in this case
+                    }
+                    else
+                    */
+                    String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
+                    if (null != outputStringForChar)
+                    {
+                        writer.write(outputStringForChar);
+                    }
+                    else if (escapingNotNeeded(ch))
+                    {
+                        writer.write(ch); // no escaping in this case
+                    }
+                    else
+                    {
+                        writer.write("&#");
+                        writer.write(Integer.toString(ch));
+                        writer.write(';');
+                    }
+                }
+                cleanStart = i + 1;
+            }
+        } // end of for()
+        
+        // are there any clean characters at the end of the array
+        // that we haven't processed yet?
+        if (cleanLength > 1)
+        {
+            // if the whole string can be written out as-is do so
+            // otherwise write out the clean chars at the end of the
+            // array
+            if (cleanStart == 0)
+                writer.write(string);
+            else
+                writer.write(chars, cleanStart, cleanLength);
+        }
+        else if (cleanLength == 1)
+        {
+            // a little optimization for 1 clean character
+            // (we could have let the previous if(...) handle them all)
+            writer.write(ch);
+        }
+    }
+
+
+
+    /**
+     * Receive notification of character data.
+     *
+     * <p>The Parser will call this method to report each chunk of
+     * character data.  SAX parsers may return all contiguous character
+     * data in a single chunk, or they may split it into several
+     * chunks; however, all of the characters in any single event
+     * must come from the same external entity, so that the Locator
+     * provides useful information.</p>
+     *
+     * <p>The application must not attempt to read from the array
+     * outside of the specified range.</p>
+     *
+     * <p>Note that some parsers will report whitespace using the
+     * ignorableWhitespace() method rather than this one (validating
+     * parsers must do so).</p>
+     *
+     * @param chars The characters from the XML document.
+     * @param start The start position in the array.
+     * @param length The number of characters to read from the array.
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     * @see #ignorableWhitespace
+     * @see org.xml.sax.Locator
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public final void characters(char chars[], int start, int length)
+        throws org.xml.sax.SAXException
+    {
+
+        if (m_elemContext.m_isRaw)
+        { 
+            try
+            {
+                // Clean up some pending issues.
+                if (m_elemContext.m_startTagOpen)
+                {
+                    closeStartTag();
+                    m_elemContext.m_startTagOpen = false;
+                }
+                
+                m_ispreserve = true;
+                
+                writeNormalizedChars(chars, start, length, false, m_lineSepUse);
+                
+                // time to generate characters event
+                if (m_tracer != null)
+                    super.fireCharEvent(chars, start, length);
+                
+                return;
+            }
+            catch (IOException ioe)
+            {
+                throw new org.xml.sax.SAXException(
+                    Utils.messages.createMessage(MsgKey.ER_OIERROR,null),ioe);
+            }
+        }
+        else
+        {
+            super.characters(chars, start, length);
+        }
+    }
+
+    /**
+     *  Receive notification of cdata.
+     *
+     *  <p>The Parser will call this method to report each chunk of
+     *  character data.  SAX parsers may return all contiguous character
+     *  data in a single chunk, or they may split it into several
+     *  chunks; however, all of the characters in any single event
+     *  must come from the same external entity, so that the Locator
+     *  provides useful information.</p>
+     *
+     *  <p>The application must not attempt to read from the array
+     *  outside of the specified range.</p>
+     *
+     *  <p>Note that some parsers will report whitespace using the
+     *  ignorableWhitespace() method rather than this one (validating
+     *  parsers must do so).</p>
+     *
+     *  @param ch The characters from the XML document.
+     *  @param start The start position in the array.
+     *  @param length The number of characters to read from the array.
+     *  @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *             wrapping another exception.
+     *  @see #ignorableWhitespace
+     *  @see org.xml.sax.Locator
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public final void cdata(char ch[], int start, int length)
+        throws org.xml.sax.SAXException
+    {
+
+        if ((null != m_elemContext.m_elementName)
+            && (m_elemContext.m_elementName.equalsIgnoreCase("SCRIPT")
+                || m_elemContext.m_elementName.equalsIgnoreCase("STYLE")))
+        {
+            try
+            {
+                if (m_elemContext.m_startTagOpen)
+                {
+                    closeStartTag();
+                    m_elemContext.m_startTagOpen = false;
+                }
+
+                m_ispreserve = true;
+
+                if (shouldIndent())
+                    indent();
+
+                // writer.write(ch, start, length);
+                writeNormalizedChars(ch, start, length, true, m_lineSepUse);
+            }
+            catch (IOException ioe)
+            {
+                throw new org.xml.sax.SAXException(
+                    Utils.messages.createMessage(
+                        MsgKey.ER_OIERROR,
+                        null),
+                    ioe);
+                //"IO error", ioe);
+            }
+        }
+        else
+        {
+            super.cdata(ch, start, length);
+        }
+    }
+
+    /**
+     *  Receive notification of a processing instruction.
+     *
+     *  @param target The processing instruction target.
+     *  @param data The processing instruction data, or null if
+     *         none was supplied.
+     *  @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *             wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void processingInstruction(String target, String data)
+        throws org.xml.sax.SAXException
+    {
+
+        // Process any pending starDocument and startElement first.
+        flushPending(); 
+        
+        // Use a fairly nasty hack to tell if the next node is supposed to be 
+        // unescaped text.
+        if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING))
+        {
+            startNonEscaping();
+        }
+        else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING))
+        {
+            endNonEscaping();
+        }
+        else
+        {
+            try
+            {
+                // clean up any pending things first
+                if (m_elemContext.m_startTagOpen)
+                {
+                    closeStartTag();
+                    m_elemContext.m_startTagOpen = false;
+                }
+                else if (m_cdataTagOpen)
+                {
+                    closeCDATA();
+                }
+                else if (m_needToCallStartDocument)
+                {
+                    startDocumentInternal();
+                }
+            
+
+            /*
+             * Perhaps processing instructions can be written out in HTML before
+             * the DOCTYPE, in which case this could be emitted with the
+             * startElement call, that knows the name of the document element
+             * doing it right.
+             */
+            if (true == m_needToOutputDocTypeDecl)
+                outputDocTypeDecl("html"); // best guess for the upcoming element
+
+ 
+            if (shouldIndent())
+                indent();
+
+            final java.io.Writer writer = m_writer;
+            //writer.write("<?" + target);
+            writer.write("<?");
+            writer.write(target);
+
+            if (data.length() > 0 && !Character.isSpaceChar(data.charAt(0)))
+                writer.write(' '); 
+
+            //writer.write(data + ">"); // different from XML
+            writer.write(data); // different from XML
+            writer.write('>'); // different from XML
+
+            // Always output a newline char if not inside of an 
+            // element. The whitespace is not significant in that
+            // case.
+            if (m_elemContext.m_currentElemDepth <= 0)
+                outputLineSep();
+
+            m_startNewLine = true;
+            }
+            catch(IOException e)
+            {
+                throw new SAXException(e);
+            }
+        }
+               
+        // now generate the PI event
+        if (m_tracer != null)
+            super.fireEscapingEvent(target, data);
+     }
+
+    /**
+     * Receive notivication of a entityReference.
+     *
+     * @param name non-null reference to entity name string.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public final void entityReference(String name)
+        throws org.xml.sax.SAXException
+    {
+        try
+        {
+
+        final java.io.Writer writer = m_writer;
+        writer.write('&');
+        writer.write(name);
+        writer.write(';');
+        
+        } catch(IOException e)
+        {
+            throw new SAXException(e);
+        }
+    }
+    /**
+     * @see ExtendedContentHandler#endElement(String)
+     */
+    public final void endElement(String elemName) throws SAXException
+    {
+        endElement(null, null, elemName);
+    }
+
+    /**
+     * Process the attributes, which means to write out the currently
+     * collected attributes to the writer. The attributes are not
+     * cleared by this method
+     * 
+     * @param writer the writer to write processed attributes to.
+     * @param nAttrs the number of attributes in m_attributes 
+     * to be processed
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void processAttributes(java.io.Writer writer, int nAttrs)
+        throws IOException,SAXException
+    {
+            /* 
+             * process the collected attributes
+             */
+            for (int i = 0; i < nAttrs; i++)
+            {
+                processAttribute(
+                    writer,
+                    m_attributes.getQName(i),
+                    m_attributes.getValue(i),
+                    m_elemContext.m_elementDesc);
+            }
+    }
+
+    /**
+     * For the enclosing elements starting tag write out out any attributes
+     * followed by ">". At this point we also mark if this element is
+     * a cdata-section-element.
+     *
+     *@throws org.xml.sax.SAXException
+     */
+    protected void closeStartTag() throws SAXException
+    {
+            try
+            {
+
+            // finish processing attributes, time to fire off the start element event
+            if (m_tracer != null)
+                super.fireStartElem(m_elemContext.m_elementName);  
+            
+            int nAttrs = m_attributes.getLength();   
+            if (nAttrs>0)
+            {
+                processAttributes(m_writer, nAttrs);
+                // clear attributes object for re-use with next element
+                m_attributes.clear();
+            }
+
+            m_writer.write('>');
+
+            /* At this point we have the prefix mappings now, so
+             * lets determine if the current element is specified in the cdata-
+             * section-elements list.
+             */
+            if (m_CdataElems != null) // if there are any cdata sections
+                m_elemContext.m_isCdataSection = isCdataSection();
+            if (m_doIndent)
+            {
+                m_isprevtext = false;
+                m_preserves.push(m_ispreserve);
+            }
+            
+            }
+            catch(IOException e)
+            {
+                throw new SAXException(e);
+            }
+    }
+    
+
+   
+        /**
+         * This method is used when a prefix/uri namespace mapping
+         * is indicated after the element was started with a
+         * startElement() and before and endElement().
+         * startPrefixMapping(prefix,uri) would be used before the
+         * startElement() call.
+         * @param uri the URI of the namespace
+         * @param prefix the prefix associated with the given URI.
+         *
+         * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
+         */
+        public void namespaceAfterStartElement(String prefix, String uri)
+            throws SAXException
+        {
+            // hack for XSLTC with finding URI for default namespace
+            if (m_elemContext.m_elementURI == null)
+            {
+                String prefix1 = getPrefixPart(m_elemContext.m_elementName);
+                if (prefix1 == null && EMPTYSTRING.equals(prefix))
+                {
+                    // the elements URI is not known yet, and it
+                    // doesn't have a prefix, and we are currently
+                    // setting the uri for prefix "", so we have
+                    // the uri for the element... lets remember it
+                    m_elemContext.m_elementURI = uri;
+                }
+            }            
+            startPrefixMapping(prefix,uri,false);
+        }
+
+    public void startDTD(String name, String publicId, String systemId)
+        throws SAXException
+    {
+        m_inDTD = true;
+        super.startDTD(name, publicId, systemId);
+    }
+
+    /**
+     * Report the end of DTD declarations.
+     * @throws org.xml.sax.SAXException The application may raise an exception.
+     * @see #startDTD
+     */
+    public void endDTD() throws org.xml.sax.SAXException
+    {
+        m_inDTD = false;
+        /* for ToHTMLStream the DOCTYPE is entirely output in the
+         * startDocumentInternal() method, so don't do anything here
+         */
+    }
+    /**
+     * This method does nothing.
+     */
+    public void attributeDecl(
+        String eName,
+        String aName,
+        String type,
+        String valueDefault,
+        String value)
+        throws SAXException
+    {
+        // The internal DTD subset is not serialized by the ToHTMLStream serializer
+    }
+
+    /**
+     * This method does nothing.
+     */
+    public void elementDecl(String name, String model) throws SAXException
+    {
+        // The internal DTD subset is not serialized by the ToHTMLStream serializer
+    }
+    /**
+     * This method does nothing.
+     */
+    public void internalEntityDecl(String name, String value)
+        throws SAXException
+    {
+        // The internal DTD subset is not serialized by the ToHTMLStream serializer
+    }
+    /**
+     * This method does nothing.
+     */
+    public void externalEntityDecl(
+        String name,
+        String publicId,
+        String systemId)
+        throws SAXException
+    {
+        // The internal DTD subset is not serialized by the ToHTMLStream serializer
+    }
+
+    /**
+     * This method is used to add an attribute to the currently open element. 
+     * The caller has guaranted that this attribute is unique, which means that it
+     * not been seen before and will not be seen again.
+     * 
+     * @param name the qualified name of the attribute
+     * @param value the value of the attribute which can contain only
+     * ASCII printable characters characters in the range 32 to 127 inclusive.
+     * @param flags the bit values of this integer give optimization information.
+     */
+    public void addUniqueAttribute(String name, String value, int flags)
+        throws SAXException
+    {
+        try
+        {
+            final java.io.Writer writer = m_writer;
+            if ((flags & NO_BAD_CHARS) > 0 && m_htmlcharInfo.onlyQuotAmpLtGt)
+            {
+                // "flags" has indicated that the characters
+                // '>'  '<'   '&'  and '"' are not in the value and
+                // m_htmlcharInfo has recorded that there are no other
+                // entities in the range 0 to 127 so we write out the
+                // value directly
+                writer.write(' ');
+                writer.write(name);
+                writer.write("=\"");
+                writer.write(value);
+                writer.write('"');
+            }
+            else if (
+                (flags & HTML_ATTREMPTY) > 0
+                    && (value.length() == 0 || value.equalsIgnoreCase(name)))
+            {
+                writer.write(' ');
+                writer.write(name);
+            }
+            else
+            {
+                writer.write(' ');
+                writer.write(name);
+                writer.write("=\"");
+                if ((flags & HTML_ATTRURL) > 0)
+                {
+                    writeAttrURI(writer, value, m_specialEscapeURLs);
+                }
+                else
+                {
+                    writeAttrString(writer, value, this.getEncoding());
+                }
+                writer.write('"');
+            }
+        } catch (IOException e) {
+            throw new SAXException(e);
+        }
+    }
+
+    public void comment(char ch[], int start, int length)
+            throws SAXException
+    {
+        // The internal DTD subset is not serialized by the ToHTMLStream serializer
+        if (m_inDTD)
+            return;
+        
+        // Clean up some pending issues, just in case
+        // this call is coming right after a startElement()
+        // or we are in the middle of writing out CDATA
+        // or if a startDocument() call was not received
+        if (m_elemContext.m_startTagOpen)
+        {
+            closeStartTag();
+            m_elemContext.m_startTagOpen = false;
+        }
+        else if (m_cdataTagOpen)
+        {
+            closeCDATA();
+        }
+        else if (m_needToCallStartDocument)
+        {
+            startDocumentInternal();
+        }
+
+        /*
+         * Perhaps comments can be written out in HTML before the DOCTYPE.
+         * In this case we might delete this call to writeOutDOCTYPE, and
+         * it would be handled within the startElement() call.
+         */
+        if (m_needToOutputDocTypeDecl)
+            outputDocTypeDecl("html"); // best guess for the upcoming element
+
+        super.comment(ch, start, length);
+    }
+    
+    public boolean reset()
+    {
+        boolean ret = super.reset();
+        if (!ret)
+            return false;
+        resetToHTMLStream();
+        return true;        
+    }
+    
+    private void resetToHTMLStream()
+    {
+        // m_htmlcharInfo remains unchanged
+        // m_htmlInfo = null;  // Don't reset
+        m_inBlockElem = false;
+        m_inDTD = false;
+        m_omitMetaTag = false;
+        m_specialEscapeURLs = true;     
+    }
+    
+    static class Trie
+    {
+        /**
+         * A digital search trie for 7-bit ASCII text
+         * The API is a subset of java.util.Hashtable
+         * The key must be a 7-bit ASCII string
+         * The value may be any Java Object
+         * One can get an object stored in a trie from its key, 
+         * but the search is either case sensitive or case 
+         * insensitive to the characters in the key, and this
+         * choice of sensitivity or insensitivity is made when
+         * the Trie is created, before any objects are put in it.
+         * 
+         * This class is a copy of the one in org.apache.xml.utils. 
+         * It exists to cut the serializers dependancy on that package.
+         *  
+         * @xsl.usage internal
+         */
+
+        /** Size of the m_nextChar array.  */
+        public static final int ALPHA_SIZE = 128;
+
+        /** The root node of the tree.    */
+        final Node m_Root;
+
+        /** helper buffer to convert Strings to char arrays */
+        private char[] m_charBuffer = new char[0];
+
+        /** true if the search for an object is lower case only with the key */
+        private final boolean m_lowerCaseOnly;
+
+        /**
+         * Construct the trie that has a case insensitive search.
+         */
+        public Trie()
+        {
+            m_Root = new Node();
+            m_lowerCaseOnly = false;
+        }
+
+        /**
+         * Construct the trie given the desired case sensitivity with the key.
+         * @param lowerCaseOnly true if the search keys are to be loser case only,
+         * not case insensitive.
+         */
+        public Trie(boolean lowerCaseOnly)
+        {
+            m_Root = new Node();
+            m_lowerCaseOnly = lowerCaseOnly;
+        }
+
+        /**
+         * Put an object into the trie for lookup.
+         *
+         * @param key must be a 7-bit ASCII string
+         * @param value any java object.
+         *
+         * @return The old object that matched key, or null.
+         */
+        public Object put(String key, Object value)
+        {
+
+            final int len = key.length();
+            if (len > m_charBuffer.length)
+            {
+                // make the biggest buffer ever needed in get(String)
+                m_charBuffer = new char[len];
+            }
+
+            Node node = m_Root;
+
+            for (int i = 0; i < len; i++)
+            {
+                Node nextNode =
+                    node.m_nextChar[Character.toLowerCase(key.charAt(i))];
+
+                if (nextNode != null)
+                {
+                    node = nextNode;
+                }
+                else
+                {
+                    for (; i < len; i++)
+                    {
+                        Node newNode = new Node();
+                        if (m_lowerCaseOnly)
+                        {
+                            // put this value into the tree only with a lower case key 
+                            node.m_nextChar[Character.toLowerCase(
+                                key.charAt(i))] =
+                                newNode;
+                        }
+                        else
+                        {
+                            // put this value into the tree with a case insensitive key
+                            node.m_nextChar[Character.toUpperCase(
+                                key.charAt(i))] =
+                                newNode;
+                            node.m_nextChar[Character.toLowerCase(
+                                key.charAt(i))] =
+                                newNode;
+                        }
+                        node = newNode;
+                    }
+                    break;
+                }
+            }
+
+            Object ret = node.m_Value;
+
+            node.m_Value = value;
+
+            return ret;
+        }
+
+        /**
+         * Get an object that matches the key.
+         *
+         * @param key must be a 7-bit ASCII string
+         *
+         * @return The object that matches the key, or null.
+         */
+        public Object get(final String key)
+        {
+
+            final int len = key.length();
+
+            /* If the name is too long, we won't find it, this also keeps us
+             * from overflowing m_charBuffer
+             */
+            if (m_charBuffer.length < len)
+                return null;
+
+            Node node = m_Root;
+            switch (len) // optimize the look up based on the number of chars
+            {
+                // case 0 looks silly, but the generated bytecode runs
+                // faster for lookup of elements of length 2 with this in
+                // and a fair bit faster.  Don't know why.
+                case 0 :
+                    {
+                        return null;
+                    }
+
+                case 1 :
+                    {
+                        final char ch = key.charAt(0);
+                        if (ch < ALPHA_SIZE)
+                        {
+                            node = node.m_nextChar[ch];
+                            if (node != null)
+                                return node.m_Value;
+                        }
+                        return null;
+                    }
+                    //                comment out case 2 because the default is faster            
+                    //                case 2 :
+                    //                    {
+                    //                        final char ch0 = key.charAt(0);
+                    //                        final char ch1 = key.charAt(1);
+                    //                        if (ch0 < ALPHA_SIZE && ch1 < ALPHA_SIZE)
+                    //                        {
+                    //                            node = node.m_nextChar[ch0];
+                    //                            if (node != null)
+                    //                            {
+                    //                        
+                    //                                if (ch1 < ALPHA_SIZE) 
+                    //                                {
+                    //                                    node = node.m_nextChar[ch1];
+                    //                                    if (node != null)
+                    //                                        return node.m_Value;
+                    //                                }
+                    //                            }
+                    //                        }
+                    //                        return null;
+                    //                   }
+                default :
+                    {
+                        for (int i = 0; i < len; i++)
+                        {
+                            // A thread-safe way to loop over the characters
+                            final char ch = key.charAt(i);
+                            if (ALPHA_SIZE <= ch)
+                            {
+                                // the key is not 7-bit ASCII so we won't find it here
+                                return null;
+                            }
+
+                            node = node.m_nextChar[ch];
+                            if (node == null)
+                                return null;
+                        }
+
+                        return node.m_Value;
+                    }
+            }
+        }
+
+        /**
+         * The node representation for the trie.
+         * @xsl.usage internal
+         */
+        private class Node
+        {
+
+            /**
+             * Constructor, creates a Node[ALPHA_SIZE].
+             */
+            Node()
+            {
+                m_nextChar = new Node[ALPHA_SIZE];
+                m_Value = null;
+            }
+
+            /** The next nodes.   */
+            final Node m_nextChar[];
+
+            /** The value.   */
+            Object m_Value;
+        }
+        /**
+         * Construct the trie from another Trie.
+         * Both the existing Trie and this new one share the same table for
+         * lookup, and it is assumed that the table is fully populated and
+         * not changing anymore.
+         * 
+         * @param existingTrie the Trie that this one is a copy of.
+         */
+        public Trie(Trie existingTrie)
+        {
+            // copy some fields from the existing Trie into this one.
+            m_Root = existingTrie.m_Root;
+            m_lowerCaseOnly = existingTrie.m_lowerCaseOnly;
+
+            // get a buffer just big enough to hold the longest key in the table.
+            int max = existingTrie.getLongestKeyLength();
+            m_charBuffer = new char[max];
+        }
+
+        /**
+         * Get an object that matches the key.
+         * This method is faster than get(), but is not thread-safe.
+         *
+         * @param key must be a 7-bit ASCII string
+         *
+         * @return The object that matches the key, or null.
+         */
+        public Object get2(final String key)
+        {
+
+            final int len = key.length();
+
+            /* If the name is too long, we won't find it, this also keeps us
+             * from overflowing m_charBuffer
+             */
+            if (m_charBuffer.length < len)
+                return null;
+
+            Node node = m_Root;
+            switch (len) // optimize the look up based on the number of chars
+            {
+                // case 0 looks silly, but the generated bytecode runs
+                // faster for lookup of elements of length 2 with this in
+                // and a fair bit faster.  Don't know why.
+                case 0 :
+                    {
+                        return null;
+                    }
+
+                case 1 :
+                    {
+                        final char ch = key.charAt(0);
+                        if (ch < ALPHA_SIZE)
+                        {
+                            node = node.m_nextChar[ch];
+                            if (node != null)
+                                return node.m_Value;
+                        }
+                        return null;
+                    }
+                default :
+                    {
+                        /* Copy string into array. This is not thread-safe because
+                         * it modifies the contents of m_charBuffer. If multiple
+                         * threads were to use this Trie they all would be
+                         * using this same array (not good). So this 
+                         * method is not thread-safe, but it is faster because
+                         * converting to a char[] and looping over elements of
+                         * the array is faster than a String's charAt(i).
+                         */
+                        key.getChars(0, len, m_charBuffer, 0);
+
+                        for (int i = 0; i < len; i++)
+                        {
+                            final char ch = m_charBuffer[i];
+                            if (ALPHA_SIZE <= ch)
+                            {
+                                // the key is not 7-bit ASCII so we won't find it here
+                                return null;
+                            }
+
+                            node = node.m_nextChar[ch];
+                            if (node == null)
+                                return null;
+                        }
+
+                        return node.m_Value;
+                    }
+            }
+        }
+
+        /**
+         * Get the length of the longest key used in the table. 
+         */
+        public int getLongestKeyLength()
+        {
+            return m_charBuffer.length;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/ToSAXHandler.java b/src/main/java/org/apache/xml/serializer/ToSAXHandler.java
new file mode 100644
index 0000000..97adcba
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ToSAXHandler.java
@@ -0,0 +1,438 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ToSAXHandler.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.util.Vector;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * This class is used to provide a base behavior to be inherited
+ * by other To...SAXHandler serializers.
+ * 
+ * This class is not a public API.
+ * 
+ * @xsl.usage internal
+ */
+public abstract class ToSAXHandler extends SerializerBase 
+{
+    public ToSAXHandler()
+    {
+    }
+
+    public ToSAXHandler(
+        ContentHandler hdlr,
+        LexicalHandler lex,
+        String encoding)
+    {
+        setContentHandler(hdlr);
+        setLexHandler(lex);
+        setEncoding(encoding);
+    }
+    public ToSAXHandler(ContentHandler handler, String encoding)
+    {
+        setContentHandler(handler);
+        setEncoding(encoding);
+    }
+
+    /**
+     * Underlying SAX handler. Taken from XSLTC
+     */
+    protected ContentHandler m_saxHandler;
+
+    /**
+     * Underlying LexicalHandler. Taken from XSLTC
+     */
+    protected LexicalHandler m_lexHandler;
+
+    /**
+     * A startPrefixMapping() call on a ToSAXHandler will pass that call
+     * on to the wrapped ContentHandler, but should we also mirror these calls
+     * with matching attributes, if so this field is true.
+     * For example if this field is true then a call such as
+     * startPrefixMapping("prefix1","uri1") will also cause the additional
+     * internally generated attribute xmlns:prefix1="uri1" to be effectively added
+     * to the attributes passed to the wrapped ContentHandler.
+     */ 
+    private boolean m_shouldGenerateNSAttribute = true;
+    
+    /** If this is true, then the content handler wrapped by this
+     * serializer implements the TransformState interface which
+     * will give the content handler access to the state of
+     * the transform. */
+    protected TransformStateSetter m_state = null;
+
+    /**
+     * Pass callback to the SAX Handler
+     */
+    protected void startDocumentInternal() throws SAXException
+    {
+        if (m_needToCallStartDocument)  
+        {
+            super.startDocumentInternal();
+
+            m_saxHandler.startDocument();
+            m_needToCallStartDocument = false;
+        }
+    }
+    /**
+     * Do nothing.
+     * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String)
+     */
+    public void startDTD(String arg0, String arg1, String arg2)
+        throws SAXException
+    {
+        // do nothing for now
+    }
+
+    /**
+     * Receive notification of character data.
+     *
+     * @param characters The string of characters to process.
+     *
+     * @throws org.xml.sax.SAXException
+     *
+     * @see ExtendedContentHandler#characters(String)
+     */
+    public void characters(String characters) throws SAXException
+    {
+        final int len = characters.length();
+        if (len > m_charsBuff.length)
+        {
+           m_charsBuff = new char[len*2 + 1];             
+        }
+        characters.getChars(0,len, m_charsBuff, 0);   
+        characters(m_charsBuff, 0, len);
+    }
+
+    /**
+     * Receive notification of a comment.
+     *
+     * @see ExtendedLexicalHandler#comment(String)
+     */
+    public void comment(String comment) throws SAXException
+    {
+        flushPending();
+
+        // Ignore if a lexical handler has not been set
+        if (m_lexHandler != null)
+        {
+            final int len = comment.length();
+            if (len > m_charsBuff.length)
+            {
+               m_charsBuff = new char[len*2 + 1];              
+            }
+            comment.getChars(0,len, m_charsBuff, 0);            
+            m_lexHandler.comment(m_charsBuff, 0, len);
+            // time to fire off comment event
+            if (m_tracer != null)
+                super.fireCommentEvent(m_charsBuff, 0, len);
+        }
+
+    }
+
+    /**
+     * Do nothing as this is an abstract class. All subclasses will need to
+     * define their behavior if it is different.
+     * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
+     */
+    public void processingInstruction(String target, String data)
+        throws SAXException
+    {
+        // Redefined in SAXXMLOutput
+    }
+
+    protected void closeStartTag() throws SAXException
+    {
+    }
+
+    protected void closeCDATA() throws SAXException
+    {
+        // Redefined in SAXXMLOutput
+    }
+    
+    /**
+     * Receive notification of the beginning of an element, although this is a
+     * SAX method additional namespace or attribute information can occur before
+     * or after this call, that is associated with this element.
+     *
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     * @see org.xml.sax.ContentHandler#startElement
+     * @see org.xml.sax.ContentHandler#endElement
+     * @see org.xml.sax.AttributeList
+     *
+     * @throws org.xml.sax.SAXException
+     *
+     * @see org.xml.sax.ContentHandler#startElement(String,String,String,Attributes)
+     */
+    public void startElement(
+        String arg0,
+        String arg1,
+        String arg2,
+        Attributes arg3)
+        throws SAXException
+    {
+        if (m_state != null) {
+            m_state.resetState(getTransformer());
+        }
+
+        // fire off the start element event
+        if (m_tracer != null)
+            super.fireStartElem(arg2);
+    }
+
+    /**
+     * Sets the LexicalHandler.
+     * @param _lexHandler The LexicalHandler to set
+     */
+    public void setLexHandler(LexicalHandler _lexHandler)
+    {
+        this.m_lexHandler = _lexHandler;
+    }
+
+    /**
+     * Sets the SAX ContentHandler.
+     * @param _saxHandler The ContentHandler to set
+     */
+    public void setContentHandler(ContentHandler _saxHandler)
+    {
+        this.m_saxHandler = _saxHandler;
+        if (m_lexHandler == null && _saxHandler instanceof LexicalHandler)
+        {
+            // we are not overwriting an existing LexicalHandler, and _saxHandler
+            // is also implements LexicalHandler, so lets use it
+            m_lexHandler = (LexicalHandler) _saxHandler;
+        }
+    }
+
+    /**
+     * Does nothing. The setting of CDATA section elements has an impact on
+     * stream serializers.
+     * @see SerializationHandler#setCdataSectionElements(java.util.Vector)
+     */
+    public void setCdataSectionElements(Vector URI_and_localNames)
+    {
+        // do nothing
+    }
+    
+    /** Set whether or not namespace declarations (e.g. 
+     * xmlns:foo) should appear as attributes of 
+     * elements
+     * @param doOutputNSAttr whether or not namespace declarations
+     * should appear as attributes
+     */
+    public void setShouldOutputNSAttr(boolean doOutputNSAttr)
+    {
+        m_shouldGenerateNSAttribute = doOutputNSAttr;
+    }
+ 
+    /** 
+     * Returns true if namespace declarations from calls such as
+     * startPrefixMapping("prefix1","uri1") should
+     * also be mirrored with self generated additional attributes of elements 
+     * that declare the namespace, for example the attribute xmlns:prefix1="uri1"
+     */
+    boolean getShouldOutputNSAttr()
+    {
+        return m_shouldGenerateNSAttribute;
+    }
+
+    /**
+     * This method flushes any pending events, which can be startDocument()
+     * closing the opening tag of an element, or closing an open CDATA section.
+     */
+    public void flushPending() throws SAXException
+    {
+    
+            if (m_needToCallStartDocument)
+            {
+                startDocumentInternal();
+                m_needToCallStartDocument = false;
+            }
+
+            if (m_elemContext.m_startTagOpen)
+            {
+                closeStartTag();
+                m_elemContext.m_startTagOpen = false;
+            }
+            
+            if (m_cdataTagOpen)
+            {
+                closeCDATA();
+                m_cdataTagOpen = false;
+            }
+
+    }
+
+    /**
+     * Pass in a reference to a TransformState object, which
+     * can be used during SAX ContentHandler events to obtain
+     * information about he state of the transformation. This
+     * method will be called  before each startDocument event.
+     *
+     * @param ts A reference to a TransformState object
+     */
+    public void setTransformState(TransformStateSetter ts) {
+        this.m_state = ts;
+    }
+    
+    /**
+     * Receives notification that an element starts, but attributes are not
+     * fully known yet.
+     *
+     * @param uri the URI of the namespace of the element (optional)
+     * @param localName the element name, but without prefix (optional)
+     * @param qName the element name, with prefix, if any (required)
+     *
+     * @see ExtendedContentHandler#startElement(String, String, String)
+     */
+    public void startElement(String uri, String localName, String qName)
+        throws SAXException {
+            
+        if (m_state != null) {
+            m_state.resetState(getTransformer());
+        }
+
+        // fire off the start element event
+        if (m_tracer != null)
+            super.fireStartElem(qName);         
+    }
+
+    /**
+     * An element starts, but attributes are not fully known yet.
+     *
+     * @param qName the element name, with prefix (if any).
+
+     * @see ExtendedContentHandler#startElement(String)
+     */
+    public void startElement(String qName) throws SAXException {
+        if (m_state != null) {
+            m_state.resetState(getTransformer());
+        }        
+        // fire off the start element event
+        if (m_tracer != null)
+            super.fireStartElem(qName);              
+    }
+    
+    /**
+     * This method gets the node's value as a String and uses that String as if
+     * it were an input character notification.
+     * @param node the Node to serialize
+     * @throws org.xml.sax.SAXException
+     */    
+    public void characters(org.w3c.dom.Node node)
+        throws org.xml.sax.SAXException
+    {
+        // remember the current node
+        if (m_state != null)
+        {
+            m_state.setCurrentNode(node);
+        }
+        
+        // Get the node's value as a String and use that String as if
+        // it were an input character notification.
+        String data = node.getNodeValue();
+        if (data != null) {
+            this.characters(data);
+        }
+    }    
+
+    /**
+     * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
+     */
+    public void fatalError(SAXParseException exc) throws SAXException {
+        super.fatalError(exc);
+        
+        m_needToCallStartDocument = false;
+        
+        if (m_saxHandler instanceof ErrorHandler) {
+            ((ErrorHandler)m_saxHandler).fatalError(exc);            
+        }
+    }
+
+    /**
+     * @see org.xml.sax.ErrorHandler#error(SAXParseException)
+     */
+    public void error(SAXParseException exc) throws SAXException {
+        super.error(exc);
+        
+        if (m_saxHandler instanceof ErrorHandler)
+            ((ErrorHandler)m_saxHandler).error(exc);        
+        
+    }
+
+    /**
+     * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
+     */
+    public void warning(SAXParseException exc) throws SAXException {
+        super.warning(exc);
+        
+        if (m_saxHandler instanceof ErrorHandler)
+            ((ErrorHandler)m_saxHandler).warning(exc);        
+    }
+    
+       
+    /**
+     * Try's to reset the super class and reset this class for 
+     * re-use, so that you don't need to create a new serializer 
+     * (mostly for performance reasons).
+     * 
+     * @return true if the class was successfuly reset.
+     * @see Serializer#reset()
+     */
+    public boolean reset()
+    {
+        boolean wasReset = false;
+        if (super.reset())
+        {
+            resetToSAXHandler();
+            wasReset = true;
+        }
+        return wasReset;
+    }
+    
+    /**
+     * Reset all of the fields owned by ToSAXHandler class
+     *
+     */
+    private void resetToSAXHandler()
+    {
+        this.m_lexHandler = null;
+        this.m_saxHandler = null;
+        this.m_state = null;
+        this.m_shouldGenerateNSAttribute = false;
+    }  
+
+    /**
+     * Add a unique attribute
+     */
+    public void addUniqueAttribute(String qName, String value, int flags)
+        throws SAXException
+    {
+        addAttribute(qName, value); 
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/ToStream.java b/src/main/java/org/apache/xml/serializer/ToStream.java
new file mode 100644
index 0000000..c56f747
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ToStream.java
@@ -0,0 +1,3601 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ToStream.java 475894 2006-11-16 19:43:59Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.EmptyStackException;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.serializer.utils.MsgKey;
+import org.apache.xml.serializer.utils.Utils;
+import org.apache.xml.serializer.utils.WrappedRuntimeException;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * This abstract class is a base class for other stream 
+ * serializers (xml, html, text ...) that write output to a stream.
+ * 
+ * @xsl.usage internal
+ */
+abstract public class ToStream extends SerializerBase
+{
+
+    private static final String COMMENT_BEGIN = "<!--";
+    private static final String COMMENT_END = "-->";
+
+    /** Stack to keep track of disabling output escaping. */
+    protected BoolStack m_disableOutputEscapingStates = new BoolStack();
+
+
+    /**
+     * The encoding information associated with this serializer.
+     * Although initially there is no encoding,
+     * there is a dummy EncodingInfo object that will say
+     * that every character is in the encoding. This is useful
+     * for a serializer that is in temporary output state and has
+     * no associated encoding. A serializer in final output state
+     * will have an encoding, and will worry about whether 
+     * single chars or surrogate pairs of high/low chars form
+     * characters in the output encoding. 
+     */
+    EncodingInfo m_encodingInfo = new EncodingInfo(null,null, '\u0000');
+    
+    /**
+     * Stack to keep track of whether or not we need to
+     * preserve whitespace.
+     * 
+     * Used to push/pop values used for the field m_ispreserve, but
+     * m_ispreserve is only relevant if m_doIndent is true.
+     * If m_doIndent is false this field has no impact.
+     * 
+     */
+    protected BoolStack m_preserves = new BoolStack();
+
+    /**
+     * State flag to tell if preservation of whitespace
+     * is important. 
+     * 
+     * Used only in shouldIndent() but only if m_doIndent is true.
+     * If m_doIndent is false this flag has no impact.
+     * 
+     */
+    protected boolean m_ispreserve = false;
+
+    /**
+     * State flag that tells if the previous node processed
+     * was text, so we can tell if we should preserve whitespace.
+     * 
+     * Used in endDocument() and shouldIndent() but
+     * only if m_doIndent is true. 
+     * If m_doIndent is false this flag has no impact.
+     */
+    protected boolean m_isprevtext = false;
+        
+    private static final char[] s_systemLineSep;
+    static {
+        SecuritySupport ss = SecuritySupport.getInstance();
+        s_systemLineSep = ss.getSystemProperty("line.separator").toCharArray();
+    }
+    
+    /**
+     * The system line separator for writing out line breaks.
+     * The default value is from the system property,
+     * but this value can be set through the xsl:output
+     * extension attribute xalan:line-separator.
+     */
+    protected char[] m_lineSep = s_systemLineSep;
+        
+        
+    /**
+     * True if the the system line separator is to be used.
+     */    
+    protected boolean m_lineSepUse = true;    
+
+    /**
+     * The length of the line seperator, since the write is done
+     * one character at a time.
+     */
+    protected int m_lineSepLen = m_lineSep.length;
+
+    /**
+     * Map that tells which characters should have special treatment, and it
+     *  provides character to entity name lookup.
+     */
+    protected CharInfo m_charInfo;
+
+    /** True if we control the buffer, and we should flush the output on endDocument. */
+    boolean m_shouldFlush = true;
+
+    /**
+     * Add space before '/>' for XHTML.
+     */
+    protected boolean m_spaceBeforeClose = false;
+
+    /**
+     * Flag to signal that a newline should be added.
+     * 
+     * Used only in indent() which is called only if m_doIndent is true.
+     * If m_doIndent is false this flag has no impact.
+     */
+    boolean m_startNewLine;
+
+    /**
+     * Tells if we're in an internal document type subset.
+     */
+    protected boolean m_inDoctype = false;
+
+    /**
+       * Flag to quickly tell if the encoding is UTF8.
+       */
+    boolean m_isUTF8 = false;
+
+
+    /**
+     * remembers if we are in between the startCDATA() and endCDATA() callbacks
+     */
+    protected boolean m_cdataStartCalled = false;
+    
+    /**
+     * If this flag is true DTD entity references are not left as-is,
+     * which is exiting older behavior.
+     */
+    private boolean m_expandDTDEntities = true;
+  
+
+    /**
+     * Default constructor
+     */
+    public ToStream()
+    {
+    }
+
+    /**
+     * This helper method to writes out "]]>" when closing a CDATA section.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    protected void closeCDATA() throws org.xml.sax.SAXException
+    {
+        try
+        {
+            m_writer.write(CDATA_DELIMITER_CLOSE);
+            // write out a CDATA section closing "]]>"
+            m_cdataTagOpen = false; // Remember that we have done so.
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+    }
+
+    /**
+     * Serializes the DOM node. Throws an exception only if an I/O
+     * exception occured while serializing.
+     *
+     * @param node Node to serialize.
+     * @throws IOException An I/O exception occured while serializing
+     */
+    public void serialize(Node node) throws IOException
+    {
+
+        try
+        {
+            TreeWalker walker =
+                new TreeWalker(this);
+
+            walker.traverse(node);
+        }
+        catch (org.xml.sax.SAXException se)
+        {
+            throw new WrappedRuntimeException(se);
+        }
+    }
+
+    /**
+     * Taken from XSLTC 
+     */
+    protected boolean m_escaping = true;
+
+    /**
+     * Flush the formatter's result stream.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    protected final void flushWriter() throws org.xml.sax.SAXException
+    {
+        final java.io.Writer writer = m_writer;
+        if (null != writer)
+        {
+            try
+            {
+                if (writer instanceof WriterToUTF8Buffered)
+                {
+                    if (m_shouldFlush)
+                         ((WriterToUTF8Buffered) writer).flush();
+                    else
+                         ((WriterToUTF8Buffered) writer).flushBuffer();
+                }
+                if (writer instanceof WriterToASCI)
+                {
+                    if (m_shouldFlush)
+                        writer.flush();
+                }
+                else
+                {
+                    // Flush always. 
+                    // Not a great thing if the writer was created 
+                    // by this class, but don't have a choice.
+                    writer.flush();
+                }
+            }
+            catch (IOException ioe)
+            {
+                throw new org.xml.sax.SAXException(ioe);
+            }
+        }
+    }
+
+    OutputStream m_outputStream;
+    /**
+     * Get the output stream where the events will be serialized to.
+     *
+     * @return reference to the result stream, or null of only a writer was
+     * set.
+     */
+    public OutputStream getOutputStream()
+    {
+        return m_outputStream;
+    }
+
+    // Implement DeclHandler
+
+    /**
+     *   Report an element type declaration.
+     *  
+     *   <p>The content model will consist of the string "EMPTY", the
+     *   string "ANY", or a parenthesised group, optionally followed
+     *   by an occurrence indicator.  The model will be normalized so
+     *   that all whitespace is removed,and will include the enclosing
+     *   parentheses.</p>
+     *  
+     *   @param name The element type name.
+     *   @param model The content model as a normalized string.
+     *   @exception SAXException The application may raise an exception.
+     */
+    public void elementDecl(String name, String model) throws SAXException
+    {
+        // Do not inline external DTD
+        if (m_inExternalDTD)
+            return;
+        try
+        {
+            final java.io.Writer writer = m_writer;
+            DTDprolog();
+
+            writer.write("<!ELEMENT ");
+            writer.write(name);
+            writer.write(' ');
+            writer.write(model);
+            writer.write('>');
+            writer.write(m_lineSep, 0, m_lineSepLen);
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+    }
+
+    /**
+     * Report an internal entity declaration.
+     *
+     * <p>Only the effective (first) declaration for each entity
+     * will be reported.</p>
+     *
+     * @param name The name of the entity.  If it is a parameter
+     *        entity, the name will begin with '%'.
+     * @param value The replacement text of the entity.
+     * @exception SAXException The application may raise an exception.
+     * @see #externalEntityDecl
+     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+     */
+    public void internalEntityDecl(String name, String value)
+        throws SAXException
+    {
+        // Do not inline external DTD
+        if (m_inExternalDTD)
+            return;
+        try
+        {
+            DTDprolog();
+            outputEntityDecl(name, value);
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+    }
+
+    /**
+     * Output the doc type declaration.
+     *
+     * @param name non-null reference to document type name.
+     * NEEDSDOC @param value
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    void outputEntityDecl(String name, String value) throws IOException
+    {
+        final java.io.Writer writer = m_writer;
+        writer.write("<!ENTITY ");
+        writer.write(name);
+        writer.write(" \"");
+        writer.write(value);
+        writer.write("\">");
+        writer.write(m_lineSep, 0, m_lineSepLen);
+    }
+
+    /**
+     * Output a system-dependent line break.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    protected final void outputLineSep() throws IOException
+    {
+
+        m_writer.write(m_lineSep, 0, m_lineSepLen);
+    }
+
+    void setProp(String name, String val, boolean defaultVal) {
+        if (val != null) {
+
+
+            char first = getFirstCharLocName(name);
+            switch (first) {
+            case 'c':
+                if (OutputKeys.CDATA_SECTION_ELEMENTS.equals(name)) {
+                    String cdataSectionNames = val;
+                    addCdataSectionElements(cdataSectionNames);
+                }
+                break;
+            case 'd':
+                if (OutputKeys.DOCTYPE_SYSTEM.equals(name)) {
+                    this.m_doctypeSystem = val;
+                } else if (OutputKeys.DOCTYPE_PUBLIC.equals(name)) {
+                    this.m_doctypePublic = val;
+                    if (val.startsWith("-//W3C//DTD XHTML"))
+                        m_spaceBeforeClose = true;
+                }
+                break;
+            case 'e':
+                String newEncoding = val;
+                if (OutputKeys.ENCODING.equals(name)) {
+                    String possible_encoding = Encodings.getMimeEncoding(val);
+                    if (possible_encoding != null) {
+                        // if the encoding is being set, try to get the
+                        // preferred
+                        // mime-name and set it too.
+                        super.setProp("mime-name", possible_encoding,
+                                defaultVal);
+                    }
+                    final String oldExplicitEncoding = getOutputPropertyNonDefault(OutputKeys.ENCODING);
+                    final String oldDefaultEncoding  = getOutputPropertyDefault(OutputKeys.ENCODING);
+                    if ( (defaultVal && ( oldDefaultEncoding == null || !oldDefaultEncoding.equalsIgnoreCase(newEncoding)))
+                            || ( !defaultVal && (oldExplicitEncoding == null || !oldExplicitEncoding.equalsIgnoreCase(newEncoding) ))) {
+                       // We are trying to change the default or the non-default setting of the encoding to a different value
+                       // from what it was
+                       
+                       EncodingInfo encodingInfo = Encodings.getEncodingInfo(newEncoding);
+                       if (newEncoding != null && encodingInfo.name == null) {
+                        // We tried to get an EncodingInfo for Object for the given
+                        // encoding, but it came back with an internall null name
+                        // so the encoding is not supported by the JDK, issue a message.
+                        final String msg = Utils.messages.createMessage(
+                                MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ newEncoding });
+                        
+                        final String msg2 = 
+                            "Warning: encoding \"" + newEncoding + "\" not supported, using "
+                                   + Encodings.DEFAULT_MIME_ENCODING;
+                        try {
+                                // Prepare to issue the warning message
+                                final Transformer tran = super.getTransformer();
+                                if (tran != null) {
+                                    final ErrorListener errHandler = tran
+                                            .getErrorListener();
+                                    // Issue the warning message
+                                    if (null != errHandler
+                                            && m_sourceLocator != null) {
+                                        errHandler
+                                                .warning(new TransformerException(
+                                                        msg, m_sourceLocator));
+                                        errHandler
+                                                .warning(new TransformerException(
+                                                        msg2, m_sourceLocator));
+                                    } else {
+                                        System.out.println(msg);
+                                        System.out.println(msg2);
+                                    }
+                                } else {
+                                    System.out.println(msg);
+                                    System.out.println(msg2);
+                                }
+                            } catch (Exception e) {
+                            }
+
+                            // We said we are using UTF-8, so use it
+                            newEncoding = Encodings.DEFAULT_MIME_ENCODING;
+                            val = Encodings.DEFAULT_MIME_ENCODING; // to store the modified value into the properties a little later
+                            encodingInfo = Encodings.getEncodingInfo(newEncoding);
+
+                        } 
+                       // The encoding was good, or was forced to UTF-8 above
+                       
+                       
+                       // If there is already a non-default set encoding and we 
+                       // are trying to set the default encoding, skip the this block
+                       // as the non-default value is already the one to use.
+                       if (defaultVal == false || oldExplicitEncoding == null) {
+                           m_encodingInfo = encodingInfo;
+                           if (newEncoding != null)
+                               m_isUTF8 = newEncoding.equals(Encodings.DEFAULT_MIME_ENCODING);
+                           
+                           // if there was a previously set OutputStream
+                           OutputStream os = getOutputStream();
+                           if (os != null) {
+                               Writer w = getWriter();
+                               
+                               // If the writer was previously set, but
+                               // set by the user, or if the new encoding is the same
+                               // as the old encoding, skip this block
+                               String oldEncoding = getOutputProperty(OutputKeys.ENCODING);
+                               if ((w == null || !m_writer_set_by_user) 
+                                       && !newEncoding.equalsIgnoreCase(oldEncoding)) {
+                                   // Make the change of encoding in our internal
+                                   // table, then call setOutputStreamInternal
+                                   // which will stomp on the old Writer (if any)
+                                   // with a new Writer with the new encoding.
+                                   super.setProp(name, val, defaultVal);
+                                   setOutputStreamInternal(os,false);
+                               }
+                           }
+                       }
+                    }
+                }
+                break;
+            case 'i':
+                if (OutputPropertiesFactory.S_KEY_INDENT_AMOUNT.equals(name)) {
+                    setIndentAmount(Integer.parseInt(val));
+                } else if (OutputKeys.INDENT.equals(name)) {
+                    boolean b = "yes".equals(val) ? true : false;
+                    m_doIndent = b;
+                }
+
+                break;
+            case 'l':
+                if (OutputPropertiesFactory.S_KEY_LINE_SEPARATOR.equals(name)) {
+                    m_lineSep = val.toCharArray();
+                    m_lineSepLen = m_lineSep.length;
+                }
+
+                break;
+            case 'm':
+                if (OutputKeys.MEDIA_TYPE.equals(name)) {
+                    m_mediatype = val;
+                }
+                break;
+            case 'o':
+                if (OutputKeys.OMIT_XML_DECLARATION.equals(name)) {
+                    boolean b = "yes".equals(val) ? true : false;
+                    this.m_shouldNotWriteXMLHeader = b;
+                }
+                break;
+            case 's':
+                // if standalone was explicitly specified
+                if (OutputKeys.STANDALONE.equals(name)) {
+                    if (defaultVal) {
+                        setStandaloneInternal(val);
+                    } else {
+                        m_standaloneWasSpecified = true;
+                        setStandaloneInternal(val);
+                    }
+                }
+
+                break;
+            case 'v':
+                if (OutputKeys.VERSION.equals(name)) {
+                    m_version = val;
+                }
+                break;
+            default:
+                break;
+
+            } 
+            super.setProp(name, val, defaultVal);
+        }
+    }
+    /**
+     * Specifies an output format for this serializer. It the
+     * serializer has already been associated with an output format,
+     * it will switch to the new format. This method should not be
+     * called while the serializer is in the process of serializing
+     * a document.
+     *
+     * @param format The output format to use
+     */
+    public void setOutputFormat(Properties format)
+    {
+
+        boolean shouldFlush = m_shouldFlush;
+        
+        if (format != null)
+        {
+            // Set the default values first,
+            // and the non-default values after that, 
+            // just in case there is some unexpected
+            // residual values left over from over-ridden default values
+            Enumeration propNames;
+            propNames = format.propertyNames();
+            while (propNames.hasMoreElements())
+            {
+                String key = (String) propNames.nextElement();
+                // Get the value, possibly a default value
+                String value = format.getProperty(key);
+                // Get the non-default value (if any).
+                String explicitValue = (String) format.get(key);
+                if (explicitValue == null && value != null) {
+                    // This is a default value
+                    this.setOutputPropertyDefault(key,value);
+                }
+                if (explicitValue != null) {
+                    // This is an explicit non-default value
+                    this.setOutputProperty(key,explicitValue);
+                }
+            } 
+        }   
+
+        // Access this only from the Hashtable level... we don't want to 
+        // get default properties.
+        String entitiesFileName =
+            (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
+
+        if (null != entitiesFileName)
+        {
+
+            String method = 
+                (String) format.get(OutputKeys.METHOD);
+            
+            m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
+        }
+
+
+         
+
+        m_shouldFlush = shouldFlush;
+    }
+
+    /**
+     * Returns the output format for this serializer.
+     *
+     * @return The output format in use
+     */
+    public Properties getOutputFormat() {
+        Properties def = new Properties();
+        {
+            Set s = getOutputPropDefaultKeys();
+            Iterator i = s.iterator();
+            while (i.hasNext()) {
+                String key = (String) i.next();
+                String val = getOutputPropertyDefault(key);
+                def.put(key, val);
+            }
+        }
+        
+        Properties props = new Properties(def);
+        {
+            Set s = getOutputPropKeys();
+            Iterator i = s.iterator();
+            while (i.hasNext()) {
+                String key = (String) i.next();
+                String val = getOutputPropertyNonDefault(key);
+                if (val != null)
+                    props.put(key, val);
+            }
+        }
+        return props;
+    }
+
+    /**
+     * Specifies a writer to which the document should be serialized.
+     * This method should not be called while the serializer is in
+     * the process of serializing a document.
+     *
+     * @param writer The output writer stream
+     */
+    public void setWriter(Writer writer)
+    {        
+        setWriterInternal(writer, true);
+    }
+    
+    private boolean m_writer_set_by_user;
+    private void setWriterInternal(Writer writer, boolean setByUser) {
+
+        m_writer_set_by_user = setByUser;
+        m_writer = writer;
+        // if we are tracing events we need to trace what
+        // characters are written to the output writer.
+        if (m_tracer != null) {
+            boolean noTracerYet = true;
+            Writer w2 = m_writer;
+            while (w2 instanceof WriterChain) {
+                if (w2 instanceof SerializerTraceWriter) {
+                    noTracerYet = false;
+                    break;
+                }
+                w2 = ((WriterChain)w2).getWriter();
+            }
+            if (noTracerYet)
+                m_writer = new SerializerTraceWriter(m_writer, m_tracer);
+        }
+    }
+    
+    /**
+     * Set if the operating systems end-of-line line separator should
+     * be used when serializing.  If set false NL character 
+     * (decimal 10) is left alone, otherwise the new-line will be replaced on
+     * output with the systems line separator. For example on UNIX this is
+     * NL, while on Windows it is two characters, CR NL, where CR is the
+     * carriage-return (decimal 13).
+     *  
+     * @param use_sytem_line_break True if an input NL is replaced with the 
+     * operating systems end-of-line separator.
+     * @return The previously set value of the serializer.
+     */
+    public boolean setLineSepUse(boolean use_sytem_line_break)
+    {
+        boolean oldValue = m_lineSepUse;
+        m_lineSepUse = use_sytem_line_break;
+        return oldValue;
+    }
+
+    /**
+     * Specifies an output stream to which the document should be
+     * serialized. This method should not be called while the
+     * serializer is in the process of serializing a document.
+     * <p>
+     * The encoding specified in the output properties is used, or
+     * if no encoding was specified, the default for the selected
+     * output method.
+     *
+     * @param output The output stream
+     */
+    public void setOutputStream(OutputStream output)
+    {
+        setOutputStreamInternal(output, true);
+    }
+    
+    private void setOutputStreamInternal(OutputStream output, boolean setByUser)
+    {
+        m_outputStream = output;
+        String encoding = getOutputProperty(OutputKeys.ENCODING);        
+        if (Encodings.DEFAULT_MIME_ENCODING.equalsIgnoreCase(encoding))
+        {
+            // We wrap the OutputStream with a writer, but
+            // not one set by the user
+            setWriterInternal(new WriterToUTF8Buffered(output), false);
+        } else if (
+                "WINDOWS-1250".equals(encoding)
+                || "US-ASCII".equals(encoding)
+                || "ASCII".equals(encoding))
+        {
+            setWriterInternal(new WriterToASCI(output), false);
+        } else if (encoding != null) {
+            Writer osw = null;
+                try
+                {
+                    osw = Encodings.getWriter(output, encoding);
+                }
+                catch (UnsupportedEncodingException uee)
+                {
+                    osw = null;
+                }
+
+            
+            if (osw == null) {
+                System.out.println(
+                    "Warning: encoding \""
+                        + encoding
+                        + "\" not supported"
+                        + ", using "
+                        + Encodings.DEFAULT_MIME_ENCODING);
+
+                encoding = Encodings.DEFAULT_MIME_ENCODING;
+                setEncoding(encoding);
+                try {
+                    osw = Encodings.getWriter(output, encoding);
+                } catch (UnsupportedEncodingException e) {
+                    // We can't really get here, UTF-8 is always supported
+                    // This try-catch exists to make the compiler happy
+                    e.printStackTrace();
+                }
+            }
+            setWriterInternal(osw,false);
+        }
+        else {
+            // don't have any encoding, but we have an OutputStream
+            Writer osw = new OutputStreamWriter(output);
+            setWriterInternal(osw,false);
+        }
+    }
+
+    /**
+     * @see SerializationHandler#setEscaping(boolean)
+     */
+    public boolean setEscaping(boolean escape)
+    {
+        final boolean temp = m_escaping;
+        m_escaping = escape;
+        return temp;
+
+    }
+
+
+    /**
+     * Might print a newline character and the indentation amount
+     * of the given depth.
+     * 
+     * @param depth the indentation depth (element nesting depth)
+     *
+     * @throws org.xml.sax.SAXException if an error occurs during writing.
+     */
+    protected void indent(int depth) throws IOException
+    {
+
+        if (m_startNewLine)
+            outputLineSep();
+        /* For m_indentAmount > 0 this extra test might be slower
+         * but Xalan's default value is 0, so this extra test
+         * will run faster in that situation.
+         */
+        if (m_indentAmount > 0)
+            printSpace(depth * m_indentAmount);
+
+    }
+    
+    /**
+     * Indent at the current element nesting depth.
+     * @throws IOException
+     */
+    protected void indent() throws IOException
+    {
+        indent(m_elemContext.m_currentElemDepth);
+    }
+    /**
+     * Prints <var>n</var> spaces.
+     * @param n         Number of spaces to print.
+     *
+     * @throws org.xml.sax.SAXException if an error occurs when writing.
+     */
+    private void printSpace(int n) throws IOException
+    {
+        final java.io.Writer writer = m_writer;
+        for (int i = 0; i < n; i++)
+        {
+            writer.write(' ');
+        }
+
+    }
+
+    /**
+     * Report an attribute type declaration.
+     *
+     * <p>Only the effective (first) declaration for an attribute will
+     * be reported.  The type will be one of the strings "CDATA",
+     * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
+     * "ENTITIES", or "NOTATION", or a parenthesized token group with
+     * the separator "|" and all whitespace removed.</p>
+     *
+     * @param eName The name of the associated element.
+     * @param aName The name of the attribute.
+     * @param type A string representing the attribute type.
+     * @param valueDefault A string representing the attribute default
+     *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
+     *        none of these applies.
+     * @param value A string representing the attribute's default value,
+     *        or null if there is none.
+     * @exception SAXException The application may raise an exception.
+     */
+    public void attributeDecl(
+        String eName,
+        String aName,
+        String type,
+        String valueDefault,
+        String value)
+        throws SAXException
+    {
+        // Do not inline external DTD
+        if (m_inExternalDTD)
+            return;
+        try
+        {
+            final java.io.Writer writer = m_writer;
+            DTDprolog();
+
+            writer.write("<!ATTLIST ");
+            writer.write(eName);
+            writer.write(' ');
+
+            writer.write(aName);
+            writer.write(' ');
+            writer.write(type);
+            if (valueDefault != null)
+            {
+                writer.write(' ');
+                writer.write(valueDefault);
+            }
+
+            //writer.write(" ");
+            //writer.write(value);
+            writer.write('>');
+            writer.write(m_lineSep, 0, m_lineSepLen);
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+    }
+
+    /**
+     * Get the character stream where the events will be serialized to.
+     *
+     * @return Reference to the result Writer, or null.
+     */
+    public Writer getWriter()
+    {
+        return m_writer;
+    }
+
+    /**
+     * Report a parsed external entity declaration.
+     *
+     * <p>Only the effective (first) declaration for each entity
+     * will be reported.</p>
+     *
+     * @param name The name of the entity.  If it is a parameter
+     *        entity, the name will begin with '%'.
+     * @param publicId The declared public identifier of the entity, or
+     *        null if none was declared.
+     * @param systemId The declared system identifier of the entity.
+     * @exception SAXException The application may raise an exception.
+     * @see #internalEntityDecl
+     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+     */
+    public void externalEntityDecl(
+        String name,
+        String publicId,
+        String systemId)
+        throws SAXException
+    {
+        try {
+            DTDprolog();
+            
+            m_writer.write("<!ENTITY ");            
+            m_writer.write(name);
+            if (publicId != null) {
+                m_writer.write(" PUBLIC \"");
+                m_writer.write(publicId);
+  
+            }
+            else {
+                m_writer.write(" SYSTEM \"");
+                m_writer.write(systemId);
+            }
+            m_writer.write("\" >");
+            m_writer.write(m_lineSep, 0, m_lineSepLen);
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+
+    }
+
+    /**
+     * Tell if this character can be written without escaping.
+     */
+    protected boolean escapingNotNeeded(char ch)
+    {
+        final boolean ret;
+        if (ch < 127)
+        {
+            // This is the old/fast code here, but is this 
+            // correct for all encodings?
+            if (ch >= CharInfo.S_SPACE || (CharInfo.S_LINEFEED == ch || 
+                    CharInfo.S_CARRIAGERETURN == ch || CharInfo.S_HORIZONAL_TAB == ch))
+                ret= true;
+            else
+                ret = false;
+        }
+        else {            
+            ret = m_encodingInfo.isInEncoding(ch);
+        }
+        return ret;
+    }
+
+    /**
+     * Once a surrogate has been detected, write out the pair of
+     * characters if it is in the encoding, or if there is no
+     * encoding, otherwise write out an entity reference
+     * of the value of the unicode code point of the character
+     * represented by the high/low surrogate pair.
+     * <p>
+     * An exception is thrown if there is no low surrogate in the pair,
+     * because the array ends unexpectely, or if the low char is there
+     * but its value is such that it is not a low surrogate.
+     *
+     * @param c the first (high) part of the surrogate, which
+     * must be confirmed before calling this method.
+     * @param ch Character array.
+     * @param i position Where the surrogate was detected.
+     * @param end The end index of the significant characters.
+     * @return 0 if the pair of characters was written out as-is,
+     * the unicode code point of the character represented by
+     * the surrogate pair if an entity reference with that value
+     * was written out. 
+     * 
+     * @throws IOException
+     * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected.
+     */
+    protected int writeUTF16Surrogate(char c, char ch[], int i, int end)
+        throws IOException
+    {
+        int codePoint = 0;
+        if (i + 1 >= end)
+        {
+            throw new IOException(
+                Utils.messages.createMessage(
+                    MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    new Object[] { Integer.toHexString((int) c)}));
+        }
+        
+        final char high = c;
+        final char low = ch[i+1];
+        if (!Encodings.isLowUTF16Surrogate(low)) {
+            throw new IOException(
+                Utils.messages.createMessage(
+                    MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    new Object[] {
+                        Integer.toHexString((int) c)
+                            + " "
+                            + Integer.toHexString(low)}));
+        }
+
+        final java.io.Writer writer = m_writer;
+                
+        // If we make it to here we have a valid high, low surrogate pair
+        if (m_encodingInfo.isInEncoding(c,low)) {
+            // If the character formed by the surrogate pair
+            // is in the encoding, so just write it out
+            writer.write(ch,i,2);
+        }
+        else {
+            // Don't know what to do with this char, it is
+            // not in the encoding and not a high char in
+            // a surrogate pair, so write out as an entity ref
+            final String encoding = getEncoding();
+            if (encoding != null) {
+                /* The output encoding is known, 
+                 * so somthing is wrong.
+                  */
+                codePoint = Encodings.toCodePoint(high, low);
+                // not in the encoding, so write out a character reference
+                writer.write('&');
+                writer.write('#');
+                writer.write(Integer.toString(codePoint));
+                writer.write(';');
+            } else {
+                /* The output encoding is not known,
+                 * so just write it out as-is.
+                 */
+                writer.write(ch, i, 2);
+            }
+        }
+        // non-zero only if character reference was written out.
+        return codePoint;
+    }
+
+    /**
+     * Handle one of the default entities, return false if it
+     * is not a default entity.
+     *
+     * @param ch character to be escaped.
+     * @param i index into character array.
+     * @param chars non-null reference to character array.
+     * @param len length of chars.
+     * @param fromTextNode true if the characters being processed
+     * are from a text node, false if they are from an attribute value
+     * @param escLF true if the linefeed should be escaped.
+     *
+     * @return i+1 if the character was written, else i.
+     *
+     * @throws java.io.IOException
+     */
+    int accumDefaultEntity(
+        java.io.Writer writer,
+        char ch,
+        int i,
+        char[] chars,
+        int len,
+        boolean fromTextNode,
+        boolean escLF)
+        throws IOException
+    {
+
+        if (!escLF && CharInfo.S_LINEFEED == ch)
+        {
+            writer.write(m_lineSep, 0, m_lineSepLen);
+        }
+        else
+        {
+            // if this is text node character and a special one of those,
+            // or if this is a character from attribute value and a special one of those
+            if ((fromTextNode && m_charInfo.shouldMapTextChar(ch)) || (!fromTextNode && m_charInfo.shouldMapAttrChar(ch)))
+            {
+                String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
+
+                if (null != outputStringForChar)
+                {
+                    writer.write(outputStringForChar);
+                }
+                else
+                    return i;
+            }
+            else
+                return i;
+        }
+
+        return i + 1;
+
+    }
+    /**
+     * Normalize the characters, but don't escape.
+     *
+     * @param ch The characters from the XML document.
+     * @param start The start position in the array.
+     * @param length The number of characters to read from the array.
+     * @param isCData true if a CDATA block should be built around the characters.
+     * @param useSystemLineSeparator true if the operating systems 
+     * end-of-line separator should be output rather than a new-line character.
+     *
+     * @throws IOException
+     * @throws org.xml.sax.SAXException
+     */
+    void writeNormalizedChars(
+        char ch[],
+        int start,
+        int length,
+        boolean isCData,
+        boolean useSystemLineSeparator)
+        throws IOException, org.xml.sax.SAXException
+    {
+        final java.io.Writer writer = m_writer;
+        int end = start + length;
+
+        for (int i = start; i < end; i++)
+        {
+            char c = ch[i];
+
+            if (CharInfo.S_LINEFEED == c && useSystemLineSeparator)
+            {
+                writer.write(m_lineSep, 0, m_lineSepLen);
+            }
+            else if (isCData && (!escapingNotNeeded(c)))
+            {
+                //                if (i != 0)
+                if (m_cdataTagOpen)
+                    closeCDATA();
+
+                // This needs to go into a function... 
+                if (Encodings.isHighUTF16Surrogate(c))
+                {
+                    writeUTF16Surrogate(c, ch, i, end);
+                    i++ ; // process two input characters
+                }
+                else
+                {
+                    writer.write("&#");
+
+                    String intStr = Integer.toString((int) c);
+
+                    writer.write(intStr);
+                    writer.write(';');
+                }
+
+                //                if ((i != 0) && (i < (end - 1)))
+                //                if (!m_cdataTagOpen && (i < (end - 1)))
+                //                {
+                //                    writer.write(CDATA_DELIMITER_OPEN);
+                //                    m_cdataTagOpen = true;
+                //                }
+            }
+            else if (
+                isCData
+                    && ((i < (end - 2))
+                        && (']' == c)
+                        && (']' == ch[i + 1])
+                        && ('>' == ch[i + 2])))
+            {
+                writer.write(CDATA_CONTINUE);
+
+                i += 2;
+            }
+            else
+            {
+                if (escapingNotNeeded(c))
+                {
+                    if (isCData && !m_cdataTagOpen)
+                    {
+                        writer.write(CDATA_DELIMITER_OPEN);
+                        m_cdataTagOpen = true;
+                    }
+                    writer.write(c);
+                }
+
+                // This needs to go into a function... 
+                else if (Encodings.isHighUTF16Surrogate(c))
+                {
+                    if (m_cdataTagOpen)
+                        closeCDATA();
+                    writeUTF16Surrogate(c, ch, i, end);
+                    i++; // process two input characters
+                }
+                else
+                {
+                    if (m_cdataTagOpen)
+                        closeCDATA();
+                    writer.write("&#");
+
+                    String intStr = Integer.toString((int) c);
+
+                    writer.write(intStr);
+                    writer.write(';');
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Ends an un-escaping section.
+     *
+     * @see #startNonEscaping
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void endNonEscaping() throws org.xml.sax.SAXException
+    {
+        m_disableOutputEscapingStates.pop();
+    }
+
+    /**
+     * Starts an un-escaping section. All characters printed within an un-
+     * escaping section are printed as is, without escaping special characters
+     * into entity references. Only XML and HTML serializers need to support
+     * this method.
+     * <p> The contents of the un-escaping section will be delivered through the
+     * regular <tt>characters</tt> event.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void startNonEscaping() throws org.xml.sax.SAXException
+    {
+        m_disableOutputEscapingStates.push(true);
+    }
+
+    /**
+     * Receive notification of cdata.
+     *
+     * <p>The Parser will call this method to report each chunk of
+     * character data.  SAX parsers may return all contiguous character
+     * data in a single chunk, or they may split it into several
+     * chunks; however, all of the characters in any single event
+     * must come from the same external entity, so that the Locator
+     * provides useful information.</p>
+     *
+     * <p>The application must not attempt to read from the array
+     * outside of the specified range.</p>
+     *
+     * <p>Note that some parsers will report whitespace using the
+     * ignorableWhitespace() method rather than this one (validating
+     * parsers must do so).</p>
+     *
+     * @param ch The characters from the XML document.
+     * @param start The start position in the array.
+     * @param length The number of characters to read from the array.
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     * @see #ignorableWhitespace
+     * @see org.xml.sax.Locator
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    protected void cdata(char ch[], int start, final int length)
+        throws org.xml.sax.SAXException
+    {
+
+        try
+        {
+            final int old_start = start;
+            if (m_elemContext.m_startTagOpen)
+            {
+                closeStartTag();
+                m_elemContext.m_startTagOpen = false;
+            }
+            m_ispreserve = true;
+
+            if (shouldIndent())
+                indent();
+
+            boolean writeCDataBrackets =
+                (((length >= 1) && escapingNotNeeded(ch[start])));
+
+            /* Write out the CDATA opening delimiter only if
+             * we are supposed to, and if we are not already in
+             * the middle of a CDATA section  
+             */
+            if (writeCDataBrackets && !m_cdataTagOpen)
+            {
+                m_writer.write(CDATA_DELIMITER_OPEN);
+                m_cdataTagOpen = true;
+            }
+
+            // writer.write(ch, start, length);
+            if (isEscapingDisabled())
+            {
+                charactersRaw(ch, start, length);
+            }
+            else
+                writeNormalizedChars(ch, start, length, true, m_lineSepUse);
+
+            /* used to always write out CDATA closing delimiter here,
+             * but now we delay, so that we can merge CDATA sections on output.    
+             * need to write closing delimiter later
+             */
+            if (writeCDataBrackets)
+            {
+                /* if the CDATA section ends with ] don't leave it open
+                 * as there is a chance that an adjacent CDATA sections
+                 * starts with ]>.  
+                 * We don't want to merge ]] with > , or ] with ]> 
+                 */
+                if (ch[start + length - 1] == ']')
+                    closeCDATA();
+            }
+
+            // time to fire off CDATA event
+            if (m_tracer != null)
+                super.fireCDATAEvent(ch, old_start, length);
+        }
+        catch (IOException ioe)
+        {
+            throw new org.xml.sax.SAXException(
+                Utils.messages.createMessage(
+                    MsgKey.ER_OIERROR,
+                    null),
+                ioe);
+            //"IO error", ioe);
+        }
+    }
+
+    /**
+     * Tell if the character escaping should be disabled for the current state.
+     *
+     * @return true if the character escaping should be disabled.
+     */
+    private boolean isEscapingDisabled()
+    {
+        return m_disableOutputEscapingStates.peekOrFalse();
+    }
+
+    /**
+     * If available, when the disable-output-escaping attribute is used,
+     * output raw text without escaping.
+     *
+     * @param ch The characters from the XML document.
+     * @param start The start position in the array.
+     * @param length The number of characters to read from the array.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    protected void charactersRaw(char ch[], int start, int length)
+        throws org.xml.sax.SAXException
+    {
+
+        if (m_inEntityRef)
+            return;
+        try
+        {
+            if (m_elemContext.m_startTagOpen)
+            {
+                closeStartTag();
+                m_elemContext.m_startTagOpen = false;
+            }
+
+            m_ispreserve = true;
+
+            m_writer.write(ch, start, length);
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+    }
+
+    /**
+     * Receive notification of character data.
+     *
+     * <p>The Parser will call this method to report each chunk of
+     * character data.  SAX parsers may return all contiguous character
+     * data in a single chunk, or they may split it into several
+     * chunks; however, all of the characters in any single event
+     * must come from the same external entity, so that the Locator
+     * provides useful information.</p>
+     *
+     * <p>The application must not attempt to read from the array
+     * outside of the specified range.</p>
+     *
+     * <p>Note that some parsers will report whitespace using the
+     * ignorableWhitespace() method rather than this one (validating
+     * parsers must do so).</p>
+     *
+     * @param chars The characters from the XML document.
+     * @param start The start position in the array.
+     * @param length The number of characters to read from the array.
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     * @see #ignorableWhitespace
+     * @see org.xml.sax.Locator
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void characters(final char chars[], final int start, final int length)
+        throws org.xml.sax.SAXException
+    {
+        // It does not make sense to continue with rest of the method if the number of 
+        // characters to read from array is 0.
+        // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node
+        // is created if string is empty.	
+        if (length == 0 || (m_inEntityRef && !m_expandDTDEntities))
+            return;
+            
+        m_docIsEmpty = false;
+        
+        if (m_elemContext.m_startTagOpen)
+        {
+            closeStartTag();
+            m_elemContext.m_startTagOpen = false;
+        }
+        else if (m_needToCallStartDocument)
+        {
+            startDocumentInternal();
+        }
+
+        if (m_cdataStartCalled || m_elemContext.m_isCdataSection)
+        {
+            /* either due to startCDATA() being called or due to 
+             * cdata-section-elements atribute, we need this as cdata
+             */
+            cdata(chars, start, length);
+
+            return;
+        }
+
+        if (m_cdataTagOpen)
+            closeCDATA();
+        
+        if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
+        {
+            charactersRaw(chars, start, length);
+
+            // time to fire off characters generation event
+            if (m_tracer != null)
+                super.fireCharEvent(chars, start, length);
+
+            return;
+        }
+
+        if (m_elemContext.m_startTagOpen)
+        {
+            closeStartTag();
+            m_elemContext.m_startTagOpen = false;
+        }
+
+        
+        try
+        {
+            int i;
+            int startClean;
+            
+            // skip any leading whitspace 
+            // don't go off the end and use a hand inlined version
+            // of isWhitespace(ch)
+            final int end = start + length;
+            int lastDirtyCharProcessed = start - 1; // last non-clean character that was processed
+													// that was processed
+            final Writer writer = m_writer;
+            boolean isAllWhitespace = true;
+
+            // process any leading whitspace
+            i = start;
+            while (i < end && isAllWhitespace) {
+                char ch1 = chars[i];
+
+                if (m_charInfo.shouldMapTextChar(ch1)) {
+                    // The character is supposed to be replaced by a String
+                    // so write out the clean whitespace characters accumulated
+                    // so far
+                    // then the String.
+                    writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+                    String outputStringForChar = m_charInfo
+                            .getOutputStringForChar(ch1);
+                    writer.write(outputStringForChar);
+                    // We can't say that everything we are writing out is
+                    // all whitespace, we just wrote out a String.
+                    isAllWhitespace = false;
+                    lastDirtyCharProcessed = i; // mark the last non-clean
+                    // character processed
+                    i++;
+                } else {
+                    // The character is clean, but is it a whitespace ?
+                    switch (ch1) {
+                    // TODO: Any other whitespace to consider?
+                    case CharInfo.S_SPACE:
+                        // Just accumulate the clean whitespace
+                        i++;
+                        break;
+                    case CharInfo.S_LINEFEED:
+                        lastDirtyCharProcessed = processLineFeed(chars, i,
+                                lastDirtyCharProcessed, writer);
+                        i++;
+                        break;
+                    case CharInfo.S_CARRIAGERETURN:
+                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+                        writer.write("&#13;");
+                        lastDirtyCharProcessed = i;
+                        i++;
+                        break;
+                    case CharInfo.S_HORIZONAL_TAB:
+                        // Just accumulate the clean whitespace
+                        i++;
+                        break;
+                    default:
+                        // The character was clean, but not a whitespace
+                        // so break the loop to continue with this character
+                        // (we don't increment index i !!)
+                        isAllWhitespace = false;
+                        break;
+                    }
+                }
+            }
+
+            /* If there is some non-whitespace, mark that we may need
+             * to preserve this. This is only important if we have indentation on.
+             */            
+            if (i < end || !isAllWhitespace) 
+                m_ispreserve = true;
+            
+            
+            for (; i < end; i++)
+            {
+                char ch = chars[i];
+                
+                if (m_charInfo.shouldMapTextChar(ch)) {
+                    // The character is supposed to be replaced by a String
+                    // e.g.   '&'  -->  "&amp;"
+                    // e.g.   '<'  -->  "&lt;"
+                    writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+                    String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
+                    writer.write(outputStringForChar);
+                    lastDirtyCharProcessed = i;
+                }
+                else {
+                    if (ch <= 0x1F) {
+                        // Range 0x00 through 0x1F inclusive
+                        //
+                        // This covers the non-whitespace control characters
+                        // in the range 0x1 to 0x1F inclusive.
+                        // It also covers the whitespace control characters in the same way:
+                        // 0x9   TAB
+                        // 0xA   NEW LINE
+                        // 0xD   CARRIAGE RETURN
+                        //
+                        // We also cover 0x0 ... It isn't valid
+                        // but we will output "&#0;" 
+                        
+                        // The default will handle this just fine, but this
+                        // is a little performance boost to handle the more
+                        // common TAB, NEW-LINE, CARRIAGE-RETURN
+                        switch (ch) {
+
+                        case CharInfo.S_HORIZONAL_TAB:
+                            // Leave whitespace TAB as a real character
+                            break;
+                        case CharInfo.S_LINEFEED:
+                            lastDirtyCharProcessed = processLineFeed(chars, i, lastDirtyCharProcessed, writer);
+                            break;
+                        case CharInfo.S_CARRIAGERETURN:
+                        	writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+                        	writer.write("&#13;");
+                        	lastDirtyCharProcessed = i;
+                            // Leave whitespace carriage return as a real character
+                            break;
+                        default:
+                            writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+                            writer.write("&#");
+                            writer.write(Integer.toString(ch));
+                            writer.write(';');
+                            lastDirtyCharProcessed = i;
+                            break;
+
+                        }
+                    }
+                    else if (ch < 0x7F) {  
+                        // Range 0x20 through 0x7E inclusive
+                        // Normal ASCII chars, do nothing, just add it to
+                        // the clean characters
+                            
+                    }
+                    else if (ch <= 0x9F){
+                        // Range 0x7F through 0x9F inclusive
+                        // More control characters, including NEL (0x85)
+                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+                        writer.write("&#");
+                        writer.write(Integer.toString(ch));
+                        writer.write(';');
+                        lastDirtyCharProcessed = i;
+                    }
+                    else if (ch == CharInfo.S_LINE_SEPARATOR) {
+                        // LINE SEPARATOR
+                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+                        writer.write("&#8232;");
+                        lastDirtyCharProcessed = i;
+                    }
+                    else if (m_encodingInfo.isInEncoding(ch)) {
+                        // If the character is in the encoding, and
+                        // not in the normal ASCII range, we also
+                        // just leave it get added on to the clean characters
+                        
+                    }
+                    else {
+                        // This is a fallback plan, we should never get here
+                        // but if the character wasn't previously handled
+                        // (i.e. isn't in the encoding, etc.) then what
+                        // should we do?  We choose to write out an entity
+                        writeOutCleanChars(chars, i, lastDirtyCharProcessed);
+                        writer.write("&#");
+                        writer.write(Integer.toString(ch));
+                        writer.write(';');
+                        lastDirtyCharProcessed = i;
+                    }
+                }
+            }
+            
+            // we've reached the end. Any clean characters at the
+            // end of the array than need to be written out?
+            startClean = lastDirtyCharProcessed + 1;
+            if (i > startClean)
+            {
+                int lengthClean = i - startClean;
+                m_writer.write(chars, startClean, lengthClean);
+            }
+
+            // For indentation purposes, mark that we've just writen text out
+            m_isprevtext = true;
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+        // time to fire off characters generation event
+        if (m_tracer != null)
+            super.fireCharEvent(chars, start, length);
+    }
+
+	private int processLineFeed(final char[] chars, int i, int lastProcessed, final Writer writer) throws IOException {
+		if (!m_lineSepUse 
+		|| (m_lineSepLen ==1 && m_lineSep[0] == CharInfo.S_LINEFEED)){
+		    // We are leaving the new-line alone, and it is just
+		    // being added to the 'clean' characters,
+			// so the last dirty character processed remains unchanged
+		}
+		else {
+		    writeOutCleanChars(chars, i, lastProcessed);
+		    writer.write(m_lineSep, 0, m_lineSepLen);
+		    lastProcessed = i;
+		}
+		return lastProcessed;
+	}
+
+    private void writeOutCleanChars(final char[] chars, int i, int lastProcessed) throws IOException {
+        int startClean;
+        startClean = lastProcessed + 1;
+        if (startClean < i)
+        {
+            int lengthClean = i - startClean;
+            m_writer.write(chars, startClean, lengthClean);
+        }
+    }     
+    /**
+     * This method checks if a given character is between C0 or C1 range
+     * of Control characters.
+     * This method is added to support Control Characters for XML 1.1
+     * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method
+     * return false. Since they are whitespace characters, no special processing is needed.
+     * 
+     * @param ch
+     * @return boolean
+     */
+    private static boolean isCharacterInC0orC1Range(char ch)
+    {
+        if(ch == 0x09 || ch == 0x0A || ch == 0x0D)
+        	return false;
+        else        	    	
+        	return (ch >= 0x7F && ch <= 0x9F)|| (ch >= 0x01 && ch <= 0x1F);
+    }
+    /**
+     * This method checks if a given character either NEL (0x85) or LSEP (0x2028)
+     * These are new end of line charcters added in XML 1.1.  These characters must be
+     * written as Numeric Character References (NCR) in XML 1.1 output document.
+     * 
+     * @param ch
+     * @return boolean
+     */
+    private static boolean isNELorLSEPCharacter(char ch)
+    {
+        return (ch == 0x85 || ch == 0x2028);
+    }
+    /**
+     * Process a dirty character and any preeceding clean characters
+     * that were not yet processed.
+     * @param chars array of characters being processed
+     * @param end one (1) beyond the last character 
+     * in chars to be processed
+     * @param i the index of the dirty character
+     * @param ch the character in chars[i]
+     * @param lastDirty the last dirty character previous to i
+     * @param fromTextNode true if the characters being processed are
+     * from a text node, false if they are from an attribute value.
+     * @return the index of the last character processed
+     */
+    private int processDirty(
+        char[] chars, 
+        int end,
+        int i, 
+        char ch,
+        int lastDirty,
+        boolean fromTextNode) throws IOException
+    {
+        int startClean = lastDirty + 1;
+        // if we have some clean characters accumulated
+        // process them before the dirty one.                   
+        if (i > startClean)
+        {
+            int lengthClean = i - startClean;
+            m_writer.write(chars, startClean, lengthClean);
+        }
+
+        // process the "dirty" character
+        if (CharInfo.S_LINEFEED == ch && fromTextNode)
+        {
+            m_writer.write(m_lineSep, 0, m_lineSepLen);
+        }
+        else
+        {
+            startClean =
+                accumDefaultEscape(
+                    m_writer,
+                    (char)ch,
+                    i,
+                    chars,
+                    end,
+                    fromTextNode,
+                    false);
+            i = startClean - 1;
+        }
+        // Return the index of the last character that we just processed 
+        // which is a dirty character.
+        return i;
+    }
+
+    /**
+     * Receive notification of character data.
+     *
+     * @param s The string of characters to process.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void characters(String s) throws org.xml.sax.SAXException
+    {
+        if (m_inEntityRef && !m_expandDTDEntities)
+            return;
+        final int length = s.length();
+        if (length > m_charsBuff.length)
+        {
+            m_charsBuff = new char[length * 2 + 1];
+        }
+        s.getChars(0, length, m_charsBuff, 0);
+        characters(m_charsBuff, 0, length);
+    }
+
+    /**
+     * Escape and writer.write a character.
+     *
+     * @param ch character to be escaped.
+     * @param i index into character array.
+     * @param chars non-null reference to character array.
+     * @param len length of chars.
+     * @param fromTextNode true if the characters being processed are
+     * from a text node, false if the characters being processed are from
+     * an attribute value.
+     * @param escLF true if the linefeed should be escaped.
+     *
+     * @return i+1 if a character was written, i+2 if two characters
+     * were written out, else return i.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    private int accumDefaultEscape(
+        Writer writer,
+        char ch,
+        int i,
+        char[] chars,
+        int len,
+        boolean fromTextNode,
+        boolean escLF)
+        throws IOException
+    {
+
+        int pos = accumDefaultEntity(writer, ch, i, chars, len, fromTextNode, escLF);
+
+        if (i == pos)
+        {
+            if (Encodings.isHighUTF16Surrogate(ch))
+            {
+
+                // Should be the UTF-16 low surrogate of the hig/low pair.
+                char next;
+                // Unicode code point formed from the high/low pair.
+                int codePoint = 0;
+
+                if (i + 1 >= len)
+                {
+                    throw new IOException(
+                        Utils.messages.createMessage(
+                            MsgKey.ER_INVALID_UTF16_SURROGATE,
+                            new Object[] { Integer.toHexString(ch)}));
+                    //"Invalid UTF-16 surrogate detected: "
+
+                    //+Integer.toHexString(ch)+ " ?");
+                }
+                else
+                {
+                    next = chars[++i];
+
+                    if (!(Encodings.isLowUTF16Surrogate(next)))
+                        throw new IOException(
+                            Utils.messages.createMessage(
+                                MsgKey
+                                    .ER_INVALID_UTF16_SURROGATE,
+                                new Object[] {
+                                    Integer.toHexString(ch)
+                                        + " "
+                                        + Integer.toHexString(next)}));
+                    //"Invalid UTF-16 surrogate detected: "
+
+                    //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
+                    codePoint = Encodings.toCodePoint(ch,next);
+                }
+
+                writer.write("&#");
+                writer.write(Integer.toString(codePoint));
+                writer.write(';');
+                pos += 2; // count the two characters that went into writing out this entity
+            }
+            else
+            {
+                /*  This if check is added to support control characters in XML 1.1.
+                 *  If a character is a Control Character within C0 and C1 range, it is desirable
+                 *  to write it out as Numeric Character Reference(NCR) regardless of XML Version
+                 *  being used for output document.
+                 */ 
+                if (isCharacterInC0orC1Range(ch) || isNELorLSEPCharacter(ch))
+                {
+                    writer.write("&#");
+                    writer.write(Integer.toString(ch));
+                    writer.write(';');
+                }
+                else if ((!escapingNotNeeded(ch) || 
+                    (  (fromTextNode && m_charInfo.shouldMapTextChar(ch))
+                     || (!fromTextNode && m_charInfo.shouldMapAttrChar(ch)))) 
+                && m_elemContext.m_currentElemDepth > 0)
+                {
+                    writer.write("&#");
+                    writer.write(Integer.toString(ch));
+                    writer.write(';');
+                }
+                else
+                {
+                    writer.write(ch);
+                }
+                pos++;  // count the single character that was processed
+            }
+
+        }
+        return pos;
+    }
+
+    /**
+     * Receive notification of the beginning of an element, although this is a
+     * SAX method additional namespace or attribute information can occur before
+     * or after this call, that is associated with this element.
+     *
+     *
+     * @param namespaceURI The Namespace URI, or the empty string if the
+     *        element has no Namespace URI or if Namespace
+     *        processing is not being performed.
+     * @param localName The local name (without prefix), or the
+     *        empty string if Namespace processing is not being
+     *        performed.
+     * @param name The element type name.
+     * @param atts The attributes attached to the element, if any.
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     * @see org.xml.sax.ContentHandler#startElement
+     * @see org.xml.sax.ContentHandler#endElement
+     * @see org.xml.sax.AttributeList
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void startElement(
+        String namespaceURI,
+        String localName,
+        String name,
+        Attributes atts)
+        throws org.xml.sax.SAXException
+    {
+        if (m_inEntityRef)
+            return;
+
+        if (m_needToCallStartDocument)
+        {
+            startDocumentInternal();
+            m_needToCallStartDocument = false;
+            m_docIsEmpty = false;
+        }
+        else if (m_cdataTagOpen)
+            closeCDATA();
+        try
+        {
+            if (m_needToOutputDocTypeDecl) {
+                if(null != getDoctypeSystem()) {
+                    outputDocTypeDecl(name, true);
+                }
+                m_needToOutputDocTypeDecl = false;
+            }
+        
+            /* before we over-write the current elementLocalName etc.
+             * lets close out the old one (if we still need to)
+             */
+            if (m_elemContext.m_startTagOpen)
+            {
+                closeStartTag();
+                m_elemContext.m_startTagOpen = false;
+            }
+
+            if (namespaceURI != null)
+                ensurePrefixIsDeclared(namespaceURI, name);
+                
+            m_ispreserve = false;
+
+            if (shouldIndent() && m_startNewLine)
+            {
+                indent();
+            }
+
+            m_startNewLine = true;
+
+            final java.io.Writer writer = m_writer;
+            writer.write('<');
+            writer.write(name);
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+        // process the attributes now, because after this SAX call they might be gone
+        if (atts != null)
+            addAttributes(atts);
+            
+        m_elemContext = m_elemContext.push(namespaceURI,localName,name);
+        m_isprevtext = false;
+
+        if (m_tracer != null)
+            firePseudoAttributes();
+    }
+
+    /**
+      * Receive notification of the beginning of an element, additional
+      * namespace or attribute information can occur before or after this call,
+      * that is associated with this element.
+      *
+      *
+      * @param elementNamespaceURI The Namespace URI, or the empty string if the
+      *        element has no Namespace URI or if Namespace
+      *        processing is not being performed.
+      * @param elementLocalName The local name (without prefix), or the
+      *        empty string if Namespace processing is not being
+      *        performed.
+      * @param elementName The element type name.
+      * @throws org.xml.sax.SAXException Any SAX exception, possibly
+      *            wrapping another exception.
+      * @see org.xml.sax.ContentHandler#startElement
+      * @see org.xml.sax.ContentHandler#endElement
+      * @see org.xml.sax.AttributeList
+      *
+      * @throws org.xml.sax.SAXException
+      */
+    public void startElement(
+        String elementNamespaceURI,
+        String elementLocalName,
+        String elementName)
+        throws SAXException
+    {
+        startElement(elementNamespaceURI, elementLocalName, elementName, null);
+    }
+
+    public void startElement(String elementName) throws SAXException
+    {
+        startElement(null, null, elementName, null);
+    }
+
+    /**
+     * Output the doc type declaration.
+     *
+     * @param name non-null reference to document type name.
+     * NEEDSDOC @param closeDecl
+     *
+     * @throws java.io.IOException
+     */
+    void outputDocTypeDecl(String name, boolean closeDecl) throws SAXException
+    {
+        if (m_cdataTagOpen)
+            closeCDATA();
+        try
+        {
+            final java.io.Writer writer = m_writer;
+            writer.write("<!DOCTYPE ");
+            writer.write(name);
+
+            String doctypePublic = getDoctypePublic();
+            if (null != doctypePublic)
+            {
+                writer.write(" PUBLIC \"");
+                writer.write(doctypePublic);
+                writer.write('\"');
+            }
+
+            String doctypeSystem = getDoctypeSystem();
+            if (null != doctypeSystem)
+            {
+                if (null == doctypePublic)
+                    writer.write(" SYSTEM \"");
+                else
+                    writer.write(" \"");
+
+                writer.write(doctypeSystem);
+
+                if (closeDecl)
+                {
+                    writer.write("\">");
+                    writer.write(m_lineSep, 0, m_lineSepLen);
+                    closeDecl = false; // done closing
+                }
+                else
+                    writer.write('\"');
+            }
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+    }
+
+    /**
+     * Process the attributes, which means to write out the currently
+     * collected attributes to the writer. The attributes are not
+     * cleared by this method
+     * 
+     * @param writer the writer to write processed attributes to.
+     * @param nAttrs the number of attributes in m_attributes 
+     * to be processed
+     *
+     * @throws java.io.IOException
+     * @throws org.xml.sax.SAXException
+     */
+    public void processAttributes(java.io.Writer writer, int nAttrs) throws IOException, SAXException
+    {
+            /* real SAX attributes are not passed in, so process the 
+             * attributes that were collected after the startElement call.
+             * _attribVector is a "cheap" list for Stream serializer output
+             * accumulated over a series of calls to attribute(name,value)
+             */
+
+            String encoding = getEncoding();
+            for (int i = 0; i < nAttrs; i++)
+            {
+                // elementAt is JDK 1.1.8
+                final String name = m_attributes.getQName(i);
+                final String value = m_attributes.getValue(i);
+                writer.write(' ');
+                writer.write(name);
+                writer.write("=\"");
+                writeAttrString(writer, value, encoding);
+                writer.write('\"');
+            }
+    }
+
+    /**
+     * Returns the specified <var>string</var> after substituting <VAR>specials</VAR>,
+     * and UTF-16 surrogates for chracter references <CODE>&amp;#xnn</CODE>.
+     *
+     * @param   string      String to convert to XML format.
+     * @param   encoding    CURRENTLY NOT IMPLEMENTED.
+     *
+     * @throws java.io.IOException
+     */
+    public void writeAttrString(
+        Writer writer,
+        String string,
+        String encoding)
+        throws IOException
+    {
+        final int len = string.length();
+        if (len > m_attrBuff.length)
+        {
+           m_attrBuff = new char[len*2 + 1];             
+        }
+        string.getChars(0,len, m_attrBuff, 0);   
+        final char[] stringChars = m_attrBuff;
+
+        for (int i = 0; i < len; i++)
+        {
+            char ch = stringChars[i];
+            
+            if (m_charInfo.shouldMapAttrChar(ch)) {
+                // The character is supposed to be replaced by a String
+                // e.g.   '&'  -->  "&amp;"
+                // e.g.   '<'  -->  "&lt;"
+                accumDefaultEscape(writer, ch, i, stringChars, len, false, true);
+            }
+            else {
+                if (0x0 <= ch && ch <= 0x1F) {
+                    // Range 0x00 through 0x1F inclusive
+                    // This covers the non-whitespace control characters
+                    // in the range 0x1 to 0x1F inclusive.
+                    // It also covers the whitespace control characters in the same way:
+                    // 0x9   TAB
+                    // 0xA   NEW LINE
+                    // 0xD   CARRIAGE RETURN
+                    //
+                    // We also cover 0x0 ... It isn't valid
+                    // but we will output "&#0;" 
+                    
+                    // The default will handle this just fine, but this
+                    // is a little performance boost to handle the more
+                    // common TAB, NEW-LINE, CARRIAGE-RETURN
+                    switch (ch) {
+
+                    case CharInfo.S_HORIZONAL_TAB:
+                        writer.write("&#9;");
+                        break;
+                    case CharInfo.S_LINEFEED:
+                        writer.write("&#10;");
+                        break;
+                    case CharInfo.S_CARRIAGERETURN:
+                        writer.write("&#13;");
+                        break;
+                    default:
+                        writer.write("&#");
+                        writer.write(Integer.toString(ch));
+                        writer.write(';');
+                        break;
+
+                    }
+                }
+                else if (ch < 0x7F) {   
+                    // Range 0x20 through 0x7E inclusive
+                    // Normal ASCII chars
+                        writer.write(ch);
+                }
+                else if (ch <= 0x9F){
+                    // Range 0x7F through 0x9F inclusive
+                    // More control characters
+                    writer.write("&#");
+                    writer.write(Integer.toString(ch));
+                    writer.write(';');
+                }
+                else if (ch == CharInfo.S_LINE_SEPARATOR) {
+                    // LINE SEPARATOR
+                    writer.write("&#8232;");
+                }
+                else if (m_encodingInfo.isInEncoding(ch)) {
+                    // If the character is in the encoding, and
+                    // not in the normal ASCII range, we also
+                    // just write it out
+                    writer.write(ch);
+                }
+                else {
+                    // This is a fallback plan, we should never get here
+                    // but if the character wasn't previously handled
+                    // (i.e. isn't in the encoding, etc.) then what
+                    // should we do?  We choose to write out a character ref
+                    writer.write("&#");
+                    writer.write(Integer.toString(ch));
+                    writer.write(';');
+                }
+                    
+            }
+        }
+    }
+
+    /**
+     * Receive notification of the end of an element.
+     *
+     *
+     * @param namespaceURI The Namespace URI, or the empty string if the
+     *        element has no Namespace URI or if Namespace
+     *        processing is not being performed.
+     * @param localName The local name (without prefix), or the
+     *        empty string if Namespace processing is not being
+     *        performed.
+     * @param name The element type name
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void endElement(String namespaceURI, String localName, String name)
+        throws org.xml.sax.SAXException
+    {
+        if (m_inEntityRef)
+            return;
+
+        // namespaces declared at the current depth are no longer valid
+        // so get rid of them    
+        m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
+
+        try
+        {
+            final java.io.Writer writer = m_writer;
+            if (m_elemContext.m_startTagOpen)
+            {
+                if (m_tracer != null)
+                    super.fireStartElem(m_elemContext.m_elementName);
+                int nAttrs = m_attributes.getLength();
+                if (nAttrs > 0)
+                {
+                    processAttributes(m_writer, nAttrs);
+                    // clear attributes object for re-use with next element
+                    m_attributes.clear();
+                }
+                if (m_spaceBeforeClose)
+                    writer.write(" />");
+                else
+                    writer.write("/>");
+                /* don't need to pop cdataSectionState because
+                 * this element ended so quickly that we didn't get
+                 * to push the state.
+                 */
+
+            }
+            else
+            {
+                if (m_cdataTagOpen)
+                    closeCDATA();
+
+                if (shouldIndent())
+                    indent(m_elemContext.m_currentElemDepth - 1);
+                writer.write('<');
+                writer.write('/');
+                writer.write(name);
+                writer.write('>');
+            }
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+        if (!m_elemContext.m_startTagOpen && m_doIndent)
+        {
+            m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
+        }
+
+        m_isprevtext = false;
+
+        // fire off the end element event
+        if (m_tracer != null)
+            super.fireEndElem(name);
+        m_elemContext = m_elemContext.m_prev;
+    }
+
+    /**
+     * Receive notification of the end of an element.
+     * @param name The element type name
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *     wrapping another exception.
+     */
+    public void endElement(String name) throws org.xml.sax.SAXException
+    {
+        endElement(null, null, name);
+    }
+
+    /**
+     * Begin the scope of a prefix-URI Namespace mapping
+     * just before another element is about to start.
+     * This call will close any open tags so that the prefix mapping
+     * will not apply to the current element, but the up comming child.
+     * 
+     * @see org.xml.sax.ContentHandler#startPrefixMapping
+     * 
+     * @param prefix The Namespace prefix being declared.
+     * @param uri The Namespace URI the prefix is mapped to.
+     * 
+     * @throws org.xml.sax.SAXException The client may throw
+     *            an exception during processing.
+     * 
+     */
+    public void startPrefixMapping(String prefix, String uri)
+        throws org.xml.sax.SAXException
+    {
+        // the "true" causes the flush of any open tags
+        startPrefixMapping(prefix, uri, true);
+    }
+
+    /**
+     * Handle a prefix/uri mapping, which is associated with a startElement()
+     * that is soon to follow. Need to close any open start tag to make
+     * sure than any name space attributes due to this event are associated wih
+     * the up comming element, not the current one.
+     * @see ExtendedContentHandler#startPrefixMapping
+     *
+     * @param prefix The Namespace prefix being declared.
+     * @param uri The Namespace URI the prefix is mapped to.
+     * @param shouldFlush true if any open tags need to be closed first, this
+     * will impact which element the mapping applies to (open parent, or its up
+     * comming child)
+     * @return returns true if the call made a change to the current 
+     * namespace information, false if it did not change anything, e.g. if the
+     * prefix/namespace mapping was already in scope from before.
+     * 
+     * @throws org.xml.sax.SAXException The client may throw
+     *            an exception during processing.
+     *
+     *
+     */
+    public boolean startPrefixMapping(
+        String prefix,
+        String uri,
+        boolean shouldFlush)
+        throws org.xml.sax.SAXException
+    {
+
+        /* Remember the mapping, and at what depth it was declared
+         * This is one greater than the current depth because these
+         * mappings will apply to the next depth. This is in
+         * consideration that startElement() will soon be called
+         */
+
+        boolean pushed;
+        int pushDepth;
+        if (shouldFlush)
+        {
+            flushPending();
+            // the prefix mapping applies to the child element (one deeper)
+            pushDepth = m_elemContext.m_currentElemDepth + 1;
+        }
+        else
+        {
+            // the prefix mapping applies to the current element
+            pushDepth = m_elemContext.m_currentElemDepth;
+        }
+        pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
+
+        if (pushed)
+        {
+            /* Brian M.: don't know if we really needto do this. The
+             * callers of this object should have injected both
+             * startPrefixMapping and the attributes.  We are 
+             * just covering our butt here.
+             */
+            String name;
+            if (EMPTYSTRING.equals(prefix))
+            {
+                name = "xmlns";
+                addAttributeAlways(XMLNS_URI, name, name, "CDATA", uri, false);
+            }
+            else
+            {
+                if (!EMPTYSTRING.equals(uri))
+                    // hack for XSLTC attribset16 test
+                { // that maps ns1 prefix to "" URI
+                    name = "xmlns:" + prefix;
+
+                    /* for something like xmlns:abc="w3.pretend.org"
+                     *  the      uri is the value, that is why we pass it in the
+                     * value, or 5th slot of addAttributeAlways()
+                     */
+                    addAttributeAlways(XMLNS_URI, prefix, name, "CDATA", uri, false);
+                }
+            }
+        }
+        return pushed;
+    }
+
+    /**
+     * Receive notification of an XML comment anywhere in the document. This
+     * callback will be used for comments inside or outside the document
+     * element, including comments in the external DTD subset (if read).
+     * @param ch An array holding the characters in the comment.
+     * @param start The starting position in the array.
+     * @param length The number of characters to use from the array.
+     * @throws org.xml.sax.SAXException The application may raise an exception.
+     */
+    public void comment(char ch[], int start, int length)
+        throws org.xml.sax.SAXException
+    {
+
+        int start_old = start;
+        if (m_inEntityRef)
+            return;
+        if (m_elemContext.m_startTagOpen)
+        {
+            closeStartTag();
+            m_elemContext.m_startTagOpen = false;
+        }
+        else if (m_needToCallStartDocument)
+        {
+            startDocumentInternal();
+            m_needToCallStartDocument = false;
+        }
+
+        try
+        {
+            final int limit = start + length;
+            boolean wasDash = false;
+            if (m_cdataTagOpen)
+                closeCDATA();
+            
+            if (shouldIndent())
+                indent();
+            
+            final java.io.Writer writer = m_writer;    
+            writer.write(COMMENT_BEGIN);
+            // Detect occurrences of two consecutive dashes, handle as necessary.
+            for (int i = start; i < limit; i++)
+            {
+                if (wasDash && ch[i] == '-')
+                {
+                    writer.write(ch, start, i - start);
+                    writer.write(" -");
+                    start = i + 1;
+                }
+                wasDash = (ch[i] == '-');
+            }
+
+            // if we have some chars in the comment
+            if (length > 0)
+            {
+                // Output the remaining characters (if any)
+                final int remainingChars = (limit - start);
+                if (remainingChars > 0)
+                    writer.write(ch, start, remainingChars);
+                // Protect comment end from a single trailing dash
+                if (ch[limit - 1] == '-')
+                    writer.write(' ');
+            }
+            writer.write(COMMENT_END);
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+        /*
+         * Don't write out any indentation whitespace now,
+         * because there may be non-whitespace text after this.
+         * 
+         * Simply mark that at this point if we do decide
+         * to indent that we should 
+         * add a newline on the end of the current line before
+         * the indentation at the start of the next line.
+         */ 
+        m_startNewLine = true;
+        // time to generate comment event
+        if (m_tracer != null)
+            super.fireCommentEvent(ch, start_old,length);
+    }
+
+    /**
+     * Report the end of a CDATA section.
+     * @throws org.xml.sax.SAXException The application may raise an exception.
+     *
+     *  @see  #startCDATA
+     */
+    public void endCDATA() throws org.xml.sax.SAXException
+    {
+        if (m_cdataTagOpen)
+            closeCDATA();
+        m_cdataStartCalled = false;
+    }
+
+    /**
+     * Report the end of DTD declarations.
+     * @throws org.xml.sax.SAXException The application may raise an exception.
+     * @see #startDTD
+     */
+    public void endDTD() throws org.xml.sax.SAXException
+    {
+        try
+        {
+            if (m_needToOutputDocTypeDecl)
+            {
+                outputDocTypeDecl(m_elemContext.m_elementName, false);
+                m_needToOutputDocTypeDecl = false;
+            }
+            final java.io.Writer writer = m_writer;
+            if (!m_inDoctype)
+                writer.write("]>");
+            else
+            {
+                writer.write('>');
+            }
+
+            writer.write(m_lineSep, 0, m_lineSepLen);
+        }
+        catch (IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+    }
+
+    /**
+     * End the scope of a prefix-URI Namespace mapping.
+     * @see org.xml.sax.ContentHandler#endPrefixMapping
+     * 
+     * @param prefix The prefix that was being mapping.
+     * @throws org.xml.sax.SAXException The client may throw
+     *            an exception during processing.
+     */
+    public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException
+    { // do nothing
+    }
+
+    /**
+     * Receive notification of ignorable whitespace in element content.
+     * 
+     * Not sure how to get this invoked quite yet.
+     * 
+     * @param ch The characters from the XML document.
+     * @param start The start position in the array.
+     * @param length The number of characters to read from the array.
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     * @see #characters
+     * 
+     * @throws org.xml.sax.SAXException
+     */
+    public void ignorableWhitespace(char ch[], int start, int length)
+        throws org.xml.sax.SAXException
+    {
+
+        if (0 == length)
+            return;
+        characters(ch, start, length);
+    }
+
+    /**
+     * Receive notification of a skipped entity.
+     * @see org.xml.sax.ContentHandler#skippedEntity
+     * 
+     * @param name The name of the skipped entity.  If it is a
+     *       parameter                   entity, the name will begin with '%',
+     * and if it is the external DTD subset, it will be the string
+     * "[dtd]".
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
+     * another exception.
+     */
+    public void skippedEntity(String name) throws org.xml.sax.SAXException
+    { // TODO: Should handle
+    }
+
+    /**
+     * Report the start of a CDATA section.
+     * 
+     * @throws org.xml.sax.SAXException The application may raise an exception.
+     * @see #endCDATA
+     */
+    public void startCDATA() throws org.xml.sax.SAXException
+    {
+        m_cdataStartCalled = true;
+    }
+
+    /**
+     * Report the beginning of an entity.
+     * 
+     * The start and end of the document entity are not reported.
+     * The start and end of the external DTD subset are reported
+     * using the pseudo-name "[dtd]".  All other events must be
+     * properly nested within start/end entity events.
+     * 
+     * @param name The name of the entity.  If it is a parameter
+     *        entity, the name will begin with '%'.
+     * @throws org.xml.sax.SAXException The application may raise an exception.
+     * @see #endEntity
+     * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
+     * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
+     */
+    public void startEntity(String name) throws org.xml.sax.SAXException
+    {
+        if (name.equals("[dtd]"))
+            m_inExternalDTD = true;
+
+        if (!m_expandDTDEntities && !m_inExternalDTD) {
+            /* Only leave the entity as-is if
+             * we've been told not to expand them
+             * and this is not the magic [dtd] name.
+             */
+            startNonEscaping();
+            characters("&" + name + ';');
+            endNonEscaping();
+        }
+
+        m_inEntityRef = true;
+    }
+
+    /**
+     * For the enclosing elements starting tag write out
+     * out any attributes followed by ">"
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    protected void closeStartTag() throws SAXException
+    {
+
+        if (m_elemContext.m_startTagOpen)
+        {
+
+            try
+            {
+                if (m_tracer != null)
+                    super.fireStartElem(m_elemContext.m_elementName);
+                int nAttrs = m_attributes.getLength();
+                if (nAttrs > 0)
+                {
+                    processAttributes(m_writer, nAttrs);
+                    // clear attributes object for re-use with next element
+                    m_attributes.clear();
+                }
+                m_writer.write('>');
+            }
+            catch (IOException e)
+            {
+                throw new SAXException(e);
+            }
+
+            /* whether Xalan or XSLTC, we have the prefix mappings now, so
+             * lets determine if the current element is specified in the cdata-
+             * section-elements list.
+             */
+            if (m_CdataElems != null)
+                m_elemContext.m_isCdataSection = isCdataSection();
+
+            if (m_doIndent)
+            {
+                m_isprevtext = false;
+                m_preserves.push(m_ispreserve);
+            }
+        }
+
+    }
+
+    /**
+     * Report the start of DTD declarations, if any.
+     *
+     * Any declarations are assumed to be in the internal subset unless
+     * otherwise indicated.
+     * 
+     * @param name The document type name.
+     * @param publicId The declared public identifier for the
+     *        external DTD subset, or null if none was declared.
+     * @param systemId The declared system identifier for the
+     *        external DTD subset, or null if none was declared.
+     * @throws org.xml.sax.SAXException The application may raise an
+     *            exception.
+     * @see #endDTD
+     * @see #startEntity
+     */
+    public void startDTD(String name, String publicId, String systemId)
+        throws org.xml.sax.SAXException
+    {
+        setDoctypeSystem(systemId);
+        setDoctypePublic(publicId);
+
+        m_elemContext.m_elementName = name;
+        m_inDoctype = true;
+    }
+
+    /**
+     * Returns the m_indentAmount.
+     * @return int
+     */
+    public int getIndentAmount()
+    {
+        return m_indentAmount;
+    }
+
+    /**
+     * Sets the m_indentAmount.
+     * 
+     * @param m_indentAmount The m_indentAmount to set
+     */
+    public void setIndentAmount(int m_indentAmount)
+    {
+        this.m_indentAmount = m_indentAmount;
+    }
+
+    /**
+     * Tell if, based on space preservation constraints and the doIndent property,
+     * if an indent should occur.
+     *
+     * @return True if an indent should occur.
+     */
+    protected boolean shouldIndent()
+    {
+        return m_doIndent && (!m_ispreserve && !m_isprevtext) && m_elemContext.m_currentElemDepth > 0;
+    }
+
+    /**
+     * Searches for the list of qname properties with the specified key in the
+     * property list. If the key is not found in this property list, the default
+     * property list, and its defaults, recursively, are then checked. The
+     * method returns <code>null</code> if the property is not found.
+     *
+     * @param   key   the property key.
+     * @param props the list of properties to search in.
+     * 
+     * Sets the vector of local-name/URI pairs of the cdata section elements
+     * specified in the cdata-section-elements property.
+     * 
+     * This method is essentially a copy of getQNameProperties() from
+     * OutputProperties. Eventually this method should go away and a call
+     * to setCdataSectionElements(Vector v) should be made directly.
+     */
+    private void setCdataSectionElements(String key, Properties props)
+    {
+
+        String s = props.getProperty(key);
+
+        if (null != s)
+        {
+            // Vector of URI/LocalName pairs
+            Vector v = new Vector();
+            int l = s.length();
+            boolean inCurly = false;
+            StringBuffer buf = new StringBuffer();
+
+            // parse through string, breaking on whitespaces.  I do this instead
+            // of a tokenizer so I can track whitespace inside of curly brackets,
+            // which theoretically shouldn't happen if they contain legal URLs.
+            for (int i = 0; i < l; i++)
+            {
+                char c = s.charAt(i);
+
+                if (Character.isWhitespace(c))
+                {
+                    if (!inCurly)
+                    {
+                        if (buf.length() > 0)
+                        {
+                            addCdataSectionElement(buf.toString(), v);
+                            buf.setLength(0);
+                        }
+                        continue;
+                    }
+                }
+                else if ('{' == c)
+                    inCurly = true;
+                else if ('}' == c)
+                    inCurly = false;
+
+                buf.append(c);
+            }
+
+            if (buf.length() > 0)
+            {
+                addCdataSectionElement(buf.toString(), v);
+                buf.setLength(0);
+            }
+            // call the official, public method to set the collected names
+            setCdataSectionElements(v);
+        }
+
+    }
+
+    /**
+     * Adds a URI/LocalName pair of strings to the list.
+     *
+     * @param URI_and_localName String of the form "{uri}local" or "local" 
+     * 
+     * @return a QName object
+     */
+    private void addCdataSectionElement(String URI_and_localName, Vector v)
+    {
+
+        StringTokenizer tokenizer =
+            new StringTokenizer(URI_and_localName, "{}", false);
+        String s1 = tokenizer.nextToken();
+        String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+
+        if (null == s2)
+        {
+            // add null URI and the local name
+            v.addElement(null);
+            v.addElement(s1);
+        }
+        else
+        {
+            // add URI, then local name
+            v.addElement(s1);
+            v.addElement(s2);
+        }
+    }
+
+    /**
+     * Remembers the cdata sections specified in the cdata-section-elements.
+     * The "official way to set URI and localName pairs. 
+     * This method should be used by both Xalan and XSLTC.
+     * 
+     * @param URI_and_localNames a vector of pairs of Strings (URI/local)
+     */
+    public void setCdataSectionElements(Vector URI_and_localNames)
+    {
+        // convert to the new way.
+        if (URI_and_localNames != null)
+        {
+            final int len = URI_and_localNames.size() - 1;
+            if (len > 0)
+            {
+                final StringBuffer sb = new StringBuffer();
+                for (int i = 0; i < len; i += 2)
+                {
+                    // whitspace separated "{uri1}local1 {uri2}local2 ..."
+                    if (i != 0)
+                        sb.append(' ');
+                    final String uri = (String) URI_and_localNames.elementAt(i);
+                    final String localName =
+                        (String) URI_and_localNames.elementAt(i + 1);
+                    if (uri != null)
+                    {
+                        // If there is no URI don't put this in, just the localName then.
+                        sb.append('{');
+                        sb.append(uri);
+                        sb.append('}');
+                    }
+                    sb.append(localName);
+                }
+                m_StringOfCDATASections = sb.toString();
+            }
+        }
+        initCdataElems(m_StringOfCDATASections);
+    }
+
+    /**
+     * Makes sure that the namespace URI for the given qualified attribute name
+     * is declared.
+     * @param ns the namespace URI
+     * @param rawName the qualified name 
+     * @return returns null if no action is taken, otherwise it returns the
+     * prefix used in declaring the namespace. 
+     * @throws SAXException
+     */
+    protected String ensureAttributesNamespaceIsDeclared(
+        String ns,
+        String localName,
+        String rawName)
+        throws org.xml.sax.SAXException
+    {
+
+        if (ns != null && ns.length() > 0)
+        {
+
+            // extract the prefix in front of the raw name
+            int index = 0;
+            String prefixFromRawName =
+                (index = rawName.indexOf(":")) < 0
+                    ? ""
+                    : rawName.substring(0, index);
+
+            if (index > 0)
+            {
+                // we have a prefix, lets see if it maps to a namespace 
+                String uri = m_prefixMap.lookupNamespace(prefixFromRawName);
+                if (uri != null && uri.equals(ns))
+                {
+                    // the prefix in the raw name is already maps to the given namespace uri
+                    // so we don't need to do anything
+                    return null;
+                }
+                else
+                {
+                    // The uri does not map to the prefix in the raw name,
+                    // so lets make the mapping.
+                    this.startPrefixMapping(prefixFromRawName, ns, false);
+                    this.addAttribute(
+                        "http://www.w3.org/2000/xmlns/",
+                        prefixFromRawName,
+                        "xmlns:" + prefixFromRawName,
+                        "CDATA",
+                        ns, false);
+                    return prefixFromRawName;
+                }
+            }
+            else
+            {
+                // we don't have a prefix in the raw name.
+                // Does the URI map to a prefix already?
+                String prefix = m_prefixMap.lookupPrefix(ns);
+                if (prefix == null)
+                {
+                    // uri is not associated with a prefix,
+                    // so lets generate a new prefix to use
+                    prefix = m_prefixMap.generateNextPrefix();
+                    this.startPrefixMapping(prefix, ns, false);
+                    this.addAttribute(
+                        "http://www.w3.org/2000/xmlns/",
+                        prefix,
+                        "xmlns:" + prefix,
+                        "CDATA",
+                        ns, false);
+                }
+
+                return prefix;
+
+            }
+        }
+        return null;
+    }
+
+    void ensurePrefixIsDeclared(String ns, String rawName)
+        throws org.xml.sax.SAXException
+    {
+
+        if (ns != null && ns.length() > 0)
+        {
+            int index;
+            final boolean no_prefix = ((index = rawName.indexOf(":")) < 0);
+            String prefix = (no_prefix) ? "" : rawName.substring(0, index);
+
+            if (null != prefix)
+            {
+                String foundURI = m_prefixMap.lookupNamespace(prefix);
+
+                if ((null == foundURI) || !foundURI.equals(ns))
+                {
+                    this.startPrefixMapping(prefix, ns);
+
+                    // Bugzilla1133: Generate attribute as well as namespace event.
+                    // SAX does expect both.
+
+                    this.addAttributeAlways(
+                        "http://www.w3.org/2000/xmlns/",
+                        no_prefix ? "xmlns" : prefix,  // local name
+                        no_prefix ? "xmlns" : ("xmlns:"+ prefix), // qname
+                        "CDATA",
+                        ns,
+                        false);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * This method flushes any pending events, which can be startDocument()
+     * closing the opening tag of an element, or closing an open CDATA section.
+     */
+    public void flushPending() throws SAXException
+    {
+            if (m_needToCallStartDocument)
+            {
+                startDocumentInternal();
+                m_needToCallStartDocument = false;
+            }
+            if (m_elemContext.m_startTagOpen)
+            {
+                closeStartTag();
+                m_elemContext.m_startTagOpen = false;
+            }
+
+            if (m_cdataTagOpen)
+            {
+                closeCDATA();
+                m_cdataTagOpen = false;
+            }
+            if (m_writer != null) {
+                try {
+                    m_writer.flush();
+                }
+                catch(IOException e) {
+                    // what? me worry?
+                }
+            }
+    }
+
+    public void setContentHandler(ContentHandler ch)
+    {
+        // this method is really only useful in the ToSAXHandler classes but it is
+        // in the interface.  If the method defined here is ever called
+        // we are probably in trouble.
+    }
+
+    /**
+     * Adds the given attribute to the set of attributes, even if there is
+     * no currently open element. This is useful if a SAX startPrefixMapping()
+     * should need to add an attribute before the element name is seen.
+     * 
+     * This method is a copy of its super classes method, except that some
+     * tracing of events is done.  This is so the tracing is only done for
+     * stream serializers, not for SAX ones.
+     *
+     * @param uri the URI of the attribute
+     * @param localName the local name of the attribute
+     * @param rawName   the qualified name of the attribute
+     * @param type the type of the attribute (probably CDATA)
+     * @param value the value of the attribute
+     * @param xslAttribute true if this attribute is coming from an xsl:attribute element.
+     * @return true if the attribute value was added, 
+     * false if the attribute already existed and the value was
+     * replaced with the new value.
+     */
+    public boolean addAttributeAlways(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean xslAttribute)
+    {
+        boolean was_added;
+        int index;
+        if (uri == null || localName == null || uri.length() == 0)
+            index = m_attributes.getIndex(rawName);
+        else {
+            index = m_attributes.getIndex(uri, localName);
+        }
+
+        if (index >= 0)
+        {
+            String old_value = null;
+            if (m_tracer != null)
+            {
+                old_value = m_attributes.getValue(index);
+                if (value.equals(old_value))
+                    old_value = null;
+            }
+
+            /* We've seen the attribute before.
+             * We may have a null uri or localName, but all we really
+             * want to re-set is the value anyway.
+             */
+            m_attributes.setValue(index, value);
+            was_added = false;
+            if (old_value != null)
+                firePseudoAttributes();
+
+        }
+        else
+        {
+            // the attribute doesn't exist yet, create it
+            if (xslAttribute)
+            {
+                /*
+                 * This attribute is from an xsl:attribute element so we take some care in
+                 * adding it, e.g.
+                 *   <elem1  foo:attr1="1" xmlns:foo="uri1">
+                 *       <xsl:attribute name="foo:attr2">2</xsl:attribute>
+                 *   </elem1>
+                 * 
+                 * We are adding attr1 and attr2 both as attributes of elem1,
+                 * and this code is adding attr2 (the xsl:attribute ).
+                 * We could have a collision with the prefix like in the example above.
+                 */
+
+                // In the example above, is there a prefix like foo ?
+                final int colonIndex = rawName.indexOf(':');
+                if (colonIndex > 0)
+                {
+                    String prefix = rawName.substring(0,colonIndex);
+                    NamespaceMappings.MappingRecord existing_mapping = m_prefixMap.getMappingFromPrefix(prefix);
+
+                    /* Before adding this attribute (foo:attr2),
+                     * is the prefix for it (foo) already mapped at the current depth?
+                     */
+                    if (existing_mapping != null 
+                    && existing_mapping.m_declarationDepth == m_elemContext.m_currentElemDepth
+                    && !existing_mapping.m_uri.equals(uri))
+                    {
+                        /*
+                         * There is an existing mapping of this prefix,
+                         * it differs from the one we need,
+                         * and unfortunately it is at the current depth so we 
+                         * can not over-ride it.
+                         */
+
+                        /*
+                         * Are we lucky enough that an existing other prefix maps to this URI ?
+                         */
+                        prefix = m_prefixMap.lookupPrefix(uri);
+                        if (prefix == null)
+                        {
+                            /* Unfortunately there is no existing prefix that happens to map to ours,
+                             * so to avoid a prefix collision we must generated a new prefix to use. 
+                             * This is OK because the prefix URI mapping
+                             * defined in the xsl:attribute is short in scope, 
+                             * just the xsl:attribute element itself, 
+                             * and at this point in serialization the body of the
+                             * xsl:attribute, if any, is just a String. Right?
+                             *   . . . I sure hope so - Brian M. 
+                             */
+                            prefix = m_prefixMap.generateNextPrefix();
+                        }
+
+                        rawName = prefix + ':' + localName;
+                    }
+                }
+
+                try
+                {
+                    /* This is our last chance to make sure the namespace for this
+                     * attribute is declared, especially if we just generated an alternate
+                     * prefix to avoid a collision (the new prefix/rawName will go out of scope
+                     * soon and be lost ...  last chance here.
+                     */
+                    String prefixUsed =
+                        ensureAttributesNamespaceIsDeclared(
+                            uri,
+                            localName,
+                            rawName);
+                }
+                catch (SAXException e)
+                {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+            m_attributes.addAttribute(uri, localName, rawName, type, value);
+            was_added = true;
+            if (m_tracer != null)
+                firePseudoAttributes();
+        }
+        return was_added;
+    }
+
+    /**
+     * To fire off the pseudo characters of attributes, as they currently
+     * exist. This method should be called everytime an attribute is added,
+     * or when an attribute value is changed, or an element is created.
+     */
+
+    protected void firePseudoAttributes()
+    {
+        if (m_tracer != null)
+        {
+            try
+            {
+                // flush out the "<elemName" if not already flushed
+                m_writer.flush();
+                
+                // make a StringBuffer to write the name="value" pairs to.
+                StringBuffer sb = new StringBuffer();
+                int nAttrs = m_attributes.getLength();
+                if (nAttrs > 0)
+                {
+                    // make a writer that internally appends to the same
+                    // StringBuffer
+                    java.io.Writer writer =
+                        new ToStream.WritertoStringBuffer(sb);
+
+                    processAttributes(writer, nAttrs);
+                    // Don't clear the attributes! 
+                    // We only want to see what would be written out
+                    // at this point, we don't want to loose them.
+                }
+                sb.append('>');  // the potential > after the attributes.
+                // convert the StringBuffer to a char array and
+                // emit the trace event that these characters "might"
+                // be written                
+                char ch[] = sb.toString().toCharArray();
+                m_tracer.fireGenerateEvent(
+                    SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
+                    ch,
+                    0,
+                    ch.length);                
+            }
+            catch (IOException ioe)
+            {
+                // ignore ?
+            }
+            catch (SAXException se)
+            {
+                // ignore ?
+            }
+        }
+    }
+
+    /**
+     * This inner class is used only to collect attribute values
+     * written by the method writeAttrString() into a string buffer.
+     * In this manner trace events, and the real writing of attributes will use
+     * the same code.
+     */
+    private class WritertoStringBuffer extends java.io.Writer
+    {
+        final private StringBuffer m_stringbuf;
+        /**
+         * @see java.io.Writer#write(char[], int, int)
+         */
+        WritertoStringBuffer(StringBuffer sb)
+        {
+            m_stringbuf = sb;
+        }
+
+        public void write(char[] arg0, int arg1, int arg2) throws IOException
+        {
+            m_stringbuf.append(arg0, arg1, arg2);
+        }
+        /**
+         * @see java.io.Writer#flush()
+         */
+        public void flush() throws IOException
+        {
+        }
+        /**
+         * @see java.io.Writer#close()
+         */
+        public void close() throws IOException
+        {
+        }
+
+        public void write(int i)
+        {
+            m_stringbuf.append((char) i);
+        }
+        
+        public void write(String s)
+        {
+            m_stringbuf.append(s);
+        }
+    }
+
+    /**
+     * @see SerializationHandler#setTransformer(Transformer)
+     */
+    public void setTransformer(Transformer transformer) {
+        super.setTransformer(transformer);
+        if (m_tracer != null
+         && !(m_writer instanceof SerializerTraceWriter)  )
+            setWriterInternal(new SerializerTraceWriter(m_writer, m_tracer), false);        
+        
+        
+    }
+    /**
+     * Try's to reset the super class and reset this class for 
+     * re-use, so that you don't need to create a new serializer 
+     * (mostly for performance reasons).
+     * 
+     * @return true if the class was successfuly reset.
+     */
+    public boolean reset()
+    {
+        boolean wasReset = false;
+        if (super.reset())
+        {
+            resetToStream();
+            wasReset = true;
+        }
+        return wasReset;
+    }
+    
+    /**
+     * Reset all of the fields owned by ToStream class
+     *
+     */
+    private void resetToStream()
+    {
+         this.m_cdataStartCalled = false;
+         /* The stream is being reset. It is one of
+          * ToXMLStream, ToHTMLStream ... and this type can't be changed
+          * so neither should m_charInfo which is associated with the
+          * type of Stream. Just leave m_charInfo as-is for the next re-use.
+          * 
+          */
+         // this.m_charInfo = null; // don't set to null 
+         this.m_disableOutputEscapingStates.clear();
+         // this.m_encodingInfo = null; // don't set to null
+         
+         this.m_escaping = true;
+         // Leave m_format alone for now - Brian M.
+         // this.m_format = null;
+         this.m_expandDTDEntities = true; 
+         this.m_inDoctype = false;
+         this.m_ispreserve = false;
+         this.m_isprevtext = false;
+         this.m_isUTF8 = false; //  ?? used anywhere ??
+         this.m_lineSep = s_systemLineSep;
+         this.m_lineSepLen = s_systemLineSep.length;
+         this.m_lineSepUse = true;
+         // this.m_outputStream = null; // Don't reset it may be re-used
+         this.m_preserves.clear();
+         this.m_shouldFlush = true;
+         this.m_spaceBeforeClose = false;
+         this.m_startNewLine = false;
+         this.m_writer_set_by_user = false;
+    }        
+    
+    /**
+      * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
+      * @param encoding the character encoding
+      */
+     public void setEncoding(String encoding)
+     {
+         setOutputProperty(OutputKeys.ENCODING,encoding);
+     }
+     
+    /**
+     * Simple stack for boolean values.
+     * 
+     * This class is a copy of the one in org.apache.xml.utils. 
+     * It exists to cut the serializers dependancy on that package.
+     * A minor changes from that package are:
+     * doesn't implement Clonable
+     *  
+     * @xsl.usage internal
+     */
+    static final class BoolStack
+    {
+
+      /** Array of boolean values          */
+      private boolean m_values[];
+
+      /** Array size allocated           */
+      private int m_allocatedSize;
+
+      /** Index into the array of booleans          */
+      private int m_index;
+
+      /**
+       * Default constructor.  Note that the default
+       * block size is very small, for small lists.
+       */
+      public BoolStack()
+      {
+        this(32);
+      }
+
+      /**
+       * Construct a IntVector, using the given block size.
+       *
+       * @param size array size to allocate
+       */
+      public BoolStack(int size)
+      {
+
+        m_allocatedSize = size;
+        m_values = new boolean[size];
+        m_index = -1;
+      }
+
+      /**
+       * Get the length of the list.
+       *
+       * @return Current length of the list
+       */
+      public final int size()
+      {
+        return m_index + 1;
+      }
+
+      /**
+       * Clears the stack.
+       *
+       */
+      public final void clear()
+      {
+        m_index = -1;
+      }
+
+      /**
+       * Pushes an item onto the top of this stack.
+       *
+       *
+       * @param val the boolean to be pushed onto this stack.
+       * @return  the <code>item</code> argument.
+       */
+      public final boolean push(boolean val)
+      {
+
+        if (m_index == m_allocatedSize - 1)
+          grow();
+
+        return (m_values[++m_index] = val);
+      }
+
+      /**
+       * Removes the object at the top of this stack and returns that
+       * object as the value of this function.
+       *
+       * @return     The object at the top of this stack.
+       * @throws  EmptyStackException  if this stack is empty.
+       */
+      public final boolean pop()
+      {
+        return m_values[m_index--];
+      }
+
+      /**
+       * Removes the object at the top of this stack and returns the
+       * next object at the top as the value of this function.
+       *
+       *
+       * @return Next object to the top or false if none there
+       */
+      public final boolean popAndTop()
+      {
+
+        m_index--;
+
+        return (m_index >= 0) ? m_values[m_index] : false;
+      }
+
+      /**
+       * Set the item at the top of this stack  
+       *
+       *
+       * @param b Object to set at the top of this stack
+       */
+      public final void setTop(boolean b)
+      {
+        m_values[m_index] = b;
+      }
+
+      /**
+       * Looks at the object at the top of this stack without removing it
+       * from the stack.
+       *
+       * @return     the object at the top of this stack.
+       * @throws  EmptyStackException  if this stack is empty.
+       */
+      public final boolean peek()
+      {
+        return m_values[m_index];
+      }
+
+      /**
+       * Looks at the object at the top of this stack without removing it
+       * from the stack.  If the stack is empty, it returns false.
+       *
+       * @return     the object at the top of this stack.
+       */
+      public final boolean peekOrFalse()
+      {
+        return (m_index > -1) ? m_values[m_index] : false;
+      }
+
+      /**
+       * Looks at the object at the top of this stack without removing it
+       * from the stack.  If the stack is empty, it returns true.
+       *
+       * @return     the object at the top of this stack.
+       */
+      public final boolean peekOrTrue()
+      {
+        return (m_index > -1) ? m_values[m_index] : true;
+      }
+
+      /**
+       * Tests if this stack is empty.
+       *
+       * @return  <code>true</code> if this stack is empty;
+       *          <code>false</code> otherwise.
+       */
+      public boolean isEmpty()
+      {
+        return (m_index == -1);
+      }
+
+      /**
+       * Grows the size of the stack
+       *
+       */
+      private void grow()
+      {
+
+        m_allocatedSize *= 2;
+
+        boolean newVector[] = new boolean[m_allocatedSize];
+
+        System.arraycopy(m_values, 0, newVector, 0, m_index + 1);
+
+        m_values = newVector;
+      }
+    }
+    
+    // Implement DTDHandler
+    /**
+     * If this method is called, the serializer is used as a
+     * DTDHandler, which changes behavior how the serializer 
+     * handles document entities. 
+     * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
+     */
+    public void notationDecl(String name, String pubID, String sysID) throws SAXException {
+        // TODO Auto-generated method stub
+        try {
+            DTDprolog();
+            
+            m_writer.write("<!NOTATION ");            
+            m_writer.write(name);
+            if (pubID != null) {
+                m_writer.write(" PUBLIC \"");
+                m_writer.write(pubID);
+  
+            }
+            else {
+                m_writer.write(" SYSTEM \"");
+                m_writer.write(sysID);
+            }
+            m_writer.write("\" >");
+            m_writer.write(m_lineSep, 0, m_lineSepLen);
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * If this method is called, the serializer is used as a
+     * DTDHandler, which changes behavior how the serializer 
+     * handles document entities. 
+     * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+     */
+    public void unparsedEntityDecl(String name, String pubID, String sysID, String notationName) throws SAXException {
+        // TODO Auto-generated method stub
+        try {
+            DTDprolog();       
+            
+            m_writer.write("<!ENTITY ");            
+            m_writer.write(name);
+            if (pubID != null) {
+                m_writer.write(" PUBLIC \"");
+                m_writer.write(pubID);
+  
+            }
+            else {
+                m_writer.write(" SYSTEM \"");
+                m_writer.write(sysID);
+            }
+            m_writer.write("\" NDATA ");
+            m_writer.write(notationName);
+            m_writer.write(" >");
+            m_writer.write(m_lineSep, 0, m_lineSepLen);
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }        
+    }
+    
+    /**
+     * A private helper method to output the 
+     * @throws SAXException
+     * @throws IOException
+     */
+    private void DTDprolog() throws SAXException, IOException {
+        final java.io.Writer writer = m_writer;
+        if (m_needToOutputDocTypeDecl)
+        {
+            outputDocTypeDecl(m_elemContext.m_elementName, false);
+            m_needToOutputDocTypeDecl = false;
+        }
+        if (m_inDoctype)
+        {
+            writer.write(" [");
+            writer.write(m_lineSep, 0, m_lineSepLen);
+            m_inDoctype = false;
+        }
+    }
+    
+    /**
+     * If set to false the serializer does not expand DTD entities,
+     * but leaves them as is, the default value is true;
+     */
+    public void setDTDEntityExpansion(boolean expand) { 
+        m_expandDTDEntities = expand;     
+    }
+        
+    /**
+     * Sets the end of line characters to be used during serialization
+     * @param eolChars A character array corresponding to the characters to be used.
+     */    
+    public void setNewLine (char[] eolChars) {
+        m_lineSep = eolChars;
+        m_lineSepLen = eolChars.length;
+    }
+
+    /**
+     * Remembers the cdata sections specified in the cdata-section-elements by appending the given
+     * cdata section elements to the list. This method can be called multiple times, but once an
+     * element is put in the list of cdata section elements it can not be removed.
+     * This method should be used by both Xalan and XSLTC.
+     * 
+     * @param URI_and_localNames a whitespace separated list of element names, each element
+     * is a URI in curly braces (optional) and a local name. An example of such a parameter is:
+     * "{http://company.com}price {myURI2}book chapter"
+     */
+    public void addCdataSectionElements(String URI_and_localNames)
+    {
+        if (URI_and_localNames != null)
+            initCdataElems(URI_and_localNames);
+        if (m_StringOfCDATASections == null)
+            m_StringOfCDATASections = URI_and_localNames;
+        else
+            m_StringOfCDATASections += (" " + URI_and_localNames);
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/ToTextSAXHandler.java b/src/main/java/org/apache/xml/serializer/ToTextSAXHandler.java
new file mode 100644
index 0000000..f472a6d
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ToTextSAXHandler.java
@@ -0,0 +1,413 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ToTextSAXHandler.java 475978 2006-11-16 23:31:20Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Properties;
+
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * This class converts SAX-like event to SAX events for
+ * xsl:output method "text". 
+ * 
+ * This class is only to be used internally. This class is not a public API.
+ * 
+ * @deprecated As of Xalan 2.7.1, replaced by the use of {@link ToXMLSAXHandler}.
+ * 
+ * @xsl.usage internal
+ */
+public final class ToTextSAXHandler extends ToSAXHandler 
+{
+    /**
+     * From XSLTC
+     * @see ExtendedContentHandler#endElement(String)
+     */
+    public void endElement(String elemName) throws SAXException
+    {
+        if (m_tracer != null)
+            super.fireEndElem(elemName);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endElement(String, String, String)
+     */
+    public void endElement(String arg0, String arg1, String arg2)
+        throws SAXException
+    {
+		if (m_tracer != null)
+            super.fireEndElem(arg2);    	
+    }
+
+    public ToTextSAXHandler(ContentHandler hdlr, LexicalHandler lex, String encoding)
+    {
+        super(hdlr, lex, encoding);
+    }
+    
+        /**
+     * From XSLTC
+     */
+    public ToTextSAXHandler(ContentHandler handler, String encoding)
+    {
+        super(handler,encoding);
+    }
+
+    public void comment(char ch[], int start, int length)
+        throws org.xml.sax.SAXException
+    {
+        if (m_tracer != null)
+            super.fireCommentEvent(ch, start, length);
+    }
+
+    public void comment(String data) throws org.xml.sax.SAXException
+    {
+        final int length = data.length();
+        if (length > m_charsBuff.length)
+        {
+            m_charsBuff = new char[length*2 + 1];
+        }
+        data.getChars(0, length, m_charsBuff, 0);
+        comment(m_charsBuff, 0, length);
+    }
+
+    /**
+     * @see Serializer#getOutputFormat()
+     */
+    public Properties getOutputFormat()
+    {
+        return null;
+    }
+
+    /**
+     * @see Serializer#getOutputStream()
+     */
+    public OutputStream getOutputStream()
+    {
+        return null;
+    }
+
+    /**
+     * @see Serializer#getWriter()
+     */
+    public Writer getWriter()
+    {
+        return null;
+    }
+
+    /**
+     * Does nothing because 
+     * the indent attribute is ignored for text output.
+     *
+     */
+    public void indent(int n) throws SAXException
+    {
+    }
+
+    /**
+     * @see Serializer#reset()
+     */
+    public boolean reset()
+    {
+        return false;
+    }
+
+    /**
+     * @see DOMSerializer#serialize(Node)
+     */
+    public void serialize(Node node) throws IOException
+    {
+    }
+
+    /**
+     * @see SerializationHandler#setEscaping(boolean)
+     */
+    public boolean setEscaping(boolean escape)
+    {
+        return false;
+    }
+
+    /**
+     * @see SerializationHandler#setIndent(boolean)
+     */
+    public void setIndent(boolean indent)
+    {
+    }
+
+    /**
+     * @see Serializer#setOutputFormat(Properties)
+     */
+    public void setOutputFormat(Properties format)
+    {
+    }
+
+    /**
+     * @see Serializer#setOutputStream(OutputStream)
+     */
+    public void setOutputStream(OutputStream output)
+    {
+    }
+
+    /**
+     * @see Serializer#setWriter(Writer)
+     */
+    public void setWriter(Writer writer)
+    {
+    }
+
+    /**
+     * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean XSLAttribute)
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String)
+     */
+    public void attributeDecl(
+        String arg0,
+        String arg1,
+        String arg2,
+        String arg3,
+        String arg4)
+        throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String)
+     */
+    public void elementDecl(String arg0, String arg1) throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String)
+     */
+    public void externalEntityDecl(String arg0, String arg1, String arg2)
+        throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(String, String)
+     */
+    public void internalEntityDecl(String arg0, String arg1)
+        throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
+     */
+    public void endPrefixMapping(String arg0) throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+     */
+    public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
+        throws SAXException
+    {
+    }
+
+    /**
+     * From XSLTC
+     * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
+     */
+    public void processingInstruction(String arg0, String arg1)
+        throws SAXException
+    {
+        if (m_tracer != null)
+            super.fireEscapingEvent(arg0, arg1);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
+     */
+    public void setDocumentLocator(Locator arg0)
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#skippedEntity(String)
+     */
+    public void skippedEntity(String arg0) throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
+     */
+    public void startElement(
+        String arg0,
+        String arg1,
+        String arg2,
+        Attributes arg3)
+        throws SAXException
+    {
+        flushPending();
+        super.startElement(arg0, arg1, arg2, arg3);
+    }
+
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#endCDATA()
+     */
+    public void endCDATA() throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#endDTD()
+     */
+    public void endDTD() throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#startCDATA()
+     */
+    public void startCDATA() throws SAXException
+    {
+    }
+
+
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#startEntity(String)
+     */
+    public void startEntity(String arg0) throws SAXException
+    {
+    }
+
+
+    /**
+     * From XSLTC
+     * @see ExtendedContentHandler#startElement(String)
+     */
+    public void startElement(
+    String elementNamespaceURI,
+    String elementLocalName,
+    String elementName) throws SAXException
+    {
+        super.startElement(elementNamespaceURI, elementLocalName, elementName);
+    }
+    
+    public void startElement(
+    String elementName) throws SAXException
+    {
+        super.startElement(elementName);
+    }
+    
+
+    /**
+     * From XSLTC
+     * @see org.xml.sax.ContentHandler#endDocument()
+     */
+    public void endDocument() throws SAXException { 
+        
+        flushPending();
+        m_saxHandler.endDocument();
+		
+        if (m_tracer != null)
+            super.fireEndDoc();
+    }
+ 
+    /**
+	 *	
+     * @see ExtendedContentHandler#characters(String)
+     */
+    public void characters(String characters) 
+    throws SAXException 
+    { 
+        final int length = characters.length();
+        if (length > m_charsBuff.length)
+        {
+            m_charsBuff = new char[length*2 + 1];
+        }
+        characters.getChars(0, length, m_charsBuff, 0);
+   
+        m_saxHandler.characters(m_charsBuff, 0, length);
+    
+    }
+    /**
+	 * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+     */
+    public void characters(char[] characters, int offset, int length)
+    throws SAXException 
+    { 
+    
+        m_saxHandler.characters(characters, offset, length);
+
+        // time to fire off characters event
+		if (m_tracer != null)
+            super.fireCharEvent(characters, offset, length);                
+    }
+
+    /**
+     * From XSLTC
+     */
+    public void addAttribute(String name, String value) 
+    {
+        // do nothing
+    }
+
+
+    public boolean startPrefixMapping(
+        String prefix,
+        String uri,
+        boolean shouldFlush)
+        throws SAXException
+    {
+        // no namespace support for HTML
+        return false;
+    }
+
+
+    public void startPrefixMapping(String prefix, String uri)
+        throws org.xml.sax.SAXException
+    {
+        // no namespace support for HTML
+    }
+
+
+    public void namespaceAfterStartElement(
+        final String prefix,
+        final String uri)
+        throws SAXException
+    {
+        // no namespace support for HTML
+    }
+
+}
diff --git a/src/main/java/org/apache/xml/serializer/ToTextStream.java b/src/main/java/org/apache/xml/serializer/ToTextStream.java
new file mode 100644
index 0000000..b0d066e
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ToTextStream.java
@@ -0,0 +1,634 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ToTextStream.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+
+import org.apache.xml.serializer.utils.MsgKey;
+import org.apache.xml.serializer.utils.Utils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * This class is not a public API.
+ * It is only public because it is used in other packages. 
+ * This class converts SAX or SAX-like calls to a 
+ * serialized document for xsl:output method of "text".
+ * @xsl.usage internal
+ */
+public class ToTextStream extends ToStream 
+{ 
+
+       
+  /**
+   * Default constructor.
+   */
+  public ToTextStream()
+  {
+    super();
+  }
+
+ 
+ 
+  /**
+   * Receive notification of the beginning of a document.
+   *
+   * <p>The SAX parser will invoke this method only once, before any
+   * other methods in this interface or in DTDHandler (except for
+   * setDocumentLocator).</p>
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  protected void startDocumentInternal() throws org.xml.sax.SAXException
+  {
+    super.startDocumentInternal();
+
+    m_needToCallStartDocument = false;
+
+    // No action for the moment.
+  }
+
+  /**
+   * Receive notification of the end of a document.
+   *
+   * <p>The SAX parser will invoke this method only once, and it will
+   * be the last method invoked during the parse.  The parser shall
+   * not invoke this method until it has either abandoned parsing
+   * (because of an unrecoverable error) or reached the end of
+   * input.</p>
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void endDocument() throws org.xml.sax.SAXException
+  {
+    flushPending();
+    flushWriter();
+    if (m_tracer != null)
+        super.fireEndDoc();
+  }
+
+  /**
+   * Receive notification of the beginning of an element.
+   *
+   * <p>The Parser will invoke this method at the beginning of every
+   * element in the XML document; there will be a corresponding
+   * endElement() event for every startElement() event (even when the
+   * element is empty). All of the element's content will be
+   * reported, in order, before the corresponding endElement()
+   * event.</p>
+   *
+   * <p>If the element name has a namespace prefix, the prefix will
+   * still be attached.  Note that the attribute list provided will
+   * contain only attributes with explicit values (specified or
+   * defaulted): #IMPLIED attributes will be omitted.</p>
+   *
+   *
+   * @param namespaceURI The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param name The qualified name (with prefix), or the
+   *        empty string if qualified names are not available.
+   * @param atts The attributes attached to the element, if any.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see #endElement
+   * @see org.xml.sax.AttributeList
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void startElement(
+          String namespaceURI, String localName, String name, Attributes atts)
+            throws org.xml.sax.SAXException
+  {
+    // time to fire off startElement event
+    if (m_tracer != null) {
+        super.fireStartElem(name);
+        this.firePseudoAttributes();
+    }
+    return;
+  }
+
+  /**
+   * Receive notification of the end of an element.
+   *
+   * <p>The SAX parser will invoke this method at the end of every
+   * element in the XML document; there will be a corresponding
+   * startElement() event for every endElement() event (even when the
+   * element is empty).</p>
+   *
+   * <p>If the element name has a namespace prefix, the prefix will
+   * still be attached to the name.</p>
+   *
+   *
+   * @param namespaceURI The Namespace URI, or the empty string if the
+   *        element has no Namespace URI or if Namespace
+   *        processing is not being performed.
+   * @param localName The local name (without prefix), or the
+   *        empty string if Namespace processing is not being
+   *        performed.
+   * @param name The qualified name (with prefix), or the
+   *        empty string if qualified names are not available.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void endElement(String namespaceURI, String localName, String name)
+          throws org.xml.sax.SAXException
+  {
+        if (m_tracer != null)
+            super.fireEndElem(name);           
+  }
+
+  /**
+   * Receive notification of character data.
+   *
+   * <p>The Parser will call this method to report each chunk of
+   * character data.  SAX parsers may return all contiguous character
+   * data in a single chunk, or they may split it into several
+   * chunks; however, all of the characters in any single event
+   * must come from the same external entity, so that the Locator
+   * provides useful information.</p>
+   *
+   * <p>The application must not attempt to read from the array
+   * outside of the specified range.</p>
+   *
+   * <p>Note that some parsers will report whitespace using the
+   * ignorableWhitespace() method rather than this one (validating
+   * parsers must do so).</p>
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see #ignorableWhitespace
+   * @see org.xml.sax.Locator
+   */
+  public void characters(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+
+    flushPending();    
+    
+    try
+    {
+        if (inTemporaryOutputState()) {
+            /* leave characters un-processed as we are
+             * creating temporary output, the output generated by
+             * this serializer will be input to a final serializer 
+             * later on and it will do the processing in final
+             * output state (not temporary output state).
+             * 
+             * A "temporary" ToTextStream serializer is used to
+             * evaluate attribute value templates (for example),
+             * and the result of evaluating such a thing
+             * is fed into a final serializer later on.
+             */
+            m_writer.write(ch, start, length);
+        }
+        else {
+            // In final output state we do process the characters!
+            writeNormalizedChars(ch, start, length, m_lineSepUse);
+        }
+            
+        if (m_tracer != null)
+            super.fireCharEvent(ch, start, length);      
+    }
+    catch(IOException ioe)
+    {
+      throw new SAXException(ioe);
+    }
+  }
+
+  /**
+   * If available, when the disable-output-escaping attribute is used,
+   * output raw text without escaping.
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   *
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void charactersRaw(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      writeNormalizedChars(ch, start, length, m_lineSepUse);
+    }
+    catch(IOException ioe)
+    {
+      throw new SAXException(ioe);
+    }
+  }
+  
+    /**
+     * Normalize the characters, but don't escape.  Different from 
+     * SerializerToXML#writeNormalizedChars because it does not attempt to do 
+     * XML escaping at all.
+     *
+     * @param ch The characters from the XML document.
+     * @param start The start position in the array.
+     * @param length The number of characters to read from the array.
+     * @param useLineSep true if the operating systems 
+     * end-of-line separator should be output rather than a new-line character.
+     * 
+     * @throws IOException
+     * @throws org.xml.sax.SAXException
+     */
+    void writeNormalizedChars(
+        final char ch[],
+            final int start,
+            final int length,
+            final boolean useLineSep)
+            throws IOException, org.xml.sax.SAXException 
+    {
+        final String encoding = getEncoding();
+        final java.io.Writer writer = m_writer;
+        final int end = start + length;
+
+        /* copy a few "constants" before the loop for performance */
+        final char S_LINEFEED = CharInfo.S_LINEFEED;
+
+        // This for() loop always increments i by one at the end
+        // of the loop.  Additional increments of i adjust for when
+        // two input characters (a high/low UTF16 surrogate pair)
+        // are processed.
+        for (int i = start; i < end; i++) {
+            final char c = ch[i];
+
+            if (S_LINEFEED == c && useLineSep) {
+                writer.write(m_lineSep, 0, m_lineSepLen);
+                // one input char processed
+            } else if (m_encodingInfo.isInEncoding(c)) {
+                writer.write(c);
+                // one input char processed    
+            } else if (Encodings.isHighUTF16Surrogate(c)) {
+                final int codePoint = writeUTF16Surrogate(c, ch, i, end);
+                if (codePoint != 0) {
+                    // I think we can just emit the message,
+                    // not crash and burn.
+                    final String integralValue = Integer.toString(codePoint);
+                    final String msg = Utils.messages.createMessage(
+                        MsgKey.ER_ILLEGAL_CHARACTER,
+                        new Object[] { integralValue, encoding });
+                      
+                    //Older behavior was to throw the message,
+                    //but newer gentler behavior is to write a message to System.err
+                    //throw new SAXException(msg);
+                    System.err.println(msg);                            
+
+                }
+                i++; // two input chars processed               
+            } else {
+                // Don't know what to do with this char, it is
+                // not in the encoding and not a high char in
+                // a surrogate pair, so write out as an entity ref
+                if (encoding != null) {
+                    /* The output encoding is known, 
+                     * so somthing is wrong.
+                     */
+
+                    // not in the encoding, so write out a character reference
+                    writer.write('&');
+                    writer.write('#');
+                    writer.write(Integer.toString(c));
+                    writer.write(';');
+
+                    // I think we can just emit the message,
+                    // not crash and burn.
+                    final String integralValue = Integer.toString(c);
+                    final String msg = Utils.messages.createMessage(
+                        MsgKey.ER_ILLEGAL_CHARACTER,
+                        new Object[] { integralValue, encoding });
+                      
+                    //Older behavior was to throw the message,
+                    //but newer gentler behavior is to write a message to System.err
+                    //throw new SAXException(msg);
+                    System.err.println(msg); 
+                } else {
+                    /* The output encoding is not known,
+                     * so just write it out as-is.
+                     */
+                    writer.write(c);
+                }
+
+                // one input char was processed
+            }
+        }
+    }
+
+  /**
+   * Receive notification of cdata.
+   *
+   * <p>The Parser will call this method to report each chunk of
+   * character data.  SAX parsers may return all contiguous character
+   * data in a single chunk, or they may split it into several
+   * chunks; however, all of the characters in any single event
+   * must come from the same external entity, so that the Locator
+   * provides useful information.</p>
+   *
+   * <p>The application must not attempt to read from the array
+   * outside of the specified range.</p>
+   *
+   * <p>Note that some parsers will report whitespace using the
+   * ignorableWhitespace() method rather than this one (validating
+   * parsers must do so).</p>
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see #ignorableWhitespace
+   * @see org.xml.sax.Locator
+   */
+  public void cdata(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+    try
+    {
+        writeNormalizedChars(ch, start, length, m_lineSepUse);
+        if (m_tracer != null)
+            super.fireCDATAEvent(ch, start, length);              
+    }
+    catch(IOException ioe)
+    {
+      throw new SAXException(ioe);
+    }
+  }
+
+  /**
+   * Receive notification of ignorable whitespace in element content.
+   *
+   * <p>Validating Parsers must use this method to report each chunk
+   * of ignorable whitespace (see the W3C XML 1.0 recommendation,
+   * section 2.10): non-validating parsers may also use this method
+   * if they are capable of parsing and using content models.</p>
+   *
+   * <p>SAX parsers may return all contiguous whitespace in a single
+   * chunk, or they may split it into several chunks; however, all of
+   * the characters in any single event must come from the same
+   * external entity, so that the Locator provides useful
+   * information.</p>
+   *
+   * <p>The application must not attempt to read from the array
+   * outside of the specified range.</p>
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see #characters
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void ignorableWhitespace(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+
+    try
+    {
+      writeNormalizedChars(ch, start, length, m_lineSepUse);
+    }
+    catch(IOException ioe)
+    {
+      throw new SAXException(ioe);
+    }
+  }
+
+  /**
+   * Receive notification of a processing instruction.
+   *
+   * <p>The Parser will invoke this method once for each processing
+   * instruction found: note that processing instructions may occur
+   * before or after the main document element.</p>
+   *
+   * <p>A SAX parser should never report an XML declaration (XML 1.0,
+   * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
+   * using this method.</p>
+   *
+   * @param target The processing instruction target.
+   * @param data The processing instruction data, or null if
+   *        none was supplied.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void processingInstruction(String target, String data)
+          throws org.xml.sax.SAXException
+  {
+    // flush anything pending first
+    flushPending();  
+    
+    if (m_tracer != null)
+        super.fireEscapingEvent(target, data);  
+  }
+
+  /**
+   * Called when a Comment is to be constructed.
+   * Note that Xalan will normally invoke the other version of this method.
+   * %REVIEW% In fact, is this one ever needed, or was it a mistake?
+   *
+   * @param   data  The comment data.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void comment(String data) throws org.xml.sax.SAXException
+  {
+      final int length = data.length();
+      if (length > m_charsBuff.length)
+      {
+          m_charsBuff = new char[length*2 + 1];
+      }
+      data.getChars(0, length, m_charsBuff, 0);
+      comment(m_charsBuff, 0, length);
+  }
+
+  /**
+   * Report an XML comment anywhere in the document.
+   *
+   * This callback will be used for comments inside or outside the
+   * document element, including comments in the external DTD
+   * subset (if read).
+   *
+   * @param ch An array holding the characters in the comment.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   * @throws org.xml.sax.SAXException The application may raise an exception.
+   */
+  public void comment(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+
+    flushPending();
+    if (m_tracer != null)
+        super.fireCommentEvent(ch, start, length);
+  }
+
+  /**
+   * Receive notivication of a entityReference.
+   *
+   * @param name non-null reference to the name of the entity.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void entityReference(String name) throws org.xml.sax.SAXException
+  {
+        if (m_tracer != null)
+            super.fireEntityReference(name);    
+  }
+  
+    /**
+     * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean XSLAttribute)
+    {
+        // do nothing, just forget all about the attribute
+    }
+ 
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#endCDATA()
+     */
+    public void endCDATA() throws SAXException
+    {
+        // do nothing
+    }
+
+    /**
+     * @see ExtendedContentHandler#endElement(String)
+     */
+    public void endElement(String elemName) throws SAXException
+    {
+        if (m_tracer != null)
+            super.fireEndElem(elemName);                       
+    }
+ 
+    /**
+     * From XSLTC
+     */
+    public void startElement(
+    String elementNamespaceURI,
+    String elementLocalName,
+    String elementName) 
+    throws SAXException 
+    {
+        if (m_needToCallStartDocument)
+            startDocumentInternal();        
+        // time to fire off startlement event.
+        if (m_tracer != null) {
+            super.fireStartElem(elementName);
+            this.firePseudoAttributes();
+        }
+        
+        return;
+    }
+
+
+    /**
+     * From XSLTC
+     */
+    public void characters(String characters) 
+    throws SAXException 
+    { 
+        final int length = characters.length();
+        if (length > m_charsBuff.length)
+        {
+            m_charsBuff = new char[length*2 + 1];
+        }
+        characters.getChars(0, length, m_charsBuff, 0);
+        characters(m_charsBuff, 0, length); 
+    }
+
+
+    /**
+     * From XSLTC
+     */
+    public void addAttribute(String name, String value)
+    {
+        // do nothing, forget about the attribute
+    }
+    
+    /**
+     * Add a unique attribute
+     */
+    public void addUniqueAttribute(String qName, String value, int flags)
+        throws SAXException
+    {
+        // do nothing, forget about the attribute 
+    }
+
+    public boolean startPrefixMapping(
+        String prefix,
+        String uri,
+        boolean shouldFlush)
+        throws SAXException
+    {
+        // no namespace support for HTML
+        return false;
+    }
+
+
+    public void startPrefixMapping(String prefix, String uri)
+        throws org.xml.sax.SAXException
+    {
+        // no namespace support for HTML
+    }
+
+
+    public void namespaceAfterStartElement(
+        final String prefix,
+        final String uri)
+        throws SAXException
+    {
+        // no namespace support for HTML
+    }    
+
+    public void flushPending() throws org.xml.sax.SAXException
+    {
+            if (m_needToCallStartDocument)
+            {
+                startDocumentInternal();
+                m_needToCallStartDocument = false;
+            }
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/ToUnknownStream.java b/src/main/java/org/apache/xml/serializer/ToUnknownStream.java
new file mode 100644
index 0000000..baa1ca2
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ToUnknownStream.java
@@ -0,0 +1,1316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ToUnknownStream.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Properties;
+import java.util.Vector;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.Transformer;
+
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+
+/**
+ *This class wraps another SerializationHandler. The wrapped object will either
+ * handler XML or HTML, which is not known until a little later when the first XML
+ * tag is seen.  If the first tag is <html> then the wrapped object is an HTML
+ * handler, otherwise it is an XML handler.
+ *
+ * This class effectively caches the first few calls to it then passes them
+ * on to the wrapped handler (once it exists).  After that subsequent calls a
+ * simply passed directly to the wrapped handler.
+ *
+ * The user of this class doesn't know if the output is ultimatley XML or HTML.
+ * 
+ * This class is not a public API, it is public because it is used within Xalan.
+ * @xsl.usage internal
+ */
+public final class ToUnknownStream extends SerializerBase
+{
+
+    /**
+     * The wrapped handler, initially XML but possibly switched to HTML
+     */
+    private SerializationHandler m_handler;
+
+    /**
+     * A String with no characters
+     */
+    private static final String EMPTYSTRING = "";
+
+    /**
+     * true if the underlying handler (XML or HTML) is fully initialized
+     */
+    private boolean m_wrapped_handler_not_initialized = false;
+
+
+    /**
+     * the prefix of the very first tag in the document
+     */
+    private String m_firstElementPrefix;
+    /**
+     * the element name (including any prefix) of the very first tag in the document
+     */
+    private String m_firstElementName;
+
+    /**
+     * the namespace URI associated with the first element
+     */
+    private String m_firstElementURI;
+    
+    /**
+     * the local name (no prefix) associated with the first element
+     */
+    private String m_firstElementLocalName = null;
+
+    /**
+     * true if the first tag has been emitted to the wrapped handler
+     */
+    private boolean m_firstTagNotEmitted = true;
+
+    /**
+     * A collection of namespace URI's (only for first element).
+     * _namespacePrefix has the matching prefix for these URI's
+     */
+    private Vector m_namespaceURI = null;
+    /**
+     * A collection of namespace Prefix (only for first element)
+     * _namespaceURI has the matching URIs for these prefix'
+     */
+    private Vector m_namespacePrefix = null;
+
+    /**
+     * true if startDocument() was called before the underlying handler
+     * was initialized
+     */
+    private boolean m_needToCallStartDocument = false;
+    /**
+     * true if setVersion() was called before the underlying handler
+     * was initialized
+     */
+    private boolean m_setVersion_called = false;
+    /**
+     * true if setDoctypeSystem() was called before the underlying handler
+     * was initialized
+     */
+    private boolean m_setDoctypeSystem_called = false;
+    /**
+     * true if setDoctypePublic() was called before the underlying handler
+     * was initialized
+     */
+    private boolean m_setDoctypePublic_called = false;
+    /**
+     * true if setMediaType() was called before the underlying handler
+     * was initialized
+     */
+    private boolean m_setMediaType_called = false;
+
+    /**
+     * Default constructor.
+     * Initially this object wraps an XML Stream object, so _handler is never null.
+     * That may change later to an HTML Stream object.
+     */
+    public ToUnknownStream()
+    {
+        m_handler = new ToXMLStream();
+    }
+
+    /**
+     * @see Serializer#asContentHandler()
+     * @return the wrapped XML or HTML handler
+     */
+    public ContentHandler asContentHandler() throws IOException
+    {
+        /* don't return the real handler ( m_handler ) because
+         * that would expose the real handler to the outside.
+         * Keep m_handler private so it can be internally swapped
+         * to an HTML handler.
+         */
+        return this;
+    }
+
+    /**
+     * @see SerializationHandler#close()
+     */
+    public void close()
+    {
+        m_handler.close();
+    }
+
+    /**
+     * @see Serializer#getOutputFormat()
+     * @return the properties of the underlying handler
+     */
+    public Properties getOutputFormat()
+    {
+        return m_handler.getOutputFormat();
+    }
+
+    /**
+     * @see Serializer#getOutputStream()
+     * @return the OutputStream of the underlying XML or HTML handler
+     */
+    public OutputStream getOutputStream()
+    {
+        return m_handler.getOutputStream();
+    }
+
+    /**
+     * @see Serializer#getWriter()
+     * @return the Writer of the underlying XML or HTML handler
+     */
+    public Writer getWriter()
+    {
+        return m_handler.getWriter();
+    }
+
+    /**
+     * passes the call on to the underlying HTML or XML handler
+     * @see Serializer#reset()
+     * @return ???
+     */
+    public boolean reset()
+    {
+        return m_handler.reset();
+    }
+
+    /**
+     * Converts the DOM node to output
+     * @param node the DOM node to transform to output
+     * @see DOMSerializer#serialize(Node)
+     *
+     */
+    public void serialize(Node node) throws IOException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+        m_handler.serialize(node);
+    }
+
+    /**
+     * @see SerializationHandler#setEscaping(boolean)
+     */
+    public boolean setEscaping(boolean escape) throws SAXException
+    {
+        return m_handler.setEscaping(escape);
+    }
+
+    /**
+     * Set the properties of the handler
+     * @param format the output properties to set
+     * @see Serializer#setOutputFormat(Properties)
+     */
+    public void setOutputFormat(Properties format)
+    {
+        m_handler.setOutputFormat(format);
+    }
+
+    /**
+     * Sets the output stream to write to
+     * @param output the OutputStream to write to
+     * @see Serializer#setOutputStream(OutputStream)
+     */
+    public void setOutputStream(OutputStream output)
+    {
+        m_handler.setOutputStream(output);
+    }
+
+    /**
+     * Sets the writer to write to
+     * @param writer the writer to write to
+     * @see Serializer#setWriter(Writer)
+     */
+    public void setWriter(Writer writer)
+    {
+        m_handler.setWriter(writer);
+    }
+
+    /**
+     * Adds an attribute to the currenly open tag
+     * @param uri the URI of a namespace
+     * @param localName the attribute name, without prefix
+     * @param rawName the attribute name, with prefix (if any)
+     * @param type the type of the attribute, typically "CDATA"
+     * @param value the value of the parameter
+     * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
+     * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean XSLAttribute)
+        throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+        m_handler.addAttribute(uri, localName, rawName, type, value, XSLAttribute);
+    }
+    /**
+     * Adds an attribute to the currenly open tag
+     * @param rawName the attribute name, with prefix (if any)
+     * @param value the value of the parameter
+     * @see ExtendedContentHandler#addAttribute(String, String)
+     */
+    public void addAttribute(String rawName, String value)
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+        m_handler.addAttribute(rawName, value);
+ 
+    }
+
+    /**
+     * Adds a unique attribute to the currenly open tag
+     */
+    public void addUniqueAttribute(String rawName, String value, int flags)
+        throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+        m_handler.addUniqueAttribute(rawName, value, flags);
+ 
+    }
+
+    /**
+     * Converts the String to a character array and calls the SAX method 
+     * characters(char[],int,int);
+     * 
+     * @see ExtendedContentHandler#characters(String)
+     */
+    public void characters(String chars) throws SAXException
+    {
+        final int length = chars.length();
+        if (length > m_charsBuff.length)
+        {
+            m_charsBuff = new char[length*2 + 1];
+        }
+        chars.getChars(0, length, m_charsBuff, 0);
+        this.characters(m_charsBuff, 0, length);  
+    }    
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see ExtendedContentHandler#endElement(String)
+     */
+    public void endElement(String elementName) throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+        m_handler.endElement(elementName);
+    }
+
+
+    /**
+     * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
+     * @param prefix The prefix that maps to the URI
+     * @param uri The URI for the namespace
+     */
+    public void startPrefixMapping(String prefix, String uri) throws SAXException
+    {
+        this.startPrefixMapping(prefix,uri, true);
+    }
+
+    /**
+     * This method is used when a prefix/uri namespace mapping
+     * is indicated after the element was started with a
+     * startElement() and before and endElement().
+     * startPrefixMapping(prefix,uri) would be used before the
+     * startElement() call.
+     * @param uri the URI of the namespace
+     * @param prefix the prefix associated with the given URI.
+     *
+     * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
+     */    
+    public void namespaceAfterStartElement(String prefix, String uri)
+        throws SAXException 
+    {  
+        // hack for XSLTC with finding URI for default namespace
+        if (m_firstTagNotEmitted && m_firstElementURI == null && m_firstElementName != null)
+        {
+            String prefix1 = getPrefixPart(m_firstElementName);
+            if (prefix1 == null && EMPTYSTRING.equals(prefix))
+            {
+                // the elements URI is not known yet, and it
+                // doesn't have a prefix, and we are currently
+                // setting the uri for prefix "", so we have
+                // the uri for the element... lets remember it
+                m_firstElementURI = uri;
+            }
+        }         
+        startPrefixMapping(prefix,uri, false);          
+    }
+    
+    public boolean startPrefixMapping(String prefix, String uri, boolean shouldFlush)
+        throws SAXException
+    {
+        boolean pushed = false;
+        if (m_firstTagNotEmitted)
+        {
+            if (m_firstElementName != null && shouldFlush)
+            {
+                /* we've already seen a startElement, and this is a prefix mapping
+                 * for the up coming element, so flush the old element
+                 * then send this event on its way.
+                 */
+                flush();
+                pushed = m_handler.startPrefixMapping(prefix, uri, shouldFlush);
+            } 
+            else 
+            {           
+                if (m_namespacePrefix == null)
+                {
+                    m_namespacePrefix = new Vector();
+                    m_namespaceURI = new Vector();
+                }
+                m_namespacePrefix.addElement(prefix);
+                m_namespaceURI.addElement(uri);
+            
+                if (m_firstElementURI == null)
+                {
+                    if (prefix.equals(m_firstElementPrefix))
+                        m_firstElementURI = uri;
+                }
+            }
+
+        }
+        else
+        {
+           pushed = m_handler.startPrefixMapping(prefix, uri, shouldFlush);
+        }
+        return pushed;
+    }
+
+    /**
+      * This method cannot be cached because default is different in
+      * HTML and XML (we need more than a boolean).
+      */
+
+    public void setVersion(String version)
+    {
+        m_handler.setVersion(version);
+
+        // Cache call to setVersion()
+        //       super.setVersion(version);
+        m_setVersion_called = true;
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startDocument()
+     */
+    public void startDocument() throws SAXException
+    {
+        m_needToCallStartDocument = true;
+    }
+
+ 
+    
+    public void startElement(String qName) throws SAXException
+    {
+        this.startElement(null, null, qName, null);
+    }
+    
+    public void startElement(String namespaceURI, String localName, String qName) throws SAXException
+    {
+        this.startElement(namespaceURI, localName, qName, null);
+    }
+
+    public void startElement(
+        String namespaceURI,
+        String localName,
+        String elementName,
+        Attributes atts) throws SAXException
+    {
+        /* we are notified of the start of an element */
+        if (m_firstTagNotEmitted)
+        {
+            /* we have not yet sent the first element on its way */
+            if (m_firstElementName != null) 
+            {
+                /* this is not the first element, but a later one.
+                 * But we have the old element pending, so flush it out,
+                 * then send this one on its way. 
+                 */
+                flush();
+                m_handler.startElement(namespaceURI, localName, elementName,  atts);                
+            }
+            else
+            {
+                /* this is the very first element that we have seen, 
+                 * so save it for flushing later.  We may yet get to know its
+                 * URI due to added attributes.
+                 */
+                 
+                m_wrapped_handler_not_initialized = true;
+                m_firstElementName = elementName;
+                
+                // null if not known
+                m_firstElementPrefix = getPrefixPartUnknown(elementName);
+                
+                // null if not known
+                m_firstElementURI = namespaceURI;
+                
+                // null if not known
+                m_firstElementLocalName = localName;
+
+                if (m_tracer != null)
+                    firePseudoElement(elementName);
+                    
+                /* we don't want to call our own addAttributes, which
+                 * merely delegates to the wrapped handler, but we want to
+                 * add these attributes to m_attributes. So me must call super.
+                 * addAttributes() In this case m_attributes is only used for the
+                 * first element, after that this class totally delegates to the
+                 * wrapped handler which is either XML or HTML.
+                 */
+                if (atts != null)   
+                    super.addAttributes(atts);
+                
+                // if there are attributes, then lets make the flush()
+                // call the startElement on the handler and send the
+                // attributes on their way.
+                if (atts != null)   
+                    flush();
+                
+            }
+        }
+        else
+        {
+            // this is not the first element, but a later one, so just
+            // send it on its way.
+            m_handler.startElement(namespaceURI, localName, elementName,  atts);
+        }
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see ExtendedLexicalHandler#comment(String)
+     */
+    public void comment(String comment) throws SAXException
+    {
+        if (m_firstTagNotEmitted && m_firstElementName != null)
+        {
+            emitFirstTag();
+        }
+        else if (m_needToCallStartDocument)
+        {
+            m_handler.startDocument();
+            m_needToCallStartDocument = false;
+        }
+
+        m_handler.comment(comment);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getDoctypePublic()
+     */
+    public String getDoctypePublic()
+    {
+
+        return m_handler.getDoctypePublic();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getDoctypeSystem()
+     */
+    public String getDoctypeSystem()
+    {
+        return m_handler.getDoctypeSystem();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getEncoding()
+     */
+    public String getEncoding()
+    {
+        return m_handler.getEncoding();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getIndent()
+     */
+    public boolean getIndent()
+    {
+        return m_handler.getIndent();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getIndentAmount()
+     */
+    public int getIndentAmount()
+    {
+        return m_handler.getIndentAmount();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getMediaType()
+     */
+    public String getMediaType()
+    {
+        return m_handler.getMediaType();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getOmitXMLDeclaration()
+     */
+    public boolean getOmitXMLDeclaration()
+    {
+        return m_handler.getOmitXMLDeclaration();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getStandalone()
+     */
+    public String getStandalone()
+    {
+        return m_handler.getStandalone();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#getVersion()
+     */
+    public String getVersion()
+    {
+        return m_handler.getVersion();
+    }
+
+    /**
+     * @see XSLOutputAttributes#setDoctype(String, String)
+     */
+    public void setDoctype(String system, String pub)
+    {
+        m_handler.setDoctypePublic(pub);
+        m_handler.setDoctypeSystem(system);
+    }
+
+    /**
+     * Set the doctype in the underlying XML handler. Remember that this method
+     * was called, just in case we need to transfer this doctype to an HTML handler
+     * @param doctype the public doctype to set
+     * @see XSLOutputAttributes#setDoctypePublic(String)
+     */
+    public void setDoctypePublic(String doctype)
+    {
+        m_handler.setDoctypePublic(doctype);
+        m_setDoctypePublic_called = true;
+    }
+
+    /**
+     * Set the doctype in the underlying XML handler. Remember that this method
+     * was called, just in case we need to transfer this doctype to an HTML handler
+     * @param doctype the system doctype to set
+     * @see XSLOutputAttributes#setDoctypeSystem(String)
+     */
+    public void setDoctypeSystem(String doctype)
+    {
+        m_handler.setDoctypeSystem(doctype);
+        m_setDoctypeSystem_called = true;
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#setEncoding(String)
+     */
+    public void setEncoding(String encoding)
+    {
+        m_handler.setEncoding(encoding);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#setIndent(boolean)
+     */
+    public void setIndent(boolean indent)
+    {
+        m_handler.setIndent(indent);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     */
+    public void setIndentAmount(int value)
+    {
+        m_handler.setIndentAmount(value);
+    }
+
+    /**
+     * @see XSLOutputAttributes#setMediaType(String)
+     */
+    public void setMediaType(String mediaType)
+    {
+        m_handler.setMediaType(mediaType);
+        m_setMediaType_called = true;
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#setOmitXMLDeclaration(boolean)
+     */
+    public void setOmitXMLDeclaration(boolean b)
+    {
+        m_handler.setOmitXMLDeclaration(b);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see XSLOutputAttributes#setStandalone(String)
+     */
+    public void setStandalone(String standalone)
+    {
+        m_handler.setStandalone(standalone);
+    }
+
+    /**
+     * @see XSLOutputAttributes#setVersion(String)
+     */
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String)
+     */
+    public void attributeDecl(
+        String arg0,
+        String arg1,
+        String arg2,
+        String arg3,
+        String arg4)
+        throws SAXException
+    {
+        m_handler.attributeDecl(arg0, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String)
+     */
+    public void elementDecl(String arg0, String arg1) throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            emitFirstTag();
+        }
+        m_handler.elementDecl(arg0, arg1);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String)
+     */
+    public void externalEntityDecl(
+        String name,
+        String publicId,
+        String systemId)
+        throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+        m_handler.externalEntityDecl(name, publicId, systemId);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(String, String)
+     */
+    public void internalEntityDecl(String arg0, String arg1)
+        throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+        m_handler.internalEntityDecl(arg0, arg1);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+     */
+    public void characters(char[] characters, int offset, int length)
+        throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+
+        m_handler.characters(characters, offset, length);
+
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ContentHandler#endDocument()
+     */
+    public void endDocument() throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+
+        m_handler.endDocument();
+        
+    
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ContentHandler#endElement(String, String, String)
+     */
+    public void endElement(String namespaceURI, String localName, String qName)
+        throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+            if (namespaceURI == null && m_firstElementURI != null)
+                namespaceURI = m_firstElementURI;
+
+
+            if (localName == null && m_firstElementLocalName != null)
+                localName = m_firstElementLocalName;
+        }
+        
+        m_handler.endElement(namespaceURI, localName, qName);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
+     */
+    public void endPrefixMapping(String prefix) throws SAXException
+    {
+        m_handler.endPrefixMapping(prefix);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+     */
+    public void ignorableWhitespace(char[] ch, int start, int length)
+        throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+        m_handler.ignorableWhitespace(ch, start, length);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
+     */
+    public void processingInstruction(String target, String data)
+        throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+
+        m_handler.processingInstruction(target, data);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
+     */
+    public void setDocumentLocator(Locator locator)
+    {
+        m_handler.setDocumentLocator(locator);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ContentHandler#skippedEntity(String)
+     */
+    public void skippedEntity(String name) throws SAXException
+    {
+        m_handler.skippedEntity(name);
+    }
+
+
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
+     */
+    public void comment(char[] ch, int start, int length) throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            flush();
+        }
+
+        m_handler.comment(ch, start, length);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.LexicalHandler#endCDATA()
+     */
+    public void endCDATA() throws SAXException
+    {
+
+        m_handler.endCDATA();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.LexicalHandler#endDTD()
+     */
+    public void endDTD() throws SAXException
+    {
+
+        m_handler.endDTD();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.LexicalHandler#endEntity(String)
+     */
+    public void endEntity(String name) throws SAXException
+    {
+        if (m_firstTagNotEmitted)
+        {
+            emitFirstTag();
+        }
+        m_handler.endEntity(name);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.LexicalHandler#startCDATA()
+     */
+    public void startCDATA() throws SAXException
+    {
+        m_handler.startCDATA();
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String)
+     */
+    public void startDTD(String name, String publicId, String systemId)
+        throws SAXException
+    {
+        m_handler.startDTD(name, publicId, systemId);
+    }
+
+    /**
+     * Pass the call on to the underlying handler
+     * @see org.xml.sax.ext.LexicalHandler#startEntity(String)
+     */
+    public void startEntity(String name) throws SAXException
+    {
+        m_handler.startEntity(name);
+    }
+
+    /**
+     * Initialize the wrapped output stream (XML or HTML).
+     * If the stream handler should be HTML, then replace the XML handler with
+     * an HTML handler. After than send the starting method calls that were cached
+     * to the wrapped handler.
+     *
+     */
+    private void initStreamOutput() throws SAXException
+    {
+
+        // Try to rule out if this is an not to be an HTML document based on prefix
+        boolean firstElementIsHTML = isFirstElemHTML();
+
+        if (firstElementIsHTML)
+        {
+            // create an HTML output handler, and initialize it
+
+            // keep a reference to the old handler, ... it will soon be gone
+            SerializationHandler oldHandler = m_handler;
+
+            /* We have to make sure we get an output properties with the proper
+             * defaults for the HTML method.  The easiest way to do this is to
+             * have the OutputProperties class do it.
+             */
+
+            Properties htmlProperties =
+                OutputPropertiesFactory.getDefaultMethodProperties(Method.HTML);
+            Serializer serializer =
+                SerializerFactory.getSerializer(htmlProperties);
+
+            // The factory should be returning a ToStream
+            // Don't know what to do if it doesn't
+            // i.e. the user has over-ridden the content-handler property
+            // for html
+            m_handler = (SerializationHandler) serializer;
+            //m_handler = new ToHTMLStream();
+
+            Writer writer = oldHandler.getWriter();
+
+            if (null != writer)
+                m_handler.setWriter(writer);
+            else
+            {
+                OutputStream os = oldHandler.getOutputStream();
+
+                if (null != os)
+                    m_handler.setOutputStream(os);
+            }
+
+            // need to copy things from the old handler to the new one here
+
+            //            if (_setVersion_called)
+            //            {
+            m_handler.setVersion(oldHandler.getVersion());
+            //            }
+            //            if (_setDoctypeSystem_called)
+            //            {
+            m_handler.setDoctypeSystem(oldHandler.getDoctypeSystem());
+            //            }
+            //            if (_setDoctypePublic_called)
+            //            {
+            m_handler.setDoctypePublic(oldHandler.getDoctypePublic());
+            //            }
+            //            if (_setMediaType_called)
+            //            {
+            m_handler.setMediaType(oldHandler.getMediaType());
+            //            }
+            
+            m_handler.setTransformer(oldHandler.getTransformer());
+        }
+
+        /* Now that we have a real wrapped handler (XML or HTML) lets
+         * pass any cached calls to it
+         */
+        // Call startDocument() if necessary
+        if (m_needToCallStartDocument)
+        {
+            m_handler.startDocument();
+            m_needToCallStartDocument = false;
+        }
+
+        // the wrapped handler is now fully initialized
+        m_wrapped_handler_not_initialized = false;
+    }
+
+    private void emitFirstTag() throws SAXException
+    {   
+        if (m_firstElementName != null)
+        {
+            if (m_wrapped_handler_not_initialized)
+            {
+                initStreamOutput();
+                m_wrapped_handler_not_initialized = false;
+            }
+            // Output first tag
+            m_handler.startElement(m_firstElementURI, null, m_firstElementName, m_attributes);
+            // don't need the collected attributes of the first element anymore.
+            m_attributes = null;
+
+            // Output namespaces of first tag
+            if (m_namespacePrefix != null)
+            {
+                final int n = m_namespacePrefix.size();
+                for (int i = 0; i < n; i++)
+                {
+                    final String prefix =
+                        (String) m_namespacePrefix.elementAt(i);
+                    final String uri = (String) m_namespaceURI.elementAt(i);
+                    m_handler.startPrefixMapping(prefix, uri, false);
+                }
+                m_namespacePrefix = null;
+                m_namespaceURI = null;
+            }
+            m_firstTagNotEmitted = false;
+        }
+    }
+
+    /**
+     * Utility function for calls to local-name().
+     *
+     * Don't want to override static function on SerializerBase
+     * So added Unknown suffix to method name.
+     */
+    private String getLocalNameUnknown(String value)
+    {
+        int idx = value.lastIndexOf(':');
+        if (idx >= 0)
+            value = value.substring(idx + 1);
+        idx = value.lastIndexOf('@');
+        if (idx >= 0)
+            value = value.substring(idx + 1);
+        return (value);
+    }
+
+    /**
+         * Utility function to return prefix
+         *
+         * Don't want to override static function on SerializerBase
+         * So added Unknown suffix to method name.
+         */
+    private String getPrefixPartUnknown(String qname)
+    {
+        final int index = qname.indexOf(':');
+        return (index > 0) ? qname.substring(0, index) : EMPTYSTRING;
+    }
+
+    /**
+     * Determine if the firts element in the document is <html> or <HTML>
+     * This uses the cached first element name, first element prefix and the
+     * cached namespaces from previous method calls
+     *
+     * @return true if the first element is an opening <html> tag
+     */
+    private boolean isFirstElemHTML()
+    {
+        boolean isHTML;
+
+        // is the first tag html, not considering the prefix ?
+        isHTML =
+            getLocalNameUnknown(m_firstElementName).equalsIgnoreCase("html");
+
+        // Try to rule out if this is not to be an HTML document based on URI
+        if (isHTML
+            && m_firstElementURI != null
+            && !EMPTYSTRING.equals(m_firstElementURI))
+        {
+            // the <html> element has a non-trivial namespace
+            isHTML = false;
+        }
+        // Try to rule out if this is an not to be an HTML document based on prefix
+        if (isHTML && m_namespacePrefix != null)
+        {
+            /* the first element has a name of "html", but lets check the prefix.
+             * If the prefix points to a namespace with a URL that is not ""
+             * then the doecument doesn't start with an <html> tag, and isn't html
+             */
+            final int max = m_namespacePrefix.size();
+            for (int i = 0; i < max; i++)
+            {
+                final String prefix = (String) m_namespacePrefix.elementAt(i);
+                final String uri = (String) m_namespaceURI.elementAt(i);
+
+                if (m_firstElementPrefix != null
+                    && m_firstElementPrefix.equals(prefix)
+                    && !EMPTYSTRING.equals(uri))
+                {
+                    // The first element has a prefix, so it can't be <html>
+                    isHTML = false;
+                    break;
+                }
+            }
+
+        }
+        return isHTML;
+    }
+    /**
+     * @see Serializer#asDOMSerializer()
+     */
+    public DOMSerializer asDOMSerializer() throws IOException
+    {
+        return m_handler.asDOMSerializer();
+    }
+
+    /**
+     * @param URI_and_localNames Vector a list of pairs of URI/localName
+     * specified in the cdata-section-elements attribute.
+     * @see SerializationHandler#setCdataSectionElements(java.util.Vector)
+     */
+    public void setCdataSectionElements(Vector URI_and_localNames)
+    {
+        m_handler.setCdataSectionElements(URI_and_localNames);
+    }
+    /**
+     * @see ExtendedContentHandler#addAttributes(org.xml.sax.Attributes)
+     */
+    public void addAttributes(Attributes atts) throws SAXException
+    {
+        m_handler.addAttributes(atts);
+    }
+
+    /**
+     * Get the current namespace mappings.
+     * Simply returns the mappings of the wrapped handler.
+     * @see ExtendedContentHandler#getNamespaceMappings()
+     */
+    public NamespaceMappings getNamespaceMappings()
+    {
+        NamespaceMappings mappings = null;
+        if (m_handler != null)
+        {
+            mappings = m_handler.getNamespaceMappings();
+        }
+        return mappings;
+    }
+    /**
+     * @see SerializationHandler#flushPending()
+     */
+    public void flushPending() throws SAXException
+    {
+ 
+        flush();
+      
+        m_handler.flushPending();
+    }
+    
+    private void flush()
+    {
+        try
+        {
+        if (m_firstTagNotEmitted)
+        {
+            emitFirstTag();
+        }
+        if (m_needToCallStartDocument)
+        {
+            m_handler.startDocument();
+            m_needToCallStartDocument = false;
+        }
+        }
+        catch(SAXException e)
+        {
+            throw new RuntimeException(e.toString());
+        }
+          
+    
+    }
+
+    /**
+     * @see ExtendedContentHandler#getPrefix
+     */
+    public String getPrefix(String namespaceURI)
+    {
+        return m_handler.getPrefix(namespaceURI);
+    }
+    /**
+     * @see ExtendedContentHandler#entityReference(java.lang.String)
+     */
+    public void entityReference(String entityName) throws SAXException
+    {
+        m_handler.entityReference(entityName);
+    }
+
+    /**
+     * @see ExtendedContentHandler#getNamespaceURI(java.lang.String, boolean)
+     */
+    public String getNamespaceURI(String qname, boolean isElement)
+    {
+        return m_handler.getNamespaceURI(qname, isElement);
+    }
+
+    public String getNamespaceURIFromPrefix(String prefix)
+    {
+        return m_handler.getNamespaceURIFromPrefix(prefix);
+    }
+
+    public void setTransformer(Transformer t)
+    {       
+        m_handler.setTransformer(t);
+        if ((t instanceof SerializerTrace) &&
+            (((SerializerTrace) t).hasTraceListeners())) {
+           m_tracer = (SerializerTrace) t;
+        } else {
+           m_tracer = null;
+        }        
+    }
+    public Transformer getTransformer()
+    {
+        return m_handler.getTransformer();
+    }
+
+    /**
+     * @see SerializationHandler#setContentHandler(org.xml.sax.ContentHandler)
+     */
+    public void setContentHandler(ContentHandler ch)
+    {
+        m_handler.setContentHandler(ch);
+    }
+    /**
+     * This method is used to set the source locator, which might be used to
+     * generated an error message.
+     * @param locator the source locator
+     *
+     * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator)
+     */    
+    public void setSourceLocator(SourceLocator locator)
+    {
+        m_handler.setSourceLocator(locator);
+    }
+
+    protected void firePseudoElement(String elementName)
+    {
+        
+        if (m_tracer != null) {
+            StringBuffer sb = new StringBuffer();
+                
+            sb.append('<');
+            sb.append(elementName);
+            
+            // convert the StringBuffer to a char array and
+            // emit the trace event that these characters "might"
+            // be written
+            char ch[] = sb.toString().toCharArray();
+            m_tracer.fireGenerateEvent(
+                SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
+                ch,
+                0,
+                ch.length);
+        }
+    }
+
+    /**
+     * @see org.apache.xml.serializer.Serializer#asDOM3Serializer()
+     */
+    public Object asDOM3Serializer() throws IOException
+    {
+        return m_handler.asDOM3Serializer();
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/ToXMLSAXHandler.java b/src/main/java/org/apache/xml/serializer/ToXMLSAXHandler.java
new file mode 100644
index 0000000..edb1e35
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ToXMLSAXHandler.java
@@ -0,0 +1,774 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ToXMLSAXHandler.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+ package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Properties;
+
+import javax.xml.transform.Result;
+
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * This class receives notification of SAX-like events, and with gathered
+ * information over these calls it will invoke the equivalent SAX methods
+ * on a handler, the ultimate xsl:output method is known to be "xml".
+ * 
+ * This class is not a public API.
+ * @xsl.usage internal
+ */
+public final class ToXMLSAXHandler extends ToSAXHandler
+{
+
+    /**
+     * Keeps track of whether output escaping is currently enabled
+     */
+    protected boolean m_escapeSetting = true;
+
+    public ToXMLSAXHandler()
+    {
+        // default constructor (need to set content handler ASAP !)
+        m_prefixMap = new NamespaceMappings();
+        initCDATA();
+    }
+
+    /**
+     * @see Serializer#getOutputFormat()
+     */
+    public Properties getOutputFormat()
+    {
+        return null;
+    }
+
+    /**
+     * @see Serializer#getOutputStream()
+     */
+    public OutputStream getOutputStream()
+    {
+        return null;
+    }
+
+    /**
+     * @see Serializer#getWriter()
+     */
+    public Writer getWriter()
+    {
+        return null;
+    }
+
+    /**
+     * Do nothing for SAX.
+     */
+    public void indent(int n) throws SAXException
+    {
+    }
+
+
+    /**
+     * @see DOMSerializer#serialize(Node)
+     */
+    public void serialize(Node node) throws IOException
+    {
+    }
+
+    /**
+     * @see SerializationHandler#setEscaping(boolean)
+     */
+    public boolean setEscaping(boolean escape) throws SAXException
+    {
+        boolean oldEscapeSetting = m_escapeSetting;
+        m_escapeSetting = escape;
+
+        if (escape) {
+            processingInstruction(Result.PI_ENABLE_OUTPUT_ESCAPING, "");
+        } else {
+            processingInstruction(Result.PI_DISABLE_OUTPUT_ESCAPING, "");
+        }
+
+        return oldEscapeSetting;
+    }
+
+    /**
+     * @see Serializer#setOutputFormat(Properties)
+     */
+    public void setOutputFormat(Properties format)
+    {
+    }
+
+    /**
+     * @see Serializer#setOutputStream(OutputStream)
+     */
+    public void setOutputStream(OutputStream output)
+    {
+    }
+
+    /**
+     * @see Serializer#setWriter(Writer)
+     */
+    public void setWriter(Writer writer)
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String)
+     */
+    public void attributeDecl(
+        String arg0,
+        String arg1,
+        String arg2,
+        String arg3,
+        String arg4)
+        throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String)
+     */
+    public void elementDecl(String arg0, String arg1) throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String)
+     */
+    public void externalEntityDecl(String arg0, String arg1, String arg2)
+        throws SAXException
+    {
+    }
+
+    /**
+     * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(String, String)
+     */
+    public void internalEntityDecl(String arg0, String arg1)
+        throws SAXException
+    {
+    }
+
+    /**
+     * Receives notification of the end of the document.
+     * @see org.xml.sax.ContentHandler#endDocument()
+     */
+    public void endDocument() throws SAXException
+    {
+
+        flushPending();
+
+        // Close output document
+        m_saxHandler.endDocument();
+
+        if (m_tracer != null)
+            super.fireEndDoc();
+    }
+
+    /**
+     * This method is called when all the data needed for a call to the
+     * SAX handler's startElement() method has been gathered.
+     */
+    protected void closeStartTag() throws SAXException
+    {
+
+        m_elemContext.m_startTagOpen = false;
+
+        final String localName = getLocalName(m_elemContext.m_elementName);
+        final String uri = getNamespaceURI(m_elemContext.m_elementName, true);
+
+        // Now is time to send the startElement event
+        if (m_needToCallStartDocument)
+        {
+            startDocumentInternal();
+        }
+        m_saxHandler.startElement(uri, localName, m_elemContext.m_elementName, m_attributes);
+        // we've sent the official SAX attributes on their way,
+        // now we don't need them anymore.
+        m_attributes.clear();
+
+        if(m_state != null)
+          m_state.setCurrentNode(null);
+    }
+
+    /**
+     * Closes ane open cdata tag, and
+     * unlike the this.endCDATA() method (from the LexicalHandler) interface,
+     * this "internal" method will send the endCDATA() call to the wrapped
+     * handler.
+     * 
+     */
+    public void closeCDATA() throws SAXException
+    {
+
+        // Output closing bracket - "]]>"
+        if (m_lexHandler != null && m_cdataTagOpen) {
+            m_lexHandler.endCDATA();
+        }
+        
+
+        // There are no longer any calls made to 
+        // m_lexHandler.startCDATA() without a balancing call to
+        // m_lexHandler.endCDATA()
+        // so we set m_cdataTagOpen to false to remember this.
+        m_cdataTagOpen = false;        
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endElement(String, String, String)
+     */
+    public void endElement(String namespaceURI, String localName, String qName)
+        throws SAXException
+    {
+        // Close any open elements etc.
+        flushPending();
+        
+        if (namespaceURI == null)
+        {
+            if (m_elemContext.m_elementURI != null)
+                namespaceURI = m_elemContext.m_elementURI;
+            else
+                namespaceURI = getNamespaceURI(qName, true);
+        }
+        
+        if (localName == null)
+        {
+            if (m_elemContext.m_elementLocalName != null)
+                localName = m_elemContext.m_elementLocalName;
+            else
+                localName = getLocalName(qName);
+        }
+
+        m_saxHandler.endElement(namespaceURI, localName, qName);
+
+        if (m_tracer != null)
+            super.fireEndElem(qName);       
+
+        /* Pop all namespaces at the current element depth.
+         * We are not waiting for official endPrefixMapping() calls.
+         */
+        m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth,
+            m_saxHandler);
+        m_elemContext = m_elemContext.m_prev;
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
+     */
+    public void endPrefixMapping(String prefix) throws SAXException
+    {
+        /* poping all prefix mappings should have been done
+         * in endElement() already
+         */
+         return;
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+     */
+    public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
+        throws SAXException
+    {
+        m_saxHandler.ignorableWhitespace(arg0,arg1,arg2);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
+     */
+    public void setDocumentLocator(Locator arg0)
+    {
+        m_saxHandler.setDocumentLocator(arg0);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#skippedEntity(String)
+     */
+    public void skippedEntity(String arg0) throws SAXException
+    {
+        m_saxHandler.skippedEntity(arg0);
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
+     * @param prefix The prefix that maps to the URI
+     * @param uri The URI for the namespace
+     */
+    public void startPrefixMapping(String prefix, String uri)
+        throws SAXException
+    {
+       startPrefixMapping(prefix, uri, true);
+    }
+
+    /**
+     * Remember the prefix/uri mapping at the current nested element depth.
+     *
+     * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
+     * @param prefix The prefix that maps to the URI
+     * @param uri The URI for the namespace
+     * @param shouldFlush a flag indicating if the mapping applies to the
+     * current element or an up coming child (not used).
+     */
+
+    public boolean startPrefixMapping(
+        String prefix,
+        String uri,
+        boolean shouldFlush)
+        throws org.xml.sax.SAXException
+    {
+
+        /* Remember the mapping, and at what depth it was declared
+         * This is one greater than the current depth because these
+         * mappings will apply to the next depth. This is in
+         * consideration that startElement() will soon be called
+         */
+
+        boolean pushed;
+        int pushDepth;
+        if (shouldFlush)
+        {
+            flushPending();
+            // the prefix mapping applies to the child element (one deeper)
+            pushDepth = m_elemContext.m_currentElemDepth + 1;
+        }
+        else
+        {
+            // the prefix mapping applies to the current element
+            pushDepth = m_elemContext.m_currentElemDepth;
+        }
+        pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
+
+        if (pushed)
+        {
+            m_saxHandler.startPrefixMapping(prefix,uri);
+            
+            if (getShouldOutputNSAttr()) 
+            {
+
+	              /* I don't know if we really needto do this. The
+	               * callers of this object should have injected both
+	               * startPrefixMapping and the attributes.  We are
+	               * just covering our butt here.
+	               */
+	              String name;
+  	            if (EMPTYSTRING.equals(prefix))
+  	            {
+  	                name = "xmlns";
+  	                addAttributeAlways(XMLNS_URI, name, name,"CDATA",uri, false);
+  	            }
+  	            else 
+                {
+  	                if (!EMPTYSTRING.equals(uri)) // hack for attribset16 test
+  	                {                             // that maps ns1 prefix to "" URI 
+  	                    name = "xmlns:" + prefix;
+  	
+  	                    /* for something like xmlns:abc="w3.pretend.org"
+  	             	 	     *  the uri is the value, that is why we pass it in the
+  	             	 	     * value, or 5th slot of addAttributeAlways()
+  	                 	   */
+  	                    addAttributeAlways(XMLNS_URI, prefix, name,"CDATA",uri, false );
+  	                }
+  	            }
+            }
+        }
+        return pushed;
+    }
+        
+
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
+     */
+    public void comment(char[] arg0, int arg1, int arg2) throws SAXException
+    {
+        flushPending();
+        if (m_lexHandler != null)
+            m_lexHandler.comment(arg0, arg1, arg2);
+            
+        if (m_tracer != null)            
+            super.fireCommentEvent(arg0, arg1, arg2);
+    }
+
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#endCDATA()
+     */
+    public void endCDATA() throws SAXException
+    {
+        /* Normally we would do somthing with this but we ignore it.
+         * The neccessary call to m_lexHandler.endCDATA() will be made
+         * in flushPending().
+         * 
+         * This is so that if we get calls like these:
+         *   this.startCDATA();
+         *   this.characters(chars1, off1, len1);
+         *   this.endCDATA();
+         *   this.startCDATA();
+         *   this.characters(chars2, off2, len2);
+         *   this.endCDATA();
+         * 
+         * that we will only make these calls to the wrapped handlers:
+         * 
+         *   m_lexHandler.startCDATA();
+         *   m_saxHandler.characters(chars1, off1, len1);
+         *   m_saxHandler.characters(chars1, off2, len2);
+         *   m_lexHandler.endCDATA();
+         * 
+         * We will merge adjacent CDATA blocks.
+         */ 
+    }
+
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#endDTD()
+     */
+    public void endDTD() throws SAXException
+    {
+        if (m_lexHandler != null)
+            m_lexHandler.endDTD();
+    }
+
+    /**
+     * @see org.xml.sax.ext.LexicalHandler#startEntity(String)
+     */
+    public void startEntity(String arg0) throws SAXException
+    {
+        if (m_lexHandler != null)
+            m_lexHandler.startEntity(arg0);
+    }
+
+    /**
+     * @see ExtendedContentHandler#characters(String)
+     */
+    public void characters(String chars) throws SAXException
+    {
+        final int length = chars.length();
+        if (length > m_charsBuff.length)
+        {
+            m_charsBuff = new char[length*2 + 1];
+        }
+        chars.getChars(0, length, m_charsBuff, 0);
+        this.characters(m_charsBuff, 0, length); 
+    }
+
+    public ToXMLSAXHandler(ContentHandler handler, String encoding)
+    {
+        super(handler, encoding);
+
+        initCDATA();
+        // initNamespaces();
+        m_prefixMap = new NamespaceMappings();
+    }
+
+    public ToXMLSAXHandler(
+        ContentHandler handler,
+        LexicalHandler lex,
+        String encoding)
+    {
+        super(handler, lex, encoding);
+
+        initCDATA();
+        //      initNamespaces();
+        m_prefixMap = new NamespaceMappings();
+    }
+
+    /**
+     * Start an element in the output document. This might be an XML element
+     * (<elem>data</elem> type) or a CDATA section.
+     */
+    public void startElement(
+    String elementNamespaceURI,
+    String elementLocalName,
+    String elementName) throws SAXException
+    {
+        startElement(
+            elementNamespaceURI,elementLocalName,elementName, null);
+
+
+    }
+    public void startElement(String elementName) throws SAXException
+    {
+        startElement(null, null, elementName, null);
+    }
+
+
+    public void characters(char[] ch, int off, int len) throws SAXException
+    {
+        // We do the first two things in flushPending() but we don't
+        // close any open CDATA calls.        
+        if (m_needToCallStartDocument)
+        {
+            startDocumentInternal();
+            m_needToCallStartDocument = false;
+        }
+
+        if (m_elemContext.m_startTagOpen)
+        {
+            closeStartTag();
+            m_elemContext.m_startTagOpen = false;
+        }
+
+        if (m_elemContext.m_isCdataSection && !m_cdataTagOpen
+        && m_lexHandler != null) 
+        {
+            m_lexHandler.startCDATA();
+            // We have made a call to m_lexHandler.startCDATA() with
+            // no balancing call to m_lexHandler.endCDATA()
+            // so we set m_cdataTagOpen true to remember this.
+            m_cdataTagOpen = true;
+        }
+        
+        /* If there are any occurances of "]]>" in the character data
+         * let m_saxHandler worry about it, we've already warned them with
+         * the previous call of m_lexHandler.startCDATA();
+         */ 
+        m_saxHandler.characters(ch, off, len);
+
+        // time to generate characters event
+        if (m_tracer != null)
+            fireCharEvent(ch, off, len);
+    }
+    
+
+    /**
+     * @see ExtendedContentHandler#endElement(String)
+     */
+    public void endElement(String elemName) throws SAXException
+    {
+        endElement(null, null, elemName);
+    }    
+
+
+    /**
+     * Send a namespace declaration in the output document. The namespace
+     * declaration will not be include if the namespace is already in scope
+     * with the same prefix.
+     */
+    public void namespaceAfterStartElement(
+        final String prefix,
+        final String uri)
+        throws SAXException
+    {
+        startPrefixMapping(prefix,uri,false);
+    }
+
+    /**
+     *
+     * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
+     * Send a processing instruction to the output document
+     */
+    public void processingInstruction(String target, String data)
+        throws SAXException
+    {
+        flushPending();
+
+        // Pass the processing instruction to the SAX handler
+        m_saxHandler.processingInstruction(target, data);
+
+        // we don't want to leave serializer to fire off this event,
+        // so do it here.
+        if (m_tracer != null)
+            super.fireEscapingEvent(target, data);
+    }
+
+    /**
+     * Undeclare the namespace that is currently pointed to by a given
+     * prefix. Inform SAX handler if prefix was previously mapped.
+     */
+    protected boolean popNamespace(String prefix)
+    {
+        try
+        {
+            if (m_prefixMap.popNamespace(prefix))
+            {
+                m_saxHandler.endPrefixMapping(prefix);
+                return true;
+            }
+        }
+        catch (SAXException e)
+        {
+            // falls through
+        }
+        return false;
+    }
+
+    public void startCDATA() throws SAXException
+    {
+        /* m_cdataTagOpen can only be true here if we have ignored the
+         * previous call to this.endCDATA() and the previous call 
+         * this.startCDATA() before that is still "open". In this way
+         * we merge adjacent CDATA. If anything else happened after the 
+         * ignored call to this.endCDATA() and this call then a call to 
+         * flushPending() would have been made which would have
+         * closed the CDATA and set m_cdataTagOpen to false.
+         */
+        if (!m_cdataTagOpen ) 
+        {
+            flushPending();
+            if (m_lexHandler != null) {
+                m_lexHandler.startCDATA();
+
+                // We have made a call to m_lexHandler.startCDATA() with
+                // no balancing call to m_lexHandler.endCDATA()
+                // so we set m_cdataTagOpen true to remember this.                
+                m_cdataTagOpen = true;     
+            }              
+        }        
+    }
+
+    /**
+     * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
+     */
+    public void startElement(
+    String namespaceURI,
+    String localName,
+    String name,
+    Attributes atts)
+        throws SAXException
+    {
+        flushPending();
+        super.startElement(namespaceURI, localName, name, atts);
+
+        // Handle document type declaration (for first element only)
+         if (m_needToOutputDocTypeDecl)
+         {
+             String doctypeSystem = getDoctypeSystem();
+             if (doctypeSystem != null && m_lexHandler != null)
+             {
+                 String doctypePublic = getDoctypePublic();
+                 if (doctypeSystem != null)
+                     m_lexHandler.startDTD(
+                         name,
+                         doctypePublic,
+                         doctypeSystem);
+             }
+             m_needToOutputDocTypeDecl = false;
+         }
+        m_elemContext = m_elemContext.push(namespaceURI, localName, name);
+
+        // ensurePrefixIsDeclared depends on the current depth, so
+        // the previous increment is necessary where it is.
+        if (namespaceURI != null)
+            ensurePrefixIsDeclared(namespaceURI, name);
+
+        // add the attributes to the collected ones
+        if (atts != null)
+            addAttributes(atts);
+
+         
+        // do we really need this CDATA section state?
+        m_elemContext.m_isCdataSection = isCdataSection();
+   
+    }
+ 
+    private void ensurePrefixIsDeclared(String ns, String rawName)
+        throws org.xml.sax.SAXException
+    {
+
+        if (ns != null && ns.length() > 0)
+        {
+            int index;
+            final boolean no_prefix = ((index = rawName.indexOf(":")) < 0);
+            String prefix = (no_prefix) ? "" : rawName.substring(0, index);
+
+
+            if (null != prefix)
+            {
+                String foundURI = m_prefixMap.lookupNamespace(prefix);
+
+                if ((null == foundURI) || !foundURI.equals(ns))
+                {
+                    this.startPrefixMapping(prefix, ns, false);
+
+                    if (getShouldOutputNSAttr()) {
+                        // Bugzilla1133: Generate attribute as well as namespace event.
+                        // SAX does expect both.
+                        this.addAttributeAlways(
+                            "http://www.w3.org/2000/xmlns/",
+                            no_prefix ? "xmlns" : prefix,  // local name
+                            no_prefix ? "xmlns" : ("xmlns:"+ prefix), // qname
+                            "CDATA",
+                            ns,
+                            false);
+                    }
+                }
+
+            }
+        }
+    }
+    /**
+     * Adds the given attribute to the set of attributes, and also makes sure
+     * that the needed prefix/uri mapping is declared, but only if there is a
+     * currently open element.
+     * 
+     * @param uri the URI of the attribute
+     * @param localName the local name of the attribute
+     * @param rawName    the qualified name of the attribute
+     * @param type the type of the attribute (probably CDATA)
+     * @param value the value of the attribute
+     * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
+     * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean XSLAttribute)
+        throws SAXException
+    {      
+        if (m_elemContext.m_startTagOpen)
+        {
+            ensurePrefixIsDeclared(uri, rawName);
+            addAttributeAlways(uri, localName, rawName, type, value, false);
+        }
+
+    } 
+       
+    /**
+     * Try's to reset the super class and reset this class for 
+     * re-use, so that you don't need to create a new serializer 
+     * (mostly for performance reasons).
+     * 
+     * @return true if the class was successfuly reset.
+     * @see Serializer#reset()
+     */
+    public boolean reset()
+    {
+        boolean wasReset = false;
+        if (super.reset())
+        {
+            resetToXMLSAXHandler();
+            wasReset = true;
+        }
+        return wasReset;
+    }
+    
+    /**
+     * Reset all of the fields owned by ToXMLSAXHandler class
+     *
+     */
+    private void resetToXMLSAXHandler()
+    {
+        this.m_escapeSetting = true;
+    }  
+
+}
diff --git a/src/main/java/org/apache/xml/serializer/ToXMLStream.java b/src/main/java/org/apache/xml/serializer/ToXMLStream.java
new file mode 100644
index 0000000..d962189
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/ToXMLStream.java
@@ -0,0 +1,646 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ToXMLStream.java 469359 2006-10-31 03:43:19Z minchau $
+ */
+ package org.apache.xml.serializer;
+
+import java.io.IOException;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.serializer.utils.MsgKey;
+import org.apache.xml.serializer.utils.Utils;
+import org.xml.sax.SAXException;
+
+/**
+ * This class converts SAX or SAX-like calls to a 
+ * serialized xml document.  The xsl:output method is "xml".
+ * 
+ * This class is used explicitly in code generated by XSLTC, 
+ * so it is "public", but it should 
+ * be viewed as internal or package private, this is not an API.
+ * 
+ * @xsl.usage internal
+ */
+public class ToXMLStream extends ToStream
+{
+    /**
+     * Map that tells which XML characters should have special treatment, and it
+     *  provides character to entity name lookup.
+     */
+    private CharInfo m_xmlcharInfo =
+        CharInfo.getCharInfo(CharInfo.XML_ENTITIES_RESOURCE, Method.XML);
+
+    /**
+     * Default constructor.
+     */
+    public ToXMLStream()
+    {
+        m_charInfo = m_xmlcharInfo;
+
+        initCDATA();
+        // initialize namespaces
+        m_prefixMap = new NamespaceMappings();
+
+    }
+
+    /**
+     * Copy properties from another SerializerToXML.
+     *
+     * @param xmlListener non-null reference to a SerializerToXML object.
+     */
+    public void CopyFrom(ToXMLStream xmlListener)
+    {
+
+        setWriter(xmlListener.m_writer);
+
+
+        // m_outputStream = xmlListener.m_outputStream;
+        String encoding = xmlListener.getEncoding();
+        setEncoding(encoding);
+
+        setOmitXMLDeclaration(xmlListener.getOmitXMLDeclaration());
+
+        m_ispreserve = xmlListener.m_ispreserve;
+        m_preserves = xmlListener.m_preserves;
+        m_isprevtext = xmlListener.m_isprevtext;
+        m_doIndent = xmlListener.m_doIndent;
+        setIndentAmount(xmlListener.getIndentAmount());
+        m_startNewLine = xmlListener.m_startNewLine;
+        m_needToOutputDocTypeDecl = xmlListener.m_needToOutputDocTypeDecl;
+        setDoctypeSystem(xmlListener.getDoctypeSystem());
+        setDoctypePublic(xmlListener.getDoctypePublic());        
+        setStandalone(xmlListener.getStandalone());
+        setMediaType(xmlListener.getMediaType());
+        m_encodingInfo = xmlListener.m_encodingInfo;
+        m_spaceBeforeClose = xmlListener.m_spaceBeforeClose;
+        m_cdataStartCalled = xmlListener.m_cdataStartCalled;
+
+    }
+
+    /**
+     * Receive notification of the beginning of a document.
+     *
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void startDocumentInternal() throws org.xml.sax.SAXException
+    {
+
+        if (m_needToCallStartDocument)
+        { 
+            super.startDocumentInternal();
+            m_needToCallStartDocument = false;
+
+            if (m_inEntityRef)
+                return;
+
+            m_needToOutputDocTypeDecl = true;
+            m_startNewLine = false;
+            /* The call to getXMLVersion() might emit an error message
+             * and we should emit this message regardless of if we are 
+             * writing out an XML header or not.
+             */ 
+            final String version = getXMLVersion();
+            if (getOmitXMLDeclaration() == false)
+            {
+                String encoding = Encodings.getMimeEncoding(getEncoding());
+                String standalone;
+
+                if (m_standaloneWasSpecified)
+                {
+                    standalone = " standalone=\"" + getStandalone() + "\"";
+                }
+                else
+                {
+                    standalone = "";
+                }
+
+                try
+                {
+                    final java.io.Writer writer = m_writer;
+                    writer.write("<?xml version=\"");
+                    writer.write(version);
+                    writer.write("\" encoding=\"");
+                    writer.write(encoding);
+                    writer.write('\"');
+                    writer.write(standalone);
+                    writer.write("?>");
+                    if (m_doIndent) {
+                        if (m_standaloneWasSpecified
+                                || getDoctypePublic() != null
+                                || getDoctypeSystem() != null) {
+                            // We almost never put a newline after the XML
+                            // header because this XML could be used as
+                            // an extenal general parsed entity
+                            // and we don't know the context into which it
+                            // will be used in the future.  Only when
+                            // standalone, or a doctype system or public is
+                            // specified are we free to insert a new line
+                            // after the header.  Is it even worth bothering
+                            // in these rare cases?                           
+                            writer.write(m_lineSep, 0, m_lineSepLen);
+                        }
+                    }
+                } 
+                catch(IOException e)
+                {
+                    throw new SAXException(e);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Receive notification of the end of a document.
+     *
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void endDocument() throws org.xml.sax.SAXException
+    {
+        flushPending();
+        if (m_doIndent && !m_isprevtext)
+        {
+            try
+            {
+            outputLineSep();
+            }
+            catch(IOException e)
+            {
+                throw new SAXException(e);
+            }
+        }
+
+        flushWriter();
+        
+        if (m_tracer != null)
+            super.fireEndDoc();
+    }
+
+    /**
+     * Starts a whitespace preserving section. All characters printed
+     * within a preserving section are printed without indentation and
+     * without consolidating multiple spaces. This is equivalent to
+     * the <tt>xml:space=&quot;preserve&quot;</tt> attribute. Only XML
+     * and HTML serializers need to support this method.
+     * <p>
+     * The contents of the whitespace preserving section will be delivered
+     * through the regular <tt>characters</tt> event.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void startPreserving() throws org.xml.sax.SAXException
+    {
+
+        // Not sure this is really what we want.  -sb
+        m_preserves.push(true);
+
+        m_ispreserve = true;
+    }
+
+    /**
+     * Ends a whitespace preserving section.
+     *
+     * @see #startPreserving
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void endPreserving() throws org.xml.sax.SAXException
+    {
+
+        // Not sure this is really what we want.  -sb
+        m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
+    }
+
+    /**
+     * Receive notification of a processing instruction.
+     *
+     * @param target The processing instruction target.
+     * @param data The processing instruction data, or null if
+     *        none was supplied.
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void processingInstruction(String target, String data)
+        throws org.xml.sax.SAXException
+    {
+        if (m_inEntityRef)
+            return;
+        
+        flushPending();   
+
+        if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING))
+        {
+            startNonEscaping();
+        }
+        else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING))
+        {
+            endNonEscaping();
+        }
+        else
+        {
+            try
+            {
+                if (m_elemContext.m_startTagOpen)
+                {
+                    closeStartTag();
+                    m_elemContext.m_startTagOpen = false;
+                }
+                else if (m_needToCallStartDocument)
+                    startDocumentInternal();                
+
+                if (shouldIndent())
+                    indent();
+
+                final java.io.Writer writer = m_writer;
+                writer.write("<?");
+                writer.write(target);
+
+                if (data.length() > 0
+                    && !Character.isSpaceChar(data.charAt(0)))
+                    writer.write(' ');
+
+                int indexOfQLT = data.indexOf("?>");
+
+                if (indexOfQLT >= 0)
+                {
+
+                    // See XSLT spec on error recovery of "?>" in PIs.
+                    if (indexOfQLT > 0)
+                    {
+                        writer.write(data.substring(0, indexOfQLT));
+                    }
+
+                    writer.write("? >"); // add space between.
+
+                    if ((indexOfQLT + 2) < data.length())
+                    {
+                        writer.write(data.substring(indexOfQLT + 2));
+                    }
+                }
+                else
+                {
+                    writer.write(data);
+                }
+
+                writer.write('?');
+                writer.write('>');
+                
+                /*
+                 * Don't write out any indentation whitespace now,
+                 * because there may be non-whitespace text after this.
+                 * 
+                 * Simply mark that at this point if we do decide
+                 * to indent that we should 
+                 * add a newline on the end of the current line before
+                 * the indentation at the start of the next line.
+                 */ 
+                m_startNewLine = true;
+            }
+            catch(IOException e)
+            {
+                throw new SAXException(e);
+            }
+        }
+        
+        if (m_tracer != null)
+            super.fireEscapingEvent(target, data);  
+    }
+
+    /**
+     * Receive notivication of a entityReference.
+     *
+     * @param name The name of the entity.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void entityReference(String name) throws org.xml.sax.SAXException
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+            closeStartTag();
+            m_elemContext.m_startTagOpen = false;
+        }
+
+        try
+        {
+            if (shouldIndent())
+                indent();
+
+            final java.io.Writer writer = m_writer;
+            writer.write('&');
+            writer.write(name);
+            writer.write(';');
+        }
+        catch(IOException e)
+        {
+            throw new SAXException(e);
+        }
+        
+        if (m_tracer != null)
+            super.fireEntityReference(name);            
+    }
+
+    /**
+     * This method is used to add an attribute to the currently open element. 
+     * The caller has guaranted that this attribute is unique, which means that it
+     * not been seen before and will not be seen again.
+     * 
+     * @param name the qualified name of the attribute
+     * @param value the value of the attribute which can contain only
+     * ASCII printable characters characters in the range 32 to 127 inclusive.
+     * @param flags the bit values of this integer give optimization information.
+     */
+    public void addUniqueAttribute(String name, String value, int flags)
+        throws SAXException
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+           
+            try
+            {
+                final String patchedName = patchName(name);
+                final java.io.Writer writer = m_writer;
+                if ((flags & NO_BAD_CHARS) > 0 && m_xmlcharInfo.onlyQuotAmpLtGt)
+                {
+                    // "flags" has indicated that the characters
+                    // '>'  '<'   '&'  and '"' are not in the value and
+                    // m_htmlcharInfo has recorded that there are no other
+                    // entities in the range 32 to 127 so we write out the
+                    // value directly
+                    
+                    writer.write(' ');
+                    writer.write(patchedName);
+                    writer.write("=\"");
+                    writer.write(value);
+                    writer.write('"');
+                }
+                else
+                {
+                    writer.write(' ');
+                    writer.write(patchedName);
+                    writer.write("=\"");
+                    writeAttrString(writer, value, this.getEncoding());
+                    writer.write('"');
+                }
+            } catch (IOException e) {
+                throw new SAXException(e);
+            }
+        }
+    }
+
+    /**
+     * Add an attribute to the current element.
+     * @param uri the URI associated with the element name
+     * @param localName local part of the attribute name
+     * @param rawName   prefix:localName
+     * @param type
+     * @param value the value of the attribute
+     * @param xslAttribute true if this attribute is from an xsl:attribute,
+     * false if declared within the elements opening tag.
+     * @throws SAXException
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean xslAttribute)
+        throws SAXException
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+            boolean was_added = addAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
+            
+
+            /*
+             * We don't run this block of code if:
+             * 1. The attribute value was only replaced (was_added is false).
+             * 2. The attribute is from an xsl:attribute element (that is handled
+             *    in the addAttributeAlways() call just above.
+             * 3. The name starts with "xmlns", i.e. it is a namespace declaration.
+             */
+            if (was_added && !xslAttribute && !rawName.startsWith("xmlns"))
+            {
+                String prefixUsed =
+                    ensureAttributesNamespaceIsDeclared(
+                        uri,
+                        localName,
+                        rawName);
+                if (prefixUsed != null
+                    && rawName != null
+                    && !rawName.startsWith(prefixUsed))
+                {
+                    // use a different raw name, with the prefix used in the
+                    // generated namespace declaration
+                    rawName = prefixUsed + ":" + localName;
+
+                }
+            }
+            addAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
+        }
+        else
+        {
+            /*
+             * The startTag is closed, yet we are adding an attribute?
+             *
+             * Section: 7.1.3 Creating Attributes Adding an attribute to an
+             * element after a PI (for example) has been added to it is an
+             * error. The attributes can be ignored. The spec doesn't explicitly
+             * say this is disallowed, as it does for child elements, but it
+             * makes sense to have the same treatment.
+             *
+             * We choose to ignore the attribute which is added too late.
+             */
+            // Generate a warning of the ignored attributes
+
+            // Create the warning message
+            String msg = Utils.messages.createMessage(
+                    MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,new Object[]{ localName });
+
+            try {
+                // Prepare to issue the warning message
+                Transformer tran = super.getTransformer();
+                ErrorListener errHandler = tran.getErrorListener();
+
+
+                // Issue the warning message
+                if (null != errHandler && m_sourceLocator != null)
+                  errHandler.warning(new TransformerException(msg, m_sourceLocator));
+                else
+                  System.out.println(msg);
+                }
+            catch (TransformerException e){
+                // A user defined error handler, errHandler, may throw
+                // a TransformerException if it chooses to, and if it does
+                // we will wrap it with a SAXException and re-throw.
+                // Of course if the handler throws another type of
+                // exception, like a RuntimeException, then that is OK too.
+                SAXException se = new SAXException(e);
+                throw se;                
+            }             
+        }
+    }
+
+    /**
+     * @see ExtendedContentHandler#endElement(String)
+     */
+    public void endElement(String elemName) throws SAXException
+    {
+        endElement(null, null, elemName);
+    }
+
+    /**
+     * This method is used to notify the serializer of a namespace mapping (or node)
+     * that applies to the current element whose startElement() call has already been seen.
+     * The official SAX startPrefixMapping(prefix,uri) is to define a mapping for a child
+     * element that is soon to be seen with a startElement() call. The official SAX call 
+     * does not apply to the current element, hence the reason for this method.
+     */
+    public void namespaceAfterStartElement(
+        final String prefix,
+        final String uri)
+        throws SAXException
+    {
+
+        // hack for XSLTC with finding URI for default namespace
+        if (m_elemContext.m_elementURI == null)
+        {
+            String prefix1 = getPrefixPart(m_elemContext.m_elementName);
+            if (prefix1 == null && EMPTYSTRING.equals(prefix))
+            {
+                // the elements URI is not known yet, and it
+                // doesn't have a prefix, and we are currently
+                // setting the uri for prefix "", so we have
+                // the uri for the element... lets remember it
+                m_elemContext.m_elementURI = uri;
+            }
+        }            
+        startPrefixMapping(prefix,uri,false);
+        return;
+
+    }
+
+    /**
+     * From XSLTC
+     * Declare a prefix to point to a namespace URI. Inform SAX handler
+     * if this is a new prefix mapping.
+     */
+    protected boolean pushNamespace(String prefix, String uri)
+    {
+        try
+        {
+            if (m_prefixMap.pushNamespace(
+                prefix, uri, m_elemContext.m_currentElemDepth))
+            {
+                startPrefixMapping(prefix, uri);
+                return true;
+            }
+        }
+        catch (SAXException e)
+        {
+            // falls through
+        }
+        return false;
+    }
+    /**
+     * Try's to reset the super class and reset this class for 
+     * re-use, so that you don't need to create a new serializer 
+     * (mostly for performance reasons).
+     * 
+     * @return true if the class was successfuly reset.
+     */
+    public boolean reset()
+    {
+        boolean wasReset = false;
+        if (super.reset())
+        {
+            // Make this call when resetToXMLStream does
+            // something.
+            // resetToXMLStream();
+            wasReset = true;
+        }
+        return wasReset;
+    }
+    
+    /**
+     * Reset all of the fields owned by ToStream class
+     *
+     */
+    private void resetToXMLStream()
+    {
+        // This is an empty method, but is kept for future use
+        // as a place holder for a location to reset fields
+        // defined within this class
+        return;
+    }  
+
+    /**
+     * This method checks for the XML version of output document.
+     * If XML version of output document is not specified, then output 
+     * document is of version XML 1.0.
+     * If XML version of output doucment is specified, but it is not either 
+     * XML 1.0 or XML 1.1, a warning message is generated, the XML Version of
+     * output document is set to XML 1.0 and processing continues.
+     * @return string (XML version)
+     */
+    private String getXMLVersion()
+    {
+        String xmlVersion = getVersion();
+        if(xmlVersion == null || xmlVersion.equals(XMLVERSION10))
+        {
+            xmlVersion = XMLVERSION10;
+        }
+        else if(xmlVersion.equals(XMLVERSION11))
+        {
+            xmlVersion = XMLVERSION11;
+        }
+        else
+        {
+            String msg = Utils.messages.createMessage(
+                               MsgKey.ER_XML_VERSION_NOT_SUPPORTED,new Object[]{ xmlVersion });
+            try 
+            {
+                // Prepare to issue the warning message
+                Transformer tran = super.getTransformer();
+                ErrorListener errHandler = tran.getErrorListener();
+                // Issue the warning message
+                if (null != errHandler && m_sourceLocator != null)
+                    errHandler.warning(new TransformerException(msg, m_sourceLocator));
+                else
+                    System.out.println(msg);
+            }
+            catch (Exception e){}
+            xmlVersion = XMLVERSION10;								
+        }
+        return xmlVersion;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/TransformStateSetter.java b/src/main/java/org/apache/xml/serializer/TransformStateSetter.java
new file mode 100644
index 0000000..1342f35
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/TransformStateSetter.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TransformStateSetter.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import javax.xml.transform.Transformer;
+
+import org.w3c.dom.Node;
+/**
+ * This interface is meant to be used by a base interface to
+ * TransformState, but which as only the setters which have non Xalan
+ * specific types in their signature, so that there are no dependancies
+ * of the serializer on Xalan.
+ * 
+ * This interface is not a public API, it is only public because it is
+ * used by Xalan.
+ * 
+ * @see org.apache.xalan.transformer.TransformState
+ * @xsl.usage internal
+ */
+public interface TransformStateSetter
+{
+
+
+  /**
+   * Set the current node.
+   *
+   * @param n The current node.
+   */
+  void setCurrentNode(Node n);
+
+  /**
+   * Reset the state on the given transformer object.
+   *
+   * @param transformer
+   */
+  void resetState(Transformer transformer);
+
+}
diff --git a/src/main/java/org/apache/xml/serializer/TreeWalker.java b/src/main/java/org/apache/xml/serializer/TreeWalker.java
new file mode 100644
index 0000000..bf9c568
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/TreeWalker.java
@@ -0,0 +1,527 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TreeWalker.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.File;
+
+import org.apache.xml.serializer.utils.AttList;
+import org.apache.xml.serializer.utils.DOM2Helper;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.LocatorImpl;
+
+
+/**
+ * This class does a pre-order walk of the DOM tree, calling a ContentHandler
+ * interface as it goes.
+ * 
+ * This class is a copy of the one in org.apache.xml.utils. 
+ * It exists to cut the serializers dependancy on that package.
+ *  
+ * @xsl.usage internal
+ */
+
+public final class TreeWalker
+{
+
+  /** Local reference to a ContentHandler          */
+  final private ContentHandler m_contentHandler;
+  /** 
+   * If m_contentHandler is a SerializationHandler, then this is 
+   * a reference to the same object. 
+   */
+  final private SerializationHandler m_Serializer;
+
+  // ARGHH!!  JAXP Uses Xerces without setting the namespace processing to ON!
+  // DOM2Helper m_dh = new DOM2Helper();
+
+  /** DomHelper for this TreeWalker          */
+  final protected DOM2Helper m_dh;
+        
+  /** Locator object for this TreeWalker          */
+  final private LocatorImpl m_locator = new LocatorImpl();
+
+  /**
+   * Get the ContentHandler used for the tree walk.
+   *
+   * @return the ContentHandler used for the tree walk
+   */
+  public ContentHandler getContentHandler()
+  {
+    return m_contentHandler;
+  }
+  
+  public TreeWalker(ContentHandler ch) {
+      this(ch,null);
+  }
+  /**
+   * Constructor.
+   * @param   contentHandler The implemention of the
+   * contentHandler operation (toXMLString, digest, ...)
+   */
+  public TreeWalker(ContentHandler contentHandler, String systemId)
+  {
+      // Set the content handler
+      m_contentHandler = contentHandler;
+      if (m_contentHandler instanceof SerializationHandler) {
+          m_Serializer = (SerializationHandler) m_contentHandler;
+      }
+      else
+          m_Serializer = null;
+          
+      // Set the system ID, if it is given
+      m_contentHandler.setDocumentLocator(m_locator);
+      if (systemId != null)
+          m_locator.setSystemId(systemId);
+      else {
+          try {
+            // Bug see Bugzilla  26741
+            m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
+           }
+           catch (SecurityException se) {// user.dir not accessible from applet             
+           }
+      }
+          
+      // Set the document locator  
+                if (m_contentHandler != null)
+                        m_contentHandler.setDocumentLocator(m_locator);
+                try {
+                   // Bug see Bugzilla  26741
+                  m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
+                } 
+                catch (SecurityException se){// user.dir not accessible from applet
+                  
+    }
+    m_dh = new DOM2Helper();
+  }
+
+  /**
+   * Perform a pre-order traversal non-recursive style.  
+   *
+   * Note that TreeWalker assumes that the subtree is intended to represent 
+   * a complete (though not necessarily well-formed) document and, during a 
+   * traversal, startDocument and endDocument will always be issued to the 
+   * SAX listener.
+   *  
+   * @param pos Node in the tree where to start traversal
+   *
+   * @throws TransformerException
+   */
+  public void traverse(Node pos) throws org.xml.sax.SAXException
+  {
+
+    this.m_contentHandler.startDocument();
+
+    Node top = pos;
+
+    while (null != pos)
+    {
+      startNode(pos);
+
+      Node nextNode = pos.getFirstChild();
+
+      while (null == nextNode)
+      {
+        endNode(pos);
+
+        if (top.equals(pos))
+          break;
+
+        nextNode = pos.getNextSibling();
+
+        if (null == nextNode)
+        {
+          pos = pos.getParentNode();
+
+          if ((null == pos) || (top.equals(pos)))
+          {
+            if (null != pos)
+              endNode(pos);
+
+            nextNode = null;
+
+            break;
+          }
+        }
+      }
+
+      pos = nextNode;
+    }
+    this.m_contentHandler.endDocument();
+  }
+
+  /**
+   * Perform a pre-order traversal non-recursive style.
+
+   * Note that TreeWalker assumes that the subtree is intended to represent 
+   * a complete (though not necessarily well-formed) document and, during a 
+   * traversal, startDocument and endDocument will always be issued to the 
+   * SAX listener.
+   *
+   * @param pos Node in the tree where to start traversal
+   * @param top Node in the tree where to end traversal
+   *
+   * @throws TransformerException
+   */
+  public void traverse(Node pos, Node top) throws org.xml.sax.SAXException
+  {
+
+    this.m_contentHandler.startDocument();
+    
+    while (null != pos)
+    {
+      startNode(pos);
+
+      Node nextNode = pos.getFirstChild();
+
+      while (null == nextNode)
+      {
+        endNode(pos);
+
+        if ((null != top) && top.equals(pos))
+          break;
+
+        nextNode = pos.getNextSibling();
+
+        if (null == nextNode)
+        {
+          pos = pos.getParentNode();
+
+          if ((null == pos) || ((null != top) && top.equals(pos)))
+          {
+            nextNode = null;
+
+            break;
+          }
+        }
+      }
+
+      pos = nextNode;
+    }
+    this.m_contentHandler.endDocument();
+  }
+
+  /** Flag indicating whether following text to be processed is raw text          */
+  boolean nextIsRaw = false;
+  
+  /**
+   * Optimized dispatch of characters.
+   */
+  private final void dispatachChars(Node node)
+     throws org.xml.sax.SAXException
+  {
+    if(m_Serializer != null)
+    {
+      this.m_Serializer.characters(node);
+    }
+    else
+    {
+      String data = ((Text) node).getData();
+      this.m_contentHandler.characters(data.toCharArray(), 0, data.length());
+    }
+  }
+
+  /**
+   * Start processing given node
+   *
+   *
+   * @param node Node to process
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  protected void startNode(Node node) throws org.xml.sax.SAXException
+  {
+
+//   TODO: <REVIEW>
+//    A Serializer implements ContentHandler, but not NodeConsumer
+//    so drop this reference to NodeConsumer which would otherwise
+//    pull in all sorts of things
+//    if (m_contentHandler instanceof NodeConsumer)
+//    {
+//      ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
+//    }
+//    TODO: </REVIEW>
+                
+                if (node instanceof Locator)
+                {
+                        Locator loc = (Locator)node;
+                        m_locator.setColumnNumber(loc.getColumnNumber());
+                        m_locator.setLineNumber(loc.getLineNumber());
+                        m_locator.setPublicId(loc.getPublicId());
+                        m_locator.setSystemId(loc.getSystemId());
+                }
+                else
+                {
+                        m_locator.setColumnNumber(0);
+      m_locator.setLineNumber(0);
+                }
+
+    switch (node.getNodeType())
+    {
+    case Node.COMMENT_NODE :
+    {
+      String data = ((Comment) node).getData();
+
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
+
+        lh.comment(data.toCharArray(), 0, data.length());
+      }
+    }
+    break;
+    case Node.DOCUMENT_FRAGMENT_NODE :
+
+      // ??;
+      break;
+    case Node.DOCUMENT_NODE :
+    
+      break;
+    case Node.ELEMENT_NODE :
+      Element elem_node = (Element) node;
+      {
+          // Make sure the namespace node
+          // for the element itself is declared
+          // to the ContentHandler
+          String uri = elem_node.getNamespaceURI();
+          if (uri != null) {
+              String prefix = elem_node.getPrefix();
+              if (prefix==null)
+                prefix="";
+              this.m_contentHandler.startPrefixMapping(prefix,uri);              
+          }
+      }
+      NamedNodeMap atts = elem_node.getAttributes();
+      int nAttrs = atts.getLength();
+      // System.out.println("TreeWalker#startNode: "+node.getNodeName());
+
+      
+      // Make sure the namespace node of
+      // each attribute is declared to the ContentHandler
+      for (int i = 0; i < nAttrs; i++)
+      {
+        final Node attr = atts.item(i);
+        final String attrName = attr.getNodeName();
+        final int colon = attrName.indexOf(':');
+        final String prefix;
+
+        // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
+        if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
+        {
+          // Use "" instead of null, as Xerces likes "" for the 
+          // name of the default namespace.  Fix attributed 
+          // to "Steven Murray" <smurray@ebt.com>.
+          if (colon < 0)
+            prefix = "";
+          else
+            prefix = attrName.substring(colon + 1);
+
+          this.m_contentHandler.startPrefixMapping(prefix,
+                                                   attr.getNodeValue());
+        }
+        else if (colon > 0) {
+            prefix = attrName.substring(0,colon);
+            String uri = attr.getNamespaceURI();
+            if (uri != null)
+                this.m_contentHandler.startPrefixMapping(prefix,uri);
+        }        
+      }
+
+      String ns = m_dh.getNamespaceOfNode(node);
+      if(null == ns)
+        ns = "";
+      this.m_contentHandler.startElement(ns,
+                                         m_dh.getLocalNameOfNode(node),
+                                         node.getNodeName(),
+                                         new AttList(atts, m_dh));
+      break;
+    case Node.PROCESSING_INSTRUCTION_NODE :
+    {
+      ProcessingInstruction pi = (ProcessingInstruction) node;
+      String name = pi.getNodeName();
+
+      // String data = pi.getData();
+      if (name.equals("xslt-next-is-raw"))
+      {
+        nextIsRaw = true;
+      }
+      else
+      {
+        this.m_contentHandler.processingInstruction(pi.getNodeName(),
+                                                    pi.getData());
+      }
+    }
+    break;
+    case Node.CDATA_SECTION_NODE :
+    {
+      boolean isLexH = (m_contentHandler instanceof LexicalHandler);
+      LexicalHandler lh = isLexH
+                          ? ((LexicalHandler) this.m_contentHandler) : null;
+
+      if (isLexH)
+      {
+        lh.startCDATA();
+      }
+      
+      dispatachChars(node);
+
+      {
+        if (isLexH)
+        {
+          lh.endCDATA();
+        }
+      }
+    }
+    break;
+    case Node.TEXT_NODE :
+    {
+      //String data = ((Text) node).getData();
+
+      if (nextIsRaw)
+      {
+        nextIsRaw = false;
+
+        m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
+        dispatachChars(node);
+        m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
+      }
+      else
+      {
+        dispatachChars(node);
+      }
+    }
+    break;
+    case Node.ENTITY_REFERENCE_NODE :
+    {
+      EntityReference eref = (EntityReference) node;
+
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        ((LexicalHandler) this.m_contentHandler).startEntity(
+          eref.getNodeName());
+      }
+      else
+      {
+
+        // warning("Can not output entity to a pure SAX ContentHandler");
+      }
+    }
+    break;
+    default :
+    }
+  }
+
+  /**
+   * End processing of given node 
+   *
+   *
+   * @param node Node we just finished processing
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  protected void endNode(Node node) throws org.xml.sax.SAXException
+  {
+
+    switch (node.getNodeType())
+    {
+    case Node.DOCUMENT_NODE :
+      break;
+      
+    case Node.ELEMENT_NODE :
+      String ns = m_dh.getNamespaceOfNode(node);
+      if(null == ns)
+        ns = "";
+      this.m_contentHandler.endElement(ns,
+                                         m_dh.getLocalNameOfNode(node),
+                                         node.getNodeName());
+
+      if (m_Serializer == null) {
+      // Don't bother with endPrefixMapping calls if the ContentHandler is a
+      // SerializationHandler because SerializationHandler's ignore the
+      // endPrefixMapping() calls anyways. . . .  This is an optimization.    
+      Element elem_node = (Element) node;    
+      NamedNodeMap atts = elem_node.getAttributes();
+      int nAttrs = atts.getLength();
+
+      // do the endPrefixMapping calls in reverse order 
+      // of the startPrefixMapping calls
+      for (int i = (nAttrs-1); 0 <= i; i--)
+      {
+        final Node attr = atts.item(i);
+        final String attrName = attr.getNodeName();
+        final int colon = attrName.indexOf(':');
+        final String prefix;
+
+        if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
+        {
+          // Use "" instead of null, as Xerces likes "" for the 
+          // name of the default namespace.  Fix attributed 
+          // to "Steven Murray" <smurray@ebt.com>.
+          if (colon < 0)
+            prefix = "";
+          else
+            prefix = attrName.substring(colon + 1);
+
+          this.m_contentHandler.endPrefixMapping(prefix);
+        }
+        else if (colon > 0) {
+            prefix = attrName.substring(0, colon);
+            this.m_contentHandler.endPrefixMapping(prefix);
+        }
+      }
+      {
+          String uri = elem_node.getNamespaceURI();
+          if (uri != null) {
+              String prefix = elem_node.getPrefix();
+              if (prefix==null)
+                prefix="";
+              this.m_contentHandler.endPrefixMapping(prefix);              
+          }
+      }
+      }
+      break;
+    case Node.CDATA_SECTION_NODE :
+      break;
+    case Node.ENTITY_REFERENCE_NODE :
+    {
+      EntityReference eref = (EntityReference) node;
+
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
+
+        lh.endEntity(eref.getNodeName());
+      }
+    }
+    break;
+    default :
+    }
+  }
+}  //TreeWalker
+
diff --git a/src/main/java/org/apache/xml/serializer/Version.java b/src/main/java/org/apache/xml/serializer/Version.java
new file mode 100644
index 0000000..17e73d5
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/Version.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Version.java 477252 2006-11-20 16:52:00Z minchau $
+ */
+package org.apache.xml.serializer;
+
+/**
+ * Administrative class to keep track of the version number of
+ * the Serializer release.
+ * <P>This class implements the upcoming standard of having
+ * org.apache.project-name.Version.getVersion() be a standard way 
+ * to get version information.</P> 
+ * @xsl.usage general
+ */
+public final class Version
+{
+
+  /**
+   * Get the basic version string for the current Serializer.
+   * Version String formatted like 
+   * <CODE>"<B>Serializer</B> <B>Java</B> v.r[.dd| <B>D</B>nn]"</CODE>.
+   *
+   * Futurework: have this read version info from jar manifest.
+   *
+   * @return String denoting our current version
+   */
+  public static String getVersion()
+  {
+     return getProduct()+" "+getImplementationLanguage()+" "
+           +getMajorVersionNum()+"."+getReleaseVersionNum()+"."
+           +( (getDevelopmentVersionNum() > 0) ? 
+               ("D"+getDevelopmentVersionNum()) : (""+getMaintenanceVersionNum()));  
+  }
+
+  /**
+   * Print the processor version to the command line.
+   *
+   * @param argv command line arguments, unused.
+   */
+  public static void main(String argv[])
+  {
+    System.out.println(getVersion());
+  }
+  
+  /**
+   * Name of product: Serializer.
+   */
+  public static String getProduct()
+  {
+    return "Serializer";
+  }
+
+  /**
+   * Implementation Language: Java.
+   */
+  public static String getImplementationLanguage()
+  {
+    return "Java";
+  }
+  
+  
+  /**
+   * Major version number.
+   * Version number. This changes only when there is a
+   *          significant, externally apparent enhancement from
+   *          the previous release. 'n' represents the n'th
+   *          version.
+   *
+   *          Clients should carefully consider the implications
+   *          of new versions as external interfaces and behaviour
+   *          may have changed.
+   */
+  public static int getMajorVersionNum()
+  {
+    return 2;
+    
+  }  
+
+  /**
+   * Release Number.
+   * Release number. This changes when:
+   *            -  a new set of functionality is to be added, eg,
+   *               implementation of a new W3C specification.
+   *            -  API or behaviour change.
+   *            -  its designated as a reference release.
+   */
+  public static int getReleaseVersionNum()
+  {
+    return 7;
+  }
+  
+  /**
+   * Maintenance Drop Number.
+   * Optional identifier used to designate maintenance
+   *          drop applied to a specific release and contains
+   *          fixes for defects reported. It maintains compatibility
+   *          with the release and contains no API changes.
+   *          When missing, it designates the final and complete
+   *          development drop for a release.
+   */
+  public static int getMaintenanceVersionNum()
+  {
+    return 1;
+  }
+
+  /**
+   * Development Drop Number.
+   * Optional identifier designates development drop of
+   *          a specific release. D01 is the first development drop
+   *          of a new release.
+   *
+   *          Development drops are works in progress towards a
+   *          compeleted, final release. A specific development drop
+   *          may not completely implement all aspects of a new
+   *          feature, which may take several development drops to
+   *          complete. At the point of the final drop for the
+   *          release, the D suffix will be omitted.
+   *
+   *          Each 'D' drops can contain functional enhancements as
+   *          well as defect fixes. 'D' drops may not be as stable as
+   *          the final releases.
+   */
+  public static int getDevelopmentVersionNum()
+  { 
+    try {   
+        if ((new String("")).length() == 0)
+          return 0;
+        else  
+          return Integer.parseInt("");
+    } catch (NumberFormatException nfe) {
+           return 0;
+    }    
+  }      
+}
diff --git a/src/main/java/org/apache/xml/serializer/Version.src b/src/main/java/org/apache/xml/serializer/Version.src
new file mode 100644
index 0000000..46402cb
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/Version.src
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Version.src 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+/**
+ * Administrative class to keep track of the version number of
+ * the Serializer release.
+ * <P>This class implements the upcoming standard of having
+ * org.apache.project-name.Version.getVersion() be a standard way 
+ * to get version information.</P> 
+ * @xsl.usage general
+ */
+public final class Version
+{
+
+  /**
+   * Get the basic version string for the current Serializer.
+   * Version String formatted like 
+   * <CODE>"<B>Serializer</B> <B>Java</B> v.r[.dd| <B>D</B>nn]"</CODE>.
+   *
+   * Futurework: have this read version info from jar manifest.
+   *
+   * @return String denoting our current version
+   */
+  public static String getVersion()
+  {
+     return getProduct()+" "+getImplementationLanguage()+" "
+           +getMajorVersionNum()+"."+getReleaseVersionNum()+"."
+           +( (getDevelopmentVersionNum() > 0) ? 
+               ("D"+getDevelopmentVersionNum()) : (""+getMaintenanceVersionNum()));  
+  }
+
+  /**
+   * Print the processor version to the command line.
+   *
+   * @param argv command line arguments, unused.
+   */
+  public static void main(String argv[])
+  {
+    System.out.println(getVersion());
+  }
+  
+  /**
+   * Name of product: Serializer.
+   */
+  public static String getProduct()
+  {
+    return "Serializer";
+  }
+
+  /**
+   * Implementation Language: Java.
+   */
+  public static String getImplementationLanguage()
+  {
+    return "Java";
+  }
+  
+  
+  /**
+   * Major version number.
+   * Version number. This changes only when there is a
+   *          significant, externally apparent enhancement from
+   *          the previous release. 'n' represents the n'th
+   *          version.
+   *
+   *          Clients should carefully consider the implications
+   *          of new versions as external interfaces and behaviour
+   *          may have changed.
+   */
+  public static int getMajorVersionNum()
+  {
+    return @version.VERSION@;
+    
+  }  
+
+  /**
+   * Release Number.
+   * Release number. This changes when:
+   *            -  a new set of functionality is to be added, eg,
+   *               implementation of a new W3C specification.
+   *            -  API or behaviour change.
+   *            -  its designated as a reference release.
+   */
+  public static int getReleaseVersionNum()
+  {
+    return @version.RELEASE@;
+  }
+  
+  /**
+   * Maintenance Drop Number.
+   * Optional identifier used to designate maintenance
+   *          drop applied to a specific release and contains
+   *          fixes for defects reported. It maintains compatibility
+   *          with the release and contains no API changes.
+   *          When missing, it designates the final and complete
+   *          development drop for a release.
+   */
+  public static int getMaintenanceVersionNum()
+  {
+    return @version.MINOR@;
+  }
+
+  /**
+   * Development Drop Number.
+   * Optional identifier designates development drop of
+   *          a specific release. D01 is the first development drop
+   *          of a new release.
+   *
+   *          Development drops are works in progress towards a
+   *          compeleted, final release. A specific development drop
+   *          may not completely implement all aspects of a new
+   *          feature, which may take several development drops to
+   *          complete. At the point of the final drop for the
+   *          release, the D suffix will be omitted.
+   *
+   *          Each 'D' drops can contain functional enhancements as
+   *          well as defect fixes. 'D' drops may not be as stable as
+   *          the final releases.
+   */
+  public static int getDevelopmentVersionNum()
+  { 
+    try {   
+        if ((new String("@version.DEVELOPER@")).length() == 0)
+          return 0;
+        else  
+          return Integer.parseInt("@version.DEVELOPER@");
+    } catch (NumberFormatException nfe) {
+           return 0;
+    }    
+  }      
+}
diff --git a/src/main/java/org/apache/xml/serializer/WriterChain.java b/src/main/java/org/apache/xml/serializer/WriterChain.java
new file mode 100644
index 0000000..24246e6
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/WriterChain.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WriterChain.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+
+/**
+ * It is unfortunate that java.io.Writer is a class rather than an interface.
+ * The serializer has a number of classes that extend java.io.Writer
+ * and which send their ouput to a yet another wrapped Writer or OutputStream.
+ * 
+ * The purpose of this interface is to force such classes to over-ride all of
+ * the important methods defined on the java.io.Writer class, namely these:
+ * <code>
+ * write(int val)
+ * write(char[] chars)
+ * write(char[] chars, int start, int count)
+ * write(String chars)
+ * write(String chars, int start, int count)
+ * flush()
+ * close()
+ * </code>
+ * In this manner nothing will accidentally go directly to 
+ * the base class rather than to the wrapped Writer or OutputStream. 
+ * 
+ * The purpose of this class is to have a uniform way of chaining the output of one writer to
+ * the next writer in the chain. In addition there are methods to obtain the Writer or 
+ * OutputStream that this object sends its output to.
+ * 
+ * This interface is only for internal use withing the serializer. 
+ * @xsl.usage internal
+ */
+interface WriterChain
+{
+    /** This method forces us to over-ride the method defined in java.io.Writer */
+    public void write(int val) throws IOException;
+    /** This method forces us to over-ride the method defined in java.io.Writer */
+    public void write(char[] chars) throws IOException;
+    /** This method forces us to over-ride the method defined in java.io.Writer */
+    public void write(char[] chars, int start, int count) throws IOException;
+    /** This method forces us to over-ride the method defined in java.io.Writer */
+    public void write(String chars) throws IOException;
+    /** This method forces us to over-ride the method defined in java.io.Writer */
+    public void write(String chars, int start, int count) throws IOException;
+    /** This method forces us to over-ride the method defined in java.io.Writer */
+    public void flush() throws IOException;
+    /** This method forces us to over-ride the method defined in java.io.Writer */
+    public void close() throws IOException;
+
+    /**
+     * If this method returns null, getOutputStream() must return non-null.
+     * Get the writer that this writer sends its output to.
+     * 
+     * It is possible that the Writer returned by this method does not
+     * implement the WriterChain interface.
+     */
+    public java.io.Writer getWriter();
+    
+    /**
+     * If this method returns null, getWriter() must return non-null.
+     * Get the OutputStream that this writer sends its output to.
+     */
+    public java.io.OutputStream getOutputStream();
+}
diff --git a/src/main/java/org/apache/xml/serializer/WriterToASCI.java b/src/main/java/org/apache/xml/serializer/WriterToASCI.java
new file mode 100644
index 0000000..acc4f5a
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/WriterToASCI.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WriterToASCI.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+
+
+/**
+ * This class writes ASCII to a byte stream as quickly as possible.  For the
+ * moment it does not do buffering, though I reserve the right to do some
+ * buffering down the line if I can prove that it will be faster even if the
+ * output stream is buffered.
+ * 
+ * This class is only used internally within Xalan.
+ * 
+ * @xsl.usage internal
+ */
+class WriterToASCI extends Writer implements WriterChain
+{
+
+  /** The byte stream to write to.  */
+  private final OutputStream m_os;
+
+  /**
+   * Create an unbuffered ASCII writer.
+   *
+   *
+   * @param os The byte stream to write to.
+   */
+  public WriterToASCI(OutputStream os)
+  {
+    m_os = os;
+  }
+
+  /**
+   * Write a portion of an array of characters.
+   *
+   * @param  chars  Array of characters
+   * @param  start   Offset from which to start writing characters
+   * @param  length   Number of characters to write
+   *
+   * @exception  IOException  If an I/O error occurs
+   *
+   * @throws java.io.IOException
+   */
+  public void write(char chars[], int start, int length)
+          throws java.io.IOException
+  {
+
+    int n = length+start;
+
+    for (int i = start; i < n; i++)
+    {
+      m_os.write(chars[i]);
+    }
+  }
+
+  /**
+   * Write a single character.  The character to be written is contained in
+   * the 16 low-order bits of the given integer value; the 16 high-order bits
+   * are ignored.
+   *
+   * <p> Subclasses that intend to support efficient single-character output
+   * should override this method.
+   *
+   * @param c  int specifying a character to be written.
+   * @exception  IOException  If an I/O error occurs
+   */
+  public void write(int c) throws IOException
+  {
+    m_os.write(c);
+  }
+
+  /**
+   * Write a string.
+   *
+   * @param  s String to be written
+   *
+   * @exception  IOException  If an I/O error occurs
+   */
+  public void write(String s) throws IOException
+  {
+    int n = s.length();
+    for (int i = 0; i < n; i++)
+    {
+      m_os.write(s.charAt(i));
+    }
+  }
+
+  /**
+   * Flush the stream.  If the stream has saved any characters from the
+   * various write() methods in a buffer, write them immediately to their
+   * intended destination.  Then, if that destination is another character or
+   * byte stream, flush it.  Thus one flush() invocation will flush all the
+   * buffers in a chain of Writers and OutputStreams.
+   *
+   * @exception  IOException  If an I/O error occurs
+   */
+  public void flush() throws java.io.IOException
+  {
+    m_os.flush();
+  }
+
+  /**
+   * Close the stream, flushing it first.  Once a stream has been closed,
+   * further write() or flush() invocations will cause an IOException to be
+   * thrown.  Closing a previously-closed stream, however, has no effect.
+   *
+   * @exception  IOException  If an I/O error occurs
+   */
+  public void close() throws java.io.IOException
+  {
+    m_os.close();
+  }
+
+  /**
+   * Get the output stream where the events will be serialized to.
+   *
+   * @return reference to the result stream, or null of only a writer was
+   * set.
+   */
+  public OutputStream getOutputStream()
+  {
+    return m_os;
+  }
+
+  /**
+   * Get the writer that this writer directly chains to.
+   */
+  public Writer getWriter()
+  {
+      return null;
+  }
+}
diff --git a/src/main/java/org/apache/xml/serializer/WriterToUTF8Buffered.java b/src/main/java/org/apache/xml/serializer/WriterToUTF8Buffered.java
new file mode 100644
index 0000000..abff1b9
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/WriterToUTF8Buffered.java
@@ -0,0 +1,502 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WriterToUTF8Buffered.java 469356 2006-10-31 03:20:34Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+
+/**
+ * This class writes unicode characters to a byte stream (java.io.OutputStream)
+ * as quickly as possible. It buffers the output in an internal
+ * buffer which must be flushed to the OutputStream when done. This flushing
+ * is done via the close() flush() or flushBuffer() method. 
+ * 
+ * This class is only used internally within Xalan.
+ * 
+ * @xsl.usage internal
+ */
+final class WriterToUTF8Buffered extends Writer implements WriterChain
+{
+    
+  /** number of bytes that the byte buffer can hold.
+   * This is a fixed constant is used rather than m_outputBytes.lenght for performance.
+   */
+  private static final int BYTES_MAX=16*1024;
+  /** number of characters that the character buffer can hold.
+   * This is 1/3 of the number of bytes because UTF-8 encoding
+   * can expand one unicode character by up to 3 bytes.
+   */
+  private static final int CHARS_MAX=(BYTES_MAX/3);
+  
+ // private static final int 
+  
+  /** The byte stream to write to. (sc & sb remove final to compile in JDK 1.1.8) */
+  private final OutputStream m_os;
+
+  /**
+   * The internal buffer where data is stored.
+   * (sc & sb remove final to compile in JDK 1.1.8)
+   */
+  private final byte m_outputBytes[];
+  
+  private final char m_inputChars[];
+
+  /**
+   * The number of valid bytes in the buffer. This value is always
+   * in the range <tt>0</tt> through <tt>m_outputBytes.length</tt>; elements
+   * <tt>m_outputBytes[0]</tt> through <tt>m_outputBytes[count-1]</tt> contain valid
+   * byte data.
+   */
+  private int count;
+
+  /**
+   * Create an buffered UTF-8 writer.
+   *
+   *
+   * @param   out    the underlying output stream.
+   *
+   * @throws UnsupportedEncodingException
+   */
+  public WriterToUTF8Buffered(OutputStream out)
+  {
+      m_os = out;
+      // get 3 extra bytes to make buffer overflow checking simpler and faster
+      // we won't have to keep checking for a few extra characters
+      m_outputBytes = new byte[BYTES_MAX + 3];
+      
+      // Big enough to hold the input chars that will be transformed
+      // into output bytes in m_ouputBytes.
+      m_inputChars = new char[CHARS_MAX + 2];
+      count = 0;
+      
+//      the old body of this constructor, before the buffersize was changed to a constant      
+//      this(out, 8*1024);
+  }
+
+  /**
+   * Create an buffered UTF-8 writer to write data to the
+   * specified underlying output stream with the specified buffer
+   * size.
+   *
+   * @param   out    the underlying output stream.
+   * @param   size   the buffer size.
+   * @exception IllegalArgumentException if size <= 0.
+   */
+//  public WriterToUTF8Buffered(final OutputStream out, final int size)
+//  {
+//
+//    m_os = out;
+//
+//    if (size <= 0)
+//    {
+//      throw new IllegalArgumentException(
+//        SerializerMessages.createMessage(SerializerErrorResources.ER_BUFFER_SIZE_LESSTHAN_ZERO, null)); //"Buffer size <= 0");
+//    }
+//
+//    m_outputBytes = new byte[size];
+//    count = 0;
+//  }
+
+  /**
+   * Write a single character.  The character to be written is contained in
+   * the 16 low-order bits of the given integer value; the 16 high-order bits
+   * are ignored.
+   *
+   * <p> Subclasses that intend to support efficient single-character output
+   * should override this method.
+   *
+   * @param c  int specifying a character to be written.
+   * @exception  IOException  If an I/O error occurs
+   */
+  public void write(final int c) throws IOException
+  {
+    
+    /* If we are close to the end of the buffer then flush it.
+     * Remember the buffer can hold a few more bytes than BYTES_MAX
+     */ 
+    if (count >= BYTES_MAX)
+        flushBuffer();
+
+    if (c < 0x80)
+    {
+       m_outputBytes[count++] = (byte) (c);
+    }
+    else if (c < 0x800)
+    {
+      m_outputBytes[count++] = (byte) (0xc0 + (c >> 6));
+      m_outputBytes[count++] = (byte) (0x80 + (c & 0x3f));
+    }
+    else if (c < 0x10000)
+    {
+      m_outputBytes[count++] = (byte) (0xe0 + (c >> 12));
+      m_outputBytes[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
+      m_outputBytes[count++] = (byte) (0x80 + (c & 0x3f));
+    }
+	else
+	{
+	  m_outputBytes[count++] = (byte) (0xf0 + (c >> 18));
+	  m_outputBytes[count++] = (byte) (0x80 + ((c >> 12) & 0x3f));
+	  m_outputBytes[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
+	  m_outputBytes[count++] = (byte) (0x80 + (c & 0x3f));
+	}
+
+  }
+
+
+  /**
+   * Write a portion of an array of characters.
+   *
+   * @param  chars  Array of characters
+   * @param  start   Offset from which to start writing characters
+   * @param  length   Number of characters to write
+   *
+   * @exception  IOException  If an I/O error occurs
+   *
+   * @throws java.io.IOException
+   */
+  public void write(final char chars[], final int start, final int length)
+          throws java.io.IOException
+  {
+
+    // We multiply the length by three since this is the maximum length
+    // of the characters that we can put into the buffer.  It is possible
+    // for each Unicode character to expand to three bytes.
+
+    int lengthx3 = 3*length;
+
+    if (lengthx3 >= BYTES_MAX - count)
+    {
+      // The requested length is greater than the unused part of the buffer
+      flushBuffer();
+
+      if (lengthx3 > BYTES_MAX)
+      {
+        /*
+         * The requested length exceeds the size of the buffer.
+         * Cut the buffer up into chunks, each of which will
+         * not cause an overflow to the output buffer m_outputBytes,
+         * and make multiple recursive calls.
+         * Be careful about integer overflows in multiplication.
+         */
+        int split = length/CHARS_MAX; 
+        final int chunks;
+        if (length % CHARS_MAX > 0) 
+            chunks = split + 1;
+        else
+            chunks = split;
+        int end_chunk = start;
+        for (int chunk = 1; chunk <= chunks; chunk++)
+        {
+            int start_chunk = end_chunk;
+            end_chunk = start + (int) ((((long) length) * chunk) / chunks);
+            
+            // Adjust the end of the chunk if it ends on a high char 
+            // of a Unicode surrogate pair and low char of the pair
+            // is not going to be in the same chunk
+            final char c = chars[end_chunk - 1]; 
+            int ic = chars[end_chunk - 1];
+            if (c >= 0xD800 && c <= 0xDBFF) {
+                // The last Java char that we were going
+                // to process is the first of a
+                // Java surrogate char pair that
+                // represent a Unicode character.
+
+                if (end_chunk < start + length) {
+                    // Avoid spanning by including the low
+                    // char in the current chunk of chars.
+                    end_chunk++;
+                } else {
+                    /* This is the last char of the last chunk,
+                     * and it is the high char of a high/low pair with
+                     * no low char provided.
+                     * TODO: error message needed.
+                     * The char array incorrectly ends in a high char
+                     * of a high/low surrogate pair, but there is
+                     * no corresponding low as the high is the last char 
+                     */
+                    end_chunk--;
+                }
+            }
+
+
+            int len_chunk = (end_chunk - start_chunk);
+            this.write(chars,start_chunk, len_chunk);
+        }
+        return;
+      }
+    }
+
+
+
+    final int n = length+start;
+    final byte[] buf_loc = m_outputBytes; // local reference for faster access
+    int count_loc = count;      // local integer for faster access
+    int i = start;
+    {
+        /* This block could be omitted and the code would produce
+         * the same result. But this block exists to give the JIT
+         * a better chance of optimizing a tight and common loop which
+         * occurs when writing out ASCII characters. 
+         */ 
+        char c;
+        for(; i < n && (c = chars[i])< 0x80 ; i++ )
+            buf_loc[count_loc++] = (byte)c;
+    }
+    for (; i < n; i++)
+    {
+
+      final char c = chars[i];
+
+      if (c < 0x80)
+        buf_loc[count_loc++] = (byte) (c);
+      else if (c < 0x800)
+      {
+        buf_loc[count_loc++] = (byte) (0xc0 + (c >> 6));
+        buf_loc[count_loc++] = (byte) (0x80 + (c & 0x3f));
+      }
+      /**
+        * The following else if condition is added to support XML 1.1 Characters for 
+        * UTF-8:   [1111 0uuu] [10uu zzzz] [10yy yyyy] [10xx xxxx]*
+        * Unicode: [1101 10ww] [wwzz zzyy] (high surrogate)
+        *          [1101 11yy] [yyxx xxxx] (low surrogate)
+        *          * uuuuu = wwww + 1
+        */
+      else if (c >= 0xD800 && c <= 0xDBFF) 
+      {
+          char high, low;
+          high = c;
+          i++;
+          low = chars[i];
+
+          buf_loc[count_loc++] = (byte) (0xF0 | (((high + 0x40) >> 8) & 0xf0));
+          buf_loc[count_loc++] = (byte) (0x80 | (((high + 0x40) >> 2) & 0x3f));
+          buf_loc[count_loc++] = (byte) (0x80 | ((low >> 6) & 0x0f) + ((high << 4) & 0x30));
+          buf_loc[count_loc++] = (byte) (0x80 | (low & 0x3f));
+      }
+      else
+      {
+        buf_loc[count_loc++] = (byte) (0xe0 + (c >> 12));
+        buf_loc[count_loc++] = (byte) (0x80 + ((c >> 6) & 0x3f));
+        buf_loc[count_loc++] = (byte) (0x80 + (c & 0x3f));
+      }
+    }
+    // Store the local integer back into the instance variable
+    count = count_loc;
+
+  }
+
+  /**
+   * Write a string.
+   *
+   * @param  s  String to be written
+   *
+   * @exception  IOException  If an I/O error occurs
+   */
+  public void write(final String s) throws IOException
+  {
+
+    // We multiply the length by three since this is the maximum length
+    // of the characters that we can put into the buffer.  It is possible
+    // for each Unicode character to expand to three bytes.
+    final int length = s.length();
+    int lengthx3 = 3*length;
+
+    if (lengthx3 >= BYTES_MAX - count)
+    {
+      // The requested length is greater than the unused part of the buffer
+      flushBuffer();
+
+      if (lengthx3 > BYTES_MAX)
+      {
+        /*
+         * The requested length exceeds the size of the buffer,
+         * so break it up in chunks that don't exceed the buffer size.
+         */
+         final int start = 0;
+         int split = length/CHARS_MAX; 
+         final int chunks;
+         if (length % CHARS_MAX > 0) 
+             chunks = split + 1;
+         else
+             chunks = split;
+         int end_chunk = 0;
+         for (int chunk = 1; chunk <= chunks; chunk++)
+         {
+             int start_chunk = end_chunk;
+             end_chunk = start + (int) ((((long) length) * chunk) / chunks);
+             s.getChars(start_chunk,end_chunk, m_inputChars,0);
+             int len_chunk = (end_chunk - start_chunk);
+
+             // Adjust the end of the chunk if it ends on a high char 
+             // of a Unicode surrogate pair and low char of the pair
+             // is not going to be in the same chunk
+             final char c = m_inputChars[len_chunk - 1];
+             if (c >= 0xD800 && c <= 0xDBFF) {
+                 // Exclude char in this chunk, 
+                 // to avoid spanning a Unicode character 
+                 // that is in two Java chars as a high/low surrogate
+                 end_chunk--;
+                 len_chunk--;
+                 if (chunk == chunks) {
+                     /* TODO: error message needed.
+                      * The String incorrectly ends in a high char
+                      * of a high/low surrogate pair, but there is
+                      * no corresponding low as the high is the last char
+                      * Recover by ignoring this last char.
+                      */
+                 }
+             }
+
+             this.write(m_inputChars,0, len_chunk);
+         }
+         return;
+      }
+    }
+
+
+    s.getChars(0, length , m_inputChars, 0);
+    final char[] chars = m_inputChars;
+    final int n = length;
+    final byte[] buf_loc = m_outputBytes; // local reference for faster access
+    int count_loc = count;      // local integer for faster access
+    int i = 0;
+    {
+        /* This block could be omitted and the code would produce
+         * the same result. But this block exists to give the JIT
+         * a better chance of optimizing a tight and common loop which
+         * occurs when writing out ASCII characters. 
+         */ 
+        char c;
+        for(; i < n && (c = chars[i])< 0x80 ; i++ )
+            buf_loc[count_loc++] = (byte)c;
+    }
+    for (; i < n; i++)
+    {
+
+      final char c = chars[i];
+
+      if (c < 0x80)
+        buf_loc[count_loc++] = (byte) (c);
+      else if (c < 0x800)
+      {
+        buf_loc[count_loc++] = (byte) (0xc0 + (c >> 6));
+        buf_loc[count_loc++] = (byte) (0x80 + (c & 0x3f));
+      }
+    /**
+      * The following else if condition is added to support XML 1.1 Characters for 
+      * UTF-8:   [1111 0uuu] [10uu zzzz] [10yy yyyy] [10xx xxxx]*
+      * Unicode: [1101 10ww] [wwzz zzyy] (high surrogate)
+      *          [1101 11yy] [yyxx xxxx] (low surrogate)
+      *          * uuuuu = wwww + 1
+      */
+    else if (c >= 0xD800 && c <= 0xDBFF) 
+    {
+        char high, low;
+        high = c;
+        i++;
+        low = chars[i];
+
+        buf_loc[count_loc++] = (byte) (0xF0 | (((high + 0x40) >> 8) & 0xf0));
+        buf_loc[count_loc++] = (byte) (0x80 | (((high + 0x40) >> 2) & 0x3f));
+        buf_loc[count_loc++] = (byte) (0x80 | ((low >> 6) & 0x0f) + ((high << 4) & 0x30));
+        buf_loc[count_loc++] = (byte) (0x80 | (low & 0x3f));
+    }
+      else
+      {
+        buf_loc[count_loc++] = (byte) (0xe0 + (c >> 12));
+        buf_loc[count_loc++] = (byte) (0x80 + ((c >> 6) & 0x3f));
+        buf_loc[count_loc++] = (byte) (0x80 + (c & 0x3f));
+      }
+    }
+    // Store the local integer back into the instance variable
+    count = count_loc;
+
+  }
+
+  /**
+   * Flush the internal buffer
+   *
+   * @throws IOException
+   */
+  public void flushBuffer() throws IOException
+  {
+
+    if (count > 0)
+    {
+      m_os.write(m_outputBytes, 0, count);
+
+      count = 0;
+    }
+  }
+
+  /**
+   * Flush the stream.  If the stream has saved any characters from the
+   * various write() methods in a buffer, write them immediately to their
+   * intended destination.  Then, if that destination is another character or
+   * byte stream, flush it.  Thus one flush() invocation will flush all the
+   * buffers in a chain of Writers and OutputStreams.
+   *
+   * @exception  IOException  If an I/O error occurs
+   *
+   * @throws java.io.IOException
+   */
+  public void flush() throws java.io.IOException
+  {
+    flushBuffer();
+    m_os.flush();
+  }
+
+  /**
+   * Close the stream, flushing it first.  Once a stream has been closed,
+   * further write() or flush() invocations will cause an IOException to be
+   * thrown.  Closing a previously-closed stream, however, has no effect.
+   *
+   * @exception  IOException  If an I/O error occurs
+   *
+   * @throws java.io.IOException
+   */
+  public void close() throws java.io.IOException
+  {
+    flushBuffer();
+    m_os.close();
+  }
+
+  /**
+   * Get the output stream where the events will be serialized to.
+   *
+   * @return reference to the result stream, or null of only a writer was
+   * set.
+   */
+  public OutputStream getOutputStream()
+  {
+    return m_os;
+  }
+
+  public Writer getWriter()
+  {
+    // Only one of getWriter() or getOutputStream() can return null
+    // This type of writer wraps an OutputStream, not a Writer.
+    return null;
+  }
+}
diff --git a/src/main/java/org/apache/xml/serializer/XMLEntities.properties b/src/main/java/org/apache/xml/serializer/XMLEntities.properties
new file mode 100644
index 0000000..c30bed3
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/XMLEntities.properties
@@ -0,0 +1,30 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the  "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+#
+# $Id: XMLEntities.properties 468654 2006-10-28 07:09:23Z minchau $
+#
+# @version $Revision: 468654 $ $Date: 2006-10-28 00:09:23 -0700 (Sat, 28 Oct 2006) $
+# This file must be encoded in UTF-8; see CharInfo.java
+#
+# Character entity references for markup-significant
+#
+quot=34
+amp=38
+lt=60
+gt=62
+
diff --git a/src/main/java/org/apache/xml/serializer/XSLOutputAttributes.java b/src/main/java/org/apache/xml/serializer/XSLOutputAttributes.java
new file mode 100644
index 0000000..a6fa0f8
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/XSLOutputAttributes.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XSLOutputAttributes.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer;
+
+import java.util.Vector;
+
+/**
+ * This interface has methods associated with the XSLT xsl:output attribues
+ * specified in the stylesheet that effect the format of the document output.
+ * 
+ * In an XSLT stylesheet these attributes appear for example as:
+ * <pre>
+ * <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/> 
+ * </pre>
+ * The xsl:output attributes covered in this interface are:
+ * <pre>
+ * version
+ * encoding
+ * omit-xml-declarations
+ * standalone
+ * doctype-public
+ * doctype-system
+ * cdata-section-elements
+ * indent
+ * media-type
+ * </pre>
+ * 
+ * The one attribute not covered in this interface is <code>method</code> as
+ * this value is implicitly chosen by the serializer that is created, for
+ * example ToXMLStream vs. ToHTMLStream or another one.
+ * 
+ * This interface is only used internally within Xalan.
+ * 
+ * @xsl.usage internal
+ */
+interface XSLOutputAttributes
+{
+    /**
+     * Returns the previously set value of the value to be used as the public
+     * identifier in the document type declaration (DTD).
+     *
+     *@return the public identifier to be used in the DOCTYPE declaration in the
+     * output document.
+     */
+    public String getDoctypePublic();
+    /**
+     * Returns the previously set value of the value to be used
+     * as the system identifier in the document type declaration (DTD).
+     * @return the system identifier to be used in the DOCTYPE declaration in
+     * the output document.
+     *
+     */ 
+    public String getDoctypeSystem();
+    /**
+     * @return the character encoding to be used in the output document.
+     */    
+    public String getEncoding();
+    /**
+	 * @return true if the output document should be indented to visually
+	 * indicate its structure.
+     */    
+    public boolean getIndent();
+    
+    /**
+     * @return the number of spaces to indent for each indentation level.
+     */
+    public int getIndentAmount();
+    /**
+     * @return the mediatype the media-type or MIME type associated with the
+     * output document.
+     */    
+    public String getMediaType();
+    /**
+     * @return true if the XML declaration is to be omitted from the output
+     * document.
+     */    
+    public boolean getOmitXMLDeclaration();
+    /**
+      * @return a value of "yes" if the <code>standalone</code> delaration is to
+      * be included in the output document.
+      */    
+    public String getStandalone();
+    /**
+     * @return the version of the output format.
+     */    
+    public String getVersion();
+
+
+
+
+
+
+    /**
+     * Sets the value coming from the xsl:output cdata-section-elements
+     * stylesheet property.
+     * 
+     * This sets the elements whose text elements are to be output as CDATA
+     * sections.
+     * @param URI_and_localNames pairs of namespace URI and local names that
+     * identify elements whose text elements are to be output as CDATA sections.
+     * The namespace of the local element must be the given URI to match. The
+     * qName is not given because the prefix does not matter, only the namespace
+     * URI to which that prefix would map matters, so the prefix itself is not
+     * relevant in specifying which elements have their text to be output as
+     * CDATA sections.
+     */
+    public void setCdataSectionElements(Vector URI_and_localNames);
+
+    /** Set the value coming from the xsl:output doctype-public and doctype-system stylesheet properties
+     * @param system the system identifier to be used in the DOCTYPE declaration
+     * in the output document.
+     * @param pub the public identifier to be used in the DOCTYPE declaration in
+     * the output document.
+     */
+    public void setDoctype(String system, String pub);
+
+    /** Set the value coming from the xsl:output doctype-public stylesheet attribute.
+      * @param doctype the public identifier to be used in the DOCTYPE
+      * declaration in the output document.
+      */
+    public void setDoctypePublic(String doctype);
+    /** Set the value coming from the xsl:output doctype-system stylesheet attribute.
+      * @param doctype the system identifier to be used in the DOCTYPE
+      * declaration in the output document.
+      */
+    public void setDoctypeSystem(String doctype);
+    /**
+     * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
+     * @param encoding the character encoding
+     */
+    public void setEncoding(String encoding);
+    /**
+     * Sets the value coming from the xsl:output indent stylesheet
+     * attribute.
+     * @param indent true if the output document should be indented to visually
+     * indicate its structure.
+     */
+    public void setIndent(boolean indent);
+    /**
+     * Sets the value coming from the xsl:output media-type stylesheet attribute.
+     * @param mediatype the media-type or MIME type associated with the output
+     * document.
+     */
+    public void setMediaType(String mediatype);
+    /**
+     * Sets the value coming from the xsl:output omit-xml-declaration stylesheet attribute
+     * @param b true if the XML declaration is to be omitted from the output
+     * document.
+     */
+    public void setOmitXMLDeclaration(boolean b);
+    /**
+     * Sets the value coming from the xsl:output standalone stylesheet attribute.
+     * @param standalone a value of "yes" indicates that the
+     * <code>standalone</code> delaration is to be included in the output
+     * document.
+     */
+    public void setStandalone(String standalone);
+    /**
+     * Sets the value coming from the xsl:output version attribute.
+     * @param version the version of the output format.
+     */
+    public void setVersion(String version);
+
+    /**
+     * Get the value for a property that affects seraialization,
+     * if a property was set return that value, otherwise return
+     * the default value, otherwise return null.
+     * @param name The name of the property, which is just the local name
+     * if it is in no namespace, but is the URI in curly braces followed by
+     * the local name if it is in a namespace, for example:
+     * <ul>
+     * <li> "encoding"
+     * <li> "method"
+     * <li> "{http://xml.apache.org/xalan}indent-amount"
+     * <li> "{http://xml.apache.org/xalan}line-separator"
+     * </ul>
+     * @return The value of the parameter
+     */
+    public String getOutputProperty(String name);
+    /**
+     * Get the default value for a property that affects seraialization,
+     * or null if there is none. It is possible that a non-default value
+     * was set for the property, however the value returned by this method
+     * is unaffected by any non-default settings.
+     * @param name The name of the property.
+     * @return The default value of the parameter, or null if there is no default value.
+     */    
+    public String getOutputPropertyDefault(String name);
+    /**
+     * Set the non-default value for a property that affects seraialization.
+     * @param name The name of the property, which is just the local name
+     * if it is in no namespace, but is the URI in curly braces followed by
+     * the local name if it is in a namespace, for example:
+     * <ul>
+     * <li> "encoding"
+     * <li> "method"
+     * <li> "{http://xml.apache.org/xalan}indent-amount"
+     * <li> "{http://xml.apache.org/xalan}line-separator"
+     * </ul>
+     * @val The non-default value of the parameter
+     */    
+    public void   setOutputProperty(String name, String val);
+    
+    /**
+     * Set the default value for a property that affects seraialization.
+     * @param name The name of the property, which is just the local name
+     * if it is in no namespace, but is the URI in curly braces followed by
+     * the local name if it is in a namespace, for example:
+     * <ul>
+     * <li> "encoding"
+     * <li> "method"
+     * <li> "{http://xml.apache.org/xalan}indent-amount"
+     * <li> "{http://xml.apache.org/xalan}line-separator"
+     * </ul>
+     * @val The default value of the parameter
+     */ 
+    public void   setOutputPropertyDefault(String name, String val);
+}
diff --git a/src/main/java/org/apache/xml/serializer/dom3/DOM3SerializerImpl.java b/src/main/java/org/apache/xml/serializer/dom3/DOM3SerializerImpl.java
new file mode 100644
index 0000000..fcc9882
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/DOM3SerializerImpl.java
@@ -0,0 +1,158 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+import java.io.IOException;

+

+import org.apache.xml.serializer.DOM3Serializer;

+import org.apache.xml.serializer.SerializationHandler;

+import org.apache.xml.serializer.utils.WrappedRuntimeException;

+import org.w3c.dom.DOMErrorHandler;

+import org.w3c.dom.Node;

+import org.w3c.dom.ls.LSSerializerFilter;

+

+/**

+ * This class implements the DOM3Serializer interface.

+ * 

+ * @xsl.usage internal

+ */

+public final class DOM3SerializerImpl implements DOM3Serializer {

+

+    /**

+     * Private class members

+     */

+    // The DOMErrorHandler

+    private DOMErrorHandler fErrorHandler;

+

+    // A LSSerializerFilter

+    private LSSerializerFilter fSerializerFilter;

+

+    // A LSSerializerFilter

+    private String fNewLine;

+

+    // A SerializationHandler ex. an instance of ToXMLStream

+    private SerializationHandler fSerializationHandler;

+

+    /**

+     * Constructor

+     * 

+     * @param handler An instance of the SerializationHandler interface. 

+     */

+    public DOM3SerializerImpl(SerializationHandler handler) {

+        fSerializationHandler = handler;

+    }

+

+    // Public memebers

+

+    /**

+     * Returns a DOMErrorHandler set on the DOM Level 3 Serializer.

+     * 

+     * This interface is a public API.

+     *

+     * @return A Level 3 DOMErrorHandler

+     */

+    public DOMErrorHandler getErrorHandler() {

+        return fErrorHandler;

+    }

+

+    /**

+     * Returns a LSSerializerFilter set on the DOM Level 3 Serializer to filter nodes

+     * during serialization.

+     * 

+     * This interface is a public API.

+     *

+     * @return The Level 3 LSSerializerFilter

+     */

+    public LSSerializerFilter getNodeFilter() {

+        return fSerializerFilter;

+    }

+    

+    /**

+     * Gets the end-of-line sequence of characters to be used during serialization.

+     */

+    public char[] getNewLine() {

+        return (fNewLine != null) ? fNewLine.toCharArray() : null;

+    }

+

+    /**

+     * Serializes the Level 3 DOM node by creating an instance of DOM3TreeWalker

+     * which traverses the DOM tree and invokes handler events to serialize

+     * the DOM NOde. Throws an exception only if an I/O exception occured

+     * while serializing.

+     * This interface is a public API.

+     *

+     * @param node the Level 3 DOM node to serialize

+     * @throws IOException if an I/O exception occured while serializing

+     */

+    public void serializeDOM3(Node node) throws IOException {

+        try {

+            DOM3TreeWalker walker = new DOM3TreeWalker(fSerializationHandler,

+                    fErrorHandler, fSerializerFilter, fNewLine);

+

+            walker.traverse(node);

+        } catch (org.xml.sax.SAXException se) {

+            throw new WrappedRuntimeException(se);

+        }

+    }

+

+    /**

+     * Sets a DOMErrorHandler on the DOM Level 3 Serializer.

+     * 

+     * This interface is a public API.

+     *

+     * @param handler the Level 3 DOMErrorHandler

+     */

+    public void setErrorHandler(DOMErrorHandler handler) {

+        fErrorHandler = handler;

+    }

+

+    /**

+     * Sets a LSSerializerFilter on the DOM Level 3 Serializer to filter nodes

+     * during serialization.

+     * 

+     * This interface is a public API.

+     *

+     * @param filter the Level 3 LSSerializerFilter

+     */

+    public void setNodeFilter(LSSerializerFilter filter) {

+        fSerializerFilter = filter;

+    }

+

+    /**

+     * Sets a SerializationHandler on the DOM Serializer.

+     * 

+     * This interface is a public API.

+     *

+     * @param handler An instance of SerializationHandler

+     */

+    public void setSerializationHandler(SerializationHandler handler) {

+        fSerializationHandler = handler;

+    }

+

+    /**

+     * Sets the end-of-line sequence of characters to be used during serialization.

+     * @param newLine The end-of-line sequence of characters to be used during serialization.

+     */

+    public void setNewLine(char[] newLine) {

+        fNewLine = (newLine != null) ? new String(newLine) : null;

+    }

+}

diff --git a/src/main/java/org/apache/xml/serializer/dom3/DOM3TreeWalker.java b/src/main/java/org/apache/xml/serializer/dom3/DOM3TreeWalker.java
new file mode 100644
index 0000000..0b62423
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/DOM3TreeWalker.java
@@ -0,0 +1,2149 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+import java.io.File;

+import java.io.IOException;

+import java.io.Writer;

+import java.util.Enumeration;

+import java.util.Hashtable;

+import java.util.Properties;

+

+import org.apache.xml.serializer.dom3.NamespaceSupport;

+import org.apache.xml.serializer.OutputPropertiesFactory;

+import org.apache.xml.serializer.SerializationHandler;

+import org.apache.xml.serializer.utils.MsgKey;

+import org.apache.xml.serializer.utils.Utils;

+import org.apache.xml.serializer.utils.XML11Char;

+import org.apache.xml.serializer.utils.XMLChar;

+import org.w3c.dom.Attr;

+import org.w3c.dom.CDATASection;

+import org.w3c.dom.Comment;

+import org.w3c.dom.DOMError;

+import org.w3c.dom.DOMErrorHandler;

+import org.w3c.dom.Document;

+import org.w3c.dom.DocumentType;

+import org.w3c.dom.Element;

+import org.w3c.dom.Entity;

+import org.w3c.dom.EntityReference;

+import org.w3c.dom.NamedNodeMap;

+import org.w3c.dom.Node;

+import org.w3c.dom.NodeList;

+import org.w3c.dom.ProcessingInstruction;

+import org.w3c.dom.Text;

+import org.w3c.dom.ls.LSSerializerFilter;

+import org.w3c.dom.traversal.NodeFilter;

+import org.xml.sax.Locator;

+import org.xml.sax.SAXException;

+import org.xml.sax.ext.LexicalHandler;

+import org.xml.sax.helpers.LocatorImpl;

+

+/**

+ * Built on org.apache.xml.serializer.TreeWalker and adds functionality to

+ * traverse and serialize a DOM Node (Level 2 or Level 3) as specified in 

+ * the DOM Level 3 LS Recommedation by evaluating and applying DOMConfiguration 

+ * parameters and filters if any during serialization.

+ *   

+ * @xsl.usage internal

+ */

+final class DOM3TreeWalker {

+

+    /**

+     * The SerializationHandler, it extends ContentHandler and when

+     * this class is instantiated via the constructor provided, a

+     * SerializationHandler object is passed to it.

+     */

+    private SerializationHandler fSerializer = null;

+

+    /** We do not need DOM2Helper since DOM Level 3 LS applies to DOM Level 2 or newer */

+

+    /** Locator object for this TreeWalker          */

+    private LocatorImpl fLocator = new LocatorImpl();

+

+    /** ErrorHandler */

+    private DOMErrorHandler fErrorHandler = null;

+

+    /** LSSerializerFilter */

+    private LSSerializerFilter fFilter = null;

+

+    /** If the serializer is an instance of a LexicalHandler */

+    private LexicalHandler fLexicalHandler = null;

+

+    private int fWhatToShowFilter;

+

+    /** New Line character to use in serialization */

+    private String fNewLine = null;

+

+    /** DOMConfiguration Properties */

+    private Properties fDOMConfigProperties = null;

+

+    /** Keeps track if we are in an entity reference when entities=true */

+    private boolean fInEntityRef = false;

+

+    /** Stores the version of the XML document to be serialize */

+    private String fXMLVersion = null;

+

+    /** XML Version, default 1.0 */

+    private boolean fIsXMLVersion11 = false;

+

+    /** Is the Node a Level 3 DOM node */

+    private boolean fIsLevel3DOM = false;

+

+    /** DOM Configuration Parameters */

+    private int fFeatures = 0;

+

+    /** Flag indicating whether following text to be processed is raw text          */

+    boolean fNextIsRaw = false;

+

+    // 

+    private static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";

+

+    //

+    private static final String XMLNS_PREFIX = "xmlns";

+

+    // 

+    private static final String XML_URI = "http://www.w3.org/XML/1998/namespace";

+

+    // 

+    private static final String XML_PREFIX = "xml";

+

+    /** stores namespaces in scope */

+    protected NamespaceSupport fNSBinder;

+

+    /** stores all namespace bindings on the current element */

+    protected NamespaceSupport fLocalNSBinder;

+    

+    /** stores the current element depth */

+    private int fElementDepth = 0;

+

+    // ***********************************************************************

+    // DOMConfiguration paramter settings 

+    // ***********************************************************************

+    // Parameter canonical-form, true [optional] - NOT SUPPORTED 

+    private final static int CANONICAL = 0x1 << 0;

+

+    // Parameter cdata-sections, true [required] (default)

+    private final static int CDATA = 0x1 << 1;

+

+    // Parameter check-character-normalization, true [optional] - NOT SUPPORTED 

+    private final static int CHARNORMALIZE = 0x1 << 2;

+

+    // Parameter comments, true [required] (default)

+    private final static int COMMENTS = 0x1 << 3;

+

+    // Parameter datatype-normalization, true [optional] - NOT SUPPORTED

+    private final static int DTNORMALIZE = 0x1 << 4;

+

+    // Parameter element-content-whitespace, true [required] (default) - value - false [optional] NOT SUPPORTED

+    private final static int ELEM_CONTENT_WHITESPACE = 0x1 << 5;

+

+    // Parameter entities, true [required] (default)

+    private final static int ENTITIES = 0x1 << 6;

+

+    // Parameter infoset, true [required] (default), false has no effect --> True has no effect for the serializer

+    private final static int INFOSET = 0x1 << 7;

+

+    // Parameter namespaces, true [required] (default)

+    private final static int NAMESPACES = 0x1 << 8;

+

+    // Parameter namespace-declarations, true [required] (default)

+    private final static int NAMESPACEDECLS = 0x1 << 9;

+

+    // Parameter normalize-characters, true [optional] - NOT SUPPORTED

+    private final static int NORMALIZECHARS = 0x1 << 10;

+

+    // Parameter split-cdata-sections, true [required] (default)

+    private final static int SPLITCDATA = 0x1 << 11;

+

+    // Parameter validate, true [optional] - NOT SUPPORTED

+    private final static int VALIDATE = 0x1 << 12;

+

+    // Parameter validate-if-schema, true [optional] - NOT SUPPORTED

+    private final static int SCHEMAVALIDATE = 0x1 << 13;

+

+    // Parameter split-cdata-sections, true [required] (default)

+    private final static int WELLFORMED = 0x1 << 14;

+

+    // Parameter discard-default-content, true [required] (default)

+    // Not sure how this will be used in level 2 Documents

+    private final static int DISCARDDEFAULT = 0x1 << 15;

+

+    // Parameter format-pretty-print, true [optional] 

+    private final static int PRETTY_PRINT = 0x1 << 16;

+

+    // Parameter ignore-unknown-character-denormalizations, true [required] (default)

+    // We currently do not support XML 1.1 character normalization

+    private final static int IGNORE_CHAR_DENORMALIZE = 0x1 << 17;

+

+    // Parameter discard-default-content, true [required] (default)

+    private final static int XMLDECL = 0x1 << 18;

+

+    /**

+     * Constructor.

+     * @param   contentHandler serialHandler The implemention of the SerializationHandler interface

+     */

+    DOM3TreeWalker(

+        SerializationHandler serialHandler,

+        DOMErrorHandler errHandler,

+        LSSerializerFilter filter,

+        String newLine) {

+        fSerializer = serialHandler;

+        //fErrorHandler = errHandler == null ? new DOMErrorHandlerImpl() : errHandler; // Should we be using the default?

+        fErrorHandler = errHandler;

+        fFilter = filter;

+        fLexicalHandler = null;

+        fNewLine = newLine;

+

+        fNSBinder = new NamespaceSupport();

+        fLocalNSBinder = new NamespaceSupport();

+        

+        fDOMConfigProperties = fSerializer.getOutputFormat();

+        fSerializer.setDocumentLocator(fLocator);

+        initProperties(fDOMConfigProperties);

+        

+        try {

+            // Bug see Bugzilla  26741

+            fLocator.setSystemId(

+                System.getProperty("user.dir") + File.separator + "dummy.xsl");

+        } catch (SecurityException se) { // user.dir not accessible from applet

+

+        }

+    }

+

+    /**

+     * Perform a pre-order traversal non-recursive style.  

+     *

+     * Note that TreeWalker assumes that the subtree is intended to represent 

+     * a complete (though not necessarily well-formed) document and, during a 

+     * traversal, startDocument and endDocument will always be issued to the 

+     * SAX listener.

+     *  

+     * @param pos Node in the tree where to start traversal

+     *

+     * @throws TransformerException

+     */

+    public void traverse(Node pos) throws org.xml.sax.SAXException {

+        this.fSerializer.startDocument();

+

+        // Determine if the Node is a DOM Level 3 Core Node.

+        if (pos.getNodeType() != Node.DOCUMENT_NODE) {

+            Document ownerDoc = pos.getOwnerDocument();

+            if (ownerDoc != null

+                && ownerDoc.getImplementation().hasFeature("Core", "3.0")) {

+                fIsLevel3DOM = true;

+            }

+        } else {

+            if (((Document) pos)

+                .getImplementation()

+                .hasFeature("Core", "3.0")) {

+                fIsLevel3DOM = true;

+            }

+        }

+

+        if (fSerializer instanceof LexicalHandler) {

+            fLexicalHandler = ((LexicalHandler) this.fSerializer);

+        }

+

+        if (fFilter != null)

+            fWhatToShowFilter = fFilter.getWhatToShow();

+

+        Node top = pos;

+

+        while (null != pos) {

+            startNode(pos);

+

+            Node nextNode = null;

+

+            nextNode = pos.getFirstChild();

+

+            while (null == nextNode) {

+                endNode(pos);

+

+                if (top.equals(pos))

+                    break;

+

+                nextNode = pos.getNextSibling();

+

+                if (null == nextNode) {

+                    pos = pos.getParentNode();

+

+                    if ((null == pos) || (top.equals(pos))) {

+                        if (null != pos)

+                            endNode(pos);

+

+                        nextNode = null;

+

+                        break;

+                    }

+                }

+            }

+

+            pos = nextNode;

+        }

+        this.fSerializer.endDocument();

+    }

+

+    /**

+     * Perform a pre-order traversal non-recursive style.

+     

+     * Note that TreeWalker assumes that the subtree is intended to represent 

+     * a complete (though not necessarily well-formed) document and, during a 

+     * traversal, startDocument and endDocument will always be issued to the 

+     * SAX listener.

+     *

+     * @param pos Node in the tree where to start traversal

+     * @param top Node in the tree where to end traversal

+     *

+     * @throws TransformerException

+     */

+    public void traverse(Node pos, Node top) throws org.xml.sax.SAXException {

+

+        this.fSerializer.startDocument();

+

+        // Determine if the Node is a DOM Level 3 Core Node.

+        if (pos.getNodeType() != Node.DOCUMENT_NODE) {

+            Document ownerDoc = pos.getOwnerDocument();

+            if (ownerDoc != null

+                && ownerDoc.getImplementation().hasFeature("Core", "3.0")) {

+                fIsLevel3DOM = true;

+            }

+        } else {

+            if (((Document) pos)

+                .getImplementation()

+                .hasFeature("Core", "3.0")) {

+                fIsLevel3DOM = true;

+            }

+        }

+

+        if (fSerializer instanceof LexicalHandler) {

+            fLexicalHandler = ((LexicalHandler) this.fSerializer);

+        }

+

+        if (fFilter != null)

+            fWhatToShowFilter = fFilter.getWhatToShow();

+

+        while (null != pos) {

+            startNode(pos);

+

+            Node nextNode = null;

+

+            nextNode = pos.getFirstChild();

+

+            while (null == nextNode) {

+                endNode(pos);

+

+                if ((null != top) && top.equals(pos))

+                    break;

+

+                nextNode = pos.getNextSibling();

+

+                if (null == nextNode) {

+                    pos = pos.getParentNode();

+

+                    if ((null == pos) || ((null != top) && top.equals(pos))) {

+                        nextNode = null;

+

+                        break;

+                    }

+                }

+            }

+

+            pos = nextNode;

+        }

+        this.fSerializer.endDocument();

+    }

+

+    /**

+     * Optimized dispatch of characters.

+     */

+    private final void dispatachChars(Node node)

+        throws org.xml.sax.SAXException {

+        if (fSerializer != null) {

+            this.fSerializer.characters(node);

+        } else {

+            String data = ((Text) node).getData();

+            this.fSerializer.characters(data.toCharArray(), 0, data.length());

+        }

+    }

+

+    /**

+     * Start processing given node

+     *

+     * @param node Node to process

+     *

+     * @throws org.xml.sax.SAXException

+     */

+    protected void startNode(Node node) throws org.xml.sax.SAXException {

+        if (node instanceof Locator) {

+            Locator loc = (Locator) node;

+            fLocator.setColumnNumber(loc.getColumnNumber());

+            fLocator.setLineNumber(loc.getLineNumber());

+            fLocator.setPublicId(loc.getPublicId());

+            fLocator.setSystemId(loc.getSystemId());

+        } else {

+            fLocator.setColumnNumber(0);

+            fLocator.setLineNumber(0);

+        }

+

+        switch (node.getNodeType()) {

+            case Node.DOCUMENT_TYPE_NODE :

+                serializeDocType((DocumentType) node, true);

+                break;

+            case Node.COMMENT_NODE :

+                serializeComment((Comment) node);

+                break;

+            case Node.DOCUMENT_FRAGMENT_NODE :

+                // Children are traversed

+                break;

+            case Node.DOCUMENT_NODE :

+                break;

+            case Node.ELEMENT_NODE :

+                serializeElement((Element) node, true);

+                break;

+            case Node.PROCESSING_INSTRUCTION_NODE :

+                serializePI((ProcessingInstruction) node);

+                break;

+            case Node.CDATA_SECTION_NODE :

+                serializeCDATASection((CDATASection) node);

+                break;

+            case Node.TEXT_NODE :

+                serializeText((Text) node);

+                break;

+            case Node.ENTITY_REFERENCE_NODE :

+                serializeEntityReference((EntityReference) node, true);

+                break;

+            default :

+                }

+    }

+

+    /**

+     * End processing of given node 

+     *

+     *

+     * @param node Node we just finished processing

+     *

+     * @throws org.xml.sax.SAXException

+     */

+    protected void endNode(Node node) throws org.xml.sax.SAXException {

+

+        switch (node.getNodeType()) {

+            case Node.DOCUMENT_NODE :

+                break;

+            case Node.DOCUMENT_TYPE_NODE :

+                serializeDocType((DocumentType) node, false);

+                break;

+            case Node.ELEMENT_NODE :

+                serializeElement((Element) node, false);

+                break;

+            case Node.CDATA_SECTION_NODE :

+                break;

+            case Node.ENTITY_REFERENCE_NODE :

+                serializeEntityReference((EntityReference) node, false);

+                break;

+            default :

+                }

+    }

+

+    // ***********************************************************************

+    // Node serialization methods

+    // ***********************************************************************

+    /**

+     * Applies a filter on the node to serialize

+     * 

+     * @param node The Node to serialize

+     * @return True if the node is to be serialized else false if the node 

+     *         is to be rejected or skipped. 

+     */

+    protected boolean applyFilter(Node node, int nodeType) {

+        if (fFilter != null && (fWhatToShowFilter & nodeType) != 0) {

+

+            short code = fFilter.acceptNode(node);

+            switch (code) {

+                case NodeFilter.FILTER_REJECT :

+                case NodeFilter.FILTER_SKIP :

+                    return false; // skip the node

+                default : // fall through..

+            }

+        }

+        return true;

+    }

+

+    /**

+     * Serializes a Document Type Node.

+     * 

+     * @param node The Docuemnt Type Node to serialize

+     * @param bStart Invoked at the start or end of node.  Default true. 

+     */

+    protected void serializeDocType(DocumentType node, boolean bStart)

+        throws SAXException {

+        // The DocType and internalSubset can not be modified in DOM and is

+        // considered to be well-formed as the outcome of successful parsing.

+        String docTypeName = node.getNodeName();

+        String publicId = node.getPublicId();

+        String systemId = node.getSystemId();

+        String internalSubset = node.getInternalSubset();

+

+        //DocumentType nodes are never passed to the filter

+        

+        if (internalSubset != null && !"".equals(internalSubset)) {

+

+            if (bStart) {

+                try {

+                    // The Serializer does not provide a way to write out the

+                    // DOCTYPE internal subset via an event call, so we write it

+                    // out here.

+                    Writer writer = fSerializer.getWriter();

+                    StringBuffer dtd = new StringBuffer();

+

+                    dtd.append("<!DOCTYPE ");

+                    dtd.append(docTypeName);

+                    if (null != publicId) {

+                        dtd.append(" PUBLIC \"");

+                        dtd.append(publicId);

+                        dtd.append('\"');

+                    }

+

+                    if (null != systemId) {

+                        if (null == publicId) {

+                            dtd.append(" SYSTEM \"");

+                        } else {

+                            dtd.append(" \"");

+                        }    

+                        dtd.append(systemId);

+                        dtd.append('\"');

+                    }

+                    

+                    dtd.append(" [ ");

+                    

+                    dtd.append(fNewLine);

+                    dtd.append(internalSubset);

+                    dtd.append("]>");

+                    dtd.append(new String(fNewLine));

+                    

+                    writer.write(dtd.toString());

+                    writer.flush();

+                    

+                } catch (IOException e) {

+                    throw new SAXException(Utils.messages.createMessage(

+                            MsgKey.ER_WRITING_INTERNAL_SUBSET, null), e);

+                }

+            } // else if !bStart do nothing

+            

+        } else {

+            

+            if (bStart) {

+                if (fLexicalHandler != null) {

+                    fLexicalHandler.startDTD(docTypeName, publicId, systemId);

+                }

+            } else {

+                if (fLexicalHandler != null) {

+                    fLexicalHandler.endDTD();

+                }

+            }

+        }

+    }

+

+    /**

+     * Serializes a Comment Node.

+     * 

+     * @param node The Comment Node to serialize

+     */

+    protected void serializeComment(Comment node) throws SAXException {

+        // comments=true

+        if ((fFeatures & COMMENTS) != 0) {

+            String data = node.getData();

+

+            // well-formed=true

+            if ((fFeatures & WELLFORMED) != 0) {

+                isCommentWellFormed(data);

+            }

+

+            if (fLexicalHandler != null) {

+                // apply the LSSerializer filter after the operations requested by the 

+                // DOMConfiguration parameters have been applied 

+                if (!applyFilter(node, NodeFilter.SHOW_COMMENT)) {

+                    return;

+                }

+

+                fLexicalHandler.comment(data.toCharArray(), 0, data.length());

+            }

+        }

+    }

+

+    /**

+     * Serializes an Element Node.

+     * 

+     * @param node The Element Node to serialize

+     * @param bStart Invoked at the start or end of node.   

+     */

+    protected void serializeElement(Element node, boolean bStart)

+        throws SAXException {

+        if (bStart) {

+            fElementDepth++;

+

+            // We use the Xalan specific startElement and starPrefixMapping calls 

+            // (and addAttribute and namespaceAfterStartElement) as opposed to

+            // SAX specific, for performance reasons as they reduce the overhead

+            // of creating an AttList object upfront.

+

+            // well-formed=true

+            if ((fFeatures & WELLFORMED) != 0) {

+                isElementWellFormed(node);

+            }

+

+            // REVISIT: We apply the LSSerializer filter for elements before

+            // namesapce fixup

+            if (!applyFilter(node, NodeFilter.SHOW_ELEMENT)) {

+                return;

+            }

+

+            // namespaces=true, record and fixup namspaced element

+            if ((fFeatures & NAMESPACES) != 0) {

+            	fNSBinder.pushContext();

+            	fLocalNSBinder.reset();

+            	

+                recordLocalNSDecl(node);

+                fixupElementNS(node);

+            }

+

+            // Namespace normalization

+            fSerializer.startElement(

+            		node.getNamespaceURI(),

+                    node.getLocalName(),

+                    node.getNodeName());

+

+            serializeAttList(node);

+            

+        } else {

+        	fElementDepth--;

+

+            // apply the LSSerializer filter

+            if (!applyFilter(node, NodeFilter.SHOW_ELEMENT)) {

+                return;

+            }

+

+            this.fSerializer.endElement(

+            	node.getNamespaceURI(),

+                node.getLocalName(),

+                node.getNodeName());

+            // since endPrefixMapping was not used by SerializationHandler it was removed

+            // for performance reasons.

+

+            if ((fFeatures & NAMESPACES) != 0 ) {

+                    fNSBinder.popContext();

+            }

+            

+        }

+    }

+

+    /**

+     * Serializes the Attr Nodes of an Element.

+     * 

+     * @param node The OwnerElement whose Attr Nodes are to be serialized.

+     */

+    protected void serializeAttList(Element node) throws SAXException {

+        NamedNodeMap atts = node.getAttributes();

+        int nAttrs = atts.getLength();

+

+        for (int i = 0; i < nAttrs; i++) {

+            Node attr = atts.item(i);

+

+            String localName = attr.getLocalName();

+            String attrName = attr.getNodeName();

+            String attrPrefix = attr.getPrefix() == null ? "" : attr.getPrefix();

+            String attrValue = attr.getNodeValue();

+

+            // Determine the Attr's type.

+            String type = null;

+            if (fIsLevel3DOM) {

+                type = ((Attr) attr).getSchemaTypeInfo().getTypeName();

+            }

+            type = type == null ? "CDATA" : type;

+

+            String attrNS = attr.getNamespaceURI();

+            if (attrNS !=null && attrNS.length() == 0) {

+            	attrNS=null;

+                // we must remove prefix for this attribute

+            	attrName=attr.getLocalName();

+            }

+

+            boolean isSpecified = ((Attr) attr).getSpecified();

+            boolean addAttr = true;

+            boolean applyFilter = false;

+            boolean xmlnsAttr =

+                attrName.equals("xmlns") || attrName.startsWith("xmlns:");

+

+            // well-formed=true

+            if ((fFeatures & WELLFORMED) != 0) {

+                isAttributeWellFormed(attr);

+            }

+            

+            //-----------------------------------------------------------------

+            // start Attribute namespace fixup

+            //-----------------------------------------------------------------

+            // namespaces=true, normalize all non-namespace attributes

+            // Step 3. Attribute

+            if ((fFeatures & NAMESPACES) != 0 && !xmlnsAttr) {

+           	

+        		// If the Attr has a namespace URI

+        		if (attrNS != null) {

+        			attrPrefix = attrPrefix == null ? "" : attrPrefix;

+

+        			String declAttrPrefix = fNSBinder.getPrefix(attrNS);

+        			String declAttrNS = fNSBinder.getURI(attrPrefix);

+        			

+        			// attribute has no prefix (default namespace decl does not apply to

+        			// attributes)

+        			// OR

+        			// attribute prefix is not declared

+        			// OR

+        			// conflict: attribute has a prefix that conflicts with a binding

+        			if ("".equals(attrPrefix) || "".equals(declAttrPrefix)

+        					|| !attrPrefix.equals(declAttrPrefix)) {

+

+        				// namespaceURI matches an in scope declaration of one or

+        				// more prefixes

+        				if (declAttrPrefix != null && !"".equals(declAttrPrefix)) {

+        					// pick the prefix that was found and change attribute's

+        					// prefix and nodeName.

+        					attrPrefix = declAttrPrefix;

+        					

+        					if (declAttrPrefix.length() > 0 ) { 

+        						attrName = declAttrPrefix + ":" + localName;

+        					} else {

+        						attrName = localName;

+        					}	

+        				} else {

+        					// The current prefix is not null and it has no in scope

+        					// declaration

+        					if (attrPrefix != null && !"".equals(attrPrefix)

+        							&& declAttrNS == null) {

+        						// declare this prefix

+        						if ((fFeatures & NAMESPACEDECLS) != 0) {

+        							fSerializer.addAttribute(XMLNS_URI, attrPrefix,

+        									XMLNS_PREFIX + ":" + attrPrefix, "CDATA",

+        									attrNS);

+        							fNSBinder.declarePrefix(attrPrefix, attrNS);

+        							fLocalNSBinder.declarePrefix(attrPrefix, attrNS);

+        						}

+        					} else {

+        						// find a prefix following the pattern "NS" +index

+        						// (starting at 1)

+        						// make sure this prefix is not declared in the current

+        						// scope.

+        						int counter = 1;

+        						attrPrefix = "NS" + counter++;

+

+        						while (fLocalNSBinder.getURI(attrPrefix) != null) {

+        							attrPrefix = "NS" + counter++;

+        						}

+        						// change attribute's prefix and Name

+        						attrName = attrPrefix + ":" + localName;

+        						

+        						// create a local namespace declaration attribute

+        						// Add the xmlns declaration attribute

+        						if ((fFeatures & NAMESPACEDECLS) != 0) {

+                                       							

+        							fSerializer.addAttribute(XMLNS_URI, attrPrefix,

+        									XMLNS_PREFIX + ":" + attrPrefix, "CDATA",

+        									attrNS);

+            						fNSBinder.declarePrefix(attrPrefix, attrNS);

+            						fLocalNSBinder.declarePrefix(attrPrefix, attrNS);

+        						}

+        					}

+        				}

+        			}

+

+        		} else { // if the Attr has no namespace URI

+        			// Attr has no localName

+        			if (localName == null) {

+        				// DOM Level 1 node!

+        				String msg = Utils.messages.createMessage(

+        						MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,

+        						new Object[] { attrName });

+

+        				if (fErrorHandler != null) {

+        					fErrorHandler

+        							.handleError(new DOMErrorImpl(

+        									DOMError.SEVERITY_ERROR, msg,

+        									MsgKey.ER_NULL_LOCAL_ELEMENT_NAME, null,

+        									null, null));

+        				}

+

+        			} else { // uri=null and no colon

+        				// attr has no namespace URI and no prefix

+        				// no action is required, since attrs don't use default

+        			}

+        		}

+

+            }

+

+            

+            // discard-default-content=true

+            // Default attr's are not passed to the filter and this contraint

+            // is applied only when discard-default-content=true 

+            // What about default xmlns attributes???? check for xmlnsAttr

+            if ((((fFeatures & DISCARDDEFAULT) != 0) && isSpecified)

+                || ((fFeatures & DISCARDDEFAULT) == 0)) {

+                applyFilter = true;

+            } else {

+            	addAttr = false;

+            }

+

+            if (applyFilter) {

+                // apply the filter for Attributes that are not default attributes

+                // or namespace decl attributes

+                if (fFilter != null

+                    && (fFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE)

+                        != 0) {

+

+                    if (!xmlnsAttr) {

+                        short code = fFilter.acceptNode(attr);

+                        switch (code) {

+                            case NodeFilter.FILTER_REJECT :

+                            case NodeFilter.FILTER_SKIP :

+                                addAttr = false;

+                                break;

+                            default : //fall through..

+                        }

+                    }

+                }

+            }

+

+            // if the node is a namespace node

+            if (addAttr && xmlnsAttr) {

+                // If namespace-declarations=true, add the node , else don't add it

+                if ((fFeatures & NAMESPACEDECLS) != 0) {

+               		// The namespace may have been fixed up, in that case don't add it.

+                	if (localName != null && !"".equals(localName)) {

+                		fSerializer.addAttribute(attrNS, localName, attrName, type, attrValue);

+                	} 

+                }

+            } else if (

+                addAttr && !xmlnsAttr) { // if the node is not a namespace node

+                // If namespace-declarations=true, add the node with the Attr nodes namespaceURI

+                // else add the node setting it's namespace to null or else the serializer will later

+                // attempt to add a xmlns attr for the prefixed attribute

+                if (((fFeatures & NAMESPACEDECLS) != 0) && (attrNS != null)) {

+                    fSerializer.addAttribute(

+                        attrNS,

+                        localName,

+                        attrName,

+                        type,

+                        attrValue);

+                } else {

+                    fSerializer.addAttribute(

+                        "",

+                        localName,

+                        attrName,

+                        type,

+                        attrValue);

+                }

+            }

+

+            // 

+            if (xmlnsAttr && ((fFeatures & NAMESPACEDECLS) != 0)) {

+                int index;

+                // Use "" instead of null, as Xerces likes "" for the 

+                // name of the default namespace.  Fix attributed 

+                // to "Steven Murray" <smurray@ebt.com>.

+                String prefix =

+                    (index = attrName.indexOf(":")) < 0

+                        ? ""

+                        : attrName.substring(index + 1);

+

+                if (!"".equals(prefix)) {

+                    fSerializer.namespaceAfterStartElement(prefix, attrValue);

+                }

+            }

+        }

+        

+    }

+   

+    /**

+     * Serializes an ProcessingInstruction Node.

+     * 

+     * @param node The ProcessingInstruction Node to serialize

+     */

+    protected void serializePI(ProcessingInstruction node)

+        throws SAXException {

+        ProcessingInstruction pi = node;

+        String name = pi.getNodeName();

+

+        // well-formed=true

+        if ((fFeatures & WELLFORMED) != 0) {

+            isPIWellFormed(node);

+        }

+

+        // apply the LSSerializer filter

+        if (!applyFilter(node, NodeFilter.SHOW_PROCESSING_INSTRUCTION)) {

+            return;

+        }

+

+        // String data = pi.getData();

+        if (name.equals("xslt-next-is-raw")) {

+            fNextIsRaw = true;

+        } else {

+            this.fSerializer.processingInstruction(name, pi.getData());

+        }

+    }

+

+    /**

+     * Serializes an CDATASection Node.

+     * 

+     * @param node The CDATASection Node to serialize

+     */

+    protected void serializeCDATASection(CDATASection node)

+        throws SAXException {

+        // well-formed=true

+        if ((fFeatures & WELLFORMED) != 0) {

+            isCDATASectionWellFormed(node);

+        }

+

+        // cdata-sections = true

+        if ((fFeatures & CDATA) != 0) {

+

+            // split-cdata-sections = true

+            // Assumption: This parameter has an effect only when

+			// cdata-sections=true

+            // ToStream, by default splits cdata-sections. Hence the check

+			// below.

+            String nodeValue = node.getNodeValue();

+            int endIndex = nodeValue.indexOf("]]>");

+            if ((fFeatures & SPLITCDATA) != 0) {

+                if (endIndex >= 0) {

+                    // The first node split will contain the ]] markers

+                    String relatedData = nodeValue.substring(0, endIndex + 2);

+

+                    String msg =

+                        Utils.messages.createMessage(

+                            MsgKey.ER_CDATA_SECTIONS_SPLIT,

+                            null);

+

+                    if (fErrorHandler != null) {

+                        fErrorHandler.handleError(

+                            new DOMErrorImpl(

+                                DOMError.SEVERITY_WARNING,

+                                msg,

+                                MsgKey.ER_CDATA_SECTIONS_SPLIT,

+                                null,

+                                relatedData,

+                                null));

+                    }

+                }

+            } else {

+                if (endIndex >= 0) {

+                    // The first node split will contain the ]] markers 

+                    String relatedData = nodeValue.substring(0, endIndex + 2);

+

+                    String msg =

+                        Utils.messages.createMessage(

+                            MsgKey.ER_CDATA_SECTIONS_SPLIT,

+                            null);

+

+                    if (fErrorHandler != null) {

+                        fErrorHandler.handleError(

+                            new DOMErrorImpl(

+                                DOMError.SEVERITY_ERROR,

+                                msg,

+                                MsgKey.ER_CDATA_SECTIONS_SPLIT));

+                    }

+                    // Report an error and return.  What error???

+                    return;

+                }

+            }

+

+            // apply the LSSerializer filter

+            if (!applyFilter(node, NodeFilter.SHOW_CDATA_SECTION)) {

+                return;

+            }

+

+            // splits the cdata-section

+            if (fLexicalHandler != null) {

+                fLexicalHandler.startCDATA();

+            }

+            dispatachChars(node);

+            if (fLexicalHandler != null) {

+                fLexicalHandler.endCDATA();

+            }

+        } else {

+            dispatachChars(node);

+        }

+    }

+

+    /**

+     * Serializes an Text Node.

+     * 

+     * @param node The Text Node to serialize

+     */

+    protected void serializeText(Text node) throws SAXException {

+        if (fNextIsRaw) {

+            fNextIsRaw = false;

+            fSerializer.processingInstruction(

+                javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING,

+                "");

+            dispatachChars(node);

+            fSerializer.processingInstruction(

+                javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING,

+                "");

+        } else {

+            // keep track of dispatch or not to avoid duplicaiton of filter code

+            boolean bDispatch = false;

+

+            // well-formed=true

+            if ((fFeatures & WELLFORMED) != 0) {

+                isTextWellFormed(node);

+            }

+

+            // if the node is whitespace

+            // Determine the Attr's type.

+            boolean isElementContentWhitespace = false;

+            if (fIsLevel3DOM) {

+                isElementContentWhitespace =

+                       node.isElementContentWhitespace();

+            }

+

+            if (isElementContentWhitespace) {

+                // element-content-whitespace=true

+                if ((fFeatures & ELEM_CONTENT_WHITESPACE) != 0) {

+                    bDispatch = true;

+                }

+            } else {

+                bDispatch = true;

+            }

+

+            // apply the LSSerializer filter

+            if (!applyFilter(node, NodeFilter.SHOW_TEXT)) {

+                return;

+            }

+

+            if (bDispatch) {

+                dispatachChars(node);

+            }

+        }

+    }

+

+    /**

+     * Serializes an EntityReference Node.

+     * 

+     * @param node The EntityReference Node to serialize

+     * @param bStart Inicates if called from start or endNode 

+     */

+    protected void serializeEntityReference(

+        EntityReference node,

+        boolean bStart)

+        throws SAXException {

+        if (bStart) {

+            EntityReference eref = node;

+            // entities=true

+            if ((fFeatures & ENTITIES) != 0) {

+                

+                // perform well-formedness and other checking only if 

+                // entities = true

+

+                // well-formed=true

+                if ((fFeatures & WELLFORMED) != 0) {

+                    isEntityReferneceWellFormed(node);

+                }

+

+                // check "unbound-prefix-in-entity-reference" [fatal] 

+                // Raised if the configuration parameter "namespaces" is set to true

+                if ((fFeatures & NAMESPACES) != 0) {

+                    checkUnboundPrefixInEntRef(node);

+                }

+

+                // The filter should not apply in this case, since the

+                // EntityReference is not being expanded.

+                // should we pass entity reference nodes to the filter???

+            } 

+            

+            if (fLexicalHandler != null) {

+

+                // startEntity outputs only Text but not Element, Attr, Comment 

+                // and PI child nodes.  It does so by setting the m_inEntityRef 

+                // in ToStream and using this to decide if a node is to be 

+                // serialized or not.

+                fLexicalHandler.startEntity(eref.getNodeName());

+            } 

+

+        } else {

+            EntityReference eref = node;

+            // entities=true or false, 

+            if (fLexicalHandler != null) {

+                fLexicalHandler.endEntity(eref.getNodeName());

+            }

+        }

+    }

+

+    

+    // ***********************************************************************

+    // Methods to check well-formedness

+    // ***********************************************************************

+    /**

+     * Taken from org.apache.xerces.dom.CoreDocumentImpl

+     * 

+     * Check the string against XML's definition of acceptable names for

+     * elements and attributes and so on using the XMLCharacterProperties

+     * utility class

+     */

+    protected boolean isXMLName(String s, boolean xml11Version) {

+

+        if (s == null) {

+            return false;

+        }

+        if (!xml11Version)

+            return XMLChar.isValidName(s);

+        else

+            return XML11Char.isXML11ValidName(s);

+    }

+

+    /**

+     * Taken from org.apache.xerces.dom.CoreDocumentImpl

+     *  

+     * Checks if the given qualified name is legal with respect

+     * to the version of XML to which this document must conform.

+     *

+     * @param prefix prefix of qualified name

+     * @param local local part of qualified name

+     */

+    protected boolean isValidQName(

+        String prefix,

+        String local,

+        boolean xml11Version) {

+

+        // check that both prefix and local part match NCName

+        if (local == null)

+            return false;

+        boolean validNCName = false;

+

+        if (!xml11Version) {

+            validNCName =

+                (prefix == null || XMLChar.isValidNCName(prefix))

+                    && XMLChar.isValidNCName(local);

+        } else {

+            validNCName =

+                (prefix == null || XML11Char.isXML11ValidNCName(prefix))

+                    && XML11Char.isXML11ValidNCName(local);

+        }

+

+        return validNCName;

+    }

+

+    /**

+     * Checks if a XML character is well-formed

+     * 

+     * @param characters A String of characters to be checked for Well-Formedness

+     * @param refInvalidChar A reference to the character to be returned that was determined invalid. 

+     */

+    protected boolean isWFXMLChar(String chardata, Character refInvalidChar) {

+        if (chardata == null || (chardata.length() == 0)) {

+            return true;

+        }

+

+        char[] dataarray = chardata.toCharArray();

+        int datalength = dataarray.length;

+

+        // version of the document is XML 1.1

+        if (fIsXMLVersion11) {

+            //we need to check all characters as per production rules of XML11

+            int i = 0;

+            while (i < datalength) {

+                if (XML11Char.isXML11Invalid(dataarray[i++])) {

+                    // check if this is a supplemental character

+                    char ch = dataarray[i - 1];

+                    if (XMLChar.isHighSurrogate(ch) && i < datalength) {

+                        char ch2 = dataarray[i++];

+                        if (XMLChar.isLowSurrogate(ch2)

+                            && XMLChar.isSupplemental(

+                                XMLChar.supplemental(ch, ch2))) {

+                            continue;

+                        }

+                    }

+                    // Reference to invalid character which is returned

+                    refInvalidChar = new Character(ch);

+                    return false;

+                }

+            }

+        } // version of the document is XML 1.0

+        else {

+            // we need to check all characters as per production rules of XML 1.0

+            int i = 0;

+            while (i < datalength) {

+                if (XMLChar.isInvalid(dataarray[i++])) {

+                    // check if this is a supplemental character

+                    char ch = dataarray[i - 1];

+                    if (XMLChar.isHighSurrogate(ch) && i < datalength) {

+                        char ch2 = dataarray[i++];

+                        if (XMLChar.isLowSurrogate(ch2)

+                            && XMLChar.isSupplemental(

+                                XMLChar.supplemental(ch, ch2))) {

+                            continue;

+                        }

+                    }

+                    // Reference to invalid character which is returned                    

+                    refInvalidChar = new Character(ch);

+                    return false;

+                }

+            }

+        } // end-else fDocument.isXMLVersion()

+

+        return true;

+    } // isXMLCharWF

+

+    /**

+     * Checks if a XML character is well-formed.  If there is a problem with

+     * the character a non-null Character is returned else null is returned.

+     * 

+     * @param characters A String of characters to be checked for Well-Formedness

+     * @return Character A reference to the character to be returned that was determined invalid. 

+     */

+    protected Character isWFXMLChar(String chardata) {

+    	Character refInvalidChar;

+        if (chardata == null || (chardata.length() == 0)) {

+            return null;

+        }

+

+        char[] dataarray = chardata.toCharArray();

+        int datalength = dataarray.length;

+

+        // version of the document is XML 1.1

+        if (fIsXMLVersion11) {

+            //we need to check all characters as per production rules of XML11

+            int i = 0;

+            while (i < datalength) {

+                if (XML11Char.isXML11Invalid(dataarray[i++])) {

+                    // check if this is a supplemental character

+                    char ch = dataarray[i - 1];

+                    if (XMLChar.isHighSurrogate(ch) && i < datalength) {

+                        char ch2 = dataarray[i++];

+                        if (XMLChar.isLowSurrogate(ch2)

+                            && XMLChar.isSupplemental(

+                                XMLChar.supplemental(ch, ch2))) {

+                            continue;

+                        }

+                    }

+                    // Reference to invalid character which is returned

+                    refInvalidChar = new Character(ch);

+                    return refInvalidChar;

+                }

+            }

+        } // version of the document is XML 1.0

+        else {

+            // we need to check all characters as per production rules of XML 1.0

+            int i = 0;

+            while (i < datalength) {

+                if (XMLChar.isInvalid(dataarray[i++])) {

+                    // check if this is a supplemental character

+                    char ch = dataarray[i - 1];

+                    if (XMLChar.isHighSurrogate(ch) && i < datalength) {

+                        char ch2 = dataarray[i++];

+                        if (XMLChar.isLowSurrogate(ch2)

+                            && XMLChar.isSupplemental(

+                                XMLChar.supplemental(ch, ch2))) {

+                            continue;

+                        }

+                    }

+                    // Reference to invalid character which is returned                    

+                    refInvalidChar = new Character(ch);

+                    return refInvalidChar;

+                }

+            }

+        } // end-else fDocument.isXMLVersion()

+

+        return null;

+    } // isXMLCharWF

+

+    /**

+     * Checks if a comment node is well-formed

+     * 

+     * @param data The contents of the comment node

+     * @return a boolean indiacating if the comment is well-formed or not.

+     */

+    protected void isCommentWellFormed(String data) {

+        if (data == null || (data.length() == 0)) {

+            return;

+        }

+

+        char[] dataarray = data.toCharArray();

+        int datalength = dataarray.length;

+

+        // version of the document is XML 1.1

+        if (fIsXMLVersion11) {

+            // we need to check all chracters as per production rules of XML11

+            int i = 0;

+            while (i < datalength) {

+                char c = dataarray[i++];

+                if (XML11Char.isXML11Invalid(c)) {

+                    // check if this is a supplemental character

+                    if (XMLChar.isHighSurrogate(c) && i < datalength) {

+                        char c2 = dataarray[i++];

+                        if (XMLChar.isLowSurrogate(c2)

+                            && XMLChar.isSupplemental(

+                                XMLChar.supplemental(c, c2))) {

+                            continue;

+                        }

+                    }

+                    String msg =

+                        Utils.messages.createMessage(

+                            MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,

+                            new Object[] { new Character(c)});

+

+                    if (fErrorHandler != null) {

+                        fErrorHandler.handleError(

+                            new DOMErrorImpl(

+                                DOMError.SEVERITY_FATAL_ERROR,

+                                msg,

+                                MsgKey.ER_WF_INVALID_CHARACTER,

+                                null,

+                                null,

+                                null));

+                    }

+                } else if (c == '-' && i < datalength && dataarray[i] == '-') {

+                    String msg =

+                        Utils.messages.createMessage(

+                            MsgKey.ER_WF_DASH_IN_COMMENT,

+                            null);

+

+                    if (fErrorHandler != null) {

+                        fErrorHandler.handleError(

+                            new DOMErrorImpl(

+                                DOMError.SEVERITY_FATAL_ERROR,

+                                msg,

+                                MsgKey.ER_WF_INVALID_CHARACTER,

+                                null,

+                                null,

+                                null));

+                    }

+                }

+            }

+        } // version of the document is XML 1.0

+        else {

+            // we need to check all chracters as per production rules of XML 1.0

+            int i = 0;

+            while (i < datalength) {

+                char c = dataarray[i++];

+                if (XMLChar.isInvalid(c)) {

+                    // check if this is a supplemental character

+                    if (XMLChar.isHighSurrogate(c) && i < datalength) {

+                        char c2 = dataarray[i++];

+                        if (XMLChar.isLowSurrogate(c2)

+                            && XMLChar.isSupplemental(

+                                XMLChar.supplemental(c, c2))) {

+                            continue;

+                        }

+                    }

+                    String msg =

+                        Utils.messages.createMessage(

+                            MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,

+                            new Object[] { new Character(c)});

+

+                    if (fErrorHandler != null) {

+                        fErrorHandler.handleError(

+                            new DOMErrorImpl(

+                                DOMError.SEVERITY_FATAL_ERROR,

+                                msg,

+                                MsgKey.ER_WF_INVALID_CHARACTER,

+                                null,

+                                null,

+                                null));

+                    }

+                } else if (c == '-' && i < datalength && dataarray[i] == '-') {

+                    String msg =

+                        Utils.messages.createMessage(

+                            MsgKey.ER_WF_DASH_IN_COMMENT,

+                            null);

+

+                    if (fErrorHandler != null) {

+                        fErrorHandler.handleError(

+                            new DOMErrorImpl(

+                                DOMError.SEVERITY_FATAL_ERROR,

+                                msg,

+                                MsgKey.ER_WF_INVALID_CHARACTER,

+                                null,

+                                null,

+                                null));

+                    }

+                }

+            }

+        }

+        return;

+    }

+

+    /**

+     * Checks if an element node is well-formed, by checking its Name for well-formedness.

+     * 

+     * @param data The contents of the comment node

+     * @return a boolean indiacating if the comment is well-formed or not.

+     */

+    protected void isElementWellFormed(Node node) {

+        boolean isNameWF = false;

+        if ((fFeatures & NAMESPACES) != 0) {

+            isNameWF =

+                isValidQName(

+                    node.getPrefix(),

+                    node.getLocalName(),

+                    fIsXMLVersion11);

+        } else {

+            isNameWF = isXMLName(node.getNodeName(), fIsXMLVersion11);

+        }

+

+        if (!isNameWF) {

+            String msg =

+                Utils.messages.createMessage(

+                    MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                    new Object[] { "Element", node.getNodeName()});

+

+            if (fErrorHandler != null) {

+                fErrorHandler.handleError(

+                    new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR,

+                        msg,

+                        MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                        null,

+                        null,

+                        null));

+            }

+        }

+    }

+

+    /**

+     * Checks if an attr node is well-formed, by checking it's Name and value

+     * for well-formedness.

+     * 

+     * @param data The contents of the comment node

+     * @return a boolean indiacating if the comment is well-formed or not.

+     */

+    protected void isAttributeWellFormed(Node node) {

+        boolean isNameWF = false;

+        if ((fFeatures & NAMESPACES) != 0) {

+            isNameWF =

+                isValidQName(

+                    node.getPrefix(),

+                    node.getLocalName(),

+                    fIsXMLVersion11);

+        } else {

+            isNameWF = isXMLName(node.getNodeName(), fIsXMLVersion11);

+        }

+

+        if (!isNameWF) {

+            String msg =

+                Utils.messages.createMessage(

+                    MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                    new Object[] { "Attr", node.getNodeName()});

+

+            if (fErrorHandler != null) {

+                fErrorHandler.handleError(

+                    new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR,

+                        msg,

+                        MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                        null,

+                        null,

+                        null));

+            }

+        }

+

+        // Check the Attr's node value

+        // WFC: No < in Attribute Values

+        String value = node.getNodeValue();

+        if (value.indexOf('<') >= 0) {

+            String msg =

+                Utils.messages.createMessage(

+                    MsgKey.ER_WF_LT_IN_ATTVAL,

+                    new Object[] {

+                        ((Attr) node).getOwnerElement().getNodeName(),

+                        node.getNodeName()});

+

+            if (fErrorHandler != null) {

+                fErrorHandler.handleError(

+                    new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR,

+                        msg,

+                        MsgKey.ER_WF_LT_IN_ATTVAL,

+                        null,

+                        null,

+                        null));

+            }

+        }

+

+        // we need to loop through the children of attr nodes and check their values for

+        // well-formedness  

+        NodeList children = node.getChildNodes();

+        for (int i = 0; i < children.getLength(); i++) {

+            Node child = children.item(i);

+            // An attribute node with no text or entity ref child for example

+            // doc.createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ns");

+            // followes by

+            // element.setAttributeNodeNS(attribute);

+            // can potentially lead to this situation.  If the attribute

+            // was a prefix Namespace attribute declaration then then DOM Core 

+            // should have some exception defined for this.

+            if (child == null) {

+                // we should probably report an error

+                continue;

+            }

+            switch (child.getNodeType()) {

+                case Node.TEXT_NODE :

+                    isTextWellFormed((Text) child);

+                    break;

+                case Node.ENTITY_REFERENCE_NODE :

+                    isEntityReferneceWellFormed((EntityReference) child);

+                    break;

+                default :

+            }

+        }

+

+        // TODO:

+        // WFC: Check if the attribute prefix is bound to 

+        // http://www.w3.org/2000/xmlns/  

+

+        // WFC: Unique Att Spec

+        // Perhaps pass a seen boolean value to this method.  serializeAttList will determine

+        // if the attr was seen before.

+    }

+

+    /**

+     * Checks if a PI node is well-formed, by checking it's Name and data

+     * for well-formedness.

+     * 

+     * @param data The contents of the comment node

+     */

+    protected void isPIWellFormed(ProcessingInstruction node) {

+        // Is the PI Target a valid XML name

+        if (!isXMLName(node.getNodeName(), fIsXMLVersion11)) {

+            String msg =

+                Utils.messages.createMessage(

+                    MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                    new Object[] { "ProcessingInstruction", node.getTarget()});

+

+            if (fErrorHandler != null) {

+                fErrorHandler.handleError(

+                    new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR,

+                        msg,

+                        MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                        null,

+                        null,

+                        null));

+            }

+        }

+

+        // Does the PI Data carry valid XML characters

+

+        // REVISIT: Should we check if the PI DATA contains a ?> ???

+        Character invalidChar = isWFXMLChar(node.getData());

+        if (invalidChar != null) {

+            String msg =

+                Utils.messages.createMessage(

+                    MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,

+                    new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });

+

+            if (fErrorHandler != null) {

+                fErrorHandler.handleError(

+                    new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR,

+                        msg,

+                        MsgKey.ER_WF_INVALID_CHARACTER,

+                        null,

+                        null,

+                        null));

+            }

+        }

+    }

+

+    /**

+     * Checks if an CDATASection node is well-formed, by checking it's data

+     * for well-formedness.  Note that the presence of a CDATA termination mark

+     * in the contents of a CDATASection is handled by the parameter 

+     * spli-cdata-sections

+     * 

+     * @param data The contents of the comment node

+     */

+    protected void isCDATASectionWellFormed(CDATASection node) {

+        // Does the data valid XML character data        

+        Character invalidChar = isWFXMLChar(node.getData());

+        //if (!isWFXMLChar(node.getData(), invalidChar)) {

+        if (invalidChar != null) {

+            String msg =

+                Utils.messages.createMessage(

+                    MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,

+                    new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });

+

+            if (fErrorHandler != null) {

+                fErrorHandler.handleError(

+                    new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR,

+                        msg,

+                        MsgKey.ER_WF_INVALID_CHARACTER,

+                        null,

+                        null,

+                        null));

+            }

+        }

+    }

+

+    /**

+     * Checks if an Text node is well-formed, by checking if it contains invalid

+     * XML characters.

+     * 

+     * @param data The contents of the comment node

+     */

+    protected void isTextWellFormed(Text node) {

+        // Does the data valid XML character data        

+    	Character invalidChar = isWFXMLChar(node.getData());

+    	if (invalidChar != null) {

+            String msg =

+                Utils.messages.createMessage(

+                    MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,

+                    new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });

+

+            if (fErrorHandler != null) {

+                fErrorHandler.handleError(

+                    new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR,

+                        msg,

+                        MsgKey.ER_WF_INVALID_CHARACTER,

+                        null,

+                        null,

+                        null));

+            }

+        }

+    }

+

+    /**

+     * Checks if an EntityRefernece node is well-formed, by checking it's node name.  Then depending

+     * on whether it is referenced in Element content or in an Attr Node, checks if the EntityReference

+     * references an unparsed entity or a external entity and if so throws raises the 

+     * appropriate well-formedness error.  

+     * 

+     * @param data The contents of the comment node

+     * @parent The parent of the EntityReference Node

+     */

+    protected void isEntityReferneceWellFormed(EntityReference node) {

+        // Is the EntityReference name a valid XML name

+        if (!isXMLName(node.getNodeName(), fIsXMLVersion11)) {

+            String msg =

+                Utils.messages.createMessage(

+                    MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                    new Object[] { "EntityReference", node.getNodeName()});

+

+            if (fErrorHandler != null) {

+                fErrorHandler.handleError(

+                    new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR,

+                        msg,

+                        MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                        null,

+                        null,

+                        null));

+            }

+        }

+

+        // determine the parent node

+        Node parent = node.getParentNode();

+

+        // Traverse the declared entities and check if the nodeName and namespaceURI

+        // of the EntityReference matches an Entity.  If so, check the if the notationName

+        // is not null, if so, report an error.

+        DocumentType docType = node.getOwnerDocument().getDoctype();

+        if (docType != null) {

+            NamedNodeMap entities = docType.getEntities();

+            for (int i = 0; i < entities.getLength(); i++) {

+                Entity ent = (Entity) entities.item(i);

+

+                String nodeName =

+                    node.getNodeName() == null ? "" : node.getNodeName();

+                String nodeNamespaceURI =

+                    node.getNamespaceURI() == null

+                        ? ""

+                        : node.getNamespaceURI();

+                String entName =

+                    ent.getNodeName() == null ? "" : ent.getNodeName();

+                String entNamespaceURI =

+                    ent.getNamespaceURI() == null ? "" : ent.getNamespaceURI();

+                // If referenced in Element content

+                // WFC: Parsed Entity

+                if (parent.getNodeType() == Node.ELEMENT_NODE) {

+                    if (entNamespaceURI.equals(nodeNamespaceURI)

+                        && entName.equals(nodeName)) {

+

+                        if (ent.getNotationName() != null) {

+                            String msg =

+                                Utils.messages.createMessage(

+                                    MsgKey.ER_WF_REF_TO_UNPARSED_ENT,

+                                    new Object[] { node.getNodeName()});

+

+                            if (fErrorHandler != null) {

+                                fErrorHandler.handleError(

+                                    new DOMErrorImpl(

+                                        DOMError.SEVERITY_FATAL_ERROR,

+                                        msg,

+                                        MsgKey.ER_WF_REF_TO_UNPARSED_ENT,

+                                        null,

+                                        null,

+                                        null));

+                            }

+                        }

+                    }

+                } // end if WFC: Parsed Entity

+

+                // If referenced in an Attr value

+                // WFC: No External Entity References

+                if (parent.getNodeType() == Node.ATTRIBUTE_NODE) {

+                    if (entNamespaceURI.equals(nodeNamespaceURI)

+                        && entName.equals(nodeName)) {

+

+                        if (ent.getPublicId() != null

+                            || ent.getSystemId() != null

+                            || ent.getNotationName() != null) {

+                            String msg =

+                                Utils.messages.createMessage(

+                                    MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,

+                                    new Object[] { node.getNodeName()});

+

+                            if (fErrorHandler != null) {

+                                fErrorHandler.handleError(

+                                    new DOMErrorImpl(

+                                        DOMError.SEVERITY_FATAL_ERROR,

+                                        msg,

+                                        MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,

+                                        null,

+                                        null,

+                                        null));

+                            }

+                        }

+                    }

+                } //end if WFC: No External Entity References

+            }

+        }

+    } // isEntityReferneceWellFormed    

+

+    /**

+     * If the configuration parameter "namespaces" is set to true, this methods

+     * checks if an entity whose replacement text contains unbound namespace 

+     * prefixes is referenced in a location where there are no bindings for 

+     * the namespace prefixes and if so raises a LSException with the error-type

+     * "unbound-prefix-in-entity-reference"   

+     * 

+     * @param Node, The EntityReference nodes whose children are to be checked

+     */

+    protected void checkUnboundPrefixInEntRef(Node node) {

+        Node child, next;

+        for (child = node.getFirstChild(); child != null; child = next) {

+            next = child.getNextSibling();

+

+            if (child.getNodeType() == Node.ELEMENT_NODE) {

+

+                //If a NamespaceURI is not declared for the current

+                //node's prefix, raise a fatal error.

+                String prefix = child.getPrefix();

+                if (prefix != null

+                		&& fNSBinder.getURI(prefix) == null) {

+                    String msg =

+                        Utils.messages.createMessage(

+                            MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,

+                            new Object[] {

+                                node.getNodeName(),

+                                child.getNodeName(),

+                                prefix });

+

+                    if (fErrorHandler != null) {

+                        fErrorHandler.handleError(

+                            new DOMErrorImpl(

+                                DOMError.SEVERITY_FATAL_ERROR,

+                                msg,

+                                MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,

+                                null,

+                                null,

+                                null));

+                    }

+                }

+

+                NamedNodeMap attrs = child.getAttributes();

+

+                for (int i = 0; i < attrs.getLength(); i++) {

+                    String attrPrefix = attrs.item(i).getPrefix();

+                    if (attrPrefix != null

+                    		&& fNSBinder.getURI(attrPrefix) == null) {

+                        String msg =

+                            Utils.messages.createMessage(

+                                MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,

+                                new Object[] {

+                                    node.getNodeName(),

+                                    child.getNodeName(),

+                                    attrs.item(i)});

+

+                        if (fErrorHandler != null) {

+                            fErrorHandler.handleError(

+                                new DOMErrorImpl(

+                                    DOMError.SEVERITY_FATAL_ERROR,

+                                    msg,

+                                    MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,

+                                    null,

+                                    null,

+                                    null));

+                        }

+                    }

+                }

+            }

+

+            if (child.hasChildNodes()) {

+                checkUnboundPrefixInEntRef(child);

+            }

+        }

+    }

+

+    // ***********************************************************************

+    // Namespace normalization

+    // ***********************************************************************

+    /**

+     * Records local namespace declarations, to be used for normalization later

+     * 

+     * @param Node, The element node, whose namespace declarations are to be recorded

+     */

+    protected void recordLocalNSDecl(Node node) {

+        NamedNodeMap atts = ((Element) node).getAttributes();

+        int length = atts.getLength();

+

+        for (int i = 0; i < length; i++) {

+            Node attr = atts.item(i);

+

+            String localName = attr.getLocalName();

+            String attrPrefix = attr.getPrefix();

+            String attrValue = attr.getNodeValue();

+            String attrNS = attr.getNamespaceURI();

+

+            localName =

+                localName == null

+                    || XMLNS_PREFIX.equals(localName) ? "" : localName;

+            attrPrefix = attrPrefix == null ? "" : attrPrefix;

+            attrValue = attrValue == null ? "" : attrValue;

+            attrNS = attrNS == null ? "" : attrNS;

+

+            // check if attribute is a namespace decl

+            if (XMLNS_URI.equals(attrNS)) {

+

+                // No prefix may be bound to http://www.w3.org/2000/xmlns/.

+                if (XMLNS_URI.equals(attrValue)) {

+                    String msg =

+                        Utils.messages.createMessage(

+                            MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,

+                            new Object[] { attrPrefix, XMLNS_URI });

+

+                    if (fErrorHandler != null) {

+                        fErrorHandler.handleError(

+                            new DOMErrorImpl(

+                                DOMError.SEVERITY_ERROR,

+                                msg,

+                                MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,

+                                null,

+                                null,

+                                null));

+                    }

+                } else {

+                    // store the namespace-declaration

+                	if (XMLNS_PREFIX.equals(attrPrefix) ) {

+                        // record valid decl

+                        if (attrValue.length() != 0) {

+                            fNSBinder.declarePrefix(localName, attrValue);

+                        } else {

+                            // Error; xmlns:prefix=""

+                        }

+                    } else { // xmlns

+                        // empty prefix is always bound ("" or some string)

+                        fNSBinder.declarePrefix("", attrValue);

+                    }

+                }

+                

+            }

+        }

+    }

+

+    /**

+     * Fixes an element's namespace

+     * 

+     * @param Node, The element node, whose namespace is to be fixed

+     */

+    protected void fixupElementNS(Node node) throws SAXException {

+        String namespaceURI = ((Element) node).getNamespaceURI();

+        String prefix = ((Element) node).getPrefix();

+        String localName = ((Element) node).getLocalName();

+

+        if (namespaceURI != null) {

+            //if ( Element's prefix/namespace pair (or default namespace,

+            // if no prefix) are within the scope of a binding )

+            prefix = prefix == null ? "" : prefix;

+            String inScopeNamespaceURI = fNSBinder.getURI(prefix);

+

+            if ((inScopeNamespaceURI != null

+                && inScopeNamespaceURI.equals(namespaceURI))) {

+                // do nothing, declaration in scope is inherited

+                

+            } else {

+                // Create a local namespace declaration attr for this namespace,

+                // with Element's current prefix (or a default namespace, if

+                // no prefix). If there's a conflicting local declaration

+                // already present, change its value to use this namespace.

+                

+                // Add the xmlns declaration attribute

+            	//fNSBinder.pushNamespace(prefix, namespaceURI, fElementDepth);

+                if ((fFeatures & NAMESPACEDECLS) != 0) {

+                    if ("".equals(prefix) || "".equals(namespaceURI)) {

+                    	((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX, namespaceURI);

+                    } else {

+                    	((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX + ":" + prefix, namespaceURI);

+                    }

+                }

+                fLocalNSBinder.declarePrefix(prefix, namespaceURI);

+                fNSBinder.declarePrefix(prefix, namespaceURI);

+

+            } 

+        } else {

+            // Element has no namespace

+            // DOM Level 1

+            if (localName == null || "".equals(localName)) {

+                //  DOM Level 1 node!

+                String msg =

+                    Utils.messages.createMessage(

+                        MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,

+                        new Object[] { node.getNodeName()});

+

+                if (fErrorHandler != null) {

+                    fErrorHandler.handleError(

+                        new DOMErrorImpl(

+                            DOMError.SEVERITY_ERROR,

+                            msg,

+                            MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,

+                            null,

+                            null,

+                            null));

+                }

+            } else {

+            	namespaceURI = fNSBinder.getURI("");

+            	if (namespaceURI !=null && namespaceURI.length() > 0) {

+            	    ((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX, "");

+            		fLocalNSBinder.declarePrefix("", "");

+                    fNSBinder.declarePrefix("", "");

+            	}

+            }

+        }

+    }

+    /** 

+     * This table is a quick lookup of a property key (String) to the integer that

+     * is the bit to flip in the fFeatures field, so the integers should have

+     * values 1,2,4,8,16...

+     * 

+     */

+    private static final Hashtable s_propKeys = new Hashtable();

+    static {

+

+        // Initialize the mappings of property keys to bit values (Integer objects)

+        // or mappings to a String object "", which indicates we are interested

+        // in the property, but it does not have a simple bit value to flip

+

+        // cdata-sections

+        int i = CDATA;

+        Integer val = new Integer(i);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS,

+            val);

+

+        // comments

+        int i1 = COMMENTS;

+        val = new Integer(i1);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS,

+            val);

+

+        // element-content-whitespace

+        int i2 = ELEM_CONTENT_WHITESPACE;

+        val = new Integer(i2);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,

+            val);

+        int i3 = ENTITIES;

+

+        // entities

+        val = new Integer(i3);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES,

+            val);

+

+        // namespaces

+        int i4 = NAMESPACES;

+        val = new Integer(i4);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES,

+            val);

+

+        // namespace-declarations

+        int i5 = NAMESPACEDECLS;

+        val = new Integer(i5);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_NAMESPACE_DECLARATIONS,

+            val);

+

+        // split-cdata-sections

+        int i6 = SPLITCDATA;

+        val = new Integer(i6);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_SPLIT_CDATA,

+            val);

+

+        // discard-default-content	

+        int i7 = WELLFORMED;

+        val = new Integer(i7);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED,

+            val);

+

+        // discard-default-content	

+        int i8 = DISCARDDEFAULT;

+        val = new Integer(i8);

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT,

+            val);

+

+        // We are interested in these properties, but they don't have a simple

+        // bit value to deal with.

+        s_propKeys.put(

+            DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_FORMAT_PRETTY_PRINT,

+            "");

+        s_propKeys.put(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "");

+        s_propKeys.put(

+            DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION,

+            "");

+        s_propKeys.put(DOMConstants.S_XSL_OUTPUT_ENCODING, "");

+        s_propKeys.put(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, "");

+    }

+

+    /**

+     * Initializes fFeatures based on the DOMConfiguration Parameters set.

+     *

+     * @param properties DOMConfiguraiton properties that were set and which are

+     * to be used while serializing the DOM. 

+     */

+    protected void initProperties(Properties properties) {

+

+        for (Enumeration keys = properties.keys(); keys.hasMoreElements();) {

+

+            final String key = (String) keys.nextElement();

+    

+            // caonical-form

+            // Other features will be enabled or disabled when this is set to true or false.

+

+            // error-handler; set via the constructor

+

+            // infoset

+            // Other features will be enabled or disabled when this is set to true

+

+            // A quick lookup for the given set of properties (cdata-sections ...)

+            final Object iobj = s_propKeys.get(key);

+            if (iobj != null) {

+                if (iobj instanceof Integer) {

+                    // Dealing with a property that has a simple bit value that

+                    // we need to set

+

+                    // cdata-sections			

+                    // comments

+                    // element-content-whitespace

+                    // entities

+                    // namespaces

+                    // namespace-declarations

+                    // split-cdata-sections

+                    // well-formed

+                    // discard-default-content

+                    final int BITFLAG = ((Integer) iobj).intValue();

+                    if ((properties.getProperty(key).endsWith("yes"))) {

+                        fFeatures = fFeatures | BITFLAG;

+                    } else {

+                        fFeatures = fFeatures & ~BITFLAG;

+                    }

+                } else {

+                    // We are interested in the property, but it is not

+                    // a simple bit that we need to set.

+

+                    if ((DOMConstants.S_DOM3_PROPERTIES_NS

+                        + DOMConstants.DOM_FORMAT_PRETTY_PRINT)

+                        .equals(key)) {

+                        // format-pretty-print; set internally on the serializers via xsl:output properties in LSSerializer

+                        if ((properties.getProperty(key).endsWith("yes"))) {

+                            fSerializer.setIndent(true);

+                            fSerializer.setIndentAmount(3);

+                        } else {

+                            fSerializer.setIndent(false);

+                        }

+                    } else if (

+                        (DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL).equals(

+                            key)) {

+                        // omit-xml-declaration; set internally on the serializers via xsl:output properties in LSSerializer

+                        if ((properties.getProperty(key).endsWith("yes"))) {

+                            fSerializer.setOmitXMLDeclaration(true);

+                        } else {

+                            fSerializer.setOmitXMLDeclaration(false);

+                        }

+                    } else if (

+                        (

+                            DOMConstants.S_XERCES_PROPERTIES_NS

+                                + DOMConstants.S_XML_VERSION).equals(

+                            key)) {

+                        // Retreive the value of the XML Version attribute via the xml-version

+                        String version = properties.getProperty(key);

+                        if ("1.1".equals(version)) {

+                            fIsXMLVersion11 = true;

+                            fSerializer.setVersion(version);

+                        } else {

+                            fSerializer.setVersion("1.0");

+                        }

+                    } else if (

+                        (DOMConstants.S_XSL_OUTPUT_ENCODING).equals(key)) {

+                        // Retreive the value of the XML Encoding attribute

+                        String encoding = properties.getProperty(key);

+                        if (encoding != null) {

+                            fSerializer.setEncoding(encoding);

+                        }

+                    } else if ((DOMConstants.S_XERCES_PROPERTIES_NS

+                            + DOMConstants.DOM_ENTITIES).equals(key)) {

+                        // Preserve entity references in the document

+                        if ((properties.getProperty(key).endsWith("yes"))) {

+                            fSerializer.setDTDEntityExpansion(false);

+                        }

+                        else {

+                            fSerializer.setDTDEntityExpansion(true);

+                        }

+                    } else {

+                        // We shouldn't get here, ever, now what?

+                    }

+                }

+            }

+        }

+        // Set the newLine character to use

+        if (fNewLine != null) {

+            fSerializer.setOutputProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR, fNewLine);

+        }

+    }

+

+} //TreeWalker

diff --git a/src/main/java/org/apache/xml/serializer/dom3/DOMConstants.java b/src/main/java/org/apache/xml/serializer/dom3/DOMConstants.java
new file mode 100644
index 0000000..dae4f59
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/DOMConstants.java
@@ -0,0 +1,134 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+/**

+ * DOM Constants used by the DOM Level 3 LSSerializer implementation.

+ *

+ * @xsl.usage internal

+ */

+final class DOMConstants {

+    //

+    // Constants: DOM Level 3 feature ids

+    //

+    public static final String DOM3_REC_URL = "http://www.w3.org/TR/DOM-Level-3-LS";

+

+    public static final String XERCES_URL = "http://xml.apache.org/xerces-2j";

+

+    // The namespace used to qualified DOM Level 3 DOMConfiguration parameters

+    public static final String S_DOM3_PROPERTIES_NS = "{"

+            + DOMConstants.DOM3_REC_URL + "}";

+

+    public static final String S_XERCES_PROPERTIES_NS = "{"

+            + DOMConstants.XERCES_URL + "}";

+

+    // xmlns namespaces 

+    private static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";

+

+    // namespace prefix

+    private static final String XMLNS_PREFIX = "xmlns";

+

+    // ************************************************************************

+    // DOM Level 3 DOM Configuration parameter names

+    // ************************************************************************

+    // DOM Level 3 parameters defined in Core

+    public static final String DOM_CANONICAL_FORM = "canonical-form"; // Unsupported, we only appear to support this

+

+    public static final String DOM_CDATA_SECTIONS = "cdata-sections";

+

+    public static final String DOM_CHECK_CHAR_NORMALIZATION = "check-character-normalization"; // Unsupported

+

+    public static final String DOM_COMMENTS = "comments";

+

+    public static final String DOM_DATATYPE_NORMALIZATION = "datatype-normalization"; // Unsupported

+

+    public static final String DOM_ELEMENT_CONTENT_WHITESPACE = "element-content-whitespace";

+

+    public static final String DOM_ENTITIES = "entities";

+

+    public static final String DOM_INFOSET = "infoset";

+

+    public static final String DOM_NAMESPACES = "namespaces";

+

+    public static final String DOM_NAMESPACE_DECLARATIONS = "namespace-declarations";

+

+    public static final String DOM_NORMALIZE_CHARACTERS = "normalize-characters"; // Unsupported

+

+    public static final String DOM_SPLIT_CDATA = "split-cdata-sections";

+

+    public static final String DOM_VALIDATE_IF_SCHEMA = "validate-if-schema"; // Unsopported

+

+    public static final String DOM_VALIDATE = "validate"; // Unsopported

+

+    public static final String DOM_WELLFORMED = "well-formed";

+

+    // DOM Level 3 Save

+    public static final String DOM_DISCARD_DEFAULT_CONTENT = "discard-default-content";

+

+    public static final String DOM_FORMAT_PRETTY_PRINT = "format-pretty-print";

+

+    public static final String DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS = "ignore-unknown-character-denormalizations"; // Unsupported

+

+    public static final String DOM_XMLDECL = "xml-declaration";

+

+    // DOM Properties

+    public static final String DOM_ERROR_HANDLER = "error-handler";

+

+    public static final String DOM_SCHEMA_TYPE = "schema-type"; // Unsupported

+

+    public static final String DOM_SCHEMA_LOCATION = "schema-location"; // Unsupported

+

+    // ************************************************************************

+

+    // XSL Output properties

+    // The xsl:output 'indent' property used in LSSerializer 

+    public static final String S_XSL_OUTPUT_INDENT = "indent";

+

+    // The xsl:output 'indent' property used in LSSerializer 

+    public static final String S_XSL_OUTPUT_ENCODING = "encoding";

+

+    // The xsl:output 'omit-xml-declaration' property used in LSSerializer 

+    public static final String S_XSL_OUTPUT_OMIT_XML_DECL = "omit-xml-declaration";

+

+    // The xerces serializer specific 'omit-xml-declaration' property used in LSSerializer 

+    public static final String S_XML_VERSION = "xml-version";

+

+    //     

+    public static final String S_XSL_VALUE_ENTITIES = "org/apache/xml/serializer/XMLEntities";

+    

+    // Parameter values

+    public static final String DOM3_EXPLICIT_TRUE = "explicit:yes";

+

+    public static final String DOM3_DEFAULT_TRUE = "default:yes";

+

+    public static final String DOM3_EXPLICIT_FALSE = "explicit:no";

+

+    public static final String DOM3_DEFAULT_FALSE = "default:no";

+

+    // DOM Exceptions

+    public static final String DOM_EXCEPTION_FEATURE_NOT_FOUND = "FEATURE_NOT_FOUND";

+

+    public static final String DOM_EXCEPTION_FEATURE_NOT_SUPPORTED = "FEATURE_NOT_SUPPORTED";

+

+    public static final String DOM_LSEXCEPTION_SERIALIZER_ERR = "SERIALIZER_ERROR";

+

+}

diff --git a/src/main/java/org/apache/xml/serializer/dom3/DOMErrorHandlerImpl.java b/src/main/java/org/apache/xml/serializer/dom3/DOMErrorHandlerImpl.java
new file mode 100644
index 0000000..fd88266
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/DOMErrorHandlerImpl.java
@@ -0,0 +1,67 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+import org.w3c.dom.DOMError;

+import org.w3c.dom.DOMErrorHandler;

+

+/**

+ * This is the default implementation of the ErrorHandler interface and is 

+ * used if one is not provided.  The default implementation simply reports

+ * DOMErrors to System.err.

+ * 

+ * @xsl.usage internal

+ */

+final class DOMErrorHandlerImpl implements DOMErrorHandler {

+    

+    /**

+     * Default Constructor 

+     */

+    DOMErrorHandlerImpl() {

+    }

+    

+    /**

+     * Implementation of DOMErrorHandler.handleError that

+     * adds copy of error to list for later retrieval.

+     *

+     */

+    public boolean handleError(DOMError error) {

+        boolean fail = true;

+        String severity = null;

+        if (error.getSeverity() == DOMError.SEVERITY_WARNING) {

+            fail = false;

+            severity = "[Warning]";

+        } else if (error.getSeverity() == DOMError.SEVERITY_ERROR) {

+            severity = "[Error]";

+        } else if (error.getSeverity() == DOMError.SEVERITY_FATAL_ERROR) {

+            severity = "[Fatal Error]";

+        }

+        

+        System.err.println(severity + ": " + error.getMessage() + "\t");

+        System.err.println("Type : " + error.getType() + "\t" + "Related Data: "

+                + error.getRelatedData() + "\t" + "Related Exception: "

+                + error.getRelatedException() );

+        

+        return fail;

+    }

+}

+

diff --git a/src/main/java/org/apache/xml/serializer/dom3/DOMErrorImpl.java b/src/main/java/org/apache/xml/serializer/dom3/DOMErrorImpl.java
new file mode 100644
index 0000000..3895a53
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/DOMErrorImpl.java
@@ -0,0 +1,176 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+import org.w3c.dom.DOMError;

+import org.w3c.dom.DOMLocator;

+

+/**

+ * Implementation of the DOM Level 3 DOMError interface.

+ *

+ * <p>See also the <a href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ERROR-Interfaces-DOMError'>DOMError Interface definition from Document Object Model (DOM) Level 3 Core Specification</a>.

+ * 

+ * @xsl.usage internal 

+ */

+

+public final class DOMErrorImpl implements DOMError {

+    

+    /** private data members */

+    

+    // The DOMError Severity

+    private short fSeverity = DOMError.SEVERITY_WARNING;

+    

+    // The Error message

+    private String fMessage = null;

+    

+    //  A String indicating which related data is expected in relatedData. 

+    private String fType;

+    

+    // The platform related exception

+    private Exception fException = null;

+    

+    //  

+    private Object fRelatedData;

+    

+    // The location of the exception

+    private DOMLocatorImpl fLocation = new DOMLocatorImpl();

+    

+    

+    //

+    // Constructors

+    //

+    

+    /** 

+     * Default constructor. 

+     */    

+    DOMErrorImpl () {

+    }

+    

+    /**

+     * @param severity

+     * @param message

+     * @param type

+     */

+    public DOMErrorImpl(short severity, String message, String type) {

+        fSeverity = severity;

+        fMessage = message;

+        fType = type;

+    }

+    

+    /**

+     * @param severity

+     * @param message

+     * @param type

+     * @param exception

+     */

+    public DOMErrorImpl(short severity, String message, String type,

+            Exception exception) {

+        fSeverity = severity;

+        fMessage = message;

+        fType = type;

+        fException = exception;

+    }

+    

+    /**

+     * @param severity

+     * @param message

+     * @param type

+     * @param exception

+     * @param relatedData

+     * @param location

+     */

+    public DOMErrorImpl(short severity, String message, String type,

+            Exception exception, Object relatedData, DOMLocatorImpl location) {

+        fSeverity = severity;

+        fMessage = message;

+        fType = type;

+        fException = exception;

+        fRelatedData = relatedData;

+        fLocation = location;

+    }

+    

+    

+    /**

+     * The severity of the error, either <code>SEVERITY_WARNING</code>, 

+     * <code>SEVERITY_ERROR</code>, or <code>SEVERITY_FATAL_ERROR</code>.

+     * 

+     * @return A short containing the DOMError severity

+     */

+    public short getSeverity() {

+        return fSeverity;

+    }

+    

+    /**

+     * The DOMError message string.

+     * 

+     * @return String

+     */

+    public String getMessage() {

+        return fMessage;

+    }

+    

+    /**

+     * The location of the DOMError.

+     * 

+     * @return A DOMLocator object containing the DOMError location.

+     */

+    public DOMLocator getLocation() {

+        return fLocation;

+    }

+    

+    /**

+     * The related platform dependent exception if any.

+     * 

+     * @return A java.lang.Exception 

+     */

+    public Object getRelatedException(){

+        return fException;

+    }

+    

+    /**

+     * Returns a String indicating which related data is expected in relatedData.

+     * 

+     * @return A String

+     */

+    public String getType(){

+        return fType;

+    }

+    

+    /**

+     * The related DOMError.type dependent data if any.

+     * 

+     * @return java.lang.Object 

+     */

+    public Object getRelatedData(){

+        return fRelatedData;

+    }

+    

+    public void reset(){

+        fSeverity = DOMError.SEVERITY_WARNING; 

+        fException = null;

+        fMessage = null;

+        fType = null;

+        fRelatedData = null;

+        fLocation = null;        

+    }

+    

+}// class DOMErrorImpl

diff --git a/src/main/java/org/apache/xml/serializer/dom3/DOMLocatorImpl.java b/src/main/java/org/apache/xml/serializer/dom3/DOMLocatorImpl.java
new file mode 100644
index 0000000..8add00c
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/DOMLocatorImpl.java
@@ -0,0 +1,180 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+import org.w3c.dom.DOMLocator;

+import org.w3c.dom.Node;

+

+

+/**

+ * <code>DOMLocatorImpl</code> is an implementaion that describes a location (e.g. 

+ * where an error occured).

+ * <p>See also the <a href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document Object Model (DOM) Level 3 Core Specification</a>.

+ * This class is a copy of the Xerces-2J class org.apache.xerces.dom.DOMLocatorImpl.java v 1.10 

+ *

+ * @author Gopal Sharma, SUN Microsystems Inc.

+ * @version $Id: 

+ * 

+ * @xsl.usage internal

+ */

+final class DOMLocatorImpl implements DOMLocator {

+    

+    //

+    // Data

+    //

+    

+    /**

+     * The column number where the error occured, 

+     * or -1 if there is no column number available.

+     */

+    private final int fColumnNumber;

+    

+    /**

+     * The line number where the error occured, 

+     * or -1 if there is no line number available.

+     */

+    private final int fLineNumber;

+    

+    /** related data node*/

+    private final Node fRelatedNode;

+    

+    /**

+     * The URI where the error occured, 

+     * or null if there is no URI available.

+     */

+    private final String fUri;

+    

+    /**

+     * The byte offset into the input source this locator is pointing to or -1 

+     * if there is no byte offset available

+     */

+    private final int fByteOffset;

+    

+    /**

+     * The UTF-16, as defined in [Unicode] and Amendment 1 of [ISO/IEC 10646], 

+     * offset into the input source this locator is pointing to or -1 if there 

+     * is no UTF-16 offset available.

+     */

+    private final int fUtf16Offset;

+    

+    //

+    // Constructors

+    //

+    

+    DOMLocatorImpl(){

+        fColumnNumber = -1;

+        fLineNumber = -1;

+        fRelatedNode = null;

+        fUri = null;

+        fByteOffset = -1;

+        fUtf16Offset = -1;

+    }

+    

+    DOMLocatorImpl (int lineNumber, int columnNumber, String uri ){

+        fLineNumber = lineNumber ;

+        fColumnNumber = columnNumber ;

+        fUri = uri;

+        

+        fRelatedNode = null;

+        fByteOffset = -1;

+        fUtf16Offset = -1;

+    } // DOMLocatorImpl (int lineNumber, int columnNumber, String uri )

+    

+    DOMLocatorImpl (int lineNumber, int columnNumber, int utf16Offset, String uri ){

+        fLineNumber = lineNumber ;

+        fColumnNumber = columnNumber ;

+        fUri = uri;

+        fUtf16Offset = utf16Offset;

+        

+

+        fRelatedNode = null;

+        fByteOffset = -1;

+    } // DOMLocatorImpl (int lineNumber, int columnNumber, int utf16Offset, String uri )

+    

+    DOMLocatorImpl (int lineNumber, int columnNumber, int byteoffset, Node relatedData, String uri ){

+        fLineNumber = lineNumber ;

+        fColumnNumber = columnNumber ;

+        fByteOffset = byteoffset ;

+        fRelatedNode = relatedData ;

+        fUri = uri;

+        

+        fUtf16Offset = -1;

+    } // DOMLocatorImpl (int lineNumber, int columnNumber, int offset, Node errorNode, String uri )

+    

+    DOMLocatorImpl (int lineNumber, int columnNumber, int byteoffset, Node relatedData, String uri, int utf16Offset ){

+        fLineNumber = lineNumber ;

+        fColumnNumber = columnNumber ;

+        fByteOffset = byteoffset ;

+        fRelatedNode = relatedData ;

+        fUri = uri;

+        fUtf16Offset = utf16Offset;

+    } // DOMLocatorImpl (int lineNumber, int columnNumber, int offset, Node errorNode, String uri )

+    

+    

+    /**

+     * The line number where the error occured, or -1 if there is no line 

+     * number available.

+     */

+    public int getLineNumber(){

+        return fLineNumber;

+    }

+    

+    /**

+     * The column number where the error occured, or -1 if there is no column 

+     * number available.

+     */

+    public int getColumnNumber(){

+        return fColumnNumber;

+    }

+    

+    

+    /**

+     * The URI where the error occured, or null if there is no URI available.

+     */

+    public String getUri(){

+        return fUri;

+    }

+    

+    

+    public Node getRelatedNode(){

+        return fRelatedNode;

+    }

+    

+    

+    /**

+     * The byte offset into the input source this locator is pointing to or -1 

+     * if there is no byte offset available

+     */

+    public int getByteOffset(){

+        return fByteOffset;

+    }

+    

+    /**

+     * The UTF-16, as defined in [Unicode] and Amendment 1 of [ISO/IEC 10646], 

+     * offset into the input source this locator is pointing to or -1 if there 

+     * is no UTF-16 offset available.

+     */

+    public int getUtf16Offset(){

+        return fUtf16Offset;

+    }

+    

+}// class DOMLocatorImpl

diff --git a/src/main/java/org/apache/xml/serializer/dom3/DOMOutputImpl.java b/src/main/java/org/apache/xml/serializer/dom3/DOMOutputImpl.java
new file mode 100644
index 0000000..54327c0
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/DOMOutputImpl.java
@@ -0,0 +1,177 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+import org.w3c.dom.ls.LSOutput;

+

+import java.io.Writer;

+import java.io.OutputStream;

+

+/**

+ * This is a copy of the Xerces-2J class org.apache.xerces.dom.DOMOutputImpl.java

+ * 

+ * This class represents an output destination for data.

+ * This interface allows an application to encapsulate information about an

+ * output destination in a single object, which may include a URI, a byte stream

+ * (possibly with a specifiedencoding), a base URI, and/or a character stream.

+ * The exact definitions of a byte stream and a character stream are binding

+ * dependent.

+ * The application is expected to provide objects that implement this interface

+ * whenever such objects are needed. The application can either provide its

+ * own objects that implement this interface, or it can use the generic factory

+ * method DOMImplementationLS.createLSOutput() to create objects that

+ * implement this interface.

+ * The DOMSerializer will use the LSOutput object to determine where to

+ * serialize the output to. The DOMSerializer will look at the different

+ * outputs specified in the LSOutput in the following order to know which one

+ * to output to, the first one that data can be output to will be used:

+ * 1.LSOutput.characterStream

+ * 2.LSOutput.byteStream

+ * 3.LSOutput.systemId

+ * LSOutput objects belong to the application. The DOM implementation will

+ * never modify them (though it may make copies and modify the copies,

+ * if necessary).

+ *

+ *

+ * @author Arun Yadav, Sun Microsytems

+ * @author Gopal Sharma, Sun Microsystems

+ * @version $Id : 

+ * @xsl.usage internal 

+ */

+

+final class DOMOutputImpl implements LSOutput {

+    

+    private Writer fCharStream = null;

+    private OutputStream fByteStream = null;

+    private String fSystemId = null;

+    private String fEncoding = null;

+    

+    /**

+     * Default Constructor

+     */

+    DOMOutputImpl() {}

+    

+    /**

+     * An attribute of a language and binding dependent type that represents a

+     * writable stream of bytes. If the application knows the character encoding

+     * of the byte stream, it should set the encoding attribute. Setting the

+     * encoding in this way will override any encoding specified in an XML

+     * declaration in the data.

+     */

+    

+    public Writer getCharacterStream(){

+        return fCharStream;

+    };

+    

+    /**

+     * An attribute of a language and binding dependent type that represents a

+     * writable stream of bytes. If the application knows the character encoding

+     * of the byte stream, it should set the encoding attribute. Setting the

+     * encoding in this way will override any encoding specified in an XML

+     * declaration in the data.

+     */

+    

+    public void setCharacterStream(Writer characterStream){

+        fCharStream = characterStream;

+    };

+    

+    /**

+     * Depending on the language binding in use, this attribute may not be

+     * available. An attribute of a language and binding dependent type that

+     * represents a writable stream to which 16-bit units can be output. The

+     * application must encode the stream using UTF-16 (defined in [Unicode] and

+     *  Amendment 1 of [ISO/IEC 10646]).

+     */

+    

+    public OutputStream getByteStream(){

+        return fByteStream;

+    };

+    

+    /**

+     * Depending on the language binding in use, this attribute may not be

+     * available. An attribute of a language and binding dependent type that

+     * represents a writable stream to which 16-bit units can be output. The

+     * application must encode the stream using UTF-16 (defined in [Unicode] and

+     *  Amendment 1 of [ISO/IEC 10646]).

+     */

+    

+    public void setByteStream(OutputStream byteStream){

+        fByteStream = byteStream;

+    };

+    

+    /**

+     * The system identifier, a URI reference [IETF RFC 2396], for this output

+     *  destination. If the application knows the character encoding of the

+     *  object pointed to by the system identifier, it can set the encoding

+     *  using the encoding attribute. If the system ID is a relative URI

+     *  reference (see section 5 in [IETF RFC 2396]), the behavior is

+     *  implementation dependent.

+     */

+    

+    public String getSystemId(){

+        return fSystemId;

+    };

+    

+    /**

+     * The system identifier, a URI reference [IETF RFC 2396], for this output

+     *  destination. If the application knows the character encoding of the

+     *  object pointed to by the system identifier, it can set the encoding

+     *  using the encoding attribute. If the system ID is a relative URI

+     *  reference (see section 5 in [IETF RFC 2396]), the behavior is

+     *  implementation dependent.

+     */

+    

+    public void setSystemId(String systemId){

+        fSystemId = systemId;

+    };

+    

+    /**

+     * The character encoding, if known. The encoding must be a string

+     * acceptable for an XML encoding declaration ([XML 1.0] section 4.3.3

+     * "Character Encoding in Entities"). This attribute has no effect when the

+     * application provides a character stream or string data. For other sources

+     * of input, an encoding specified by means of this attribute will override

+     * any encoding specified in the XML declaration or the Text declaration, or

+     * an encoding obtained from a higher level protocol, such as HTTP

+     * [IETF RFC 2616].

+     */

+    

+    public String getEncoding(){

+        return fEncoding;

+    };

+    

+    /**

+     * The character encoding, if known. The encoding must be a string

+     * acceptable for an XML encoding declaration ([XML 1.0] section 4.3.3

+     * "Character Encoding in Entities"). This attribute has no effect when the

+     * application provides a character stream or string data. For other sources

+     * of input, an encoding specified by means of this attribute will override

+     * any encoding specified in the XML declaration or the Text declaration, or

+     * an encoding obtained from a higher level protocol, such as HTTP

+     * [IETF RFC 2616].

+     */

+    

+    public void setEncoding(String encoding){

+        fEncoding = encoding;

+    };

+    

+}//DOMOutputImpl

diff --git a/src/main/java/org/apache/xml/serializer/dom3/DOMStringListImpl.java b/src/main/java/org/apache/xml/serializer/dom3/DOMStringListImpl.java
new file mode 100644
index 0000000..5ace83a
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/DOMStringListImpl.java
@@ -0,0 +1,99 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+package org.apache.xml.serializer.dom3;

+

+import java.util.Vector;

+

+//import org.apache.xerces.dom3.DOMStringList;

+import org.w3c.dom.DOMStringList;

+

+/**

+ * This class implemets the DOM Level 3 Core interface DOMStringList.

+ * 

+ * @xsl.usage internal

+ */

+final class DOMStringListImpl implements DOMStringList {

+    

+    //A collection of DOMString values

+    private Vector fStrings;

+    

+    /** 

+     * Construct an empty list of DOMStringListImpl

+     */ 

+    DOMStringListImpl() {

+        fStrings = new Vector();    

+    }

+    

+    /** 

+     * Construct an empty list of DOMStringListImpl

+     */ 

+    DOMStringListImpl(Vector params) {

+        fStrings = params;    

+    }

+    

+    /** 

+     * Construct an empty list of DOMStringListImpl

+     */ 

+    DOMStringListImpl(String[] params ) {

+        fStrings = new Vector();

+        if (params != null) {

+            for (int i=0; i < params.length; i++) {

+                fStrings.add(params[i]);    

+            }

+        }

+    }

+    

+    /**

+     * @see org.apache.xerces.dom3.DOMStringList#item(int)

+     */

+    public String item(int index) {

+        try {

+            return (String) fStrings.elementAt(index);

+        } catch (ArrayIndexOutOfBoundsException e) {

+            return null;

+        }

+    }

+    

+    /**

+     * @see org.apache.xerces.dom3.DOMStringList#getLength()

+     */

+    public int getLength() {

+        return fStrings.size();

+    }

+    

+    /**

+     * @see org.apache.xerces.dom3.DOMStringList#contains(String)

+     */

+    public boolean contains(String param) {

+        return fStrings.contains(param) ;

+    }

+    

+    /**

+     * DOM Internal:

+     * Add a <code>DOMString</code> to the list.

+     * 

+     * @param domString A string to add to the list

+     */

+    public void add(String param) {

+        fStrings.add(param);

+    }

+    

+}

diff --git a/src/main/java/org/apache/xml/serializer/dom3/LSSerializerImpl.java b/src/main/java/org/apache/xml/serializer/dom3/LSSerializerImpl.java
new file mode 100644
index 0000000..acac6fc
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/LSSerializerImpl.java
@@ -0,0 +1,1537 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+import java.io.FileOutputStream;

+import java.io.OutputStream;

+import java.io.StringWriter;

+import java.io.UnsupportedEncodingException;

+import java.io.Writer;

+import java.net.HttpURLConnection;

+import java.net.URL;

+import java.net.URLConnection;

+import java.security.AccessController;

+import java.security.PrivilegedAction;

+import java.util.Properties;

+import java.util.StringTokenizer;

+

+import org.apache.xml.serializer.DOM3Serializer;

+import org.apache.xml.serializer.Encodings;

+import org.apache.xml.serializer.OutputPropertiesFactory;

+import org.apache.xml.serializer.Serializer;

+import org.apache.xml.serializer.SerializerFactory;

+import org.apache.xml.serializer.utils.MsgKey;

+import org.apache.xml.serializer.utils.SystemIDResolver;

+import org.apache.xml.serializer.utils.Utils;

+import org.w3c.dom.DOMConfiguration;

+import org.w3c.dom.DOMError;

+import org.w3c.dom.DOMErrorHandler;

+import org.w3c.dom.DOMException;

+import org.w3c.dom.DOMStringList;

+import org.w3c.dom.Document;

+import org.w3c.dom.Node;

+import org.w3c.dom.ls.LSException;

+import org.w3c.dom.ls.LSOutput;

+import org.w3c.dom.ls.LSSerializer;

+import org.w3c.dom.ls.LSSerializerFilter;

+

+/**

+ * Implemenatation of DOM Level 3 org.w3c.ls.LSSerializer and 

+ * org.w3c.dom.ls.DOMConfiguration.  Serialization is achieved by delegating 

+ * serialization calls to <CODE>org.apache.xml.serializer.ToStream</CODE> or 

+ * one of its derived classes depending on the serialization method, while walking

+ * the DOM in DOM3TreeWalker.  

+ * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html#LS-LSSerializer">org.w3c.dom.ls.LSSerializer</a>

+ * @see <a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#DOMConfiguration">org.w3c.dom.DOMConfiguration</a>

+ *  

+ * @version $Id:  

+ * 

+ * @xsl.usage internal 

+ */

+final public class LSSerializerImpl implements DOMConfiguration, LSSerializer {

+    

+    // The default end-of-line character sequence used in serialization.

+    private static final String DEFAULT_END_OF_LINE;

+    static {

+        String lineSeparator = (String) AccessController.doPrivileged(new PrivilegedAction() {

+            public Object run() {

+                try {

+                    return System.getProperty("line.separator");

+                }

+                catch (SecurityException ex) {}

+                return null;

+            }

+        });

+        // The DOM Level 3 Load and Save specification requires that implementations choose a default

+        // sequence which matches one allowed by XML 1.0 (or XML 1.1). If the value of "line.separator" 

+        // isn't one of the XML 1.0 end-of-line sequences then we select "\n" as the default value.

+        DEFAULT_END_OF_LINE = lineSeparator != null && 

+            (lineSeparator.equals("\r\n") || lineSeparator.equals("\r")) ? lineSeparator : "\n";

+    }

+    

+    /** private data members */

+    private Serializer fXMLSerializer = null;

+    

+    // Tracks DOMConfiguration features. 

+    protected int fFeatures = 0;

+    

+    // Common DOM serializer

+    private  DOM3Serializer fDOMSerializer = null;

+    

+    // A filter set on the LSSerializer

+    private LSSerializerFilter fSerializerFilter = null;  

+    

+    // Stores the nodeArg parameter to speed up multiple writes of the same node.

+    private Node fVisitedNode = null;

+    

+    // The end-of-line character sequence used in serialization. "\n" is whats used on the web.

+    private String fEndOfLine = DEFAULT_END_OF_LINE;

+    

+    // The DOMErrorhandler.

+    private DOMErrorHandler fDOMErrorHandler = null;

+    

+    // The Configuration parameter to pass to the Underlying serilaizer.

+    private Properties fDOMConfigProperties = null;

+    

+    // The encoding to use during serialization.

+    private String fEncoding; 

+	

+    // ************************************************************************

+    // DOM Level 3 DOM Configuration parameter names

+    // ************************************************************************    

+    // Parameter canonical-form, true [optional] - NOT SUPPORTED 

+    private final static int CANONICAL = 0x1 << 0;

+    

+    // Parameter cdata-sections, true [required] (default)

+    private final static int CDATA = 0x1 << 1;

+    

+    // Parameter check-character-normalization, true [optional] - NOT SUPPORTED 

+    private final static int CHARNORMALIZE = 0x1 << 2;

+    

+    // Parameter comments, true [required] (default)

+    private final static int COMMENTS = 0x1 << 3;

+    

+    // Parameter datatype-normalization, true [optional] - NOT SUPPORTED

+    private final static int DTNORMALIZE = 0x1 << 4;    

+    

+    // Parameter element-content-whitespace, true [required] (default) - value - false [optional] NOT SUPPORTED

+    private final static int ELEM_CONTENT_WHITESPACE = 0x1 << 5;

+    

+    // Parameter entities, true [required] (default)

+    private final static int ENTITIES = 0x1 << 6;

+    

+    // Parameter infoset, true [required] (default), false has no effect --> True has no effect for the serializer

+    private final static int INFOSET = 0x1 << 7;

+    

+    // Parameter namespaces, true [required] (default)

+    private final static int NAMESPACES = 0x1 << 8;

+    

+    // Parameter namespace-declarations, true [required] (default)

+    private final static int NAMESPACEDECLS = 0x1 << 9;

+    

+    // Parameter normalize-characters, true [optional] - NOT SUPPORTED

+    private final static int NORMALIZECHARS = 0x1 << 10;

+    

+    // Parameter split-cdata-sections, true [required] (default)

+    private final static int SPLITCDATA = 0x1 << 11;   

+    

+    // Parameter validate, true [optional] - NOT SUPPORTED

+    private final static int VALIDATE = 0x1 << 12;   

+    

+    // Parameter validate-if-schema, true [optional] - NOT SUPPORTED

+    private final static int SCHEMAVALIDATE = 0x1 << 13;

+    

+    // Parameter split-cdata-sections, true [required] (default)

+    private final static int WELLFORMED = 0x1 << 14;   

+    

+    // Parameter discard-default-content, true [required] (default)

+    // Not sure how this will be used in level 2 Documents

+    private final static int DISCARDDEFAULT = 0x1 << 15;       

+    

+    // Parameter format-pretty-print, true [optional] 

+    private final static int PRETTY_PRINT = 0x1 << 16;

+    

+    // Parameter ignore-unknown-character-denormalizations, true [required] (default)

+    // We currently do not support XML 1.1 character normalization

+    private final static int IGNORE_CHAR_DENORMALIZE = 0x1 << 17;

+    

+    // Parameter discard-default-content, true [required] (default)

+    private final static int XMLDECL = 0x1 << 18;    

+    // ************************************************************************

+    

+    // Recognized parameters for which atleast one value can be set

+    private String fRecognizedParameters [] = {

+            DOMConstants.DOM_CANONICAL_FORM,

+            DOMConstants.DOM_CDATA_SECTIONS,

+            DOMConstants.DOM_CHECK_CHAR_NORMALIZATION,

+            DOMConstants.DOM_COMMENTS,

+            DOMConstants.DOM_DATATYPE_NORMALIZATION,

+            DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,

+            DOMConstants.DOM_ENTITIES,

+            DOMConstants.DOM_INFOSET,

+            DOMConstants.DOM_NAMESPACES,

+            DOMConstants.DOM_NAMESPACE_DECLARATIONS,

+            //DOMConstants.DOM_NORMALIZE_CHARACTERS,

+            DOMConstants.DOM_SPLIT_CDATA,

+            DOMConstants.DOM_VALIDATE,

+            DOMConstants.DOM_VALIDATE_IF_SCHEMA,

+            DOMConstants.DOM_WELLFORMED,

+            DOMConstants.DOM_DISCARD_DEFAULT_CONTENT,

+            DOMConstants.DOM_FORMAT_PRETTY_PRINT,

+            DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS,

+            DOMConstants.DOM_XMLDECL,

+            DOMConstants.DOM_ERROR_HANDLER

+    };

+    

+    

+    /**

+     * Constructor:  Creates a LSSerializerImpl object.  The underlying

+     * XML 1.0 or XML 1.1 org.apache.xml.serializer.Serializer object is

+     * created and initialized the first time any of the write methods are  

+     * invoked to serialize the Node.  Subsequent write methods on the same

+     * LSSerializerImpl object will use the previously created Serializer object.

+     */

+    public LSSerializerImpl () {

+        // set default parameters

+        fFeatures |= CDATA;

+        fFeatures |= COMMENTS;

+        fFeatures |= ELEM_CONTENT_WHITESPACE;

+        fFeatures |= ENTITIES;

+        fFeatures |= NAMESPACES;

+        fFeatures |= NAMESPACEDECLS;

+        fFeatures |= SPLITCDATA;

+        fFeatures |= WELLFORMED;

+        fFeatures |= DISCARDDEFAULT;

+        fFeatures |= XMLDECL;

+        

+        // New OutputFormat properties

+        fDOMConfigProperties = new Properties();

+        

+        // Initialize properties to be passed on the underlying serializer

+        initializeSerializerProps();

+        

+        // Create the underlying serializer.

+        Properties  configProps = OutputPropertiesFactory.getDefaultMethodProperties("xml");

+        

+        // change xml version from 1.0 to 1.1

+        //configProps.setProperty("version", "1.1");

+        

+        // Get a serializer that seriailizes according the the properties,

+        // which in this case is to xml

+        fXMLSerializer = SerializerFactory.getSerializer(configProps);

+        

+        // Initialize Serializer

+        fXMLSerializer.setOutputFormat(fDOMConfigProperties);

+    }

+    

+    /**

+     * Initializes the underlying serializer's configuration depending on the

+     * default DOMConfiguration parameters. This method must be called before a

+     * node is to be serialized.

+     * 

+     * @xsl.usage internal

+     */

+    public void initializeSerializerProps () {

+        // canonical-form

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_CANONICAL_FORM, DOMConstants.DOM3_DEFAULT_FALSE);

+        

+        // cdata-sections

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_DEFAULT_TRUE);

+        

+        // "check-character-normalization"

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_CHECK_CHAR_NORMALIZATION,

+                DOMConstants.DOM3_DEFAULT_FALSE);

+        

+        // comments

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_DEFAULT_TRUE);

+        

+        // datatype-normalization

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_DATATYPE_NORMALIZATION,

+                DOMConstants.DOM3_DEFAULT_FALSE);

+        

+        // element-content-whitespace

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,

+                DOMConstants.DOM3_DEFAULT_TRUE);

+        

+        // entities

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_TRUE);

+        // preserve entities

+        fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS

+                + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_TRUE);

+

+        // error-handler

+        // Should we set our default ErrorHandler

+        /*

+         * if (fDOMConfig.getParameter(Constants.DOM_ERROR_HANDLER) != null) {

+         * fDOMErrorHandler =

+         * (DOMErrorHandler)fDOMConfig.getParameter(Constants.DOM_ERROR_HANDLER); }

+         */

+        

+        // infoset

+        if ((fFeatures & INFOSET) != 0) {

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_DEFAULT_TRUE);

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_NAMESPACE_DECLARATIONS,

+                    DOMConstants.DOM3_DEFAULT_TRUE);

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_DEFAULT_TRUE);

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,

+                    DOMConstants.DOM3_DEFAULT_TRUE);

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_DEFAULT_TRUE);

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_FALSE);

+            // preserve entities

+            fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS

+                    + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_DEFAULT_FALSE);

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_CDATA_SECTIONS,

+                    DOMConstants.DOM3_DEFAULT_FALSE);

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_VALIDATE_IF_SCHEMA,

+                    DOMConstants.DOM3_DEFAULT_FALSE);

+            fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                    + DOMConstants.DOM_DATATYPE_NORMALIZATION,

+                    DOMConstants.DOM3_DEFAULT_FALSE);

+        }

+        

+        // namespaces

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_DEFAULT_TRUE);

+        

+        // namespace-declarations

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_NAMESPACE_DECLARATIONS,

+                DOMConstants.DOM3_DEFAULT_TRUE);

+        

+        // normalize-characters

+        /*

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_NORMALIZE_CHARACTERS,

+                DOMConstants.DOM3_DEFAULT_FALSE);

+        */

+        

+        // split-cdata-sections

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_DEFAULT_TRUE);

+        

+        // validate

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_VALIDATE, DOMConstants.DOM3_DEFAULT_FALSE);

+        

+        // validate-if-schema

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_VALIDATE_IF_SCHEMA,

+                DOMConstants.DOM3_DEFAULT_FALSE);

+        

+        // well-formed

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_DEFAULT_TRUE);

+        

+        // pretty-print

+        fDOMConfigProperties.setProperty(

+                DOMConstants.S_XSL_OUTPUT_INDENT,

+                DOMConstants.DOM3_DEFAULT_TRUE);

+        fDOMConfigProperties.setProperty(

+                OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, Integer.toString(3));

+        

+        // 

+        

+        // discard-default-content

+        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS

+                + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT,

+                DOMConstants.DOM3_DEFAULT_TRUE);

+        

+        // xml-declaration

+        fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "no");

+        

+    }    

+    

+    // ************************************************************************

+    // DOMConfiguraiton implementation

+    // ************************************************************************

+    

+    /** 

+     * Checks if setting a parameter to a specific value is supported.    

+     *  

+     * @see org.w3c.dom.DOMConfiguration#canSetParameter(java.lang.String, java.lang.Object)

+     * @since DOM Level 3

+     * @param name A String containing the DOMConfiguration parameter name.

+     * @param value An Object specifying the value of the corresponding parameter. 

+     */

+    public boolean canSetParameter(String name, Object value) {

+        if (value instanceof Boolean){

+            if ( name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES)    

+                    || name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED)    

+                    || name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)                

+                    || name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)){

+                // both values supported

+                return true;

+            }

+            else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)

+                    // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)

+                    ) {

+                // true is not supported

+                return !((Boolean)value).booleanValue();

+            }

+            else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) {

+                // false is not supported

+                return ((Boolean)value).booleanValue();

+            }

+        }

+        else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER) &&

+                value == null || value instanceof DOMErrorHandler){

+            return true;

+        }

+        return false;

+    }

+    /**

+     * This method returns the value of a parameter if known.

+     * 

+     * @see org.w3c.dom.DOMConfiguration#getParameter(java.lang.String)

+     * 

+     * @param name A String containing the DOMConfiguration parameter name 

+     *             whose value is to be returned.

+     * @return Object The value of the parameter if known. 

+     */

+    public Object getParameter(String name) throws DOMException {

+        if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS)) {

+            return ((fFeatures & COMMENTS) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS)) {

+            return ((fFeatures & CDATA) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES)) {

+            return ((fFeatures & ENTITIES) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES)) {

+            return ((fFeatures & NAMESPACES) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS)) {

+            return ((fFeatures & NAMESPACEDECLS) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA)) {

+            return ((fFeatures & SPLITCDATA) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED)) {

+            return ((fFeatures & WELLFORMED) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        }  else if (name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT)) {

+            return ((fFeatures & DISCARDDEFAULT) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) {

+            return ((fFeatures & PRETTY_PRINT) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)) {

+            return ((fFeatures & XMLDECL) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE)) {

+            return ((fFeatures & ELEM_CONTENT_WHITESPACE) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) {

+            return ((fFeatures & PRETTY_PRINT) != 0) ? Boolean.TRUE : Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) {

+            return Boolean.TRUE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)

+                || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION)

+                || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION) 

+                // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)                

+                || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)

+                || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) {

+            return Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)){

+            if ((fFeatures & ENTITIES) == 0 &&

+                    (fFeatures & CDATA) == 0 &&

+                    (fFeatures & ELEM_CONTENT_WHITESPACE) != 0 &&

+                    (fFeatures & NAMESPACES) != 0 &&

+                    (fFeatures & NAMESPACEDECLS) != 0 &&

+                    (fFeatures & WELLFORMED) != 0 &&

+                    (fFeatures & COMMENTS) != 0) {

+                return Boolean.TRUE;

+            }                 

+            return Boolean.FALSE;

+        } else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER)) {

+            return fDOMErrorHandler;

+        } else if (

+                name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION)

+                || name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) {

+            return null;

+        } else {

+            // Here we have to add the Xalan specific DOM Message Formatter

+            String msg = Utils.messages.createMessage(

+                    MsgKey.ER_FEATURE_NOT_FOUND,

+                    new Object[] { name });

+            throw new DOMException(DOMException.NOT_FOUND_ERR, msg);

+        }

+    }

+    

+    /**

+     * This method returns a of the parameters supported by this DOMConfiguration object 

+     * and for which at least one value can be set by the application

+     * 

+     * @see org.w3c.dom.DOMConfiguration#getParameterNames()

+     * 

+     * @return DOMStringList A list of DOMConfiguration parameters recognized

+     *                       by the serializer

+     */

+    public DOMStringList getParameterNames() {

+        return new DOMStringListImpl(fRecognizedParameters);

+    }

+    

+    /**

+     * This method sets the value of the named parameter.

+     *   

+     * @see org.w3c.dom.DOMConfiguration#setParameter(java.lang.String, java.lang.Object)

+     * 

+     * @param name A String containing the DOMConfiguration parameter name.

+     * @param value An Object contaiing the parameters value to set.

+     */

+    public void setParameter(String name, Object value) throws DOMException {

+        // If the value is a boolean

+        if (value instanceof Boolean) {

+            boolean state = ((Boolean) value).booleanValue();

+            

+            if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS)) {

+                fFeatures = state ? fFeatures | COMMENTS : fFeatures

+                        & ~COMMENTS;

+                // comments

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_FALSE);

+                }                

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS)) {

+                fFeatures =  state ? fFeatures | CDATA : fFeatures

+                        & ~CDATA;

+                // cdata-sections

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_FALSE);

+                }

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES)) {

+                fFeatures = state ? fFeatures | ENTITIES : fFeatures

+                        & ~ENTITIES;

+                // entities

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_TRUE);

+                    fDOMConfigProperties.setProperty(

+                            DOMConstants.S_XERCES_PROPERTIES_NS

+                            + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    fDOMConfigProperties.setProperty(

+                            DOMConstants.S_XERCES_PROPERTIES_NS

+                            + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE);

+                }

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES)) {

+                fFeatures = state ? fFeatures | NAMESPACES : fFeatures

+                        & ~NAMESPACES;

+                // namespaces

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_FALSE); 

+                }       

+            } else if (name

+                    .equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS)) {

+                fFeatures = state ? fFeatures | NAMESPACEDECLS

+                        : fFeatures & ~NAMESPACEDECLS;

+                // namespace-declarations

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_FALSE); 

+                } 

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA)) {

+                fFeatures = state ? fFeatures | SPLITCDATA : fFeatures

+                        & ~SPLITCDATA;

+                // split-cdata-sections

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_SPLIT_CDATA, DOMConstants.DOM3_EXPLICIT_FALSE); 

+                }  

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED)) {

+                fFeatures = state ? fFeatures | WELLFORMED : fFeatures

+                        & ~WELLFORMED;

+                // well-formed

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_FALSE); 

+                }                  

+            } else if (name

+                    .equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT)) {

+                fFeatures = state ? fFeatures | DISCARDDEFAULT

+                        : fFeatures & ~DISCARDDEFAULT;

+                // discard-default-content

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT, DOMConstants.DOM3_EXPLICIT_FALSE); 

+                }                    

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT)) {

+                fFeatures = state ? fFeatures | PRETTY_PRINT : fFeatures

+                        & ~PRETTY_PRINT;

+                // format-pretty-print

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_FORMAT_PRETTY_PRINT, DOMConstants.DOM3_EXPLICIT_TRUE);

+                }

+                else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_FORMAT_PRETTY_PRINT, DOMConstants.DOM3_EXPLICIT_FALSE);

+                }

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL)) {

+                fFeatures = state ? fFeatures | XMLDECL : fFeatures

+                        & ~XMLDECL;

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "no");

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "yes"); 

+                }       

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE)) {

+                fFeatures = state ? fFeatures | ELEM_CONTENT_WHITESPACE : fFeatures

+                        & ~ELEM_CONTENT_WHITESPACE;

+                // element-content-whitespace

+                if (state) {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_TRUE);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_FALSE);

+                }            

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) {

+                // false is not supported

+                if (!state) {

+                    // Here we have to add the Xalan specific DOM Message Formatter

+                    String msg = Utils.messages.createMessage(

+                            MsgKey.ER_FEATURE_NOT_SUPPORTED,

+                            new Object[] { name });

+                    throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);

+                } else {

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS, DOMConstants.DOM3_EXPLICIT_TRUE);

+                }

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION)

+                    || name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION)

+                    // || name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)

+                    ) {

+                // true is not supported

+                if (state) {

+                    String msg = Utils.messages.createMessage(

+                            MsgKey.ER_FEATURE_NOT_SUPPORTED,

+                            new Object[] { name });

+                    throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);

+                } else {

+                    if (name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM)) {

+                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                                + DOMConstants.DOM_CANONICAL_FORM, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) {

+                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                                + DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE)) {

+                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                                + DOMConstants.DOM_VALIDATE, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    } else if (name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA)) {

+                        fDOMConfigProperties.setProperty(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION 

+                                + DOMConstants.DOM_CHECK_CHAR_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    } else if (name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION)) {

+                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                                + DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    } /* else if (name.equalsIgnoreCase(DOMConstants.DOM_NORMALIZE_CHARACTERS)) {

+                        fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                                + DOMConstants.DOM_NORMALIZE_CHARACTERS, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    } */

+                }

+            } else if (name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)) {

+                // infoset

+                if (state) {

+                    fFeatures &= ~ENTITIES;

+                    fFeatures &= ~CDATA;

+                    fFeatures &= ~SCHEMAVALIDATE;

+                    fFeatures &= ~DTNORMALIZE;

+                    fFeatures |= NAMESPACES;

+                    fFeatures |= NAMESPACEDECLS;

+                    fFeatures |= WELLFORMED;

+                    fFeatures |= ELEM_CONTENT_WHITESPACE;

+                    fFeatures |= COMMENTS;

+                    

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_NAMESPACES, DOMConstants.DOM3_EXPLICIT_TRUE); 

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_NAMESPACE_DECLARATIONS, DOMConstants.DOM3_EXPLICIT_TRUE);

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_COMMENTS, DOMConstants.DOM3_EXPLICIT_TRUE);

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE, DOMConstants.DOM3_EXPLICIT_TRUE);

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_WELLFORMED, DOMConstants.DOM3_EXPLICIT_TRUE);

+                    

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS

+                            + DOMConstants.DOM_ENTITIES, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_CDATA_SECTIONS, DOMConstants.DOM3_EXPLICIT_FALSE);

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_VALIDATE_IF_SCHEMA, DOMConstants.DOM3_EXPLICIT_FALSE);            

+                    fDOMConfigProperties.setProperty(DOMConstants.S_DOM3_PROPERTIES_NS 

+                            + DOMConstants.DOM_DATATYPE_NORMALIZATION, DOMConstants.DOM3_EXPLICIT_FALSE);

+                }

+            } else {

+                // If this is a non-boolean parameter a type mismatch should be thrown.

+                if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) {

+                    String msg = Utils.messages.createMessage(

+                            MsgKey.ER_TYPE_MISMATCH_ERR,

+                            new Object[] { name });

+                    throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg);

+                }

+                

+                // Parameter is not recognized

+                String msg = Utils.messages.createMessage(

+                        MsgKey.ER_FEATURE_NOT_FOUND,

+                        new Object[] { name });

+                throw new DOMException(DOMException.NOT_FOUND_ERR, msg);

+            }

+        } // If the parameter value is not a boolean 

+        else if (name.equalsIgnoreCase(DOMConstants.DOM_ERROR_HANDLER)) {

+            if (value == null || value instanceof DOMErrorHandler) {

+                fDOMErrorHandler = (DOMErrorHandler)value;

+            } else {

+                String msg = Utils.messages.createMessage(

+                        MsgKey.ER_TYPE_MISMATCH_ERR,

+                        new Object[] { name });

+                throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg);

+            }

+        } else if (

+                name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_LOCATION)

+                || name.equalsIgnoreCase(DOMConstants.DOM_SCHEMA_TYPE)) {

+            if (value != null) {

+                if (!(value instanceof String)) {

+                    String msg = Utils.messages.createMessage(

+                            MsgKey.ER_TYPE_MISMATCH_ERR,

+                            new Object[] { name });

+                    throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg);

+                }

+                String msg = Utils.messages.createMessage(

+                        MsgKey.ER_FEATURE_NOT_SUPPORTED,

+                        new Object[] { name });

+                throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);

+            }

+        } else {

+            // If this is a boolean parameter a type mismatch should be thrown.

+            if (name.equalsIgnoreCase(DOMConstants.DOM_COMMENTS) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_CDATA_SECTIONS) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_ENTITIES) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACES) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_NAMESPACE_DECLARATIONS) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_SPLIT_CDATA) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_WELLFORMED) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_DISCARD_DEFAULT_CONTENT) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_FORMAT_PRETTY_PRINT) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_XMLDECL) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_CANONICAL_FORM) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE_IF_SCHEMA) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_VALIDATE) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_CHECK_CHAR_NORMALIZATION) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_DATATYPE_NORMALIZATION) ||

+                    name.equalsIgnoreCase(DOMConstants.DOM_INFOSET)) {

+                String msg = Utils.messages.createMessage(

+                        MsgKey.ER_TYPE_MISMATCH_ERR,

+                        new Object[] { name });

+                throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg);

+            }

+            

+            // Parameter is not recognized

+            String msg = Utils.messages.createMessage(

+                    MsgKey.ER_FEATURE_NOT_FOUND,

+                    new Object[] { name });

+            throw new DOMException(DOMException.NOT_FOUND_ERR, msg);

+        }

+    }

+    // ************************************************************************

+    

+    

+    // ************************************************************************

+    // DOMConfiguraiton implementation

+    // ************************************************************************

+    

+    /** 

+     * Returns the DOMConfiguration of the LSSerializer.

+     *  

+     * @see org.w3c.dom.ls.LSSerializer#getDomConfig()

+     * @since DOM Level 3

+     * @return A DOMConfiguration object.

+     */

+    public DOMConfiguration getDomConfig() {

+        return (DOMConfiguration)this;

+    }

+    

+    /** 

+     * Returns the DOMConfiguration of the LSSerializer.

+     *  

+     * @see org.w3c.dom.ls.LSSerializer#getFilter()

+     * @since DOM Level 3

+     * @return A LSSerializerFilter object.

+     */

+    public LSSerializerFilter getFilter() {

+        return fSerializerFilter;

+    }

+    

+    /** 

+     * Returns the End-Of-Line sequence of characters to be used in the XML 

+     * being serialized.  If none is set a default "\n" is returned.

+     * 

+     * @see org.w3c.dom.ls.LSSerializer#getNewLine()

+     * @since DOM Level 3

+     * @return A String containing the end-of-line character sequence  used in 

+     * serialization.

+     */

+    public String getNewLine() {

+        return fEndOfLine;

+    }

+    

+    /** 

+     * Set a LSSerilizerFilter on the LSSerializer.  When set, the filter is

+     * called before each node is serialized which depending on its implemention

+     * determines if the node is to be serialized or not.    

+     *  

+     * @see org.w3c.dom.ls.LSSerializer#setFilter

+     * @since DOM Level 3

+     * @param filter A LSSerializerFilter to be applied to the stream to serialize.

+     */

+    public void setFilter(LSSerializerFilter filter) {

+        fSerializerFilter = filter;

+    }

+    

+    /** 

+     * Sets the End-Of-Line sequence of characters to be used in the XML 

+     * being serialized.  Setting this attribute to null will reset its 

+     * value to the default value i.e. "\n".

+     * 

+     * @see org.w3c.dom.ls.LSSerializer#setNewLine

+     * @since DOM Level 3

+     * @param newLine a String that is the end-of-line character sequence to be used in 

+     * serialization.

+     */

+    public void setNewLine(String newLine) {

+        fEndOfLine = (newLine != null) ? newLine : DEFAULT_END_OF_LINE;

+    }

+    

+    /** 

+     * Serializes the specified node to the specified LSOutput and returns true if the Node 

+     * was successfully serialized. 

+     * 

+     * @see org.w3c.dom.ls.LSSerializer#write(org.w3c.dom.Node, org.w3c.dom.ls.LSOutput)

+     * @since DOM Level 3

+     * @param nodeArg The Node to serialize.

+     * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the 

+     * LSSerializer was unable to serialize the node.

+     *      

+     */

+    public boolean write(Node nodeArg, LSOutput destination) throws LSException {

+        // If the destination is null

+        if (destination == null) {

+            String msg = Utils.messages

+            .createMessage(

+                    MsgKey.ER_NO_OUTPUT_SPECIFIED,

+                    null);

+            if (fDOMErrorHandler != null) {

+                fDOMErrorHandler.handleError(new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR, msg,

+                        MsgKey.ER_NO_OUTPUT_SPECIFIED));

+            }

+            throw new LSException(LSException.SERIALIZE_ERR, msg);

+        } 

+        

+        // If nodeArg is null, return false.  Should we throw and LSException instead?

+        if (nodeArg == null ) {

+            return false;

+        }

+

+        // Obtain a reference to the serializer to use

+        // Serializer serializer = getXMLSerializer(xmlVersion);

+        Serializer serializer = fXMLSerializer;

+        serializer.reset();

+        

+        // If the node has not been seen

+        if ( nodeArg != fVisitedNode) {

+            // Determine the XML Document version of the Node 

+            String xmlVersion = getXMLVersion(nodeArg);

+            

+            // Determine the encoding: 1.LSOutput.encoding, 2.Document.inputEncoding, 3.Document.xmlEncoding. 

+            fEncoding = destination.getEncoding();

+            if (fEncoding == null ) {

+            	fEncoding = getInputEncoding(nodeArg);

+            	fEncoding = fEncoding != null ? fEncoding : getXMLEncoding(nodeArg) == null? "UTF-8": getXMLEncoding(nodeArg);

+            }

+

+            // If the encoding is not recognized throw an exception.

+            // Note: The serializer defaults to UTF-8 when created

+            if (!Encodings.isRecognizedEncoding(fEncoding)) {

+                String msg = Utils.messages

+                .createMessage(

+                        MsgKey.ER_UNSUPPORTED_ENCODING,

+                        null);

+                if (fDOMErrorHandler != null) {

+                    fDOMErrorHandler.handleError(new DOMErrorImpl(

+                            DOMError.SEVERITY_FATAL_ERROR, msg,

+                            MsgKey.ER_UNSUPPORTED_ENCODING));

+                }

+                throw new LSException(LSException.SERIALIZE_ERR, msg);            	

+            }

+            

+            serializer.getOutputFormat().setProperty("version", xmlVersion);

+

+            // Set the output encoding and xml version properties

+            fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion);

+            fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, fEncoding);

+            

+            // If the node to be serialized is not a Document, Element, or Entity

+            // node

+            // then the XML declaration, or text declaration, should be never be

+            // serialized.

+            if ( (nodeArg.getNodeType() != Node.DOCUMENT_NODE

+                    || nodeArg.getNodeType() != Node.ELEMENT_NODE

+                    || nodeArg.getNodeType() != Node.ENTITY_NODE)

+                    && ((fFeatures & XMLDECL) != 0)) {

+                fDOMConfigProperties.setProperty(

+                        DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL,

+                        DOMConstants.DOM3_DEFAULT_FALSE);

+            }

+

+            fVisitedNode = nodeArg;

+        } 

+        

+        // Update the serializer properties

+        fXMLSerializer.setOutputFormat(fDOMConfigProperties);

+        

+        // 

+        try {

+            

+            // The LSSerializer will use the LSOutput object to determine 

+            // where to serialize the output to in the following order the  

+            // first one that is not null and not an empty string will be    

+            // used: 1.LSOutput.characterStream, 2.LSOutput.byteStream,   

+            // 3. LSOutput.systemId 

+            // 1.LSOutput.characterStream

+            Writer writer = destination.getCharacterStream();

+            if (writer == null ) {

+                

+                // 2.LSOutput.byteStream

+                OutputStream outputStream = destination.getByteStream();

+                if ( outputStream == null) {

+                    

+                    // 3. LSOutput.systemId

+                    String uri = destination.getSystemId();

+                    if (uri == null) {

+                        String msg = Utils.messages

+                        .createMessage(

+                                MsgKey.ER_NO_OUTPUT_SPECIFIED,

+                                null);

+                        if (fDOMErrorHandler != null) {

+                            fDOMErrorHandler.handleError(new DOMErrorImpl(

+                                    DOMError.SEVERITY_FATAL_ERROR, msg,

+                                    MsgKey.ER_NO_OUTPUT_SPECIFIED));

+                        }

+                        throw new LSException(LSException.SERIALIZE_ERR, msg);

+                        

+                    } else {

+                        // Expand the System Id and obtain an absolute URI for it.

+                        String absoluteURI = SystemIDResolver.getAbsoluteURI(uri);

+                        

+                        URL url = new URL(absoluteURI);

+                        OutputStream urlOutStream = null;

+                        String protocol = url.getProtocol();

+                        String host = url.getHost();

+                        

+                        // For file protocols, there is no need to use a URL to get its

+                        // corresponding OutputStream

+                        

+                        // Scheme names consist of a sequence of characters. The lower case

+                        // letters "a"--"z", digits, and the characters plus ("+"), period

+                        // ("."), and hyphen ("-") are allowed. For resiliency, programs

+                        // interpreting URLs should treat upper case letters as equivalent to

+                        // lower case in scheme names (e.g., allow "HTTP" as well as "http").

+                        if (protocol.equalsIgnoreCase("file") 

+                                && (host == null || host.length() == 0 || host.equals("localhost"))) {

+                            // do we also need to check for host.equals(hostname)

+                            urlOutStream = new FileOutputStream(getPathWithoutEscapes(url.getPath()));

+                           

+                        } else {

+                            // This should support URL's whose schemes are mentioned in 

+                            // RFC1738 other than file

+                            

+                            URLConnection urlCon = url.openConnection();

+                            urlCon.setDoInput(false);

+                            urlCon.setDoOutput(true);

+                            urlCon.setUseCaches(false); 

+                            urlCon.setAllowUserInteraction(false);

+                            

+                            // When writing to a HTTP URI, a HTTP PUT is performed.

+                            if (urlCon instanceof HttpURLConnection) {

+                                HttpURLConnection httpCon = (HttpURLConnection) urlCon;

+                                httpCon.setRequestMethod("PUT");

+                            }

+                            urlOutStream = urlCon.getOutputStream();

+                        }

+                        // set the OutputStream to that obtained from the systemId

+                        serializer.setOutputStream(urlOutStream);

+                    }

+                } else {

+                    // 2.LSOutput.byteStream

+                    serializer.setOutputStream(outputStream);     

+                }

+            } else {

+                // 1.LSOutput.characterStream

+                serializer.setWriter(writer);

+            }

+            

+            // The associated media type by default is set to text/xml on 

+            // org.apache.xml.serializer.SerializerBase.  

+            

+            // Get a reference to the serializer then lets you serilize a DOM

+            // Use this hack till Xalan support JAXP1.3

+            if (fDOMSerializer == null) {

+               fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer();

+            } 

+            

+            // Set the error handler on the DOM3Serializer interface implementation

+            if (fDOMErrorHandler != null) {

+                fDOMSerializer.setErrorHandler(fDOMErrorHandler);

+            }

+            

+            // Set the filter on the DOM3Serializer interface implementation

+            if (fSerializerFilter != null) {

+                fDOMSerializer.setNodeFilter(fSerializerFilter);

+            }

+            

+            // Set the NewLine character to be used

+            fDOMSerializer.setNewLine(fEndOfLine.toCharArray());

+            

+            // Serializer your DOM, where node is an org.w3c.dom.Node

+            // Assuming that Xalan's serializer can serialize any type of DOM node

+            fDOMSerializer.serializeDOM3(nodeArg);

+            

+        } catch( UnsupportedEncodingException ue) {

+            

+            String msg = Utils.messages

+            .createMessage(

+                    MsgKey.ER_UNSUPPORTED_ENCODING,

+                    null);

+            if (fDOMErrorHandler != null) {

+                fDOMErrorHandler.handleError(new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR, msg,

+                        MsgKey.ER_UNSUPPORTED_ENCODING, ue));

+            }

+            throw (LSException) createLSException(LSException.SERIALIZE_ERR, ue).fillInStackTrace();

+        } catch (LSException lse) {

+            // Rethrow LSException.

+            throw lse;

+        } catch (RuntimeException e) {

+            throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();

+        }  catch (Exception e) {

+            if (fDOMErrorHandler != null) {

+                fDOMErrorHandler.handleError(new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR, e.getMessage(),

+                        null, e));

+            }

+            throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();

+        }        

+        return true;

+    }

+    

+    /** 

+     * Serializes the specified node and returns a String with the serialized

+     * data to the caller.  

+     * 

+     * @see org.w3c.dom.ls.LSSerializer#writeToString(org.w3c.dom.Node)

+     * @since DOM Level 3

+     * @param nodeArg The Node to serialize.

+     * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the 

+     * LSSerializer was unable to serialize the node.

+     *      

+     */

+    public String writeToString(Node nodeArg) throws DOMException, LSException {

+        // return null is nodeArg is null.  Should an Exception be thrown instead?

+        if (nodeArg == null) {

+            return null;

+        }

+

+        // Should we reset the serializer configuration before each write operation?

+        // Obtain a reference to the serializer to use

+        Serializer serializer = fXMLSerializer;

+        serializer.reset();

+        

+        if (nodeArg != fVisitedNode){

+            // Determine the XML Document version of the Node 

+            String xmlVersion = getXMLVersion(nodeArg);

+            

+            serializer.getOutputFormat().setProperty("version", xmlVersion);

+            

+            // Set the output encoding and xml version properties

+            fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion);

+            fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, "UTF-16");

+            

+            // If the node to be serialized is not a Document, Element, or Entity

+            // node

+            // then the XML declaration, or text declaration, should be never be

+            // serialized.

+            if  ((nodeArg.getNodeType() != Node.DOCUMENT_NODE

+                    || nodeArg.getNodeType() != Node.ELEMENT_NODE

+                    || nodeArg.getNodeType() != Node.ENTITY_NODE)

+                    && ((fFeatures & XMLDECL) != 0)) {

+                fDOMConfigProperties.setProperty(

+                        DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL,

+                        DOMConstants.DOM3_DEFAULT_FALSE);

+            }            

+

+            fVisitedNode = nodeArg;       

+        } 

+        // Update the serializer properties

+        fXMLSerializer.setOutputFormat(fDOMConfigProperties);

+        

+        // StringWriter to Output to

+        StringWriter output = new StringWriter();

+        

+        // 

+        try {

+            

+            // Set the Serializer's Writer to a StringWriter

+            serializer.setWriter(output);

+            

+            // Get a reference to the serializer then lets you serilize a DOM

+            // Use this hack till Xalan support JAXP1.3

+            if (fDOMSerializer == null) {

+                fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer();

+            } 

+                        

+            // Set the error handler on the DOM3Serializer interface implementation

+            if (fDOMErrorHandler != null) {

+                fDOMSerializer.setErrorHandler(fDOMErrorHandler);

+            }

+            

+            // Set the filter on the DOM3Serializer interface implementation

+            if (fSerializerFilter != null) {

+                fDOMSerializer.setNodeFilter(fSerializerFilter);

+            }

+            

+            // Set the NewLine character to be used

+            fDOMSerializer.setNewLine(fEndOfLine.toCharArray());

+            

+            // Serializer your DOM, where node is an org.w3c.dom.Node

+            fDOMSerializer.serializeDOM3(nodeArg);

+        } catch (LSException lse) {

+            // Rethrow LSException.

+            throw lse;

+        } catch (RuntimeException e) {

+            throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();

+        }  catch (Exception e) {

+            if (fDOMErrorHandler != null) {

+                fDOMErrorHandler.handleError(new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR, e.getMessage(),

+                        null, e));

+            }

+            throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();

+        }        

+        

+        // return the serialized string

+        return output.toString();

+    }

+    

+    /** 

+     * Serializes the specified node to the specified URI and returns true if the Node 

+     * was successfully serialized. 

+     * 

+     * @see org.w3c.dom.ls.LSSerializer#writeToURI(org.w3c.dom.Node, String)

+     * @since DOM Level 3

+     * @param nodeArg The Node to serialize.

+     * @throws org.w3c.dom.ls.LSException SERIALIZE_ERR: Raised if the 

+     * LSSerializer was unable to serialize the node.

+     *      

+     */

+    public boolean writeToURI(Node nodeArg, String uri) throws LSException {

+        // If nodeArg is null, return false.  Should we throw and LSException instead?

+        if (nodeArg == null ) {

+            return false;

+        }

+

+        // Obtain a reference to the serializer to use

+        Serializer serializer = fXMLSerializer;

+        serializer.reset();

+        

+        if (nodeArg != fVisitedNode) {

+            // Determine the XML Document version of the Node 

+            String xmlVersion = getXMLVersion(nodeArg);

+            

+            // Determine the encoding: 1.LSOutput.encoding,

+            // 2.Document.inputEncoding, 3.Document.xmlEncoding.

+            fEncoding = getInputEncoding(nodeArg);

+            if (fEncoding == null ) {

+            	fEncoding = fEncoding != null ? fEncoding : getXMLEncoding(nodeArg) == null? "UTF-8": getXMLEncoding(nodeArg);

+            }

+            

+            serializer.getOutputFormat().setProperty("version", xmlVersion);

+            

+            // Set the output encoding and xml version properties

+            fDOMConfigProperties.setProperty(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION, xmlVersion);

+            fDOMConfigProperties.setProperty(DOMConstants.S_XSL_OUTPUT_ENCODING, fEncoding);

+            

+            // If the node to be serialized is not a Document, Element, or Entity

+            // node

+            // then the XML declaration, or text declaration, should be never be

+            // serialized.

+            if ( (nodeArg.getNodeType() != Node.DOCUMENT_NODE

+                    || nodeArg.getNodeType() != Node.ELEMENT_NODE

+                    || nodeArg.getNodeType() != Node.ENTITY_NODE)

+                    && ((fFeatures & XMLDECL) != 0))  {

+                fDOMConfigProperties.setProperty(

+                        DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL,

+                        DOMConstants.DOM3_DEFAULT_FALSE);

+            }

+       

+            fVisitedNode = nodeArg;

+        } 

+        

+        // Update the serializer properties

+        fXMLSerializer.setOutputFormat(fDOMConfigProperties);

+        

+        // 

+        try {

+            // If the specified encoding is not supported an

+            // "unsupported-encoding" fatal error is raised. ??

+            if (uri == null) {

+                String msg = Utils.messages.createMessage(

+                        MsgKey.ER_NO_OUTPUT_SPECIFIED, null);

+                if (fDOMErrorHandler != null) {

+                    fDOMErrorHandler.handleError(new DOMErrorImpl(

+                            DOMError.SEVERITY_FATAL_ERROR, msg,

+                            MsgKey.ER_NO_OUTPUT_SPECIFIED));

+                }

+                throw new LSException(LSException.SERIALIZE_ERR, msg);

+                

+            } else {

+                // REVISIT: Can this be used to get an absolute expanded URI

+                String absoluteURI = SystemIDResolver.getAbsoluteURI(uri);

+                

+                URL url = new URL(absoluteURI);

+                OutputStream urlOutStream = null;

+                String protocol = url.getProtocol();

+                String host = url.getHost();

+                

+                // For file protocols, there is no need to use a URL to get its

+                // corresponding OutputStream

+                

+                // Scheme names consist of a sequence of characters. The lower 

+                // case letters "a"--"z", digits, and the characters plus ("+"), 

+                // period ("."), and hyphen ("-") are allowed. For resiliency, 

+                // programs interpreting URLs should treat upper case letters as

+                // equivalent to lower case in scheme names 

+                // (e.g., allow "HTTP" as well as "http").

+                if (protocol.equalsIgnoreCase("file")

+                        && (host == null || host.length() == 0 || host

+                                .equals("localhost"))) {

+                    // do we also need to check for host.equals(hostname)

+                    urlOutStream = new FileOutputStream(getPathWithoutEscapes(url.getPath()));

+                    

+                } else {

+                    // This should support URL's whose schemes are mentioned in

+                    // RFC1738 other than file

+                    

+                    URLConnection urlCon = url.openConnection();

+                    urlCon.setDoInput(false);

+                    urlCon.setDoOutput(true);

+                    urlCon.setUseCaches(false);

+                    urlCon.setAllowUserInteraction(false);

+                    

+                    // When writing to a HTTP URI, a HTTP PUT is performed.

+                    if (urlCon instanceof HttpURLConnection) {

+                        HttpURLConnection httpCon = (HttpURLConnection) urlCon;

+                        httpCon.setRequestMethod("PUT");

+                    }

+                    urlOutStream = urlCon.getOutputStream();

+                }

+                // set the OutputStream to that obtained from the systemId

+                serializer.setOutputStream(urlOutStream);

+            }

+            

+            // Get a reference to the serializer then lets you serilize a DOM

+            // Use this hack till Xalan support JAXP1.3

+            if (fDOMSerializer == null) {

+                fDOMSerializer = (DOM3Serializer)serializer.asDOM3Serializer();

+            } 

+            

+            // Set the error handler on the DOM3Serializer interface implementation

+            if (fDOMErrorHandler != null) {

+                fDOMSerializer.setErrorHandler(fDOMErrorHandler);

+            }

+            

+            // Set the filter on the DOM3Serializer interface implementation

+            if (fSerializerFilter != null) {

+                fDOMSerializer.setNodeFilter(fSerializerFilter);

+            }

+            

+            // Set the NewLine character to be used

+            fDOMSerializer.setNewLine(fEndOfLine.toCharArray());

+            

+            // Serializer your DOM, where node is an org.w3c.dom.Node

+            // Assuming that Xalan's serializer can serialize any type of DOM

+            // node

+            fDOMSerializer.serializeDOM3(nodeArg);

+            

+        } catch (LSException lse) {

+            // Rethrow LSException.

+            throw lse;

+        } catch (RuntimeException e) {

+            throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();

+        }  catch (Exception e) {

+            if (fDOMErrorHandler != null) {

+                fDOMErrorHandler.handleError(new DOMErrorImpl(

+                        DOMError.SEVERITY_FATAL_ERROR, e.getMessage(),

+                        null, e));

+            }

+            throw (LSException) createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace();

+        }        

+        

+        return true;

+    }

+    // ************************************************************************

+    

+    

+    // ************************************************************************

+    // Implementaion methods

+    // ************************************************************************

+    

+    /** 

+     * Determines the XML Version of the Document Node to serialize.  If the Document Node

+     * is not a DOM Level 3 Node, then the default version returned is 1.0.

+     * 

+     * @param  nodeArg The Node to serialize

+     * @return A String containing the version pseudo-attribute of the XMLDecl.  

+     * @throws Throwable if the DOM implementation does not implement Document.getXmlVersion()      

+     */

+    //protected String getXMLVersion(Node nodeArg) throws Throwable {

+    protected String getXMLVersion(Node nodeArg) {

+        Document doc = null;

+        

+        // Determine the XML Version of the document

+        if (nodeArg != null) {

+            if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) {

+                // The Document node is the Node argument

+                doc = (Document)nodeArg;

+            } else { 

+                // The Document node is the Node argument's ownerDocument

+                doc = nodeArg.getOwnerDocument();

+            }

+            

+            // Determine the DOM Version.

+            if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) {

+                return doc.getXmlVersion();

+            }

+        } 

+        // The version will be treated as "1.0" which may result in

+        // an ill-formed document being serialized.

+        // If nodeArg does not have an ownerDocument, treat this as XML 1.0

+        return "1.0";

+    }

+    

+    /** 

+     * Determines the XML Encoding of the Document Node to serialize.  If the Document Node

+     * is not a DOM Level 3 Node, then the default encoding "UTF-8" is returned.

+     * 

+     * @param  nodeArg The Node to serialize

+     * @return A String containing the encoding pseudo-attribute of the XMLDecl.  

+     * @throws Throwable if the DOM implementation does not implement Document.getXmlEncoding()     

+     */

+    protected String getXMLEncoding(Node nodeArg) {

+        Document doc = null;

+        

+        // Determine the XML Encoding of the document

+        if (nodeArg != null) {

+            if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) {

+                // The Document node is the Node argument

+                doc = (Document)nodeArg;

+            } else { 

+                // The Document node is the Node argument's ownerDocument

+                doc = nodeArg.getOwnerDocument();

+            }

+            

+            // Determine the XML Version. 

+            if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) {

+                return doc.getXmlEncoding();

+            }

+        } 

+        // The default encoding is UTF-8 except for the writeToString method

+        return "UTF-8";

+    }

+    

+    /** 

+     * Determines the Input Encoding of the Document Node to serialize.  If the Document Node

+     * is not a DOM Level 3 Node, then null is returned.

+     * 

+     * @param  nodeArg The Node to serialize

+     * @return A String containing the input encoding.  

+     */

+    protected String getInputEncoding(Node nodeArg)  {

+        Document doc = null;

+        

+        // Determine the Input Encoding of the document

+        if (nodeArg != null) {

+            if (nodeArg.getNodeType() == Node.DOCUMENT_NODE) {

+                // The Document node is the Node argument

+                doc = (Document)nodeArg;

+            } else { 

+                // The Document node is the Node argument's ownerDocument

+                doc = nodeArg.getOwnerDocument();

+            }

+            

+            // Determine the DOM Version.

+            if (doc != null && doc.getImplementation().hasFeature("Core","3.0")) {

+                return doc.getInputEncoding();

+            }

+        } 

+        // The default encoding returned is null

+        return null;

+    }

+    

+    /**

+     * This method returns the LSSerializer's error handler.

+     * 

+     * @return Returns the fDOMErrorHandler.

+     */

+    public DOMErrorHandler getErrorHandler() {

+        return fDOMErrorHandler;

+    }

+    

+    /**

+     * Replaces all escape sequences in the given path with their literal characters.

+     */

+    private static String getPathWithoutEscapes(String origPath) {

+        if (origPath != null && origPath.length() != 0 && origPath.indexOf('%') != -1) {

+            // Locate the escape characters

+            StringTokenizer tokenizer = new StringTokenizer(origPath, "%");

+            StringBuffer result = new StringBuffer(origPath.length());

+            int size = tokenizer.countTokens();

+            result.append(tokenizer.nextToken());

+            for(int i = 1; i < size; ++i) {

+                String token = tokenizer.nextToken();

+                if (token.length() >= 2 && isHexDigit(token.charAt(0)) && 

+                        isHexDigit(token.charAt(1))) {

+                    // Decode the 2 digit hexadecimal number following % in '%nn'

+                    result.append((char)Integer.valueOf(token.substring(0, 2), 16).intValue());

+                    token = token.substring(2);

+                }

+                result.append(token);

+            }

+            return result.toString();

+        }

+        return origPath;

+    }

+

+    /** 

+     * Returns true if the given character is a valid hex character.

+     */

+    private static boolean isHexDigit(char c) {

+        return (c >= '0' && c <= '9' || 

+                c >= 'a' && c <= 'f' || 

+                c >= 'A' && c <= 'F');

+    }

+    

+    /**

+     * Creates an LSException. On J2SE 1.4 and above the cause for the exception will be set.

+     */

+    private static LSException createLSException(short code, Throwable cause) {

+        LSException lse = new LSException(code, cause != null ? cause.getMessage() : null);

+        if (cause != null && ThrowableMethods.fgThrowableMethodsAvailable) {

+            try {

+                ThrowableMethods.fgThrowableInitCauseMethod.invoke(lse, new Object [] {cause});

+            }

+            // Something went wrong. There's not much we can do about it.

+            catch (Exception e) {}

+        }

+        return lse;

+    }

+    

+    /**

+     * Holder of methods from java.lang.Throwable.

+     */

+    static class ThrowableMethods {

+        

+        // Method: java.lang.Throwable.initCause(java.lang.Throwable)

+        private static java.lang.reflect.Method fgThrowableInitCauseMethod = null;

+        

+        // Flag indicating whether or not Throwable methods available.

+        private static boolean fgThrowableMethodsAvailable = false;

+        

+        private ThrowableMethods() {}

+        

+        // Attempt to get methods for java.lang.Throwable on class initialization.

+        static {

+            try {

+                fgThrowableInitCauseMethod = Throwable.class.getMethod("initCause", new Class [] {Throwable.class});

+                fgThrowableMethodsAvailable = true;

+            }

+            // ClassNotFoundException, NoSuchMethodException or SecurityException

+            // Whatever the case, we cannot use java.lang.Throwable.initCause(java.lang.Throwable).

+            catch (Exception exc) {

+                fgThrowableInitCauseMethod = null;

+                fgThrowableMethodsAvailable = false;

+            }

+        }

+    }

+}

diff --git a/src/main/java/org/apache/xml/serializer/dom3/NamespaceSupport.java b/src/main/java/org/apache/xml/serializer/dom3/NamespaceSupport.java
new file mode 100644
index 0000000..fc2b0ea
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/dom3/NamespaceSupport.java
@@ -0,0 +1,315 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id:  $

+ */

+

+package org.apache.xml.serializer.dom3;

+

+import java.util.Enumeration;

+import java.util.NoSuchElementException;

+

+/**

+ * Namespace support for XML document handlers. This class doesn't 

+ * perform any error checking and assumes that all strings passed

+ * as arguments to methods are unique symbols. The SymbolTable class

+ * can be used for this purpose.

+ * 

+ * Derived from org.apache.xerces.util.NamespaceSupport

+ *

+ * @author Andy Clark, IBM

+ *

+ * @version $Id: Exp $

+ */

+public class NamespaceSupport {

+

+	static final String PREFIX_XML = "xml".intern();

+	

+	static final String PREFIX_XMLNS = "xmlns".intern(); 

+    

+    /**

+     * The XML Namespace ("http://www.w3.org/XML/1998/namespace"). This is

+     * the Namespace URI that is automatically mapped to the "xml" prefix.

+     */

+    public final static String XML_URI = "http://www.w3.org/XML/1998/namespace".intern();

+

+    /**

+     * XML Information Set REC

+     * all namespace attributes (including those named xmlns, 

+     * whose [prefix] property has no value) have a namespace URI of http://www.w3.org/2000/xmlns/

+     */

+    public final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/".intern();

+

+	//

+    // Data

+    //

+

+    /** 

+     * Namespace binding information. This array is composed of a

+     * series of tuples containing the namespace binding information:

+     * &lt;prefix, uri&gt;. The default size can be set to anything

+     * as long as it is a power of 2 greater than 1.

+     *

+     * @see #fNamespaceSize

+     * @see #fContext

+     */

+    protected String[] fNamespace = new String[16 * 2];

+

+    /** The top of the namespace information array. */

+    protected int fNamespaceSize;

+

+    // NOTE: The constructor depends on the initial context size 

+    //       being at least 1. -Ac

+

+    /** 

+     * Context indexes. This array contains indexes into the namespace

+     * information array. The index at the current context is the start

+     * index of declared namespace bindings and runs to the size of the

+     * namespace information array.

+     *

+     * @see #fNamespaceSize

+     */

+    protected int[] fContext = new int[8];

+

+    /** The current context. */

+    protected int fCurrentContext;

+    

+    protected String[] fPrefixes = new String[16];

+    

+    //

+    // Constructors

+    //

+

+    /** Default constructor. */

+    public NamespaceSupport() {

+    } // <init>()

+

+    //

+    // Public methods

+    //

+    

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#reset()

+	 */

+    public void reset() {

+

+        // reset namespace and context info

+        fNamespaceSize = 0;

+        fCurrentContext = 0;

+        fContext[fCurrentContext] = fNamespaceSize;

+

+        // bind "xml" prefix to the XML uri

+        fNamespace[fNamespaceSize++] = PREFIX_XML;

+        fNamespace[fNamespaceSize++] = XML_URI;

+        // bind "xmlns" prefix to the XMLNS uri

+        fNamespace[fNamespaceSize++] = PREFIX_XMLNS;

+        fNamespace[fNamespaceSize++] = XMLNS_URI;

+        ++fCurrentContext;

+

+    } // reset(SymbolTable)

+

+

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#pushContext()

+	 */

+    public void pushContext() {

+

+        // extend the array, if necessary

+        if (fCurrentContext + 1 == fContext.length) {

+            int[] contextarray = new int[fContext.length * 2];

+            System.arraycopy(fContext, 0, contextarray, 0, fContext.length);

+            fContext = contextarray;

+        }

+

+        // push context

+        fContext[++fCurrentContext] = fNamespaceSize;

+

+    } // pushContext()

+

+

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#popContext()

+	 */

+    public void popContext() {

+        fNamespaceSize = fContext[fCurrentContext--];

+    } // popContext()

+

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#declarePrefix(String, String)

+	 */

+    public boolean declarePrefix(String prefix, String uri) {

+        // ignore "xml" and "xmlns" prefixes

+        if (prefix == PREFIX_XML || prefix == PREFIX_XMLNS) {

+            return false;

+        }

+

+        // see if prefix already exists in current context

+        for (int i = fNamespaceSize; i > fContext[fCurrentContext]; i -= 2) {

+            //if (fNamespace[i - 2] == prefix) {

+        	if (fNamespace[i - 2].equals(prefix) )  {

+                // REVISIT: [Q] Should the new binding override the

+                //          previously declared binding or should it

+                //          it be ignored? -Ac

+                // NOTE:    The SAX2 "NamespaceSupport" helper allows

+                //          re-bindings with the new binding overwriting

+                //          the previous binding. -Ac

+                fNamespace[i - 1] = uri;

+                return true;

+            }

+        }

+

+        // resize array, if needed

+        if (fNamespaceSize == fNamespace.length) {

+            String[] namespacearray = new String[fNamespaceSize * 2];

+            System.arraycopy(fNamespace, 0, namespacearray, 0, fNamespaceSize);

+            fNamespace = namespacearray;

+        }

+

+        // bind prefix to uri in current context

+        fNamespace[fNamespaceSize++] = prefix;

+        fNamespace[fNamespaceSize++] = uri;

+

+        return true;

+

+    } // declarePrefix(String,String):boolean

+

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#getURI(String)

+	 */

+    public String getURI(String prefix) {

+        

+        // find prefix in current context

+        for (int i = fNamespaceSize; i > 0; i -= 2) {

+            //if (fNamespace[i - 2] == prefix) {

+        	if (fNamespace[i - 2].equals(prefix) ) {

+                return fNamespace[i - 1];

+            }

+        }

+

+        // prefix not found

+        return null;

+

+    } // getURI(String):String

+

+

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#getPrefix(String)

+	 */

+    public String getPrefix(String uri) {

+

+        // find uri in current context

+        for (int i = fNamespaceSize; i > 0; i -= 2) {

+            //if (fNamespace[i - 1] == uri) {

+        	if (fNamespace[i - 1].equals(uri) ) {

+                //if (getURI(fNamespace[i - 2]) == uri)

+        		if (getURI(fNamespace[i - 2]).equals(uri) )

+                    return fNamespace[i - 2];

+            }

+        }

+

+        // uri not found

+        return null;

+

+    } // getPrefix(String):String

+

+

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#getDeclaredPrefixCount()

+	 */

+    public int getDeclaredPrefixCount() {

+        return (fNamespaceSize - fContext[fCurrentContext]) / 2;

+    } // getDeclaredPrefixCount():int

+

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#getDeclaredPrefixAt(int)

+	 */

+    public String getDeclaredPrefixAt(int index) {

+        return fNamespace[fContext[fCurrentContext] + index * 2];

+    } // getDeclaredPrefixAt(int):String

+

+	/**

+	 * @see org.apache.xerces.xni.NamespaceContext#getAllPrefixes()

+	 */

+	public Enumeration getAllPrefixes() {

+        int count = 0;

+        if (fPrefixes.length < (fNamespace.length/2)) {

+            // resize prefix array          

+            String[] prefixes = new String[fNamespaceSize];

+            fPrefixes = prefixes;

+        }

+        String prefix = null;

+        boolean unique = true;

+        for (int i = 2; i < (fNamespaceSize-2); i += 2) {

+            prefix = fNamespace[i + 2];            

+            for (int k=0;k<count;k++){

+                if (fPrefixes[k]==prefix){

+                    unique = false;

+                    break;

+                }               

+            }

+            if (unique){

+                fPrefixes[count++] = prefix;

+            }

+            unique = true;

+        }

+		return new Prefixes(fPrefixes, count);

+	}

+    

+    protected final class Prefixes implements Enumeration {

+        private String[] prefixes;

+        private int counter = 0;

+        private int size = 0;

+               

+		/**

+		 * Constructor for Prefixes.

+		 */

+		public Prefixes(String [] prefixes, int size) {

+			this.prefixes = prefixes;

+            this.size = size;

+		}

+

+       /**

+		 * @see java.util.Enumeration#hasMoreElements()

+		 */

+		public boolean hasMoreElements() {           

+			return (counter< size);

+		}

+

+		/**

+		 * @see java.util.Enumeration#nextElement()

+		 */

+		public Object nextElement() {

+            if (counter< size){

+                return fPrefixes[counter++];

+            }

+			throw new NoSuchElementException("Illegal access to Namespace prefixes enumeration.");

+		}

+        

+        public String toString(){

+            StringBuffer buf = new StringBuffer();

+            for (int i=0;i<size;i++){

+                buf.append(prefixes[i]);

+                buf.append(" ");

+            }

+                

+            return buf.toString(); 

+        }

+

+}

+

+} // class NamespaceSupport

diff --git a/src/main/java/org/apache/xml/serializer/output_html.properties b/src/main/java/org/apache/xml/serializer/output_html.properties
new file mode 100644
index 0000000..71ca138
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/output_html.properties
@@ -0,0 +1,44 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the  "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+#
+# $Id: output_html.properties 468654 2006-10-28 07:09:23Z minchau $
+#
+# Specify defaults when method="html".  These defaults use output_xml.properties 
+# as a base.
+#
+
+# XSLT properties do not need namespace qualification.
+method=html
+indent=yes
+media-type=text/html
+version=4.0
+
+# Xalan-specific output properties.  These can be overridden in the stylesheet 
+# assigning a xalan namespace.  For example:
+# <xsl:stylesheet version="1.0"
+#          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+#          xmlns:xalan="http://xml.apache.org/xalan">
+#  <xsl:output method="html" encoding="UTF-8"
+#              xalan:content-handler="MyContentHandler"/>
+#  ...
+# Note that the colon after the protocol needs to be escaped.
+{http\u003a//xml.apache.org/xalan}indent-amount=0
+{http\u003a//xml.apache.org/xalan}content-handler=org.apache.xml.serializer.ToHTMLStream
+{http\u003a//xml.apache.org/xalan}entities=org/apache/xml/serializer/HTMLEntities
+{http\u003a//xml.apache.org/xalan}use-url-escaping=yes
+{http\u003a//xml.apache.org/xalan}omit-meta-tag=no
diff --git a/src/main/java/org/apache/xml/serializer/output_text.properties b/src/main/java/org/apache/xml/serializer/output_text.properties
new file mode 100644
index 0000000..25f906e
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/output_text.properties
@@ -0,0 +1,37 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the  "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+#
+# $Id: output_text.properties 468654 2006-10-28 07:09:23Z minchau $
+#
+# Specify defaults when method="text".
+#
+
+# XSLT properties do not need namespace qualification.
+method=text
+media-type=text/plain
+
+# Xalan-specific output properties.  These can be overridden in the stylesheet 
+# assigning a xalan namespace.  For example:
+# <xsl:stylesheet version="1.0"
+#          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+#          xmlns:xalan="http://xml.apache.org/xalan">
+#  <xsl:output method="html" encoding="UTF-8"
+#              xalan:content-handler="MyContentHandler"/>
+#  ...
+# Note that the colon after the protocol needs to be escaped.
+{http\u003a//xml.apache.org/xalan}content-handler=org.apache.xml.serializer.ToTextStream
diff --git a/src/main/java/org/apache/xml/serializer/output_unknown.properties b/src/main/java/org/apache/xml/serializer/output_unknown.properties
new file mode 100644
index 0000000..dd70ff0
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/output_unknown.properties
@@ -0,0 +1,46 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the  "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+#
+# $Id: output_unknown.properties 468654 2006-10-28 07:09:23Z minchau $
+#
+# Specify defaults when no method="..." is specified.
+# This type of output will quickly switch to "xml" or "html"
+# depending on the first element name.
+#
+
+# XSLT properties do not need namespace qualification.
+method=xml
+version=1.0
+encoding=UTF-8
+indent=no
+omit-xml-declaration=no
+standalone=no
+media-type=text/xml
+
+# Xalan-specific output properties.  These can be overridden in the stylesheet 
+# assigning a xalan namespace.  For example:
+# <xsl:stylesheet version="1.0"
+#          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+#          xmlns:xalan="http://xml.apache.org/xalan">
+#  <xsl:output method="html" encoding="UTF-8"
+#              xalan:content-handler="MyContentHandler"/>
+#  ...
+# Note that the colon after the protocol needs to be escaped.
+{http\u003a//xml.apache.org/xalan}indent-amount=0
+{http\u003a//xml.apache.org/xalan}content-handler=org.apache.xml.serializer.ToUnknownStream
+
diff --git a/src/main/java/org/apache/xml/serializer/output_xml.properties b/src/main/java/org/apache/xml/serializer/output_xml.properties
new file mode 100644
index 0000000..6a9617c
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/output_xml.properties
@@ -0,0 +1,46 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the  "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+#
+# $Id: output_xml.properties 468654 2006-10-28 07:09:23Z minchau $
+#
+# Specify defaults when method="xml".  These defaults serve as a base for 
+# other defaults, such as output_html and output_text.
+#
+
+# XSLT properties do not need namespace qualification.
+method=xml
+version=1.0
+encoding=UTF-8
+indent=no
+omit-xml-declaration=no
+standalone=no
+media-type=text/xml
+
+# Xalan-specific output properties.  These can be overridden in the stylesheet 
+# assigning a xalan namespace.  For example:
+# <xsl:stylesheet version="1.0"
+#          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+#          xmlns:xalan="http://xml.apache.org/xalan">
+#  <xsl:output method="html" encoding="UTF-8"
+#              xalan:content-handler="MyContentHandler"/>
+#  ...
+# Note that the colon after the protocol needs to be escaped.
+{http\u003a//xml.apache.org/xalan}indent-amount=0
+{http\u003a//xml.apache.org/xalan}content-handler=org.apache.xml.serializer.ToXMLStream
+{http\u003a//xml.apache.org/xalan}entities=org/apache/xml/serializer/XMLEntities
+
diff --git a/src/main/java/org/apache/xml/serializer/package.html b/src/main/java/org/apache/xml/serializer/package.html
new file mode 100644
index 0000000..c6bde48
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/package.html
@@ -0,0 +1,43 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468654 2006-10-28 07:09:23Z minchau $ -->
+<html>
+  <title>Xalan Serializer Package.</title>
+  <body>
+    <p>Processes SAX events into streams.</p>
+    
+    <p>The {@link org.apache.xml.serializer.SerializerFactory} is used to 
+    create a {@link org.apache.xml.serializer.Serializer} from a set of 
+    output properties (see {@link javax.xml.transform.OutputKeys}).</p>
+    <p>{@link org.apache.xml.serializer.ToStream} acts as the main 
+    baseclass for the Xalan serializer implementations.  
+    {@link org.apache.xml.serializer.ToHTMLStream} derives from this 
+    to implement HTML serialization.  
+    {@link org.apache.xml.serializer.ToTextStream}
+    implements plain text serialization.
+    {@link org.apache.xml.serializer.ToXMLStream}
+    implements XML serialization.
+    </p>
+    <p>XML mapping from characters to entity references is defined in 
+    XMLEntities.res.  HTML entity reference mapping is defined in HTMLEntities.res.
+    </p>
+    <p>Encoding information is defined in {@link org.apache.xml.serializer.Encodings}.</p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xml/serializer/utils/AttList.java b/src/main/java/org/apache/xml/serializer/utils/AttList.java
new file mode 100644
index 0000000..2d676fa
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/AttList.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AttList.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import org.xml.sax.Attributes;
+
+/**
+ * Wraps a DOM attribute list in a SAX Attributes.
+ * 
+ * This class is a copy of the one in org.apache.xml.utils. 
+ * It exists to cut the serializers dependancy on that package.
+ * A minor changes from that package are:
+ * DOMHelper reference changed to DOM2Helper, class is not "public"
+ *  
+ * This class is not a public API, it is only public because it is 
+ * used in org.apache.xml.serializer.
+ * 
+ * @xsl.usage internal
+ */
+public final class AttList implements Attributes
+{
+
+  /** List of attribute nodes          */
+  NamedNodeMap m_attrs;
+
+  /** Index of last attribute node          */
+  int m_lastIndex;
+
+  // ARGHH!!  JAXP Uses Xerces without setting the namespace processing to ON!
+  // DOM2Helper m_dh = new DOM2Helper();
+
+  /** Local reference to DOMHelper          */
+  DOM2Helper m_dh;
+
+//  /**
+//   * Constructor AttList
+//   *
+//   *
+//   * @param attrs List of attributes this will contain
+//   */
+//  public AttList(NamedNodeMap attrs)
+//  {
+//
+//    m_attrs = attrs;
+//    m_lastIndex = m_attrs.getLength() - 1;
+//    m_dh = new DOM2Helper();
+//  }
+
+  /**
+   * Constructor AttList
+   *
+   *
+   * @param attrs List of attributes this will contain
+   * @param dh DOMHelper 
+   */
+  public AttList(NamedNodeMap attrs, DOM2Helper dh)
+  {
+    
+    m_attrs = attrs;
+    m_lastIndex = m_attrs.getLength() - 1;
+    m_dh = dh;
+  }
+
+  /**
+   * Get the number of attribute nodes in the list 
+   *
+   *
+   * @return number of attribute nodes
+   */
+  public int getLength()
+  {
+    return m_attrs.getLength();
+  }
+
+  /**
+   * Look up an attribute's Namespace URI by index.
+   *
+   * @param index The attribute index (zero-based).
+   * @return The Namespace URI, or the empty string if none
+   *         is available, or null if the index is out of
+   *         range.
+   */
+  public String getURI(int index)
+  {
+    String ns = m_dh.getNamespaceOfNode(((Attr) m_attrs.item(index)));
+    if(null == ns)
+      ns = "";
+    return ns;
+  }
+
+  /**
+   * Look up an attribute's local name by index.
+   *
+   * @param index The attribute index (zero-based).
+   * @return The local name, or the empty string if Namespace
+   *         processing is not being performed, or null
+   *         if the index is out of range.
+   */
+  public String getLocalName(int index)
+  {
+    return m_dh.getLocalNameOfNode(((Attr) m_attrs.item(index)));
+  }
+
+  /**
+   * Look up an attribute's qualified name by index.
+   *
+   *
+   * @param i The attribute index (zero-based).
+   *
+   * @return The attribute's qualified name
+   */
+  public String getQName(int i)
+  {
+    return ((Attr) m_attrs.item(i)).getName();
+  }
+
+  /**
+   * Get the attribute's node type by index
+   *
+   *
+   * @param i The attribute index (zero-based)
+   *
+   * @return the attribute's node type
+   */
+  public String getType(int i)
+  {
+    return "CDATA";  // for the moment
+  }
+
+  /**
+   * Get the attribute's node value by index
+   *
+   *
+   * @param i The attribute index (zero-based)
+   *
+   * @return the attribute's node value
+   */
+  public String getValue(int i)
+  {
+    return ((Attr) m_attrs.item(i)).getValue();
+  }
+
+  /**
+   * Get the attribute's node type by name
+   *
+   *
+   * @param name Attribute name
+   *
+   * @return the attribute's node type
+   */
+  public String getType(String name)
+  {
+    return "CDATA";  // for the moment
+  }
+
+  /**
+   * Look up an attribute's type by Namespace name.
+   *
+   * @param uri The Namespace URI, or the empty String if the
+   *        name has no Namespace URI.
+   * @param localName The local name of the attribute.
+   * @return The attribute type as a string, or null if the
+   *         attribute is not in the list or if Namespace
+   *         processing is not being performed.
+   */
+  public String getType(String uri, String localName)
+  {
+    return "CDATA";  // for the moment
+  }
+
+  /**
+   * Look up an attribute's value by name.
+   *
+   *
+   * @param name The attribute node's name
+   *
+   * @return The attribute node's value
+   */
+  public String getValue(String name)
+  {
+    Attr attr = ((Attr) m_attrs.getNamedItem(name));
+    return (null != attr) 
+          ? attr.getValue() : null;
+  }
+
+  /**
+   * Look up an attribute's value by Namespace name.
+   *
+   * @param uri The Namespace URI, or the empty String if the
+   *        name has no Namespace URI.
+   * @param localName The local name of the attribute.
+   * @return The attribute value as a string, or null if the
+   *         attribute is not in the list.
+   */
+  public String getValue(String uri, String localName)
+  {
+        Node a=m_attrs.getNamedItemNS(uri,localName);
+        return (a==null) ? null : a.getNodeValue();
+  }
+
+  /**
+   * Look up the index of an attribute by Namespace name.
+   *
+   * @param uri The Namespace URI, or the empty string if
+   *        the name has no Namespace URI.
+   * @param localPart The attribute's local name.
+   * @return The index of the attribute, or -1 if it does not
+   *         appear in the list.
+   */
+  public int getIndex(String uri, String localPart)
+  {
+    for(int i=m_attrs.getLength()-1;i>=0;--i)
+    {
+      Node a=m_attrs.item(i);
+      String u=a.getNamespaceURI();
+      if( (u==null ? uri==null : u.equals(uri))
+      &&
+      a.getLocalName().equals(localPart) )
+    return i;
+    }
+    return -1;
+  }
+
+  /**
+   * Look up the index of an attribute by raw XML 1.0 name.
+   *
+   * @param qName The qualified (prefixed) name.
+   * @return The index of the attribute, or -1 if it does not
+   *         appear in the list.
+   */
+  public int getIndex(String qName)
+  {
+    for(int i=m_attrs.getLength()-1;i>=0;--i)
+    {
+      Node a=m_attrs.item(i);
+      if(a.getNodeName().equals(qName) )
+    return i;
+    }
+    return -1;
+  }
+}
+
diff --git a/src/main/java/org/apache/xml/serializer/utils/DOM2Helper.java b/src/main/java/org/apache/xml/serializer/utils/DOM2Helper.java
new file mode 100644
index 0000000..cafd825
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/DOM2Helper.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOM2Helper.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+import org.w3c.dom.Node;
+
+/**
+ * This class provides a DOM level 2 "helper", which provides services currently 
+ * not provided be the DOM standard.
+ * 
+ * This class is a copy of the one in org.apache.xml.utils. 
+ * It exists to cut the serializers dependancy on that package.
+ * 
+ * The differences from the original class are:
+ * it doesn't extend DOMHelper, not depricated, 
+ * dropped method isNodeAfter(Node node1, Node node2)
+ * dropped method parse(InputSource)
+ * dropped method supportSAX()
+ * dropped method setDocument(doc) 
+ * dropped method checkNode(Node)
+ * dropped method getDocument()
+ * dropped method getElementByID(String id, Document doc)
+ * dropped method getParentOfNode(Node node)
+ * dropped field Document m_doc;
+ * made class non-public
+ *   
+ * This class is not a public API, it is only public because it is 
+ * used in org.apache.xml.serializer.
+ * 
+ * @xsl.usage internal
+ */
+public final class DOM2Helper
+{
+
+  /**
+   * Construct an instance.
+   */
+  public DOM2Helper(){}
+
+  /**
+   * Returns the local name of the given node, as defined by the
+   * XML Namespaces specification. This is prepared to handle documents
+   * built using DOM Level 1 methods by falling back upon explicitly
+   * parsing the node name.
+   *
+   * @param n Node to be examined
+   *
+   * @return String containing the local name, or null if the node
+   * was not assigned a Namespace.
+   */
+  public String getLocalNameOfNode(Node n)
+  {
+
+    String name = n.getLocalName();
+
+    return (null == name) ? getLocalNameOfNodeFallback(n) : name;
+  }
+  
+  /**
+   * Returns the local name of the given node. If the node's name begins
+   * with a namespace prefix, this is the part after the colon; otherwise
+   * it's the full node name.
+   * 
+   * This method is copied from org.apache.xml.utils.DOMHelper
+   *
+   * @param n the node to be examined.
+   *
+   * @return String containing the Local Name
+   */
+  private String getLocalNameOfNodeFallback(Node n)
+  {
+
+    String qname = n.getNodeName();
+    int index = qname.indexOf(':');
+
+    return (index < 0) ? qname : qname.substring(index + 1);
+  }
+
+  /**
+   * Returns the Namespace Name (Namespace URI) for the given node.
+   * In a Level 2 DOM, you can ask the node itself. Note, however, that
+   * doing so conflicts with our decision in getLocalNameOfNode not
+   * to trust the that the DOM was indeed created using the Level 2
+   * methods. If Level 1 methods were used, these two functions will
+   * disagree with each other.
+   * <p>
+   * TODO: Reconcile with getLocalNameOfNode.
+   *
+   * @param n Node to be examined
+   *
+   * @return String containing the Namespace URI bound to this DOM node
+   * at the time the Node was created.
+   */
+  public String getNamespaceOfNode(Node n)
+  {
+    return n.getNamespaceURI();
+  }
+
+  /** Field m_useDOM2getNamespaceURI is a compile-time flag which
+   *  gates some of the parser options used to build a DOM -- but 
+   * that code is commented out at this time and nobody else
+   * references it, so I've commented this out as well. */
+  //private boolean m_useDOM2getNamespaceURI = false;
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/Messages.java b/src/main/java/org/apache/xml/serializer/utils/Messages.java
new file mode 100644
index 0000000..e70fc3a
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/Messages.java
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Messages.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * A utility class for issuing error messages.
+ * 
+ * A user of this class normally would create a singleton 
+ * instance of this class, passing the name
+ * of the message class on the constructor. For example:
+ * <CODE>
+ * static Messages x = new Messages("org.package.MyMessages");
+ * </CODE>
+ * Later the message is typically generated this way if there are no 
+ * substitution arguments:
+ * <CODE>
+ * String msg = x.createMessage(org.package.MyMessages.KEY_ONE, null); 
+ * </CODE>
+ * If there are arguments substitutions then something like this:
+ * <CODE>
+ * String filename = ...;
+ * String directory = ...;
+ * String msg = x.createMessage(org.package.MyMessages.KEY_TWO, 
+ *   new Object[] {filename, directory) ); 
+ * </CODE>
+ *  
+ * The constructor of an instance of this class must be given
+ * the class name of a class that extends java.util.ListResourceBundle 
+ * ("org.package.MyMessages" in the example above).  
+ * The name should not have any language suffix 
+ * which will be added automatically by this utility class.
+ * 
+ * The message class ("org.package.MyMessages")
+ * must define the abstract method getContents() that is
+ * declared in its base class, for example:
+ * <CODE>
+ * public Object[][] getContents() {return contents;}
+ * </CODE>
+ * 
+ * It is suggested that the message class expose its
+ * message keys like this:
+ * <CODE>
+ *   public static final String KEY_ONE = "KEY1";
+ *   public static final String KEY_TWO = "KEY2";
+ *   . . . 
+ * </CODE>
+ * and used through their names (KEY_ONE ...) rather than
+ * their values ("KEY1" ...).
+ * 
+ * The field contents (returned by getContents()
+ * should be initialized something like this:
+ * <CODE>
+ * public static final Object[][] contents = {
+ * { KEY_ONE, "Something has gone wrong!" },
+ * { KEY_TWO, "The file ''{0}'' does not exist in directory ''{1}''." },
+ * . . .
+ * { KEY_N, "Message N" }  }
+ * </CODE>
+ * 
+ * Where that section of code with the KEY to Message mappings
+ * (where the message classes 'contents' field is initialized)
+ * can have the Message strings translated in an alternate language
+ * in a errorResourceClass with a language suffix.
+ * 
+ * More sophisticated use of this class would be to pass null
+ * when contructing it, but then call loadResourceBundle()
+ * before creating any messages.
+ * 
+ * This class is not a public API, it is only public because it is 
+ * used in org.apache.xml.serializer.
+ *
+ *  @xsl.usage internal
+ */
+public final class Messages
+{
+    /** The local object to use.  */
+    private final Locale m_locale = Locale.getDefault();
+
+    /** The language specific resource object for messages.  */
+    private ListResourceBundle m_resourceBundle;
+
+    /** The class name of the error message string table with no language suffix. */
+    private String m_resourceBundleName;
+
+
+
+    /**
+     * Constructor.
+     * @param resourceBundle the class name of the ListResourceBundle
+     * that the instance of this class is associated with and will use when
+     * creating messages.
+     * The class name is without a language suffix. If the value passed
+     * is null then loadResourceBundle(errorResourceClass) needs to be called
+     * explicitly before any messages are created.
+     * 
+     * @xsl.usage internal
+     */
+    Messages(String resourceBundle)
+    {
+
+        m_resourceBundleName = resourceBundle;
+    }
+    
+    /*
+     * Set the Locale object to use. If this method is not called the
+     * default locale is used. This method needs to be called before
+     * loadResourceBundle().
+     * 
+     * @param locale non-null reference to Locale object.
+     * @xsl.usage internal
+     */
+//    public void setLocale(Locale locale)
+//    {
+//        m_locale = locale;
+//    }
+
+    /**
+     * Get the Locale object that is being used.
+     * 
+     * @return non-null reference to Locale object.
+     * @xsl.usage internal
+     */
+    private Locale getLocale()
+    {
+        return m_locale;
+    }
+
+    /**
+     * Get the ListResourceBundle being used by this Messages instance which was
+     * previously set by a call to loadResourceBundle(className)
+     * @xsl.usage internal
+     */
+    private ListResourceBundle getResourceBundle()
+    {
+        return m_resourceBundle;
+    }
+
+    /**
+     * Creates a message from the specified key and replacement
+     * arguments, localized to the given locale.
+     *
+     * @param msgKey  The key for the message text.
+     * @param args    The arguments to be used as replacement text
+     * in the message created.
+     *
+     * @return The formatted message string.
+     * @xsl.usage internal
+     */
+    public final String createMessage(String msgKey, Object args[])
+    {
+        if (m_resourceBundle == null)
+            m_resourceBundle = loadResourceBundle(m_resourceBundleName);
+
+        if (m_resourceBundle != null)
+        {
+            return createMsg(m_resourceBundle, msgKey, args);
+        }
+        else
+            return "Could not load the resource bundles: "+ m_resourceBundleName;
+    }
+
+    /**
+     * Creates a message from the specified key and replacement
+     * arguments, localized to the given locale.
+     *
+     * @param errorCode The key for the message text.
+     *
+     * @param fResourceBundle The resource bundle to use.
+     * @param msgKey  The message key to use.
+     * @param args      The arguments to be used as replacement text
+     *                  in the message created.
+     *
+     * @return The formatted message string.
+     * @xsl.usage internal
+     */
+    private final String createMsg(
+        ListResourceBundle fResourceBundle,
+        String msgKey,
+        Object args[]) //throws Exception
+    {
+
+        String fmsg = null;
+        boolean throwex = false;
+        String msg = null;
+
+        if (msgKey != null)
+            msg = fResourceBundle.getString(msgKey);
+        else
+            msgKey = "";
+
+        if (msg == null)
+        {
+            throwex = true;
+            /* The message is not in the bundle . . . this is bad,
+             * so try to get the message that the message is not in the bundle
+             */
+            try
+            {
+
+                msg =
+                    java.text.MessageFormat.format(
+                        MsgKey.BAD_MSGKEY,
+                        new Object[] { msgKey, m_resourceBundleName });
+            }
+            catch (Exception e)
+            {
+                /* even the message that the message is not in the bundle is
+                 * not there ... this is really bad
+                 */
+                msg =
+                    "The message key '"
+                        + msgKey
+                        + "' is not in the message class '"
+                        + m_resourceBundleName+"'";
+            }
+        }
+        else if (args != null)
+        {
+            try
+            {
+                // Do this to keep format from crying.
+                // This is better than making a bunch of conditional
+                // code all over the place.
+                int n = args.length;
+
+                for (int i = 0; i < n; i++)
+                {
+                    if (null == args[i])
+                        args[i] = "";
+                }
+
+                fmsg = java.text.MessageFormat.format(msg, args);
+                // if we get past the line above we have create the message ... hurray!
+            }
+            catch (Exception e)
+            {
+                throwex = true;
+                try
+                {
+                    // Get the message that the format failed.
+                    fmsg =
+                        java.text.MessageFormat.format(
+                            MsgKey.BAD_MSGFORMAT,
+                            new Object[] { msgKey, m_resourceBundleName });
+                    fmsg += " " + msg;
+                }
+                catch (Exception formatfailed)
+                {
+                    // We couldn't even get the message that the format of
+                    // the message failed ... so fall back to English.
+                    fmsg =
+                        "The format of message '"
+                            + msgKey
+                            + "' in message class '"
+                            + m_resourceBundleName
+                            + "' failed.";
+                }
+            }
+        }
+        else
+            fmsg = msg;
+
+        if (throwex)
+        {
+            throw new RuntimeException(fmsg);
+        }
+
+        return fmsg;
+    }
+
+    /**
+     * Return a named ResourceBundle for a particular locale.  This method mimics the behavior
+     * of ResourceBundle.getBundle().
+     * 
+     * @param className the name of the class that implements ListResourceBundle,
+     * without language suffix.
+     * @return the ResourceBundle
+     * @throws MissingResourceException
+     * @xsl.usage internal
+     */
+    private ListResourceBundle loadResourceBundle(String resourceBundle)
+        throws MissingResourceException
+    {
+        m_resourceBundleName = resourceBundle;
+        Locale locale = getLocale();
+
+        ListResourceBundle lrb;
+
+        try
+        {
+
+            ResourceBundle rb =
+                ResourceBundle.getBundle(m_resourceBundleName, locale);
+            lrb = (ListResourceBundle) rb;
+        }
+        catch (MissingResourceException e)
+        {
+            try // try to fall back to en_US if we can't load
+                {
+
+                // Since we can't find the localized property file,
+                // fall back to en_US.
+                lrb =
+                    (ListResourceBundle) ResourceBundle.getBundle(
+                        m_resourceBundleName,
+                        new Locale("en", "US"));
+            }
+            catch (MissingResourceException e2)
+            {
+
+                // Now we are really in trouble.
+                // very bad, definitely very bad...not going to get very far
+                throw new MissingResourceException(
+                    "Could not load any resource bundles." + m_resourceBundleName,
+                    m_resourceBundleName,
+                    "");
+            }
+        }
+        m_resourceBundle = lrb;
+        return lrb;
+    }
+
+    /**
+     * Return the resource file suffic for the indicated locale
+     * For most locales, this will be based the language code.  However
+     * for Chinese, we do distinguish between Taiwan and PRC
+     *
+     * @param locale the locale
+     * @return an String suffix which can be appended to a resource name
+     * @xsl.usage internal
+     */
+    private static String getResourceSuffix(Locale locale)
+    {
+
+        String suffix = "_" + locale.getLanguage();
+        String country = locale.getCountry();
+
+        if (country.equals("TW"))
+            suffix += "_" + country;
+
+        return suffix;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/MsgKey.java b/src/main/java/org/apache/xml/serializer/utils/MsgKey.java
new file mode 100644
index 0000000..8ec6336
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/MsgKey.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: MsgKey.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+/**
+ * This class is not a public API,
+ * It is used internally by serializer and is public,
+ * in the Java sense, only because its use crosses
+ * package boundaries.
+ * <p>
+ * This class holds only the message keys used
+ * when generating messages.
+ */
+public class MsgKey {
+
+    /** An internal error with the messages,
+     * this is the message to use if the message key can't be found 
+     */
+    public static final String BAD_MSGKEY = "BAD_MSGKEY";
+
+    /** 
+     * An internal error with the messages,
+     * this is the message to use if the message format operation failed.  
+     */
+    public static final String BAD_MSGFORMAT = "BAD_MSGFORMAT";
+
+    public static final String ER_RESOURCE_COULD_NOT_FIND =
+        "ER_RESOURCE_COULD_NOT_FIND";
+    public static final String ER_RESOURCE_COULD_NOT_LOAD =
+        "ER_RESOURCE_COULD_NOT_LOAD";
+    public static final String ER_BUFFER_SIZE_LESSTHAN_ZERO =
+        "ER_BUFFER_SIZE_LESSTHAN_ZERO";
+    public static final String ER_INVALID_UTF16_SURROGATE =
+        "ER_INVALID_UTF16_SURROGATE";
+    public static final String ER_OIERROR = "ER_OIERROR";
+    public static final String ER_NAMESPACE_PREFIX = "ER_NAMESPACE_PREFIX";
+    public static final String ER_STRAY_ATTRIBUTE = "ER_STRAY_ATTRIBUTE";
+    public static final String ER_STRAY_NAMESPACE = "ER_STRAY_NAMESPACE";
+    public static final String ER_COULD_NOT_LOAD_RESOURCE =
+        "ER_COULD_NOT_LOAD_RESOURCE";
+    public static final String ER_COULD_NOT_LOAD_METHOD_PROPERTY =
+        "ER_COULD_NOT_LOAD_METHOD_PROPERTY";
+    public static final String ER_SERIALIZER_NOT_CONTENTHANDLER =
+        "ER_SERIALIZER_NOT_CONTENTHANDLER";
+    public static final String ER_ILLEGAL_ATTRIBUTE_POSITION =
+        "ER_ILLEGAL_ATTRIBUTE_POSITION";
+    public static final String ER_ILLEGAL_CHARACTER = "ER_ILLEGAL_CHARACTER";
+
+    public static final String ER_INVALID_PORT = "ER_INVALID_PORT";
+    public static final String ER_PORT_WHEN_HOST_NULL =
+        "ER_PORT_WHEN_HOST_NULL";
+    public static final String ER_HOST_ADDRESS_NOT_WELLFORMED =
+        "ER_HOST_ADDRESS_NOT_WELLFORMED";
+    public static final String ER_SCHEME_NOT_CONFORMANT =
+        "ER_SCHEME_NOT_CONFORMANT";
+    public static final String ER_SCHEME_FROM_NULL_STRING =
+        "ER_SCHEME_FROM_NULL_STRING";
+    public static final String ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE =
+        "ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE";
+    public static final String ER_PATH_INVALID_CHAR = "ER_PATH_INVALID_CHAR";
+    public static final String ER_NO_SCHEME_INURI = "ER_NO_SCHEME_INURI";
+    public static final String ER_FRAG_INVALID_CHAR = "ER_FRAG_INVALID_CHAR";
+    public static final String ER_FRAG_WHEN_PATH_NULL =
+        "ER_FRAG_WHEN_PATH_NULL";
+    public static final String ER_FRAG_FOR_GENERIC_URI =
+        "ER_FRAG_FOR_GENERIC_URI";
+    public static final String ER_NO_SCHEME_IN_URI = "ER_NO_SCHEME_IN_URI";
+    public static final String ER_CANNOT_INIT_URI_EMPTY_PARMS =
+        "ER_CANNOT_INIT_URI_EMPTY_PARMS";
+    public static final String ER_NO_FRAGMENT_STRING_IN_PATH =
+        "ER_NO_FRAGMENT_STRING_IN_PATH";
+    public static final String ER_NO_QUERY_STRING_IN_PATH =
+        "ER_NO_QUERY_STRING_IN_PATH";
+    public static final String ER_NO_PORT_IF_NO_HOST = "ER_NO_PORT_IF_NO_HOST";
+    public static final String ER_NO_USERINFO_IF_NO_HOST =
+        "ER_NO_USERINFO_IF_NO_HOST";
+    public static final String ER_SCHEME_REQUIRED = "ER_SCHEME_REQUIRED";
+    public static final String ER_XML_VERSION_NOT_SUPPORTED = "ER_XML_VERSION_NOT_SUPPORTED";
+    public static final String ER_FACTORY_PROPERTY_MISSING = "ER_FACTORY_PROPERTY_MISSING";
+    public static final String ER_ENCODING_NOT_SUPPORTED = "ER_ENCODING_NOT_SUPPORTED";
+    // DOM Exceptions
+    public static final String ER_FEATURE_NOT_FOUND = "FEATURE_NOT_FOUND";
+    public static final String ER_FEATURE_NOT_SUPPORTED = "FEATURE_NOT_SUPPORTED";
+    public static final String ER_STRING_TOO_LONG = "DOMSTRING_SIZE_ERR";
+    public static final String ER_TYPE_MISMATCH_ERR = "TYPE_MISMATCH_ERR";  
+    
+    // DOM Level 3 load and save messages
+    public static final String ER_NO_OUTPUT_SPECIFIED = "no-output-specified";   
+    public static final String ER_UNSUPPORTED_ENCODING = "unsupported-encoding";
+    public static final String ER_ELEM_UNBOUND_PREFIX_IN_ENTREF = "unbound-prefix-in-entity-reference";
+    public static final String ER_ATTR_UNBOUND_PREFIX_IN_ENTREF = "unbound-prefix-in-entity-reference";
+    public static final String ER_CDATA_SECTIONS_SPLIT = "cdata-sections-splitted";
+    public static final String ER_WF_INVALID_CHARACTER = "wf-invalid-character";
+    public static final String ER_WF_INVALID_CHARACTER_IN_NODE_NAME = "wf-invalid-character-in-node-name";
+    
+    // DOM Level 3 Implementation specific Exceptions
+    public static final String ER_UNABLE_TO_SERIALIZE_NODE = "ER_UNABLE_TO_SERIALIZE_NODE";
+    public static final String ER_WARNING_WF_NOT_CHECKED = "ER_WARNING_WF_NOT_CHECKED";
+    
+    public static final String ER_WF_INVALID_CHARACTER_IN_COMMENT = "ER_WF_INVALID_CHARACTER_IN_COMMENT";
+    public static final String ER_WF_INVALID_CHARACTER_IN_PI = "ER_WF_INVALID_CHARACTER_IN_PI";
+    public static final String ER_WF_INVALID_CHARACTER_IN_CDATA = "ER_WF_INVALID_CHARACTER_IN_CDATA";
+    public static final String ER_WF_INVALID_CHARACTER_IN_TEXT = "ER_WF_INVALID_CHARACTER_IN_TEXT";
+    public static final String ER_WF_DASH_IN_COMMENT = "ER_WF_DASH_IN_COMMENT";
+    public static final String ER_WF_LT_IN_ATTVAL = "ER_WF_LT_IN_ATTVAL";
+    public static final String ER_WF_REF_TO_UNPARSED_ENT = "ER_WF_REF_TO_UNPARSED_ENT";
+    public static final String ER_WF_REF_TO_EXTERNAL_ENT =  "ER_WF_REF_TO_EXTERNAL_ENT";
+    public static final String ER_NS_PREFIX_CANNOT_BE_BOUND =  "ER_NS_PREFIX_CANNOT_BE_BOUND";
+    public static final String ER_NULL_LOCAL_ELEMENT_NAME = "ER_NULL_LOCAL_ELEMENT_NAME";
+    public static final String ER_NULL_LOCAL_ATTR_NAME = "ER_NULL_LOCAL_ATTR_NAME";
+    public static final String ER_WRITING_INTERNAL_SUBSET = "ER_WRITING_INTERNAL_SUBSET";
+
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages.java
new file mode 100644
index 0000000..997bfc1
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated. 
+ * 
+ * This class is not a public API, it is only public because it is 
+ * used in the serializer.
+ * 
+ * @xsl.usage internal
+ */
+public class SerializerMessages extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+    
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+    
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "The message key ''{0}'' is not in the message class ''{1}''" },
+                
+            {   MsgKey.BAD_MSGFORMAT,
+                "The format of message ''{0}'' in message class ''{1}'' failed." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "The serializer class ''{0}'' does not implement org.xml.sax.ContentHandler." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "The resource [ {0} ] could not be found.\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "The resource [ {0} ] could not load: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO, 
+                    "Buffer size <=0" }, 
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "Invalid UTF-16 surrogate detected: {0} ?" },
+
+            {   MsgKey.ER_OIERROR, 
+                "IO error" }, 
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Cannot add attribute {0} after child nodes or before an element is produced.  Attribute will be ignored." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */ 
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "Namespace for prefix ''{0}'' has not been declared." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */ 
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "Attribute ''{0}'' outside of element." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */ 
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "Namespace declaration ''{0}''=''{1}'' outside of element." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "Could not load ''{0}'' (check CLASSPATH), now using just the defaults" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Attempt to output character of integral value {0} that is not represented in specified output encoding of {1}." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "Could not load the propery file ''{0}'' for output method ''{1}'' (check CLASSPATH)" },
+
+            {   MsgKey.ER_INVALID_PORT, 
+                "Invalid port number" }, 
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "Port cannot be set when host is null" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "Host is not a well formed address" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "The scheme is not conformant." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Cannot set scheme from null string" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "Path contains invalid escape sequence" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "Path contains invalid character: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "Fragment contains invalid character" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "Fragment cannot be set when path is null" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "Fragment can only be set for a generic URI" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI, 
+                "No scheme found in URI" }, 
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "Cannot initialize URI with empty parameters" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "Fragment cannot be specified in both the path and fragment" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "Query string cannot be specified in path and query string" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "Port may not be specified if host is not specified" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Userinfo may not be specified if host is not specified" },
+
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Warning:  The version of the output document is requested to be ''{0}''.  This version of XML is not supported.  The version of the output document will be ''1.0''." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED, 
+                "Scheme is required!" },
+                
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */    
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "The Properties object passed to the SerializerFactory does not have a ''{0}'' property." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Warning:  The encoding ''{0}'' is not supported by the Java runtime." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "The parameter ''{0}'' is not recognized."},
+            
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "The parameter ''{0}'' is recognized but the requested value cannot be set."},
+            
+             {MsgKey.ER_STRING_TOO_LONG,
+             "The resulting string is too long to fit in a DOMString: ''{0}''."},  
+            
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "The value type for this parameter name is incompatible with the expected value type."},
+            
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "The output destination for data to be written to was null."},
+            
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "An unsupported encoding is encountered."},
+            
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "The node could not be serialized."},
+            
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT, 
+             "The CDATA Section contains one or more termination markers ']]>'."},
+            
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED, 
+                 "An instance of the Well-Formedness checker could not be created.  The well-formed parameter was set to true but well-formedness checking can not be performed."    
+             },
+            
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "The node ''{0}'' contains invalid XML characters."
+             },
+            
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "An invalid XML character (Unicode: 0x{0}) was found in the comment."
+             },
+            
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "An invalid XML character (Unicode: 0x{0}) was found in the processing instructiondata."
+             },             
+            
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "An invalid XML character (Unicode: 0x{0}) was found in the contents of the CDATASection."
+             },
+            
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "An invalid XML character (Unicode: 0x{0}) was found in the node''s character data content."
+             },                        
+            
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "An invalid XML character(s) was found in the {0} node named ''{1}''."
+             },
+            
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "The string \"--\" is not permitted within comments."
+             },
+            
+             {MsgKey.ER_WF_LT_IN_ATTVAL,  
+                 "The value of attribute \"{1}\" associated with an element type \"{0}\" must not contain the ''<'' character." 
+             },
+            
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,  
+                 "The unparsed entity reference \"&{0};\" is not permitted." 
+             },
+            
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,  
+                 "The external entity reference \"&{0};\" is not permitted in an attribute value." 
+             },             
+            
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "The prefix \"{0}\" can not be bound to namespace \"{1}\"."
+             },
+            
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "The local name of element \"{0}\" is null."
+             },
+            
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "The local name of attr \"{0}\" is null."
+             },             
+            
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "The replacement text of the entity node \"{0}\" contains an element node \"{1}\" with an unbound prefix \"{2}\"."
+             },
+            
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "The replacement text of the entity node \"{0}\" contains an attribute node \"{1}\" with an unbound prefix \"{2}\"."
+             },
+             
+             { MsgKey.ER_WRITING_INTERNAL_SUBSET,
+                 "An error occured while writing the internal subset."
+             },
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ca.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ca.java
new file mode 100644
index 0000000..ad5b3ae
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ca.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_ca.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_ca extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "La clau del missatge ''{0}'' no est\u00e0 a la classe del missatge ''{1}''" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "El format del missatge ''{0}'' a la classe del missatge ''{1}'' ha fallat." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "La classe de serialitzador ''{0}'' no implementa org.xml.sax.ContentHandler." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "No s''ha trobat el recurs [ {0} ].\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "No s''ha pogut carregar el recurs [ {0} ]: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Grand\u00e0ria del buffer <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "S''ha detectat un suplent UTF-16 no v\u00e0lid: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "Error d'E/S" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "No es pot afegir l''atribut {0} despr\u00e9s dels nodes subordinats o abans que es produeixi un element. Es passar\u00e0 per alt l''atribut." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "No s''ha declarat l''espai de noms pel prefix ''{0}''." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "L''atribut ''{0}'' es troba fora de l''element." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "La declaraci\u00f3 de l''espai de noms ''{0}''=''{1}'' es troba fora de l''element." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "No s''ha pogut carregar ''{0}'' (comproveu CLASSPATH), ara s''est\u00e0 fent servir els valors per defecte." },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "S''ha intentat un car\u00e0cter de sortida del valor integral {0} que no est\u00e0 representat a una codificaci\u00f3 de sortida especificada de {1}." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "No s''ha pogut carregar el fitxer de propietats ''{0}'' del m\u00e8tode de sortida ''{1}'' (comproveu CLASSPATH)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "N\u00famero de port no v\u00e0lid" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "El port no es pot establir quan el sistema principal \u00e9s nul" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "El format de l'adre\u00e7a del sistema principal no \u00e9s el correcte" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "L'esquema no t\u00e9 conformitat." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "No es pot establir un esquema des d'una cadena nul\u00b7la" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "La via d'acc\u00e9s cont\u00e9 una seq\u00fc\u00e8ncia d'escapament no v\u00e0lida" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "La via d''acc\u00e9s cont\u00e9 un car\u00e0cter no v\u00e0lid {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "El fragment cont\u00e9 un car\u00e0cter no v\u00e0lid" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "El fragment no es pot establir si la via d'acc\u00e9s \u00e9s nul\u00b7la" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "El fragment nom\u00e9s es pot establir per a un URI gen\u00e8ric" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "No s'ha trobat cap esquema a l'URI" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "No es pot inicialitzar l'URI amb par\u00e0metres buits" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "No es pot especificar un fragment tant en la via d'acc\u00e9s com en el fragment" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "No es pot especificar una cadena de consulta en la via d'acc\u00e9s i la cadena de consulta" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "No es pot especificar el port si no s'especifica el sistema principal" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "No es pot especificar informaci\u00f3 de l'usuari si no s'especifica el sistema principal" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Av\u00eds: la versi\u00f3 del document de sortida s''ha sol\u00b7licitat que sigui ''{0}''. Aquesta versi\u00f3 de XML no est\u00e0 suportada. La versi\u00f3 del document de sortida ser\u00e0 ''1.0''." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "Es necessita l'esquema" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "L''objecte de propietats passat a SerializerFactory no t\u00e9 cap propietat ''{0}''." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Av\u00eds: el temps d''execuci\u00f3 de Java no d\u00f3na suport a la codificaci\u00f3 ''{0}''." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "El par\u00e0metre ''{0}'' no es reconeix."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "El par\u00e0metre ''{0}'' es reconeix per\u00f2 el valor sol\u00b7licitat no es pot establir."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "La cadena resultant \u00e9s massa llarga per cabre en una DOMString: ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "El tipus de valor per a aquest nom de par\u00e0metre \u00e9s incompatible amb el tipus de valor esperat."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "La destinaci\u00f3 de sortida per a les dades que s'ha d'escriure era nul\u00b7la."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "S'ha trobat una codificaci\u00f3 no suportada."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "El node no s'ha pogut serialitzat."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "La secci\u00f3 CDATA cont\u00e9 un o m\u00e9s marcadors d'acabament ']]>'."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "No s'ha pogut crear cap inst\u00e0ncia per comprovar si t\u00e9 un format correcte o no. El par\u00e0metre del tipus ben format es va establir en cert, per\u00f2 la comprovaci\u00f3 de format no s'ha pogut realitzar."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "El node ''{0}'' cont\u00e9 car\u00e0cters XML no v\u00e0lids."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "S''ha trobat un car\u00e0cter XML no v\u00e0lid (Unicode: 0x{0}) en el comentari."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "S''ha trobat un car\u00e0cter XML no v\u00e0lid (Unicode: 0x{0}) en les dades d''instrucci\u00f3 de proc\u00e9s."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "S''ha trobat un car\u00e0cter XML no v\u00e0lid (Unicode: 0x''{0})'' en els continguts de la CDATASection."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "S''ha trobat un car\u00e0cter XML no v\u00e0lid (Unicode: 0x''{0})'' en el contingut de dades de car\u00e0cter del node."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "S''han trobat car\u00e0cters XML no v\u00e0lids al node {0} anomenat ''{1}''."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "La cadena \"--\" no est\u00e0 permesa dins dels comentaris."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "El valor d''atribut \"{1}\" associat amb un tipus d''element \"{0}\" no pot contenir el car\u00e0cter ''<''."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "La refer\u00e8ncia de l''entitat no analitzada \"&{0};\" no est\u00e0 permesa."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "La refer\u00e8ncia externa de l''entitat \"&{0};\" no est\u00e0 permesa en un valor d''atribut."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "El prefix \"{0}\" no es pot vincular a l''espai de noms \"{1}\"."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "El nom local de l''element \"{0}\" \u00e9s nul."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "El nom local d''atr \"{0}\" \u00e9s nul."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "El text de recanvi del node de l''entitat \"{0}\" cont\u00e9 un node d''element \"{1}\" amb un prefix de no enlla\u00e7at \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "El text de recanvi del node de l''entitat \"{0}\" cont\u00e9 un node d''atribut \"{1}\" amb un prefix de no enlla\u00e7at \"{2}\"."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_cs.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_cs.java
new file mode 100644
index 0000000..12b61bc
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_cs.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_cs.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_cs extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "Kl\u00ed\u010d zpr\u00e1vy ''{0}'' nen\u00ed obsa\u017een ve t\u0159\u00edd\u011b zpr\u00e1v ''{1}''" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "Form\u00e1t zpr\u00e1vy ''{0}'' ve t\u0159\u00edd\u011b zpr\u00e1v ''{1}'' selhal. " },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "T\u0159\u00edda serializace ''{0}'' neimplementuje obslu\u017en\u00fd program org.xml.sax.ContentHandler." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "Nelze naj\u00edt zdroj [ {0} ].\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "Nelze zav\u00e9st zdroj [ {0} ]: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Velikost vyrovn\u00e1vac\u00ed pam\u011bti <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "Byla zji\u0161t\u011bna neplatn\u00e1 n\u00e1hrada UTF-16: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "Chyba vstupu/v\u00fdstupu" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Nelze p\u0159idat atribut {0} po uzlech potomk\u016f ani p\u0159ed t\u00edm, ne\u017e je vytvo\u0159en prvek. Atribut bude ignorov\u00e1n." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "Obor n\u00e1zv\u016f pro p\u0159edponu ''{0}'' nebyl deklarov\u00e1n." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "Atribut ''{0}'' se nach\u00e1z\u00ed vn\u011b prvku." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "Deklarace oboru n\u00e1zv\u016f ''{0}''=''{1}'' se nach\u00e1z\u00ed vn\u011b prvku." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "Nelze zav\u00e9st prost\u0159edek ''{0}'' (zkontrolujte prom\u011bnnou CLASSPATH) - budou pou\u017eity pouze v\u00fdchoz\u00ed prost\u0159edky" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Byl proveden pokus o v\u00fdstup znaku s celo\u010d\u00edselnou hodnotou {0}, kter\u00e1 nen\u00ed reprezentov\u00e1na v ur\u010den\u00e9m v\u00fdstupn\u00edm k\u00f3dov\u00e1n\u00ed {1}." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "Nelze na\u010d\u00edst soubor vlastnost\u00ed ''{0}'' pro v\u00fdstupn\u00ed metodu ''{1}'' (zkontrolujte prom\u011bnnou CLASSPATH)." },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "Neplatn\u00e9 \u010d\u00edslo portu." },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "M\u00e1-li hostitel hodnotu null, nelze nastavit port." },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "Adresa hostitele m\u00e1 nespr\u00e1vn\u00fd form\u00e1t." },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "Sch\u00e9ma nevyhovuje." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Nelze nastavit sch\u00e9ma \u0159et\u011bzce s hodnotou null." },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "Cesta obsahuje neplatnou escape sekvenci" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "Cesta obsahuje neplatn\u00fd znak: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "Fragment obsahuje neplatn\u00fd znak." },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "M\u00e1-li cesta hodnotu null, nelze nastavit fragment." },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "Fragment lze nastavit jen u generick\u00e9ho URI." },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "V URI nebylo nalezeno \u017e\u00e1dn\u00e9 sch\u00e9ma" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "URI nelze inicializovat s pr\u00e1zdn\u00fdmi parametry." },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "Fragment nelze ur\u010dit z\u00e1rove\u0148 v cest\u011b i ve fragmentu." },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "V \u0159et\u011bzci cesty a dotazu nelze zadat \u0159et\u011bzec dotazu." },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "Nen\u00ed-li ur\u010den hostitel, nelze zadat port." },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Nen\u00ed-li ur\u010den hostitel, nelze zadat \u00fadaje o u\u017eivateli." },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Varov\u00e1n\u00ed: Je po\u017eadov\u00e1na verze ''{0}'' v\u00fdstupn\u00edho dokumentu. Tato verze form\u00e1tu XML nen\u00ed podporov\u00e1na. Bude pou\u017eita verze ''1.0'' v\u00fdstupn\u00edho dokumentu. " },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "Je vy\u017eadov\u00e1no sch\u00e9ma!" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "Objekt vlastnost\u00ed p\u0159edan\u00fd faktorii SerializerFactory neobsahuje vlastnost ''{0}''. " },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Varov\u00e1n\u00ed: K\u00f3dov\u00e1n\u00ed ''{0}'' nen\u00ed v b\u011bhov\u00e9m prost\u0159ed\u00ed Java podporov\u00e1no." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "Parametr ''{0}'' nebyl rozpozn\u00e1n."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "Parametr ''{0}'' byl rozpozn\u00e1n, ale nelze nastavit po\u017eadovanou hodnotu."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "V\u00fdsledn\u00fd \u0159et\u011bzec je p\u0159\u00edli\u0161 dlouh\u00fd pro \u0159et\u011bzec DOMString: ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "Typ hodnoty pro tento n\u00e1zev parametru nen\u00ed kompatibiln\u00ed s o\u010dek\u00e1van\u00fdm typem hodnoty."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "C\u00edlov\u00e9 um\u00edst\u011bn\u00ed v\u00fdstupu pro data ur\u010den\u00e1 k z\u00e1pisu je rovno hodnot\u011b Null. "},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "Bylo nalezeno nepodporovan\u00e9 k\u00f3dov\u00e1n\u00ed."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "Nelze prov\u00e9st serializaci uzlu. "},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "Sekce CDATA obsahuje jednu nebo v\u00edce ukon\u010dovac\u00edch zna\u010dek ']]>'."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "Nelze vytvo\u0159it instanci modulu pro kontrolu spr\u00e1vn\u00e9ho utvo\u0159en\u00ed. Parametr spr\u00e1vn\u00e9ho utvo\u0159en\u00ed byl nastaven na hodnotu true, nepoda\u0159ilo se v\u0161ak zkontrolovat spr\u00e1vnost utvo\u0159en\u00ed. "
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "Uzel ''{0}'' obsahuje neplatn\u00e9 znaky XML. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "V pozn\u00e1mce byl zji\u0161t\u011bn neplatn\u00fd znak XML (Unicode: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "V datech instrukce zpracov\u00e1n\u00ed byl nalezen neplatn\u00fd znak XML (Unicode: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "V odd\u00edlu CDATASection byl nalezen neplatn\u00fd znak XML (Unicode: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "V obsahu znakov\u00fdch dat uzlu byl nalezen neplatn\u00fd znak XML (Unicode: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "V objektu {0} s n\u00e1zvem ''{1}'' byl nalezen neplatn\u00fd znak XML. "
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "V pozn\u00e1mk\u00e1ch nen\u00ed povolen \u0159et\u011bzec \"--\"."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "Hodnota atributu \"{1}\" souvisej\u00edc\u00edho s typem prvku \"{0}\" nesm\u00ed obsahovat znak ''<''."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "Odkaz na neanalyzovanou entitu \"&{0};\" nen\u00ed povolen."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "Extern\u00ed odkaz na entitu \"&{0};\" nen\u00ed v hodnot\u011b atributu povolen."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "P\u0159edpona \"{0}\" nesm\u00ed b\u00fdt v\u00e1zan\u00e1 k oboru n\u00e1zv\u016f \"{1}\"."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "Lok\u00e1ln\u00ed n\u00e1zev prvku \"{0}\" m\u00e1 hodnotu Null. "
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "Lok\u00e1ln\u00ed n\u00e1zev atributu \"{0}\" m\u00e1 hodnotu Null. "
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "Nov\u00fd text uzlu entity \"{0}\" obsahuje uzel prvku \"{1}\" s nesv\u00e1zanou p\u0159edponou \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "Nov\u00fd text uzlu entity \"{0}\" obsahuje uzel atributu \"{1}\" s nesv\u00e1zanou p\u0159edponou \"{2}\". "
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_de.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_de.java
new file mode 100644
index 0000000..e583e87
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_de.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_de.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_de extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "Der Nachrichtenschl\u00fcssel ''{0}'' ist nicht in der Nachrichtenklasse ''{1}'' enthalten." },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "Das Format der Nachricht ''{0}'' in der Nachrichtenklasse ''{1}'' ist fehlgeschlagen." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "Die Parallel-Seriell-Umsetzerklasse ''{0}'' implementiert org.xml.sax.ContentHandler nicht." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "Die Ressource [ {0} ] konnte nicht gefunden werden.\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "Die Ressource [ {0} ] konnte nicht geladen werden: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Puffergr\u00f6\u00dfe <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "Ung\u00fcltige UTF-16-Ersetzung festgestellt: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "E/A-Fehler" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Attribut {0} kann nicht nach Kindknoten oder vor dem Erstellen eines Elements hinzugef\u00fcgt werden.  Das Attribut wird ignoriert." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "Der Namensbereich f\u00fcr Pr\u00e4fix ''{0}'' wurde nicht deklariert." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "Attribut ''{0}'' befindet sich nicht in einem Element." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "Namensbereichdeklaration ''{0}''=''{1}'' befindet sich nicht in einem Element." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "''{0}'' konnte nicht geladen werden (CLASSPATH pr\u00fcfen). Es werden die Standardwerte verwendet." },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Es wurde versucht, ein Zeichen des Integralwerts {0} auszugeben, der nicht in der angegebenen Ausgabeverschl\u00fcsselung von {1} dargestellt ist." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "Die Merkmaldatei ''{0}'' konnte f\u00fcr die Ausgabemethode ''{1}'' nicht geladen werden (CLASSPATH pr\u00fcfen)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "Ung\u00fcltige Portnummer" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "Der Port kann nicht festgelegt werden, wenn der Host gleich Null ist." },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "Der Host ist keine syntaktisch korrekte Adresse." },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "Das Schema ist nicht angepasst." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Schema kann nicht von Nullzeichenfolge festgelegt werden." },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "Der Pfad enth\u00e4lt eine ung\u00fcltige Escapezeichenfolge." },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "Pfad enth\u00e4lt ung\u00fcltiges Zeichen: {0}." },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "Fragment enth\u00e4lt ein ung\u00fcltiges Zeichen." },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "Fragment kann nicht festgelegt werden, wenn der Pfad gleich Null ist." },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "Fragment kann nur f\u00fcr eine generische URI (Uniform Resource Identifier) festgelegt werden." },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "Kein Schema gefunden in URI" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "URI (Uniform Resource Identifier) kann nicht mit leeren Parametern initialisiert werden." },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "Fragment kann nicht im Pfad und im Fragment angegeben werden." },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "Abfragezeichenfolge kann nicht im Pfad und in der Abfragezeichenfolge angegeben werden." },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "Der Port kann nicht angegeben werden, wenn der Host nicht angegeben wurde." },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Benutzerinformationen k\u00f6nnen nicht angegeben werden, wenn der Host nicht angegeben wurde." },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Warnung: Die Version des Ausgabedokuments muss ''{0}'' lauten.  Diese XML-Version wird nicht unterst\u00fctzt.  Die Version des Ausgabedokuments ist ''1.0''." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "Schema ist erforderlich!" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "Das an SerializerFactory \u00fcbermittelte Merkmalobjekt weist kein Merkmal ''{0}'' auf." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Warnung:  Die Codierung ''{0}'' wird von Java Runtime nicht unterst\u00fctzt." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "Der Parameter ''{0}'' wird nicht erkannt."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "Der Parameter ''{0}'' wird erkannt, der angeforderte Wert kann jedoch nicht festgelegt werden."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "Die Ergebniszeichenfolge ist zu lang f\u00fcr eine DOM-Zeichenfolge: ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "Der Werttyp f\u00fcr diesen Parameternamen ist nicht kompatibel mit dem erwarteten Werttyp."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "Das Ausgabeziel f\u00fcr die zu schreibenden Daten war leer."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "Eine nicht unterst\u00fctzte Codierung wurde festgestellt."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "Der Knoten konnte nicht serialisiert werden."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "Der Abschnitt CDATA enth\u00e4lt mindestens eine Beendigungsmarkierung ']]>'."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "Eine Instanz des Pr\u00fcfprogramms f\u00fcr korrekte Formatierung konnte nicht erstellt werden.  F\u00fcr den korrekt formatierten Parameter wurde der Wert 'True' festgelegt, die Pr\u00fcfung auf korrekte Formatierung kann jedoch nicht durchgef\u00fchrt werden."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "Der Knoten ''{0}'' enth\u00e4lt ung\u00fcltige XML-Zeichen."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "Im Kommentar wurde ein ung\u00fcltiges XML-Zeichen (Unicode: 0x{0}) gefunden."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "In der Verarbeitungsanweisung wurde ein ung\u00fcltiges XML-Zeichen (Unicode: 0x{0}) gefunden."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "Im Inhalt von CDATASection wurde ein ung\u00fcltiges XML-Zeichen (Unicode: 0x{0}) gefunden."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "Ein ung\u00fcltiges XML-Zeichen  (Unicode: 0x{0}) wurde im Inhalt der Zeichendaten des Knotens gefunden."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "Ung\u00fcltige XML-Zeichen wurden gefunden in {0} im Knoten ''{1}''."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "Die Zeichenfolge \"--\" ist innerhalb von Kommentaren nicht zul\u00e4ssig."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "Der Wert des Attributs \"{1}\" mit einem Elementtyp \"{0}\" darf nicht das Zeichen ''<'' enthalten."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "Der syntaktisch nicht analysierte Entit\u00e4tenverweis \"&{0};\" ist nicht zul\u00e4ssig."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "Der externe Entit\u00e4tenverweis \"&{0};\" ist in einem Attributwert nicht zul\u00e4ssig."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "Das Pr\u00e4fix \"{0}\" kann nicht an den Namensbereich \"{1}\" gebunden werden."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "Der lokale Name von Element \"{0}\" ist nicht angegeben."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "Der lokale Name des Attributs \"{0}\" ist nicht angegeben."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "Der Ersatztext des Entit\u00e4tenknotens \"{0}\" enth\u00e4lt einen Elementknoten \"{1}\" mit einem nicht gebundenen Pr\u00e4fix \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "Der Ersatztext des Entit\u00e4tenknotens \"{0}\" enth\u00e4lt einen Attributknoten \"{1}\" mit einem nicht gebundenen Pr\u00e4fix \"{2}\"."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_en.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_en.java
new file mode 100644
index 0000000..f79d1c6
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_en.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_en.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+
+/**
+ * Default implementation.  This is just an empty class.
+ * @xsl.usage internal
+ */
+public final class SerializerMessages_en extends SerializerMessages
+{
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_es.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_es.java
new file mode 100644
index 0000000..7f69976
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_es.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_es.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_es extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "La clave de mensaje ''{0}'' no est\u00e1 en la clase de mensaje ''{1}''" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "Se ha producido un error en el formato de mensaje ''{0}'' de la clase de mensaje ''{1}''." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "La clase serializer ''{0}'' no implementa org.xml.sax.ContentHandler." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "No se ha podido encontrar el recurso [ {0} ].\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "No se ha podido cargar el recurso [ {0} ]: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Tama\u00f1o de almacenamiento intermedio <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "\u00bfSe ha detectado un sustituto UTF-16 no v\u00e1lido: {0}?" },
+
+            {   MsgKey.ER_OIERROR,
+                "Error de ES" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "No se puede a\u00f1adir el atributo {0} despu\u00e9s de nodos hijo o antes de que se produzca un elemento.  Se ignorar\u00e1 el atributo." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "No se ha declarado el espacio de nombres para el prefijo ''{0}''." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "Atributo ''{0}'' fuera del elemento." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "Declaraci\u00f3n del espacio de nombres ''{0}''=''{1}'' fuera del elemento." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "No se ha podido cargar ''{0}'' (compruebe la CLASSPATH), ahora s\u00f3lo se est\u00e1n utilizando los valores predeterminados" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Se ha intentado dar salida a un car\u00e1cter del valor integral {0} que no est\u00e1 representado en la codificaci\u00f3n de salida especificada de {1}." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "No se ha podido cargar el archivo de propiedades ''{0}'' para el m\u00e9todo de salida ''{1}'' (compruebe la CLASSPATH)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "N\u00famero de puerto no v\u00e1lido" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "No se puede establecer el puerto si el sistema principal es nulo" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "El sistema principal no es una direcci\u00f3n bien formada" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "El esquema no es compatible." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "No se puede establecer un esquema de una serie nula" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "La v\u00eda de acceso contiene una secuencia de escape no v\u00e1lida" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "La v\u00eda de acceso contiene un car\u00e1cter no v\u00e1lido: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "El fragmento contiene un car\u00e1cter no v\u00e1lido" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "No se puede establecer el fragmento si la v\u00eda de acceso es nula" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "S\u00f3lo se puede establecer el fragmento para un URI gen\u00e9rico" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "No se ha encontrado un esquema en el URI" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "No se puede inicializar el URI con par\u00e1metros vac\u00edos" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "No se puede especificar el fragmento en la v\u00eda de acceso y en el fragmento" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "No se puede especificar la serie de consulta en la v\u00eda de acceso y en la serie de consulta" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "No se puede especificar el puerto si no se ha especificado el sistema principal" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "No se puede especificar la informaci\u00f3n de usuario si no se ha especificado el sistema principal" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Aviso: la versi\u00f3n del documento de salida tiene que ser ''{0}''.  No se admite esta versi\u00f3n de XML.  La versi\u00f3n del documento de salida ser\u00e1 ''1.0''." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "\u00a1Se necesita un esquema!" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "El objeto Properties pasado a SerializerFactory no tiene una propiedad ''{0}''." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Aviso: La codificaci\u00f3n ''{0}'' no est\u00e1 soportada por Java Runtime." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "El par\u00e1metro ''{0}'' no se reconoce."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "Se reconoce el par\u00e1metro ''{0}'' pero no puede establecerse el valor solicitado."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "La serie producida es demasiado larga para ajustarse a DOMString: ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "El tipo de valor para este nombre de par\u00e1metro es incompatible con el tipo de valor esperado."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "El destino de salida de escritura de los datos es nulo."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "Se ha encontrado una codificaci\u00f3n no soportada."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "No se ha podido serializar el nodo."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "La secci\u00f3n CDATA contiene uno o m\u00e1s marcadores ']]>' de terminaci\u00f3n."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "No se ha podido crear una instancia del comprobador de gram\u00e1tica correcta.  El par\u00e1metro well-formed se ha establecido en true pero no se puede realizar la comprobaci\u00f3n de gram\u00e1tica correcta."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "El nodo ''{0}'' contiene caracteres XML no v\u00e1lidos."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "Se ha encontrado un car\u00e1cter XML no v\u00e1lido (Unicode: 0x{0}) en el comentario."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "Se ha encontrado un car\u00e1cter XML no v\u00e1lido (Unicode: 0x{0}) en los datos de la instrucci\u00f3n de proceso."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "Se ha encontrado un car\u00e1cter XML no v\u00e1lido (Unicode: 0x{0}) en el contenido de CDATASection."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "Se ha encontrado un car\u00e1cter XML no v\u00e1lido (Unicode: 0x{0}) en el contenido de datos de caracteres del nodo."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "Se ha encontrado un car\u00e1cter o caracteres XML no v\u00e1lidos en el nodo {0} denominado ''{1}''."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "No se permite la serie \"--\" dentro de los comentarios."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "El valor del atributo \"{1}\" asociado a un tipo de elemento \"{0}\" no debe contener el car\u00e1cter ''''<''''."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "No se permite la referencia de entidad no analizada \"&{0};\"."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "La referencia de entidad externa \"&{0};\" no est\u00e1 permitida en un valor de atributo."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "No se puede encontrar el prefijo \"{0}\" en el espacio de nombres \"{1}\"."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "El nombre local del elemento \"{0}\" es null."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "El nombre local del atributo \"{0}\" es null."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "El texto de sustituci\u00f3n del nodo de entidad \"{0}\" contiene un nodo de elemento \"{1}\" con un prefijo no enlazado \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "El texto de sustituci\u00f3n del nodo de entidad \"{0}\" contiene un nodo de atributo \"{1}\" con un prefijo no enlazado \"{2}\"."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_fr.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_fr.java
new file mode 100644
index 0000000..1f45524
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_fr.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_fr.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_fr extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "La cl\u00e9 du message ''{0}'' ne se trouve pas dans la classe du message ''{1}''" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "Le format du message ''{0}'' de la classe du message ''{1}'' est incorrect." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "La classe de la m\u00e9thode de s\u00e9rialisation ''{0}'' n''impl\u00e9mente pas org.xml.sax.ContentHandler." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "La ressource [ {0} ] est introuvable.\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "La ressource [ {0} ] n''a pas pu charger : {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Taille du tampon <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "Substitut UTF-16 non valide d\u00e9tect\u00e9 : {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "Erreur d'E-S" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Ajout impossible de l''attribut {0} apr\u00e8s des noeuds enfants ou avant la production d''un \u00e9l\u00e9ment.  L''attribut est ignor\u00e9." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "L''espace de noms du pr\u00e9fixe ''{0}'' n''a pas \u00e9t\u00e9 d\u00e9clar\u00e9." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "L''attribut ''{0}'' est \u00e0 l''ext\u00e9rieur de l''\u00e9l\u00e9ment." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "La d\u00e9claration d''espace de noms ''{0}''=''{1}'' est \u00e0 l''ext\u00e9rieur de l''\u00e9l\u00e9ment." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "Impossible de charger ''{0}'' (v\u00e9rifier CLASSPATH), les valeurs par d\u00e9faut sont donc employ\u00e9es" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Tentative de sortie d''un caract\u00e8re de la valeur enti\u00e8re {0} non repr\u00e9sent\u00e9e dans l''encodage de sortie de {1}." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "Impossible de charger le fichier de propri\u00e9t\u00e9s ''{0}'' pour la m\u00e9thode de sortie ''{1}'' (v\u00e9rifier CLASSPATH)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "Num\u00e9ro de port non valide" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "Le port ne peut \u00eatre d\u00e9fini quand l'h\u00f4te est vide" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "L'h\u00f4te n'est pas une adresse bien form\u00e9e" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "Le processus n'est pas conforme." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Impossible de d\u00e9finir le processus \u00e0 partir de la cha\u00eene vide" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "Le chemin d'acc\u00e8s contient une s\u00e9quence d'\u00e9chappement non valide" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "Le chemin contient un caract\u00e8re non valide : {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "Le fragment contient un caract\u00e8re non valide" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "Le fragment ne peut \u00eatre d\u00e9fini quand le chemin d'acc\u00e8s est vide" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "Le fragment ne peut \u00eatre d\u00e9fini que pour un URI g\u00e9n\u00e9rique" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "Processus introuvable dans l'URI" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "Impossible d'initialiser l'URI avec des param\u00e8tres vides" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "Le fragment ne doit pas \u00eatre indiqu\u00e9 \u00e0 la fois dans le chemin et dans le fragment" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "La cha\u00eene de requ\u00eate ne doit pas figurer dans un chemin et une cha\u00eene de requ\u00eate" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "Le port peut ne pas \u00eatre sp\u00e9cifi\u00e9 si l'h\u00f4te n'est pas sp\u00e9cifi\u00e9" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Userinfo ne peut \u00eatre sp\u00e9cifi\u00e9 si l'h\u00f4te ne l'est pas" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Avertissement : La version du document de sortie doit \u00eatre ''{0}''.  Cette version XML n''est pas prise en charge.  La version du document de sortie sera ''1.0''." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "Processus requis !" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "L''objet Properties transmis \u00e0 SerializerFactory ne dispose pas de propri\u00e9t\u00e9 ''{0}''." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Avertissement : Le codage ''{0}'' n''est pas pris en charge par l''environnement d''ex\u00e9cution Java." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "Le param\u00e8tre ''{0}'' n''est pas reconnu."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "Le param\u00e8tre ''{0}'' est reconnu mas la valeur demand\u00e9e ne peut pas \u00eatre d\u00e9finie."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "La cha\u00eene obtenue est trop longue pour un DOMString : ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "Le type de valeur de ce param\u00e8tre est incompatible avec le type de valeur attendu."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "La sortie de destination des donn\u00e9es \u00e0 \u00e9crire \u00e9tait vide."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "Codage non pris en charge."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "Le noeud ne peut pas \u00eatre s\u00e9rialis\u00e9."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "La section CDATA contient un ou plusieurs marqueurs de fin ']]>'."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "Aucune instance du programme de v\u00e9rification de la formation n'a pu \u00eatre cr\u00e9\u00e9e.  La valeur true a \u00e9t\u00e9 attribu\u00e9e au param\u00e8tre well-formed mais la v\u00e9rification de la formation n'a pas pu \u00eatre effectu\u00e9e."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "Le noeud ''{0}'' contient des caract\u00e8res XML non valides."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "Un caract\u00e8re XML non valide (Unicode : 0x{0}) a \u00e9t\u00e9 trouv\u00e9 dans le commentaire."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "Un caract\u00e8re XML non valide (Unicode : 0x{0}) a \u00e9t\u00e9 trouv\u00e9 dans les donn\u00e9es de l''instruction de traitement."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "Un caract\u00e8re XML non valide (Unicode: 0x{0}) a \u00e9t\u00e9 trouv\u00e9 dans le contenu de la CDATASection"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "Un caract\u00e8re XML non valide (Unicode : 0x{0}) a \u00e9t\u00e9 trouv\u00e9 dans le contenu des donn\u00e9es de type caract\u00e8res du noeud."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "Un ou plusieurs caract\u00e8res non valides ont \u00e9t\u00e9 trouv\u00e9s dans le noeud {0} nomm\u00e9 ''{1}''."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "La cha\u00eene \"--\" est interdite dans des commentaires."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "La valeur de l''attribut \"{1}\" associ\u00e9 \u00e0 un type d''\u00e9l\u00e9ment \"{0}\" ne doit pas contenir le caract\u00e8re ''<''."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "La r\u00e9f\u00e9rence d''entit\u00e9 non analys\u00e9e \"&{0};\" n''est pas admise."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "La r\u00e9f\u00e9rence d''entit\u00e9 externe \"&{0};\" n''est pas admise dans une valeur d''attribut."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "Le pr\u00e9fixe \"{0}\" ne peut pas \u00eatre li\u00e9 \u00e0 l''espace de noms \"{1}\"."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "Le nom local de l''\u00e9l\u00e9ment \"{0}\" a une valeur null."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "Le nom local de l''attribut \"{0}\" a une valeur null."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "le texte de remplacement du noeud de l''entit\u00e9 \"{0}\" contaient un noeud d''\u00e9l\u00e9ment \"{1}\" avec un pr\u00e9fixe non li\u00e9 \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "Le texte de remplacement du noeud de l''entit\u00e9 \"{0}\" contient un noeud d''attribut \"{1}\" avec un pr\u00e9fixe non li\u00e9 \"{2}\"."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_hu.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_hu.java
new file mode 100644
index 0000000..3e3c60e
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_hu.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_hu.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_hu extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "A(z) ''{0}'' \u00fczenetkulcs nem tal\u00e1lhat\u00f3 a(z) ''{1}'' \u00fczenetoszt\u00e1lyban." },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "A(z) ''{1}'' \u00fczenetoszt\u00e1ly ''{0}'' \u00fczenet\u00e9nek form\u00e1tuma hib\u00e1s." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "A(z) ''{0}'' p\u00e9ld\u00e1nyos\u00edt\u00f3 oszt\u00e1ly nem val\u00f3s\u00edtja meg az org.xml.sax.ContentHandler f\u00fcggv\u00e9nyt." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "A(z) [ {0} ] er\u0151forr\u00e1s nem tal\u00e1lhat\u00f3.\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "A(z) [ {0} ] er\u0151forr\u00e1st nem lehet bet\u00f6lteni: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Pufferm\u00e9ret <= 0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "\u00c9rv\u00e9nytelen UTF-16 helyettes\u00edt\u00e9s: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "IO hiba" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Nem lehet {0} attrib\u00fatumot hozz\u00e1adni ut\u00f3d csom\u00f3pontok ut\u00e1n vagy egy elem el\u0151\u00e1ll\u00edt\u00e1sa el\u0151tt.  Az attrib\u00fatum figyelmen k\u00edv\u00fcl marad." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "A(z) ''{0}'' el\u0151tag n\u00e9vtere nincs deklar\u00e1lva." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "A(z) ''{0}'' attrib\u00fatum k\u00edv\u00fcl esik az elemen." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "A(z) ''{0}''=''{1}'' n\u00e9vt\u00e9rdeklar\u00e1ci\u00f3 k\u00edv\u00fcl esik az elemen." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "Nem lehet bet\u00f6lteni ''{0}'' er\u0151forr\u00e1st (ellen\u0151rizze a CLASSPATH be\u00e1ll\u00edt\u00e1st), a rendszer az alap\u00e9rtelmez\u00e9seket haszn\u00e1lja." },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "K\u00eds\u00e9rletet tett {0} \u00e9rt\u00e9k\u00e9nek karakteres ki\u00edr\u00e1s\u00e1ra, de nem jelen\u00edthet\u0151 meg a megadott {1} kimeneti k\u00f3dol\u00e1ssal." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "Nem lehet bet\u00f6lteni a(z) ''{0}'' tulajdons\u00e1gf\u00e1jlt a(z) ''{1}'' met\u00f3dushoz (ellen\u0151rizze a CLASSPATH be\u00e1ll\u00edt\u00e1st)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "\u00c9rv\u00e9nytelen portsz\u00e1m" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "A portot nem \u00e1ll\u00edthatja be, ha a hoszt null" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "A hoszt nem j\u00f3l form\u00e1zott c\u00edm" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "A s\u00e9ma nem megfelel\u0151." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Nem lehet be\u00e1ll\u00edtani a s\u00e9m\u00e1t null karaktersorozatb\u00f3l" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "Az el\u00e9r\u00e9si \u00fat \u00e9rv\u00e9nytelen vez\u00e9rl\u0151 jelsorozatot tartalmaz" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "Az el\u00e9r\u00e9si \u00fat \u00e9rv\u00e9nytelen karaktert tartalmaz: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "A t\u00f6red\u00e9k \u00e9rv\u00e9nytelen karaktert tartalmaz" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "A t\u00f6red\u00e9ket nem \u00e1ll\u00edthatja be, ha az el\u00e9r\u00e9si \u00fat null" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "Csak \u00e1ltal\u00e1nos URI-hoz \u00e1ll\u00edthat be t\u00f6red\u00e9ket" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "Nem tal\u00e1lhat\u00f3 s\u00e9ma az URI-ban" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "Az URI nem inicializ\u00e1lhat\u00f3 \u00fcres param\u00e9terekkel" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "Nem adhat meg t\u00f6red\u00e9ket az el\u00e9r\u00e9si \u00fatban \u00e9s a t\u00f6red\u00e9kben is" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "Nem adhat meg lek\u00e9rdez\u00e9si karaktersorozatot az el\u00e9r\u00e9si \u00fatban \u00e9s a lek\u00e9rdez\u00e9si karaktersorozatban" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "Nem adhatja meg a portot, ha nincs megadva hoszt" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Nem adhatja meg a felhaszn\u00e1l\u00f3i inform\u00e1ci\u00f3kat, ha nincs megadva hoszt" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Figyelmeztet\u00e9s: A kimeneti dokumentum k\u00e9rt verzi\u00f3ja ''{0}''.  Az XML ezen verzi\u00f3ja nem t\u00e1mogatott.  A kimeneti dokumentum verzi\u00f3ja ''1.0'' lesz." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "S\u00e9m\u00e1ra van sz\u00fcks\u00e9g!" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "A SerializerFactory oszt\u00e1lynak \u00e1tadott Properties objektumnak nincs ''{0}'' tulajdons\u00e1ga." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Figyelmeztet\u00e9s: A(z) ''{0}'' k\u00f3dol\u00e1st nem t\u00e1mogatja a Java fut\u00e1si k\u00f6rnyezet." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "A(z) ''{0}'' param\u00e9ter nem ismerhet\u0151 fel."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "A(z) ''{0}'' param\u00e9ter ismert, de a k\u00e9rt \u00e9rt\u00e9k nem \u00e1ll\u00edthat\u00f3 be."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "A l\u00e9trej\u00f6v\u0151 karaktersorozat t\u00fal hossz\u00fa, nem f\u00e9r el egy DOMString-ben: ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "A param\u00e9tern\u00e9v \u00e9rt\u00e9k\u00e9nek t\u00edpusa nem kompatibilis a v\u00e1rt t\u00edpussal."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "Az adatki\u00edr\u00e1s c\u00e9ljak\u00e9nt megadott \u00e9rt\u00e9k \u00fcres volt."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "Nem t\u00e1mogatott k\u00f3dol\u00e1s."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "A csom\u00f3pont nem p\u00e9ld\u00e1nyos\u00edthat\u00f3."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "A CDATA szakasz legal\u00e1bb egy ']]>' lez\u00e1r\u00f3 jelz\u0151t tartalmaz."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "A szab\u00e1lyos form\u00e1z\u00e1st ellen\u0151rz\u0151 p\u00e9ld\u00e1nyt nem siker\u00fclt l\u00e9trehozni.  A well-formed param\u00e9ter \u00e9rt\u00e9ke true, de a szab\u00e1lyos form\u00e1z\u00e1st nem lehet ellen\u0151rizni."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "A(z) ''{0}'' csom\u00f3pont \u00e9rv\u00e9nytelen XML karaktereket tartalmaz."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "\u00c9rv\u00e9nytelen XML karakter (Unicode: 0x{0}) szerepelt a megjegyz\u00e9sben."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "\u00c9rv\u00e9nytelen XML karakter (Unicode: 0x{0}) szerepelt a feldolgoz\u00e1si utas\u00edt\u00e1sadatokban."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "\u00c9rv\u00e9nytelen XML karakter (Unicode: 0x{0}) szerepelt a CDATASection tartalm\u00e1ban."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "\u00c9rv\u00e9nytelen XML karakter (Unicode: 0x{0}) szerepelt a csom\u00f3pont karakteradat tartalm\u00e1ban."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "\u00c9rv\u00e9nytelen XML karakter tal\u00e1lhat\u00f3 a(z) ''{1}'' nev\u0171 {0} csom\u00f3pontban."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "A \"--\" karaktersorozat nem megengedett a megjegyz\u00e9sekben."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "A(z) \"{0}\" elemt\u00edpussal t\u00e1rs\u00edtott \"{1}\" attrib\u00fatum \u00e9rt\u00e9ke nem tartalmazhat ''<'' karaktert."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "Az \u00e9rtelmez\u00e9s n\u00e9lk\u00fcli \"&{0};\" entit\u00e1shivatkoz\u00e1s nem megengedett."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "A(z) \"&{0};\" k\u00fcls\u0151 entit\u00e1shivatkoz\u00e1s nem megengedett egy attrib\u00fatum\u00e9rt\u00e9kben."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "A(z) \"{0}\" el\u0151tag nem k\u00f6thet\u0151 a(z) \"{1}\" n\u00e9vt\u00e9rhez."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "A(z) \"{0}\" elem helyi neve null."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "A(z) \"{0}\" attrib\u00fatum helyi neve null."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "A(z) \"{0}\" entit\u00e1scsom\u00f3pont helyettes\u00edt\u0151 sz\u00f6vege a(z) \"{1}\" elemcsom\u00f3pontot tartalmazza, amelynek nem k\u00f6t\u00f6tt el\u0151tagja \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "A(z) \"{0}\" entit\u00e1scsom\u00f3pont helyettes\u00edt\u0151 sz\u00f6vege a(z) \"{1}\" attrib\u00fatum-csom\u00f3pontot tartalmazza, amelynek nem k\u00f6t\u00f6tt el\u0151tagja \"{2}\"."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_it.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_it.java
new file mode 100644
index 0000000..d116bd9
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_it.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_it.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_it extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "La chiave messaggio ''{0}'' non si trova nella classe del messaggio ''{1}''" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "Il formato del messaggio ''{0}'' nella classe del messaggio ''{1}'' non \u00e8 riuscito." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "La classe del serializzatore ''{0}'' non implementa org.xml.sax.ContentHandler." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "Risorsa [ {0} ] non trovata.\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "Impossibile caricare la risorsa [ {0} ]: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Dimensione buffer <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "Rilevato surrogato UTF-16 non valido: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "Errore IO" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Impossibile aggiungere l''''attributo {0} dopo i nodi secondari o prima che sia prodotto un elemento.  L''''attributo verr\u00e0 ignorato." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "Lo spazio nomi per il prefisso ''{0}'' non \u00e8 stato dichiarato." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "L''''attributo ''{0}'' al di fuori dell''''elemento." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "Dichiarazione dello spazio nome ''{0}''=''{1}'' al di fuori dell''''elemento." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "Impossibile caricare ''{0}'' (verificare CLASSPATH), verranno utilizzati i valori predefiniti" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Tentare di generare l''''output del carattere di valor integrale {0} che non \u00e8 rappresentato nella codifica di output specificata di {1}." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "Impossibile caricare il file delle propriet\u00e0 ''{0}'' per il metodo di emissione ''{1}'' (verificare CLASSPATH)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "Numero di porta non valido" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "La porta non pu\u00f2 essere impostata se l'host \u00e8 nullo" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "Host non \u00e8 un'indirizzo corretto" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "Lo schema non \u00e8 conforme." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Impossibile impostare lo schema da una stringa nulla" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "Il percorso contiene sequenza di escape non valida" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "Il percorso contiene un carattere non valido: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "Il frammento contiene un carattere non valido" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "Il frammento non pu\u00f2 essere impostato se il percorso \u00e8 nullo" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "Il frammento pu\u00f2 essere impostato solo per un URI generico" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "Non \u00e8 stato trovato alcuno schema nell'URI" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "Impossibile inizializzare l'URI con i parametri vuoti" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "Il frammento non pu\u00f2 essere specificato sia nel percorso che nel frammento" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "La stringa di interrogazione non pu\u00f2 essere specificata nella stringa di interrogazione e percorso." },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "La porta non pu\u00f2 essere specificata se l'host non S specificato" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Userinfo non pu\u00f2 essere specificato se l'host non S specificato" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Attenzione:  La versione del documento di emissione \u00e8 obbligatorio che sia ''{0}''.  Questa versione di XML non \u00e8 supportata.  La versione del documento di emissione sar\u00e0 ''1.0''." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "Lo schema \u00e8 obbligatorio." },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "L''''oggetto Properties passato al SerializerFactory non ha una propriet\u00e0 ''{0}''." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Avvertenza:  La codifica ''{0}'' non \u00e8 supportata da Java runtime." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "Il parametro ''{0}'' non \u00e8 riconosciuto."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "Il parametro ''{0}'' \u00e8 riconosciuto ma non \u00e8 possibile impostare il valore richiesto."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "La stringa risultante \u00e8 troppo lunga per essere inserita in DOMString: ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "Il tipo di valore per questo nome di parametro non \u00e8 compatibile con il tipo di valore previsto."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "La destinazione di output in cui scrivere i dati era nulla."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "\u00c8 stata rilevata una codifica non supportata."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "Impossibile serializzare il nodo."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "La Sezione CDATA contiene uno o pi\u00f9 markers di termine ']]>'."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "Impossibile creare un'istanza del controllore Well-Formedness.  Il parametro well-formed \u00e8 stato impostato su true ma non \u00e8 possibile eseguire i controlli well-formedness."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "Il nodo ''{0}'' contiene caratteri XML non validi."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "Trovato un carattere XML non valido (Unicode: 0x{0}) nel commento."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "Carattere XML non valido (Unicode: 0x{0}) rilevato nell''elaborazione di instructiondata."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "Carattere XML non valido (Unicode: 0x{0}) rilevato nel contenuto di CDATASection."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "Carattere XML non valido (Unicode: 0x{0}) rilevato nel contenuto dati di caratteri del nodo. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "Carattere XML non valido rilevato nel nodo {0} denominato ''{1}''."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "La stringa \"--\" non \u00e8 consentita nei commenti."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "Il valore dell''''attributo \"{1}\" associato con un tipo di elemento \"{0}\" non deve contenere il carattere ''<''."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "Il riferimento entit\u00e0 non analizzata \"&{0};\" non \u00e8 permesso."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "Il riferimento all''''entit\u00e0 esterna \"&{0};\" non \u00e8 permesso in un valore attributo."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "Il prefisso \"{0}\" non pu\u00f2 essere associato allo spazio nome \"{1}\"."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "Il nome locale dell''''elemento \"{0}\" \u00e8 null."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "Il nome locale dell''''attributo \"{0}\" \u00e8  null."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "Il testo di sostituzione del nodo di entit\u00e0 \"{0}\" contiene un nodo di elemento \"{1}\" con un prefisso non associato \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "Il testo di sostituzione del nodo di entit\u00e0 \"{0}\" contiene un nodo di attributo \"{1}\" con un prefisso non associato \"{2}\"."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ja.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ja.java
new file mode 100644
index 0000000..0a969b9
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ja.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_ja.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_ja extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "\u30e1\u30c3\u30bb\u30fc\u30b8\u30fb\u30ad\u30fc ''{0}'' \u306f\u30e1\u30c3\u30bb\u30fc\u30b8\u30fb\u30af\u30e9\u30b9 ''{1}'' \u306b\u3042\u308a\u307e\u305b\u3093\u3002" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "\u30e1\u30c3\u30bb\u30fc\u30b8\u30fb\u30af\u30e9\u30b9 ''{1}'' \u306e\u30e1\u30c3\u30bb\u30fc\u30b8 ''{0}'' \u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u8a2d\u5b9a\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3002" },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "\u30b7\u30ea\u30a2\u30e9\u30a4\u30b6\u30fc\u30fb\u30af\u30e9\u30b9 ''{0}'' \u306f org.xml.sax.ContentHandler \u3092\u5b9f\u88c5\u3057\u307e\u305b\u3093\u3002" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "\u30ea\u30bd\u30fc\u30b9 [ {0} ] \u306f\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "\u30ea\u30bd\u30fc\u30b9 [ {0} ] \u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "\u30d0\u30c3\u30d5\u30a1\u30fc\u30fb\u30b5\u30a4\u30ba <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "\u7121\u52b9\u306a UTF-16 \u30b5\u30ed\u30b2\u30fc\u30c8\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "\u5165\u51fa\u529b\u30a8\u30e9\u30fc" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "\u4e0b\u4f4d\u30ce\u30fc\u30c9\u306e\u5f8c\u307e\u305f\u306f\u8981\u7d20\u304c\u751f\u6210\u3055\u308c\u308b\u524d\u306b\u5c5e\u6027 {0} \u306f\u8ffd\u52a0\u3067\u304d\u307e\u305b\u3093\u3002  \u5c5e\u6027\u306f\u7121\u8996\u3055\u308c\u307e\u3059\u3002" },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "\u63a5\u982d\u90e8 ''{0}'' \u306e\u540d\u524d\u7a7a\u9593\u304c\u5ba3\u8a00\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002" },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "\u5c5e\u6027 ''{0}'' \u304c\u8981\u7d20\u306e\u5916\u5074\u3067\u3059\u3002" },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "\u540d\u524d\u7a7a\u9593\u5ba3\u8a00 ''{0}''=''{1}'' \u304c\u8981\u7d20\u306e\u5916\u5074\u3067\u3059\u3002" },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "''{0}'' \u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f (CLASSPATH \u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044)\u3002\u73fe\u5728\u306f\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u3082\u306e\u306e\u307f\u3092\u4f7f\u7528\u3057\u3066\u3044\u307e\u3059\u3002" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "{1} \u306e\u6307\u5b9a\u3055\u308c\u305f\u51fa\u529b\u30a8\u30f3\u30b3\u30fc\u30c9\u3067\u8868\u305b\u306a\u3044\u6574\u6570\u5024 {0} \u306e\u6587\u5b57\u306e\u51fa\u529b\u3092\u8a66\u307f\u307e\u3057\u305f\u3002" },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "\u51fa\u529b\u30e1\u30bd\u30c3\u30c9 ''{1}'' \u306e\u30d7\u30ed\u30d1\u30c6\u30a3\u30fc\u30fb\u30d5\u30a1\u30a4\u30eb ''{0}'' \u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f (CLASSPATH \u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "\u7121\u52b9\u306a\u30dd\u30fc\u30c8\u756a\u53f7" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "\u30db\u30b9\u30c8\u304c\u30cc\u30eb\u3067\u3042\u308b\u3068\u30dd\u30fc\u30c8\u3092\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "\u30db\u30b9\u30c8\u306f\u3046\u307e\u304f\u69cb\u6210\u3055\u308c\u305f\u30a2\u30c9\u30ec\u30b9\u3067\u3042\u308a\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "\u30b9\u30ad\u30fc\u30e0\u306f\u4e00\u81f4\u3057\u3066\u3044\u307e\u305b\u3093\u3002" },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "\u30cc\u30eb\u30fb\u30b9\u30c8\u30ea\u30f3\u30b0\u304b\u3089\u306f\u30b9\u30ad\u30fc\u30e0\u3092\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "\u30d1\u30b9\u306b\u7121\u52b9\u306a\u30a8\u30b9\u30b1\u30fc\u30d7\u30fb\u30b7\u30fc\u30b1\u30f3\u30b9\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "\u30d1\u30b9\u306b\u7121\u52b9\u6587\u5b57: {0} \u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306b\u7121\u52b9\u6587\u5b57\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "\u30d1\u30b9\u304c\u30cc\u30eb\u3067\u3042\u308b\u3068\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u3092\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "\u7dcf\u79f0 URI \u306e\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u3057\u304b\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "\u30b9\u30ad\u30fc\u30e0\u306f URI \u3067\u898b\u3064\u304b\u308a\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "URI \u306f\u7a7a\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3092\u4f7f\u7528\u3057\u3066\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306f\u30d1\u30b9\u3068\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306e\u4e21\u65b9\u306b\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "\u7167\u4f1a\u30b9\u30c8\u30ea\u30f3\u30b0\u306f\u30d1\u30b9\u304a\u3088\u3073\u7167\u4f1a\u30b9\u30c8\u30ea\u30f3\u30b0\u5185\u306b\u6307\u5b9a\u3067\u304d\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "\u30db\u30b9\u30c8\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u30dd\u30fc\u30c8\u3092\u6307\u5b9a\u3057\u3066\u306f\u3044\u3051\u307e\u305b\u3093" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "\u30db\u30b9\u30c8\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f Userinfo \u3092\u6307\u5b9a\u3057\u3066\u306f\u3044\u3051\u307e\u305b\u3093" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "\u8b66\u544a: \u51fa\u529b\u6587\u66f8\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u3068\u3057\u3066 ''{0}'' \u304c\u8981\u6c42\u3055\u308c\u307e\u3057\u305f\u3002  \u3053\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u306e XML \u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u307e\u305b\u3093\u3002  \u51fa\u529b\u6587\u66f8\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u306f ''1.0'' \u306b\u306a\u308a\u307e\u3059\u3002" },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "\u30b9\u30ad\u30fc\u30e0\u304c\u5fc5\u8981\u3067\u3059\u3002" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "SerializerFactory \u306b\u6e21\u3055\u308c\u305f Properties \u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306b\u306f ''{0}'' \u30d7\u30ed\u30d1\u30c6\u30a3\u30fc\u304c\u3042\u308a\u307e\u305b\u3093\u3002" },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "\u8b66\u544a:  \u30a8\u30f3\u30b3\u30fc\u30c9 ''{0}'' \u306f\u3053\u306e Java \u30e9\u30f3\u30bf\u30a4\u30e0\u3067\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002" },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc ''{0}'' \u306f\u8a8d\u8b58\u3055\u308c\u307e\u305b\u3093\u3002"},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc ''{0}'' \u306f\u8a8d\u8b58\u3055\u308c\u307e\u3059\u304c\u3001\u8981\u6c42\u3055\u308c\u305f\u5024\u306f\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093\u3002"},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "\u7d50\u679c\u306e\u30b9\u30c8\u30ea\u30f3\u30b0\u304c\u9577\u3059\u304e\u308b\u305f\u3081\u3001DOMString \u5185\u306b\u53ce\u307e\u308a\u307e\u305b\u3093: ''{0}''\u3002"},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "\u3053\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u540d\u306e\u5024\u306e\u578b\u306f\u3001\u671f\u5f85\u3055\u308c\u308b\u5024\u306e\u578b\u3068\u4e0d\u9069\u5408\u3067\u3059\u3002"},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "\u66f8\u304d\u8fbc\u307e\u308c\u308b\u30c7\u30fc\u30bf\u306e\u51fa\u529b\u5b9b\u5148\u304c\u30cc\u30eb\u3067\u3059\u3002"},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u306a\u3044\u30a8\u30f3\u30b3\u30fc\u30c9\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\u3002"},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "\u30ce\u30fc\u30c9\u3092\u76f4\u5217\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002"},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "CDATA \u30bb\u30af\u30b7\u30e7\u30f3\u306b 1 \u3064\u4ee5\u4e0a\u306e\u7d42\u4e86\u30de\u30fc\u30ab\u30fc ']]>' \u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002"},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "\u6574\u5f62\u5f0f\u6027\u30c1\u30a7\u30c3\u30ab\u30fc\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002  well-formed \u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u306e\u8a2d\u5b9a\u306f true \u3067\u3057\u305f\u304c\u3001\u6574\u5f62\u5f0f\u6027\u306e\u691c\u67fb\u306f\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002"
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "\u30ce\u30fc\u30c9 ''{0}'' \u306b\u7121\u52b9\u306a XML \u6587\u5b57\u304c\u3042\u308a\u307e\u3059\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "\u30b3\u30e1\u30f3\u30c8\u306e\u4e2d\u306b\u7121\u52b9\u306a XML \u6587\u5b57 (Unicode: 0x{0}) \u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "\u51e6\u7406\u547d\u4ee4\u30c7\u30fc\u30bf\u306e\u4e2d\u306b\u7121\u52b9\u306a XML \u6587\u5b57 (Unicode: 0x{0}) \u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "CDATA \u30bb\u30af\u30b7\u30e7\u30f3\u306e\u4e2d\u306b\u7121\u52b9\u306a XML \u6587\u5b57 (Unicode: 0x{0}) \u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "\u30ce\u30fc\u30c9\u306e\u6587\u5b57\u30c7\u30fc\u30bf\u306e\u5185\u5bb9\u306b\u7121\u52b9\u306a XML \u6587\u5b57 (Unicode: 0x{0}) \u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "''{1}'' \u3068\u3044\u3046\u540d\u524d\u306e {0} \u30ce\u30fc\u30c9\u306e\u4e2d\u306b\u7121\u52b9\u306a XML \u6587\u5b57\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u3002"
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "\u30b9\u30c8\u30ea\u30f3\u30b0 \"--\" \u306f\u30b3\u30e1\u30f3\u30c8\u5185\u3067\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3002"
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "\u8981\u7d20\u578b \"{0}\" \u306b\u95a2\u9023\u3057\u305f\u5c5e\u6027 \"{1}\" \u306e\u5024\u306b\u306f ''<'' \u6587\u5b57\u3092\u542b\u3081\u3066\u306f\u3044\u3051\u307e\u305b\u3093\u3002"
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "\u89e3\u6790\u5bfe\u8c61\u5916\u5b9f\u4f53\u53c2\u7167 \"&{0};\" \u306f\u8a31\u53ef\u3055\u308c\u307e\u305b\u3093\u3002"
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "\u5c5e\u6027\u5024\u3067\u306e\u5916\u90e8\u5b9f\u4f53\u53c2\u7167 \"&{0};\" \u306f\u8a31\u53ef\u3055\u308c\u307e\u305b\u3093\u3002"
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "\u63a5\u982d\u90e8 \"{0}\" \u306f\u540d\u524d\u7a7a\u9593 \"{1}\" \u306b\u7d50\u5408\u3067\u304d\u307e\u305b\u3093\u3002"
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "\u8981\u7d20 \"{0}\" \u306e\u30ed\u30fc\u30ab\u30eb\u540d\u304c\u30cc\u30eb\u3067\u3059\u3002"
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "\u5c5e\u6027 \"{0}\" \u306e\u30ed\u30fc\u30ab\u30eb\u540d\u304c\u30cc\u30eb\u3067\u3059\u3002"
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "\u5b9f\u4f53\u30ce\u30fc\u30c9 \"{0}\" \u306e\u7f6e\u63db\u30c6\u30ad\u30b9\u30c8\u306b\u3001\u672a\u7d50\u5408\u306e\u63a5\u982d\u90e8 \"{2}\" \u3092\u6301\u3064\u8981\u7d20\u30ce\u30fc\u30c9 \"{1}\" \u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002"
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "\u5b9f\u4f53\u30ce\u30fc\u30c9 \"{0}\" \u306e\u7f6e\u63db\u30c6\u30ad\u30b9\u30c8\u306b\u3001\u672a\u7d50\u5408\u306e\u63a5\u982d\u90e8 \"{2}\" \u3092\u6301\u3064\u5c5e\u6027\u30ce\u30fc\u30c9 \"{1}\" \u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002"
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ko.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ko.java
new file mode 100644
index 0000000..3d2f011
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ko.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_ko.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_ko extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "''{0}'' \uba54\uc2dc\uc9c0 \ud0a4\uac00 ''{1}'' \uba54\uc2dc\uc9c0 \ud074\ub798\uc2a4\uc5d0 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "''{1}'' \uba54\uc2dc\uc9c0 \ud074\ub798\uc2a4\uc5d0 \uc788\ub294 ''{0}'' \uba54\uc2dc\uc9c0\uc758 \ud615\uc2dd\uc774 \uc798\ubabb \ub418\uc5c8\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "''{0}'' \uc9c1\ub82c \ud504\ub85c\uadf8\ub7a8 \ud074\ub798\uc2a4\uac00 org.xml.sax.ContentHandler\ub97c \uad6c\ud604\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "[ {0} ] \uc790\uc6d0\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.\n{1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "[ {0} ] \uc790\uc6d0\uc774 {1} \n {2} \t {3}\uc744(\ub97c) \ub85c\ub4dc\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "\ubc84\ud37c \ud06c\uae30 <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "\uc798\ubabb\ub41c UTF-16 \ub300\ub9ac\uc790(surrogate)\uac00 \ubc1c\uacac\ub418\uc5c8\uc2b5\ub2c8\ub2e4: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "IO \uc624\ub958" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "\ud558\uc704 \ub178\ub4dc\uac00 \uc0dd\uc131\ub41c \uc774\ud6c4 \ub610\ub294 \uc694\uc18c\uac00 \uc791\uc131\ub418\uae30 \uc774\uc804\uc5d0 {0} \uc18d\uc131\uc744 \ucd94\uac00\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uc18d\uc131\uc774 \ubb34\uc2dc\ub429\ub2c8\ub2e4." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "''{0}'' \uc811\ub450\ubd80\uc5d0 \ub300\ud55c \uc774\ub984 \uacf5\uac04\uc774 \uc120\uc5b8\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "''{0}'' \uc18d\uc131\uc774 \uc694\uc18c\uc758 \uc678\ubd80\uc5d0 \uc788\uc2b5\ub2c8\ub2e4." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "''{0}''=''{1}'' \uc774\ub984 \uacf5\uac04 \uc120\uc5b8\uc774 \uc694\uc18c\uc758 \uc678\ubd80\uc5d0 \uc788\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "''{0}''(CLASSPATH \ud655\uc778)\uc744(\ub97c) \ub85c\ub4dc\ud560 \uc218 \uc5c6\uc73c\ubbc0\ub85c, \ud604\uc7ac \uae30\ubcf8\uac12\ub9cc\uc744 \uc0ac\uc6a9\ud558\ub294 \uc911\uc785\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "{1}\uc758 \uc9c0\uc815\ub41c \ucd9c\ub825 \uc778\ucf54\ub529\uc5d0 \ud45c\uc2dc\ub418\uc9c0 \uc54a\uc740 \ubb34\uacb0\uc131 \uac12 {0}\uc758 \ubb38\uc790\ub97c \ucd9c\ub825\ud558\uc2ed\uc2dc\uc624. " },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "''{1}'' \ucd9c\ub825 \uba54\uc18c\ub4dc(CLASSPATH \ud655\uc778)\uc5d0 \ub300\ud55c ''{0}'' \ud2b9\uc131 \ud30c\uc77c\uc744 \ub85c\ub4dc\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "\uc798\ubabb\ub41c \ud3ec\ud2b8 \ubc88\ud638" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "\ud638\uc2a4\ud2b8\uac00 \ub110(null)\uc774\uba74 \ud3ec\ud2b8\ub97c \uc124\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "\ud638\uc2a4\ud2b8\uac00 \uc644\uc804\ud55c \uc8fc\uc18c\uac00 \uc544\ub2d9\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "\uc2a4\ud0a4\ub9c8\uac00 \uc77c\uce58\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "\ub110(null) \ubb38\uc790\uc5f4\uc5d0\uc11c \uc2a4\ud0a4\ub9c8\ub97c \uc124\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "\uacbd\ub85c\uc5d0 \uc798\ubabb\ub41c \uc774\uc2a4\ucf00\uc774\ud504 \uc21c\uc11c\uac00 \uc788\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "\uacbd\ub85c\uc5d0 \uc798\ubabb\ub41c \ubb38\uc790\uac00 \uc788\uc2b5\ub2c8\ub2e4: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "\ub2e8\ud3b8\uc5d0 \uc798\ubabb\ub41c \ubb38\uc790\uac00 \uc788\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "\uacbd\ub85c\uac00 \ub110(null)\uc774\uba74 \ub2e8\ud3b8\uc744 \uc124\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "\uc77c\ubc18 URI\uc5d0 \ub300\ud574\uc11c\ub9cc \ub2e8\ud3b8\uc744 \uc124\uc815\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "URI\uc5d0 \uc2a4\ud0a4\ub9c8\uac00 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "\ube48 \ub9e4\uac1c\ubcc0\uc218\ub85c URI\ub97c \ucd08\uae30\ud654\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "\uacbd\ub85c \ubc0f \ub2e8\ud3b8 \ub458 \ub2e4\uc5d0 \ub2e8\ud3b8\uc744 \uc9c0\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "\uacbd\ub85c \ubc0f \uc870\ud68c \ubb38\uc790\uc5f4\uc5d0 \uc870\ud68c \ubb38\uc790\uc5f4\uc744 \uc9c0\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "\ud638\uc2a4\ud2b8\ub97c \uc9c0\uc815\ud558\uc9c0 \uc54a\uc740 \uacbd\uc6b0\uc5d0\ub294 \ud3ec\ud2b8\ub97c \uc9c0\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "\ud638\uc2a4\ud2b8\ub97c \uc9c0\uc815\ud558\uc9c0 \uc54a\uc740 \uacbd\uc6b0\uc5d0\ub294 Userinfo\ub97c \uc9c0\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "\uacbd\uace0:  \uc694\uccad\ub41c \ucd9c\ub825 \ubb38\uc11c\uc758 \ubc84\uc804\uc740 ''{0}''\uc785\ub2c8\ub2e4. \ud558\uc9c0\ub9cc \uc774 XML \ubc84\uc804\uc740 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ucd9c\ub825 \ubb38\uc11c\uc758 \ubc84\uc804\uc740 ''1.0''\uc774 \ub429\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "\uc2a4\ud0a4\ub9c8\uac00 \ud544\uc694\ud569\ub2c8\ub2e4." },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "SerializerFactory\uc5d0 \uc804\ub2ec\ub41c \ud2b9\uc131 \uc624\ube0c\uc81d\ud2b8\uc5d0 ''{0}'' \ud2b9\uc131\uc774 \uc5c6\uc2b5\ub2c8\ub2e4." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "\uacbd\uace0: ''{0}'' \uc778\ucf54\ub529\uc740 Java Runtime\uc744 \uc9c0\uc6d0\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "''{0}'' \ub9e4\uac1c\ubcc0\uc218\ub97c \uc778\uc2dd\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "''{0}'' \ub9e4\uac1c\ubcc0\uc218\ub294 \uc778\uc2dd\ud560 \uc218 \uc788\uc73c\ub098 \uc694\uccad\ub41c \uac12\uc744 \uc124\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "\uacb0\uacfc \ubb38\uc790\uc5f4\uc774 \ub108\ubb34 \uae38\uc5b4 DOMString\uc5d0 \ub9de\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4: ''{0}'' "},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "\uc774 \ub9e4\uac1c\ubcc0\uc218 \uc774\ub984\uc5d0 \ub300\ud55c \uac12 \uc720\ud615\uc774 \uc608\uc0c1 \uac12 \uc720\ud615\uacfc \ud638\ud658\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "\ub370\uc774\ud130\ub97c \uae30\ub85d\ud560 \ucd9c\ub825 \ub300\uc0c1\uc774 \ub110(null)\uc785\ub2c8\ub2e4."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc778\ucf54\ub529\uc774 \ubc1c\uacac\ub418\uc5c8\uc2b5\ub2c8\ub2e4."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "\ub178\ub4dc\ub97c \uc9c1\ub82c\ud654\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "CDATA \uc139\uc158\uc5d0 \uc885\ub8cc \ud45c\uc2dc \ubb38\uc790\uc778 ']]>'\uac00 \ud558\ub098 \uc774\uc0c1 \ud3ec\ud568\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "Well-Formedness \uac80\uc0ac\uae30\uc758 \uc778\uc2a4\ud134\uc2a4\ub97c \uc791\uc131\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. Well-Formed \ub9e4\uac1c\ubcc0\uc218\uac00 true\ub85c \uc124\uc815\ub418\uc5c8\uc9c0\ub9cc Well-Formedness \uac80\uc0ac\ub97c \uc218\ud589\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "''{0}'' \ub178\ub4dc\uc5d0 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc740 XML \ubb38\uc790\uac00 \uc788\uc2b5\ub2c8\ub2e4."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "\uc124\uba85\uc5d0 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc740 XML \ubb38\uc790(Unicode: 0x{0})\uac00 \uc788\uc2b5\ub2c8\ub2e4. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "\ucc98\ub9ac \uba85\ub839\uc5b4 \ub370\uc774\ud130\uc5d0 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc740 XML \ubb38\uc790(Unicode: 0x{0})\uac00 \uc788\uc2b5\ub2c8\ub2e4 "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "CDATASection\uc758 \ub0b4\uc6a9\uc5d0 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc740 XML \ubb38\uc790(Unicode: 0x{0})\uac00 \uc788\uc2b5\ub2c8\ub2e4. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "\ub178\ub4dc\uc758 \ubb38\uc790 \ub370\uc774\ud130 \ub0b4\uc6a9\uc5d0 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc740 XML \ubb38\uc790(Unicode: 0x{0})\uac00 \uc788\uc2b5\ub2c8\ub2e4. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "\uc774\ub984\uc774 ''{1}''\uc778 {0} \ub178\ub4dc\uc5d0 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc740 XML \ubb38\uc790\uac00 \uc788\uc2b5\ub2c8\ub2e4. "
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "\uc124\uba85 \ub0b4\uc5d0\uc11c\ub294 \"--\" \ubb38\uc790\uc5f4\uc774 \ud5c8\uc6a9\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "\"{0}\" \uc694\uc18c \uc720\ud615\uacfc \uc5f0\uad00\ub41c \"{1}\" \uc18d\uc131\uac12\uc5d0 ''<'' \ubb38\uc790\uac00 \ud3ec\ud568\ub418\uba74 \uc548\ub429\ub2c8\ub2e4."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "\"&{0};\"\uc758 \uad6c\ubd84 \ubd84\uc11d\ub418\uc9c0 \uc54a\uc740 \uc5d4\ud2f0\ud2f0 \ucc38\uc870\ub294 \ud5c8\uc6a9\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. "
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "\uc18d\uc131\uac12\uc5d0\ub294 \"&{0};\" \uc678\ubd80 \uc5d4\ud2f0\ud2f0 \ucc38\uc870\uac00 \ud5c8\uc6a9\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. "
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "\"{0}\" \uc811\ub450\ubd80\ub97c \"{1}\" \uc774\ub984 \uacf5\uac04\uc5d0 \ubc14\uc778\ub4dc\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "\"{0}\" \uc694\uc18c\uc758 \ub85c\uceec \uc774\ub984\uc774 \ub110(null)\uc785\ub2c8\ub2e4."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "\"{0}\" \uc18d\uc131\uc758 \ub85c\uceec \uc774\ub984\uc774 \ub110(null)\uc785\ub2c8\ub2e4."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "\"{0}\" \uc5d4\ud2f0\ud2f0 \ub178\ub4dc\uc758 \ub300\uccb4 \ud14d\uc2a4\ud2b8\uc5d0 \ubc14\uc778\ub4dc\ub418\uc9c0 \uc54a\uc740 \uc811\ub450\ubd80 \"{2}\"\uc744(\ub97c) \uac16\ub294 \"{1}\" \uc694\uc18c \ub178\ub4dc\uac00 \uc788\uc2b5\ub2c8\ub2e4."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "\"{0}\" \uc5d4\ud2f0\ud2f0 \ub178\ub4dc\uc758 \ub300\uccb4 \ud14d\uc2a4\ud2b8\uc5d0 \ubc14\uc778\ub4dc\ub418\uc9c0 \uc54a\uc740 \uc811\ub450\ubd80 \"{2}\"\uc744(\ub97c) \uac16\ub294 \"{1}\" \uc18d\uc131 \ub178\ub4dc\uac00 \uc788\uc2b5\ub2c8\ub2e4."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_pl.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_pl.java
new file mode 100644
index 0000000..06d97ae
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_pl.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_pl.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_pl extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "Klucz komunikatu ''{0}'' nie znajduje si\u0119 w klasie komunikat\u00f3w ''{1}''" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "Nie powiod\u0142o si\u0119 sformatowanie komunikatu ''{0}'' w klasie komunikat\u00f3w ''{1}''." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "Klasa szereguj\u0105ca ''{0}'' nie implementuje org.xml.sax.ContentHandler." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "Nie mo\u017cna znale\u017a\u0107 zasobu [ {0} ].\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "Zas\u00f3b [ {0} ] nie m\u00f3g\u0142 za\u0142adowa\u0107: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Wielko\u015b\u0107 buforu <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "Wykryto niepoprawny odpowiednik UTF-16: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "B\u0142\u0105d we/wy" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Nie mo\u017cna doda\u0107 atrybutu {0} po bezpo\u015brednich w\u0119z\u0142ach potomnych ani przed wyprodukowaniem elementu.  Atrybut zostanie zignorowany." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "Nie zadeklarowano przestrzeni nazw dla przedrostka ''{0}''." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "Atrybut ''{0}'' znajduje si\u0119 poza elementem." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "Deklaracja przestrzeni nazw ''{0}''=''{1}'' znajduje si\u0119 poza elementem." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "Nie mo\u017cna za\u0142adowa\u0107 ''{0}'' (sprawd\u017a CLASSPATH) - u\u017cywane s\u0105 teraz warto\u015bci domy\u015blne" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Pr\u00f3ba wyprowadzenia znaku warto\u015bci ca\u0142kowitej {0}, kt\u00f3ry nie jest reprezentowany w podanym kodowaniu wyj\u015bciowym {1}." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "Nie mo\u017cna za\u0142adowa\u0107 pliku w\u0142a\u015bciwo\u015bci ''{0}'' dla metody wyj\u015bciowej ''{1}'' (sprawd\u017a CLASSPATH)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "Niepoprawny numer portu" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "Nie mo\u017cna ustawi\u0107 portu, kiedy host jest pusty" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "Host nie jest poprawnie skonstruowanym adresem" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "Schemat nie jest zgodny." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Nie mo\u017cna ustawi\u0107 schematu z pustego ci\u0105gu znak\u00f3w" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "\u015acie\u017cka zawiera nieznan\u0105 sekwencj\u0119 o zmienionym znaczeniu" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "\u015acie\u017cka zawiera niepoprawny znak {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "Fragment zawiera niepoprawny znak" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "Nie mo\u017cna ustawi\u0107 fragmentu, kiedy \u015bcie\u017cka jest pusta" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "Fragment mo\u017cna ustawi\u0107 tylko dla og\u00f3lnego URI" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "Nie znaleziono schematu w URI" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "Nie mo\u017cna zainicjowa\u0107 URI z pustymi parametrami" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "Nie mo\u017cna poda\u0107 fragmentu jednocze\u015bnie w \u015bcie\u017cce i fragmencie" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "Tekstu zapytania nie mo\u017cna poda\u0107 w tek\u015bcie \u015bcie\u017cki i zapytania" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "Nie mo\u017cna poda\u0107 portu, je\u015bli nie podano hosta" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Nie mo\u017cna poda\u0107 informacji o u\u017cytkowniku, je\u015bli nie podano hosta" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Ostrze\u017cenie:  Wymagan\u0105 wersj\u0105 dokumentu wyj\u015bciowego jest ''{0}''.  Ta wersja XML nie jest obs\u0142ugiwana.  Wersj\u0105 dokumentu wyj\u015bciowego b\u0119dzie ''1.0''." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "Schemat jest wymagany!" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "Obiekt klasy Properties przekazany do klasy SerializerFactory nie ma w\u0142a\u015bciwo\u015bci ''{0}''." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Ostrze\u017cenie:  dekodowany ''{0}'' nie jest obs\u0142ugiwany przez \u015brodowisko wykonawcze Java." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "Parametr ''{0}'' nie zosta\u0142 rozpoznany."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "Parametr ''{0}'' zosta\u0142 rozpoznany, ale nie mo\u017cna ustawi\u0107 \u017c\u0105danej warto\u015bci."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "Wynikowy \u0142a\u0144cuch jest zbyt d\u0142ugi, aby si\u0119 zmie\u015bci\u0107 w obiekcie DOMString: ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "Typ warto\u015bci parametru o tej nazwie jest niezgodny z oczekiwanym typem warto\u015bci. "},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "Docelowe miejsce zapisu danych wyj\u015bciowych by\u0142o puste (null)."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "Napotkano nieobs\u0142ugiwane kodowanie."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "Nie mo\u017cna przekszta\u0142ci\u0107 w\u0119z\u0142a do postaci szeregowej."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "Sekcja CDATA zawiera jeden lub kilka znacznik\u00f3w zako\u0144czenia ']]>'."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "Nie mo\u017cna utworzy\u0107 instancji obiektu sprawdzaj\u0105cego Well-Formedness.  Parametr well-formed ustawiono na warto\u015b\u0107 true, ale nie mo\u017cna by\u0142o dokona\u0107 sprawdzenia poprawno\u015bci konstrukcji."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "W\u0119ze\u0142 ''{0}'' zawiera niepoprawne znaki XML."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "W komentarzu znaleziono niepoprawny znak XML (Unicode: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "W danych instrukcji przetwarzania znaleziono niepoprawny znak XML (Unicode: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "W sekcji CDATA znaleziono niepoprawny znak XML (Unicode: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "W tre\u015bci danych znakowych w\u0119z\u0142a znaleziono niepoprawny znak XML (Unicode: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "W {0} o nazwie ''{1}'' znaleziono niepoprawne znaki XML."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "Ci\u0105g znak\u00f3w \"--\" jest niedozwolony w komentarzu."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "Warto\u015b\u0107 atrybutu \"{1}\" zwi\u0105zanego z typem elementu \"{0}\" nie mo\u017ce zawiera\u0107 znaku ''<''."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "Odwo\u0142anie do encji nieprzetwarzanej \"&{0};\" jest niedozwolone."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "Odwo\u0142anie do zewn\u0119trznej encji \"&{0};\" jest niedozwolone w warto\u015bci atrybutu."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "Nie mo\u017cna zwi\u0105za\u0107 przedrostka \"{0}\" z przestrzeni\u0105 nazw \"{1}\"."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "Nazwa lokalna elementu \"{0}\" jest pusta (null)."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "Nazwa lokalna atrybutu \"{0}\" jest pusta (null)."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "Tekst zast\u0119puj\u0105cy w\u0119z\u0142a encji \"{0}\" zawiera w\u0119ze\u0142 elementu \"{1}\" o niezwi\u0105zanym przedrostku \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "Tekst zast\u0119puj\u0105cy w\u0119z\u0142a encji \"{0}\" zawiera w\u0119ze\u0142 atrybutu \"{1}\" o niezwi\u0105zanym przedrostku \"{2}\"."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_pt_BR.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_pt_BR.java
new file mode 100644
index 0000000..b5bdb23
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_pt_BR.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_pt_BR.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_pt_BR extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "A chave de mensagem ''{0}'' n\u00e3o est\u00e1 na classe de mensagem ''{1}''" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "O formato da mensagem ''{0}'' na classe de mensagem ''{1}'' falhou." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "A classe de serializador ''{0}'' n\u00e3o implementa org.xml.sax.ContentHandler." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "O recurso [ {0} ] n\u00e3o p\u00f4de ser encontrado.\n{1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "O recurso [ {0} ] n\u00e3o p\u00f4de carregar: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Tamanho do buffer <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "Detectado substituto UTF-16 inv\u00e1lido: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "Erro de E/S" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Imposs\u00edvel incluir atributo {0} depois de n\u00f3s filhos ou antes da gera\u00e7\u00e3o de um elemento. O atributo ser\u00e1 ignorado." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "O espa\u00e7o de nomes do prefixo ''{0}'' n\u00e3o foi declarado. " },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "Atributo ''{0}'' fora do elemento. " },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "Declara\u00e7\u00e3o de espa\u00e7o de nomes ''{0}''=''{1}'' fora do elemento. " },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "N\u00e3o foi poss\u00edvel carregar ''{0}'' (verifique CLASSPATH) agora , utilizando somente os padr\u00f5es" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Tentativa de processar o caractere de um valor integral {0} que n\u00e3o \u00e9 representado na codifica\u00e7\u00e3o de sa\u00edda especificada de {1}." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "N\u00e3o foi poss\u00edvel carregar o arquivo de propriedade ''{0}'' para o m\u00e9todo de sa\u00edda ''{1}'' (verifique CLASSPATH)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "N\u00famero de porta inv\u00e1lido" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "A porta n\u00e3o pode ser definida quando o host \u00e9 nulo" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "O host n\u00e3o \u00e9 um endere\u00e7o formado corretamente" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "O esquema n\u00e3o est\u00e1 em conformidade." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Imposs\u00edvel definir esquema a partir da cadeia nula" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "O caminho cont\u00e9m seq\u00fc\u00eancia de escape inv\u00e1lida" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "O caminho cont\u00e9m caractere inv\u00e1lido: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "O fragmento cont\u00e9m caractere inv\u00e1lido" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "O fragmento n\u00e3o pode ser definido quando o caminho \u00e9 nulo" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "O fragmento s\u00f3 pode ser definido para um URI gen\u00e9rico" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "Nenhum esquema encontrado no URI" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "Imposs\u00edvel inicializar URI com par\u00e2metros vazios" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "O fragmento n\u00e3o pode ser especificado no caminho e fragmento" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "A cadeia de consulta n\u00e3o pode ser especificada na cadeia de consulta e caminho" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "Port n\u00e3o pode ser especificado se host n\u00e3o for especificado" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Userinfo n\u00e3o pode ser especificado se host n\u00e3o for especificado" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Aviso:  A vers\u00e3o do documento de sa\u00edda precisa ser ''{0}''.  Essa vers\u00e3o do XML n\u00e3o \u00e9 suportada. A vers\u00e3o do documento de sa\u00edda ser\u00e1 ''1.0''." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "O esquema \u00e9 obrigat\u00f3rio!" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "O objeto Properties transmitido para SerializerFactory n\u00e3o tem uma propriedade ''{0}''." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Aviso:  A codifica\u00e7\u00e3o ''{0}'' n\u00e3o \u00e9 suportada pelo Java Runtime." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "O par\u00e2metro ''{0}'' n\u00e3o \u00e9 reconhecido."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "O par\u00e2metro ''{0}'' \u00e9 reconhecido, mas o valor pedido n\u00e3o pode ser definido. "},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "A cadeia resultante \u00e9 muito longa para caber em uma DOMString: ''{0}''. "},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "O tipo de valor para este nome de par\u00e2metro \u00e9 incompat\u00edvel com o tipo de valor esperado. "},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "O destino de sa\u00edda para os dados a serem gravados era nulo. "},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "Uma codifica\u00e7\u00e3o n\u00e3o suportada foi encontrada. "},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "O n\u00f3 n\u00e3o p\u00f4de ser serializado."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "A Se\u00e7\u00e3o CDATA cont\u00e9m um ou mais marcadores de t\u00e9rmino ']]>'."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "Uma inst\u00e2ncia do verificador Well-Formedness n\u00e3o p\u00f4de ser criada. O par\u00e2metro well-formed foi definido como true, mas a verifica\u00e7\u00e3o well-formedness n\u00e3o pode ser executada."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "O n\u00f3 ''{0}'' cont\u00e9m caracteres XML inv\u00e1lidos. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "Um caractere XML inv\u00e1lido (Unicode: 0x{0}) foi encontrado no coment\u00e1rio. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "Um caractere XML inv\u00e1lido (Unicode: 0x{0}) foi encontrado no processo instructiondata."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "Um caractere XML inv\u00e1lido (Unicode: 0x{0}) foi encontrado nos conte\u00fados do CDATASection. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "Um caractere XML inv\u00e1lido (Unicode: 0x{0}) foi encontrado no conte\u00fado dos dados de caractere dos n\u00f3s. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "Um caractere inv\u00e1lido foi encontrado no {0} do n\u00f3 denominado ''{1}''."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "A cadeia \"--\" n\u00e3o \u00e9 permitida dentro dos coment\u00e1rios. "
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "O valor do atributo \"{1}\" associado a um tipo de elemento \"{0}\" n\u00e3o deve conter o caractere ''<''. "
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "A refer\u00eancia de entidade n\u00e3o analisada \"&{0};\" n\u00e3o \u00e9 permitida. "
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "A refer\u00eancia de entidade externa \"&{0};\" n\u00e3o \u00e9 permitida em um valor de atributo. "
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "O prefixo \"{0}\" n\u00e3o pode ser vinculado ao espa\u00e7o de nomes \"{1}\"."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "O nome local do elemento \"{0}\" \u00e9 nulo."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "O nome local do atributo \"{0}\" \u00e9 nulo."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "O texto de substitui\u00e7\u00e3o do n\u00f3 de entidade \"{0}\" cont\u00e9m um n\u00f3 de elemento \"{1}\" com um prefixo n\u00e3o vinculado \"{2}\"."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "O texto de substitui\u00e7\u00e3o do n\u00f3 de entidade \"{0}\" cont\u00e9m um n\u00f3 de atributo \"{1}\" com um prefixo n\u00e3o vinculado \"{2}\"."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ru.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ru.java
new file mode 100644
index 0000000..2935048
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_ru.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_ru.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_ru extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "\u041a\u043b\u044e\u0447 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f ''{0}'' \u043d\u0435 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a \u043a\u043b\u0430\u0441\u0441\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f ''{1}''" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "\u0424\u043e\u0440\u043c\u0430\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f ''{0}'' \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f ''{1}'' \u0432\u044b\u0437\u0432\u0430\u043b \u043e\u0448\u0438\u0431\u043a\u0443. " },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "\u041a\u043b\u0430\u0441\u0441 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430 ''{0}'' \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 org.xml.sax.ContentHandler. " },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "\u0420\u0435\u0441\u0443\u0440\u0441 [ {0} ] \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d.\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441 [ {0} ]: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "\u0420\u0430\u0437\u043c\u0435\u0440 \u0431\u0443\u0444\u0435\u0440\u0430 <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 UTF-16: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0432\u043e\u0434\u0430-\u0432\u044b\u0432\u043e\u0434\u0430" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "\u0410\u0442\u0440\u0438\u0431\u0443\u0442 {0} \u043d\u0435\u043b\u044c\u0437\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0445 \u0443\u0437\u043b\u043e\u0432 \u0438 \u0434\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430. \u0410\u0442\u0440\u0438\u0431\u0443\u0442 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d. " },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "\u041f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e \u0438\u043c\u0435\u043d \u0434\u043b\u044f \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0430 ''{0}'' \u043d\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043e. " },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "\u0410\u0442\u0440\u0438\u0431\u0443\u0442 ''{0}'' \u0432\u043d\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430. " },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "\u041e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0430 \u0438\u043c\u0435\u043d ''{0}''=''{1}'' \u0432\u043d\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430. " },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c ''{0}'' (\u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 CLASSPATH), \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "\u041f\u043e\u043f\u044b\u0442\u043a\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u0441\u0438\u043c\u0432\u043e\u043b\u0430, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 {0} \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 {1}. " },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0441\u0432\u043e\u0439\u0441\u0442\u0432 ''{0}'' \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 ''{1}'' (\u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 CLASSPATH)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 \u043f\u043e\u0440\u0442\u0430" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u043e\u0440\u0442 \u0434\u043b\u044f \u043f\u0443\u0441\u0442\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 \u0445\u043e\u0441\u0442\u0430" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d \u0430\u0434\u0440\u0435\u0441 \u0445\u043e\u0441\u0442\u0430" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "\u0421\u0445\u0435\u043c\u0430 \u043d\u0435 \u043a\u043e\u043d\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0432\u043d\u0430." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u0443 \u0434\u043b\u044f \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "\u0412 \u0438\u043c\u0435\u043d\u0438 \u043f\u0443\u0442\u0438 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430\u044f Esc-\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "\u0412 \u0438\u043c\u0435\u043d\u0438 \u043f\u0443\u0442\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0438\u043c\u0432\u043e\u043b: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "\u0424\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0438\u043c\u0432\u043e\u043b" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043f\u0443\u0441\u0442\u043e\u0433\u043e \u043f\u0443\u0442\u0438" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "\u0424\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u0430 URI" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "\u0412 URI \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0441\u0445\u0435\u043c\u0430" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c URI \u0441 \u043f\u0443\u0441\u0442\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u043f\u0443\u0442\u0438 \u0438 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "\u041d\u0435\u043b\u044c\u0437\u044f \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043f\u0443\u0442\u0438 \u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "\u041d\u0435\u043b\u044c\u0437\u044f \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u0440\u0442, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0437\u0430\u0434\u0430\u043d \u0445\u043e\u0441\u0442" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "\u041d\u0435\u043b\u044c\u0437\u044f \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0437\u0430\u0434\u0430\u043d \u0445\u043e\u0441\u0442" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435: \u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 ''{0}''. \u042d\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f XML \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f. \u0412\u0435\u0440\u0441\u0438\u0435\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 ''1.0''. " },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0441\u0445\u0435\u043c\u0430!" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "\u041e\u0431\u044a\u0435\u043a\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u0432 SerializerFactory, \u043d\u0435 \u043e\u0431\u043b\u0430\u0434\u0430\u0435\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e\u043c ''{0}''. " },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435:  \u041a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430 ''{0}'' \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u043e\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Java." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 ''{0}'' \u043d\u0435 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d. "},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 ''{0}'' \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d, \u043d\u043e \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0442\u044c \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c. "},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "\u0421\u0442\u0440\u043e\u043a\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043b\u0438\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0432 DOMString: ''{0}''. "},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "\u0422\u0438\u043f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0441 \u044d\u0442\u0438 \u0438\u043c\u0435\u043d\u0435\u043c \u043d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c \u0441 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c \u0442\u0438\u043f\u043e\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f. "},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "\u041d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u0434\u043b\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. "},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0430 \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430. "},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0443\u0437\u0435\u043b. "},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "\u0420\u0430\u0437\u0434\u0435\u043b CDATA \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0430\u0440\u043a\u0435\u0440\u043e\u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439 ']]>'. "},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0438\u043c\u0435\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 true, \u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c. "
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "\u0423\u0437\u0435\u043b ''{0}'' \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b XML. "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "\u0412 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0438\u043c\u0432\u043e\u043b XML (\u042e\u043d\u0438\u043a\u043e\u0434: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "\u041f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 instructiondata \u0431\u044b\u043b \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0438\u043c\u0432\u043e\u043b XML (\u042e\u043d\u0438\u043a\u043e\u0434: 0x{0}). "
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "\u0412 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c CDATASection \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0438\u043c\u0432\u043e\u043b XML (\u042e\u043d\u0438\u043a\u043e\u0434: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "\u0412 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c \u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u0437\u043b\u0430 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0438\u043c\u0432\u043e\u043b XML (\u042e\u043d\u0438\u043a\u043e\u0434: 0x{0})."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b XML \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u044b \u0432 \u0443\u0437\u043b\u0435 {0} \u0441 \u0438\u043c\u0435\u043d\u0435\u043c ''{1}''. "
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "\u0421\u0442\u0440\u043e\u043a\u0430 \"--\" \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438. "
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \"{1}\", \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0441 \u0442\u0438\u043f\u043e\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \"{0}\", \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0441\u0438\u043c\u0432\u043e\u043b ''<''. "
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "\u041d\u0435\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u0430\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \"&{0};\" \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430. "
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "\u0412\u043d\u0435\u0448\u043d\u044f\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \"&{0};\" \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430 \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430. "
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \"{0}\" \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435  \u0438\u043c\u0435\u043d \"{1}\". "
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u043c\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \"{0}\" \u043f\u0443\u0441\u0442\u043e. "
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u043c\u044f \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \"{0}\" \u043f\u0443\u0441\u0442\u043e.  "
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "\u0422\u0435\u043a\u0441\u0442 \u0437\u0430\u043c\u0435\u043d\u044b \u0434\u043b\u044f \u0443\u0437\u043b\u0430 \u0437\u0430\u043f\u0438\u0441\u0438 \"{0}\" \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0443\u0437\u0435\u043b \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \"{1}\" \u0441 \u043d\u0435\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \"{2}\". "
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "\u0422\u0435\u043a\u0441\u0442 \u0437\u0430\u043c\u0435\u043d\u044b \u0434\u043b\u044f \u0443\u0437\u043b\u0430 \u0437\u0430\u043f\u0438\u0441\u0438 \"{0}\" \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0443\u0437\u0435\u043b \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \"{1}\" \u0441 \u043d\u0435\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \"{2}\". "
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sk.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sk.java
new file mode 100755
index 0000000..fdabf07
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sk.java
@@ -0,0 +1,293 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id: SerializerMessages_sk.java,v 1.7 2005/03/07 20:34:36 minchau Exp $

+ */

+

+package org.apache.xml.serializer.utils;

+

+import java.util.ListResourceBundle;

+import java.util.Locale;

+import java.util.MissingResourceException;

+import java.util.ResourceBundle;

+

+/**

+ * An instance of this class is a ListResourceBundle that

+ * has the required getContents() method that returns

+ * an array of message-key/message associations.

+ * <p>

+ * The message keys are defined in {@link MsgKey}. The

+ * messages that those keys map to are defined here.

+ * <p>

+ * The messages in the English version are intended to be

+ * translated.

+ *

+ * This class is not a public API, it is only public because it is

+ * used in org.apache.xml.serializer.

+ *

+ * @xsl.usage internal

+ */

+public class SerializerMessages_sk extends ListResourceBundle {

+

+    /*

+     * This file contains error and warning messages related to

+     * Serializer Error Handling.

+     *

+     *  General notes to translators:

+

+     *  1) A stylesheet is a description of how to transform an input XML document

+     *     into a resultant XML document (or HTML document or text).  The

+     *     stylesheet itself is described in the form of an XML document.

+

+     *

+     *  2) An element is a mark-up tag in an XML document; an attribute is a

+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>

+     *     "elem" is an element name, "attr" and "attr2" are attribute names with

+     *     the values "val" and "val2", respectively.

+     *

+     *  3) A namespace declaration is a special attribute that is used to associate

+     *     a prefix with a URI (the namespace).  The meanings of element names and

+     *     attribute names that use that prefix are defined with respect to that

+     *     namespace.

+     *

+     *

+     */

+

+    /** The lookup table for error messages.   */

+    public Object[][] getContents() {

+        Object[][] contents = new Object[][] {

+            {   MsgKey.BAD_MSGKEY,

+                "K\u013e\u00fa\u010d spr\u00e1vy ''{0}'' sa nenach\u00e1dza v triede spr\u00e1v ''{1}''" },

+

+            {   MsgKey.BAD_MSGFORMAT,

+                "Zlyhal form\u00e1t spr\u00e1vy ''{0}'' v triede spr\u00e1v ''{1}''." },

+

+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,

+                "Trieda serializ\u00e1tora ''{0}'' neimplementuje org.xml.sax.ContentHandler." },

+

+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,

+                    "Prostriedok [ {0} ] nemohol by\u0165 n\u00e1jden\u00fd.\n {1}" },

+

+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,

+                    "Prostriedok [ {0} ] sa nedal na\u010d\u00edta\u0165: {1} \n {2} \t {3}" },

+

+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,

+                    "Ve\u013ekos\u0165 vyrovn\u00e1vacej pam\u00e4te <=0" },

+

+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,

+                    "Bolo zisten\u00e9 neplatn\u00e9 nahradenie UTF-16: {0} ?" },

+

+            {   MsgKey.ER_OIERROR,

+                "chyba IO" },

+

+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,

+                "Nie je mo\u017en\u00e9 prida\u0165 atrib\u00fat {0} po uzloch potomka alebo pred vytvoren\u00edm elementu.  Atrib\u00fat bude ignorovan\u00fd." },

+

+            /*

+             * Note to translators:  The stylesheet contained a reference to a

+             * namespace prefix that was undefined.  The value of the substitution

+             * text is the name of the prefix.

+             */

+            {   MsgKey.ER_NAMESPACE_PREFIX,

+                "N\u00e1zvov\u00fd priestor pre predponu ''{0}'' nebol deklarovan\u00fd." },

+

+            /*

+             * Note to translators:  This message is reported if the stylesheet

+             * being processed attempted to construct an XML document with an

+             * attribute in a place other than on an element.  The substitution text

+             * specifies the name of the attribute.

+             */

+            {   MsgKey.ER_STRAY_ATTRIBUTE,

+                "Atrib\u00fat ''{0}'' je mimo prvku." },

+

+            /*

+             * Note to translators:  As with the preceding message, a namespace

+             * declaration has the form of an attribute and is only permitted to

+             * appear on an element.  The substitution text {0} is the namespace

+             * prefix and {1} is the URI that was being used in the erroneous

+             * namespace declaration.

+             */

+            {   MsgKey.ER_STRAY_NAMESPACE,

+                "Deklar\u00e1cia n\u00e1zvov\u00e9ho priestoru ''{0}''=''{1}'' je mimo prvku." },

+

+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,

+                "Nebolo mo\u017en\u00e9 zavies\u0165 ''{0}'' (skontrolujte CLASSPATH), teraz sa pou\u017e\u00edvaj\u00fa iba \u0161tandardn\u00e9 nastavenia" },

+

+            {   MsgKey.ER_ILLEGAL_CHARACTER,

+                "Pokus o v\u00fdstup znaku integr\u00e1lnej hodnoty {0}, ktor\u00e1 nie je reprezentovan\u00e1 v zadanom v\u00fdstupnom k\u00f3dovan\u00ed {1}." },

+

+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,

+                "Nebolo mo\u017en\u00e9 zavies\u0165 s\u00fabor vlastnost\u00ed ''{0}'' pre v\u00fdstupn\u00fa met\u00f3du ''{1}'' (skontrolujte CLASSPATH)" },

+

+            {   MsgKey.ER_INVALID_PORT,

+                "Neplatn\u00e9 \u010d\u00edslo portu" },

+

+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,

+                "Nem\u00f4\u017ee by\u0165 stanoven\u00fd port, ak je hostite\u013e nulov\u00fd" },

+

+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,

+                "Hostite\u013e nie je spr\u00e1vne form\u00e1tovan\u00e1 adresa" },

+

+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,

+                "Nezhodn\u00e1 sch\u00e9ma." },

+

+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,

+                "Nie je mo\u017en\u00e9 stanovi\u0165 sch\u00e9mu z nulov\u00e9ho re\u0165azca" },

+

+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,

+                "Cesta obsahuje neplatn\u00fa \u00fanikov\u00fa sekvenciu" },

+

+            {   MsgKey.ER_PATH_INVALID_CHAR,

+                "Cesta obsahuje neplatn\u00fd znak: {0}" },

+

+            {   MsgKey.ER_FRAG_INVALID_CHAR,

+                "Fragment obsahuje neplatn\u00fd znak" },

+

+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,

+                "Ak je cesta nulov\u00e1, nem\u00f4\u017ee by\u0165 stanoven\u00fd fragment" },

+

+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,

+                "Fragment m\u00f4\u017ee by\u0165 stanoven\u00fd len pre v\u0161eobecn\u00e9 URI" },

+

+            {   MsgKey.ER_NO_SCHEME_IN_URI,

+                "V URI nebola n\u00e1jden\u00e1 \u017eiadna sch\u00e9ma" },

+

+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,

+                "Nie je mo\u017en\u00e9 inicializova\u0165 URI s pr\u00e1zdnymi parametrami" },

+

+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,

+                "Fragment nem\u00f4\u017ee by\u0165 zadan\u00fd v ceste, ani vo fragmente" },

+

+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,

+                "Re\u0165azec dotazu nem\u00f4\u017ee by\u0165 zadan\u00fd v ceste a re\u0165azci dotazu" },

+

+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,

+                "Ak nebol zadan\u00fd hostite\u013e, mo\u017eno nebol zadan\u00fd port" },

+

+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,

+                "Ak nebol zadan\u00fd hostite\u013e, mo\u017eno nebolo zadan\u00e9 userinfo" },

+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,

+                "Varovanie:  Verzia v\u00fdstupn\u00e9ho dokumentu mus\u00ed by\u0165 povinne ''{0}''.  T\u00e1to verzia XML nie je podporovan\u00e1.  Verzia v\u00fdstupn\u00e9ho dokumentu bude ''1.0''." },

+

+            {   MsgKey.ER_SCHEME_REQUIRED,

+                "Je po\u017eadovan\u00e1 sch\u00e9ma!" },

+

+            /*

+             * Note to translators:  The words 'Properties' and

+             * 'SerializerFactory' in this message are Java class names

+             * and should not be translated.

+             */

+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,

+                "Objekt Properties, ktor\u00fd pre\u0161iel do SerializerFactory, nem\u00e1 vlastnos\u0165 ''{0}''." },

+

+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,

+                "Varovanie:  Java runtime nepodporuje k\u00f3dovanie ''{0}''." },

+

+             {MsgKey.ER_FEATURE_NOT_FOUND,

+             "Parameter ''{0}'' nebol rozpoznan\u00fd."},

+

+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,

+             "Parameter ''{0}'' bol rozpoznan\u00fd, ale vy\u017eadovan\u00e1 hodnota sa ned\u00e1 nastavi\u0165."},

+

+             {MsgKey.ER_STRING_TOO_LONG,

+             "V\u00fdsledn\u00fd re\u0165azec je pr\u00edli\u0161 dlh\u00fd a nezmest\u00ed sa do DOMString: ''{0}''."},

+

+             {MsgKey.ER_TYPE_MISMATCH_ERR,

+             "Typ hodnoty pre tento n\u00e1zov parametra je nekompatibiln\u00fd s o\u010dak\u00e1van\u00fdm typom hodnoty."},

+

+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,

+             "Cie\u013e v\u00fdstupu pre zap\u00edsanie \u00fadajov bol null."},

+

+             {MsgKey.ER_UNSUPPORTED_ENCODING,

+             "Bolo zaznamenan\u00e9 nepodporovan\u00e9 k\u00f3dovanie."},

+

+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,

+             "Uzol nebolo mo\u017en\u00e9 serializova\u0165."},

+

+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,

+             "\u010cas\u0165 CDATA obsahuje jeden alebo viacer\u00e9 ozna\u010dova\u010de konca ']]>'."},

+

+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,

+                 "Nebolo mo\u017en\u00e9 vytvori\u0165 in\u0161tanciu kontrol\u00f3ra Well-Formedness.  Parameter well-formed bol nastaven\u00fd na hodnotu true, ale kontrola well-formedness sa ned\u00e1 vykona\u0165."

+             },

+

+             {MsgKey.ER_WF_INVALID_CHARACTER,

+                 "Uzol ''{0}'' obsahuje neplatn\u00e9 znaky XML."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,

+                 "V koment\u00e1ri bol n\u00e1jden\u00fd neplatn\u00fd znak XML (Unicode: 0x{0})."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,

+                 "Pri spracovan\u00ed d\u00e1t in\u0161trukci\u00ed sa na\u0161iel neplatn\u00fd znak XML (Unicode: 0x{0})."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,

+                 "V obsahu CDATASection sa na\u0161iel neplatn\u00fd znak XML (Unicode: 0x{0})."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,

+                 "V obsahu znakov\u00fdch d\u00e1t uzla sa na\u0161iel neplatn\u00fd znak XML (Unicode: 0x{0})."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                 "V uzle {0} s n\u00e1zvom ''{1}'' sa na\u0161iel neplatn\u00fd znak XML."

+             },

+

+             { MsgKey.ER_WF_DASH_IN_COMMENT,

+                 "Re\u0165azec \"--\" nie je povolen\u00fd v r\u00e1mci koment\u00e1rov."

+             },

+

+             {MsgKey.ER_WF_LT_IN_ATTVAL,

+                 "Hodnota atrib\u00fatu \"{1}\", ktor\u00e1 je priraden\u00e1 k prvku typu \"{0}\", nesmie obsahova\u0165 znak ''<''."

+             },

+

+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,

+                 "Neanalyzovan\u00fd odkaz na entitu \"&{0};\" nie je povolen\u00fd."

+             },

+

+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,

+                 "Odkaz na extern\u00fa entitu \"&{0};\" nie je povolen\u00fd v hodnote atrib\u00fatu."

+             },

+

+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,

+                 "Predpona \"{0}\" nem\u00f4\u017ee by\u0165 naviazan\u00e1 na n\u00e1zvov\u00fd priestor \"{1}\"."

+             },

+

+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,

+                 "Lok\u00e1lny n\u00e1zov prvku \"{0}\" je null."

+             },

+

+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,

+                 "Lok\u00e1lny n\u00e1zov atrib\u00fatu \"{0}\" je null."

+             },

+

+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,

+                 "N\u00e1hradn\u00fd text pre uzol entity \"{0}\" obsahuje uzol prvku \"{1}\" s nenaviazanou predponou \"{2}\"."

+             },

+

+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,

+                 "N\u00e1hradn\u00fd text uzla entity \"{0}\" obsahuje uzol atrib\u00fatu \"{1}\" s nenaviazanou predponou \"{2}\"."

+             },

+

+        };

+

+        return contents;

+    }

+}

diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sl.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sl.java
new file mode 100755
index 0000000..fa40d4e
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sl.java
@@ -0,0 +1,293 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id: SerializerMessages_sl.java,v 1.7 2005/03/07 20:34:36 minchau Exp $

+ */

+

+package org.apache.xml.serializer.utils;

+

+import java.util.ListResourceBundle;

+import java.util.Locale;

+import java.util.MissingResourceException;

+import java.util.ResourceBundle;

+

+/**

+ * An instance of this class is a ListResourceBundle that

+ * has the required getContents() method that returns

+ * an array of message-key/message associations.

+ * <p>

+ * The message keys are defined in {@link MsgKey}. The

+ * messages that those keys map to are defined here.

+ * <p>

+ * The messages in the English version are intended to be

+ * translated.

+ *

+ * This class is not a public API, it is only public because it is

+ * used in org.apache.xml.serializer.

+ *

+ * @xsl.usage internal

+ */

+public class SerializerMessages_sl extends ListResourceBundle {

+

+    /*

+     * This file contains error and warning messages related to

+     * Serializer Error Handling.

+     *

+     *  General notes to translators:

+

+     *  1) A stylesheet is a description of how to transform an input XML document

+     *     into a resultant XML document (or HTML document or text).  The

+     *     stylesheet itself is described in the form of an XML document.

+

+     *

+     *  2) An element is a mark-up tag in an XML document; an attribute is a

+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>

+     *     "elem" is an element name, "attr" and "attr2" are attribute names with

+     *     the values "val" and "val2", respectively.

+     *

+     *  3) A namespace declaration is a special attribute that is used to associate

+     *     a prefix with a URI (the namespace).  The meanings of element names and

+     *     attribute names that use that prefix are defined with respect to that

+     *     namespace.

+     *

+     *

+     */

+

+    /** The lookup table for error messages.   */

+    public Object[][] getContents() {

+        Object[][] contents = new Object[][] {

+            {   MsgKey.BAD_MSGKEY,

+                "Klju\u010d sporo\u010dila ''{0}'' ni v rezredu sporo\u010dila ''{1}''" },

+

+            {   MsgKey.BAD_MSGFORMAT,

+                "Format sporo\u010dila ''{0}'' v razredu sporo\u010dila ''{1}'' je spodletel." },

+

+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,

+                "Razred serializerja ''{0}'' ne izvede org.xml.sax.ContentHandler." },

+

+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,

+                    "Vira [ {0} ] ni mogo\u010de najti.\n {1}" },

+

+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,

+                    "Sredstva [ {0} ] ni bilo mogo\u010de nalo\u017eiti: {1} \n {2} \t {3}" },

+

+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,

+                    "Velikost medpomnilnika <=0" },

+

+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,

+                    "Zaznan neveljaven nadomestek UTF-16: {0} ?" },

+

+            {   MsgKey.ER_OIERROR,

+                "Napaka V/I" },

+

+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,

+                "Atributa {0} ne morem dodati za podrejenimi vozli\u0161\u010di ali pred izdelavo elementa.  Atribut bo prezrt." },

+

+            /*

+             * Note to translators:  The stylesheet contained a reference to a

+             * namespace prefix that was undefined.  The value of the substitution

+             * text is the name of the prefix.

+             */

+            {   MsgKey.ER_NAMESPACE_PREFIX,

+                "Imenski prostor za predpono ''{0}'' ni bil naveden." },

+

+            /*

+             * Note to translators:  This message is reported if the stylesheet

+             * being processed attempted to construct an XML document with an

+             * attribute in a place other than on an element.  The substitution text

+             * specifies the name of the attribute.

+             */

+            {   MsgKey.ER_STRAY_ATTRIBUTE,

+                "Atribut ''{0}'' je zunaj elementa." },

+

+            /*

+             * Note to translators:  As with the preceding message, a namespace

+             * declaration has the form of an attribute and is only permitted to

+             * appear on an element.  The substitution text {0} is the namespace

+             * prefix and {1} is the URI that was being used in the erroneous

+             * namespace declaration.

+             */

+            {   MsgKey.ER_STRAY_NAMESPACE,

+                "Deklaracija imenskega prostora ''{0}''=''{1}'' je zunaj elementa." },

+

+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,

+                "Ni bilo mogo\u010de nalo\u017eiti ''{0}'' (preverite CLASSPATH), trenutno se uporabljajo samo privzete vrednosti" },

+

+            {   MsgKey.ER_ILLEGAL_CHARACTER,

+                "Poskus izpisa znaka integralne vrednosti {0}, ki v navedenem izhodnem kodiranju {1} ni zastopan." },

+

+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,

+                "Datoteke z lastnostmi ''{0}'' ni bilo mogo\u010de nalo\u017eiti za izhodno metodo ''{1}'' (preverite CLASSPATH)" },

+

+            {   MsgKey.ER_INVALID_PORT,

+                "Neveljavna \u0161tevilka vrat" },

+

+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,

+                "Ko je gostitelj NULL, nastavitev vrat ni mogo\u010da" },

+

+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,

+                "Naslov gostitelja ni pravilno oblikovan" },

+

+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,

+                "Shema ni skladna." },

+

+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,

+                "Ni mogo\u010de nastaviti sheme iz niza NULL" },

+

+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,

+                "Pot vsebuje neveljavno zaporedje za izhod" },

+

+            {   MsgKey.ER_PATH_INVALID_CHAR,

+                "Pot vsebuje neveljaven znak: {0}" },

+

+            {   MsgKey.ER_FRAG_INVALID_CHAR,

+                "Fragment vsebuje neveljaven znak" },

+

+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,

+                "Ko je pot NULL, nastavitev fragmenta ni mogo\u010da" },

+

+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,

+                "Fragment je lahko nastavljen samo za splo\u0161ni URI" },

+

+            {   MsgKey.ER_NO_SCHEME_IN_URI,

+                "Ne najdem sheme v URI" },

+

+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,

+                "Ni mogo\u010de inicializirati URI-ja s praznimi parametri" },

+

+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,

+                "Fragment ne more biti hkrati naveden v poti in v fragmentu" },

+

+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,

+                "Poizvedbeni niz ne more biti naveden v nizu poti in poizvedbenem nizu" },

+

+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,

+                "Vrata ne morejo biti navedena, \u010de ni naveden gostitelj" },

+

+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,

+                "Informacije o uporabniku ne morejo biti navedene, \u010de ni naveden gostitelj" },

+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,

+                "Opozorilo: Zahtevana razli\u010dica izhodnega dokumenta je ''{0}''.  Ta razli\u010dica XML ni podprta.  Razli\u010dica izhodnega dokumenta bo ''1.0''." },

+

+            {   MsgKey.ER_SCHEME_REQUIRED,

+                "Zahtevana je shema!" },

+

+            /*

+             * Note to translators:  The words 'Properties' and

+             * 'SerializerFactory' in this message are Java class names

+             * and should not be translated.

+             */

+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,

+                "Predmet Properties (lastnosti), ki je prene\u0161en v SerializerFactory, nima lastnosti ''{0}''." },

+

+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,

+                "Opozorilo:  Izvajalno okolje Java ne podpira kodiranja ''{0}''." },

+

+             {MsgKey.ER_FEATURE_NOT_FOUND,

+             "Parameter ''{0}'' ni prepoznan."},

+

+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,

+             "Parameter ''{0}'' je prepoznan, vendar pa zahtevane vrednosti ni mogo\u010de nastaviti."},

+

+             {MsgKey.ER_STRING_TOO_LONG,

+             "Nastali niz je predolg za DOMString: ''{0}''."},

+

+             {MsgKey.ER_TYPE_MISMATCH_ERR,

+             "Tip vrednosti za to ime parametra je nezdru\u017eljiv s pri\u010dakovanim tipom vrednosti."},

+

+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,

+             "Izhodno mesto za vpisovanje podatkov je bilo ni\u010d."},

+

+             {MsgKey.ER_UNSUPPORTED_ENCODING,

+             "Odkrito je nepodprto kodiranje."},

+

+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,

+             "Vozli\u0161\u010da ni mogo\u010de serializirati."},

+

+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,

+             "Odsek CDATA vsebuje enega ali ve\u010d ozna\u010devalnikov prekinitve ']]>'."},

+

+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,

+                 "Primerka preverjevalnika Well-Formedness ni bilo mogo\u010de ustvariti.  Parameter well-formed je bil nastavljen na True, ampak ni mogo\u010de preveriti well-formedness."

+             },

+

+             {MsgKey.ER_WF_INVALID_CHARACTER,

+                 "Vozli\u0161\u010de ''{0}'' vsebuje neveljavne znake XML."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,

+                 "V komentarju je bil najden neveljaven XML znak (Unicode: 0x{0})."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,

+                 "V podatkih navodila za obdelavo je bil najden neveljaven znak XML (Unicode: 0x{0})."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,

+                 "V vsebini odseka CDATASection je bil najden neveljaven znak XML (Unicode: 0x{0})."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,

+                 "V podatkovni vsebini znaka vozli\u0161\u010da je bil najden neveljaven znak XML (Unicode: 0x{0})."

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                 "V vozli\u0161\u010du {0} z imenom ''{1}'' je bil najden neveljaven znak XML."

+             },

+

+             { MsgKey.ER_WF_DASH_IN_COMMENT,

+                 "Niz \"--\" ni dovoljen v komentarjih."

+             },

+

+             {MsgKey.ER_WF_LT_IN_ATTVAL,

+                 "Vrednost atributa \"{1}\", ki je povezan s tipom elementa \"{0}\", ne sme vsebovati znaka ''<''."

+             },

+

+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,

+                 "Neraz\u010dlenjeni sklic entitete \"&{0};\" ni dovoljen."

+             },

+

+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,

+                 "Zunanji sklic entitete \"&{0};\" ni dovoljen v vrednosti atributa."

+             },

+

+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,

+                 "Predpona \"{0}\" ne more biti povezana z imenskim prostorom \"{1}\"."

+             },

+

+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,

+                 "Lokalno ime elementa \"{0}\" je ni\u010d."

+             },

+

+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,

+                 "Lokalno ime atributa \"{0}\" je ni\u010d."

+             },

+

+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,

+                 "Besedilo za zamenjavo za vozli\u0161\u010de entitete \"{0}\" vsebuje vozli\u0161\u010de elementa \"{1}\" z nevezano predpono \"{2}\"."

+             },

+

+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,

+                 "Besedilo za zamenjavo za vozli\u0161\u010de entitete \"{0}\" vsebuje vozli\u0161\u010de atributa \"{1}\" z nevezano predpono \"{2}\"."

+             },

+

+        };

+

+        return contents;

+    }

+}

diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sv.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sv.java
new file mode 100644
index 0000000..717ac41
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_sv.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_sv.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+
+public class SerializerMessages_sv extends ListResourceBundle {
+  public Object[][] getContents() {
+    Object[][] contents =  new Object[][] {
+        // BAD_MSGKEY needs translation
+        // BAD_MSGFORMAT needs translation
+        // ER_SERIALIZER_NOT_CONTENTHANDLER needs translation
+        // ER_RESOURCE_COULD_NOT_FIND needs translation
+        // ER_RESOURCE_COULD_NOT_LOAD needs translation
+        // ER_BUFFER_SIZE_LESSTHAN_ZERO needs translation
+        // ER_INVALID_UTF16_SURROGATE needs translation
+        // ER_OIERROR needs translation
+        // ER_ILLEGAL_ATTRIBUTE_POSITION needs translation
+        // ER_NAMESPACE_PREFIX needs translation
+        // ER_STRAY_ATTRIBUTE needs translation
+        // ER_STRAY_NAMESPACE needs translation
+        // ER_COULD_NOT_LOAD_RESOURCE needs translation
+        // ER_ILLEGAL_CHARACTER needs translation
+        // ER_COULD_NOT_LOAD_METHOD_PROPERTY needs translation
+      { MsgKey.ER_INVALID_PORT,
+        "Ogiltigt portnummer"},
+
+      { MsgKey.ER_PORT_WHEN_HOST_NULL,
+        "Port kan inte s\u00e4ttas n\u00e4r v\u00e4rd \u00e4r null"},
+
+      { MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+        "V\u00e4rd \u00e4r inte en v\u00e4lformulerad adress"},
+
+      { MsgKey.ER_SCHEME_NOT_CONFORMANT,
+        "Schemat \u00e4r inte likformigt."},
+
+      { MsgKey.ER_SCHEME_FROM_NULL_STRING,
+        "Kan inte s\u00e4tta schema fr\u00e5n null-str\u00e4ng"},
+
+      { MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+        "V\u00e4g inneh\u00e5ller ogiltig flyktsekvens"},
+
+      { MsgKey.ER_PATH_INVALID_CHAR,
+        "V\u00e4g inneh\u00e5ller ogiltigt tecken: {0}"},
+
+      { MsgKey.ER_FRAG_INVALID_CHAR,
+        "Fragment inneh\u00e5ller ogiltigt tecken"},
+
+      { MsgKey.ER_FRAG_WHEN_PATH_NULL,
+        "Fragment kan inte s\u00e4ttas n\u00e4r v\u00e4g \u00e4r null"},
+
+      { MsgKey.ER_FRAG_FOR_GENERIC_URI,
+        "Fragment kan bara s\u00e4ttas f\u00f6r en allm\u00e4n URI"},
+
+      { MsgKey.ER_NO_SCHEME_IN_URI,
+        "Schema saknas i URI: {0}"},
+
+      { MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+        "Kan inte initialisera URI med tomma parametrar"},
+
+      { MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+        "Fragment kan inte anges i b\u00e5de v\u00e4gen och fragmentet"},
+
+      { MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+        "F\u00f6rfr\u00e5gan-str\u00e4ng kan inte anges i v\u00e4g och f\u00f6rfr\u00e5gan-str\u00e4ng"},
+
+      { MsgKey.ER_NO_PORT_IF_NO_HOST,
+        "Port f\u00e5r inte anges om v\u00e4rden inte \u00e4r angiven"},
+
+      { MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+        "Userinfo f\u00e5r inte anges om v\u00e4rden inte \u00e4r angiven"},
+
+      { MsgKey.ER_SCHEME_REQUIRED,
+        "Schema kr\u00e4vs!"}
+
+    };
+    return contents;
+  }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_tr.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_tr.java
new file mode 100644
index 0000000..58d0088
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_tr.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_tr.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_tr extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "''{0}'' ileti anahtar\u0131 ''{1}'' ileti s\u0131n\u0131f\u0131nda yok" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "''{1}'' ileti s\u0131n\u0131f\u0131ndaki ''{0}'' iletisinin bi\u00e7imi ba\u015far\u0131s\u0131z." },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "''{0}'' diziselle\u015ftirme s\u0131n\u0131f\u0131 org.xml.sax.ContentHandler s\u0131n\u0131f\u0131n\u0131 ger\u00e7ekle\u015ftirmiyor." },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "Kaynak [ {0} ] bulunamad\u0131.\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "Kaynak [ {0} ] y\u00fckleyemedi: {1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "Arabellek b\u00fcy\u00fckl\u00fc\u011f\u00fc <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "UTF-16 yerine kullan\u0131lan de\u011fer ge\u00e7ersiz: {0} ?" },
+
+            {   MsgKey.ER_OIERROR,
+                "G\u00c7 hatas\u0131" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "Alt d\u00fc\u011f\u00fcmlerden sonra ya da bir \u00f6\u011fe \u00fcretilmeden \u00f6nce {0} \u00f6zniteli\u011fi eklenemez.  \u00d6znitelik yoksay\u0131lacak." },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "''{0}'' \u00f6nekine ili\u015fkin ad alan\u0131 bildirilmedi." },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "''{0}'' \u00f6zniteli\u011fi \u00f6\u011fenin d\u0131\u015f\u0131nda." },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "''{0}''=''{1}'' ad alan\u0131 bildirimi \u00f6\u011fenin d\u0131\u015f\u0131nda." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "''{0}'' y\u00fcklenemedi (CLASSPATH de\u011fi\u015fkeninizi inceleyin), yaln\u0131zca varsay\u0131lanlar kullan\u0131l\u0131yor" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "Belirtilen {1} \u00e7\u0131k\u0131\u015f kodlamas\u0131nda g\u00f6sterilmeyen {0} t\u00fcmlev de\u011feri karakteri \u00e7\u0131k\u0131\u015f giri\u015fimi." },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "''{1}'' \u00e7\u0131k\u0131\u015f y\u00f6ntemi i\u00e7in ''{0}'' \u00f6zellik dosyas\u0131 y\u00fcklenemedi (CLASSPATH de\u011fi\u015fkenini inceleyin)" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "Kap\u0131 numaras\u0131 ge\u00e7ersiz" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "Anasistem bo\u015f de\u011ferliyken kap\u0131 tan\u0131mlanamaz" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "Anasistem do\u011fru bi\u00e7imli bir adres de\u011fil" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "\u015eema uyumlu de\u011fil." },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "Bo\u015f de\u011ferli dizgiden \u015fema tan\u0131mlanamaz" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "Yol ge\u00e7ersiz ka\u00e7\u0131\u015f dizisi i\u00e7eriyor" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "Yol ge\u00e7ersiz karakter i\u00e7eriyor: {0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "Par\u00e7a ge\u00e7ersiz karakter i\u00e7eriyor" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "Yol bo\u015f de\u011ferliyken par\u00e7a tan\u0131mlanamaz" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "Par\u00e7a yaln\u0131zca soysal URI i\u00e7in tan\u0131mlanabilir" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "URI i\u00e7inde \u015fema bulunamad\u0131" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "Bo\u015f de\u011fi\u015ftirgelerle URI kullan\u0131ma haz\u0131rlanamaz" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "Par\u00e7a hem yolda, hem de par\u00e7ada belirtilemez" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "Yol ve sorgu dizgisinde sorgu dizgisi belirtilemez" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "Anasistem belirtilmediyse kap\u0131 belirtilemez" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "Anasistem belirtilmediyse kullan\u0131c\u0131 bilgisi belirtilemez" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "Uyar\u0131:  \u00c7\u0131k\u0131\u015f belgesinin s\u00fcr\u00fcm\u00fcn\u00fcn ''{0}'' olmas\u0131 isteniyor.  Bu XML s\u00fcr\u00fcm\u00fc desteklenmez.  \u00c7\u0131k\u0131\u015f dosyas\u0131n\u0131n s\u00fcr\u00fcm\u00fc ''1.0'' olacak." },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "\u015eema gerekli!" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "SerializerFactory''ye ge\u00e7irilen Properties nesnesinin bir ''{0}'' \u00f6zelli\u011fi yok." },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "Uyar\u0131: ''{0}'' kodlamas\u0131 Java Runtime taraf\u0131ndan desteklenmiyor." },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "''{0}'' de\u011fi\u015ftirgesi tan\u0131nm\u0131yor."},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "''{0}'' de\u011fi\u015ftirgesi tan\u0131n\u0131yor, ancak istenen de\u011fer tan\u0131mlanam\u0131yor."},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "Sonu\u00e7 dizgisi DOMString i\u00e7in \u00e7ok uzun: ''{0}''."},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "Bu de\u011fi\u015ftirge ad\u0131na ili\u015fkin de\u011fer tipi, beklenen de\u011fer tipiyle uyumlu de\u011fil."},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "Yaz\u0131lacak verilerin \u00e7\u0131k\u0131\u015f hedefi bo\u015f de\u011ferli."},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "Desteklenmeyen bir kodlama saptand\u0131."},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "D\u00fc\u011f\u00fcm diziselle\u015ftirilemedi."},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "CDATA k\u0131sm\u0131nda bir ya da daha \u00e7ok ']]>' sonland\u0131rma imleyicisi var."},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "Well-Formedness denet\u015feyicisinin somut \u00f6rne\u011fi yarat\u0131lamad\u0131.  well-formed de\u011fi\u015ftirgesi true de\u011ferine ayarland\u0131, ancak do\u011fru bi\u00e7im denetimi ger\u00e7ekle\u015ftirilemiyor."
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "''{0}'' d\u00fc\u011f\u00fcm\u00fc ge\u00e7ersiz XML karakterleri i\u00e7eriyor."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "A\u00e7\u0131klamada ge\u00e7ersiz bir XML karakteri (Unicode: 0x{0}) saptand\u0131."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "\u0130\u015fleme y\u00f6nergesi verilerinde ge\u00e7ersiz bir XML karakteri (Unicode: 0x{0}) saptand\u0131."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "CDATASection i\u00e7eri\u011finde ge\u00e7ersiz bir XML karakteri (Unicode: 0x{0}) saptand\u0131."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "D\u00fc\u011f\u00fcm\u00fcn karakter verileri i\u00e7eri\u011finde ge\u00e7ersiz bir XML karakteri (Unicode: 0x{0}) saptand\u0131."
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "''{1}'' adl\u0131 {0} d\u00fc\u011f\u00fcm\u00fcnde ge\u00e7ersiz XML karakteri saptand\u0131."
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "A\u00e7\u0131klamalar i\u00e7inde \"--\" dizgisine izin verilmez."
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "\"{0}\" \u00f6\u011fe tipiyle ili\u015fkilendirilen \"{1}\" \u00f6zniteli\u011finin de\u011feri ''<'' karakteri i\u00e7ermemelidir."
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "\"&{0};\" ayr\u0131\u015ft\u0131r\u0131lmam\u0131\u015f varl\u0131k ba\u015fvurusuna izin verilmez."
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "\u00d6znitelik de\u011ferinde \"&{0};\" d\u0131\u015f varl\u0131k ba\u015fvurusuna izin verilmez."
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "\"{0}\" \u00f6neki \"{1}\" ad alan\u0131na ba\u011flanam\u0131yor."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "\"{0}\" \u00f6\u011fesinin yerel ad\u0131 bo\u015f de\u011ferli."
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "\"{0}\" \u00f6zniteli\u011finin yerel ad\u0131 bo\u015f de\u011ferli."
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "\"{0}\" varl\u0131k d\u00fc\u011f\u00fcm\u00fcn\u00fcn yerine koyma metninde, ba\u011flanmam\u0131\u015f \"{2}\" \u00f6neki bulunan bir \u00f6\u011fe d\u00fc\u011f\u00fcm\u00fc (\"{1}\") var."
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "\"{0}\" varl\u0131k d\u00fc\u011f\u00fcm\u00fcn\u00fcn yerine koyma metninde, ba\u011flanmam\u0131\u015f \"{2}\" \u00f6neki bulunan bir \u00f6znitelik d\u00fc\u011f\u00fcm\u00fc (\"{1}\") var."
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh.java
new file mode 100755
index 0000000..dbb230d
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh.java
@@ -0,0 +1,291 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+/*

+ * $Id: SerializerMessages_zh.java,v 1.7 2005/03/07 20:34:36 minchau Exp $

+ */

+

+

+package org.apache.xml.serializer.utils;

+

+import java.util.ListResourceBundle;

+import java.util.Locale;

+import java.util.MissingResourceException;

+import java.util.ResourceBundle;

+

+/**

+ * An instance of this class is a ListResourceBundle that

+ * has the required getContents() method that returns

+ * an array of message-key/message associations.

+ * <p>

+ * The message keys are defined in {@link MsgKey}. The

+ * messages that those keys map to are defined here.

+ * <p>

+ * The messages in the English version are intended to be

+ * translated.

+ *

+ * This class is not a public API, it is only public because it is

+ * used in org.apache.xml.serializer.

+ *

+ * @xsl.usage internal

+ */

+public class SerializerMessages_zh extends ListResourceBundle {

+

+    /*

+     * This file contains error and warning messages related to

+     * Serializer Error Handling.

+     *

+     *  General notes to translators:

+

+     *  1) A stylesheet is a description of how to transform an input XML document

+     *     into a resultant XML document (or HTML document or text).  The

+     *     stylesheet itself is described in the form of an XML document.

+

+     *

+     *  2) An element is a mark-up tag in an XML document; an attribute is a

+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>

+     *     "elem" is an element name, "attr" and "attr2" are attribute names with

+     *     the values "val" and "val2", respectively.

+     *

+     *  3) A namespace declaration is a special attribute that is used to associate

+     *     a prefix with a URI (the namespace).  The meanings of element names and

+     *     attribute names that use that prefix are defined with respect to that

+     *     namespace.

+     *

+     *

+     */

+

+    /** The lookup table for error messages.   */

+    public Object[][] getContents() {

+        Object[][] contents = new Object[][] {

+            {   MsgKey.BAD_MSGKEY,

+                "\u6d88\u606f\u5bc6\u94a5\u201c{0}\u201d\u4e0d\u5728\u6d88\u606f\u7c7b\u201c{1}\u201d\u4e2d" },

+

+            {   MsgKey.BAD_MSGFORMAT,

+                "\u6d88\u606f\u7c7b\u201c{1}\u201d\u4e2d\u7684\u6d88\u606f\u201c{0}\u201d\u7684\u683c\u5f0f\u65e0\u6548\u3002" },

+

+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,

+                "\u4e32\u884c\u5668\u7c7b\u201c{0}\u201d\u4e0d\u80fd\u5b9e\u73b0 org.xml.sax.ContentHandler\u3002" },

+

+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,

+                    "\u627e\u4e0d\u5230\u8d44\u6e90 [ {0} ]\u3002\n {1}" },

+

+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,

+                    "\u8d44\u6e90 [ {0} ] \u65e0\u6cd5\u88c5\u5165\uff1a{1} \n {2} \t {3}" },

+

+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,

+                    "\u7f13\u51b2\u533a\u5927\u5c0f <=0" },

+

+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,

+                    "\u68c0\u6d4b\u5230\u65e0\u6548\u7684 UTF-16 \u8d85\u5927\u5b57\u7b26\u96c6\uff1a{0}\uff1f" },

+

+            {   MsgKey.ER_OIERROR,

+                "IO \u9519\u8bef" },

+

+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,

+                "\u5728\u751f\u6210\u5b50\u8282\u70b9\u4e4b\u540e\u6216\u5728\u751f\u6210\u5143\u7d20\u4e4b\u524d\u65e0\u6cd5\u6dfb\u52a0\u5c5e\u6027 {0}\u3002\u5c06\u5ffd\u7565\u5c5e\u6027\u3002" },

+

+            /*

+             * Note to translators:  The stylesheet contained a reference to a

+             * namespace prefix that was undefined.  The value of the substitution

+             * text is the name of the prefix.

+             */

+            {   MsgKey.ER_NAMESPACE_PREFIX,

+                "\u5c1a\u672a\u58f0\u660e\u524d\u7f00\u201c{0}\u201d\u7684\u540d\u79f0\u7a7a\u95f4\u3002" },

+

+            /*

+             * Note to translators:  This message is reported if the stylesheet

+             * being processed attempted to construct an XML document with an

+             * attribute in a place other than on an element.  The substitution text

+             * specifies the name of the attribute.

+             */

+            {   MsgKey.ER_STRAY_ATTRIBUTE,

+                "\u5c5e\u6027\u201c{0}\u201d\u5728\u5143\u7d20\u5916\u3002" },

+

+            /*

+             * Note to translators:  As with the preceding message, a namespace

+             * declaration has the form of an attribute and is only permitted to

+             * appear on an element.  The substitution text {0} is the namespace

+             * prefix and {1} is the URI that was being used in the erroneous

+             * namespace declaration.

+             */

+            {   MsgKey.ER_STRAY_NAMESPACE,

+                "\u540d\u79f0\u7a7a\u95f4\u58f0\u660e\u201c{0}\u201d=\u201c{1}\u201d\u5728\u5143\u7d20\u5916\u3002" },

+

+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,

+                "\u65e0\u6cd5\u88c5\u5165\u201c{0}\u201d\uff08\u68c0\u67e5 CLASSPATH\uff09\uff0c\u73b0\u5728\u53ea\u4f7f\u7528\u7f3a\u7701\u503c" },

+

+            {   MsgKey.ER_ILLEGAL_CHARACTER,

+                "\u5c1d\u8bd5\u8f93\u51fa\u6574\u6570\u503c {0}\uff08\u5b83\u4e0d\u662f\u4ee5\u6307\u5b9a\u7684 {1} \u8f93\u51fa\u7f16\u7801\u8868\u793a\uff09\u7684\u5b57\u7b26\u3002" },

+

+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,

+                "\u65e0\u6cd5\u4e3a\u8f93\u51fa\u65b9\u6cd5\u201c{1}\u201d\u88c5\u5165\u5c5e\u6027\u6587\u4ef6\u201c{0}\u201d\uff08\u68c0\u67e5 CLASSPATH\uff09" },

+

+            {   MsgKey.ER_INVALID_PORT,

+                "\u7aef\u53e3\u53f7\u65e0\u6548" },

+

+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,

+                "\u4e3b\u673a\u4e3a\u7a7a\u65f6\uff0c\u65e0\u6cd5\u8bbe\u7f6e\u7aef\u53e3" },

+

+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,

+                "\u4e3b\u673a\u4e0d\u662f\u683c\u5f0f\u6b63\u786e\u7684\u5730\u5740" },

+

+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,

+                "\u6a21\u5f0f\u4e0d\u4e00\u81f4\u3002" },

+

+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,

+                "\u65e0\u6cd5\u4ece\u7a7a\u5b57\u7b26\u4e32\u8bbe\u7f6e\u6a21\u5f0f" },

+

+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,

+                "\u8def\u5f84\u5305\u542b\u65e0\u6548\u7684\u8f6c\u4e49\u5e8f\u5217" },

+

+            {   MsgKey.ER_PATH_INVALID_CHAR,

+                "\u8def\u5f84\u5305\u542b\u65e0\u6548\u7684\u5b57\u7b26\uff1a{0}" },

+

+            {   MsgKey.ER_FRAG_INVALID_CHAR,

+                "\u7247\u6bb5\u5305\u542b\u65e0\u6548\u7684\u5b57\u7b26" },

+

+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,

+                "\u8def\u5f84\u4e3a\u7a7a\u65f6\uff0c\u65e0\u6cd5\u8bbe\u7f6e\u7247\u6bb5" },

+

+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,

+                "\u53ea\u80fd\u4e3a\u7c7b\u5c5e URI \u8bbe\u7f6e\u7247\u6bb5" },

+

+            {   MsgKey.ER_NO_SCHEME_IN_URI,

+                "URI \u4e2d\u627e\u4e0d\u5230\u4efb\u4f55\u6a21\u5f0f" },

+

+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,

+                "\u4e0d\u80fd\u4ee5\u7a7a\u53c2\u6570\u521d\u59cb\u5316 URI" },

+

+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,

+                "\u8def\u5f84\u548c\u7247\u6bb5\u4e2d\u90fd\u4e0d\u80fd\u6307\u5b9a\u7247\u6bb5" },

+

+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,

+                "\u8def\u5f84\u548c\u67e5\u8be2\u5b57\u7b26\u4e32\u4e2d\u4e0d\u80fd\u6307\u5b9a\u67e5\u8be2\u5b57\u7b26\u4e32" },

+

+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,

+                "\u5982\u679c\u6ca1\u6709\u6307\u5b9a\u4e3b\u673a\uff0c\u5219\u4e0d\u53ef\u4ee5\u6307\u5b9a\u7aef\u53e3" },

+

+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,

+                "\u5982\u679c\u6ca1\u6709\u6307\u5b9a\u4e3b\u673a\uff0c\u5219\u4e0d\u53ef\u4ee5\u6307\u5b9a\u7528\u6237\u4fe1\u606f" },

+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,

+                "\u8b66\u544a\uff1a\u8981\u6c42\u8f93\u51fa\u6587\u6863\u7684\u7248\u672c\u662f\u201c{0}\u201d\u3002\u4e0d\u652f\u6301\u6b64 XML \u7248\u672c\u3002\u8f93\u51fa\u6587\u6863\u7684\u7248\u672c\u5c06\u4f1a\u662f\u201c1.0\u201d\u3002" },

+

+            {   MsgKey.ER_SCHEME_REQUIRED,

+                "\u6a21\u5f0f\u662f\u5fc5\u9700\u7684\uff01" },

+

+            /*

+             * Note to translators:  The words 'Properties' and

+             * 'SerializerFactory' in this message are Java class names

+             * and should not be translated.

+             */

+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,

+                "\u4f20\u9012\u7ed9 SerializerFactory \u7684 Properties \u5bf9\u8c61\u4e0d\u5177\u6709\u5c5e\u6027\u201c{0}\u201d\u3002" },

+

+             {MsgKey.ER_FEATURE_NOT_FOUND,

+             "\u672a\u8bc6\u522b\u51fa\u53c2\u6570\u201c{0}\u201d\u3002"},

+

+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,

+             "\u5df2\u8bc6\u522b\u51fa\u53c2\u6570\u201c{0}\u201d\uff0c\u4f46\u65e0\u6cd5\u8bbe\u7f6e\u8bf7\u6c42\u7684\u503c\u3002"},

+

+             {MsgKey.ER_STRING_TOO_LONG,

+             "\u4ea7\u751f\u7684\u5b57\u7b26\u4e32\u8fc7\u957f\u4e0d\u80fd\u88c5\u5165 DOMString\uff1a\u201c{0}\u201d\u3002"},

+

+             {MsgKey.ER_TYPE_MISMATCH_ERR,

+             "\u6b64\u53c2\u6570\u540d\u79f0\u7684\u503c\u7c7b\u578b\u4e0e\u671f\u671b\u7684\u503c\u7c7b\u578b\u4e0d\u517c\u5bb9\u3002"},

+

+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,

+             "\u5c06\u8981\u5199\u5165\u6570\u636e\u7684\u8f93\u51fa\u76ee\u6807\u4e3a\u7a7a\u3002"},

+

+             {MsgKey.ER_UNSUPPORTED_ENCODING,

+             "\u9047\u5230\u4e0d\u53d7\u652f\u6301\u7684\u7f16\u7801\u3002"},

+

+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,

+             "\u65e0\u6cd5\u5c06\u8282\u70b9\u5e8f\u5217\u5316\u3002 "},

+

+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,

+             "CDATA \u90e8\u5206\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u7ec8\u6b62\u6807\u8bb0\u201c]]>\u201d\u3002"},

+

+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,

+                 "\u65e0\u6cd5\u521b\u5efa\u683c\u5f0f\u6b63\u786e\u6027\u68c0\u67e5\u5668\u7684\u5b9e\u4f8b\u3002\u201c\u683c\u5f0f\u6b63\u786e\u201d\u53c2\u6570\u5df2\u8bbe\u7f6e\u4e3a true\uff0c\u4f46\u65e0\u6cd5\u6267\u884c\u683c\u5f0f\u6b63\u786e\u6027\u68c0\u67e5\u3002"

+             },

+

+             {MsgKey.ER_WF_INVALID_CHARACTER,

+                 "\u8282\u70b9\u201c{0}\u201d\u5305\u542b\u65e0\u6548\u7684 XML \u5b57\u7b26\u3002"

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,

+                 "\u5728\u6ce8\u91ca\u4e2d\u627e\u5230\u65e0\u6548\u7684 XML \u5b57\u7b26 (Unicode: 0x''{0})''\u3002"

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,

+                 "\u5728\u5904\u7406\u6307\u4ee4\u6570\u636e\u4e2d\u627e\u5230\u65e0\u6548\u7684 XML \u5b57\u7b26 (Unicode: 0x''{0})''\u3002"

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,

+                 "\u5728 CDATA \u90e8\u5206\u7684\u5185\u5bb9\u4e2d\u627e\u5230\u65e0\u6548\u7684 XML \u5b57\u7b26 (Unicode: 0x''{0})''\u3002"

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,

+                 "\u5728\u8282\u70b9\u7684\u5b57\u7b26\u6570\u636e\u5185\u5bb9\u4e2d\u627e\u5230\u65e0\u6548\u7684 XML \u5b57\u7b26 (Unicode: 0x''{0})''\u3002"

+             },

+

+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,

+                 "\u540d\u79f0\u4e3a\u201c{1})\u201d\u7684\u201c{0})\u201d\u4e2d\u627e\u5230\u65e0\u6548\u7684 XML \u5b57\u7b26\u3002"

+             },

+

+             { MsgKey.ER_WF_DASH_IN_COMMENT,

+                 "\u6ce8\u91ca\u4e2d\u4e0d\u5141\u8bb8\u6709\u5b57\u7b26\u4e32\u201c--\u201d\u3002"

+             },

+

+             {MsgKey.ER_WF_LT_IN_ATTVAL,

+                 "\u4e0e\u5143\u7d20\u7c7b\u578b\u201c{0}\u201d\u5173\u8054\u7684\u5c5e\u6027\u201c{1}\u201d\u7684\u503c\u4e0d\u5f97\u5305\u542b\u201c<\u201d\u5b57\u7b26\u3002"

+             },

+

+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,

+                 "\u4e0d\u5141\u8bb8\u6709\u672a\u89e3\u6790\u7684\u5b9e\u4f53\u5f15\u7528\u201c&{0};\u201d\u3002"

+             },

+

+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,

+                 "\u5c5e\u6027\u503c\u4e2d\u4e0d\u5141\u8bb8\u6709\u5916\u90e8\u5b9e\u4f53\u5f15\u7528\u201c&{0};\u201d\u3002"

+             },

+

+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,

+                 "\u524d\u7f00\u201c{0}\u201d\u4e0d\u80fd\u7ed1\u5b9a\u5230\u540d\u79f0\u7a7a\u95f4\u201c{1}\u201d\u3002"

+             },

+

+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,

+                 "\u5143\u7d20\u201c{0}\u201d\u7684\u5c40\u90e8\u540d\u4e3a\u7a7a\u3002"

+             },

+

+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,

+                 "\u5c5e\u6027\u201c{0}\u201d\u7684\u5c40\u90e8\u540d\u4e3a\u7a7a\u3002"

+             },

+

+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,

+                 "\u5b9e\u4f53\u8282\u70b9\u201c{0}\u201d\u7684\u66ff\u4ee3\u6587\u672c\u4e2d\u5305\u542b\u5143\u7d20\u8282\u70b9\u201c{1}\u201d\uff0c\u8be5\u8282\u70b9\u5177\u6709\u672a\u7ed1\u5b9a\u7684\u524d\u7f00\u201c{2}\u201d\u3002"

+             },

+

+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,

+                 "\u5b9e\u4f53\u8282\u70b9\u201c{0}\u201d\u7684\u66ff\u4ee3\u6587\u672c\u4e2d\u5305\u542b\u5c5e\u6027\u8282\u70b9\u201c{1}\u201d\uff0c\u8be5\u8282\u70b9\u5177\u6709\u672a\u7ed1\u5b9a\u7684\u524d\u7f00\u201c{2}\u201d\u3002"

+             },

+

+        };

+

+        return contents;

+    }

+}

diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh_CN.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh_CN.java
new file mode 100644
index 0000000..e8719ac
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh_CN.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_zh_CN.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+
+public class SerializerMessages_zh_CN extends SerializerMessages_zh
+{
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh_TW.java b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh_TW.java
new file mode 100644
index 0000000..9525b18
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SerializerMessages_zh_TW.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SerializerMessages_zh_TW.java 471981 2006-11-07 04:28:00Z minchau $
+ */
+
+package org.apache.xml.serializer.utils;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An instance of this class is a ListResourceBundle that
+ * has the required getContents() method that returns
+ * an array of message-key/message associations.
+ * <p>
+ * The message keys are defined in {@link MsgKey}. The
+ * messages that those keys map to are defined here.
+ * <p>
+ * The messages in the English version are intended to be
+ * translated.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer.
+ *
+ * @xsl.usage internal
+ */
+public class SerializerMessages_zh_TW extends ListResourceBundle {
+
+    /*
+     * This file contains error and warning messages related to
+     * Serializer Error Handling.
+     *
+     *  General notes to translators:
+
+     *  1) A stylesheet is a description of how to transform an input XML document
+     *     into a resultant XML document (or HTML document or text).  The
+     *     stylesheet itself is described in the form of an XML document.
+
+     *
+     *  2) An element is a mark-up tag in an XML document; an attribute is a
+     *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+     *     "elem" is an element name, "attr" and "attr2" are attribute names with
+     *     the values "val" and "val2", respectively.
+     *
+     *  3) A namespace declaration is a special attribute that is used to associate
+     *     a prefix with a URI (the namespace).  The meanings of element names and
+     *     attribute names that use that prefix are defined with respect to that
+     *     namespace.
+     *
+     *
+     */
+
+    /** The lookup table for error messages.   */
+    public Object[][] getContents() {
+        Object[][] contents = new Object[][] {
+            {   MsgKey.BAD_MSGKEY,
+                "\u8a0a\u606f\u9375 ''{0}'' \u4e0d\u5728\u8a0a\u606f\u985e\u5225 ''{1}'' \u4e2d" },
+
+            {   MsgKey.BAD_MSGFORMAT,
+                "\u8a0a\u606f\u985e\u5225 ''{1}'' \u4e2d\u7684\u8a0a\u606f ''{0}'' \u683c\u5f0f\u5316\u5931\u6557\u3002" },
+
+            {   MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
+                "Serializer \u985e\u5225 ''{0}'' \u4e0d\u5be6\u4f5c org.xml.sax.ContentHandler\u3002" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_FIND,
+                    "\u627e\u4e0d\u5230\u8cc7\u6e90 [ {0} ]\u3002\n {1}" },
+
+            {   MsgKey.ER_RESOURCE_COULD_NOT_LOAD,
+                    "\u7121\u6cd5\u8f09\u5165\u8cc7\u6e90 [ {0} ]\uff1a{1} \n {2} \t {3}" },
+
+            {   MsgKey.ER_BUFFER_SIZE_LESSTHAN_ZERO,
+                    "\u7de9\u885d\u5340\u5927\u5c0f <=0" },
+
+            {   MsgKey.ER_INVALID_UTF16_SURROGATE,
+                    "\u5075\u6e2c\u5230\u7121\u6548\u7684 UTF-16 \u4ee3\u7406\uff1a{0}?" },
+
+            {   MsgKey.ER_OIERROR,
+                "IO \u932f\u8aa4" },
+
+            {   MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,
+                "\u5728\u7522\u751f\u5b50\u9805\u7bc0\u9ede\u4e4b\u5f8c\uff0c\u6216\u5728\u7522\u751f\u5143\u7d20\u4e4b\u524d\uff0c\u4e0d\u53ef\u65b0\u589e\u5c6c\u6027 {0}\u3002\u5c6c\u6027\u6703\u88ab\u5ffd\u7565\u3002" },
+
+            /*
+             * Note to translators:  The stylesheet contained a reference to a
+             * namespace prefix that was undefined.  The value of the substitution
+             * text is the name of the prefix.
+             */
+            {   MsgKey.ER_NAMESPACE_PREFIX,
+                "\u5b57\u9996 ''{0}'' \u7684\u540d\u7a31\u7a7a\u9593\u5c1a\u672a\u5ba3\u544a\u3002" },
+
+            /*
+             * Note to translators:  This message is reported if the stylesheet
+             * being processed attempted to construct an XML document with an
+             * attribute in a place other than on an element.  The substitution text
+             * specifies the name of the attribute.
+             */
+            {   MsgKey.ER_STRAY_ATTRIBUTE,
+                "\u5c6c\u6027 ''{0}'' \u8d85\u51fa\u5143\u7d20\u5916\u3002" },
+
+            /*
+             * Note to translators:  As with the preceding message, a namespace
+             * declaration has the form of an attribute and is only permitted to
+             * appear on an element.  The substitution text {0} is the namespace
+             * prefix and {1} is the URI that was being used in the erroneous
+             * namespace declaration.
+             */
+            {   MsgKey.ER_STRAY_NAMESPACE,
+                "\u540d\u7a31\u7a7a\u9593\u5ba3\u544a ''{0}''=''{1}'' \u8d85\u51fa\u5143\u7d20\u5916\u3002" },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_RESOURCE,
+                "\u7121\u6cd5\u8f09\u5165 ''{0}''\uff08\u6aa2\u67e5 CLASSPATH\uff09\uff0c\u76ee\u524d\u53ea\u4f7f\u7528\u9810\u8a2d\u503c" },
+
+            {   MsgKey.ER_ILLEGAL_CHARACTER,
+                "\u8a66\u5716\u8f38\u51fa\u4e0d\u662f\u4ee5\u6307\u5b9a\u7684\u8f38\u51fa\u7de8\u78bc {1} \u5448\u73fe\u7684\u6574\u6578\u503c {0} \u7684\u5b57\u5143\u3002" },
+
+            {   MsgKey.ER_COULD_NOT_LOAD_METHOD_PROPERTY,
+                "\u7121\u6cd5\u8f09\u5165\u8f38\u51fa\u65b9\u6cd5 ''{1}''\uff08\u6aa2\u67e5 CLASSPATH\uff09\u7684\u5167\u5bb9\u6a94 ''{0}''" },
+
+            {   MsgKey.ER_INVALID_PORT,
+                "\u7121\u6548\u7684\u57e0\u7de8\u865f" },
+
+            {   MsgKey.ER_PORT_WHEN_HOST_NULL,
+                "\u4e3b\u6a5f\u70ba\u7a7a\u503c\u6642\uff0c\u7121\u6cd5\u8a2d\u5b9a\u57e0" },
+
+            {   MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED,
+                "\u4e3b\u6a5f\u6c92\u6709\u5b8c\u6574\u7684\u4f4d\u5740" },
+
+            {   MsgKey.ER_SCHEME_NOT_CONFORMANT,
+                "\u7db1\u8981\u4e0d\u662f conformant\u3002" },
+
+            {   MsgKey.ER_SCHEME_FROM_NULL_STRING,
+                "\u7121\u6cd5\u5f9e\u7a7a\u5b57\u4e32\u8a2d\u5b9a\u7db1\u8981" },
+
+            {   MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE,
+                "\u8def\u5f91\u5305\u542b\u7121\u6548\u7684\u8df3\u812b\u5b57\u5143" },
+
+            {   MsgKey.ER_PATH_INVALID_CHAR,
+                "\u8def\u5f91\u5305\u542b\u7121\u6548\u7684\u5b57\u5143\uff1a{0}" },
+
+            {   MsgKey.ER_FRAG_INVALID_CHAR,
+                "\u7247\u6bb5\u5305\u542b\u7121\u6548\u7684\u5b57\u5143" },
+
+            {   MsgKey.ER_FRAG_WHEN_PATH_NULL,
+                "\u8def\u5f91\u70ba\u7a7a\u503c\u6642\uff0c\u7121\u6cd5\u8a2d\u5b9a\u7247\u6bb5" },
+
+            {   MsgKey.ER_FRAG_FOR_GENERIC_URI,
+                "\u53ea\u80fd\u5c0d\u901a\u7528\u7684 URI \u8a2d\u5b9a\u7247\u6bb5" },
+
+            {   MsgKey.ER_NO_SCHEME_IN_URI,
+                "\u5728 URI \u627e\u4e0d\u5230\u7db1\u8981" },
+
+            {   MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS,
+                "\u7121\u6cd5\u4ee5\u7a7a\u767d\u53c3\u6578\u8d77\u59cb\u8a2d\u5b9a URI" },
+
+            {   MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH,
+                "\u7247\u6bb5\u7121\u6cd5\u540c\u6642\u5728\u8def\u5f91\u548c\u7247\u6bb5\u4e2d\u6307\u5b9a" },
+
+            {   MsgKey.ER_NO_QUERY_STRING_IN_PATH,
+                "\u5728\u8def\u5f91\u53ca\u67e5\u8a62\u5b57\u4e32\u4e2d\u4e0d\u53ef\u6307\u5b9a\u67e5\u8a62\u5b57\u4e32" },
+
+            {   MsgKey.ER_NO_PORT_IF_NO_HOST,
+                "\u5982\u679c\u6c92\u6709\u6307\u5b9a\u4e3b\u6a5f\uff0c\u4e0d\u53ef\u6307\u5b9a\u57e0" },
+
+            {   MsgKey.ER_NO_USERINFO_IF_NO_HOST,
+                "\u5982\u679c\u6c92\u6709\u6307\u5b9a\u4e3b\u6a5f\uff0c\u4e0d\u53ef\u6307\u5b9a Userinfo" },
+            {   MsgKey.ER_XML_VERSION_NOT_SUPPORTED,
+                "\u8b66\u544a\uff1a\u8f38\u51fa\u6587\u4ef6\u7684\u7248\u672c\u8981\u6c42\u662f ''{0}''\u3002\u672a\u652f\u63f4\u9019\u500b\u7248\u672c\u7684 XML\u3002\u8f38\u51fa\u6587\u4ef6\u7684\u7248\u672c\u6703\u662f ''1.0''\u3002" },
+
+            {   MsgKey.ER_SCHEME_REQUIRED,
+                "\u7db1\u8981\u662f\u5fc5\u9700\u7684\uff01" },
+
+            /*
+             * Note to translators:  The words 'Properties' and
+             * 'SerializerFactory' in this message are Java class names
+             * and should not be translated.
+             */
+            {   MsgKey.ER_FACTORY_PROPERTY_MISSING,
+                "\u50b3\u905e\u5230 SerializerFactory \u7684 Properties \u7269\u4ef6\u6c92\u6709 ''{0}'' \u5167\u5bb9\u3002" },
+
+            {   MsgKey.ER_ENCODING_NOT_SUPPORTED,
+                "\u8b66\u544a\uff1aJava \u57f7\u884c\u6642\u671f\u4e0d\u652f\u63f4\u7de8\u78bc ''{0}''\u3002" },
+
+             {MsgKey.ER_FEATURE_NOT_FOUND,
+             "\u7121\u6cd5\u8fa8\u8b58\u53c3\u6578 ''{0}''\u3002"},
+
+             {MsgKey.ER_FEATURE_NOT_SUPPORTED,
+             "\u53ef\u8fa8\u8b58 ''{0}'' \u53c3\u6578\uff0c\u4f46\u6240\u8981\u6c42\u7684\u503c\u7121\u6cd5\u8a2d\u5b9a\u3002"},
+
+             {MsgKey.ER_STRING_TOO_LONG,
+             "\u7d50\u679c\u5b57\u4e32\u904e\u9577\uff0c\u7121\u6cd5\u7f6e\u5165 DOMString: ''{0}'' \u4e2d\u3002"},
+
+             {MsgKey.ER_TYPE_MISMATCH_ERR,
+             "\u9019\u500b\u53c3\u6578\u540d\u7a31\u7684\u503c\u985e\u578b\u8207\u671f\u671b\u503c\u985e\u578b\u4e0d\u76f8\u5bb9\u3002"},
+
+             {MsgKey.ER_NO_OUTPUT_SPECIFIED,
+             "\u8cc7\u6599\u8981\u5beb\u5165\u7684\u8f38\u51fa\u76ee\u7684\u5730\u70ba\u7a7a\u503c\u3002"},
+
+             {MsgKey.ER_UNSUPPORTED_ENCODING,
+             "\u767c\u73fe\u4e0d\u652f\u63f4\u7684\u7de8\u78bc\u3002"},
+
+             {MsgKey.ER_UNABLE_TO_SERIALIZE_NODE,
+             "\u7bc0\u9ede\u7121\u6cd5\u5e8f\u5217\u5316\u3002"},
+
+             {MsgKey.ER_CDATA_SECTIONS_SPLIT,
+             "CDATA \u5340\u6bb5\u5305\u542b\u4e00\u6216\u591a\u500b\u7d42\u6b62\u6a19\u8a18 ']]>'\u3002"},
+
+             {MsgKey.ER_WARNING_WF_NOT_CHECKED,
+                 "\u7121\u6cd5\u5efa\u7acb\u300c\u5f62\u5f0f\u5b8c\u6574\u300d\u6aa2\u67e5\u7a0b\u5f0f\u7684\u5be6\u4f8b\u3002Well-formed \u53c3\u6578\u96d6\u8a2d\u70ba true\uff0c\u4f46\u7121\u6cd5\u57f7\u884c\u5f62\u5f0f\u5b8c\u6574\u6aa2\u67e5\u3002"
+             },
+
+             {MsgKey.ER_WF_INVALID_CHARACTER,
+                 "\u7bc0\u9ede ''{0}'' \u5305\u542b\u7121\u6548\u7684 XML \u5b57\u5143\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
+                 "\u5728\u8a3b\u89e3\u4e2d\u767c\u73fe\u7121\u6548\u7684 XML \u5b57\u5143 (Unicode: 0x{0})\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
+                 "\u5728\u8655\u7406\u7a0b\u5e8f instructiondata \u4e2d\u767c\u73fe\u7121\u6548\u7684 XML \u5b57\u5143 (Unicode: 0x{0})\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
+                 "\u5728 CDATASection \u7684\u5167\u5bb9\u4e2d\u767c\u73fe\u7121\u6548\u7684 XML \u5b57\u5143 (Unicode: 0x{0})\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
+                 "\u5728\u7bc0\u9ede\u7684\u5b57\u5143\u8cc7\u6599\u5167\u5bb9\u4e2d\u767c\u73fe\u7121\u6548\u7684 XML \u5b57\u5143 (Unicode: 0x{0})\u3002"
+             },
+
+             { MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
+                 "\u5728\u540d\u70ba ''{1}'' \u7684 ''{0}'' \u4e2d\u767c\u73fe\u7121\u6548\u7684 XML \u5b57\u5143\u3002"
+             },
+
+             { MsgKey.ER_WF_DASH_IN_COMMENT,
+                 "\u8a3b\u89e3\u4e2d\u4e0d\u5141\u8a31\u4f7f\u7528\u5b57\u4e32 \"--\"\u3002"
+             },
+
+             {MsgKey.ER_WF_LT_IN_ATTVAL,
+                 "\u8207\u5143\u7d20\u985e\u578b \"{0}\" \u76f8\u95dc\u806f\u7684\u5c6c\u6027 \"{1}\" \u503c\u4e0d\u53ef\u5305\u542b ''<'' \u5b57\u5143\u3002"
+             },
+
+             {MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
+                 "\u4e0d\u5141\u8a31\u4f7f\u7528\u672a\u5256\u6790\u7684\u5be6\u9ad4\u53c3\u7167 \"&{0};\"\u3002"
+             },
+
+             {MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
+                 "\u5c6c\u6027\u503c\u4e2d\u4e0d\u5141\u8a31\u4f7f\u7528\u5916\u90e8\u5be6\u9ad4\u53c3\u7167 \"&{0};\"\u3002"
+             },
+
+             {MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
+                 "\u5b57\u9996 \"{0}\" \u7121\u6cd5\u9023\u7d50\u5230\u540d\u7a31\u7a7a\u9593 \"{1}\"\u3002"
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
+                 "\u5143\u7d20 \"{0}\" \u7684\u672c\u7aef\u540d\u7a31\u662f\u7a7a\u503c\u3002"
+             },
+
+             {MsgKey.ER_NULL_LOCAL_ATTR_NAME,
+                 "\u5c6c\u6027 \"{0}\" \u7684\u672c\u7aef\u540d\u7a31\u662f\u7a7a\u503c\u3002"
+             },
+
+             { MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
+                 "\u5be6\u9ad4\u7bc0\u9ede \"{0}\" \u7684\u53d6\u4ee3\u6587\u5b57\u5305\u542b\u9644\u6709\u5df2\u5207\u65b7\u9023\u7d50\u5b57\u9996 \"{2}\" \u7684\u5143\u7d20\u7bc0\u9ede \"{1}\"\u3002"
+             },
+
+             { MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
+                 "\u5be6\u9ad4\u7bc0\u9ede \"{0}\" \u7684\u53d6\u4ee3\u6587\u5b57\u5305\u542b\u9644\u6709\u5df2\u5207\u65b7\u9023\u7d50\u5b57\u9996 \"{2}\" \u7684\u5c6c\u6027\u7bc0\u9ede \"{1}\"\u3002"
+             },
+
+        };
+
+        return contents;
+    }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/StringToIntTable.java b/src/main/java/org/apache/xml/serializer/utils/StringToIntTable.java
new file mode 100644
index 0000000..13c753b
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/StringToIntTable.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StringToIntTable.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+/**
+ * A very simple lookup table that stores a list of strings, the even
+ * number strings being keys, and the odd number strings being values.
+ * 
+ * This class is a copy of the one in org.apache.xml.utils. 
+ * It exists to cut the serializers dependancy on that package.
+ * 
+ * This class is not a public API, it is only public so it can be used
+ * in org.apache.xml.serializer.
+ *  
+ * @xsl.usage internal
+ */
+public final class StringToIntTable
+{
+
+  public static final int INVALID_KEY = -10000;
+  
+  /** Block size to allocate          */
+  private int m_blocksize;
+
+  /** Array of strings this table points to. Associated with ints
+   * in m_values         */
+  private String m_map[];
+
+  /** Array of ints this table points. Associated with strings from
+   * m_map.         */
+  private int m_values[];
+
+  /** Number of ints in the table          */
+  private int m_firstFree = 0;
+
+  /** Size of this table         */
+  private int m_mapSize;
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is very small, for small lists.
+   */
+  public StringToIntTable()
+  {
+
+    m_blocksize = 8;
+    m_mapSize = m_blocksize;
+    m_map = new String[m_blocksize];
+    m_values = new int[m_blocksize];
+  }
+
+  /**
+   * Construct a StringToIntTable, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public StringToIntTable(int blocksize)
+  {
+
+    m_blocksize = blocksize;
+    m_mapSize = blocksize;
+    m_map = new String[blocksize];
+    m_values = new int[m_blocksize];
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return the length of the list 
+   */
+  public final int getLength()
+  {
+    return m_firstFree;
+  }
+
+  /**
+   * Append a string onto the vector.
+   *
+   * @param key String to append
+   * @param value The int value of the string
+   */
+  public final void put(String key, int value)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      String newMap[] = new String[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+
+      int newValues[] = new int[m_mapSize];
+
+      System.arraycopy(m_values, 0, newValues, 0, m_firstFree + 1);
+
+      m_values = newValues;
+    }
+
+    m_map[m_firstFree] = key;
+    m_values[m_firstFree] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Tell if the table contains the given string.
+   *
+   * @param key String to look for
+   *
+   * @return The String's int value
+   * 
+   */
+  public final int get(String key)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i].equals(key))
+        return m_values[i];
+    }
+
+    return INVALID_KEY;
+  }
+
+  /**
+   * Tell if the table contains the given string. Ignore case.
+   *
+   * @param key String to look for
+   *
+   * @return The string's int value
+   */
+  public final int getIgnoreCase(String key)
+  {
+
+    if (null == key)
+        return INVALID_KEY;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i].equalsIgnoreCase(key))
+        return m_values[i];
+    }
+
+    return INVALID_KEY;
+  }
+
+  /**
+   * Tell if the table contains the given string.
+   *
+   * @param key String to look for
+   *
+   * @return True if the string is in the table
+   */
+  public final boolean contains(String key)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i].equals(key))
+        return true;
+    }
+
+    return false;
+  }
+  
+  /**
+   * Return array of keys in the table.
+   * 
+   * @return Array of strings
+   */
+  public final String[] keys()
+  {
+    String [] keysArr = new String[m_firstFree];
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      keysArr[i] = m_map[i];
+    }
+
+    return keysArr;
+  }  
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/SystemIDResolver.java b/src/main/java/org/apache/xml/serializer/utils/SystemIDResolver.java
new file mode 100644
index 0000000..f10a300
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/SystemIDResolver.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SystemIDResolver.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+import java.io.File;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.serializer.utils.URI.MalformedURIException;
+
+/**
+ * This class is used to resolve relative URIs and SystemID 
+ * strings into absolute URIs.
+ *
+ * <p>This is a generic utility for resolving URIs, other than the 
+ * fact that it's declared to throw TransformerException.  Please 
+ * see code comments for details on how resolution is performed.</p>
+ * 
+ * This class is a copy of the one in org.apache.xml.utils. 
+ * It exists to cut the serializers dependancy on that package.
+ *
+ * This class is not a public API, it is only public because it is
+ * used in org.apache.xml.serializer. 
+ * 
+ * @xsl.usage internal
+ */
+public final class SystemIDResolver
+{
+
+  /**
+   * Get an absolute URI from a given relative URI (local path). 
+   * 
+   * <p>The relative URI is a local filesystem path. The path can be
+   * absolute or relative. If it is a relative path, it is resolved relative 
+   * to the system property "user.dir" if it is available; if not (i.e. in an 
+   * Applet perhaps which throws SecurityException) then we just return the
+   * relative path. The space and backslash characters are also replaced to
+   * generate a good absolute URI.</p>
+   *
+   * @param localPath The relative URI to resolve
+   *
+   * @return Resolved absolute URI
+   */
+  public static String getAbsoluteURIFromRelative(String localPath)
+  {
+    if (localPath == null || localPath.length() == 0)
+      return "";
+      
+    // If the local path is a relative path, then it is resolved against
+    // the "user.dir" system property.
+    String absolutePath = localPath;
+    if (!isAbsolutePath(localPath))
+    {
+      try 
+      {
+        absolutePath = getAbsolutePathFromRelativePath(localPath);
+      }
+      // user.dir not accessible from applet
+      catch (SecurityException se) 
+      {
+        return "file:" + localPath;
+      }
+    }
+
+    String urlString;
+    if (null != absolutePath)
+    {
+      if (absolutePath.startsWith(File.separator))
+        urlString = "file://" + absolutePath;
+      else
+        urlString = "file:///" + absolutePath;        
+    }
+    else
+      urlString = "file:" + localPath;
+    
+    return replaceChars(urlString);
+  }
+  
+  /**
+   * Return an absolute path from a relative path.
+   *
+   * @param relativePath A relative path
+   * @return The absolute path
+   */
+  private static String getAbsolutePathFromRelativePath(String relativePath)
+  {
+    return new File(relativePath).getAbsolutePath();
+  }
+  
+  /**
+   * Return true if the systemId denotes an absolute URI .
+   *
+   * @param systemId The systemId string
+   * @return true if the systemId is an an absolute URI
+   */
+  public static boolean isAbsoluteURI(String systemId)
+  {
+     /** http://www.ietf.org/rfc/rfc2396.txt
+      *   Authors should be aware that a path segment which contains a colon
+      * character cannot be used as the first segment of a relative URI path
+      * (e.g., "this:that"), because it would be mistaken for a scheme name.
+     **/
+     /** 
+      * %REVIEW% Can we assume here that systemId is a valid URI?
+      * It looks like we cannot ( See discussion of this common problem in 
+      * Bugzilla Bug 22777 ). 
+     **/
+     //"fix" for Bugzilla Bug 22777
+    if(isWindowsAbsolutePath(systemId)){
+        return false;
+     }
+    
+    final int fragmentIndex = systemId.indexOf('#');
+    final int queryIndex = systemId.indexOf('?');
+    final int slashIndex = systemId.indexOf('/');
+    final int colonIndex = systemId.indexOf(':');
+    
+    //finding substring  before '#', '?', and '/' 
+    int index = systemId.length() -1;
+    if(fragmentIndex > 0) 
+        index = fragmentIndex;
+    if((queryIndex > 0) && (queryIndex <index)) 
+        index = queryIndex;
+    if((slashIndex > 0) && (slashIndex <index))
+        index = slashIndex; 
+    // return true if there is ':' before '#', '?', and '/'
+    return ((colonIndex >0) && (colonIndex<index));
+    
+  }
+  
+  /**
+   * Return true if the local path is an absolute path.
+   *
+   * @param systemId The path string
+   * @return true if the path is absolute
+   */
+  public static boolean isAbsolutePath(String systemId)
+  {
+    if(systemId == null)
+        return false;
+    final File file = new File(systemId);
+    return file.isAbsolute();
+    
+  }
+  
+   /**
+   * Return true if the local path is a Windows absolute path.
+   *
+   * @param systemId The path string
+   * @return true if the path is a Windows absolute path
+   */
+    private static boolean isWindowsAbsolutePath(String systemId)
+  {
+    if(!isAbsolutePath(systemId))
+      return false;
+    // On Windows, an absolute path starts with "[drive_letter]:\".
+    if (systemId.length() > 2 
+        && systemId.charAt(1) == ':'
+        && Character.isLetter(systemId.charAt(0))
+        && (systemId.charAt(2) == '\\' || systemId.charAt(2) == '/'))
+      return true;
+    else
+      return false;
+  }
+  
+  /**
+   * Replace spaces with "%20" and backslashes with forward slashes in 
+   * the input string to generate a well-formed URI string.
+   *
+   * @param str The input string
+   * @return The string after conversion
+   */
+  private static String replaceChars(String str)
+  {
+    StringBuffer buf = new StringBuffer(str);
+    int length = buf.length();
+    for (int i = 0; i < length; i++)
+    {
+      char currentChar = buf.charAt(i);
+      // Replace space with "%20"
+      if (currentChar == ' ')
+      {
+        buf.setCharAt(i, '%');
+        buf.insert(i+1, "20");
+        length = length + 2;
+        i = i + 2;
+      }
+      // Replace backslash with forward slash
+      else if (currentChar == '\\')
+      {
+        buf.setCharAt(i, '/');
+      }
+    }
+    
+    return buf.toString();
+  }
+  
+  /**
+   * Take a SystemID string and try to turn it into a good absolute URI.
+   *
+   * @param systemId A URI string, which may be absolute or relative.
+   *
+   * @return The resolved absolute URI
+   */
+  public static String getAbsoluteURI(String systemId)
+  {
+    String absoluteURI = systemId;
+    if (isAbsoluteURI(systemId))
+    {
+      // Only process the systemId if it starts with "file:".
+      if (systemId.startsWith("file:"))
+      {
+        String str = systemId.substring(5);
+        
+        // Resolve the absolute path if the systemId starts with "file:///"
+        // or "file:/". Don't do anything if it only starts with "file://".
+        if (str != null && str.startsWith("/"))
+        {
+          if (str.startsWith("///") || !str.startsWith("//"))
+          {
+            // A Windows path containing a drive letter can be relative.
+            // A Unix path starting with "file:/" is always absolute.
+            int secondColonIndex = systemId.indexOf(':', 5);
+            if (secondColonIndex > 0)
+            {
+              String localPath = systemId.substring(secondColonIndex-1);
+              try {
+                if (!isAbsolutePath(localPath))
+                  absoluteURI = systemId.substring(0, secondColonIndex-1) + 
+                                getAbsolutePathFromRelativePath(localPath);
+              }
+              catch (SecurityException se) {
+                return systemId;
+              }
+            }
+          }          
+        }
+        else
+        {
+          return getAbsoluteURIFromRelative(systemId.substring(5));
+        }
+                
+        return replaceChars(absoluteURI);
+      }
+      else
+        return systemId;
+    }
+    else
+      return getAbsoluteURIFromRelative(systemId);
+    
+  }
+
+
+  /**
+   * Take a SystemID string and try to turn it into a good absolute URI.
+   *
+   * @param urlString SystemID string
+   * @param base The URI string used as the base for resolving the systemID
+   *
+   * @return The resolved absolute URI
+   * @throws TransformerException thrown if the string can't be turned into a URI.
+   */
+  public static String getAbsoluteURI(String urlString, String base)
+          throws TransformerException
+  {    
+    if (base == null)
+      return getAbsoluteURI(urlString);
+    
+    String absoluteBase = getAbsoluteURI(base);
+    URI uri = null;
+    try 
+    {
+      URI baseURI = new URI(absoluteBase);
+      uri = new URI(baseURI, urlString);
+    }
+    catch (MalformedURIException mue)
+    {
+      throw new TransformerException(mue);
+    }
+    
+    return replaceChars(uri.toString());
+  }
+  
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/URI.java b/src/main/java/org/apache/xml/serializer/utils/URI.java
new file mode 100644
index 0000000..b77518b
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/URI.java
@@ -0,0 +1,1653 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: URI.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+
+/**
+ * A class to represent a Uniform Resource Identifier (URI). This class
+ * is designed to handle the parsing of URIs and provide access to
+ * the various components (scheme, host, port, userinfo, path, query
+ * string and fragment) that may constitute a URI.
+ * <p>
+ * Parsing of a URI specification is done according to the URI
+ * syntax described in RFC 2396
+ * <http://www.ietf.org/rfc/rfc2396.txt?number=2396>. Every URI consists
+ * of a scheme, followed by a colon (':'), followed by a scheme-specific
+ * part. For URIs that follow the "generic URI" syntax, the scheme-
+ * specific part begins with two slashes ("//") and may be followed
+ * by an authority segment (comprised of user information, host, and
+ * port), path segment, query segment and fragment. Note that RFC 2396
+ * no longer specifies the use of the parameters segment and excludes
+ * the "user:password" syntax as part of the authority segment. If
+ * "user:password" appears in a URI, the entire user/password string
+ * is stored as userinfo.
+ * <p>
+ * For URIs that do not follow the "generic URI" syntax (e.g. mailto),
+ * the entire scheme-specific part is treated as the "path" portion
+ * of the URI.
+ * <p>
+ * Note that, unlike the java.net.URL class, this class does not provide
+ * any built-in network access functionality nor does it provide any
+ * scheme-specific functionality (for example, it does not know a
+ * default port for a specific scheme). Rather, it only knows the
+ * grammar and basic set of operations that can be applied to a URI.
+ *
+ * This class is a copy of the one in org.apache.xml.utils. 
+ * It exists to cut the serializers dependancy on that package.
+ * 
+ * A minor change from the original is that this class no longer implements
+ * Serializable, and the serialVersionUID magic field is dropped, and
+ * the class is no longer "public".
+ *  
+ * @xsl.usage internal
+ */
+final class URI
+{
+  /**
+   * MalformedURIExceptions are thrown in the process of building a URI
+   * or setting fields on a URI when an operation would result in an
+   * invalid URI specification.
+   *
+   */
+  public static class MalformedURIException extends IOException
+  {
+
+    /**
+     * Constructs a <code>MalformedURIException</code> with no specified
+     * detail message.
+     */
+    public MalformedURIException()
+    {
+      super();
+    }
+
+    /**
+     * Constructs a <code>MalformedURIException</code> with the
+     * specified detail message.
+     *
+     * @param p_msg the detail message.
+     */
+    public MalformedURIException(String p_msg)
+    {
+      super(p_msg);
+    }
+  }
+
+  /** reserved characters */
+  private static final String RESERVED_CHARACTERS = ";/?:@&=+$,";
+
+  /**
+   * URI punctuation mark characters - these, combined with
+   *   alphanumerics, constitute the "unreserved" characters 
+   */
+  private static final String MARK_CHARACTERS = "-_.!~*'() ";
+
+  /** scheme can be composed of alphanumerics and these characters */
+  private static final String SCHEME_CHARACTERS = "+-.";
+
+  /**
+   * userinfo can be composed of unreserved, escaped and these
+   *   characters 
+   */
+  private static final String USERINFO_CHARACTERS = ";:&=+$,";
+
+  /** Stores the scheme (usually the protocol) for this URI.
+   *  @serial */
+  private String m_scheme = null;
+
+  /** If specified, stores the userinfo for this URI; otherwise null.
+   *  @serial */
+  private String m_userinfo = null;
+
+  /** If specified, stores the host for this URI; otherwise null.
+   *  @serial */
+  private String m_host = null;
+
+  /** If specified, stores the port for this URI; otherwise -1.
+   *  @serial */
+  private int m_port = -1;
+
+  /** If specified, stores the path for this URI; otherwise null.
+   *  @serial */
+  private String m_path = null;
+
+  /**
+   * If specified, stores the query string for this URI; otherwise
+   *   null. 
+   * @serial 
+   */
+  private String m_queryString = null;
+
+  /** If specified, stores the fragment for this URI; otherwise null.
+   *  @serial */
+  private String m_fragment = null;
+
+  /** Indicate whether in DEBUG mode          */
+  private static boolean DEBUG = false;
+
+  /**
+   * Construct a new and uninitialized URI.
+   */
+  public URI(){}
+
+  /**
+   * Construct a new URI from another URI. All fields for this URI are
+   * set equal to the fields of the URI passed in.
+   *
+   * @param p_other the URI to copy (cannot be null)
+   */
+  public URI(URI p_other)
+  {
+    initialize(p_other);
+  }
+
+  /**
+   * Construct a new URI from a URI specification string. If the
+   * specification follows the "generic URI" syntax, (two slashes
+   * following the first colon), the specification will be parsed
+   * accordingly - setting the scheme, userinfo, host,port, path, query
+   * string and fragment fields as necessary. If the specification does
+   * not follow the "generic URI" syntax, the specification is parsed
+   * into a scheme and scheme-specific part (stored as the path) only.
+   *
+   * @param p_uriSpec the URI specification string (cannot be null or
+   *                  empty)
+   *
+   * @throws MalformedURIException if p_uriSpec violates any syntax
+   *                                   rules
+   */
+  public URI(String p_uriSpec) throws MalformedURIException
+  {
+    this((URI) null, p_uriSpec);
+  }
+
+  /**
+   * Construct a new URI from a base URI and a URI specification string.
+   * The URI specification string may be a relative URI.
+   *
+   * @param p_base the base URI (cannot be null if p_uriSpec is null or
+   *               empty)
+   * @param p_uriSpec the URI specification string (cannot be null or
+   *                  empty if p_base is null)
+   *
+   * @throws MalformedURIException if p_uriSpec violates any syntax
+   *                                  rules
+   */
+  public URI(URI p_base, String p_uriSpec) throws MalformedURIException
+  {
+    initialize(p_base, p_uriSpec);
+  }
+
+  /**
+   * Construct a new URI that does not follow the generic URI syntax.
+   * Only the scheme and scheme-specific part (stored as the path) are
+   * initialized.
+   *
+   * @param p_scheme the URI scheme (cannot be null or empty)
+   * @param p_schemeSpecificPart the scheme-specific part (cannot be
+   *                             null or empty)
+   *
+   * @throws MalformedURIException if p_scheme violates any
+   *                                  syntax rules
+   */
+  public URI(String p_scheme, String p_schemeSpecificPart)
+          throws MalformedURIException
+  {
+
+    if (p_scheme == null || p_scheme.trim().length() == 0)
+    {
+      throw new MalformedURIException(
+        "Cannot construct URI with null/empty scheme!");
+    }
+
+    if (p_schemeSpecificPart == null
+            || p_schemeSpecificPart.trim().length() == 0)
+    {
+      throw new MalformedURIException(
+        "Cannot construct URI with null/empty scheme-specific part!");
+    }
+
+    setScheme(p_scheme);
+    setPath(p_schemeSpecificPart);
+  }
+
+  /**
+   * Construct a new URI that follows the generic URI syntax from its
+   * component parts. Each component is validated for syntax and some
+   * basic semantic checks are performed as well.  See the individual
+   * setter methods for specifics.
+   *
+   * @param p_scheme the URI scheme (cannot be null or empty)
+   * @param p_host the hostname or IPv4 address for the URI
+   * @param p_path the URI path - if the path contains '?' or '#',
+   *               then the query string and/or fragment will be
+   *               set from the path; however, if the query and
+   *               fragment are specified both in the path and as
+   *               separate parameters, an exception is thrown
+   * @param p_queryString the URI query string (cannot be specified
+   *                      if path is null)
+   * @param p_fragment the URI fragment (cannot be specified if path
+   *                   is null)
+   *
+   * @throws MalformedURIException if any of the parameters violates
+   *                                  syntax rules or semantic rules
+   */
+  public URI(String p_scheme, String p_host, String p_path, String p_queryString, String p_fragment)
+          throws MalformedURIException
+  {
+    this(p_scheme, null, p_host, -1, p_path, p_queryString, p_fragment);
+  }
+
+  /**
+   * Construct a new URI that follows the generic URI syntax from its
+   * component parts. Each component is validated for syntax and some
+   * basic semantic checks are performed as well.  See the individual
+   * setter methods for specifics.
+   *
+   * @param p_scheme the URI scheme (cannot be null or empty)
+   * @param p_userinfo the URI userinfo (cannot be specified if host
+   *                   is null)
+   * @param p_host the hostname or IPv4 address for the URI
+   * @param p_port the URI port (may be -1 for "unspecified"; cannot
+   *               be specified if host is null)
+   * @param p_path the URI path - if the path contains '?' or '#',
+   *               then the query string and/or fragment will be
+   *               set from the path; however, if the query and
+   *               fragment are specified both in the path and as
+   *               separate parameters, an exception is thrown
+   * @param p_queryString the URI query string (cannot be specified
+   *                      if path is null)
+   * @param p_fragment the URI fragment (cannot be specified if path
+   *                   is null)
+   *
+   * @throws MalformedURIException if any of the parameters violates
+   *                                  syntax rules or semantic rules
+   */
+  public URI(String p_scheme, String p_userinfo, String p_host, int p_port, String p_path, String p_queryString, String p_fragment)
+          throws MalformedURIException
+  {
+
+    if (p_scheme == null || p_scheme.trim().length() == 0)
+    {
+      throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_SCHEME_REQUIRED, null)); //"Scheme is required!");
+    }
+
+    if (p_host == null)
+    {
+      if (p_userinfo != null)
+      {
+        throw new MalformedURIException(
+          Utils.messages.createMessage(MsgKey.ER_NO_USERINFO_IF_NO_HOST, null)); //"Userinfo may not be specified if host is not specified!");
+      }
+
+      if (p_port != -1)
+      {
+        throw new MalformedURIException(
+          Utils.messages.createMessage(MsgKey.ER_NO_PORT_IF_NO_HOST, null)); //"Port may not be specified if host is not specified!");
+      }
+    }
+
+    if (p_path != null)
+    {
+      if (p_path.indexOf('?') != -1 && p_queryString != null)
+      {
+        throw new MalformedURIException(
+          Utils.messages.createMessage(MsgKey.ER_NO_QUERY_STRING_IN_PATH, null)); //"Query string cannot be specified in path and query string!");
+      }
+
+      if (p_path.indexOf('#') != -1 && p_fragment != null)
+      {
+        throw new MalformedURIException(
+          Utils.messages.createMessage(MsgKey.ER_NO_FRAGMENT_STRING_IN_PATH, null)); //"Fragment cannot be specified in both the path and fragment!");
+      }
+    }
+
+    setScheme(p_scheme);
+    setHost(p_host);
+    setPort(p_port);
+    setUserinfo(p_userinfo);
+    setPath(p_path);
+    setQueryString(p_queryString);
+    setFragment(p_fragment);
+  }
+
+  /**
+   * Initialize all fields of this URI from another URI.
+   *
+   * @param p_other the URI to copy (cannot be null)
+   */
+  private void initialize(URI p_other)
+  {
+
+    m_scheme = p_other.getScheme();
+    m_userinfo = p_other.getUserinfo();
+    m_host = p_other.getHost();
+    m_port = p_other.getPort();
+    m_path = p_other.getPath();
+    m_queryString = p_other.getQueryString();
+    m_fragment = p_other.getFragment();
+  }
+
+  /**
+   * Initializes this URI from a base URI and a URI specification string.
+   * See RFC 2396 Section 4 and Appendix B for specifications on parsing
+   * the URI and Section 5 for specifications on resolving relative URIs
+   * and relative paths.
+   *
+   * @param p_base the base URI (may be null if p_uriSpec is an absolute
+   *               URI)
+   * @param p_uriSpec the URI spec string which may be an absolute or
+   *                  relative URI (can only be null/empty if p_base
+   *                  is not null)
+   *
+   * @throws MalformedURIException if p_base is null and p_uriSpec
+   *                                  is not an absolute URI or if
+   *                                  p_uriSpec violates syntax rules
+   */
+  private void initialize(URI p_base, String p_uriSpec)
+          throws MalformedURIException
+  {
+
+    if (p_base == null
+            && (p_uriSpec == null || p_uriSpec.trim().length() == 0))
+    {
+      throw new MalformedURIException(
+        Utils.messages.createMessage(MsgKey.ER_CANNOT_INIT_URI_EMPTY_PARMS, null)); //"Cannot initialize URI with empty parameters.");
+    }
+
+    // just make a copy of the base if spec is empty
+    if (p_uriSpec == null || p_uriSpec.trim().length() == 0)
+    {
+      initialize(p_base);
+
+      return;
+    }
+
+    String uriSpec = p_uriSpec.trim();
+    int uriSpecLen = uriSpec.length();
+    int index = 0;
+
+    // check for scheme
+    int colonIndex = uriSpec.indexOf(':');
+    if (colonIndex < 0)
+    {
+      if (p_base == null)
+      {
+        throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_NO_SCHEME_IN_URI, new Object[]{uriSpec})); //"No scheme found in URI: "+uriSpec);
+      }
+    }
+    else
+    {
+      initializeScheme(uriSpec);
+      uriSpec = uriSpec.substring(colonIndex+1);
+      uriSpecLen = uriSpec.length();
+    }
+
+    // two slashes means generic URI syntax, so we get the authority
+    if (uriSpec.startsWith("//"))
+    {
+      index += 2;
+
+      int startPos = index;
+
+      // get authority - everything up to path, query or fragment
+      char testChar = '\0';
+
+      while (index < uriSpecLen)
+      {
+        testChar = uriSpec.charAt(index);
+
+        if (testChar == '/' || testChar == '?' || testChar == '#')
+        {
+          break;
+        }
+
+        index++;
+      }
+
+      // if we found authority, parse it out, otherwise we set the
+      // host to empty string
+      if (index > startPos)
+      {
+        initializeAuthority(uriSpec.substring(startPos, index));
+      }
+      else
+      {
+        m_host = "";
+      }
+    }
+
+    initializePath(uriSpec.substring(index));
+
+    // Resolve relative URI to base URI - see RFC 2396 Section 5.2
+    // In some cases, it might make more sense to throw an exception
+    // (when scheme is specified is the string spec and the base URI
+    // is also specified, for example), but we're just following the
+    // RFC specifications 
+    if (p_base != null)
+    {
+
+      // check to see if this is the current doc - RFC 2396 5.2 #2
+      // note that this is slightly different from the RFC spec in that
+      // we don't include the check for query string being null
+      // - this handles cases where the urispec is just a query
+      // string or a fragment (e.g. "?y" or "#s") - 
+      // see <http://www.ics.uci.edu/~fielding/url/test1.html> which
+      // identified this as a bug in the RFC
+      if (m_path.length() == 0 && m_scheme == null && m_host == null)
+      {
+        m_scheme = p_base.getScheme();
+        m_userinfo = p_base.getUserinfo();
+        m_host = p_base.getHost();
+        m_port = p_base.getPort();
+        m_path = p_base.getPath();
+
+        if (m_queryString == null)
+        {
+          m_queryString = p_base.getQueryString();
+        }
+
+        return;
+      }
+
+      // check for scheme - RFC 2396 5.2 #3
+      // if we found a scheme, it means absolute URI, so we're done
+      if (m_scheme == null)
+      {
+        m_scheme = p_base.getScheme();
+      }
+
+      // check for authority - RFC 2396 5.2 #4
+      // if we found a host, then we've got a network path, so we're done
+      if (m_host == null)
+      {
+        m_userinfo = p_base.getUserinfo();
+        m_host = p_base.getHost();
+        m_port = p_base.getPort();
+      }
+      else
+      {
+        return;
+      }
+
+      // check for absolute path - RFC 2396 5.2 #5
+      if (m_path.length() > 0 && m_path.startsWith("/"))
+      {
+        return;
+      }
+
+      // if we get to this point, we need to resolve relative path
+      // RFC 2396 5.2 #6
+      String path = new String();
+      String basePath = p_base.getPath();
+
+      // 6a - get all but the last segment of the base URI path
+      if (basePath != null)
+      {
+        int lastSlash = basePath.lastIndexOf('/');
+
+        if (lastSlash != -1)
+        {
+          path = basePath.substring(0, lastSlash + 1);
+        }
+      }
+
+      // 6b - append the relative URI path
+      path = path.concat(m_path);
+
+      // 6c - remove all "./" where "." is a complete path segment
+      index = -1;
+
+      while ((index = path.indexOf("/./")) != -1)
+      {
+        path = path.substring(0, index + 1).concat(path.substring(index + 3));
+      }
+
+      // 6d - remove "." if path ends with "." as a complete path segment
+      if (path.endsWith("/."))
+      {
+        path = path.substring(0, path.length() - 1);
+      }
+
+      // 6e - remove all "<segment>/../" where "<segment>" is a complete 
+      // path segment not equal to ".."
+      index = -1;
+
+      int segIndex = -1;
+      String tempString = null;
+
+      while ((index = path.indexOf("/../")) > 0)
+      {
+        tempString = path.substring(0, path.indexOf("/../"));
+        segIndex = tempString.lastIndexOf('/');
+
+        if (segIndex != -1)
+        {
+          if (!tempString.substring(segIndex++).equals(".."))
+          {
+            path = path.substring(0, segIndex).concat(path.substring(index
+                    + 4));
+          }
+        }
+      }
+
+      // 6f - remove ending "<segment>/.." where "<segment>" is a 
+      // complete path segment
+      if (path.endsWith("/.."))
+      {
+        tempString = path.substring(0, path.length() - 3);
+        segIndex = tempString.lastIndexOf('/');
+
+        if (segIndex != -1)
+        {
+          path = path.substring(0, segIndex + 1);
+        }
+      }
+
+      m_path = path;
+    }
+  }
+
+  /**
+   * Initialize the scheme for this URI from a URI string spec.
+   *
+   * @param p_uriSpec the URI specification (cannot be null)
+   *
+   * @throws MalformedURIException if URI does not have a conformant
+   *                                  scheme
+   */
+  private void initializeScheme(String p_uriSpec) throws MalformedURIException
+  {
+
+    int uriSpecLen = p_uriSpec.length();
+    int index = 0;
+    String scheme = null;
+    char testChar = '\0';
+
+    while (index < uriSpecLen)
+    {
+      testChar = p_uriSpec.charAt(index);
+
+      if (testChar == ':' || testChar == '/' || testChar == '?'
+              || testChar == '#')
+      {
+        break;
+      }
+
+      index++;
+    }
+
+    scheme = p_uriSpec.substring(0, index);
+
+    if (scheme.length() == 0)
+    {
+      throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_NO_SCHEME_INURI, null)); //"No scheme found in URI.");
+    }
+    else
+    {
+      setScheme(scheme);
+    }
+  }
+
+  /**
+   * Initialize the authority (userinfo, host and port) for this
+   * URI from a URI string spec.
+   *
+   * @param p_uriSpec the URI specification (cannot be null)
+   *
+   * @throws MalformedURIException if p_uriSpec violates syntax rules
+   */
+  private void initializeAuthority(String p_uriSpec)
+          throws MalformedURIException
+  {
+
+    int index = 0;
+    int start = 0;
+    int end = p_uriSpec.length();
+    char testChar = '\0';
+    String userinfo = null;
+
+    // userinfo is everything up @
+    if (p_uriSpec.indexOf('@', start) != -1)
+    {
+      while (index < end)
+      {
+        testChar = p_uriSpec.charAt(index);
+
+        if (testChar == '@')
+        {
+          break;
+        }
+
+        index++;
+      }
+
+      userinfo = p_uriSpec.substring(start, index);
+
+      index++;
+    }
+
+    // host is everything up to ':'
+    String host = null;
+
+    start = index;
+
+    while (index < end)
+    {
+      testChar = p_uriSpec.charAt(index);
+
+      if (testChar == ':')
+      {
+        break;
+      }
+
+      index++;
+    }
+
+    host = p_uriSpec.substring(start, index);
+
+    int port = -1;
+
+    if (host.length() > 0)
+    {
+
+      // port
+      if (testChar == ':')
+      {
+        index++;
+
+        start = index;
+
+        while (index < end)
+        {
+          index++;
+        }
+
+        String portStr = p_uriSpec.substring(start, index);
+
+        if (portStr.length() > 0)
+        {
+          for (int i = 0; i < portStr.length(); i++)
+          {
+            if (!isDigit(portStr.charAt(i)))
+            {
+              throw new MalformedURIException(
+                portStr + " is invalid. Port should only contain digits!");
+            }
+          }
+
+          try
+          {
+            port = Integer.parseInt(portStr);
+          }
+          catch (NumberFormatException nfe)
+          {
+
+            // can't happen
+          }
+        }
+      }
+    }
+
+    setHost(host);
+    setPort(port);
+    setUserinfo(userinfo);
+  }
+
+  /**
+   * Initialize the path for this URI from a URI string spec.
+   *
+   * @param p_uriSpec the URI specification (cannot be null)
+   *
+   * @throws MalformedURIException if p_uriSpec violates syntax rules
+   */
+  private void initializePath(String p_uriSpec) throws MalformedURIException
+  {
+
+    if (p_uriSpec == null)
+    {
+      throw new MalformedURIException(
+        "Cannot initialize path from null string!");
+    }
+
+    int index = 0;
+    int start = 0;
+    int end = p_uriSpec.length();
+    char testChar = '\0';
+
+    // path - everything up to query string or fragment
+    while (index < end)
+    {
+      testChar = p_uriSpec.charAt(index);
+
+      if (testChar == '?' || testChar == '#')
+      {
+        break;
+      }
+
+      // check for valid escape sequence
+      if (testChar == '%')
+      {
+        if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1))
+                ||!isHex(p_uriSpec.charAt(index + 2)))
+        {
+          throw new MalformedURIException(
+            Utils.messages.createMessage(MsgKey.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE, null)); //"Path contains invalid escape sequence!");
+        }
+      }
+      else if (!isReservedCharacter(testChar)
+               &&!isUnreservedCharacter(testChar))
+      {
+        if ('\\' != testChar)
+          throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_PATH_INVALID_CHAR, new Object[]{String.valueOf(testChar)})); //"Path contains invalid character: "
+                                          //+ testChar);
+      }
+
+      index++;
+    }
+
+    m_path = p_uriSpec.substring(start, index);
+
+    // query - starts with ? and up to fragment or end
+    if (testChar == '?')
+    {
+      index++;
+
+      start = index;
+
+      while (index < end)
+      {
+        testChar = p_uriSpec.charAt(index);
+
+        if (testChar == '#')
+        {
+          break;
+        }
+
+        if (testChar == '%')
+        {
+          if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1))
+                  ||!isHex(p_uriSpec.charAt(index + 2)))
+          {
+            throw new MalformedURIException(
+              "Query string contains invalid escape sequence!");
+          }
+        }
+        else if (!isReservedCharacter(testChar)
+                 &&!isUnreservedCharacter(testChar))
+        {
+          throw new MalformedURIException(
+            "Query string contains invalid character:" + testChar);
+        }
+
+        index++;
+      }
+
+      m_queryString = p_uriSpec.substring(start, index);
+    }
+
+    // fragment - starts with #
+    if (testChar == '#')
+    {
+      index++;
+
+      start = index;
+
+      while (index < end)
+      {
+        testChar = p_uriSpec.charAt(index);
+
+        if (testChar == '%')
+        {
+          if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1))
+                  ||!isHex(p_uriSpec.charAt(index + 2)))
+          {
+            throw new MalformedURIException(
+              "Fragment contains invalid escape sequence!");
+          }
+        }
+        else if (!isReservedCharacter(testChar)
+                 &&!isUnreservedCharacter(testChar))
+        {
+          throw new MalformedURIException(
+            "Fragment contains invalid character:" + testChar);
+        }
+
+        index++;
+      }
+
+      m_fragment = p_uriSpec.substring(start, index);
+    }
+  }
+
+  /**
+   * Get the scheme for this URI.
+   *
+   * @return the scheme for this URI
+   */
+  public String getScheme()
+  {
+    return m_scheme;
+  }
+
+  /**
+   * Get the scheme-specific part for this URI (everything following the
+   * scheme and the first colon). See RFC 2396 Section 5.2 for spec.
+   *
+   * @return the scheme-specific part for this URI
+   */
+  public String getSchemeSpecificPart()
+  {
+
+    StringBuffer schemespec = new StringBuffer();
+
+    if (m_userinfo != null || m_host != null || m_port != -1)
+    {
+      schemespec.append("//");
+    }
+
+    if (m_userinfo != null)
+    {
+      schemespec.append(m_userinfo);
+      schemespec.append('@');
+    }
+
+    if (m_host != null)
+    {
+      schemespec.append(m_host);
+    }
+
+    if (m_port != -1)
+    {
+      schemespec.append(':');
+      schemespec.append(m_port);
+    }
+
+    if (m_path != null)
+    {
+      schemespec.append((m_path));
+    }
+
+    if (m_queryString != null)
+    {
+      schemespec.append('?');
+      schemespec.append(m_queryString);
+    }
+
+    if (m_fragment != null)
+    {
+      schemespec.append('#');
+      schemespec.append(m_fragment);
+    }
+
+    return schemespec.toString();
+  }
+
+  /**
+   * Get the userinfo for this URI.
+   *
+   * @return the userinfo for this URI (null if not specified).
+   */
+  public String getUserinfo()
+  {
+    return m_userinfo;
+  }
+
+  /**
+   * Get the host for this URI.
+   *
+   * @return the host for this URI (null if not specified).
+   */
+  public String getHost()
+  {
+    return m_host;
+  }
+
+  /**
+   * Get the port for this URI.
+   *
+   * @return the port for this URI (-1 if not specified).
+   */
+  public int getPort()
+  {
+    return m_port;
+  }
+
+  /**
+   * Get the path for this URI (optionally with the query string and
+   * fragment).
+   *
+   * @param p_includeQueryString if true (and query string is not null),
+   *                             then a "?" followed by the query string
+   *                             will be appended
+   * @param p_includeFragment if true (and fragment is not null),
+   *                             then a "#" followed by the fragment
+   *                             will be appended
+   *
+   * @return the path for this URI possibly including the query string
+   *         and fragment
+   */
+  public String getPath(boolean p_includeQueryString,
+                        boolean p_includeFragment)
+  {
+
+    StringBuffer pathString = new StringBuffer(m_path);
+
+    if (p_includeQueryString && m_queryString != null)
+    {
+      pathString.append('?');
+      pathString.append(m_queryString);
+    }
+
+    if (p_includeFragment && m_fragment != null)
+    {
+      pathString.append('#');
+      pathString.append(m_fragment);
+    }
+
+    return pathString.toString();
+  }
+
+  /**
+   * Get the path for this URI. Note that the value returned is the path
+   * only and does not include the query string or fragment.
+   *
+   * @return the path for this URI.
+   */
+  public String getPath()
+  {
+    return m_path;
+  }
+
+  /**
+   * Get the query string for this URI.
+   *
+   * @return the query string for this URI. Null is returned if there
+   *         was no "?" in the URI spec, empty string if there was a
+   *         "?" but no query string following it.
+   */
+  public String getQueryString()
+  {
+    return m_queryString;
+  }
+
+  /**
+   * Get the fragment for this URI.
+   *
+   * @return the fragment for this URI. Null is returned if there
+   *         was no "#" in the URI spec, empty string if there was a
+   *         "#" but no fragment following it.
+   */
+  public String getFragment()
+  {
+    return m_fragment;
+  }
+
+  /**
+   * Set the scheme for this URI. The scheme is converted to lowercase
+   * before it is set.
+   *
+   * @param p_scheme the scheme for this URI (cannot be null)
+   *
+   * @throws MalformedURIException if p_scheme is not a conformant
+   *                                  scheme name
+   */
+  public void setScheme(String p_scheme) throws MalformedURIException
+  {
+
+    if (p_scheme == null)
+    {
+      throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_SCHEME_FROM_NULL_STRING, null)); //"Cannot set scheme from null string!");
+    }
+
+    if (!isConformantSchemeName(p_scheme))
+    {
+      throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_SCHEME_NOT_CONFORMANT, null)); //"The scheme is not conformant.");
+    }
+
+    m_scheme = p_scheme.toLowerCase();
+  }
+
+  /**
+   * Set the userinfo for this URI. If a non-null value is passed in and
+   * the host value is null, then an exception is thrown.
+   *
+   * @param p_userinfo the userinfo for this URI
+   *
+   * @throws MalformedURIException if p_userinfo contains invalid
+   *                                  characters
+   */
+  public void setUserinfo(String p_userinfo) throws MalformedURIException
+  {
+
+    if (p_userinfo == null)
+    {
+      m_userinfo = null;
+    }
+    else
+    {
+      if (m_host == null)
+      {
+        throw new MalformedURIException(
+          "Userinfo cannot be set when host is null!");
+      }
+
+      // userinfo can contain alphanumerics, mark characters, escaped
+      // and ';',':','&','=','+','$',','
+      int index = 0;
+      int end = p_userinfo.length();
+      char testChar = '\0';
+
+      while (index < end)
+      {
+        testChar = p_userinfo.charAt(index);
+
+        if (testChar == '%')
+        {
+          if (index + 2 >= end ||!isHex(p_userinfo.charAt(index + 1))
+                  ||!isHex(p_userinfo.charAt(index + 2)))
+          {
+            throw new MalformedURIException(
+              "Userinfo contains invalid escape sequence!");
+          }
+        }
+        else if (!isUnreservedCharacter(testChar)
+                 && USERINFO_CHARACTERS.indexOf(testChar) == -1)
+        {
+          throw new MalformedURIException(
+            "Userinfo contains invalid character:" + testChar);
+        }
+
+        index++;
+      }
+    }
+
+    m_userinfo = p_userinfo;
+  }
+
+  /**
+   * Set the host for this URI. If null is passed in, the userinfo
+   * field is also set to null and the port is set to -1.
+   *
+   * @param p_host the host for this URI
+   *
+   * @throws MalformedURIException if p_host is not a valid IP
+   *                                  address or DNS hostname.
+   */
+  public void setHost(String p_host) throws MalformedURIException
+  {
+
+    if (p_host == null || p_host.trim().length() == 0)
+    {
+      m_host = p_host;
+      m_userinfo = null;
+      m_port = -1;
+    }
+    else if (!isWellFormedAddress(p_host))
+    {
+      throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_HOST_ADDRESS_NOT_WELLFORMED, null)); //"Host is not a well formed address!");
+    }
+
+    m_host = p_host;
+  }
+
+  /**
+   * Set the port for this URI. -1 is used to indicate that the port is
+   * not specified, otherwise valid port numbers are  between 0 and 65535.
+   * If a valid port number is passed in and the host field is null,
+   * an exception is thrown.
+   *
+   * @param p_port the port number for this URI
+   *
+   * @throws MalformedURIException if p_port is not -1 and not a
+   *                                  valid port number
+   */
+  public void setPort(int p_port) throws MalformedURIException
+  {
+
+    if (p_port >= 0 && p_port <= 65535)
+    {
+      if (m_host == null)
+      {
+        throw new MalformedURIException(
+          Utils.messages.createMessage(MsgKey.ER_PORT_WHEN_HOST_NULL, null)); //"Port cannot be set when host is null!");
+      }
+    }
+    else if (p_port != -1)
+    {
+      throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_INVALID_PORT, null)); //"Invalid port number!");
+    }
+
+    m_port = p_port;
+  }
+
+  /**
+   * Set the path for this URI. If the supplied path is null, then the
+   * query string and fragment are set to null as well. If the supplied
+   * path includes a query string and/or fragment, these fields will be
+   * parsed and set as well. Note that, for URIs following the "generic
+   * URI" syntax, the path specified should start with a slash.
+   * For URIs that do not follow the generic URI syntax, this method
+   * sets the scheme-specific part.
+   *
+   * @param p_path the path for this URI (may be null)
+   *
+   * @throws MalformedURIException if p_path contains invalid
+   *                                  characters
+   */
+  public void setPath(String p_path) throws MalformedURIException
+  {
+
+    if (p_path == null)
+    {
+      m_path = null;
+      m_queryString = null;
+      m_fragment = null;
+    }
+    else
+    {
+      initializePath(p_path);
+    }
+  }
+
+  /**
+   * Append to the end of the path of this URI. If the current path does
+   * not end in a slash and the path to be appended does not begin with
+   * a slash, a slash will be appended to the current path before the
+   * new segment is added. Also, if the current path ends in a slash
+   * and the new segment begins with a slash, the extra slash will be
+   * removed before the new segment is appended.
+   *
+   * @param p_addToPath the new segment to be added to the current path
+   *
+   * @throws MalformedURIException if p_addToPath contains syntax
+   *                                  errors
+   */
+  public void appendPath(String p_addToPath) throws MalformedURIException
+  {
+
+    if (p_addToPath == null || p_addToPath.trim().length() == 0)
+    {
+      return;
+    }
+
+    if (!isURIString(p_addToPath))
+    {
+      throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_PATH_INVALID_CHAR, new Object[]{p_addToPath})); //"Path contains invalid character!");
+    }
+
+    if (m_path == null || m_path.trim().length() == 0)
+    {
+      if (p_addToPath.startsWith("/"))
+      {
+        m_path = p_addToPath;
+      }
+      else
+      {
+        m_path = "/" + p_addToPath;
+      }
+    }
+    else if (m_path.endsWith("/"))
+    {
+      if (p_addToPath.startsWith("/"))
+      {
+        m_path = m_path.concat(p_addToPath.substring(1));
+      }
+      else
+      {
+        m_path = m_path.concat(p_addToPath);
+      }
+    }
+    else
+    {
+      if (p_addToPath.startsWith("/"))
+      {
+        m_path = m_path.concat(p_addToPath);
+      }
+      else
+      {
+        m_path = m_path.concat("/" + p_addToPath);
+      }
+    }
+  }
+
+  /**
+   * Set the query string for this URI. A non-null value is valid only
+   * if this is an URI conforming to the generic URI syntax and
+   * the path value is not null.
+   *
+   * @param p_queryString the query string for this URI
+   *
+   * @throws MalformedURIException if p_queryString is not null and this
+   *                                  URI does not conform to the generic
+   *                                  URI syntax or if the path is null
+   */
+  public void setQueryString(String p_queryString)
+          throws MalformedURIException
+  {
+
+    if (p_queryString == null)
+    {
+      m_queryString = null;
+    }
+    else if (!isGenericURI())
+    {
+      throw new MalformedURIException(
+        "Query string can only be set for a generic URI!");
+    }
+    else if (getPath() == null)
+    {
+      throw new MalformedURIException(
+        "Query string cannot be set when path is null!");
+    }
+    else if (!isURIString(p_queryString))
+    {
+      throw new MalformedURIException(
+        "Query string contains invalid character!");
+    }
+    else
+    {
+      m_queryString = p_queryString;
+    }
+  }
+
+  /**
+   * Set the fragment for this URI. A non-null value is valid only
+   * if this is a URI conforming to the generic URI syntax and
+   * the path value is not null.
+   *
+   * @param p_fragment the fragment for this URI
+   *
+   * @throws MalformedURIException if p_fragment is not null and this
+   *                                  URI does not conform to the generic
+   *                                  URI syntax or if the path is null
+   */
+  public void setFragment(String p_fragment) throws MalformedURIException
+  {
+
+    if (p_fragment == null)
+    {
+      m_fragment = null;
+    }
+    else if (!isGenericURI())
+    {
+      throw new MalformedURIException(
+        Utils.messages.createMessage(MsgKey.ER_FRAG_FOR_GENERIC_URI, null)); //"Fragment can only be set for a generic URI!");
+    }
+    else if (getPath() == null)
+    {
+      throw new MalformedURIException(
+        Utils.messages.createMessage(MsgKey.ER_FRAG_WHEN_PATH_NULL, null)); //"Fragment cannot be set when path is null!");
+    }
+    else if (!isURIString(p_fragment))
+    {
+      throw new MalformedURIException(Utils.messages.createMessage(MsgKey.ER_FRAG_INVALID_CHAR, null)); //"Fragment contains invalid character!");
+    }
+    else
+    {
+      m_fragment = p_fragment;
+    }
+  }
+
+  /**
+   * Determines if the passed-in Object is equivalent to this URI.
+   *
+   * @param p_test the Object to test for equality.
+   *
+   * @return true if p_test is a URI with all values equal to this
+   *         URI, false otherwise
+   */
+  public boolean equals(Object p_test)
+  {
+
+    if (p_test instanceof URI)
+    {
+      URI testURI = (URI) p_test;
+
+      if (((m_scheme == null && testURI.m_scheme == null) || (m_scheme != null && testURI.m_scheme != null && m_scheme.equals(
+              testURI.m_scheme))) && ((m_userinfo == null && testURI.m_userinfo == null) || (m_userinfo != null && testURI.m_userinfo != null && m_userinfo.equals(
+              testURI.m_userinfo))) && ((m_host == null && testURI.m_host == null) || (m_host != null && testURI.m_host != null && m_host.equals(
+              testURI.m_host))) && m_port == testURI.m_port && ((m_path == null && testURI.m_path == null) || (m_path != null && testURI.m_path != null && m_path.equals(
+              testURI.m_path))) && ((m_queryString == null && testURI.m_queryString == null) || (m_queryString != null && testURI.m_queryString != null && m_queryString.equals(
+              testURI.m_queryString))) && ((m_fragment == null && testURI.m_fragment == null) || (m_fragment != null && testURI.m_fragment != null && m_fragment.equals(
+              testURI.m_fragment))))
+      {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Get the URI as a string specification. See RFC 2396 Section 5.2.
+   *
+   * @return the URI string specification
+   */
+  public String toString()
+  {
+
+    StringBuffer uriSpecString = new StringBuffer();
+
+    if (m_scheme != null)
+    {
+      uriSpecString.append(m_scheme);
+      uriSpecString.append(':');
+    }
+
+    uriSpecString.append(getSchemeSpecificPart());
+
+    return uriSpecString.toString();
+  }
+
+  /**
+   * Get the indicator as to whether this URI uses the "generic URI"
+   * syntax.
+   *
+   * @return true if this URI uses the "generic URI" syntax, false
+   *         otherwise
+   */
+  public boolean isGenericURI()
+  {
+
+    // presence of the host (whether valid or empty) means 
+    // double-slashes which means generic uri
+    return (m_host != null);
+  }
+
+  /**
+   * Determine whether a scheme conforms to the rules for a scheme name.
+   * A scheme is conformant if it starts with an alphanumeric, and
+   * contains only alphanumerics, '+','-' and '.'.
+   *
+   *
+   * @param p_scheme The sheme name to check
+   * @return true if the scheme is conformant, false otherwise
+   */
+  public static boolean isConformantSchemeName(String p_scheme)
+  {
+
+    if (p_scheme == null || p_scheme.trim().length() == 0)
+    {
+      return false;
+    }
+
+    if (!isAlpha(p_scheme.charAt(0)))
+    {
+      return false;
+    }
+
+    char testChar;
+
+    for (int i = 1; i < p_scheme.length(); i++)
+    {
+      testChar = p_scheme.charAt(i);
+
+      if (!isAlphanum(testChar) && SCHEME_CHARACTERS.indexOf(testChar) == -1)
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Determine whether a string is syntactically capable of representing
+   * a valid IPv4 address or the domain name of a network host. A valid
+   * IPv4 address consists of four decimal digit groups separated by a
+   * '.'. A hostname consists of domain labels (each of which must
+   * begin and end with an alphanumeric but may contain '-') separated
+   * & by a '.'. See RFC 2396 Section 3.2.2.
+   *
+   *
+   * @param p_address The address string to check
+   * @return true if the string is a syntactically valid IPv4 address
+   *              or hostname
+   */
+  public static boolean isWellFormedAddress(String p_address)
+  {
+
+    if (p_address == null)
+    {
+      return false;
+    }
+
+    String address = p_address.trim();
+    int addrLength = address.length();
+
+    if (addrLength == 0 || addrLength > 255)
+    {
+      return false;
+    }
+
+    if (address.startsWith(".") || address.startsWith("-"))
+    {
+      return false;
+    }
+
+    // rightmost domain label starting with digit indicates IP address
+    // since top level domain label can only start with an alpha
+    // see RFC 2396 Section 3.2.2
+    int index = address.lastIndexOf('.');
+
+    if (address.endsWith("."))
+    {
+      index = address.substring(0, index).lastIndexOf('.');
+    }
+
+    if (index + 1 < addrLength && isDigit(p_address.charAt(index + 1)))
+    {
+      char testChar;
+      int numDots = 0;
+
+      // make sure that 1) we see only digits and dot separators, 2) that
+      // any dot separator is preceded and followed by a digit and 
+      // 3) that we find 3 dots
+      for (int i = 0; i < addrLength; i++)
+      {
+        testChar = address.charAt(i);
+
+        if (testChar == '.')
+        {
+          if (!isDigit(address.charAt(i - 1))
+                  || (i + 1 < addrLength &&!isDigit(address.charAt(i + 1))))
+          {
+            return false;
+          }
+
+          numDots++;
+        }
+        else if (!isDigit(testChar))
+        {
+          return false;
+        }
+      }
+
+      if (numDots != 3)
+      {
+        return false;
+      }
+    }
+    else
+    {
+
+      // domain labels can contain alphanumerics and '-"
+      // but must start and end with an alphanumeric
+      char testChar;
+
+      for (int i = 0; i < addrLength; i++)
+      {
+        testChar = address.charAt(i);
+
+        if (testChar == '.')
+        {
+          if (!isAlphanum(address.charAt(i - 1)))
+          {
+            return false;
+          }
+
+          if (i + 1 < addrLength &&!isAlphanum(address.charAt(i + 1)))
+          {
+            return false;
+          }
+        }
+        else if (!isAlphanum(testChar) && testChar != '-')
+        {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Determine whether a char is a digit.
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is betweeen '0' and '9', false otherwise
+   */
+  private static boolean isDigit(char p_char)
+  {
+    return p_char >= '0' && p_char <= '9';
+  }
+
+  /**
+   * Determine whether a character is a hexadecimal character.
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is betweeen '0' and '9', 'a' and 'f'
+   *         or 'A' and 'F', false otherwise
+   */
+  private static boolean isHex(char p_char)
+  {
+    return (isDigit(p_char) || (p_char >= 'a' && p_char <= 'f')
+            || (p_char >= 'A' && p_char <= 'F'));
+  }
+
+  /**
+   * Determine whether a char is an alphabetic character: a-z or A-Z
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is alphabetic, false otherwise
+   */
+  private static boolean isAlpha(char p_char)
+  {
+    return ((p_char >= 'a' && p_char <= 'z')
+            || (p_char >= 'A' && p_char <= 'Z'));
+  }
+
+  /**
+   * Determine whether a char is an alphanumeric: 0-9, a-z or A-Z
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is alphanumeric, false otherwise
+   */
+  private static boolean isAlphanum(char p_char)
+  {
+    return (isAlpha(p_char) || isDigit(p_char));
+  }
+
+  /**
+   * Determine whether a character is a reserved character:
+   * ';', '/', '?', ':', '@', '&', '=', '+', '$' or ','
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the string contains any reserved characters
+   */
+  private static boolean isReservedCharacter(char p_char)
+  {
+    return RESERVED_CHARACTERS.indexOf(p_char) != -1;
+  }
+
+  /**
+   * Determine whether a char is an unreserved character.
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is unreserved, false otherwise
+   */
+  private static boolean isUnreservedCharacter(char p_char)
+  {
+    return (isAlphanum(p_char) || MARK_CHARACTERS.indexOf(p_char) != -1);
+  }
+
+  /**
+   * Determine whether a given string contains only URI characters (also
+   * called "uric" in RFC 2396). uric consist of all reserved
+   * characters, unreserved characters and escaped characters.
+   *
+   *
+   * @param p_uric URI string
+   * @return true if the string is comprised of uric, false otherwise
+   */
+  private static boolean isURIString(String p_uric)
+  {
+
+    if (p_uric == null)
+    {
+      return false;
+    }
+
+    int end = p_uric.length();
+    char testChar = '\0';
+
+    for (int i = 0; i < end; i++)
+    {
+      testChar = p_uric.charAt(i);
+
+      if (testChar == '%')
+      {
+        if (i + 2 >= end ||!isHex(p_uric.charAt(i + 1))
+                ||!isHex(p_uric.charAt(i + 2)))
+        {
+          return false;
+        }
+        else
+        {
+          i += 2;
+
+          continue;
+        }
+      }
+
+      if (isReservedCharacter(testChar) || isUnreservedCharacter(testChar))
+      {
+        continue;
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/Utils.java b/src/main/java/org/apache/xml/serializer/utils/Utils.java
new file mode 100644
index 0000000..8990143
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/Utils.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Utils.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+/**
+ * This class contains utilities used by the serializer.
+ * 
+ * This class is not a public API, it is only public because it is
+ * used by the serializer.
+ * 
+ * @xsl.usage internal
+ */
+public final class Utils
+{
+    /**
+     * A singleton Messages object is used to load the 
+     * given resource bundle just once, it is
+     * used by multiple transformations as long as the JVM stays up.
+     */
+    public static final Messages messages= 
+        new Messages(SerializerMessages.class.getName());
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/WrappedRuntimeException.java b/src/main/java/org/apache/xml/serializer/utils/WrappedRuntimeException.java
new file mode 100644
index 0000000..e26eb3f
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/WrappedRuntimeException.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WrappedRuntimeException.java 468654 2006-10-28 07:09:23Z minchau $
+ */
+package org.apache.xml.serializer.utils;
+
+/**
+ * This class is for throwing important checked exceptions
+ * over non-checked methods.  It should be used with care,
+ * and in limited circumstances.
+ * 
+ * This class is a copy of the one in org.apache.xml.utils. 
+ * It exists to cut the serializers dependancy on that package.
+ * 
+ * This class is not a public API, it is only public because it is
+ * used by org.apache.xml.serializer.
+ * @xsl.usage internal
+ */
+public final class WrappedRuntimeException extends RuntimeException
+{
+    static final long serialVersionUID = 7140414456714658073L;
+
+  /** Primary checked exception.
+   *  @serial          */
+  private Exception m_exception;
+
+  /**
+   * Construct a WrappedRuntimeException from a
+   * checked exception.
+   *
+   * @param e Primary checked exception
+   */
+  public WrappedRuntimeException(Exception e)
+  {
+
+    super(e.getMessage());
+
+    m_exception = e;
+  }
+
+  /**
+   * Constructor WrappedRuntimeException
+   *
+   *
+   * @param msg Exception information.
+   * @param e Primary checked exception
+   */
+  public WrappedRuntimeException(String msg, Exception e)
+  {
+
+    super(msg);
+
+    m_exception = e;
+  }
+  
+  /**
+   * Get the checked exception that this runtime exception wraps.
+   *
+   * @return The primary checked exception
+   */
+  public Exception getException()
+  {
+    return m_exception;
+  }
+}
diff --git a/src/main/java/org/apache/xml/serializer/utils/XML11Char.java b/src/main/java/org/apache/xml/serializer/utils/XML11Char.java
new file mode 100644
index 0000000..2cbd522
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/XML11Char.java
@@ -0,0 +1,414 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.apache.xml.serializer.utils;

+

+import java.util.Arrays;

+

+/**

+ * THIS IS A COPY OF THE XERCES-2J CLASS org.apache.xerces.utls.XMLChar

+ *  

+ * This class defines the basic properties of characters in XML 1.1. The data

+ * in this class can be used to verify that a character is a valid

+ * XML 1.1 character or if the character is a space, name start, or name

+ * character.

+ * <p>

+ * A series of convenience methods are supplied to ease the burden

+ * of the developer.  Using the character as an index into the <code>XML11CHARS</code>

+ * array and applying the appropriate mask flag (e.g.

+ * <code>MASK_VALID</code>), yields the same results as calling the

+ * convenience methods. There is one exception: check the comments

+ * for the <code>isValid</code> method for details.

+ *

+ * @author Glenn Marcy, IBM

+ * @author Andy Clark, IBM

+ * @author Arnaud  Le Hors, IBM

+ * @author Neil Graham, IBM

+ * @author Michael Glavassevich, IBM

+ *

+ * @version $Id: $

+ */

+public class XML11Char {

+

+    //

+    // Constants

+    //

+

+    /** Character flags for XML 1.1. */

+    private static final byte XML11CHARS [] = new byte [1 << 16];

+

+    /** XML 1.1 Valid character mask. */

+    public static final int MASK_XML11_VALID = 0x01;

+

+    /** XML 1.1 Space character mask. */

+    public static final int MASK_XML11_SPACE = 0x02;

+

+    /** XML 1.1 Name start character mask. */

+    public static final int MASK_XML11_NAME_START = 0x04;

+

+    /** XML 1.1 Name character mask. */

+    public static final int MASK_XML11_NAME = 0x08;

+

+    /** XML 1.1 control character mask */

+    public static final int MASK_XML11_CONTROL = 0x10;

+

+    /** XML 1.1 content for external entities (valid - "special" chars - control chars) */

+    public static final int MASK_XML11_CONTENT = 0x20;

+

+    /** XML namespaces 1.1 NCNameStart */

+    public static final int MASK_XML11_NCNAME_START = 0x40;

+

+    /** XML namespaces 1.1 NCName */

+    public static final int MASK_XML11_NCNAME = 0x80;

+    

+    /** XML 1.1 content for internal entities (valid - "special" chars) */

+    public static final int MASK_XML11_CONTENT_INTERNAL = MASK_XML11_CONTROL | MASK_XML11_CONTENT; 

+

+    //

+    // Static initialization

+    //

+

+    static {

+    	

+        // Initializing the Character Flag Array

+        // Code generated by: XML11CharGenerator.

+        

+        Arrays.fill(XML11CHARS, 1, 9, (byte) 17 ); // Fill 8 of value (byte) 17

+        XML11CHARS[9] = 35;

+        XML11CHARS[10] = 3;

+        Arrays.fill(XML11CHARS, 11, 13, (byte) 17 ); // Fill 2 of value (byte) 17

+        XML11CHARS[13] = 3;

+        Arrays.fill(XML11CHARS, 14, 32, (byte) 17 ); // Fill 18 of value (byte) 17

+        XML11CHARS[32] = 35;

+        Arrays.fill(XML11CHARS, 33, 38, (byte) 33 ); // Fill 5 of value (byte) 33

+        XML11CHARS[38] = 1;

+        Arrays.fill(XML11CHARS, 39, 45, (byte) 33 ); // Fill 6 of value (byte) 33

+        Arrays.fill(XML11CHARS, 45, 47, (byte) -87 ); // Fill 2 of value (byte) -87

+        XML11CHARS[47] = 33;

+        Arrays.fill(XML11CHARS, 48, 58, (byte) -87 ); // Fill 10 of value (byte) -87

+        XML11CHARS[58] = 45;

+        XML11CHARS[59] = 33;

+        XML11CHARS[60] = 1;

+        Arrays.fill(XML11CHARS, 61, 65, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(XML11CHARS, 65, 91, (byte) -19 ); // Fill 26 of value (byte) -19

+        Arrays.fill(XML11CHARS, 91, 93, (byte) 33 ); // Fill 2 of value (byte) 33

+        XML11CHARS[93] = 1;

+        XML11CHARS[94] = 33;

+        XML11CHARS[95] = -19;

+        XML11CHARS[96] = 33;

+        Arrays.fill(XML11CHARS, 97, 123, (byte) -19 ); // Fill 26 of value (byte) -19

+        Arrays.fill(XML11CHARS, 123, 127, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(XML11CHARS, 127, 133, (byte) 17 ); // Fill 6 of value (byte) 17

+        XML11CHARS[133] = 35;

+        Arrays.fill(XML11CHARS, 134, 160, (byte) 17 ); // Fill 26 of value (byte) 17

+        Arrays.fill(XML11CHARS, 160, 183, (byte) 33 ); // Fill 23 of value (byte) 33

+        XML11CHARS[183] = -87;

+        Arrays.fill(XML11CHARS, 184, 192, (byte) 33 ); // Fill 8 of value (byte) 33

+        Arrays.fill(XML11CHARS, 192, 215, (byte) -19 ); // Fill 23 of value (byte) -19

+        XML11CHARS[215] = 33;

+        Arrays.fill(XML11CHARS, 216, 247, (byte) -19 ); // Fill 31 of value (byte) -19

+        XML11CHARS[247] = 33;

+        Arrays.fill(XML11CHARS, 248, 768, (byte) -19 ); // Fill 520 of value (byte) -19

+        Arrays.fill(XML11CHARS, 768, 880, (byte) -87 ); // Fill 112 of value (byte) -87

+        Arrays.fill(XML11CHARS, 880, 894, (byte) -19 ); // Fill 14 of value (byte) -19

+        XML11CHARS[894] = 33;

+        Arrays.fill(XML11CHARS, 895, 8192, (byte) -19 ); // Fill 7297 of value (byte) -19

+        Arrays.fill(XML11CHARS, 8192, 8204, (byte) 33 ); // Fill 12 of value (byte) 33

+        Arrays.fill(XML11CHARS, 8204, 8206, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(XML11CHARS, 8206, 8232, (byte) 33 ); // Fill 26 of value (byte) 33

+        XML11CHARS[8232] = 35;

+        Arrays.fill(XML11CHARS, 8233, 8255, (byte) 33 ); // Fill 22 of value (byte) 33

+        Arrays.fill(XML11CHARS, 8255, 8257, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(XML11CHARS, 8257, 8304, (byte) 33 ); // Fill 47 of value (byte) 33

+        Arrays.fill(XML11CHARS, 8304, 8592, (byte) -19 ); // Fill 288 of value (byte) -19

+        Arrays.fill(XML11CHARS, 8592, 11264, (byte) 33 ); // Fill 2672 of value (byte) 33

+        Arrays.fill(XML11CHARS, 11264, 12272, (byte) -19 ); // Fill 1008 of value (byte) -19

+        Arrays.fill(XML11CHARS, 12272, 12289, (byte) 33 ); // Fill 17 of value (byte) 33

+        Arrays.fill(XML11CHARS, 12289, 55296, (byte) -19 ); // Fill 43007 of value (byte) -19

+        Arrays.fill(XML11CHARS, 57344, 63744, (byte) 33 ); // Fill 6400 of value (byte) 33

+        Arrays.fill(XML11CHARS, 63744, 64976, (byte) -19 ); // Fill 1232 of value (byte) -19

+        Arrays.fill(XML11CHARS, 64976, 65008, (byte) 33 ); // Fill 32 of value (byte) 33

+        Arrays.fill(XML11CHARS, 65008, 65534, (byte) -19 ); // Fill 526 of value (byte) -19

+

+    } // <clinit>()

+

+    //

+    // Public static methods

+    //

+

+    /**

+     * Returns true if the specified character is a space character

+     * as amdended in the XML 1.1 specification.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11Space(int c) {

+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_SPACE) != 0);

+    } // isXML11Space(int):boolean

+

+    /**

+     * Returns true if the specified character is valid. This method

+     * also checks the surrogate character range from 0x10000 to 0x10FFFF.

+     * <p>

+     * If the program chooses to apply the mask directly to the

+     * <code>XML11CHARS</code> array, then they are responsible for checking

+     * the surrogate character range.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11Valid(int c) {

+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_VALID) != 0) 

+                || (0x10000 <= c && c <= 0x10FFFF);

+    } // isXML11Valid(int):boolean

+

+    /**

+     * Returns true if the specified character is invalid.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11Invalid(int c) {

+        return !isXML11Valid(c);

+    } // isXML11Invalid(int):boolean

+

+    /**

+     * Returns true if the specified character is valid and permitted outside

+     * of a character reference.  

+     * That is, this method will return false for the same set as

+     * isXML11Valid, except it also reports false for "control characters".

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11ValidLiteral(int c) {

+        return ((c < 0x10000 && ((XML11CHARS[c] & MASK_XML11_VALID) != 0 && (XML11CHARS[c] & MASK_XML11_CONTROL) == 0))

+            || (0x10000 <= c && c <= 0x10FFFF)); 

+    } // isXML11ValidLiteral(int):boolean

+

+    /**

+     * Returns true if the specified character can be considered 

+     * content in an external parsed entity.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11Content(int c) {

+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_CONTENT) != 0) ||

+               (0x10000 <= c && c <= 0x10FFFF);

+    } // isXML11Content(int):boolean

+    

+    /**

+     * Returns true if the specified character can be considered 

+     * content in an internal parsed entity.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11InternalEntityContent(int c) {

+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_CONTENT_INTERNAL) != 0) ||

+               (0x10000 <= c && c <= 0x10FFFF);

+    } // isXML11InternalEntityContent(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid name start

+     * character as defined by production [4] in the XML 1.1

+     * specification.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11NameStart(int c) {

+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_NAME_START) != 0)

+            || (0x10000 <= c && c < 0xF0000);

+    } // isXML11NameStart(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid name

+     * character as defined by production [4a] in the XML 1.1

+     * specification.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11Name(int c) {

+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_NAME) != 0) 

+            || (c >= 0x10000 && c < 0xF0000);

+    } // isXML11Name(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid NCName start

+     * character as defined by production [4] in Namespaces in XML

+     * 1.1 recommendation.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11NCNameStart(int c) {

+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_NCNAME_START) != 0)

+            || (0x10000 <= c && c < 0xF0000);

+    } // isXML11NCNameStart(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid NCName

+     * character as defined by production [5] in Namespaces in XML

+     * 1.1 recommendation.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11NCName(int c) {

+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_NCNAME) != 0)

+            || (0x10000 <= c && c < 0xF0000);

+    } // isXML11NCName(int):boolean

+    

+    /**

+     * Returns whether the given character is a valid 

+     * high surrogate for a name character. This includes

+     * all high surrogates for characters [0x10000-0xEFFFF].

+     * In other words everything excluding planes 15 and 16.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isXML11NameHighSurrogate(int c) {

+        return (0xD800 <= c && c <= 0xDB7F);

+    }

+

+    /*

+     * [5] Name ::= NameStartChar NameChar*

+     */

+    /**

+     * Check to see if a string is a valid Name according to [5]

+     * in the XML 1.1 Recommendation

+     *

+     * @param name string to check

+     * @return true if name is a valid Name

+     */

+    public static boolean isXML11ValidName(String name) {

+        int length = name.length();

+        if (length == 0)

+            return false;

+        int i = 1;

+        char ch = name.charAt(0);

+        if( !isXML11NameStart(ch) ) {

+            if ( length > 1 && isXML11NameHighSurrogate(ch) ) {

+                char ch2 = name.charAt(1);

+                if ( !XMLChar.isLowSurrogate(ch2) || 

+                     !isXML11NameStart(XMLChar.supplemental(ch, ch2)) ) {

+                    return false;

+                }

+                i = 2;

+            }

+            else {

+                return false;

+            }

+        }

+        while (i < length) {

+            ch = name.charAt(i);

+            if ( !isXML11Name(ch) ) {

+                if ( ++i < length && isXML11NameHighSurrogate(ch) ) {

+                    char ch2 = name.charAt(i);

+                    if ( !XMLChar.isLowSurrogate(ch2) || 

+                         !isXML11Name(XMLChar.supplemental(ch, ch2)) ) {

+                        return false;

+                    }

+                }

+                else {

+                    return false;

+                }

+            }

+            ++i;

+        }

+        return true;

+    } // isXML11ValidName(String):boolean

+    

+

+    /*

+     * from the namespace 1.1 rec

+     * [4] NCName ::= NCNameStartChar NCNameChar*

+     */

+    /**

+     * Check to see if a string is a valid NCName according to [4]

+     * from the XML Namespaces 1.1 Recommendation

+     *

+     * @param ncName string to check

+     * @return true if name is a valid NCName

+     */

+    public static boolean isXML11ValidNCName(String ncName) {

+        int length = ncName.length();

+        if (length == 0)

+            return false;

+        int i = 1;

+        char ch = ncName.charAt(0);

+        if( !isXML11NCNameStart(ch) ) {

+            if ( length > 1 && isXML11NameHighSurrogate(ch) ) {

+                char ch2 = ncName.charAt(1);

+                if ( !XMLChar.isLowSurrogate(ch2) || 

+                     !isXML11NCNameStart(XMLChar.supplemental(ch, ch2)) ) {

+                    return false;

+                }

+                i = 2;

+            }

+            else {

+                return false;

+            }

+        }

+        while (i < length) {

+            ch = ncName.charAt(i);

+            if ( !isXML11NCName(ch) ) {

+                if ( ++i < length && isXML11NameHighSurrogate(ch) ) {

+                    char ch2 = ncName.charAt(i);

+                    if ( !XMLChar.isLowSurrogate(ch2) || 

+                         !isXML11NCName(XMLChar.supplemental(ch, ch2)) ) {

+                        return false;

+                    }

+                }

+                else {

+                    return false;

+                }

+            }

+            ++i;

+        }

+        return true;

+    } // isXML11ValidNCName(String):boolean

+

+    /*

+     * [7] Nmtoken ::= (NameChar)+

+     */

+    /**

+     * Check to see if a string is a valid Nmtoken according to [7]

+     * in the XML 1.1 Recommendation

+     *

+     * @param nmtoken string to check

+     * @return true if nmtoken is a valid Nmtoken 

+     */

+    public static boolean isXML11ValidNmtoken(String nmtoken) {

+        int length = nmtoken.length();

+        if (length == 0)

+            return false;

+        for (int i = 0; i < length; ++i ) {

+            char ch = nmtoken.charAt(i);

+            if( !isXML11Name(ch) ) {

+                if ( ++i < length && isXML11NameHighSurrogate(ch) ) {

+                    char ch2 = nmtoken.charAt(i);

+                    if ( !XMLChar.isLowSurrogate(ch2) || 

+                         !isXML11Name(XMLChar.supplemental(ch, ch2)) ) {

+                        return false;

+                    }

+                }

+                else {

+                    return false;

+                }

+            }

+        }

+        return true;

+    } // isXML11ValidName(String):boolean

+

+} // class XML11Char

+

diff --git a/src/main/java/org/apache/xml/serializer/utils/XMLChar.java b/src/main/java/org/apache/xml/serializer/utils/XMLChar.java
new file mode 100644
index 0000000..313dd10
--- /dev/null
+++ b/src/main/java/org/apache/xml/serializer/utils/XMLChar.java
@@ -0,0 +1,1026 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the  "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.apache.xml.serializer.utils;

+

+import java.util.Arrays;

+

+/**

+ * THIS IS A COPY OF THE XERCES-2J CLASS org.apache.xerces.utls.XMLChar

+ * 

+ * This class defines the basic XML character properties. The data

+ * in this class can be used to verify that a character is a valid

+ * XML character or if the character is a space, name start, or name

+ * character.

+ * <p>

+ * A series of convenience methods are supplied to ease the burden

+ * of the developer. Because inlining the checks can improve per

+ * character performance, the tables of character properties are

+ * public. Using the character as an index into the <code>CHARS</code>

+ * array and applying the appropriate mask flag (e.g.

+ * <code>MASK_VALID</code>), yields the same results as calling the

+ * convenience methods. There is one exception: check the comments

+ * for the <code>isValid</code> method for details.

+ *

+ * @author Glenn Marcy, IBM

+ * @author Andy Clark, IBM

+ * @author Eric Ye, IBM

+ * @author Arnaud  Le Hors, IBM

+ * @author Michael Glavassevich, IBM

+ * @author Rahul Srivastava, Sun Microsystems Inc.

+ *

+ * @version $Id: $

+ */

+public class XMLChar {

+

+    //

+    // Constants

+    //

+

+    /** Character flags. */

+    private static final byte[] CHARS = new byte[1 << 16];

+

+    /** Valid character mask. */

+    public static final int MASK_VALID = 0x01;

+

+    /** Space character mask. */

+    public static final int MASK_SPACE = 0x02;

+

+    /** Name start character mask. */

+    public static final int MASK_NAME_START = 0x04;

+

+    /** Name character mask. */

+    public static final int MASK_NAME = 0x08;

+

+    /** Pubid character mask. */

+    public static final int MASK_PUBID = 0x10;

+    

+    /** 

+     * Content character mask. Special characters are those that can

+     * be considered the start of markup, such as '&lt;' and '&amp;'. 

+     * The various newline characters are considered special as well.

+     * All other valid XML characters can be considered content.

+     * <p>

+     * This is an optimization for the inner loop of character scanning.

+     */

+    public static final int MASK_CONTENT = 0x20;

+

+    /** NCName start character mask. */

+    public static final int MASK_NCNAME_START = 0x40;

+

+    /** NCName character mask. */

+    public static final int MASK_NCNAME = 0x80;

+

+    //

+    // Static initialization

+    //

+

+    static {

+        

+        // Initializing the Character Flag Array

+        // Code generated by: XMLCharGenerator.

+        

+        CHARS[9] = 35;

+        CHARS[10] = 19;

+        CHARS[13] = 19;

+        CHARS[32] = 51;

+        CHARS[33] = 49;

+        CHARS[34] = 33;

+        Arrays.fill(CHARS, 35, 38, (byte) 49 ); // Fill 3 of value (byte) 49

+        CHARS[38] = 1;

+        Arrays.fill(CHARS, 39, 45, (byte) 49 ); // Fill 6 of value (byte) 49

+        Arrays.fill(CHARS, 45, 47, (byte) -71 ); // Fill 2 of value (byte) -71

+        CHARS[47] = 49;

+        Arrays.fill(CHARS, 48, 58, (byte) -71 ); // Fill 10 of value (byte) -71

+        CHARS[58] = 61;

+        CHARS[59] = 49;

+        CHARS[60] = 1;

+        CHARS[61] = 49;

+        CHARS[62] = 33;

+        Arrays.fill(CHARS, 63, 65, (byte) 49 ); // Fill 2 of value (byte) 49

+        Arrays.fill(CHARS, 65, 91, (byte) -3 ); // Fill 26 of value (byte) -3

+        Arrays.fill(CHARS, 91, 93, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[93] = 1;

+        CHARS[94] = 33;

+        CHARS[95] = -3;

+        CHARS[96] = 33;

+        Arrays.fill(CHARS, 97, 123, (byte) -3 ); // Fill 26 of value (byte) -3

+        Arrays.fill(CHARS, 123, 183, (byte) 33 ); // Fill 60 of value (byte) 33

+        CHARS[183] = -87;

+        Arrays.fill(CHARS, 184, 192, (byte) 33 ); // Fill 8 of value (byte) 33

+        Arrays.fill(CHARS, 192, 215, (byte) -19 ); // Fill 23 of value (byte) -19

+        CHARS[215] = 33;

+        Arrays.fill(CHARS, 216, 247, (byte) -19 ); // Fill 31 of value (byte) -19

+        CHARS[247] = 33;

+        Arrays.fill(CHARS, 248, 306, (byte) -19 ); // Fill 58 of value (byte) -19

+        Arrays.fill(CHARS, 306, 308, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 308, 319, (byte) -19 ); // Fill 11 of value (byte) -19

+        Arrays.fill(CHARS, 319, 321, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 321, 329, (byte) -19 ); // Fill 8 of value (byte) -19

+        CHARS[329] = 33;

+        Arrays.fill(CHARS, 330, 383, (byte) -19 ); // Fill 53 of value (byte) -19

+        CHARS[383] = 33;

+        Arrays.fill(CHARS, 384, 452, (byte) -19 ); // Fill 68 of value (byte) -19

+        Arrays.fill(CHARS, 452, 461, (byte) 33 ); // Fill 9 of value (byte) 33

+        Arrays.fill(CHARS, 461, 497, (byte) -19 ); // Fill 36 of value (byte) -19

+        Arrays.fill(CHARS, 497, 500, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 500, 502, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 502, 506, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 506, 536, (byte) -19 ); // Fill 30 of value (byte) -19

+        Arrays.fill(CHARS, 536, 592, (byte) 33 ); // Fill 56 of value (byte) 33

+        Arrays.fill(CHARS, 592, 681, (byte) -19 ); // Fill 89 of value (byte) -19

+        Arrays.fill(CHARS, 681, 699, (byte) 33 ); // Fill 18 of value (byte) 33

+        Arrays.fill(CHARS, 699, 706, (byte) -19 ); // Fill 7 of value (byte) -19

+        Arrays.fill(CHARS, 706, 720, (byte) 33 ); // Fill 14 of value (byte) 33

+        Arrays.fill(CHARS, 720, 722, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 722, 768, (byte) 33 ); // Fill 46 of value (byte) 33

+        Arrays.fill(CHARS, 768, 838, (byte) -87 ); // Fill 70 of value (byte) -87

+        Arrays.fill(CHARS, 838, 864, (byte) 33 ); // Fill 26 of value (byte) 33

+        Arrays.fill(CHARS, 864, 866, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 866, 902, (byte) 33 ); // Fill 36 of value (byte) 33

+        CHARS[902] = -19;

+        CHARS[903] = -87;

+        Arrays.fill(CHARS, 904, 907, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[907] = 33;

+        CHARS[908] = -19;

+        CHARS[909] = 33;

+        Arrays.fill(CHARS, 910, 930, (byte) -19 ); // Fill 20 of value (byte) -19

+        CHARS[930] = 33;

+        Arrays.fill(CHARS, 931, 975, (byte) -19 ); // Fill 44 of value (byte) -19

+        CHARS[975] = 33;

+        Arrays.fill(CHARS, 976, 983, (byte) -19 ); // Fill 7 of value (byte) -19

+        Arrays.fill(CHARS, 983, 986, (byte) 33 ); // Fill 3 of value (byte) 33

+        CHARS[986] = -19;

+        CHARS[987] = 33;

+        CHARS[988] = -19;

+        CHARS[989] = 33;

+        CHARS[990] = -19;

+        CHARS[991] = 33;

+        CHARS[992] = -19;

+        CHARS[993] = 33;

+        Arrays.fill(CHARS, 994, 1012, (byte) -19 ); // Fill 18 of value (byte) -19

+        Arrays.fill(CHARS, 1012, 1025, (byte) 33 ); // Fill 13 of value (byte) 33

+        Arrays.fill(CHARS, 1025, 1037, (byte) -19 ); // Fill 12 of value (byte) -19

+        CHARS[1037] = 33;

+        Arrays.fill(CHARS, 1038, 1104, (byte) -19 ); // Fill 66 of value (byte) -19

+        CHARS[1104] = 33;

+        Arrays.fill(CHARS, 1105, 1117, (byte) -19 ); // Fill 12 of value (byte) -19

+        CHARS[1117] = 33;

+        Arrays.fill(CHARS, 1118, 1154, (byte) -19 ); // Fill 36 of value (byte) -19

+        CHARS[1154] = 33;

+        Arrays.fill(CHARS, 1155, 1159, (byte) -87 ); // Fill 4 of value (byte) -87

+        Arrays.fill(CHARS, 1159, 1168, (byte) 33 ); // Fill 9 of value (byte) 33

+        Arrays.fill(CHARS, 1168, 1221, (byte) -19 ); // Fill 53 of value (byte) -19

+        Arrays.fill(CHARS, 1221, 1223, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 1223, 1225, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 1225, 1227, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 1227, 1229, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 1229, 1232, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 1232, 1260, (byte) -19 ); // Fill 28 of value (byte) -19

+        Arrays.fill(CHARS, 1260, 1262, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 1262, 1270, (byte) -19 ); // Fill 8 of value (byte) -19

+        Arrays.fill(CHARS, 1270, 1272, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 1272, 1274, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 1274, 1329, (byte) 33 ); // Fill 55 of value (byte) 33

+        Arrays.fill(CHARS, 1329, 1367, (byte) -19 ); // Fill 38 of value (byte) -19

+        Arrays.fill(CHARS, 1367, 1369, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[1369] = -19;

+        Arrays.fill(CHARS, 1370, 1377, (byte) 33 ); // Fill 7 of value (byte) 33

+        Arrays.fill(CHARS, 1377, 1415, (byte) -19 ); // Fill 38 of value (byte) -19

+        Arrays.fill(CHARS, 1415, 1425, (byte) 33 ); // Fill 10 of value (byte) 33

+        Arrays.fill(CHARS, 1425, 1442, (byte) -87 ); // Fill 17 of value (byte) -87

+        CHARS[1442] = 33;

+        Arrays.fill(CHARS, 1443, 1466, (byte) -87 ); // Fill 23 of value (byte) -87

+        CHARS[1466] = 33;

+        Arrays.fill(CHARS, 1467, 1470, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[1470] = 33;

+        CHARS[1471] = -87;

+        CHARS[1472] = 33;

+        Arrays.fill(CHARS, 1473, 1475, (byte) -87 ); // Fill 2 of value (byte) -87

+        CHARS[1475] = 33;

+        CHARS[1476] = -87;

+        Arrays.fill(CHARS, 1477, 1488, (byte) 33 ); // Fill 11 of value (byte) 33

+        Arrays.fill(CHARS, 1488, 1515, (byte) -19 ); // Fill 27 of value (byte) -19

+        Arrays.fill(CHARS, 1515, 1520, (byte) 33 ); // Fill 5 of value (byte) 33

+        Arrays.fill(CHARS, 1520, 1523, (byte) -19 ); // Fill 3 of value (byte) -19

+        Arrays.fill(CHARS, 1523, 1569, (byte) 33 ); // Fill 46 of value (byte) 33

+        Arrays.fill(CHARS, 1569, 1595, (byte) -19 ); // Fill 26 of value (byte) -19

+        Arrays.fill(CHARS, 1595, 1600, (byte) 33 ); // Fill 5 of value (byte) 33

+        CHARS[1600] = -87;

+        Arrays.fill(CHARS, 1601, 1611, (byte) -19 ); // Fill 10 of value (byte) -19

+        Arrays.fill(CHARS, 1611, 1619, (byte) -87 ); // Fill 8 of value (byte) -87

+        Arrays.fill(CHARS, 1619, 1632, (byte) 33 ); // Fill 13 of value (byte) 33

+        Arrays.fill(CHARS, 1632, 1642, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 1642, 1648, (byte) 33 ); // Fill 6 of value (byte) 33

+        CHARS[1648] = -87;

+        Arrays.fill(CHARS, 1649, 1720, (byte) -19 ); // Fill 71 of value (byte) -19

+        Arrays.fill(CHARS, 1720, 1722, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 1722, 1727, (byte) -19 ); // Fill 5 of value (byte) -19

+        CHARS[1727] = 33;

+        Arrays.fill(CHARS, 1728, 1743, (byte) -19 ); // Fill 15 of value (byte) -19

+        CHARS[1743] = 33;

+        Arrays.fill(CHARS, 1744, 1748, (byte) -19 ); // Fill 4 of value (byte) -19

+        CHARS[1748] = 33;

+        CHARS[1749] = -19;

+        Arrays.fill(CHARS, 1750, 1765, (byte) -87 ); // Fill 15 of value (byte) -87

+        Arrays.fill(CHARS, 1765, 1767, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 1767, 1769, (byte) -87 ); // Fill 2 of value (byte) -87

+        CHARS[1769] = 33;

+        Arrays.fill(CHARS, 1770, 1774, (byte) -87 ); // Fill 4 of value (byte) -87

+        Arrays.fill(CHARS, 1774, 1776, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 1776, 1786, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 1786, 2305, (byte) 33 ); // Fill 519 of value (byte) 33

+        Arrays.fill(CHARS, 2305, 2308, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[2308] = 33;

+        Arrays.fill(CHARS, 2309, 2362, (byte) -19 ); // Fill 53 of value (byte) -19

+        Arrays.fill(CHARS, 2362, 2364, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[2364] = -87;

+        CHARS[2365] = -19;

+        Arrays.fill(CHARS, 2366, 2382, (byte) -87 ); // Fill 16 of value (byte) -87

+        Arrays.fill(CHARS, 2382, 2385, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2385, 2389, (byte) -87 ); // Fill 4 of value (byte) -87

+        Arrays.fill(CHARS, 2389, 2392, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2392, 2402, (byte) -19 ); // Fill 10 of value (byte) -19

+        Arrays.fill(CHARS, 2402, 2404, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 2404, 2406, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2406, 2416, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 2416, 2433, (byte) 33 ); // Fill 17 of value (byte) 33

+        Arrays.fill(CHARS, 2433, 2436, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[2436] = 33;

+        Arrays.fill(CHARS, 2437, 2445, (byte) -19 ); // Fill 8 of value (byte) -19

+        Arrays.fill(CHARS, 2445, 2447, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2447, 2449, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 2449, 2451, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2451, 2473, (byte) -19 ); // Fill 22 of value (byte) -19

+        CHARS[2473] = 33;

+        Arrays.fill(CHARS, 2474, 2481, (byte) -19 ); // Fill 7 of value (byte) -19

+        CHARS[2481] = 33;

+        CHARS[2482] = -19;

+        Arrays.fill(CHARS, 2483, 2486, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2486, 2490, (byte) -19 ); // Fill 4 of value (byte) -19

+        Arrays.fill(CHARS, 2490, 2492, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[2492] = -87;

+        CHARS[2493] = 33;

+        Arrays.fill(CHARS, 2494, 2501, (byte) -87 ); // Fill 7 of value (byte) -87

+        Arrays.fill(CHARS, 2501, 2503, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2503, 2505, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 2505, 2507, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2507, 2510, (byte) -87 ); // Fill 3 of value (byte) -87

+        Arrays.fill(CHARS, 2510, 2519, (byte) 33 ); // Fill 9 of value (byte) 33

+        CHARS[2519] = -87;

+        Arrays.fill(CHARS, 2520, 2524, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 2524, 2526, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[2526] = 33;

+        Arrays.fill(CHARS, 2527, 2530, (byte) -19 ); // Fill 3 of value (byte) -19

+        Arrays.fill(CHARS, 2530, 2532, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 2532, 2534, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2534, 2544, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 2544, 2546, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 2546, 2562, (byte) 33 ); // Fill 16 of value (byte) 33

+        CHARS[2562] = -87;

+        Arrays.fill(CHARS, 2563, 2565, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2565, 2571, (byte) -19 ); // Fill 6 of value (byte) -19

+        Arrays.fill(CHARS, 2571, 2575, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 2575, 2577, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 2577, 2579, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2579, 2601, (byte) -19 ); // Fill 22 of value (byte) -19

+        CHARS[2601] = 33;

+        Arrays.fill(CHARS, 2602, 2609, (byte) -19 ); // Fill 7 of value (byte) -19

+        CHARS[2609] = 33;

+        Arrays.fill(CHARS, 2610, 2612, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[2612] = 33;

+        Arrays.fill(CHARS, 2613, 2615, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[2615] = 33;

+        Arrays.fill(CHARS, 2616, 2618, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 2618, 2620, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[2620] = -87;

+        CHARS[2621] = 33;

+        Arrays.fill(CHARS, 2622, 2627, (byte) -87 ); // Fill 5 of value (byte) -87

+        Arrays.fill(CHARS, 2627, 2631, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 2631, 2633, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 2633, 2635, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2635, 2638, (byte) -87 ); // Fill 3 of value (byte) -87

+        Arrays.fill(CHARS, 2638, 2649, (byte) 33 ); // Fill 11 of value (byte) 33

+        Arrays.fill(CHARS, 2649, 2653, (byte) -19 ); // Fill 4 of value (byte) -19

+        CHARS[2653] = 33;

+        CHARS[2654] = -19;

+        Arrays.fill(CHARS, 2655, 2662, (byte) 33 ); // Fill 7 of value (byte) 33

+        Arrays.fill(CHARS, 2662, 2674, (byte) -87 ); // Fill 12 of value (byte) -87

+        Arrays.fill(CHARS, 2674, 2677, (byte) -19 ); // Fill 3 of value (byte) -19

+        Arrays.fill(CHARS, 2677, 2689, (byte) 33 ); // Fill 12 of value (byte) 33

+        Arrays.fill(CHARS, 2689, 2692, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[2692] = 33;

+        Arrays.fill(CHARS, 2693, 2700, (byte) -19 ); // Fill 7 of value (byte) -19

+        CHARS[2700] = 33;

+        CHARS[2701] = -19;

+        CHARS[2702] = 33;

+        Arrays.fill(CHARS, 2703, 2706, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[2706] = 33;

+        Arrays.fill(CHARS, 2707, 2729, (byte) -19 ); // Fill 22 of value (byte) -19

+        CHARS[2729] = 33;

+        Arrays.fill(CHARS, 2730, 2737, (byte) -19 ); // Fill 7 of value (byte) -19

+        CHARS[2737] = 33;

+        Arrays.fill(CHARS, 2738, 2740, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[2740] = 33;

+        Arrays.fill(CHARS, 2741, 2746, (byte) -19 ); // Fill 5 of value (byte) -19

+        Arrays.fill(CHARS, 2746, 2748, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[2748] = -87;

+        CHARS[2749] = -19;

+        Arrays.fill(CHARS, 2750, 2758, (byte) -87 ); // Fill 8 of value (byte) -87

+        CHARS[2758] = 33;

+        Arrays.fill(CHARS, 2759, 2762, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[2762] = 33;

+        Arrays.fill(CHARS, 2763, 2766, (byte) -87 ); // Fill 3 of value (byte) -87

+        Arrays.fill(CHARS, 2766, 2784, (byte) 33 ); // Fill 18 of value (byte) 33

+        CHARS[2784] = -19;

+        Arrays.fill(CHARS, 2785, 2790, (byte) 33 ); // Fill 5 of value (byte) 33

+        Arrays.fill(CHARS, 2790, 2800, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 2800, 2817, (byte) 33 ); // Fill 17 of value (byte) 33

+        Arrays.fill(CHARS, 2817, 2820, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[2820] = 33;

+        Arrays.fill(CHARS, 2821, 2829, (byte) -19 ); // Fill 8 of value (byte) -19

+        Arrays.fill(CHARS, 2829, 2831, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2831, 2833, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 2833, 2835, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2835, 2857, (byte) -19 ); // Fill 22 of value (byte) -19

+        CHARS[2857] = 33;

+        Arrays.fill(CHARS, 2858, 2865, (byte) -19 ); // Fill 7 of value (byte) -19

+        CHARS[2865] = 33;

+        Arrays.fill(CHARS, 2866, 2868, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 2868, 2870, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2870, 2874, (byte) -19 ); // Fill 4 of value (byte) -19

+        Arrays.fill(CHARS, 2874, 2876, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[2876] = -87;

+        CHARS[2877] = -19;

+        Arrays.fill(CHARS, 2878, 2884, (byte) -87 ); // Fill 6 of value (byte) -87

+        Arrays.fill(CHARS, 2884, 2887, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2887, 2889, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 2889, 2891, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 2891, 2894, (byte) -87 ); // Fill 3 of value (byte) -87

+        Arrays.fill(CHARS, 2894, 2902, (byte) 33 ); // Fill 8 of value (byte) 33

+        Arrays.fill(CHARS, 2902, 2904, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 2904, 2908, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 2908, 2910, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[2910] = 33;

+        Arrays.fill(CHARS, 2911, 2914, (byte) -19 ); // Fill 3 of value (byte) -19

+        Arrays.fill(CHARS, 2914, 2918, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 2918, 2928, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 2928, 2946, (byte) 33 ); // Fill 18 of value (byte) 33

+        Arrays.fill(CHARS, 2946, 2948, (byte) -87 ); // Fill 2 of value (byte) -87

+        CHARS[2948] = 33;

+        Arrays.fill(CHARS, 2949, 2955, (byte) -19 ); // Fill 6 of value (byte) -19

+        Arrays.fill(CHARS, 2955, 2958, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2958, 2961, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[2961] = 33;

+        Arrays.fill(CHARS, 2962, 2966, (byte) -19 ); // Fill 4 of value (byte) -19

+        Arrays.fill(CHARS, 2966, 2969, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2969, 2971, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[2971] = 33;

+        CHARS[2972] = -19;

+        CHARS[2973] = 33;

+        Arrays.fill(CHARS, 2974, 2976, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 2976, 2979, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2979, 2981, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 2981, 2984, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2984, 2987, (byte) -19 ); // Fill 3 of value (byte) -19

+        Arrays.fill(CHARS, 2987, 2990, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 2990, 2998, (byte) -19 ); // Fill 8 of value (byte) -19

+        CHARS[2998] = 33;

+        Arrays.fill(CHARS, 2999, 3002, (byte) -19 ); // Fill 3 of value (byte) -19

+        Arrays.fill(CHARS, 3002, 3006, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3006, 3011, (byte) -87 ); // Fill 5 of value (byte) -87

+        Arrays.fill(CHARS, 3011, 3014, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 3014, 3017, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[3017] = 33;

+        Arrays.fill(CHARS, 3018, 3022, (byte) -87 ); // Fill 4 of value (byte) -87

+        Arrays.fill(CHARS, 3022, 3031, (byte) 33 ); // Fill 9 of value (byte) 33

+        CHARS[3031] = -87;

+        Arrays.fill(CHARS, 3032, 3047, (byte) 33 ); // Fill 15 of value (byte) 33

+        Arrays.fill(CHARS, 3047, 3056, (byte) -87 ); // Fill 9 of value (byte) -87

+        Arrays.fill(CHARS, 3056, 3073, (byte) 33 ); // Fill 17 of value (byte) 33

+        Arrays.fill(CHARS, 3073, 3076, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[3076] = 33;

+        Arrays.fill(CHARS, 3077, 3085, (byte) -19 ); // Fill 8 of value (byte) -19

+        CHARS[3085] = 33;

+        Arrays.fill(CHARS, 3086, 3089, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[3089] = 33;

+        Arrays.fill(CHARS, 3090, 3113, (byte) -19 ); // Fill 23 of value (byte) -19

+        CHARS[3113] = 33;

+        Arrays.fill(CHARS, 3114, 3124, (byte) -19 ); // Fill 10 of value (byte) -19

+        CHARS[3124] = 33;

+        Arrays.fill(CHARS, 3125, 3130, (byte) -19 ); // Fill 5 of value (byte) -19

+        Arrays.fill(CHARS, 3130, 3134, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3134, 3141, (byte) -87 ); // Fill 7 of value (byte) -87

+        CHARS[3141] = 33;

+        Arrays.fill(CHARS, 3142, 3145, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[3145] = 33;

+        Arrays.fill(CHARS, 3146, 3150, (byte) -87 ); // Fill 4 of value (byte) -87

+        Arrays.fill(CHARS, 3150, 3157, (byte) 33 ); // Fill 7 of value (byte) 33

+        Arrays.fill(CHARS, 3157, 3159, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 3159, 3168, (byte) 33 ); // Fill 9 of value (byte) 33

+        Arrays.fill(CHARS, 3168, 3170, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 3170, 3174, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3174, 3184, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 3184, 3202, (byte) 33 ); // Fill 18 of value (byte) 33

+        Arrays.fill(CHARS, 3202, 3204, (byte) -87 ); // Fill 2 of value (byte) -87

+        CHARS[3204] = 33;

+        Arrays.fill(CHARS, 3205, 3213, (byte) -19 ); // Fill 8 of value (byte) -19

+        CHARS[3213] = 33;

+        Arrays.fill(CHARS, 3214, 3217, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[3217] = 33;

+        Arrays.fill(CHARS, 3218, 3241, (byte) -19 ); // Fill 23 of value (byte) -19

+        CHARS[3241] = 33;

+        Arrays.fill(CHARS, 3242, 3252, (byte) -19 ); // Fill 10 of value (byte) -19

+        CHARS[3252] = 33;

+        Arrays.fill(CHARS, 3253, 3258, (byte) -19 ); // Fill 5 of value (byte) -19

+        Arrays.fill(CHARS, 3258, 3262, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3262, 3269, (byte) -87 ); // Fill 7 of value (byte) -87

+        CHARS[3269] = 33;

+        Arrays.fill(CHARS, 3270, 3273, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[3273] = 33;

+        Arrays.fill(CHARS, 3274, 3278, (byte) -87 ); // Fill 4 of value (byte) -87

+        Arrays.fill(CHARS, 3278, 3285, (byte) 33 ); // Fill 7 of value (byte) 33

+        Arrays.fill(CHARS, 3285, 3287, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 3287, 3294, (byte) 33 ); // Fill 7 of value (byte) 33

+        CHARS[3294] = -19;

+        CHARS[3295] = 33;

+        Arrays.fill(CHARS, 3296, 3298, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 3298, 3302, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3302, 3312, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 3312, 3330, (byte) 33 ); // Fill 18 of value (byte) 33

+        Arrays.fill(CHARS, 3330, 3332, (byte) -87 ); // Fill 2 of value (byte) -87

+        CHARS[3332] = 33;

+        Arrays.fill(CHARS, 3333, 3341, (byte) -19 ); // Fill 8 of value (byte) -19

+        CHARS[3341] = 33;

+        Arrays.fill(CHARS, 3342, 3345, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[3345] = 33;

+        Arrays.fill(CHARS, 3346, 3369, (byte) -19 ); // Fill 23 of value (byte) -19

+        CHARS[3369] = 33;

+        Arrays.fill(CHARS, 3370, 3386, (byte) -19 ); // Fill 16 of value (byte) -19

+        Arrays.fill(CHARS, 3386, 3390, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3390, 3396, (byte) -87 ); // Fill 6 of value (byte) -87

+        Arrays.fill(CHARS, 3396, 3398, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 3398, 3401, (byte) -87 ); // Fill 3 of value (byte) -87

+        CHARS[3401] = 33;

+        Arrays.fill(CHARS, 3402, 3406, (byte) -87 ); // Fill 4 of value (byte) -87

+        Arrays.fill(CHARS, 3406, 3415, (byte) 33 ); // Fill 9 of value (byte) 33

+        CHARS[3415] = -87;

+        Arrays.fill(CHARS, 3416, 3424, (byte) 33 ); // Fill 8 of value (byte) 33

+        Arrays.fill(CHARS, 3424, 3426, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 3426, 3430, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3430, 3440, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 3440, 3585, (byte) 33 ); // Fill 145 of value (byte) 33

+        Arrays.fill(CHARS, 3585, 3631, (byte) -19 ); // Fill 46 of value (byte) -19

+        CHARS[3631] = 33;

+        CHARS[3632] = -19;

+        CHARS[3633] = -87;

+        Arrays.fill(CHARS, 3634, 3636, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 3636, 3643, (byte) -87 ); // Fill 7 of value (byte) -87

+        Arrays.fill(CHARS, 3643, 3648, (byte) 33 ); // Fill 5 of value (byte) 33

+        Arrays.fill(CHARS, 3648, 3654, (byte) -19 ); // Fill 6 of value (byte) -19

+        Arrays.fill(CHARS, 3654, 3663, (byte) -87 ); // Fill 9 of value (byte) -87

+        CHARS[3663] = 33;

+        Arrays.fill(CHARS, 3664, 3674, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 3674, 3713, (byte) 33 ); // Fill 39 of value (byte) 33

+        Arrays.fill(CHARS, 3713, 3715, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[3715] = 33;

+        CHARS[3716] = -19;

+        Arrays.fill(CHARS, 3717, 3719, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 3719, 3721, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[3721] = 33;

+        CHARS[3722] = -19;

+        Arrays.fill(CHARS, 3723, 3725, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[3725] = -19;

+        Arrays.fill(CHARS, 3726, 3732, (byte) 33 ); // Fill 6 of value (byte) 33

+        Arrays.fill(CHARS, 3732, 3736, (byte) -19 ); // Fill 4 of value (byte) -19

+        CHARS[3736] = 33;

+        Arrays.fill(CHARS, 3737, 3744, (byte) -19 ); // Fill 7 of value (byte) -19

+        CHARS[3744] = 33;

+        Arrays.fill(CHARS, 3745, 3748, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[3748] = 33;

+        CHARS[3749] = -19;

+        CHARS[3750] = 33;

+        CHARS[3751] = -19;

+        Arrays.fill(CHARS, 3752, 3754, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 3754, 3756, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[3756] = 33;

+        Arrays.fill(CHARS, 3757, 3759, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[3759] = 33;

+        CHARS[3760] = -19;

+        CHARS[3761] = -87;

+        Arrays.fill(CHARS, 3762, 3764, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 3764, 3770, (byte) -87 ); // Fill 6 of value (byte) -87

+        CHARS[3770] = 33;

+        Arrays.fill(CHARS, 3771, 3773, (byte) -87 ); // Fill 2 of value (byte) -87

+        CHARS[3773] = -19;

+        Arrays.fill(CHARS, 3774, 3776, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 3776, 3781, (byte) -19 ); // Fill 5 of value (byte) -19

+        CHARS[3781] = 33;

+        CHARS[3782] = -87;

+        CHARS[3783] = 33;

+        Arrays.fill(CHARS, 3784, 3790, (byte) -87 ); // Fill 6 of value (byte) -87

+        Arrays.fill(CHARS, 3790, 3792, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 3792, 3802, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 3802, 3864, (byte) 33 ); // Fill 62 of value (byte) 33

+        Arrays.fill(CHARS, 3864, 3866, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 3866, 3872, (byte) 33 ); // Fill 6 of value (byte) 33

+        Arrays.fill(CHARS, 3872, 3882, (byte) -87 ); // Fill 10 of value (byte) -87

+        Arrays.fill(CHARS, 3882, 3893, (byte) 33 ); // Fill 11 of value (byte) 33

+        CHARS[3893] = -87;

+        CHARS[3894] = 33;

+        CHARS[3895] = -87;

+        CHARS[3896] = 33;

+        CHARS[3897] = -87;

+        Arrays.fill(CHARS, 3898, 3902, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3902, 3904, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 3904, 3912, (byte) -19 ); // Fill 8 of value (byte) -19

+        CHARS[3912] = 33;

+        Arrays.fill(CHARS, 3913, 3946, (byte) -19 ); // Fill 33 of value (byte) -19

+        Arrays.fill(CHARS, 3946, 3953, (byte) 33 ); // Fill 7 of value (byte) 33

+        Arrays.fill(CHARS, 3953, 3973, (byte) -87 ); // Fill 20 of value (byte) -87

+        CHARS[3973] = 33;

+        Arrays.fill(CHARS, 3974, 3980, (byte) -87 ); // Fill 6 of value (byte) -87

+        Arrays.fill(CHARS, 3980, 3984, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 3984, 3990, (byte) -87 ); // Fill 6 of value (byte) -87

+        CHARS[3990] = 33;

+        CHARS[3991] = -87;

+        CHARS[3992] = 33;

+        Arrays.fill(CHARS, 3993, 4014, (byte) -87 ); // Fill 21 of value (byte) -87

+        Arrays.fill(CHARS, 4014, 4017, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 4017, 4024, (byte) -87 ); // Fill 7 of value (byte) -87

+        CHARS[4024] = 33;

+        CHARS[4025] = -87;

+        Arrays.fill(CHARS, 4026, 4256, (byte) 33 ); // Fill 230 of value (byte) 33

+        Arrays.fill(CHARS, 4256, 4294, (byte) -19 ); // Fill 38 of value (byte) -19

+        Arrays.fill(CHARS, 4294, 4304, (byte) 33 ); // Fill 10 of value (byte) 33

+        Arrays.fill(CHARS, 4304, 4343, (byte) -19 ); // Fill 39 of value (byte) -19

+        Arrays.fill(CHARS, 4343, 4352, (byte) 33 ); // Fill 9 of value (byte) 33

+        CHARS[4352] = -19;

+        CHARS[4353] = 33;

+        Arrays.fill(CHARS, 4354, 4356, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[4356] = 33;

+        Arrays.fill(CHARS, 4357, 4360, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[4360] = 33;

+        CHARS[4361] = -19;

+        CHARS[4362] = 33;

+        Arrays.fill(CHARS, 4363, 4365, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[4365] = 33;

+        Arrays.fill(CHARS, 4366, 4371, (byte) -19 ); // Fill 5 of value (byte) -19

+        Arrays.fill(CHARS, 4371, 4412, (byte) 33 ); // Fill 41 of value (byte) 33

+        CHARS[4412] = -19;

+        CHARS[4413] = 33;

+        CHARS[4414] = -19;

+        CHARS[4415] = 33;

+        CHARS[4416] = -19;

+        Arrays.fill(CHARS, 4417, 4428, (byte) 33 ); // Fill 11 of value (byte) 33

+        CHARS[4428] = -19;

+        CHARS[4429] = 33;

+        CHARS[4430] = -19;

+        CHARS[4431] = 33;

+        CHARS[4432] = -19;

+        Arrays.fill(CHARS, 4433, 4436, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 4436, 4438, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 4438, 4441, (byte) 33 ); // Fill 3 of value (byte) 33

+        CHARS[4441] = -19;

+        Arrays.fill(CHARS, 4442, 4447, (byte) 33 ); // Fill 5 of value (byte) 33

+        Arrays.fill(CHARS, 4447, 4450, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[4450] = 33;

+        CHARS[4451] = -19;

+        CHARS[4452] = 33;

+        CHARS[4453] = -19;

+        CHARS[4454] = 33;

+        CHARS[4455] = -19;

+        CHARS[4456] = 33;

+        CHARS[4457] = -19;

+        Arrays.fill(CHARS, 4458, 4461, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 4461, 4463, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 4463, 4466, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 4466, 4468, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[4468] = 33;

+        CHARS[4469] = -19;

+        Arrays.fill(CHARS, 4470, 4510, (byte) 33 ); // Fill 40 of value (byte) 33

+        CHARS[4510] = -19;

+        Arrays.fill(CHARS, 4511, 4520, (byte) 33 ); // Fill 9 of value (byte) 33

+        CHARS[4520] = -19;

+        Arrays.fill(CHARS, 4521, 4523, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[4523] = -19;

+        Arrays.fill(CHARS, 4524, 4526, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 4526, 4528, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 4528, 4535, (byte) 33 ); // Fill 7 of value (byte) 33

+        Arrays.fill(CHARS, 4535, 4537, (byte) -19 ); // Fill 2 of value (byte) -19

+        CHARS[4537] = 33;

+        CHARS[4538] = -19;

+        CHARS[4539] = 33;

+        Arrays.fill(CHARS, 4540, 4547, (byte) -19 ); // Fill 7 of value (byte) -19

+        Arrays.fill(CHARS, 4547, 4587, (byte) 33 ); // Fill 40 of value (byte) 33

+        CHARS[4587] = -19;

+        Arrays.fill(CHARS, 4588, 4592, (byte) 33 ); // Fill 4 of value (byte) 33

+        CHARS[4592] = -19;

+        Arrays.fill(CHARS, 4593, 4601, (byte) 33 ); // Fill 8 of value (byte) 33

+        CHARS[4601] = -19;

+        Arrays.fill(CHARS, 4602, 7680, (byte) 33 ); // Fill 3078 of value (byte) 33

+        Arrays.fill(CHARS, 7680, 7836, (byte) -19 ); // Fill 156 of value (byte) -19

+        Arrays.fill(CHARS, 7836, 7840, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 7840, 7930, (byte) -19 ); // Fill 90 of value (byte) -19

+        Arrays.fill(CHARS, 7930, 7936, (byte) 33 ); // Fill 6 of value (byte) 33

+        Arrays.fill(CHARS, 7936, 7958, (byte) -19 ); // Fill 22 of value (byte) -19

+        Arrays.fill(CHARS, 7958, 7960, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 7960, 7966, (byte) -19 ); // Fill 6 of value (byte) -19

+        Arrays.fill(CHARS, 7966, 7968, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 7968, 8006, (byte) -19 ); // Fill 38 of value (byte) -19

+        Arrays.fill(CHARS, 8006, 8008, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 8008, 8014, (byte) -19 ); // Fill 6 of value (byte) -19

+        Arrays.fill(CHARS, 8014, 8016, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 8016, 8024, (byte) -19 ); // Fill 8 of value (byte) -19

+        CHARS[8024] = 33;

+        CHARS[8025] = -19;

+        CHARS[8026] = 33;

+        CHARS[8027] = -19;

+        CHARS[8028] = 33;

+        CHARS[8029] = -19;

+        CHARS[8030] = 33;

+        Arrays.fill(CHARS, 8031, 8062, (byte) -19 ); // Fill 31 of value (byte) -19

+        Arrays.fill(CHARS, 8062, 8064, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 8064, 8117, (byte) -19 ); // Fill 53 of value (byte) -19

+        CHARS[8117] = 33;

+        Arrays.fill(CHARS, 8118, 8125, (byte) -19 ); // Fill 7 of value (byte) -19

+        CHARS[8125] = 33;

+        CHARS[8126] = -19;

+        Arrays.fill(CHARS, 8127, 8130, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 8130, 8133, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[8133] = 33;

+        Arrays.fill(CHARS, 8134, 8141, (byte) -19 ); // Fill 7 of value (byte) -19

+        Arrays.fill(CHARS, 8141, 8144, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 8144, 8148, (byte) -19 ); // Fill 4 of value (byte) -19

+        Arrays.fill(CHARS, 8148, 8150, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 8150, 8156, (byte) -19 ); // Fill 6 of value (byte) -19

+        Arrays.fill(CHARS, 8156, 8160, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 8160, 8173, (byte) -19 ); // Fill 13 of value (byte) -19

+        Arrays.fill(CHARS, 8173, 8178, (byte) 33 ); // Fill 5 of value (byte) 33

+        Arrays.fill(CHARS, 8178, 8181, (byte) -19 ); // Fill 3 of value (byte) -19

+        CHARS[8181] = 33;

+        Arrays.fill(CHARS, 8182, 8189, (byte) -19 ); // Fill 7 of value (byte) -19

+        Arrays.fill(CHARS, 8189, 8400, (byte) 33 ); // Fill 211 of value (byte) 33

+        Arrays.fill(CHARS, 8400, 8413, (byte) -87 ); // Fill 13 of value (byte) -87

+        Arrays.fill(CHARS, 8413, 8417, (byte) 33 ); // Fill 4 of value (byte) 33

+        CHARS[8417] = -87;

+        Arrays.fill(CHARS, 8418, 8486, (byte) 33 ); // Fill 68 of value (byte) 33

+        CHARS[8486] = -19;

+        Arrays.fill(CHARS, 8487, 8490, (byte) 33 ); // Fill 3 of value (byte) 33

+        Arrays.fill(CHARS, 8490, 8492, (byte) -19 ); // Fill 2 of value (byte) -19

+        Arrays.fill(CHARS, 8492, 8494, (byte) 33 ); // Fill 2 of value (byte) 33

+        CHARS[8494] = -19;

+        Arrays.fill(CHARS, 8495, 8576, (byte) 33 ); // Fill 81 of value (byte) 33

+        Arrays.fill(CHARS, 8576, 8579, (byte) -19 ); // Fill 3 of value (byte) -19

+        Arrays.fill(CHARS, 8579, 12293, (byte) 33 ); // Fill 3714 of value (byte) 33

+        CHARS[12293] = -87;

+        CHARS[12294] = 33;

+        CHARS[12295] = -19;

+        Arrays.fill(CHARS, 12296, 12321, (byte) 33 ); // Fill 25 of value (byte) 33

+        Arrays.fill(CHARS, 12321, 12330, (byte) -19 ); // Fill 9 of value (byte) -19

+        Arrays.fill(CHARS, 12330, 12336, (byte) -87 ); // Fill 6 of value (byte) -87

+        CHARS[12336] = 33;

+        Arrays.fill(CHARS, 12337, 12342, (byte) -87 ); // Fill 5 of value (byte) -87

+        Arrays.fill(CHARS, 12342, 12353, (byte) 33 ); // Fill 11 of value (byte) 33

+        Arrays.fill(CHARS, 12353, 12437, (byte) -19 ); // Fill 84 of value (byte) -19

+        Arrays.fill(CHARS, 12437, 12441, (byte) 33 ); // Fill 4 of value (byte) 33

+        Arrays.fill(CHARS, 12441, 12443, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 12443, 12445, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 12445, 12447, (byte) -87 ); // Fill 2 of value (byte) -87

+        Arrays.fill(CHARS, 12447, 12449, (byte) 33 ); // Fill 2 of value (byte) 33

+        Arrays.fill(CHARS, 12449, 12539, (byte) -19 ); // Fill 90 of value (byte) -19

+        CHARS[12539] = 33;

+        Arrays.fill(CHARS, 12540, 12543, (byte) -87 ); // Fill 3 of value (byte) -87

+        Arrays.fill(CHARS, 12543, 12549, (byte) 33 ); // Fill 6 of value (byte) 33

+        Arrays.fill(CHARS, 12549, 12589, (byte) -19 ); // Fill 40 of value (byte) -19

+        Arrays.fill(CHARS, 12589, 19968, (byte) 33 ); // Fill 7379 of value (byte) 33

+        Arrays.fill(CHARS, 19968, 40870, (byte) -19 ); // Fill 20902 of value (byte) -19

+        Arrays.fill(CHARS, 40870, 44032, (byte) 33 ); // Fill 3162 of value (byte) 33

+        Arrays.fill(CHARS, 44032, 55204, (byte) -19 ); // Fill 11172 of value (byte) -19

+        Arrays.fill(CHARS, 55204, 55296, (byte) 33 ); // Fill 92 of value (byte) 33

+        Arrays.fill(CHARS, 57344, 65534, (byte) 33 ); // Fill 8190 of value (byte) 33

+

+    } // <clinit>()

+

+    //

+    // Public static methods

+    //

+

+    /**

+     * Returns true if the specified character is a supplemental character.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isSupplemental(int c) {

+        return (c >= 0x10000 && c <= 0x10FFFF);

+    }

+

+    /**

+     * Returns true the supplemental character corresponding to the given

+     * surrogates.

+     *

+     * @param h The high surrogate.

+     * @param l The low surrogate.

+     */

+    public static int supplemental(char h, char l) {

+        return (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000;

+    }

+

+    /**

+     * Returns the high surrogate of a supplemental character

+     *

+     * @param c The supplemental character to "split".

+     */

+    public static char highSurrogate(int c) {

+        return (char) (((c - 0x00010000) >> 10) + 0xD800);

+    }

+

+    /**

+     * Returns the low surrogate of a supplemental character

+     *

+     * @param c The supplemental character to "split".

+     */

+    public static char lowSurrogate(int c) {

+        return (char) (((c - 0x00010000) & 0x3FF) + 0xDC00);

+    }

+

+    /**

+     * Returns whether the given character is a high surrogate

+     *

+     * @param c The character to check.

+     */

+    public static boolean isHighSurrogate(int c) {

+        return (0xD800 <= c && c <= 0xDBFF);

+    }

+

+    /**

+     * Returns whether the given character is a low surrogate

+     *

+     * @param c The character to check.

+     */

+    public static boolean isLowSurrogate(int c) {

+        return (0xDC00 <= c && c <= 0xDFFF);

+    }

+

+

+    /**

+     * Returns true if the specified character is valid. This method

+     * also checks the surrogate character range from 0x10000 to 0x10FFFF.

+     * <p>

+     * If the program chooses to apply the mask directly to the

+     * <code>CHARS</code> array, then they are responsible for checking

+     * the surrogate character range.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isValid(int c) {

+        return (c < 0x10000 && (CHARS[c] & MASK_VALID) != 0) ||

+               (0x10000 <= c && c <= 0x10FFFF);

+    } // isValid(int):boolean

+

+    /**

+     * Returns true if the specified character is invalid.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isInvalid(int c) {

+        return !isValid(c);

+    } // isInvalid(int):boolean

+

+    /**

+     * Returns true if the specified character can be considered content.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isContent(int c) {

+        return (c < 0x10000 && (CHARS[c] & MASK_CONTENT) != 0) ||

+               (0x10000 <= c && c <= 0x10FFFF);

+    } // isContent(int):boolean

+

+    /**

+     * Returns true if the specified character can be considered markup.

+     * Markup characters include '&lt;', '&amp;', and '%'.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isMarkup(int c) {

+        return c == '<' || c == '&' || c == '%';

+    } // isMarkup(int):boolean

+

+    /**

+     * Returns true if the specified character is a space character

+     * as defined by production [3] in the XML 1.0 specification.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isSpace(int c) {

+        return c <= 0x20 && (CHARS[c] & MASK_SPACE) != 0;

+    } // isSpace(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid name start

+     * character as defined by production [5] in the XML 1.0

+     * specification.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isNameStart(int c) {

+        return c < 0x10000 && (CHARS[c] & MASK_NAME_START) != 0;

+    } // isNameStart(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid name

+     * character as defined by production [4] in the XML 1.0

+     * specification.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isName(int c) {

+        return c < 0x10000 && (CHARS[c] & MASK_NAME) != 0;

+    } // isName(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid NCName start

+     * character as defined by production [4] in Namespaces in XML

+     * recommendation.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isNCNameStart(int c) {

+        return c < 0x10000 && (CHARS[c] & MASK_NCNAME_START) != 0;

+    } // isNCNameStart(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid NCName

+     * character as defined by production [5] in Namespaces in XML

+     * recommendation.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isNCName(int c) {

+        return c < 0x10000 && (CHARS[c] & MASK_NCNAME) != 0;

+    } // isNCName(int):boolean

+

+    /**

+     * Returns true if the specified character is a valid Pubid

+     * character as defined by production [13] in the XML 1.0

+     * specification.

+     *

+     * @param c The character to check.

+     */

+    public static boolean isPubid(int c) {

+        return c < 0x10000 && (CHARS[c] & MASK_PUBID) != 0;

+    } // isPubid(int):boolean

+

+    /*

+     * [5] Name ::= (Letter | '_' | ':') (NameChar)*

+     */

+    /**

+     * Check to see if a string is a valid Name according to [5]

+     * in the XML 1.0 Recommendation

+     *

+     * @param name string to check

+     * @return true if name is a valid Name

+     */

+    public static boolean isValidName(String name) {

+        if (name.length() == 0)

+            return false;

+        char ch = name.charAt(0);

+        if( isNameStart(ch) == false)

+           return false;

+        for (int i = 1; i < name.length(); i++ ) {

+           ch = name.charAt(i);

+           if( isName( ch ) == false ){

+              return false;

+           }

+        }

+        return true;

+    } // isValidName(String):boolean

+    

+

+    /*

+     * from the namespace rec

+     * [4] NCName ::= (Letter | '_') (NCNameChar)*

+     */

+    /**

+     * Check to see if a string is a valid NCName according to [4]

+     * from the XML Namespaces 1.0 Recommendation

+     *

+     * @param ncName string to check

+     * @return true if name is a valid NCName

+     */

+    public static boolean isValidNCName(String ncName) {

+        if (ncName.length() == 0)

+            return false;

+        char ch = ncName.charAt(0);

+        if( isNCNameStart(ch) == false)

+           return false;

+        for (int i = 1; i < ncName.length(); i++ ) {

+           ch = ncName.charAt(i);

+           if( isNCName( ch ) == false ){

+              return false;

+           }

+        }

+        return true;

+    } // isValidNCName(String):boolean

+

+    /*

+     * [7] Nmtoken ::= (NameChar)+

+     */

+    /**

+     * Check to see if a string is a valid Nmtoken according to [7]

+     * in the XML 1.0 Recommendation

+     *

+     * @param nmtoken string to check

+     * @return true if nmtoken is a valid Nmtoken 

+     */

+    public static boolean isValidNmtoken(String nmtoken) {

+        if (nmtoken.length() == 0)

+            return false;

+        for (int i = 0; i < nmtoken.length(); i++ ) {

+           char ch = nmtoken.charAt(i);

+           if(  ! isName( ch ) ){

+              return false;

+           }

+        }

+        return true;

+    } // isValidName(String):boolean

+

+

+

+

+

+    // encodings

+

+    /**

+     * Returns true if the encoding name is a valid IANA encoding.

+     * This method does not verify that there is a decoder available

+     * for this encoding, only that the characters are valid for an

+     * IANA encoding name.

+     *

+     * @param ianaEncoding The IANA encoding name.

+     */

+    public static boolean isValidIANAEncoding(String ianaEncoding) {

+        if (ianaEncoding != null) {

+            int length = ianaEncoding.length();

+            if (length > 0) {

+                char c = ianaEncoding.charAt(0);

+                if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {

+                    for (int i = 1; i < length; i++) {

+                        c = ianaEncoding.charAt(i);

+                        if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&

+                            (c < '0' || c > '9') && c != '.' && c != '_' &&

+                            c != '-') {

+                            return false;

+                        }

+                    }

+                    return true;

+                }

+            }

+        }

+        return false;

+    } // isValidIANAEncoding(String):boolean

+

+    /**

+     * Returns true if the encoding name is a valid Java encoding.

+     * This method does not verify that there is a decoder available

+     * for this encoding, only that the characters are valid for an

+     * Java encoding name.

+     *

+     * @param javaEncoding The Java encoding name.

+     */

+    public static boolean isValidJavaEncoding(String javaEncoding) {

+        if (javaEncoding != null) {

+            int length = javaEncoding.length();

+            if (length > 0) {

+                for (int i = 1; i < length; i++) {

+                    char c = javaEncoding.charAt(i);

+                    if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&

+                        (c < '0' || c > '9') && c != '.' && c != '_' &&

+                        c != '-') {

+                        return false;

+                    }

+                }

+                return true;

+            }

+        }

+        return false;

+    } // isValidIANAEncoding(String):boolean

+

+

+} // class XMLChar

diff --git a/src/main/java/org/apache/xml/utils/AttList.java b/src/main/java/org/apache/xml/utils/AttList.java
new file mode 100644
index 0000000..7d40f0d
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/AttList.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AttList.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import org.xml.sax.Attributes;
+
+/**
+ * Wraps a DOM attribute list in a SAX Attributes.
+ * @xsl.usage internal
+ */
+public class AttList implements Attributes
+{
+
+  /** List of attribute nodes          */
+  NamedNodeMap m_attrs;
+
+  /** Index of last attribute node          */
+  int m_lastIndex;
+
+  // ARGHH!!  JAXP Uses Xerces without setting the namespace processing to ON!
+  // DOM2Helper m_dh = new DOM2Helper();
+
+  /** Local reference to DOMHelper          */
+  DOMHelper m_dh;
+
+//  /**
+//   * Constructor AttList
+//   *
+//   *
+//   * @param attrs List of attributes this will contain
+//   */
+//  public AttList(NamedNodeMap attrs)
+//  {
+//
+//    m_attrs = attrs;
+//    m_lastIndex = m_attrs.getLength() - 1;
+//    m_dh = new DOM2Helper();
+//  }
+
+  /**
+   * Constructor AttList
+   *
+   *
+   * @param attrs List of attributes this will contain
+   * @param dh DOMHelper 
+   */
+  public AttList(NamedNodeMap attrs, DOMHelper dh)
+  {
+
+    m_attrs = attrs;
+    m_lastIndex = m_attrs.getLength() - 1;
+    m_dh = dh;
+  }
+
+  /**
+   * Get the number of attribute nodes in the list 
+   *
+   *
+   * @return number of attribute nodes
+   */
+  public int getLength()
+  {
+    return m_attrs.getLength();
+  }
+
+  /**
+   * Look up an attribute's Namespace URI by index.
+   *
+   * @param index The attribute index (zero-based).
+   * @return The Namespace URI, or the empty string if none
+   *         is available, or null if the index is out of
+   *         range.
+   */
+  public String getURI(int index)
+  {
+    String ns = m_dh.getNamespaceOfNode(((Attr) m_attrs.item(index)));
+    if(null == ns)
+      ns = "";
+    return ns;
+  }
+
+  /**
+   * Look up an attribute's local name by index.
+   *
+   * @param index The attribute index (zero-based).
+   * @return The local name, or the empty string if Namespace
+   *         processing is not being performed, or null
+   *         if the index is out of range.
+   */
+  public String getLocalName(int index)
+  {
+    return m_dh.getLocalNameOfNode(((Attr) m_attrs.item(index)));
+  }
+
+  /**
+   * Look up an attribute's qualified name by index.
+   *
+   *
+   * @param i The attribute index (zero-based).
+   *
+   * @return The attribute's qualified name
+   */
+  public String getQName(int i)
+  {
+    return ((Attr) m_attrs.item(i)).getName();
+  }
+
+  /**
+   * Get the attribute's node type by index
+   *
+   *
+   * @param i The attribute index (zero-based)
+   *
+   * @return the attribute's node type
+   */
+  public String getType(int i)
+  {
+    return "CDATA";  // for the moment
+  }
+
+  /**
+   * Get the attribute's node value by index
+   *
+   *
+   * @param i The attribute index (zero-based)
+   *
+   * @return the attribute's node value
+   */
+  public String getValue(int i)
+  {
+    return ((Attr) m_attrs.item(i)).getValue();
+  }
+
+  /**
+   * Get the attribute's node type by name
+   *
+   *
+   * @param name Attribute name
+   *
+   * @return the attribute's node type
+   */
+  public String getType(String name)
+  {
+    return "CDATA";  // for the moment
+  }
+
+  /**
+   * Look up an attribute's type by Namespace name.
+   *
+   * @param uri The Namespace URI, or the empty String if the
+   *        name has no Namespace URI.
+   * @param localName The local name of the attribute.
+   * @return The attribute type as a string, or null if the
+   *         attribute is not in the list or if Namespace
+   *         processing is not being performed.
+   */
+  public String getType(String uri, String localName)
+  {
+    return "CDATA";  // for the moment
+  }
+
+  /**
+   * Look up an attribute's value by name.
+   *
+   *
+   * @param name The attribute node's name
+   *
+   * @return The attribute node's value
+   */
+  public String getValue(String name)
+  {
+    Attr attr = ((Attr) m_attrs.getNamedItem(name));
+    return (null != attr) 
+          ? attr.getValue() : null;
+  }
+
+  /**
+   * Look up an attribute's value by Namespace name.
+   *
+   * @param uri The Namespace URI, or the empty String if the
+   *        name has no Namespace URI.
+   * @param localName The local name of the attribute.
+   * @return The attribute value as a string, or null if the
+   *         attribute is not in the list.
+   */
+  public String getValue(String uri, String localName)
+  {
+		Node a=m_attrs.getNamedItemNS(uri,localName);
+		return (a==null) ? null : a.getNodeValue();
+  }
+
+  /**
+   * Look up the index of an attribute by Namespace name.
+   *
+   * @param uri The Namespace URI, or the empty string if
+   *        the name has no Namespace URI.
+   * @param localPart The attribute's local name.
+   * @return The index of the attribute, or -1 if it does not
+   *         appear in the list.
+   */
+  public int getIndex(String uri, String localPart)
+  {
+    for(int i=m_attrs.getLength()-1;i>=0;--i)
+    {
+      Node a=m_attrs.item(i);
+      String u=a.getNamespaceURI();
+      if( (u==null ? uri==null : u.equals(uri))
+	  &&
+	  a.getLocalName().equals(localPart) )
+	return i;
+    }
+    return -1;
+  }
+
+  /**
+   * Look up the index of an attribute by raw XML 1.0 name.
+   *
+   * @param qName The qualified (prefixed) name.
+   * @return The index of the attribute, or -1 if it does not
+   *         appear in the list.
+   */
+  public int getIndex(String qName)
+  {
+    for(int i=m_attrs.getLength()-1;i>=0;--i)
+    {
+      Node a=m_attrs.item(i);
+      if(a.getNodeName().equals(qName) )
+	return i;
+    }
+    return -1;
+  }
+}
+
diff --git a/src/main/java/org/apache/xml/utils/BoolStack.java b/src/main/java/org/apache/xml/utils/BoolStack.java
new file mode 100644
index 0000000..aadb10d
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/BoolStack.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: BoolStack.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+
+/**
+ * Simple stack for boolean values.
+ * @xsl.usage internal
+ */
+public final class BoolStack implements Cloneable
+{
+
+  /** Array of boolean values          */
+  private boolean m_values[];
+
+  /** Array size allocated           */
+  private int m_allocatedSize;
+
+  /** Index into the array of booleans          */
+  private int m_index;
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is very small, for small lists.
+   */
+  public BoolStack()
+  {
+    this(32);
+  }
+
+  /**
+   * Construct a IntVector, using the given block size.
+   *
+   * @param size array size to allocate
+   */
+  public BoolStack(int size)
+  {
+
+    m_allocatedSize = size;
+    m_values = new boolean[size];
+    m_index = -1;
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return Current length of the list
+   */
+  public final int size()
+  {
+    return m_index + 1;
+  }
+
+  /**
+   * Clears the stack.
+   *
+   */
+  public final void clear()
+  {
+  	m_index = -1;
+  }
+
+  /**
+   * Pushes an item onto the top of this stack.
+   *
+   *
+   * @param val the boolean to be pushed onto this stack.
+   * @return  the <code>item</code> argument.
+   */
+  public final boolean push(boolean val)
+  {
+
+    if (m_index == m_allocatedSize - 1)
+      grow();
+
+    return (m_values[++m_index] = val);
+  }
+
+  /**
+   * Removes the object at the top of this stack and returns that
+   * object as the value of this function.
+   *
+   * @return     The object at the top of this stack.
+   * @throws  EmptyStackException  if this stack is empty.
+   */
+  public final boolean pop()
+  {
+    return m_values[m_index--];
+  }
+
+  /**
+   * Removes the object at the top of this stack and returns the
+   * next object at the top as the value of this function.
+   *
+   *
+   * @return Next object to the top or false if none there
+   */
+  public final boolean popAndTop()
+  {
+
+    m_index--;
+
+    return (m_index >= 0) ? m_values[m_index] : false;
+  }
+
+  /**
+   * Set the item at the top of this stack  
+   *
+   *
+   * @param b Object to set at the top of this stack
+   */
+  public final void setTop(boolean b)
+  {
+    m_values[m_index] = b;
+  }
+
+  /**
+   * Looks at the object at the top of this stack without removing it
+   * from the stack.
+   *
+   * @return     the object at the top of this stack.
+   * @throws  EmptyStackException  if this stack is empty.
+   */
+  public final boolean peek()
+  {
+    return m_values[m_index];
+  }
+
+  /**
+   * Looks at the object at the top of this stack without removing it
+   * from the stack.  If the stack is empty, it returns false.
+   *
+   * @return     the object at the top of this stack.
+   */
+  public final boolean peekOrFalse()
+  {
+    return (m_index > -1) ? m_values[m_index] : false;
+  }
+
+  /**
+   * Looks at the object at the top of this stack without removing it
+   * from the stack.  If the stack is empty, it returns true.
+   *
+   * @return     the object at the top of this stack.
+   */
+  public final boolean peekOrTrue()
+  {
+    return (m_index > -1) ? m_values[m_index] : true;
+  }
+
+  /**
+   * Tests if this stack is empty.
+   *
+   * @return  <code>true</code> if this stack is empty;
+   *          <code>false</code> otherwise.
+   */
+  public boolean isEmpty()
+  {
+    return (m_index == -1);
+  }
+
+  /**
+   * Grows the size of the stack
+   *
+   */
+  private void grow()
+  {
+
+    m_allocatedSize *= 2;
+
+    boolean newVector[] = new boolean[m_allocatedSize];
+
+    System.arraycopy(m_values, 0, newVector, 0, m_index + 1);
+
+    m_values = newVector;
+  }
+  
+  public Object clone() 
+    throws CloneNotSupportedException
+  {
+    return super.clone();
+  }
+
+}
diff --git a/src/main/java/org/apache/xml/utils/Constants.java b/src/main/java/org/apache/xml/utils/Constants.java
new file mode 100644
index 0000000..435f9ab
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/Constants.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Constants.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * Primary constants used by the XSLT Processor
+ * @xsl.usage advanced
+ */
+public class Constants
+{
+
+  /** 
+   * Mnemonics for standard XML Namespace URIs, as Java Strings:
+   * <ul>
+   * <li>S_XMLNAMESPACEURI (http://www.w3.org/XML/1998/namespace) is the
+   * URI permanantly assigned to the "xml:" prefix. This is used for some
+   * features built into the XML specification itself, such as xml:space 
+   * and xml:lang. It was defined by the W3C's XML Namespaces spec.</li>
+   * <li>S_XSLNAMESPACEURL (http://www.w3.org/1999/XSL/Transform) is the
+   * URI which indicates that a name may be an XSLT directive. In most
+   * XSLT stylesheets, this is bound to the "xsl:" prefix. It's defined
+   * by the W3C's XSLT Recommendation.</li>
+   * <li>S_OLDXSLNAMESPACEURL (http://www.w3.org/XSL/Transform/1.0) was
+   * used in early prototypes of XSLT processors for much the same purpose
+   * as S_XSLNAMESPACEURL. It is now considered obsolete, and the version
+   * of XSLT which it signified is not fully compatable with the final
+   * XSLT Recommendation, so what it really signifies is a badly obsolete
+   * stylesheet.</li>
+   * </ul> */
+  public static final String 
+	S_XMLNAMESPACEURI = "http://www.w3.org/XML/1998/namespace", 
+	S_XSLNAMESPACEURL = "http://www.w3.org/1999/XSL/Transform", 
+	S_OLDXSLNAMESPACEURL = "http://www.w3.org/XSL/Transform/1.0";
+
+  /** Authorship mnemonics, as Java Strings. Not standardized, 
+   * as far as I know.
+   * <ul>
+   * <li>S_VENDOR -- the name of the organization/individual who published
+   * this XSLT processor. </li>
+   * <li>S_VENDORURL -- URL where one can attempt to retrieve more
+   * information about this publisher and product.</li>
+   * </ul>
+   */
+  public static final String 
+	S_VENDOR = "Apache Software Foundation", 
+	S_VENDORURL = "http://xml.apache.org";
+
+  /** S_BUILTIN_EXTENSIONS_URL is a mnemonic for the XML Namespace 
+   *(http://xml.apache.org/xalan) predefined to signify Xalan's
+   * built-in XSLT Extensions. When used in stylesheets, this is often 
+   * bound to the "xalan:" prefix.
+   */
+  public static final String 
+    S_BUILTIN_EXTENSIONS_URL = "http://xml.apache.org/xalan"; 
+
+  /**
+   * The old built-in extension url. It is still supported for
+   * backward compatibility.
+   */
+  public static final String 
+    S_BUILTIN_OLD_EXTENSIONS_URL = "http://xml.apache.org/xslt"; 
+  
+  /**
+   * Xalan extension namespaces.
+   */
+  public static final String 
+    // The old namespace for Java extension
+    S_EXTENSIONS_OLD_JAVA_URL = "http://xml.apache.org/xslt/java",
+    // The new namespace for Java extension
+    S_EXTENSIONS_JAVA_URL = "http://xml.apache.org/xalan/java",
+    S_EXTENSIONS_LOTUSXSL_JAVA_URL = "http://xsl.lotus.com/java",
+    S_EXTENSIONS_XALANLIB_URL = "http://xml.apache.org/xalan",
+    S_EXTENSIONS_REDIRECT_URL = "http://xml.apache.org/xalan/redirect",
+    S_EXTENSIONS_PIPE_URL = "http://xml.apache.org/xalan/PipeDocument",
+    S_EXTENSIONS_SQL_URL = "http://xml.apache.org/xalan/sql";
+  
+  /**
+   * EXSLT extension namespaces.
+   */
+  public static final String
+    S_EXSLT_COMMON_URL = "http://exslt.org/common",
+    S_EXSLT_MATH_URL = "http://exslt.org/math",
+    S_EXSLT_SETS_URL = "http://exslt.org/sets",
+    S_EXSLT_DATETIME_URL = "http://exslt.org/dates-and-times",
+    S_EXSLT_FUNCTIONS_URL = "http://exslt.org/functions",
+    S_EXSLT_DYNAMIC_URL = "http://exslt.org/dynamic",
+    S_EXSLT_STRINGS_URL = "http://exslt.org/strings";
+    
+    
+  /**
+   * The minimum version of XSLT supported by this processor.
+   */
+  public static final double XSLTVERSUPPORTED = 1.0;
+}
diff --git a/src/main/java/org/apache/xml/utils/DOM2Helper.java b/src/main/java/org/apache/xml/utils/DOM2Helper.java
new file mode 100644
index 0000000..98198e6
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/DOM2Helper.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOM2Helper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.io.IOException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.xml.sax.InputSource;
+
+/**
+ * @deprecated Since the introduction of the DTM, this class will be removed.
+ * This class provides a DOM level 2 "helper", which provides services currently 
+ * not provided be the DOM standard.
+ */
+public class DOM2Helper extends DOMHelper
+{
+
+  /**
+   * Construct an instance.
+   */
+  public DOM2Helper(){}
+
+  /**
+   * Check node to see if it was created by a DOM implementation
+   * that this helper is intended to support. This is currently
+   * disabled, and assumes all nodes are acceptable rather than checking
+   * that they implement org.apache.xerces.dom.NodeImpl.
+   *
+   * @param node The node to be tested.
+   *
+   * @throws TransformerException if the node is not one which this
+   * DOM2Helper can support. If we return without throwing the exception,
+   * the node is compatable.
+   * @xsl.usage internal
+   */
+  public void checkNode(Node node) throws TransformerException
+  {
+
+    // if(!(node instanceof org.apache.xerces.dom.NodeImpl))
+    //  throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_XERCES_CANNOT_HANDLE_NODES, new Object[]{((Object)node).getClass()})); //"DOM2Helper can not handle nodes of type"
+    //+((Object)node).getClass());
+  }
+
+  /**
+   * Returns true if the DOM implementation handled by this helper
+   * supports the SAX ContentHandler interface.
+   *
+   * @return true (since Xerces does).
+   */
+  public boolean supportsSAX()
+  {
+    return true;
+  }
+
+  /** Field m_doc: Document Node for the document this helper is currently
+   * accessing or building
+   * @see #setDocument
+   * @see #getDocument
+   *  */
+  private Document m_doc;
+
+  /**
+   * Specify which document this helper is currently operating on.
+   * 	
+   * @param doc The DOM Document node for this document.
+   * @see #getDocument
+   */
+  public void setDocument(Document doc)
+  {
+    m_doc = doc;
+  }
+
+  /**
+   * Query which document this helper is currently operating on.
+   * 	
+   * @return The DOM Document node for this document.
+   * @see #setDocument
+   */
+  public Document getDocument()
+  {
+    return m_doc;
+  }
+
+  /**
+   * Parse an XML document.
+   *
+   * <p>Right now the Xerces DOMParser class is used.  This needs
+   * fixing, either via jaxp, or via some other, standard method.</p>
+   *
+   * <p>The application can use this method to instruct the SAX parser
+   * to begin parsing an XML document from any valid input
+   * source (a character stream, a byte stream, or a URI).</p>
+   *
+   * <p>Applications may not invoke this method while a parse is in
+   * progress (they should create a new Parser instead for each
+   * additional XML document).  Once a parse is complete, an
+   * application may reuse the same Parser object, possibly with a
+   * different input source.</p>
+   *
+   * @param source The input source for the top-level of the
+   *        XML document.
+   *
+   * @throws TransformerException if any checked exception is thrown.
+   * @xsl.usage internal
+   */
+  public void parse(InputSource source) throws TransformerException
+  {
+
+    try
+    {
+
+      // I guess I should use JAXP factory here... when it's legal.
+      // org.apache.xerces.parsers.DOMParser parser 
+      //  = new org.apache.xerces.parsers.DOMParser();
+      DocumentBuilderFactory builderFactory =
+        DocumentBuilderFactory.newInstance();
+
+      builderFactory.setNamespaceAware(true);
+      builderFactory.setValidating(true);
+
+      DocumentBuilder parser = builderFactory.newDocumentBuilder();
+
+      /*
+      // domParser.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes", getShouldExpandEntityRefs()? false : true);
+      if(m_useDOM2getNamespaceURI)
+      {
+      parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
+      parser.setFeature("http://xml.org/sax/features/namespaces", true);
+      }
+      else
+      {
+      parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
+      }
+
+      parser.setFeature("http://apache.org/xml/features/allow-java-encodings", true);
+      */
+
+      parser.setErrorHandler(
+        new org.apache.xml.utils.DefaultErrorHandler());
+
+      // if(null != m_entityResolver)
+      // {
+      // System.out.println("Setting the entity resolver.");
+      //  parser.setEntityResolver(m_entityResolver);
+      // }
+      setDocument(parser.parse(source));
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se);
+    }
+    catch (ParserConfigurationException pce)
+    {
+      throw new TransformerException(pce);
+    }
+    catch (IOException ioe)
+    {
+      throw new TransformerException(ioe);
+    }
+
+    // setDocument(((org.apache.xerces.parsers.DOMParser)parser).getDocument());
+  }
+
+  /**
+   * Given an XML ID, return the element. This requires assistance from the
+   * DOM and parser, and is meaningful only in the context of a DTD 
+   * or schema which declares attributes as being of type ID. This
+   * information may or may not be available in all parsers, may or
+   * may not be available for specific documents, and may or may not
+   * be available when validation is not turned on.
+   *
+   * @param id The ID to search for, as a String.
+   * @param doc The document to search within, as a DOM Document node.
+   * @return DOM Element node with an attribute of type ID whose value
+   * uniquely matches the requested id string, or null if there isn't
+   * such an element or if the DOM can't answer the question for other
+   * reasons.
+   */
+  public Element getElementByID(String id, Document doc)
+  {
+    return doc.getElementById(id);
+  }
+
+  /**
+   * Figure out whether node2 should be considered as being later
+   * in the document than node1, in Document Order as defined
+   * by the XPath model. This may not agree with the ordering defined
+   * by other XML applications.
+   * <p>
+   * There are some cases where ordering isn't defined, and neither are
+   * the results of this function -- though we'll generally return true.
+   * <p>
+   * TODO: Make sure this does the right thing with attribute nodes!!!
+   *
+   * @param node1 DOM Node to perform position comparison on.
+   * @param node2 DOM Node to perform position comparison on .
+   * 
+   * @return false if node2 comes before node1, otherwise return true.
+   * You can think of this as 
+   * <code>(node1.documentOrderPosition &lt;= node2.documentOrderPosition)</code>.
+   */
+  public static boolean isNodeAfter(Node node1, Node node2)
+  {
+
+    // Assume first that the nodes are DTM nodes, since discovering node 
+    // order is massivly faster for the DTM.
+    if(node1 instanceof DOMOrder && node2 instanceof DOMOrder)
+    {
+      int index1 = ((DOMOrder) node1).getUid();
+      int index2 = ((DOMOrder) node2).getUid();
+
+      return index1 <= index2;
+    }
+    else
+    {
+
+      // isNodeAfter will return true if node is after countedNode 
+      // in document order. The base isNodeAfter is sloooow (relatively).
+      return DOMHelper.isNodeAfter(node1, node2);
+    }
+  }
+
+  /**
+   * Get the XPath-model parent of a node.  This version takes advantage
+   * of the DOM Level 2 Attr.ownerElement() method; the base version we
+   * would otherwise inherit is prepared to fall back on exhaustively
+   * walking the document to find an Attr's parent.
+   *
+   * @param node Node to be examined
+   *
+   * @return the DOM parent of the input node, if there is one, or the
+   * ownerElement if the input node is an Attr, or null if the node is
+   * a Document, a DocumentFragment, or an orphan.
+   */
+  public static Node getParentOfNode(Node node)
+  {
+          Node parent=node.getParentNode();
+          if(parent==null && (Node.ATTRIBUTE_NODE == node.getNodeType()) )
+           parent=((Attr) node).getOwnerElement();
+          return parent;
+  }
+
+  /**
+   * Returns the local name of the given node, as defined by the
+   * XML Namespaces specification. This is prepared to handle documents
+   * built using DOM Level 1 methods by falling back upon explicitly
+   * parsing the node name.
+   *
+   * @param n Node to be examined
+   *
+   * @return String containing the local name, or null if the node
+   * was not assigned a Namespace.
+   */
+  public String getLocalNameOfNode(Node n)
+  {
+
+    String name = n.getLocalName();
+
+    return (null == name) ? super.getLocalNameOfNode(n) : name;
+  }
+
+  /**
+   * Returns the Namespace Name (Namespace URI) for the given node.
+   * In a Level 2 DOM, you can ask the node itself. Note, however, that
+   * doing so conflicts with our decision in getLocalNameOfNode not
+   * to trust the that the DOM was indeed created using the Level 2
+   * methods. If Level 1 methods were used, these two functions will
+   * disagree with each other.
+   * <p>
+   * TODO: Reconcile with getLocalNameOfNode.
+   *
+   * @param n Node to be examined
+   *
+   * @return String containing the Namespace URI bound to this DOM node
+   * at the time the Node was created.
+   */
+  public String getNamespaceOfNode(Node n)
+  {
+    return n.getNamespaceURI();
+  }
+
+  /** Field m_useDOM2getNamespaceURI is a compile-time flag which
+   *  gates some of the parser options used to build a DOM -- but 
+   * that code is commented out at this time and nobody else
+   * references it, so I've commented this out as well. */
+  //private boolean m_useDOM2getNamespaceURI = false;
+}
diff --git a/src/main/java/org/apache/xml/utils/DOMBuilder.java b/src/main/java/org/apache/xml/utils/DOMBuilder.java
new file mode 100644
index 0000000..3475ca1
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/DOMBuilder.java
@@ -0,0 +1,795 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOMBuilder.java 472634 2006-11-08 20:43:55Z jycli $
+ */
+package org.apache.xml.utils;
+
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.w3c.dom.CDATASection;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.ext.LexicalHandler;
+/**
+ * This class takes SAX events (in addition to some extra events
+ * that SAX doesn't handle yet) and adds the result to a document
+ * or document fragment.
+ * @xsl.usage general
+ */
+public class DOMBuilder
+        implements ContentHandler, LexicalHandler
+{
+
+  /** Root document          */
+  public Document m_doc;
+
+  /** Current node           */
+  protected Node m_currentNode = null;
+  
+  /** The root node          */
+  protected Node m_root = null;
+  
+  /** The next sibling node  */
+  protected Node m_nextSibling = null;
+
+  /** First node of document fragment or null if not a DocumentFragment     */
+  public DocumentFragment m_docFrag = null;
+
+  /** Vector of element nodes          */
+  protected Stack m_elemStack = new Stack();
+  
+  /** Namespace support */
+  protected Vector m_prefixMappings = new Vector();
+  
+  /**
+   * DOMBuilder instance constructor... it will add the DOM nodes
+   * to the document fragment.
+   *
+   * @param doc Root document
+   * @param node Current node
+   */
+  public DOMBuilder(Document doc, Node node)
+  {
+    m_doc = doc;
+    m_currentNode = m_root = node;
+    
+    if (node instanceof Element)
+      m_elemStack.push(node);
+  }
+
+  /**
+   * DOMBuilder instance constructor... it will add the DOM nodes
+   * to the document fragment.
+   *
+   * @param doc Root document
+   * @param docFrag Document fragment
+   */
+  public DOMBuilder(Document doc, DocumentFragment docFrag)
+  {
+    m_doc = doc;
+    m_docFrag = docFrag;
+  }
+
+  /**
+   * DOMBuilder instance constructor... it will add the DOM nodes
+   * to the document.
+   *
+   * @param doc Root document
+   */
+  public DOMBuilder(Document doc)
+  {
+    m_doc = doc;
+  }
+
+  /**
+   * Get the root document or DocumentFragment of the DOM being created.
+   *
+   * @return The root document or document fragment if not null
+   */
+  public Node getRootDocument()
+  {
+    return (null != m_docFrag) ? (Node) m_docFrag : (Node) m_doc;
+  }
+  
+  /**
+   * Get the root node of the DOM tree.
+   */
+  public Node getRootNode()
+  {
+    return m_root;
+  }
+  
+  /**
+   * Get the node currently being processed.
+   *
+   * @return the current node being processed
+   */
+  public Node getCurrentNode()
+  {
+    return m_currentNode;
+  }
+  
+  /**
+   * Set the next sibling node, which is where the result nodes 
+   * should be inserted before.
+   * 
+   * @param nextSibling the next sibling node.
+   */
+  public void setNextSibling(Node nextSibling)
+  {
+    m_nextSibling = nextSibling;
+  }
+  
+  /**
+   * Return the next sibling node.
+   * 
+   * @return the next sibling node.
+   */
+  public Node getNextSibling()
+  {
+    return m_nextSibling;
+  }
+
+  /**
+   * Return null since there is no Writer for this class.
+   *
+   * @return null
+   */
+  public java.io.Writer getWriter()
+  {
+    return null;
+  }
+
+  /**
+   * Append a node to the current container.
+   *
+   * @param newNode New node to append
+   */
+  protected void append(Node newNode) throws org.xml.sax.SAXException
+  {
+
+    Node currentNode = m_currentNode;
+
+    if (null != currentNode)
+    {
+      if (currentNode == m_root && m_nextSibling != null)
+        currentNode.insertBefore(newNode, m_nextSibling);
+      else
+        currentNode.appendChild(newNode);
+
+      // System.out.println(newNode.getNodeName());
+    }
+    else if (null != m_docFrag)
+    {
+      if (m_nextSibling != null)
+        m_docFrag.insertBefore(newNode, m_nextSibling);
+      else
+        m_docFrag.appendChild(newNode);
+    }
+    else
+    {
+      boolean ok = true;
+      short type = newNode.getNodeType();
+
+      if (type == Node.TEXT_NODE)
+      {
+        String data = newNode.getNodeValue();
+
+        if ((null != data) && (data.trim().length() > 0))
+        {
+          throw new org.xml.sax.SAXException(
+            XMLMessages.createXMLMessage(
+              XMLErrorResources.ER_CANT_OUTPUT_TEXT_BEFORE_DOC, null));  //"Warning: can't output text before document element!  Ignoring...");
+        }
+
+        ok = false;
+      }
+      else if (type == Node.ELEMENT_NODE)
+      {
+        if (m_doc.getDocumentElement() != null)
+        {
+          ok = false;
+          
+          throw new org.xml.sax.SAXException(
+            XMLMessages.createXMLMessage(
+              XMLErrorResources.ER_CANT_HAVE_MORE_THAN_ONE_ROOT, null));  //"Can't have more than one root on a DOM!");
+        }
+      }
+
+      if (ok)
+      {
+        if (m_nextSibling != null)
+          m_doc.insertBefore(newNode, m_nextSibling);
+        else
+          m_doc.appendChild(newNode);
+      }
+    }
+  }
+
+  /**
+   * Receive an object for locating the origin of SAX document events.
+   *
+   * <p>SAX parsers are strongly encouraged (though not absolutely
+   * required) to supply a locator: if it does so, it must supply
+   * the locator to the application by invoking this method before
+   * invoking any of the other methods in the ContentHandler
+   * interface.</p>
+   *
+   * <p>The locator allows the application to determine the end
+   * position of any document-related event, even if the parser is
+   * not reporting an error.  Typically, the application will
+   * use this information for reporting its own errors (such as
+   * character content that does not match an application's
+   * business rules).  The information returned by the locator
+   * is probably not sufficient for use with a search engine.</p>
+   *
+   * <p>Note that the locator will return correct information only
+   * during the invocation of the events in this interface.  The
+   * application should not attempt to use it at any other time.</p>
+   *
+   * @param locator An object that can return the location of
+   *                any SAX document event.
+   * @see org.xml.sax.Locator
+   */
+  public void setDocumentLocator(Locator locator)
+  {
+
+    // No action for the moment.
+  }
+
+  /**
+   * Receive notification of the beginning of a document.
+   *
+   * <p>The SAX parser will invoke this method only once, before any
+   * other methods in this interface or in DTDHandler (except for
+   * setDocumentLocator).</p>
+   */
+  public void startDocument() throws org.xml.sax.SAXException
+  {
+
+    // No action for the moment.
+  }
+
+  /**
+   * Receive notification of the end of a document.
+   *
+   * <p>The SAX parser will invoke this method only once, and it will
+   * be the last method invoked during the parse.  The parser shall
+   * not invoke this method until it has either abandoned parsing
+   * (because of an unrecoverable error) or reached the end of
+   * input.</p>
+   */
+  public void endDocument() throws org.xml.sax.SAXException
+  {
+
+    // No action for the moment.
+  }
+
+  /**
+   * Receive notification of the beginning of an element.
+   *
+   * <p>The Parser will invoke this method at the beginning of every
+   * element in the XML document; there will be a corresponding
+   * endElement() event for every startElement() event (even when the
+   * element is empty). All of the element's content will be
+   * reported, in order, before the corresponding endElement()
+   * event.</p>
+   *
+   * <p>If the element name has a namespace prefix, the prefix will
+   * still be attached.  Note that the attribute list provided will
+   * contain only attributes with explicit values (specified or
+   * defaulted): #IMPLIED attributes will be omitted.</p>
+   *
+   *
+   * @param ns The namespace of the node
+   * @param localName The local part of the qualified name
+   * @param name The element name.
+   * @param atts The attributes attached to the element, if any.
+   * @see #endElement
+   * @see org.xml.sax.Attributes
+   */
+  public void startElement(
+          String ns, String localName, String name, Attributes atts)
+            throws org.xml.sax.SAXException
+  {
+
+    Element elem;
+
+	// Note that the namespace-aware call must be used to correctly
+	// construct a Level 2 DOM, even for non-namespaced nodes.
+    if ((null == ns) || (ns.length() == 0))
+      elem = m_doc.createElementNS(null,name);
+    else
+      elem = m_doc.createElementNS(ns, name);
+
+    append(elem);
+
+    try
+    {
+      int nAtts = atts.getLength();
+
+      if (0 != nAtts)
+      {
+        for (int i = 0; i < nAtts; i++)
+        {
+
+          //System.out.println("type " + atts.getType(i) + " name " + atts.getLocalName(i) );
+          // First handle a possible ID attribute
+          if (atts.getType(i).equalsIgnoreCase("ID"))
+            setIDAttribute(atts.getValue(i), elem);
+
+          String attrNS = atts.getURI(i);
+
+          if("".equals(attrNS))
+            attrNS = null; // DOM represents no-namespace as null
+
+          // System.out.println("attrNS: "+attrNS+", localName: "+atts.getQName(i)
+          //                   +", qname: "+atts.getQName(i)+", value: "+atts.getValue(i));
+          // Crimson won't let us set an xmlns: attribute on the DOM.
+          String attrQName = atts.getQName(i);
+
+          // In SAX, xmlns[:] attributes have an empty namespace, while in DOM they 
+          // should have the xmlns namespace
+          if (attrQName.startsWith("xmlns:") || attrQName.equals("xmlns")) {
+            attrNS = "http://www.w3.org/2000/xmlns/";
+          }
+
+          // ALWAYS use the DOM Level 2 call!
+          elem.setAttributeNS(attrNS,attrQName, atts.getValue(i));
+        }
+      }
+      
+      /*
+       * Adding namespace nodes to the DOM tree;
+       */
+      int nDecls = m_prefixMappings.size();
+      
+      String prefix, declURL;
+      
+      for (int i = 0; i < nDecls; i += 2)
+      {
+        prefix = (String) m_prefixMappings.elementAt(i);
+
+        if (prefix == null)
+          continue;
+
+        declURL = (String) m_prefixMappings.elementAt(i + 1);
+
+        elem.setAttributeNS("http://www.w3.org/2000/xmlns/", prefix, declURL);
+      }
+      
+      m_prefixMappings.clear();
+    
+      // append(elem);
+
+      m_elemStack.push(elem);
+
+      m_currentNode = elem;
+
+      // append(elem);
+    }
+    catch(java.lang.Exception de)
+    {
+      // de.printStackTrace();
+      throw new org.xml.sax.SAXException(de);
+    }
+
+  }
+
+  /**
+
+
+
+   * Receive notification of the end of an element.
+   *
+   * <p>The SAX parser will invoke this method at the end of every
+   * element in the XML document; there will be a corresponding
+   * startElement() event for every endElement() event (even when the
+   * element is empty).</p>
+   *
+   * <p>If the element name has a namespace prefix, the prefix will
+   * still be attached to the name.</p>
+   *
+   *
+   * @param ns the namespace of the element
+   * @param localName The local part of the qualified name of the element
+   * @param name The element name
+   */
+  public void endElement(String ns, String localName, String name)
+          throws org.xml.sax.SAXException
+  {
+    m_elemStack.pop();
+    m_currentNode = m_elemStack.isEmpty() ? null : (Node)m_elemStack.peek();
+  }
+
+  /**
+   * Set an ID string to node association in the ID table.
+   *
+   * @param id The ID string.
+   * @param elem The associated ID.
+   */
+  public void setIDAttribute(String id, Element elem)
+  {
+
+    // Do nothing. This method is meant to be overiden.
+  }
+
+  /**
+   * Receive notification of character data.
+   *
+   * <p>The Parser will call this method to report each chunk of
+   * character data.  SAX parsers may return all contiguous character
+   * data in a single chunk, or they may split it into several
+   * chunks; however, all of the characters in any single event
+   * must come from the same external entity, so that the Locator
+   * provides useful information.</p>
+   *
+   * <p>The application must not attempt to read from the array
+   * outside of the specified range.</p>
+   *
+   * <p>Note that some parsers will report whitespace using the
+   * ignorableWhitespace() method rather than this one (validating
+   * parsers must do so).</p>
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @see #ignorableWhitespace
+   * @see org.xml.sax.Locator
+   */
+  public void characters(char ch[], int start, int length) throws org.xml.sax.SAXException
+  {
+    if(isOutsideDocElem()
+       && org.apache.xml.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
+      return;  // avoid DOM006 Hierarchy request error
+
+    if (m_inCData)
+    {
+      cdata(ch, start, length);
+
+      return;
+    }
+
+    String s = new String(ch, start, length);
+    Node childNode;
+    childNode =  m_currentNode != null ? m_currentNode.getLastChild(): null;
+    if( childNode != null && childNode.getNodeType() == Node.TEXT_NODE ){
+       ((Text)childNode).appendData(s);
+    }
+    else{
+       Text text = m_doc.createTextNode(s);
+       append(text);
+    }
+  }
+
+  /**
+   * If available, when the disable-output-escaping attribute is used,
+   * output raw text without escaping.  A PI will be inserted in front
+   * of the node with the name "lotusxsl-next-is-raw" and a value of
+   * "formatter-to-dom".
+   *
+   * @param ch Array containing the characters
+   * @param start Index to start of characters in the array
+   * @param length Number of characters in the array
+   */
+  public void charactersRaw(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+    if(isOutsideDocElem()
+       && org.apache.xml.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
+      return;  // avoid DOM006 Hierarchy request error
+
+
+    String s = new String(ch, start, length);
+
+    append(m_doc.createProcessingInstruction("xslt-next-is-raw",
+                                             "formatter-to-dom"));
+    append(m_doc.createTextNode(s));
+  }
+
+  /**
+   * Report the beginning of an entity.
+   *
+   * The start and end of the document entity are not reported.
+   * The start and end of the external DTD subset are reported
+   * using the pseudo-name "[dtd]".  All other events must be
+   * properly nested within start/end entity events.
+   *
+   * @param name The name of the entity.  If it is a parameter
+   *        entity, the name will begin with '%'.
+   * @see #endEntity
+   * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
+   * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
+   */
+  public void startEntity(String name) throws org.xml.sax.SAXException
+  {
+
+    // Almost certainly the wrong behavior...
+    // entityReference(name);
+  }
+
+  /**
+   * Report the end of an entity.
+   *
+   * @param name The name of the entity that is ending.
+   * @see #startEntity
+   */
+  public void endEntity(String name) throws org.xml.sax.SAXException{}
+
+  /**
+   * Receive notivication of a entityReference.
+   *
+   * @param name name of the entity reference
+   */
+  public void entityReference(String name) throws org.xml.sax.SAXException
+  {
+    append(m_doc.createEntityReference(name));
+  }
+
+  /**
+   * Receive notification of ignorable whitespace in element content.
+   *
+   * <p>Validating Parsers must use this method to report each chunk
+   * of ignorable whitespace (see the W3C XML 1.0 recommendation,
+   * section 2.10): non-validating parsers may also use this method
+   * if they are capable of parsing and using content models.</p>
+   *
+   * <p>SAX parsers may return all contiguous whitespace in a single
+   * chunk, or they may split it into several chunks; however, all of
+   * the characters in any single event must come from the same
+   * external entity, so that the Locator provides useful
+   * information.</p>
+   *
+   * <p>The application must not attempt to read from the array
+   * outside of the specified range.</p>
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @see #characters
+   */
+  public void ignorableWhitespace(char ch[], int start, int length)
+          throws org.xml.sax.SAXException
+  {
+    if(isOutsideDocElem())
+      return;  // avoid DOM006 Hierarchy request error
+
+    String s = new String(ch, start, length);
+
+    append(m_doc.createTextNode(s));
+  }
+
+  /**
+   * Tell if the current node is outside the document element.
+   *
+   * @return true if the current node is outside the document element.
+   */
+   private boolean isOutsideDocElem()
+   {
+      return (null == m_docFrag) && m_elemStack.size() == 0 && (null == m_currentNode || m_currentNode.getNodeType() == Node.DOCUMENT_NODE);
+   }
+
+  /**
+   * Receive notification of a processing instruction.
+   *
+   * <p>The Parser will invoke this method once for each processing
+   * instruction found: note that processing instructions may occur
+   * before or after the main document element.</p>
+   *
+   * <p>A SAX parser should never report an XML declaration (XML 1.0,
+   * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
+   * using this method.</p>
+   *
+   * @param target The processing instruction target.
+   * @param data The processing instruction data, or null if
+   *        none was supplied.
+   */
+  public void processingInstruction(String target, String data)
+          throws org.xml.sax.SAXException
+  {
+    append(m_doc.createProcessingInstruction(target, data));
+  }
+
+  /**
+   * Report an XML comment anywhere in the document.
+   *
+   * This callback will be used for comments inside or outside the
+   * document element, including comments in the external DTD
+   * subset (if read).
+   *
+   * @param ch An array holding the characters in the comment.
+   * @param start The starting position in the array.
+   * @param length The number of characters to use from the array.
+   */
+  public void comment(char ch[], int start, int length) throws org.xml.sax.SAXException
+  {
+    append(m_doc.createComment(new String(ch, start, length)));
+  }
+
+  /** Flag indicating that we are processing a CData section          */
+  protected boolean m_inCData = false;
+
+  /**
+   * Report the start of a CDATA section.
+   *
+   * @see #endCDATA
+   */
+  public void startCDATA() throws org.xml.sax.SAXException
+  {
+    m_inCData = true;
+    append(m_doc.createCDATASection(""));
+  }
+
+  /**
+   * Report the end of a CDATA section.
+   *
+   * @see #startCDATA
+   */
+  public void endCDATA() throws org.xml.sax.SAXException
+  {
+    m_inCData = false;
+  }
+
+  /**
+   * Receive notification of cdata.
+   *
+   * <p>The Parser will call this method to report each chunk of
+   * character data.  SAX parsers may return all contiguous character
+   * data in a single chunk, or they may split it into several
+   * chunks; however, all of the characters in any single event
+   * must come from the same external entity, so that the Locator
+   * provides useful information.</p>
+   *
+   * <p>The application must not attempt to read from the array
+   * outside of the specified range.</p>
+   *
+   * <p>Note that some parsers will report whitespace using the
+   * ignorableWhitespace() method rather than this one (validating
+   * parsers must do so).</p>
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @see #ignorableWhitespace
+   * @see org.xml.sax.Locator
+   */
+  public void cdata(char ch[], int start, int length) throws org.xml.sax.SAXException
+  {
+    if(isOutsideDocElem()
+       && org.apache.xml.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
+      return;  // avoid DOM006 Hierarchy request error
+
+    String s = new String(ch, start, length);
+
+    CDATASection section  =(CDATASection) m_currentNode.getLastChild();
+    section.appendData(s);
+  }
+
+  /**
+   * Report the start of DTD declarations, if any.
+   *
+   * Any declarations are assumed to be in the internal subset
+   * unless otherwise indicated.
+   *
+   * @param name The document type name.
+   * @param publicId The declared public identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @param systemId The declared system identifier for the
+   *        external DTD subset, or null if none was declared.
+   * @see #endDTD
+   * @see #startEntity
+   */
+  public void startDTD(String name, String publicId, String systemId)
+          throws org.xml.sax.SAXException
+  {
+
+    // Do nothing for now.
+  }
+
+  /**
+   * Report the end of DTD declarations.
+   *
+   * @see #startDTD
+   */
+  public void endDTD() throws org.xml.sax.SAXException
+  {
+
+    // Do nothing for now.
+  }
+
+  /**
+   * Begin the scope of a prefix-URI Namespace mapping.
+   *
+   * <p>The information from this event is not necessary for
+   * normal Namespace processing: the SAX XML reader will
+   * automatically replace prefixes for element and attribute
+   * names when the http://xml.org/sax/features/namespaces
+   * feature is true (the default).</p>
+   *
+   * <p>There are cases, however, when applications need to
+   * use prefixes in character data or in attribute values,
+   * where they cannot safely be expanded automatically; the
+   * start/endPrefixMapping event supplies the information
+   * to the application to expand prefixes in those contexts
+   * itself, if necessary.</p>
+   *
+   * <p>Note that start/endPrefixMapping events are not
+   * guaranteed to be properly nested relative to each-other:
+   * all startPrefixMapping events will occur before the
+   * corresponding startElement event, and all endPrefixMapping
+   * events will occur after the corresponding endElement event,
+   * but their order is not guaranteed.</p>
+   *
+   * @param prefix The Namespace prefix being declared.
+   * @param uri The Namespace URI the prefix is mapped to.
+   * @see #endPrefixMapping
+   * @see #startElement
+   */
+  public void startPrefixMapping(String prefix, String uri)
+          throws org.xml.sax.SAXException
+  {
+	      if(null == prefix || prefix.equals(""))
+	        prefix = "xmlns";
+	      else prefix = "xmlns:"+prefix;
+	      m_prefixMappings.addElement(prefix);
+	      m_prefixMappings.addElement(uri); 
+  }
+
+  /**
+   * End the scope of a prefix-URI mapping.
+   *
+   * <p>See startPrefixMapping for details.  This event will
+   * always occur after the corresponding endElement event,
+   * but the order of endPrefixMapping events is not otherwise
+   * guaranteed.</p>
+   *
+   * @param prefix The prefix that was being mapping.
+   * @see #startPrefixMapping
+   * @see #endElement
+   */
+  public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException{}
+
+  /**
+   * Receive notification of a skipped entity.
+   *
+   * <p>The Parser will invoke this method once for each entity
+   * skipped.  Non-validating processors may skip entities if they
+   * have not seen the declarations (because, for example, the
+   * entity was declared in an external DTD subset).  All processors
+   * may skip external entities, depending on the values of the
+   * http://xml.org/sax/features/external-general-entities and the
+   * http://xml.org/sax/features/external-parameter-entities
+   * properties.</p>
+   *
+   * @param name The name of the skipped entity.  If it is a
+   *        parameter entity, the name will begin with '%'.
+   */
+  public void skippedEntity(String name) throws org.xml.sax.SAXException{}
+}
diff --git a/src/main/java/org/apache/xml/utils/DOMHelper.java b/src/main/java/org/apache/xml/utils/DOMHelper.java
new file mode 100644
index 0000000..53d0adc
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/DOMHelper.java
@@ -0,0 +1,1341 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOMHelper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.xml.dtm.ref.DTMNodeProxy;
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Entity;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+/**
+ * @deprecated Since the introduction of the DTM, this class will be removed.
+ * This class provides a front-end to DOM implementations, providing
+ * a number of utility functions that either aren't yet standardized
+ * by the DOM spec or that are defined in optional DOM modules and
+ * hence may not be present in all DOMs.
+ */
+public class DOMHelper
+{
+
+  /**
+   * DOM Level 1 did not have a standard mechanism for creating a new
+   * Document object. This function provides a DOM-implementation-independent
+   * abstraction for that for that concept. It's typically used when 
+   * outputting a new DOM as the result of an operation.
+   * <p>
+   * TODO: This isn't directly compatable with DOM Level 2. 
+   * The Level 2 createDocument call also creates the root 
+   * element, and thus requires that you know what that element will be
+   * before creating the Document. We should think about whether we want
+   * to change this code, and the callers, so we can use the DOM's own 
+   * method. (It's also possible that DOM Level 3 may relax this
+   * sequence, but you may give up some intelligence in the DOM by
+   * doing so; the intent was that knowing the document type and root
+   * element might let the DOM automatically switch to a specialized
+   * subclass for particular kinds of documents.)
+   *
+   * @param isSecureProcessing state of the secure processing feature.
+   * @return The newly created DOM Document object, with no children, or
+   * null if we can't find a DOM implementation that permits creating
+   * new empty Documents.
+   */
+  public static Document createDocument(boolean isSecureProcessing)
+  {
+
+    try
+    {
+
+      // Use an implementation of the JAVA API for XML Parsing 1.0 to
+      // create a DOM Document node to contain the result.
+      DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
+
+      dfactory.setNamespaceAware(true);
+      // BEGIN android-removed
+      //     If set, DocumentBuilderFactoryImpl.newDocumentBuilder() fails
+      //     because we haven't implemented validation
+      // dfactory.setValidating(true);
+      // BEGIN android-removed
+
+      // BEGIN android-removed
+      //     We haven't implemented secure processing
+      // if (isSecureProcessing)
+      // {
+      //   try
+      //   {
+      //     dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+      //   }
+      //   catch (ParserConfigurationException pce) {}
+      // }
+      // END android-removed
+      
+      DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
+      Document outNode = docBuilder.newDocument();
+
+      return outNode;
+    }
+    catch (ParserConfigurationException pce)
+    {
+      throw new RuntimeException(
+        XMLMessages.createXMLMessage(
+          XMLErrorResources.ER_CREATEDOCUMENT_NOT_SUPPORTED, null));  //"createDocument() not supported in XPathContext!");
+
+      // return null;
+    }
+  }
+
+  /**
+   * DOM Level 1 did not have a standard mechanism for creating a new
+   * Document object. This function provides a DOM-implementation-independent
+   * abstraction for that for that concept. It's typically used when 
+   * outputting a new DOM as the result of an operation.
+   *
+   * @return The newly created DOM Document object, with no children, or
+   * null if we can't find a DOM implementation that permits creating
+   * new empty Documents.
+   */
+  public static Document createDocument()
+  {
+    return createDocument(false);
+  }
+  
+  /**
+   * Tells, through the combination of the default-space attribute
+   * on xsl:stylesheet, xsl:strip-space, xsl:preserve-space, and the
+   * xml:space attribute, whether or not extra whitespace should be stripped
+   * from the node.  Literal elements from template elements should
+   * <em>not</em> be tested with this function.
+   * @param textNode A text node from the source tree.
+   * @return true if the text node should be stripped of extra whitespace.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage advanced
+   */
+  public boolean shouldStripSourceNode(Node textNode)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // return (null == m_envSupport) ? false : m_envSupport.shouldStripSourceNode(textNode);
+    return false;
+  }
+
+  /**
+   * Supports the XPath function GenerateID by returning a unique
+   * identifier string for any given DOM Node.
+   * <p>
+   * Warning: The base implementation uses the Node object's hashCode(),
+   * which is NOT guaranteed to be unique. If that method hasn't been
+   * overridden in this DOM ipmlementation, most Java implementions will
+   * derive it from the object's address and should be OK... but if
+   * your DOM uses a different definition of hashCode (eg hashing the
+   * contents of the subtree), or if your DOM may have multiple objects
+   * that represent a single Node in the data structure (eg via proxying),
+   * you may need to find another way to assign a unique identifier.
+   * <p>
+   * Also, be aware that if nodes are destroyed and recreated, there is
+   * an open issue regarding whether an ID may be reused. Currently
+   * we're assuming that the input document is stable for the duration
+   * of the XPath/XSLT operation, so this shouldn't arise in this context.
+   * <p>
+   * (DOM Level 3 is investigating providing a unique node "key", but
+   * that won't help Level 1 and Level 2 implementations.)
+   *
+   * @param node whose identifier you want to obtain
+   *
+   * @return a string which should be different for every Node object.
+   */
+  public String getUniqueID(Node node)
+  {
+    return "N" + Integer.toHexString(node.hashCode()).toUpperCase();
+  }
+
+  /**
+   * Figure out whether node2 should be considered as being later
+   * in the document than node1, in Document Order as defined
+   * by the XPath model. This may not agree with the ordering defined
+   * by other XML applications.
+   * <p>
+   * There are some cases where ordering isn't defined, and neither are
+   * the results of this function -- though we'll generally return true.
+   * 
+   * TODO: Make sure this does the right thing with attribute nodes!!!
+   *
+   * @param node1 DOM Node to perform position comparison on.
+   * @param node2 DOM Node to perform position comparison on .
+   * 
+   * @return false if node2 comes before node1, otherwise return true.
+   * You can think of this as 
+   * <code>(node1.documentOrderPosition &lt;= node2.documentOrderPosition)</code>.
+   */
+  public static boolean isNodeAfter(Node node1, Node node2)
+  {
+    if (node1 == node2 || isNodeTheSame(node1, node2))
+      return true;
+
+        // Default return value, if there is no defined ordering
+    boolean isNodeAfter = true;
+        
+    Node parent1 = getParentOfNode(node1);
+    Node parent2 = getParentOfNode(node2);          
+
+    // Optimize for most common case
+    if (parent1 == parent2 || isNodeTheSame(parent1, parent2))  // then we know they are siblings
+    {
+      if (null != parent1)
+        isNodeAfter = isNodeAfterSibling(parent1, node1, node2);
+      else
+      {
+                  // If both parents are null, ordering is not defined.
+                  // We're returning a value in lieu of throwing an exception.
+                  // Not a case we expect to arise in XPath, but beware if you
+                  // try to reuse this method.
+                  
+                  // We can just fall through in this case, which allows us
+                  // to hit the debugging code at the end of the function.
+          //return isNodeAfter;
+      }
+    }
+    else
+    {
+
+      // General strategy: Figure out the lengths of the two 
+      // ancestor chains, reconcile the lengths, and look for
+          // the lowest common ancestor. If that ancestor is one of
+          // the nodes being compared, it comes before the other.
+      // Otherwise perform a sibling compare. 
+                //
+                // NOTE: If no common ancestor is found, ordering is undefined
+                // and we return the default value of isNodeAfter.
+                
+      // Count parents in each ancestor chain
+      int nParents1 = 2, nParents2 = 2;  // include node & parent obtained above
+
+      while (parent1 != null)
+      {
+        nParents1++;
+
+        parent1 = getParentOfNode(parent1);
+      }
+
+      while (parent2 != null)
+      {
+        nParents2++;
+
+        parent2 = getParentOfNode(parent2);
+      }
+
+          // Initially assume scan for common ancestor starts with
+          // the input nodes.
+      Node startNode1 = node1, startNode2 = node2;
+
+      // If one ancestor chain is longer, adjust its start point
+          // so we're comparing at the same depths
+      if (nParents1 < nParents2)
+      {
+        // Adjust startNode2 to depth of startNode1
+        int adjust = nParents2 - nParents1;
+
+        for (int i = 0; i < adjust; i++)
+        {
+          startNode2 = getParentOfNode(startNode2);
+        }
+      }
+      else if (nParents1 > nParents2)
+      {
+        // adjust startNode1 to depth of startNode2
+        int adjust = nParents1 - nParents2;
+
+        for (int i = 0; i < adjust; i++)
+        {
+          startNode1 = getParentOfNode(startNode1);
+        }
+      }
+
+      Node prevChild1 = null, prevChild2 = null;  // so we can "back up"
+
+      // Loop up the ancestor chain looking for common parent
+      while (null != startNode1)
+      {
+        if (startNode1 == startNode2 || isNodeTheSame(startNode1, startNode2))  // common parent?
+        {
+          if (null == prevChild1)  // first time in loop?
+          {
+
+            // Edge condition: one is the ancestor of the other.
+            isNodeAfter = (nParents1 < nParents2) ? true : false;
+
+            break;  // from while loop
+          }
+          else 
+          {
+                        // Compare ancestors below lowest-common as siblings
+            isNodeAfter = isNodeAfterSibling(startNode1, prevChild1,
+                                             prevChild2);
+
+            break;  // from while loop
+          }
+        }  // end if(startNode1 == startNode2)
+
+                // Move up one level and try again
+        prevChild1 = startNode1;
+        startNode1 = getParentOfNode(startNode1);
+        prevChild2 = startNode2;
+        startNode2 = getParentOfNode(startNode2);
+      }  // end while(parents exist to examine)
+    }  // end big else (not immediate siblings)
+        
+        // WARNING: The following diagnostic won't report the early
+        // "same node" case. Fix if/when needed.
+        
+    /* -- please do not remove... very useful for diagnostics --
+    System.out.println("node1 = "+node1.getNodeName()+"("+node1.getNodeType()+")"+
+    ", node2 = "+node2.getNodeName()
+    +"("+node2.getNodeType()+")"+
+    ", isNodeAfter = "+isNodeAfter); */
+    return isNodeAfter;
+  }  // end isNodeAfter(Node node1, Node node2)
+
+  /**
+   * Use DTMNodeProxy to determine whether two nodes are the same.
+   * 
+   * @param node1 The first DOM node to compare.
+   * @param node2 The second DOM node to compare.
+   * @return true if the two nodes are the same.
+   */
+  public static boolean isNodeTheSame(Node node1, Node node2)
+  {
+    if (node1 instanceof DTMNodeProxy && node2 instanceof DTMNodeProxy)
+      return ((DTMNodeProxy)node1).equals((DTMNodeProxy)node2);
+    else
+      return (node1 == node2);
+  }
+
+  /**
+   * Figure out if child2 is after child1 in document order.
+   * <p>
+   * Warning: Some aspects of "document order" are not well defined.
+   * For example, the order of attributes is considered
+   * meaningless in XML, and the order reported by our model will
+   * be consistant for a given invocation but may not 
+   * match that of either the source file or the serialized output.
+   * 
+   * @param parent Must be the parent of both child1 and child2.
+   * @param child1 Must be the child of parent and not equal to child2.
+   * @param child2 Must be the child of parent and not equal to child1.
+   * @return true if child 2 is after child1 in document order.
+   */
+  private static boolean isNodeAfterSibling(Node parent, Node child1,
+                                            Node child2)
+  {
+
+    boolean isNodeAfterSibling = false;
+    short child1type = child1.getNodeType();
+    short child2type = child2.getNodeType();
+
+    if ((Node.ATTRIBUTE_NODE != child1type)
+            && (Node.ATTRIBUTE_NODE == child2type))
+    {
+
+      // always sort attributes before non-attributes.
+      isNodeAfterSibling = false;
+    }
+    else if ((Node.ATTRIBUTE_NODE == child1type)
+             && (Node.ATTRIBUTE_NODE != child2type))
+    {
+
+      // always sort attributes before non-attributes.
+      isNodeAfterSibling = true;
+    }
+    else if (Node.ATTRIBUTE_NODE == child1type)
+    {
+      NamedNodeMap children = parent.getAttributes();
+      int nNodes = children.getLength();
+      boolean found1 = false, found2 = false;
+
+          // Count from the start until we find one or the other.
+      for (int i = 0; i < nNodes; i++)
+      {
+        Node child = children.item(i);
+
+        if (child1 == child || isNodeTheSame(child1, child))
+        {
+          if (found2)
+          {
+            isNodeAfterSibling = false;
+
+            break;
+          }
+
+          found1 = true;
+        }
+        else if (child2 == child || isNodeTheSame(child2, child))
+        {
+          if (found1)
+          {
+            isNodeAfterSibling = true;
+
+            break;
+          }
+
+          found2 = true;
+        }
+      }
+    }
+    else
+    {
+                // TODO: Check performance of alternate solution:
+                // There are two choices here: Count from the start of
+                // the document until we find one or the other, or count
+                // from one until we find or fail to find the other.
+                // Either can wind up scanning all the siblings in the worst
+                // case, which on a wide document can be a lot of work but
+                // is more typically is a short list. 
+                // Scanning from the start involves two tests per iteration,
+                // but it isn't clear that scanning from the middle doesn't
+                // yield more iterations on average. 
+                // We should run some testcases.
+      Node child = parent.getFirstChild();
+      boolean found1 = false, found2 = false;
+
+      while (null != child)
+      {
+
+        // Node child = children.item(i);
+        if (child1 == child || isNodeTheSame(child1, child))
+        {
+          if (found2)
+          {
+            isNodeAfterSibling = false;
+
+            break;
+          }
+
+          found1 = true;
+        }
+        else if (child2 == child || isNodeTheSame(child2, child))
+        {
+          if (found1)
+          {
+            isNodeAfterSibling = true;
+
+            break;
+          }
+
+          found2 = true;
+        }
+
+        child = child.getNextSibling();
+      }
+    }
+
+    return isNodeAfterSibling;
+  }  // end isNodeAfterSibling(Node parent, Node child1, Node child2)
+
+  //==========================================================
+  // SECTION: Namespace resolution
+  //==========================================================
+
+  /**
+   * Get the depth level of this node in the tree (equals 1 for
+   * a parentless node).
+   *
+   * @param n Node to be examined.
+   * @return the number of ancestors, plus one
+   * @xsl.usage internal
+   */
+  public short getLevel(Node n)
+  {
+
+    short level = 1;
+
+    while (null != (n = getParentOfNode(n)))
+    {
+      level++;
+    }
+
+    return level;
+  }
+
+  /**
+   * Given an XML Namespace prefix and a context in which the prefix
+   * is to be evaluated, return the Namespace Name this prefix was 
+   * bound to. Note that DOM Level 3 is expected to provide a version of
+   * this which deals with the DOM's "early binding" behavior.
+   * 
+   * Default handling:
+   *
+   * @param prefix String containing namespace prefix to be resolved, 
+   * without the ':' which separates it from the localname when used 
+   * in a Node Name. The empty sting signifies the default namespace
+   * at this point in the document.
+   * @param namespaceContext Element which provides context for resolution.
+   * (We could extend this to work for other nodes by first seeking their
+   * nearest Element ancestor.)
+   *
+   * @return a String containing the Namespace URI which this prefix
+   * represents in the specified context.
+   */
+  public String getNamespaceForPrefix(String prefix, Element namespaceContext)
+  {
+
+    int type;
+    Node parent = namespaceContext;
+    String namespace = null;
+
+    if (prefix.equals("xml"))
+    {
+      namespace = QName.S_XMLNAMESPACEURI; // Hardcoded, per Namespace spec
+    }
+        else if(prefix.equals("xmlns"))
+    {
+          // Hardcoded in the DOM spec, expected to be adopted by
+          // Namespace spec. NOTE: Namespace declarations _must_ use
+          // the xmlns: prefix; other prefixes declared as belonging
+          // to this namespace will not be recognized and should
+          // probably be rejected by parsers as erroneous declarations.
+      namespace = "http://www.w3.org/2000/xmlns/"; 
+    }
+    else
+    {
+          // Attribute name for this prefix's declaration
+          String declname=(prefix=="")
+                        ? "xmlns"
+                        : "xmlns:"+prefix;
+                                           
+          // Scan until we run out of Elements or have resolved the namespace
+      while ((null != parent) && (null == namespace)
+             && (((type = parent.getNodeType()) == Node.ELEMENT_NODE)
+                 || (type == Node.ENTITY_REFERENCE_NODE)))
+      {
+        if (type == Node.ELEMENT_NODE)
+        {
+                        
+                        // Look for the appropriate Namespace Declaration attribute,
+                        // either "xmlns:prefix" or (if prefix is "") "xmlns".
+                        // TODO: This does not handle "implicit declarations"
+                        // which may be created when the DOM is edited. DOM Level
+                        // 3 will define how those should be interpreted. But
+                        // this issue won't arise in freshly-parsed DOMs.
+                        
+                // NOTE: declname is set earlier, outside the loop.
+                        Attr attr=((Element)parent).getAttributeNode(declname);
+                        if(attr!=null)
+                        {
+                namespace = attr.getNodeValue();
+                break;
+                        }
+                }
+
+        parent = getParentOfNode(parent);
+      }
+    }
+
+    return namespace;
+  }
+
+  /**
+   * An experiment for the moment.
+   */
+  Hashtable m_NSInfos = new Hashtable();
+
+  /** Object to put into the m_NSInfos table that tells that a node has not been 
+   *  processed, but has xmlns namespace decls.  */
+  protected static final NSInfo m_NSInfoUnProcWithXMLNS = new NSInfo(false,
+                                                            true);
+
+  /** Object to put into the m_NSInfos table that tells that a node has not been 
+   *  processed, but has no xmlns namespace decls.  */
+  protected static final NSInfo m_NSInfoUnProcWithoutXMLNS = new NSInfo(false,
+                                                               false);
+
+  /** Object to put into the m_NSInfos table that tells that a node has not been 
+   *  processed, and has no xmlns namespace decls, and has no ancestor decls.  */
+  protected static final NSInfo m_NSInfoUnProcNoAncestorXMLNS =
+    new NSInfo(false, false, NSInfo.ANCESTORNOXMLNS);
+
+  /** Object to put into the m_NSInfos table that tells that a node has been 
+   *  processed, and has xmlns namespace decls.  */
+  protected static final NSInfo m_NSInfoNullWithXMLNS = new NSInfo(true,
+                                                          true);
+
+  /** Object to put into the m_NSInfos table that tells that a node has been 
+   *  processed, and has no xmlns namespace decls.  */
+  protected static final NSInfo m_NSInfoNullWithoutXMLNS = new NSInfo(true,
+                                                             false);
+
+  /** Object to put into the m_NSInfos table that tells that a node has been 
+   *  processed, and has no xmlns namespace decls. and has no ancestor decls.  */
+  protected static final NSInfo m_NSInfoNullNoAncestorXMLNS =
+    new NSInfo(true, false, NSInfo.ANCESTORNOXMLNS);
+
+  /** Vector of node (odd indexes) and NSInfos (even indexes) that tell if 
+   *  the given node is a candidate for ancestor namespace processing.  */
+  protected Vector m_candidateNoAncestorXMLNS = new Vector();
+
+  /**
+   * Returns the namespace of the given node. Differs from simply getting
+   * the node's prefix and using getNamespaceForPrefix in that it attempts
+   * to cache some of the data in NSINFO objects, to avoid repeated lookup.
+   * TODO: Should we consider moving that logic into getNamespaceForPrefix?
+   *
+   * @param n Node to be examined.
+   *
+   * @return String containing the Namespace Name (uri) for this node.
+   * Note that this is undefined for any nodes other than Elements and
+   * Attributes.
+   */
+  public String getNamespaceOfNode(Node n)
+  {
+
+    String namespaceOfPrefix;
+    boolean hasProcessedNS;
+    NSInfo nsInfo;
+    short ntype = n.getNodeType();
+
+    if (Node.ATTRIBUTE_NODE != ntype)
+    {
+      Object nsObj = m_NSInfos.get(n);  // return value
+
+      nsInfo = (nsObj == null) ? null : (NSInfo) nsObj;
+      hasProcessedNS = (nsInfo == null) ? false : nsInfo.m_hasProcessedNS;
+    }
+    else
+    {
+      hasProcessedNS = false;
+      nsInfo = null;
+    }
+
+    if (hasProcessedNS)
+    {
+      namespaceOfPrefix = nsInfo.m_namespace;
+    }
+    else
+    {
+      namespaceOfPrefix = null;
+
+      String nodeName = n.getNodeName();
+      int indexOfNSSep = nodeName.indexOf(':');
+      String prefix;
+
+      if (Node.ATTRIBUTE_NODE == ntype)
+      {
+        if (indexOfNSSep > 0)
+        {
+          prefix = nodeName.substring(0, indexOfNSSep);
+        }
+        else
+        {
+
+          // Attributes don't use the default namespace, so if 
+          // there isn't a prefix, we're done.
+          return namespaceOfPrefix;
+        }
+      }
+      else
+      {
+        prefix = (indexOfNSSep >= 0)
+                 ? nodeName.substring(0, indexOfNSSep) : "";
+      }
+
+      boolean ancestorsHaveXMLNS = false;
+      boolean nHasXMLNS = false;
+
+      if (prefix.equals("xml"))
+      {
+        namespaceOfPrefix = QName.S_XMLNAMESPACEURI;
+      }
+      else
+      {
+        int parentType;
+        Node parent = n;
+
+        while ((null != parent) && (null == namespaceOfPrefix))
+        {
+          if ((null != nsInfo)
+                  && (nsInfo.m_ancestorHasXMLNSAttrs
+                      == NSInfo.ANCESTORNOXMLNS))
+          {
+            break;
+          }
+
+          parentType = parent.getNodeType();
+
+          if ((null == nsInfo) || nsInfo.m_hasXMLNSAttrs)
+          {
+            boolean elementHasXMLNS = false;
+
+            if (parentType == Node.ELEMENT_NODE)
+            {
+              NamedNodeMap nnm = parent.getAttributes();
+
+              for (int i = 0; i < nnm.getLength(); i++)
+              {
+                Node attr = nnm.item(i);
+                String aname = attr.getNodeName();
+
+                if (aname.charAt(0) == 'x')
+                {
+                  boolean isPrefix = aname.startsWith("xmlns:");
+
+                  if (aname.equals("xmlns") || isPrefix)
+                  {
+                    if (n == parent)
+                      nHasXMLNS = true;
+
+                    elementHasXMLNS = true;
+                    ancestorsHaveXMLNS = true;
+
+                    String p = isPrefix ? aname.substring(6) : "";
+
+                    if (p.equals(prefix))
+                    {
+                      namespaceOfPrefix = attr.getNodeValue();
+
+                      break;
+                    }
+                  }
+                }
+              }
+            }
+
+            if ((Node.ATTRIBUTE_NODE != parentType) && (null == nsInfo)
+                    && (n != parent))
+            {
+              nsInfo = elementHasXMLNS
+                       ? m_NSInfoUnProcWithXMLNS : m_NSInfoUnProcWithoutXMLNS;
+
+              m_NSInfos.put(parent, nsInfo);
+            }
+          }
+
+          if (Node.ATTRIBUTE_NODE == parentType)
+          {
+            parent = getParentOfNode(parent);
+          }
+          else
+          {
+            m_candidateNoAncestorXMLNS.addElement(parent);
+            m_candidateNoAncestorXMLNS.addElement(nsInfo);
+
+            parent = parent.getParentNode();
+          }
+
+          if (null != parent)
+          {
+            Object nsObj = m_NSInfos.get(parent);  // return value
+
+            nsInfo = (nsObj == null) ? null : (NSInfo) nsObj;
+          }
+        }
+
+        int nCandidates = m_candidateNoAncestorXMLNS.size();
+
+        if (nCandidates > 0)
+        {
+          if ((false == ancestorsHaveXMLNS) && (null == parent))
+          {
+            for (int i = 0; i < nCandidates; i += 2)
+            {
+              Object candidateInfo = m_candidateNoAncestorXMLNS.elementAt(i
+                                       + 1);
+
+              if (candidateInfo == m_NSInfoUnProcWithoutXMLNS)
+              {
+                m_NSInfos.put(m_candidateNoAncestorXMLNS.elementAt(i),
+                              m_NSInfoUnProcNoAncestorXMLNS);
+              }
+              else if (candidateInfo == m_NSInfoNullWithoutXMLNS)
+              {
+                m_NSInfos.put(m_candidateNoAncestorXMLNS.elementAt(i),
+                              m_NSInfoNullNoAncestorXMLNS);
+              }
+            }
+          }
+
+          m_candidateNoAncestorXMLNS.removeAllElements();
+        }
+      }
+
+      if (Node.ATTRIBUTE_NODE != ntype)
+      {
+        if (null == namespaceOfPrefix)
+        {
+          if (ancestorsHaveXMLNS)
+          {
+            if (nHasXMLNS)
+              m_NSInfos.put(n, m_NSInfoNullWithXMLNS);
+            else
+              m_NSInfos.put(n, m_NSInfoNullWithoutXMLNS);
+          }
+          else
+          {
+            m_NSInfos.put(n, m_NSInfoNullNoAncestorXMLNS);
+          }
+        }
+        else
+        {
+          m_NSInfos.put(n, new NSInfo(namespaceOfPrefix, nHasXMLNS));
+        }
+      }
+    }
+
+    return namespaceOfPrefix;
+  }
+
+  /**
+   * Returns the local name of the given node. If the node's name begins
+   * with a namespace prefix, this is the part after the colon; otherwise
+   * it's the full node name.
+   *
+   * @param n the node to be examined.
+   *
+   * @return String containing the Local Name
+   */
+  public String getLocalNameOfNode(Node n)
+  {
+
+    String qname = n.getNodeName();
+    int index = qname.indexOf(':');
+
+    return (index < 0) ? qname : qname.substring(index + 1);
+  }
+
+  /**
+   * Returns the element name with the namespace prefix (if any) replaced
+   * by the Namespace URI it was bound to. This is not a standard 
+   * representation of a node name, but it allows convenient 
+   * single-string comparison of the "universal" names of two nodes.
+   *
+   * @param elem Element to be examined.
+   *
+   * @return String in the form "namespaceURI:localname" if the node
+   * belongs to a namespace, or simply "localname" if it doesn't.
+   * @see #getExpandedAttributeName
+   */
+  public String getExpandedElementName(Element elem)
+  {
+
+    String namespace = getNamespaceOfNode(elem);
+
+    return (null != namespace)
+           ? namespace + ":" + getLocalNameOfNode(elem)
+           : getLocalNameOfNode(elem);
+  }
+
+  /**
+   * Returns the attribute name with the namespace prefix (if any) replaced
+   * by the Namespace URI it was bound to. This is not a standard 
+   * representation of a node name, but it allows convenient 
+   * single-string comparison of the "universal" names of two nodes.
+   *
+   * @param attr Attr to be examined
+   *
+   * @return String in the form "namespaceURI:localname" if the node
+   * belongs to a namespace, or simply "localname" if it doesn't.
+   * @see #getExpandedElementName
+   */
+  public String getExpandedAttributeName(Attr attr)
+  {
+
+    String namespace = getNamespaceOfNode(attr);
+
+    return (null != namespace)
+           ? namespace + ":" + getLocalNameOfNode(attr)
+           : getLocalNameOfNode(attr);
+  }
+
+  //==========================================================
+  // SECTION: DOM Helper Functions
+  //==========================================================
+
+  /**
+   * Tell if the node is ignorable whitespace. Note that this can
+   * be determined only in the context of a DTD or other Schema,
+   * and that DOM Level 2 has nostandardized DOM API which can
+   * return that information.
+   * @deprecated
+   *
+   * @param node Node to be examined
+   *
+   * @return CURRENTLY HARDCODED TO FALSE, but should return true if
+   * and only if the node is of type Text, contains only whitespace,
+   * and does not appear as part of the #PCDATA content of an element.
+   * (Note that determining this last may require allowing for 
+   * Entity References.)
+   */
+  public boolean isIgnorableWhitespace(Text node)
+  {
+
+    boolean isIgnorable = false;  // return value
+
+    // TODO: I can probably do something to figure out if this 
+    // space is ignorable from just the information in
+    // the DOM tree.
+        // -- You need to be able to distinguish whitespace
+        // that is #PCDATA from whitespace that isn't.  That requires
+        // DTD support, which won't be standardized until DOM Level 3.
+    return isIgnorable;
+  }
+
+  /**
+   * Get the first unparented node in the ancestor chain.
+   * @deprecated
+   *
+   * @param node Starting node, to specify which chain to chase
+   *
+   * @return the topmost ancestor.
+   */
+  public Node getRoot(Node node)
+  {
+
+    Node root = null;
+
+    while (node != null)
+    {
+      root = node;
+      node = getParentOfNode(node);
+    }
+
+    return root;
+  }
+
+  /**
+   * Get the root node of the document tree, regardless of
+   * whether or not the node passed in is a document node.
+   * <p>
+   * TODO: This doesn't handle DocumentFragments or "orphaned" subtrees
+   * -- it's currently returning ownerDocument even when the tree is
+   * not actually part of the main Document tree. We should either
+   * rewrite the description to say that it finds the Document node,
+   * or change the code to walk up the ancestor chain.
+
+   *
+   * @param n Node to be examined
+   *
+   * @return the Document node. Note that this is not the correct answer
+   * if n was (or was a child of) a DocumentFragment or an orphaned node,
+   * as can arise if the DOM has been edited rather than being generated
+   * by a parser.
+   */
+  public Node getRootNode(Node n)
+  {
+    int nt = n.getNodeType();
+    return ( (Node.DOCUMENT_NODE == nt) || (Node.DOCUMENT_FRAGMENT_NODE == nt) ) 
+           ? n : n.getOwnerDocument();
+  }
+
+  /**
+   * Test whether the given node is a namespace decl node. In DOM Level 2
+   * this can be done in a namespace-aware manner, but in Level 1 DOMs
+   * it has to be done by testing the node name.
+   *
+   * @param n Node to be examined.
+   *
+   * @return boolean -- true iff the node is an Attr whose name is 
+   * "xmlns" or has the "xmlns:" prefix.
+   */
+  public boolean isNamespaceNode(Node n)
+  {
+
+    if (Node.ATTRIBUTE_NODE == n.getNodeType())
+    {
+      String attrName = n.getNodeName();
+
+      return (attrName.startsWith("xmlns:") || attrName.equals("xmlns"));
+    }
+
+    return false;
+  }
+
+  /**
+   * Obtain the XPath-model parent of a DOM node -- ownerElement for Attrs,
+   * parent for other nodes. 
+   * <p>
+   * Background: The DOM believes that you must be your Parent's
+   * Child, and thus Attrs don't have parents. XPath said that Attrs
+   * do have their owning Element as their parent. This function
+   * bridges the difference, either by using the DOM Level 2 ownerElement
+   * function or by using a "silly and expensive function" in Level 1
+   * DOMs.
+   * <p>
+   * (There's some discussion of future DOMs generalizing ownerElement 
+   * into ownerNode and making it work on all types of nodes. This
+   * still wouldn't help the users of Level 1 or Level 2 DOMs)
+   * <p>
+   *
+   * @param node Node whose XPath parent we want to obtain
+   *
+   * @return the parent of the node, or the ownerElement if it's an
+   * Attr node, or null if the node is an orphan.
+   *
+   * @throws RuntimeException if the Document has no root element.
+   * This can't arise if the Document was created
+   * via the DOM Level 2 factory methods, but is possible if other
+   * mechanisms were used to obtain it
+   */
+  public static Node getParentOfNode(Node node) throws RuntimeException
+  {
+    Node parent;
+    short nodeType = node.getNodeType();
+
+    if (Node.ATTRIBUTE_NODE == nodeType)
+    {
+      Document doc = node.getOwnerDocument();
+          /*
+      TBD:
+      if(null == doc)
+      {
+        throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CHILD_HAS_NO_OWNER_DOCUMENT, null));//"Attribute child does not have an owner document!");
+      }
+      */
+
+          // Given how expensive the tree walk may be, we should first ask 
+          // whether this DOM can answer the question for us. The additional
+          // test does slow down Level 1 DOMs slightly. DOMHelper2, which
+          // is currently specialized for Xerces, assumes it can use the
+          // Level 2 solution. We might want to have an intermediate stage,
+          // which would assume DOM Level 2 but not assume Xerces.
+          //
+          // (Shouldn't have to check whether impl is null in a compliant DOM,
+          // but let's be paranoid for a moment...)
+          DOMImplementation impl=doc.getImplementation();
+          if(impl!=null && impl.hasFeature("Core","2.0"))
+          {
+                  parent=((Attr)node).getOwnerElement();
+                  return parent;
+          }
+
+          // DOM Level 1 solution, as fallback. Hugely expensive. 
+
+      Element rootElem = doc.getDocumentElement();
+
+      if (null == rootElem)
+      {
+        throw new RuntimeException(
+          XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_CHILD_HAS_NO_OWNER_DOCUMENT_ELEMENT,
+            null));  //"Attribute child does not have an owner document element!");
+      }
+
+      parent = locateAttrParent(rootElem, node);
+
+        }
+    else
+    {
+      parent = node.getParentNode();
+
+      // if((Node.DOCUMENT_NODE != nodeType) && (null == parent))
+      // {
+      //   throw new RuntimeException("Child does not have parent!");
+      // }
+    }
+
+    return parent;
+  }
+
+  /**
+   * Given an ID, return the element. This can work only if the document
+   * is interpreted in the context of a DTD or Schema, since otherwise
+   * we don't know which attributes are or aren't IDs.
+   * <p>
+   * Note that DOM Level 1 had no ability to retrieve this information.
+   * DOM Level 2 introduced it but does not promise that it will be
+   * supported in all DOMs; those which can't support it will always
+   * return null.
+   * <p>
+   * TODO: getElementByID is currently unimplemented. Support DOM Level 2?
+   *
+   * @param id The unique identifier to be searched for.
+   * @param doc The document to search within.
+   * @return CURRENTLY HARDCODED TO NULL, but it should be:
+   * The node which has this unique identifier, or null if there
+   * is no such node or this DOM can't reliably recognize it.
+   */
+  public Element getElementByID(String id, Document doc)
+  {
+    return null;
+  }
+
+  /**
+   * The getUnparsedEntityURI function returns the URI of the unparsed
+   * entity with the specified name in the same document as the context
+   * node (see [3.3 Unparsed Entities]). It returns the empty string if
+   * there is no such entity.
+   * <p>
+   * XML processors may choose to use the System Identifier (if one
+   * is provided) to resolve the entity, rather than the URI in the
+   * Public Identifier. The details are dependent on the processor, and
+   * we would have to support some form of plug-in resolver to handle
+   * this properly. Currently, we simply return the System Identifier if
+   * present, and hope that it a usable URI or that our caller can
+   * map it to one.
+   * TODO: Resolve Public Identifiers... or consider changing function name.
+   * <p>
+   * If we find a relative URI 
+   * reference, XML expects it to be resolved in terms of the base URI 
+   * of the document. The DOM doesn't do that for us, and it isn't 
+   * entirely clear whether that should be done here; currently that's
+   * pushed up to a higher levelof our application. (Note that DOM Level 
+   * 1 didn't store the document's base URI.)
+   * TODO: Consider resolving Relative URIs.
+   * <p>
+   * (The DOM's statement that "An XML processor may choose to
+   * completely expand entities before the structure model is passed
+   * to the DOM" refers only to parsed entities, not unparsed, and hence
+   * doesn't affect this function.)
+   *
+   * @param name A string containing the Entity Name of the unparsed
+   * entity.
+   * @param doc Document node for the document to be searched.
+   *
+   * @return String containing the URI of the Unparsed Entity, or an
+   * empty string if no such entity exists.
+   */
+  public String getUnparsedEntityURI(String name, Document doc)
+  {
+
+    String url = "";
+    DocumentType doctype = doc.getDoctype();
+
+    if (null != doctype)
+    {
+      NamedNodeMap entities = doctype.getEntities();
+      if(null == entities)
+        return url;
+      Entity entity = (Entity) entities.getNamedItem(name);
+      if(null == entity)
+        return url;
+      
+      String notationName = entity.getNotationName();
+
+      if (null != notationName)  // then it's unparsed
+      {
+        // The draft says: "The XSLT processor may use the public 
+        // identifier to generate a URI for the entity instead of the URI 
+        // specified in the system identifier. If the XSLT processor does 
+        // not use the public identifier to generate the URI, it must use 
+        // the system identifier; if the system identifier is a relative 
+        // URI, it must be resolved into an absolute URI using the URI of 
+        // the resource containing the entity declaration as the base 
+        // URI [RFC2396]."
+        // So I'm falling a bit short here.
+        url = entity.getSystemId();
+
+        if (null == url)
+        {
+          url = entity.getPublicId();
+        }
+        else
+        {
+          // This should be resolved to an absolute URL, but that's hard 
+          // to do from here.
+        }        
+      }
+    }
+
+    return url;
+  }
+
+  /**
+   * Support for getParentOfNode; walks a DOM tree until it finds
+   * the Element which owns the Attr. This is hugely expensive, and
+   * if at all possible you should use the DOM Level 2 Attr.ownerElement()
+   * method instead.
+   *  <p>
+   * The DOM Level 1 developers expected that folks would keep track
+   * of the last Element they'd seen and could recover the info from
+   * that source. Obviously that doesn't work very well if the only
+   * information you've been presented with is the Attr. The DOM Level 2
+   * getOwnerElement() method fixes that, but only for Level 2 and
+   * later DOMs.
+   *
+   * @param elem Element whose subtree is to be searched for this Attr
+   * @param attr Attr whose owner is to be located.
+   *
+   * @return the first Element whose attribute list includes the provided
+   * attr. In modern DOMs, this will also be the only such Element. (Early
+   * DOMs had some hope that Attrs might be sharable, but this idea has
+   * been abandoned.)
+   */
+  private static Node locateAttrParent(Element elem, Node attr)
+  {
+
+    Node parent = null;
+
+        // This should only be called for Level 1 DOMs, so we don't have to
+        // worry about namespace issues. In later levels, it's possible
+        // for a DOM to have two Attrs with the same NodeName but
+        // different namespaces, and we'd need to get getAttributeNodeNS...
+        // but later levels also have Attr.getOwnerElement.
+        Attr check=elem.getAttributeNode(attr.getNodeName());
+        if(check==attr)
+                parent = elem;
+
+    if (null == parent)
+    {
+      for (Node node = elem.getFirstChild(); null != node;
+              node = node.getNextSibling())
+      {
+        if (Node.ELEMENT_NODE == node.getNodeType())
+        {
+          parent = locateAttrParent((Element) node, attr);
+
+          if (null != parent)
+            break;
+        }
+      }
+    }
+
+    return parent;
+  }
+
+  /**
+   * The factory object used for creating nodes
+   * in the result tree.
+   */
+  protected Document m_DOMFactory = null;
+
+  /**
+   * Store the factory object required to create DOM nodes
+   * in the result tree. In fact, that's just the result tree's
+   * Document node...
+   *
+   * @param domFactory The DOM Document Node within whose context
+   * the result tree will be built.
+   */
+  public void setDOMFactory(Document domFactory)
+  {
+    this.m_DOMFactory = domFactory;
+  }
+
+  /**
+   * Retrieve the factory object required to create DOM nodes
+   * in the result tree.
+   *
+   * @return The result tree's DOM Document Node.
+   */
+  public Document getDOMFactory()
+  {
+
+    if (null == this.m_DOMFactory)
+    {
+      this.m_DOMFactory = createDocument();
+    }
+
+    return this.m_DOMFactory;
+  }
+
+  /**
+   * Get the textual contents of the node. See
+   * getNodeData(Node,FastStringBuffer) for discussion of how
+   * whitespace nodes are handled.
+   *
+   * @param node DOM Node to be examined
+   * @return String containing a concatenation of all the 
+   * textual content within that node. 
+   * @see #getNodeData(Node,FastStringBuffer)
+   * 
+   */
+  public static String getNodeData(Node node)
+  {
+
+    FastStringBuffer buf = StringBufferPool.get();
+    String s;
+
+    try
+    {
+      getNodeData(node, buf);
+
+      s = (buf.length() > 0) ? buf.toString() : "";
+    }
+    finally
+    {
+      StringBufferPool.free(buf);
+    }
+
+    return s;
+  }
+
+  /**
+   * Retrieve the text content of a DOM subtree, appending it into a
+   * user-supplied FastStringBuffer object. Note that attributes are
+   * not considered part of the content of an element.
+   * <p>
+   * There are open questions regarding whitespace stripping. 
+   * Currently we make no special effort in that regard, since the standard
+   * DOM doesn't yet provide DTD-based information to distinguish
+   * whitespace-in-element-context from genuine #PCDATA. Note that we
+   * should probably also consider xml:space if/when we address this.
+   * DOM Level 3 may solve the problem for us.
+   *
+   * @param node Node whose subtree is to be walked, gathering the
+   * contents of all Text or CDATASection nodes.
+   * @param buf FastStringBuffer into which the contents of the text
+   * nodes are to be concatenated.
+   */
+  public static void getNodeData(Node node, FastStringBuffer buf)
+  {
+
+    switch (node.getNodeType())
+    {
+    case Node.DOCUMENT_FRAGMENT_NODE :
+    case Node.DOCUMENT_NODE :
+    case Node.ELEMENT_NODE :
+    {
+      for (Node child = node.getFirstChild(); null != child;
+              child = child.getNextSibling())
+      {
+        getNodeData(child, buf);
+      }
+    }
+    break;
+    case Node.TEXT_NODE :
+    case Node.CDATA_SECTION_NODE :
+      buf.append(node.getNodeValue());
+      break;
+    case Node.ATTRIBUTE_NODE :
+      buf.append(node.getNodeValue());
+      break;
+    case Node.PROCESSING_INSTRUCTION_NODE :
+      // warning(XPATHErrorResources.WG_PARSING_AND_PREPARING);        
+      break;
+    default :
+      // ignore
+      break;
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/DOMOrder.java b/src/main/java/org/apache/xml/utils/DOMOrder.java
new file mode 100644
index 0000000..338fced
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/DOMOrder.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOMOrder.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * @deprecated Since the introduction of the DTM, this class will be removed.
+ * Nodes that implement this index can return a document order index.
+ * Eventually, this will be replaced by DOM 3 methods. 
+ * (compareDocumentOrder and/or compareTreePosition.)
+ */
+public interface DOMOrder
+{
+
+  /**
+   * Get the UID (document order index).
+   *
+   * @return integer whose relative value corresponds to document order
+   * -- that is, if node1.getUid()<node2.getUid(), node1 comes before
+   * node2, and if they're equal node1 and node2 are the same node. No
+   * promises are made beyond that.
+   */
+  public int getUid();
+}
diff --git a/src/main/java/org/apache/xml/utils/DefaultErrorHandler.java b/src/main/java/org/apache/xml/utils/DefaultErrorHandler.java
new file mode 100644
index 0000000..bcd3860
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/DefaultErrorHandler.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DefaultErrorHandler.java 524806 2007-04-02 15:51:39Z zongaro $
+ */
+package org.apache.xml.utils;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+
+/**
+ * Implement SAX error handler for default reporting.
+ * @xsl.usage general
+ */
+public class DefaultErrorHandler implements ErrorHandler, ErrorListener
+{
+  PrintWriter m_pw;
+
+  /**
+   * if this flag is set to true, we will rethrow the exception on
+   * the error() and fatalError() methods. If it is false, the errors 
+   * are reported to System.err. 
+   */
+  boolean m_throwExceptionOnError = true;
+
+  /**
+   * Constructor DefaultErrorHandler
+   */
+  public DefaultErrorHandler(PrintWriter pw)
+  {
+    m_pw = pw;
+  }
+  
+  /**
+   * Constructor DefaultErrorHandler
+   */
+  public DefaultErrorHandler(PrintStream pw)
+  {
+    m_pw = new PrintWriter(pw, true);
+  }
+  
+  /**
+   * Constructor DefaultErrorHandler
+   */
+  public DefaultErrorHandler()
+  {
+    this(true);
+  }
+
+  /**
+   * Constructor DefaultErrorHandler
+   */
+  public DefaultErrorHandler(boolean throwExceptionOnError)
+  {
+    // Defer creation of a PrintWriter until it's actually needed
+    m_throwExceptionOnError = throwExceptionOnError;
+  }
+
+  /**
+   * Retrieve <code>java.io.PrintWriter</code> to which errors are being
+   * directed.
+   * @return The <code>PrintWriter</code> installed via the constructor
+   *         or the default <code>PrintWriter</code>
+   */
+  public PrintWriter getErrorWriter() {
+    // Defer creating the java.io.PrintWriter until an error needs to be
+    // reported.
+    if (m_pw == null) {
+      m_pw = new PrintWriter(System.err, true);
+    }
+    return m_pw;
+  }
+
+  /**
+   * Receive notification of a warning.
+   *
+   * <p>SAX parsers will use this method to report conditions that
+   * are not errors or fatal errors as defined by the XML 1.0
+   * recommendation.  The default behaviour is to take no action.</p>
+   *
+   * <p>The SAX parser must continue to provide normal parsing events
+   * after invoking this method: it should still be possible for the
+   * application to process the document through to the end.</p>
+   *
+   * @param exception The warning information encapsulated in a
+   *                  SAX parse exception.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void warning(SAXParseException exception) throws SAXException
+  {
+    PrintWriter pw = getErrorWriter();
+
+    printLocation(pw, exception);
+    pw.println("Parser warning: " + exception.getMessage());
+  }
+
+  /**
+   * Receive notification of a recoverable error.
+   *
+   * <p>This corresponds to the definition of "error" in section 1.2
+   * of the W3C XML 1.0 Recommendation.  For example, a validating
+   * parser would use this callback to report the violation of a
+   * validity constraint.  The default behaviour is to take no
+   * action.</p>
+   *
+   * <p>The SAX parser must continue to provide normal parsing events
+   * after invoking this method: it should still be possible for the
+   * application to process the document through to the end.  If the
+   * application cannot do so, then the parser should report a fatal
+   * error even if the XML 1.0 recommendation does not require it to
+   * do so.</p>
+   *
+   * @param exception The error information encapsulated in a
+   *                  SAX parse exception.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void error(SAXParseException exception) throws SAXException
+  {
+    //printLocation(exception);
+    // getErrorWriter().println(exception.getMessage());
+
+    throw exception;
+  }
+
+  /**
+   * Receive notification of a non-recoverable error.
+   *
+   * <p>This corresponds to the definition of "fatal error" in
+   * section 1.2 of the W3C XML 1.0 Recommendation.  For example, a
+   * parser would use this callback to report the violation of a
+   * well-formedness constraint.</p>
+   *
+   * <p>The application must assume that the document is unusable
+   * after the parser has invoked this method, and should continue
+   * (if at all) only for the sake of collecting addition error
+   * messages: in fact, SAX parsers are free to stop reporting any
+   * other events once this method has been invoked.</p>
+   *
+   * @param exception The error information encapsulated in a
+   *                  SAX parse exception.
+   * @throws SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public void fatalError(SAXParseException exception) throws SAXException
+  {
+    // printLocation(exception);
+    // getErrorWriter().println(exception.getMessage());
+
+    throw exception;
+  }
+  
+  /**
+   * Receive notification of a warning.
+   *
+   * <p>SAX parsers will use this method to report conditions that
+   * are not errors or fatal errors as defined by the XML 1.0
+   * recommendation.  The default behaviour is to take no action.</p>
+   *
+   * <p>The SAX parser must continue to provide normal parsing events
+   * after invoking this method: it should still be possible for the
+   * application to process the document through to the end.</p>
+   *
+   * @param exception The warning information encapsulated in a
+   *                  SAX parse exception.
+   * @throws javax.xml.transform.TransformerException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see javax.xml.transform.TransformerException
+   */
+  public void warning(TransformerException exception) throws TransformerException
+  {
+    PrintWriter pw = getErrorWriter();
+
+    printLocation(pw, exception);
+    pw.println(exception.getMessage());
+  }
+
+  /**
+   * Receive notification of a recoverable error.
+   *
+   * <p>This corresponds to the definition of "error" in section 1.2
+   * of the W3C XML 1.0 Recommendation.  For example, a validating
+   * parser would use this callback to report the violation of a
+   * validity constraint.  The default behaviour is to take no
+   * action.</p>
+   *
+   * <p>The SAX parser must continue to provide normal parsing events
+   * after invoking this method: it should still be possible for the
+   * application to process the document through to the end.  If the
+   * application cannot do so, then the parser should report a fatal
+   * error even if the XML 1.0 recommendation does not require it to
+   * do so.</p>
+   *
+   * @param exception The error information encapsulated in a
+   *                  SAX parse exception.
+   * @throws javax.xml.transform.TransformerException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see javax.xml.transform.TransformerException
+   */
+  public void error(TransformerException exception) throws TransformerException
+  {
+    // If the m_throwExceptionOnError flag is true, rethrow the exception.
+    // Otherwise report the error to System.err.
+    if (m_throwExceptionOnError)
+      throw exception;
+    else
+    {
+      PrintWriter pw = getErrorWriter();
+
+      printLocation(pw, exception);
+      pw.println(exception.getMessage());
+    }
+  }
+
+  /**
+   * Receive notification of a non-recoverable error.
+   *
+   * <p>This corresponds to the definition of "fatal error" in
+   * section 1.2 of the W3C XML 1.0 Recommendation.  For example, a
+   * parser would use this callback to report the violation of a
+   * well-formedness constraint.</p>
+   *
+   * <p>The application must assume that the document is unusable
+   * after the parser has invoked this method, and should continue
+   * (if at all) only for the sake of collecting addition error
+   * messages: in fact, SAX parsers are free to stop reporting any
+   * other events once this method has been invoked.</p>
+   *
+   * @param exception The error information encapsulated in a
+   *                  SAX parse exception.
+   * @throws javax.xml.transform.TransformerException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see javax.xml.transform.TransformerException
+   */
+  public void fatalError(TransformerException exception) throws TransformerException
+  {
+    // If the m_throwExceptionOnError flag is true, rethrow the exception.
+    // Otherwise report the error to System.err.
+    if (m_throwExceptionOnError)
+      throw exception;
+    else
+    {
+      PrintWriter pw = getErrorWriter();
+
+      printLocation(pw, exception);
+      pw.println(exception.getMessage());
+    }
+  }
+  
+  public static void ensureLocationSet(TransformerException exception)
+  {
+    // SourceLocator locator = exception.getLocator();
+    SourceLocator locator = null;
+    Throwable cause = exception;
+    
+    // Try to find the locator closest to the cause.
+    do
+    {
+      if(cause instanceof SAXParseException)
+      {
+        locator = new SAXSourceLocator((SAXParseException)cause);
+      }
+      else if (cause instanceof TransformerException)
+      {
+        SourceLocator causeLocator = ((TransformerException)cause).getLocator();
+        if(null != causeLocator)
+          locator = causeLocator;
+      }
+      
+      if(cause instanceof TransformerException)
+        cause = ((TransformerException)cause).getCause();
+      else if(cause instanceof SAXException)
+        cause = ((SAXException)cause).getException();
+      else
+        cause = null;
+    }
+    while(null != cause);
+    
+    exception.setLocator(locator);
+  }
+  
+  public static void printLocation(PrintStream pw, TransformerException exception)
+  {
+    printLocation(new PrintWriter(pw), exception);
+  }
+  
+  public static void printLocation(java.io.PrintStream pw, org.xml.sax.SAXParseException exception)
+  {
+    printLocation(new PrintWriter(pw), exception);
+  }
+  
+  public static void printLocation(PrintWriter pw, Throwable exception)
+  {
+    SourceLocator locator = null;
+    Throwable cause = exception;
+    
+    // Try to find the locator closest to the cause.
+    do
+    {
+      if(cause instanceof SAXParseException)
+      {
+        locator = new SAXSourceLocator((SAXParseException)cause);
+      }
+      else if (cause instanceof TransformerException)
+      {
+        SourceLocator causeLocator = ((TransformerException)cause).getLocator();
+        if(null != causeLocator)
+          locator = causeLocator;
+      }
+      if(cause instanceof TransformerException)
+        cause = ((TransformerException)cause).getCause();
+      else if(cause instanceof WrappedRuntimeException)
+        cause = ((WrappedRuntimeException)cause).getException();
+      else if(cause instanceof SAXException)
+        cause = ((SAXException)cause).getException();
+      else
+        cause = null;
+    }
+    while(null != cause);
+        
+    if(null != locator)
+    {
+      // getErrorWriter().println("Parser fatal error: "+exception.getMessage());
+      String id = (null != locator.getPublicId() )
+                  ? locator.getPublicId()
+                    : (null != locator.getSystemId())
+                      ? locator.getSystemId() : XMLMessages.createXMLMessage(XMLErrorResources.ER_SYSTEMID_UNKNOWN, null); //"SystemId Unknown";
+
+      pw.print(id + "; " +XMLMessages.createXMLMessage("line", null) + locator.getLineNumber()
+                         + "; " +XMLMessages.createXMLMessage("column", null) + locator.getColumnNumber()+"; ");
+    }
+    else
+      pw.print("("+XMLMessages.createXMLMessage(XMLErrorResources.ER_LOCATION_UNKNOWN, null)+")");
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/FastStringBuffer.java b/src/main/java/org/apache/xml/utils/FastStringBuffer.java
new file mode 100644
index 0000000..e79d4fe
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/FastStringBuffer.java
@@ -0,0 +1,1293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FastStringBuffer.java 469279 2006-10-30 21:18:02Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * Bare-bones, unsafe, fast string buffer. No thread-safety, no
+ * parameter range checking, exposed fields. Note that in typical
+ * applications, thread-safety of a StringBuffer is a somewhat
+ * dubious concept in any case.
+ * <p>
+ * Note that Stree and DTM used a single FastStringBuffer as a string pool,
+ * by recording start and length indices within this single buffer. This
+ * minimizes heap overhead, but of course requires more work when retrieving
+ * the data.
+ * <p>
+ * FastStringBuffer operates as a "chunked buffer". Doing so
+ * reduces the need to recopy existing information when an append
+ * exceeds the space available; we just allocate another chunk and
+ * flow across to it. (The array of chunks may need to grow,
+ * admittedly, but that's a much smaller object.) Some excess
+ * recopying may arise when we extract Strings which cross chunk
+ * boundaries; larger chunks make that less frequent.
+ * <p>
+ * The size values are parameterized, to allow tuning this code. In
+ * theory, Result Tree Fragments might want to be tuned differently 
+ * from the main document's text. 
+ * <p>
+ * %REVIEW% An experiment in self-tuning is
+ * included in the code (using nested FastStringBuffers to achieve
+ * variation in chunk sizes), but this implementation has proven to
+ * be problematic when data may be being copied from the FSB into itself.
+ * We should either re-architect that to make this safe (if possible)
+ * or remove that code and clean up for performance/maintainability reasons.
+ * <p>
+ */
+public class FastStringBuffer
+{
+  // If nonzero, forces the inial chunk size.
+  /**/static final int DEBUG_FORCE_INIT_BITS=0;
+  
+  	// %BUG% %REVIEW% *****PROBLEM SUSPECTED: If data from an FSB is being copied
+  	// back into the same FSB (variable set from previous variable, for example) 
+  	// and blocksize changes in mid-copy... there's risk of severe malfunction in 
+  	// the read process, due to how the resizing code re-jiggers storage. Arggh. 
+  	// If we want to retain the variable-size-block feature, we need to reconsider 
+  	// that issue. For now, I have forced us into fixed-size mode.
+    static final boolean DEBUG_FORCE_FIXED_CHUNKSIZE=true;
+
+	/** Manifest constant: Suppress leading whitespace.
+	 * This should be used when normalize-to-SAX is called for the first chunk of a
+	 * multi-chunk output, or one following unsuppressed whitespace in a previous
+	 * chunk.
+	 * @see #sendNormalizedSAXcharacters(org.xml.sax.ContentHandler,int,int)
+	 */
+	public static final int SUPPRESS_LEADING_WS=0x01;
+	
+	/** Manifest constant: Suppress trailing whitespace.
+	 * This should be used when normalize-to-SAX is called for the last chunk of a
+	 * multi-chunk output; it may have to be or'ed with SUPPRESS_LEADING_WS.
+	 */
+	public static final int SUPPRESS_TRAILING_WS=0x02;
+	
+	/** Manifest constant: Suppress both leading and trailing whitespace.
+	 * This should be used when normalize-to-SAX is called for a complete string.
+	 * (I'm not wild about the name of this one. Ideas welcome.)
+	 * @see #sendNormalizedSAXcharacters(org.xml.sax.ContentHandler,int,int)
+	 */
+	public static final int SUPPRESS_BOTH
+		= SUPPRESS_LEADING_WS | SUPPRESS_TRAILING_WS;
+
+	/** Manifest constant: Carry trailing whitespace of one chunk as leading 
+	 * whitespace of the next chunk. Used internally; I don't see any reason
+	 * to make it public right now.
+	 */
+	private static final int CARRY_WS=0x04;
+
+	/**
+   * Field m_chunkBits sets our chunking strategy, by saying how many
+   * bits of index can be used within a single chunk before flowing over
+   * to the next chunk. For example, if m_chunkbits is set to 15, each
+   * chunk can contain up to 2^15 (32K) characters  
+   */
+  int m_chunkBits = 15;
+
+  /**
+   * Field m_maxChunkBits affects our chunk-growth strategy, by saying what
+   * the largest permissible chunk size is in this particular FastStringBuffer
+   * hierarchy. 
+   */
+  int m_maxChunkBits = 15;
+
+  /**
+   * Field m_rechunkBits affects our chunk-growth strategy, by saying how
+   * many chunks should be allocated at one size before we encapsulate them
+   * into the first chunk of the next size up. For example, if m_rechunkBits
+   * is set to 3, then after 8 chunks at a given size we will rebundle
+   * them as the first element of a FastStringBuffer using a chunk size
+   * 8 times larger (chunkBits shifted left three bits).
+   */
+  int m_rebundleBits = 2;
+
+  /**
+   * Field m_chunkSize establishes the maximum size of one chunk of the array
+   * as 2**chunkbits characters.
+   * (Which may also be the minimum size if we aren't tuning for storage) 
+   */
+  int m_chunkSize;  // =1<<(m_chunkBits-1);
+
+  /**
+   * Field m_chunkMask is m_chunkSize-1 -- in other words, m_chunkBits
+   * worth of low-order '1' bits, useful for shift-and-mask addressing
+   * within the chunks. 
+   */
+  int m_chunkMask;  // =m_chunkSize-1;
+
+  /**
+   * Field m_array holds the string buffer's text contents, using an
+   * array-of-arrays. Note that this array, and the arrays it contains, may be
+   * reallocated when necessary in order to allow the buffer to grow;
+   * references to them should be considered to be invalidated after any
+   * append. However, the only time these arrays are directly exposed
+   * is in the sendSAXcharacters call.
+   */
+  char[][] m_array;
+
+  /**
+   * Field m_lastChunk is an index into m_array[], pointing to the last
+   * chunk of the Chunked Array currently in use. Note that additional
+   * chunks may actually be allocated, eg if the FastStringBuffer had
+   * previously been truncated or if someone issued an ensureSpace request.
+   * <p>
+   * The insertion point for append operations is addressed by the combination
+   * of m_lastChunk and m_firstFree.
+   */
+  int m_lastChunk = 0;
+
+  /**
+   * Field m_firstFree is an index into m_array[m_lastChunk][], pointing to
+   * the first character in the Chunked Array which is not part of the
+   * FastStringBuffer's current content. Since m_array[][] is zero-based,
+   * the length of that content can be calculated as
+   * (m_lastChunk<<m_chunkBits) + m_firstFree 
+   */
+  int m_firstFree = 0;
+
+  /**
+   * Field m_innerFSB, when non-null, is a FastStringBuffer whose total
+   * length equals m_chunkSize, and which replaces m_array[0]. This allows
+   * building a hierarchy of FastStringBuffers, where early appends use
+   * a smaller chunkSize (for less wasted memory overhead) but later
+   * ones use a larger chunkSize (for less heap activity overhead).
+   */
+  FastStringBuffer m_innerFSB = null;
+
+  /**
+   * Construct a FastStringBuffer, with allocation policy as per parameters.
+   * <p>
+   * For coding convenience, I've expressed both allocation sizes in terms of
+   * a number of bits. That's needed for the final size of a chunk,
+   * to permit fast and efficient shift-and-mask addressing. It's less critical
+   * for the inital size, and may be reconsidered.
+   * <p>
+   * An alternative would be to accept integer sizes and round to powers of two;
+   * that really doesn't seem to buy us much, if anything.
+   *
+   * @param initChunkBits Length in characters of the initial allocation
+   * of a chunk, expressed in log-base-2. (That is, 10 means allocate 1024
+   * characters.) Later chunks will use larger allocation units, to trade off
+   * allocation speed of large document against storage efficiency of small
+   * ones.
+   * @param maxChunkBits Number of character-offset bits that should be used for
+   * addressing within a chunk. Maximum length of a chunk is 2^chunkBits
+   * characters.
+   * @param rebundleBits Number of character-offset bits that addressing should
+   * advance before we attempt to take a step from initChunkBits to maxChunkBits
+   */
+  public FastStringBuffer(int initChunkBits, int maxChunkBits,
+                          int rebundleBits)
+  {
+    if(DEBUG_FORCE_INIT_BITS!=0) initChunkBits=DEBUG_FORCE_INIT_BITS;
+    
+    // %REVIEW%
+    // Should this force to larger value, or smaller? Smaller less efficient, but if
+    // someone requested variable mode it's because they care about storage space.
+    // On the other hand, given the other changes I'm making, odds are that we should
+    // adopt the larger size. Dither, dither, dither... This is just stopgap workaround
+    // anyway; we need a permanant solution.
+    //
+    if(DEBUG_FORCE_FIXED_CHUNKSIZE) maxChunkBits=initChunkBits;
+    //if(DEBUG_FORCE_FIXED_CHUNKSIZE) initChunkBits=maxChunkBits;
+
+    m_array = new char[16][];
+
+    // Don't bite off more than we're prepared to swallow!
+    if (initChunkBits > maxChunkBits)
+      initChunkBits = maxChunkBits;
+
+    m_chunkBits = initChunkBits;
+    m_maxChunkBits = maxChunkBits;
+    m_rebundleBits = rebundleBits;
+    m_chunkSize = 1 << (initChunkBits);
+    m_chunkMask = m_chunkSize - 1;
+    m_array[0] = new char[m_chunkSize];
+  }
+
+  /**
+   * Construct a FastStringBuffer, using a default rebundleBits value.
+   *
+   * NEEDSDOC @param initChunkBits
+   * NEEDSDOC @param maxChunkBits
+   */
+  public FastStringBuffer(int initChunkBits, int maxChunkBits)
+  {
+    this(initChunkBits, maxChunkBits, 2);
+  }
+
+  /**
+   * Construct a FastStringBuffer, using default maxChunkBits and
+   * rebundleBits values.
+   * <p>
+   * ISSUE: Should this call assert initial size, or fixed size?
+   * Now configured as initial, with a default for fixed.
+   *
+   * NEEDSDOC @param initChunkBits
+   */
+  public FastStringBuffer(int initChunkBits)
+  {
+    this(initChunkBits, 15, 2);
+  }
+
+  /**
+   * Construct a FastStringBuffer, using a default allocation policy.
+   */
+  public FastStringBuffer()
+  {
+
+    // 10 bits is 1K. 15 bits is 32K. Remember that these are character
+    // counts, so actual memory allocation unit is doubled for UTF-16 chars.
+    //
+    // For reference: In the original FastStringBuffer, we simply
+    // overallocated by blocksize (default 1KB) on each buffer-growth.
+    this(10, 15, 2);
+  }
+
+  /**
+   * Get the length of the list. Synonym for length().
+   *
+   * @return the number of characters in the FastStringBuffer's content.
+   */
+  public final int size()
+  {
+    return (m_lastChunk << m_chunkBits) + m_firstFree;
+  }
+
+  /**
+   * Get the length of the list. Synonym for size().
+   *
+   * @return the number of characters in the FastStringBuffer's content.
+   */
+  public final int length()
+  {
+    return (m_lastChunk << m_chunkBits) + m_firstFree;
+  }
+
+  /**
+   * Discard the content of the FastStringBuffer, and most of the memory
+   * that was allocated by it, restoring the initial state. Note that this
+   * may eventually be different from setLength(0), which see.
+   */
+  public final void reset()
+  {
+
+    m_lastChunk = 0;
+    m_firstFree = 0;
+
+    // Recover the original chunk size
+    FastStringBuffer innermost = this;
+
+    while (innermost.m_innerFSB != null)
+    {
+      innermost = innermost.m_innerFSB;
+    }
+
+    m_chunkBits = innermost.m_chunkBits;
+    m_chunkSize = innermost.m_chunkSize;
+    m_chunkMask = innermost.m_chunkMask;
+
+    // Discard the hierarchy
+    m_innerFSB = null;
+    m_array = new char[16][0];
+    m_array[0] = new char[m_chunkSize];
+  }
+
+  /**
+   * Directly set how much of the FastStringBuffer's storage is to be
+   * considered part of its content. This is a fast but hazardous
+   * operation. It is not protected against negative values, or values
+   * greater than the amount of storage currently available... and even
+   * if additional storage does exist, its contents are unpredictable.
+   * The only safe use for our setLength() is to truncate the FastStringBuffer
+   * to a shorter string.
+   *
+   * @param l New length. If l<0 or l>=getLength(), this operation will
+   * not report an error but future operations will almost certainly fail.
+   */
+  public final void setLength(int l)
+  {
+    m_lastChunk = l >>> m_chunkBits;
+
+    if (m_lastChunk == 0 && m_innerFSB != null)
+    {
+      // Replace this FSB with the appropriate inner FSB, truncated
+      m_innerFSB.setLength(l, this);
+    }
+    else
+    {
+      m_firstFree = l & m_chunkMask;
+      
+	  // There's an edge case if l is an exact multiple of m_chunkBits, which risks leaving
+	  // us pointing at the start of a chunk which has not yet been allocated. Rather than 
+	  // pay the cost of dealing with that in the append loops (more scattered and more
+	  // inner-loop), we correct it here by moving to the safe side of that
+	  // line -- as we would have left the indexes had we appended up to that point.
+      if(m_firstFree==0 && m_lastChunk>0)
+      {
+      	--m_lastChunk;
+      	m_firstFree=m_chunkSize;
+      }
+    }
+  }
+
+  /**
+   * Subroutine for the public setLength() method. Deals with the fact
+   * that truncation may require restoring one of the innerFSBs
+   *
+   * NEEDSDOC @param l
+   * NEEDSDOC @param rootFSB
+   */
+  private final void setLength(int l, FastStringBuffer rootFSB)
+  {
+
+    m_lastChunk = l >>> m_chunkBits;
+
+    if (m_lastChunk == 0 && m_innerFSB != null)
+    {
+      m_innerFSB.setLength(l, rootFSB);
+    }
+    else
+    {
+
+      // Undo encapsulation -- pop the innerFSB data back up to root.
+      // Inefficient, but attempts to keep the code simple.
+      rootFSB.m_chunkBits = m_chunkBits;
+      rootFSB.m_maxChunkBits = m_maxChunkBits;
+      rootFSB.m_rebundleBits = m_rebundleBits;
+      rootFSB.m_chunkSize = m_chunkSize;
+      rootFSB.m_chunkMask = m_chunkMask;
+      rootFSB.m_array = m_array;
+      rootFSB.m_innerFSB = m_innerFSB;
+      rootFSB.m_lastChunk = m_lastChunk;
+
+      // Finally, truncate this sucker.
+      rootFSB.m_firstFree = l & m_chunkMask;
+    }
+  }
+
+  /**
+   * Note that this operation has been somewhat deoptimized by the shift to a
+   * chunked array, as there is no factory method to produce a String object
+   * directly from an array of arrays and hence a double copy is needed.
+   * By using ensureCapacity we hope to minimize the heap overhead of building
+   * the intermediate StringBuffer.
+   * <p>
+   * (It really is a pity that Java didn't design String as a final subclass
+   * of MutableString, rather than having StringBuffer be a separate hierarchy.
+   * We'd avoid a <strong>lot</strong> of double-buffering.)
+   *
+   * @return the contents of the FastStringBuffer as a standard Java string.
+   */
+  public final String toString()
+  {
+
+    int length = (m_lastChunk << m_chunkBits) + m_firstFree;
+
+    return getString(new StringBuffer(length), 0, 0, length).toString();
+  }
+
+  /**
+   * Append a single character onto the FastStringBuffer, growing the
+   * storage if necessary.
+   * <p>
+   * NOTE THAT after calling append(), previously obtained
+   * references to m_array[][] may no longer be valid....
+   * though in fact they should be in this instance.
+   *
+   * @param value character to be appended.
+   */
+  public final void append(char value)
+  {
+    
+    char[] chunk;
+
+    // We may have preallocated chunks. If so, all but last should
+    // be at full size.
+
+    if (m_firstFree < m_chunkSize)  // Simplified test single-character-fits
+      chunk = m_array[m_lastChunk];
+    else
+    {
+
+      // Extend array?
+      int i = m_array.length;
+
+      if (m_lastChunk + 1 == i)
+      {
+        char[][] newarray = new char[i + 16][];
+
+        System.arraycopy(m_array, 0, newarray, 0, i);
+
+        m_array = newarray;
+      }
+
+      // Advance one chunk
+      chunk = m_array[++m_lastChunk];
+
+      if (chunk == null)
+      {
+
+        // Hierarchical encapsulation
+        if (m_lastChunk == 1 << m_rebundleBits
+                && m_chunkBits < m_maxChunkBits)
+        {
+
+          // Should do all the work of both encapsulating
+          // existing data and establishing new sizes/offsets
+          m_innerFSB = new FastStringBuffer(this);
+        }
+
+        // Add a chunk.
+        chunk = m_array[m_lastChunk] = new char[m_chunkSize];
+      }
+
+      m_firstFree = 0;
+    }
+
+    // Space exists in the chunk. Append the character.
+    chunk[m_firstFree++] = value;
+  }
+
+  /**
+   * Append the contents of a String onto the FastStringBuffer,
+   * growing the storage if necessary.
+   * <p>
+   * NOTE THAT after calling append(), previously obtained
+   * references to m_array[] may no longer be valid.
+   *
+   * @param value String whose contents are to be appended.
+   */
+  public final void append(String value)
+  {
+
+    if (value == null) 
+      return;
+    int strlen = value.length();
+
+    if (0 == strlen)
+      return;
+
+    int copyfrom = 0;
+    char[] chunk = m_array[m_lastChunk];
+    int available = m_chunkSize - m_firstFree;
+
+    // Repeat while data remains to be copied
+    while (strlen > 0)
+    {
+
+      // Copy what fits
+      if (available > strlen)
+        available = strlen;
+
+      value.getChars(copyfrom, copyfrom + available, m_array[m_lastChunk],
+                     m_firstFree);
+
+      strlen -= available;
+      copyfrom += available;
+
+      // If there's more left, allocate another chunk and continue
+      if (strlen > 0)
+      {
+
+        // Extend array?
+        int i = m_array.length;
+
+        if (m_lastChunk + 1 == i)
+        {
+          char[][] newarray = new char[i + 16][];
+
+          System.arraycopy(m_array, 0, newarray, 0, i);
+
+          m_array = newarray;
+        }
+
+        // Advance one chunk
+        chunk = m_array[++m_lastChunk];
+
+        if (chunk == null)
+        {
+
+          // Hierarchical encapsulation
+          if (m_lastChunk == 1 << m_rebundleBits
+                  && m_chunkBits < m_maxChunkBits)
+          {
+
+            // Should do all the work of both encapsulating
+            // existing data and establishing new sizes/offsets
+            m_innerFSB = new FastStringBuffer(this);
+          }
+
+          // Add a chunk. 
+          chunk = m_array[m_lastChunk] = new char[m_chunkSize];
+        }
+
+        available = m_chunkSize;
+        m_firstFree = 0;
+      }
+    }
+
+    // Adjust the insert point in the last chunk, when we've reached it.
+    m_firstFree += available;
+  }
+
+  /**
+   * Append the contents of a StringBuffer onto the FastStringBuffer,
+   * growing the storage if necessary.
+   * <p>
+   * NOTE THAT after calling append(), previously obtained
+   * references to m_array[] may no longer be valid.
+   *
+   * @param value StringBuffer whose contents are to be appended.
+   */
+  public final void append(StringBuffer value)
+  {
+
+    if (value == null) 
+      return;
+    int strlen = value.length();
+
+    if (0 == strlen)
+      return;
+
+    int copyfrom = 0;
+    char[] chunk = m_array[m_lastChunk];
+    int available = m_chunkSize - m_firstFree;
+
+    // Repeat while data remains to be copied
+    while (strlen > 0)
+    {
+
+      // Copy what fits
+      if (available > strlen)
+        available = strlen;
+
+      value.getChars(copyfrom, copyfrom + available, m_array[m_lastChunk],
+                     m_firstFree);
+
+      strlen -= available;
+      copyfrom += available;
+
+      // If there's more left, allocate another chunk and continue
+      if (strlen > 0)
+      {
+
+        // Extend array?
+        int i = m_array.length;
+
+        if (m_lastChunk + 1 == i)
+        {
+          char[][] newarray = new char[i + 16][];
+
+          System.arraycopy(m_array, 0, newarray, 0, i);
+
+          m_array = newarray;
+        }
+
+        // Advance one chunk
+        chunk = m_array[++m_lastChunk];
+
+        if (chunk == null)
+        {
+
+          // Hierarchical encapsulation
+          if (m_lastChunk == 1 << m_rebundleBits
+                  && m_chunkBits < m_maxChunkBits)
+          {
+
+            // Should do all the work of both encapsulating
+            // existing data and establishing new sizes/offsets
+            m_innerFSB = new FastStringBuffer(this);
+          }
+
+          // Add a chunk.
+          chunk = m_array[m_lastChunk] = new char[m_chunkSize];
+        }
+
+        available = m_chunkSize;
+        m_firstFree = 0;
+      }
+    }
+
+    // Adjust the insert point in the last chunk, when we've reached it.
+    m_firstFree += available;
+  }
+
+  /**
+   * Append part of the contents of a Character Array onto the
+   * FastStringBuffer,  growing the storage if necessary.
+   * <p>
+   * NOTE THAT after calling append(), previously obtained
+   * references to m_array[] may no longer be valid.
+   *
+   * @param chars character array from which data is to be copied
+   * @param start offset in chars of first character to be copied,
+   * zero-based.
+   * @param length number of characters to be copied
+   */
+  public final void append(char[] chars, int start, int length)
+  {
+
+    int strlen = length;
+
+    if (0 == strlen)
+      return;
+
+    int copyfrom = start;
+    char[] chunk = m_array[m_lastChunk];
+    int available = m_chunkSize - m_firstFree;
+
+    // Repeat while data remains to be copied
+    while (strlen > 0)
+    {
+
+      // Copy what fits
+      if (available > strlen)
+        available = strlen;
+
+      System.arraycopy(chars, copyfrom, m_array[m_lastChunk], m_firstFree,
+                       available);
+
+      strlen -= available;
+      copyfrom += available;
+
+      // If there's more left, allocate another chunk and continue
+      if (strlen > 0)
+      {
+
+        // Extend array?
+        int i = m_array.length;
+
+        if (m_lastChunk + 1 == i)
+        {
+          char[][] newarray = new char[i + 16][];
+
+          System.arraycopy(m_array, 0, newarray, 0, i);
+
+          m_array = newarray;
+        }
+
+        // Advance one chunk
+        chunk = m_array[++m_lastChunk];
+
+        if (chunk == null)
+        {
+
+          // Hierarchical encapsulation
+          if (m_lastChunk == 1 << m_rebundleBits
+                  && m_chunkBits < m_maxChunkBits)
+          {
+
+            // Should do all the work of both encapsulating
+            // existing data and establishing new sizes/offsets
+            m_innerFSB = new FastStringBuffer(this);
+          }
+
+          // Add a chunk.
+          chunk = m_array[m_lastChunk] = new char[m_chunkSize];
+        }
+
+        available = m_chunkSize;
+        m_firstFree = 0;
+      }
+    }
+
+    // Adjust the insert point in the last chunk, when we've reached it.
+    m_firstFree += available;
+  }
+
+  /**
+   * Append the contents of another FastStringBuffer onto
+   * this FastStringBuffer, growing the storage if necessary.
+   * <p>
+   * NOTE THAT after calling append(), previously obtained
+   * references to m_array[] may no longer be valid.
+   *
+   * @param value FastStringBuffer whose contents are
+   * to be appended.
+   */
+  public final void append(FastStringBuffer value)
+  {
+
+    // Complicating factor here is that the two buffers may use
+    // different chunk sizes, and even if they're the same we're
+    // probably on a different alignment due to previously appended
+    // data. We have to work through the source in bite-sized chunks.
+    if (value == null) 
+      return;
+    int strlen = value.length();
+
+    if (0 == strlen)
+      return;
+
+    int copyfrom = 0;
+    char[] chunk = m_array[m_lastChunk];
+    int available = m_chunkSize - m_firstFree;
+
+    // Repeat while data remains to be copied
+    while (strlen > 0)
+    {
+
+      // Copy what fits
+      if (available > strlen)
+        available = strlen;
+
+      int sourcechunk = (copyfrom + value.m_chunkSize - 1)
+                        >>> value.m_chunkBits;
+      int sourcecolumn = copyfrom & value.m_chunkMask;
+      int runlength = value.m_chunkSize - sourcecolumn;
+
+      if (runlength > available)
+        runlength = available;
+
+      System.arraycopy(value.m_array[sourcechunk], sourcecolumn,
+                       m_array[m_lastChunk], m_firstFree, runlength);
+
+      if (runlength != available)
+        System.arraycopy(value.m_array[sourcechunk + 1], 0,
+                         m_array[m_lastChunk], m_firstFree + runlength,
+                         available - runlength);
+
+      strlen -= available;
+      copyfrom += available;
+
+      // If there's more left, allocate another chunk and continue
+      if (strlen > 0)
+      {
+
+        // Extend array?
+        int i = m_array.length;
+
+        if (m_lastChunk + 1 == i)
+        {
+          char[][] newarray = new char[i + 16][];
+
+          System.arraycopy(m_array, 0, newarray, 0, i);
+
+          m_array = newarray;
+        }
+
+        // Advance one chunk
+        chunk = m_array[++m_lastChunk];
+
+        if (chunk == null)
+        {
+
+          // Hierarchical encapsulation
+          if (m_lastChunk == 1 << m_rebundleBits
+                  && m_chunkBits < m_maxChunkBits)
+          {
+
+            // Should do all the work of both encapsulating
+            // existing data and establishing new sizes/offsets
+            m_innerFSB = new FastStringBuffer(this);
+          }
+
+          // Add a chunk. 
+          chunk = m_array[m_lastChunk] = new char[m_chunkSize];
+        }
+
+        available = m_chunkSize;
+        m_firstFree = 0;
+      }
+    }
+
+    // Adjust the insert point in the last chunk, when we've reached it.
+    m_firstFree += available;
+  }
+
+  /**
+   * @return true if the specified range of characters are all whitespace,
+   * as defined by XMLCharacterRecognizer.
+   * <p>
+   * CURRENTLY DOES NOT CHECK FOR OUT-OF-RANGE.
+   *
+   * @param start Offset of first character in the range.
+   * @param length Number of characters to send.
+   */
+  public boolean isWhitespace(int start, int length)
+  {
+
+    int sourcechunk = start >>> m_chunkBits;
+    int sourcecolumn = start & m_chunkMask;
+    int available = m_chunkSize - sourcecolumn;
+    boolean chunkOK;
+
+    while (length > 0)
+    {
+      int runlength = (length <= available) ? length : available;
+
+      if (sourcechunk == 0 && m_innerFSB != null)
+        chunkOK = m_innerFSB.isWhitespace(sourcecolumn, runlength);
+      else
+        chunkOK = org.apache.xml.utils.XMLCharacterRecognizer.isWhiteSpace(
+          m_array[sourcechunk], sourcecolumn, runlength);
+
+      if (!chunkOK)
+        return false;
+
+      length -= runlength;
+
+      ++sourcechunk;
+
+      sourcecolumn = 0;
+      available = m_chunkSize;
+    }
+
+    return true;
+  }
+
+  /**
+   * @param start Offset of first character in the range.
+   * @param length Number of characters to send.
+   * @return a new String object initialized from the specified range of
+   * characters.
+   */
+  public String getString(int start, int length)
+  {
+    int startColumn = start & m_chunkMask;
+    int startChunk = start >>> m_chunkBits;
+    if (startColumn + length < m_chunkMask && m_innerFSB == null) {
+      return getOneChunkString(startChunk, startColumn, length);
+    }
+    return getString(new StringBuffer(length), startChunk, startColumn,
+                     length).toString();
+  }
+
+  protected String getOneChunkString(int startChunk, int startColumn,
+                                     int length) {
+    return new String(m_array[startChunk], startColumn, length);
+  }
+
+  /**
+   * @param sb StringBuffer to be appended to
+   * @param start Offset of first character in the range.
+   * @param length Number of characters to send.
+   * @return sb with the requested text appended to it
+   */
+  StringBuffer getString(StringBuffer sb, int start, int length)
+  {
+    return getString(sb, start >>> m_chunkBits, start & m_chunkMask, length);
+  }
+
+  /**
+   * Internal support for toString() and getString().
+   * PLEASE NOTE SIGNATURE CHANGE from earlier versions; it now appends into
+   * and returns a StringBuffer supplied by the caller. This simplifies
+   * m_innerFSB support.
+   * <p>
+   * Note that this operation has been somewhat deoptimized by the shift to a
+   * chunked array, as there is no factory method to produce a String object
+   * directly from an array of arrays and hence a double copy is needed.
+   * By presetting length we hope to minimize the heap overhead of building
+   * the intermediate StringBuffer.
+   * <p>
+   * (It really is a pity that Java didn't design String as a final subclass
+   * of MutableString, rather than having StringBuffer be a separate hierarchy.
+   * We'd avoid a <strong>lot</strong> of double-buffering.)
+   *
+   *
+   * @param sb
+   * @param startChunk
+   * @param startColumn
+   * @param length
+   * 
+   * @return the contents of the FastStringBuffer as a standard Java string.
+   */
+  StringBuffer getString(StringBuffer sb, int startChunk, int startColumn,
+                         int length)
+  {
+
+    int stop = (startChunk << m_chunkBits) + startColumn + length;
+    int stopChunk = stop >>> m_chunkBits;
+    int stopColumn = stop & m_chunkMask;
+
+    // Factored out
+    //StringBuffer sb=new StringBuffer(length);
+    for (int i = startChunk; i < stopChunk; ++i)
+    {
+      if (i == 0 && m_innerFSB != null)
+        m_innerFSB.getString(sb, startColumn, m_chunkSize - startColumn);
+      else
+        sb.append(m_array[i], startColumn, m_chunkSize - startColumn);
+
+      startColumn = 0;  // after first chunk
+    }
+
+    if (stopChunk == 0 && m_innerFSB != null)
+      m_innerFSB.getString(sb, startColumn, stopColumn - startColumn);
+    else if (stopColumn > startColumn)
+      sb.append(m_array[stopChunk], startColumn, stopColumn - startColumn);
+
+    return sb;
+  }
+
+  /**
+   * Get a single character from the string buffer.
+   *
+   *
+   * @param pos character position requested.
+   * @return A character from the requested position.
+   */
+  public char charAt(int pos)
+  {
+    int startChunk = pos >>> m_chunkBits;
+
+    if (startChunk == 0 && m_innerFSB != null)
+      return m_innerFSB.charAt(pos & m_chunkMask);
+    else
+      return m_array[startChunk][pos & m_chunkMask];
+  }
+
+  /**
+   * Sends the specified range of characters as one or more SAX characters()
+   * events.
+   * Note that the buffer reference passed to the ContentHandler may be
+   * invalidated if the FastStringBuffer is edited; it's the user's
+   * responsibility to manage access to the FastStringBuffer to prevent this
+   * problem from arising.
+   * <p>
+   * Note too that there is no promise that the output will be sent as a
+   * single call. As is always true in SAX, one logical string may be split
+   * across multiple blocks of memory and hence delivered as several
+   * successive events.
+   *
+   * @param ch SAX ContentHandler object to receive the event.
+   * @param start Offset of first character in the range.
+   * @param length Number of characters to send.
+   * @exception org.xml.sax.SAXException may be thrown by handler's
+   * characters() method.
+   */
+  public void sendSAXcharacters(
+          org.xml.sax.ContentHandler ch, int start, int length)
+            throws org.xml.sax.SAXException
+  {
+
+    int startChunk = start >>> m_chunkBits;
+    int startColumn = start & m_chunkMask;
+    if (startColumn + length < m_chunkMask && m_innerFSB == null) {
+        ch.characters(m_array[startChunk], startColumn, length);
+        return;
+    }
+    
+    int stop = start + length;
+    int stopChunk = stop >>> m_chunkBits;
+    int stopColumn = stop & m_chunkMask;
+
+    for (int i = startChunk; i < stopChunk; ++i)
+    {
+      if (i == 0 && m_innerFSB != null)
+        m_innerFSB.sendSAXcharacters(ch, startColumn,
+                                     m_chunkSize - startColumn);
+      else
+        ch.characters(m_array[i], startColumn, m_chunkSize - startColumn);
+
+      startColumn = 0;  // after first chunk
+    }
+
+    // Last, or only, chunk
+    if (stopChunk == 0 && m_innerFSB != null)
+      m_innerFSB.sendSAXcharacters(ch, startColumn, stopColumn - startColumn);
+    else if (stopColumn > startColumn)
+    {
+      ch.characters(m_array[stopChunk], startColumn,
+                    stopColumn - startColumn);
+    }
+  }
+  
+  /**
+   * Sends the specified range of characters as one or more SAX characters()
+   * events, normalizing the characters according to XSLT rules.
+   *
+   * @param ch SAX ContentHandler object to receive the event.
+   * @param start Offset of first character in the range.
+   * @param length Number of characters to send.
+   * @return normalization status to apply to next chunk (because we may
+   * have been called recursively to process an inner FSB):
+   * <dl>
+   * <dt>0</dt>
+   * <dd>if this output did not end in retained whitespace, and thus whitespace
+   * at the start of the following chunk (if any) should be converted to a
+   * single space.
+   * <dt>SUPPRESS_LEADING_WS</dt>
+   * <dd>if this output ended in retained whitespace, and thus whitespace
+   * at the start of the following chunk (if any) should be completely
+   * suppressed.</dd>
+   * </dd>
+   * </dl>
+   * @exception org.xml.sax.SAXException may be thrown by handler's
+   * characters() method.
+   */
+  public int sendNormalizedSAXcharacters(
+          org.xml.sax.ContentHandler ch, int start, int length)
+            throws org.xml.sax.SAXException
+  {
+	// This call always starts at the beginning of the 
+    // string being written out, either because it was called directly or
+    // because it was an m_innerFSB recursion. This is important since
+	// it gives us a well-known initial state for this flag:
+	int stateForNextChunk=SUPPRESS_LEADING_WS;
+
+    int stop = start + length;
+    int startChunk = start >>> m_chunkBits;
+    int startColumn = start & m_chunkMask;
+    int stopChunk = stop >>> m_chunkBits;
+    int stopColumn = stop & m_chunkMask;
+
+    for (int i = startChunk; i < stopChunk; ++i)
+    {
+      if (i == 0 && m_innerFSB != null)
+				stateForNextChunk=
+        m_innerFSB.sendNormalizedSAXcharacters(ch, startColumn,
+                                     m_chunkSize - startColumn);
+      else
+				stateForNextChunk=
+        sendNormalizedSAXcharacters(m_array[i], startColumn, 
+                                    m_chunkSize - startColumn, 
+																		ch,stateForNextChunk);
+
+      startColumn = 0;  // after first chunk
+    }
+
+    // Last, or only, chunk
+    if (stopChunk == 0 && m_innerFSB != null)
+			stateForNextChunk= // %REVIEW% Is this update really needed?
+      m_innerFSB.sendNormalizedSAXcharacters(ch, startColumn, stopColumn - startColumn);
+    else if (stopColumn > startColumn)
+    {
+			stateForNextChunk= // %REVIEW% Is this update really needed?
+      sendNormalizedSAXcharacters(m_array[stopChunk], 
+																	startColumn, stopColumn - startColumn,
+																	ch, stateForNextChunk | SUPPRESS_TRAILING_WS);
+    }
+		return stateForNextChunk;
+  }
+  
+  static final char[] SINGLE_SPACE = {' '};
+	  
+  /**
+   * Internal method to directly normalize and dispatch the character array.
+   * This version is aware of the fact that it may be called several times
+   * in succession if the data is made up of multiple "chunks", and thus
+   * must actively manage the handling of leading and trailing whitespace.
+   * 
+   * Note: The recursion is due to the possible recursion of inner FSBs.
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @param handler SAX ContentHandler object to receive the event.
+   * @param edgeTreatmentFlags How leading/trailing spaces should be handled. 
+   * This is a bitfield contining two flags, bitwise-ORed together:
+   * <dl>
+   * <dt>SUPPRESS_LEADING_WS</dt>
+   * <dd>When false, causes leading whitespace to be converted to a single
+   * space; when true, causes it to be discarded entirely.
+   * Should be set TRUE for the first chunk, and (in multi-chunk output)
+   * whenever the previous chunk ended in retained whitespace.</dd>
+   * <dt>SUPPRESS_TRAILING_WS</dt>
+   * <dd>When false, causes trailing whitespace to be converted to a single
+   * space; when true, causes it to be discarded entirely.
+   * Should be set TRUE for the last or only chunk.
+   * </dd>
+   * </dl>
+   * @return normalization status, as in the edgeTreatmentFlags parameter:
+   * <dl>
+   * <dt>0</dt>
+   * <dd>if this output did not end in retained whitespace, and thus whitespace
+   * at the start of the following chunk (if any) should be converted to a
+   * single space.
+   * <dt>SUPPRESS_LEADING_WS</dt>
+   * <dd>if this output ended in retained whitespace, and thus whitespace
+   * at the start of the following chunk (if any) should be completely
+   * suppressed.</dd>
+   * </dd>
+   * </dl>
+   *
+   * 
+   * @exception org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  static int sendNormalizedSAXcharacters(char ch[], 
+             int start, int length, 
+             org.xml.sax.ContentHandler handler,
+						 int edgeTreatmentFlags)
+          throws org.xml.sax.SAXException
+  {
+     boolean processingLeadingWhitespace =
+                       ((edgeTreatmentFlags & SUPPRESS_LEADING_WS) != 0);
+     boolean seenWhitespace = ((edgeTreatmentFlags & CARRY_WS) != 0);
+     int currPos = start;
+     int limit = start+length;
+
+     // Strip any leading spaces first, if required
+     if (processingLeadingWhitespace) {
+         for (; currPos < limit
+                && XMLCharacterRecognizer.isWhiteSpace(ch[currPos]);
+              currPos++) { }
+
+         // If we've only encountered leading spaces, the
+         // current state remains unchanged
+         if (currPos == limit) {
+             return edgeTreatmentFlags;
+         }
+     }
+
+     // If we get here, there are no more leading spaces to strip
+     while (currPos < limit) {
+         int startNonWhitespace = currPos;
+
+         // Grab a chunk of non-whitespace characters
+         for (; currPos < limit
+                && !XMLCharacterRecognizer.isWhiteSpace(ch[currPos]);
+              currPos++) { }
+
+         // Non-whitespace seen - emit them, along with a single
+         // space for any preceding whitespace characters
+         if (startNonWhitespace != currPos) {
+             if (seenWhitespace) {
+                 handler.characters(SINGLE_SPACE, 0, 1);
+                 seenWhitespace = false;
+             }
+             handler.characters(ch, startNonWhitespace,
+                                currPos - startNonWhitespace);
+         }
+
+         int startWhitespace = currPos;
+
+         // Consume any whitespace characters
+         for (; currPos < limit
+                && XMLCharacterRecognizer.isWhiteSpace(ch[currPos]);
+              currPos++) { }
+
+         if (startWhitespace != currPos) {
+             seenWhitespace = true;
+         }
+     }
+
+     return (seenWhitespace ? CARRY_WS : 0)
+            | (edgeTreatmentFlags & SUPPRESS_TRAILING_WS);
+  }
+
+  /**
+   * Directly normalize and dispatch the character array.
+   *
+   * @param ch The characters from the XML document.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   * @param handler SAX ContentHandler object to receive the event.
+   * @exception org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   */
+  public static void sendNormalizedSAXcharacters(char ch[], 
+             int start, int length, 
+             org.xml.sax.ContentHandler handler)
+          throws org.xml.sax.SAXException
+  {
+		sendNormalizedSAXcharacters(ch, start, length, 
+             handler, SUPPRESS_BOTH);
+	}
+		
+	/**
+   * Sends the specified range of characters as sax Comment.
+   * <p>
+   * Note that, unlike sendSAXcharacters, this has to be done as a single 
+   * call to LexicalHandler#comment.
+   *
+   * @param ch SAX LexicalHandler object to receive the event.
+   * @param start Offset of first character in the range.
+   * @param length Number of characters to send.
+   * @exception org.xml.sax.SAXException may be thrown by handler's
+   * characters() method.
+   */
+  public void sendSAXComment(
+          org.xml.sax.ext.LexicalHandler ch, int start, int length)
+            throws org.xml.sax.SAXException
+  {
+
+    // %OPT% Do it this way for now...
+    String comment = getString(start, length);
+    ch.comment(comment.toCharArray(), 0, length);
+  }
+
+  /**
+   * Copies characters from this string into the destination character
+   * array.
+   *
+   * @param      srcBegin   index of the first character in the string
+   *                        to copy.
+   * @param      srcEnd     index after the last character in the string
+   *                        to copy.
+   * @param      dst        the destination array.
+   * @param      dstBegin   the start offset in the destination array.
+   * @exception IndexOutOfBoundsException If any of the following
+   *            is true:
+   *            <ul><li><code>srcBegin</code> is negative.
+   *            <li><code>srcBegin</code> is greater than <code>srcEnd</code>
+   *            <li><code>srcEnd</code> is greater than the length of this
+   *                string
+   *            <li><code>dstBegin</code> is negative
+   *            <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
+   *                <code>dst.length</code></ul>
+   * @exception NullPointerException if <code>dst</code> is <code>null</code>
+   */
+  private void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
+  {
+    // %TBD% Joe needs to write this function.  Make public when implemented.
+  }
+
+  /**
+   * Encapsulation c'tor. After this is called, the source FastStringBuffer
+   * will be reset to use the new object as its m_innerFSB, and will have
+   * had its chunk size reset appropriately. IT SHOULD NEVER BE CALLED
+   * EXCEPT WHEN source.length()==1<<(source.m_chunkBits+source.m_rebundleBits)
+   *
+   * NEEDSDOC @param source
+   */
+  private FastStringBuffer(FastStringBuffer source)
+  {
+
+    // Copy existing information into new encapsulation
+    m_chunkBits = source.m_chunkBits;
+    m_maxChunkBits = source.m_maxChunkBits;
+    m_rebundleBits = source.m_rebundleBits;
+    m_chunkSize = source.m_chunkSize;
+    m_chunkMask = source.m_chunkMask;
+    m_array = source.m_array;
+    m_innerFSB = source.m_innerFSB;
+
+    // These have to be adjusted because we're calling just at the time
+    // when we would be about to allocate another chunk
+    m_lastChunk = source.m_lastChunk - 1;
+    m_firstFree = source.m_chunkSize;
+
+    // Establish capsule as the Inner FSB, reset chunk sizes/addressing
+    source.m_array = new char[16][];
+    source.m_innerFSB = this;
+
+    // Since we encapsulated just as we were about to append another
+    // chunk, return ready to create the chunk after the innerFSB
+    // -- 1, not 0.
+    source.m_lastChunk = 1;
+    source.m_firstFree = 0;
+    source.m_chunkBits += m_rebundleBits;
+    source.m_chunkSize = 1 << (source.m_chunkBits);
+    source.m_chunkMask = source.m_chunkSize - 1;
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/IntStack.java b/src/main/java/org/apache/xml/utils/IntStack.java
new file mode 100644
index 0000000..0212cef
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/IntStack.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: IntStack.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.util.EmptyStackException;
+
+/**
+ * Implement a stack of simple integers.
+ *
+ * %OPT%
+ * This is currently based on IntVector, which permits fast acess but pays a
+ * heavy recopying penalty if/when its size is increased. If we expect deep
+ * stacks, we should consider a version based on ChunkedIntVector.
+ * @xsl.usage internal
+ */
+public class IntStack extends IntVector
+{
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is very small, for small lists.
+   */
+  public IntStack()
+  {
+    super();
+  }
+
+  /**
+   * Construct a IntVector, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public IntStack(int blocksize)
+  {
+    super(blocksize);
+  }
+  
+  /**
+   * Copy constructor for IntStack
+   * 
+   * @param v IntStack to copy
+   */
+  public IntStack (IntStack v)
+  {
+  	super(v);
+  }
+
+  /**
+   * Pushes an item onto the top of this stack.
+   *
+   * @param   i   the int to be pushed onto this stack.
+   * @return  the <code>item</code> argument.
+   */
+  public int push(int i)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      int newMap[] = new int[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    m_map[m_firstFree] = i;
+
+    m_firstFree++;
+
+    return i;
+  }
+
+  /**
+   * Removes the object at the top of this stack and returns that
+   * object as the value of this function.
+   *
+   * @return     The object at the top of this stack.
+   */
+  public final int pop()
+  {
+    return m_map[--m_firstFree];
+  }
+
+  /**
+   * Quickly pops a number of items from the stack.
+   */
+
+  public final void quickPop(int n)
+  {
+    m_firstFree -= n;
+  }
+
+  /**
+   * Looks at the object at the top of this stack without removing it
+   * from the stack.
+   *
+   * @return     the object at the top of this stack.
+   * @throws  EmptyStackException  if this stack is empty.
+   */
+  public final int peek()
+  {
+    try {
+      return m_map[m_firstFree - 1];
+    }
+    catch (ArrayIndexOutOfBoundsException e)
+    {
+      throw new EmptyStackException();
+    }
+  }
+
+  /**
+   * Looks at the object at the position the stack counting down n items.
+   *
+   * @param n The number of items down, indexed from zero.
+   * @return     the object at n items down.
+   * @throws  EmptyStackException  if this stack is empty.
+   */
+  public int peek(int n)
+  {
+    try {
+      return m_map[m_firstFree-(1+n)];
+    }
+    catch (ArrayIndexOutOfBoundsException e)
+    {
+      throw new EmptyStackException();
+    }
+  }
+
+  /**
+   * Sets an object at a the top of the statck
+   *
+   *
+   * @param val object to set at the top
+   * @throws  EmptyStackException  if this stack is empty.
+   */
+  public void setTop(int val)
+  {
+    try {
+      m_map[m_firstFree - 1] = val;
+    }
+    catch (ArrayIndexOutOfBoundsException e)
+    {
+      throw new EmptyStackException();
+    }
+  }
+
+  /**
+   * Tests if this stack is empty.
+   *
+   * @return  <code>true</code> if this stack is empty;
+   *          <code>false</code> otherwise.
+   * @since   JDK1.0
+   */
+  public boolean empty()
+  {
+    return m_firstFree == 0;
+  }
+
+  /**
+   * Returns where an object is on this stack.
+   *
+   * @param   o   the desired object.
+   * @return  the distance from the top of the stack where the object is]
+   *          located; the return value <code>-1</code> indicates that the
+   *          object is not on the stack.
+   * @since   JDK1.0
+   */
+  public int search(int o)
+  {
+
+    int i = lastIndexOf(o);
+
+    if (i >= 0)
+    {
+      return size() - i;
+    }
+
+    return -1;
+  }
+  
+  /**
+   * Returns clone of current IntStack
+   * 
+   * @return clone of current IntStack
+   */
+  public Object clone()
+    throws CloneNotSupportedException
+  {
+  	return (IntStack) super.clone();
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/IntVector.java b/src/main/java/org/apache/xml/utils/IntVector.java
new file mode 100644
index 0000000..f815e4f
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/IntVector.java
@@ -0,0 +1,419 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: IntVector.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * A very simple table that stores a list of int.
+ *
+ * This version is based on a "realloc" strategy -- a simle array is
+ * used, and when more storage is needed, a larger array is obtained
+ * and all existing data is recopied into it. As a result, read/write
+ * access to existing nodes is O(1) fast but appending may be O(N**2)
+ * slow. See also SuballocatedIntVector.
+ * @xsl.usage internal
+ */
+public class IntVector implements Cloneable
+{
+
+  /** Size of blocks to allocate          */
+  protected int m_blocksize;
+
+  /** Array of ints          */
+  protected int m_map[]; // IntStack is trying to see this directly
+
+  /** Number of ints in array          */
+  protected int m_firstFree = 0;
+
+  /** Size of array          */
+  protected int m_mapSize;
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is very small, for small lists.
+   */
+  public IntVector()
+  {
+
+    m_blocksize = 32;
+    m_mapSize = m_blocksize;
+    m_map = new int[m_blocksize];
+  }
+
+  /**
+   * Construct a IntVector, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public IntVector(int blocksize)
+  {
+
+    m_blocksize = blocksize;
+    m_mapSize = blocksize;
+    m_map = new int[blocksize];
+  }
+  
+  /**
+   * Construct a IntVector, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public IntVector(int blocksize, int increaseSize)
+  {
+
+    m_blocksize = increaseSize;
+    m_mapSize = blocksize;
+    m_map = new int[blocksize];
+  }
+
+  /**
+   * Copy constructor for IntVector
+   * 
+   * @param v Existing IntVector to copy
+   */
+  public IntVector(IntVector v)
+  {
+  	m_map = new int[v.m_mapSize];
+    m_mapSize = v.m_mapSize;
+    m_firstFree = v.m_firstFree;
+  	m_blocksize = v.m_blocksize;
+  	System.arraycopy(v.m_map, 0, m_map, 0, m_firstFree);
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return length of the list
+   */
+  public final int size()
+  {
+    return m_firstFree;
+  }
+  
+  /**
+   * Get the length of the list.
+   *
+   * @return length of the list
+   */
+  public final void setSize(int sz)
+  {
+    m_firstFree = sz;
+  }
+
+
+  /**
+   * Append a int onto the vector.
+   *
+   * @param value Int to add to the list 
+   */
+  public final void addElement(int value)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      int newMap[] = new int[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    m_map[m_firstFree] = value;
+
+    m_firstFree++;
+  }
+  
+  /**
+   * Append several int values onto the vector.
+   *
+   * @param value Int to add to the list 
+   */
+  public final void addElements(int value, int numberOfElements)
+  {
+
+    if ((m_firstFree + numberOfElements) >= m_mapSize)
+    {
+      m_mapSize += (m_blocksize+numberOfElements);
+
+      int newMap[] = new int[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    for (int i = 0; i < numberOfElements; i++) 
+    {
+      m_map[m_firstFree] = value;
+      m_firstFree++;
+    }
+  }
+  
+  /**
+   * Append several slots onto the vector, but do not set the values.
+   *
+   * @param numberOfElements Int to add to the list 
+   */
+  public final void addElements(int numberOfElements)
+  {
+
+    if ((m_firstFree + numberOfElements) >= m_mapSize)
+    {
+      m_mapSize += (m_blocksize+numberOfElements);
+
+      int newMap[] = new int[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+    
+    m_firstFree += numberOfElements;
+  }
+  
+
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   *
+   * @param value Int to insert
+   * @param at Index of where to insert 
+   */
+  public final void insertElementAt(int value, int at)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      int newMap[] = new int[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    if (at <= (m_firstFree - 1))
+    {
+      System.arraycopy(m_map, at, m_map, at + 1, m_firstFree - at);
+    }
+
+    m_map[at] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   */
+  public final void removeAllElements()
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      m_map[i] = java.lang.Integer.MIN_VALUE;
+    }
+
+    m_firstFree = 0;
+  }
+
+  /**
+   * Removes the first occurrence of the argument from this vector.
+   * If the object is found in this vector, each component in the vector
+   * with an index greater or equal to the object's index is shifted
+   * downward to have an index one smaller than the value it had
+   * previously.
+   *
+   * @param s Int to remove from array
+   *
+   * @return True if the int was removed, false if it was not found
+   */
+  public final boolean removeElement(int s)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i] == s)
+      {
+        if ((i + 1) < m_firstFree)
+          System.arraycopy(m_map, i + 1, m_map, i - 1, m_firstFree - i);
+        else
+          m_map[i] = java.lang.Integer.MIN_VALUE;
+
+        m_firstFree--;
+
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Deletes the component at the specified index. Each component in
+   * this vector with an index greater or equal to the specified
+   * index is shifted downward to have an index one smaller than
+   * the value it had previously.
+   *
+   * @param i index of where to remove and int
+   */
+  public final void removeElementAt(int i)
+  {
+
+    if (i > m_firstFree)
+      System.arraycopy(m_map, i + 1, m_map, i, m_firstFree);
+    else
+      m_map[i] = java.lang.Integer.MIN_VALUE;
+
+    m_firstFree--;
+  }
+
+  /**
+   * Sets the component at the specified index of this vector to be the
+   * specified object. The previous component at that position is discarded.
+   *
+   * The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.
+   *
+   * @param value object to set
+   * @param index Index of where to set the object
+   */
+  public final void setElementAt(int value, int index)
+  {
+    m_map[index] = value;
+  }
+
+  /**
+   * Get the nth element.
+   *
+   * @param i index of object to get
+   *
+   * @return object at given index
+   */
+  public final int elementAt(int i)
+  {
+    return m_map[i];
+  }
+
+  /**
+   * Tell if the table contains the given node.
+   *
+   * @param s object to look for
+   *
+   * @return true if the object is in the list
+   */
+  public final boolean contains(int s)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i] == s)
+        return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem object to look for
+   * @param index Index of where to begin search
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public final int indexOf(int elem, int index)
+  {
+
+    for (int i = index; i < m_firstFree; i++)
+    {
+      if (m_map[i] == elem)
+        return i;
+    }
+
+    return java.lang.Integer.MIN_VALUE;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem object to look for
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public final int indexOf(int elem)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i] == elem)
+        return i;
+    }
+
+    return java.lang.Integer.MIN_VALUE;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Object to look for
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public final int lastIndexOf(int elem)
+  {
+
+    for (int i = (m_firstFree - 1); i >= 0; i--)
+    {
+      if (m_map[i] == elem)
+        return i;
+    }
+
+    return java.lang.Integer.MIN_VALUE;
+  }
+  
+  /**
+   * Returns clone of current IntVector
+   * 
+   * @return clone of current IntVector
+   */
+  public Object clone()
+    throws CloneNotSupportedException
+  {
+  	return new IntVector(this);
+  }
+  
+}
diff --git a/src/main/java/org/apache/xml/utils/NSInfo.java b/src/main/java/org/apache/xml/utils/NSInfo.java
new file mode 100644
index 0000000..70f4be2
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/NSInfo.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NSInfo.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * This class holds information about the namespace info
+ * of a node.  It is used to optimize namespace lookup in
+ * a generic DOM.
+ * @xsl.usage internal
+ */
+public class NSInfo
+{
+
+  /**
+   * Constructor NSInfo
+   *
+   *
+   * @param hasProcessedNS Flag indicating whether namespaces
+   * have been processed for this node 
+   * @param hasXMLNSAttrs Flag indicating whether this node
+   * has XMLNS attributes. 
+   */
+  public NSInfo(boolean hasProcessedNS, boolean hasXMLNSAttrs)
+  {
+
+    m_hasProcessedNS = hasProcessedNS;
+    m_hasXMLNSAttrs = hasXMLNSAttrs;
+    m_namespace = null;
+    m_ancestorHasXMLNSAttrs = ANCESTORXMLNSUNPROCESSED;
+  }
+
+  // Unused at the moment
+
+  /**
+   * Constructor NSInfo
+   *
+   *
+   * @param hasProcessedNS Flag indicating whether namespaces
+   * have been processed for this node 
+   * @param hasXMLNSAttrs Flag indicating whether this node
+   * has XMLNS attributes. 
+   * @param ancestorHasXMLNSAttrs Flag indicating whether one of this node's
+   * ancestor has XMLNS attributes.
+   */
+  public NSInfo(boolean hasProcessedNS, boolean hasXMLNSAttrs,
+                int ancestorHasXMLNSAttrs)
+  {
+
+    m_hasProcessedNS = hasProcessedNS;
+    m_hasXMLNSAttrs = hasXMLNSAttrs;
+    m_ancestorHasXMLNSAttrs = ancestorHasXMLNSAttrs;
+    m_namespace = null;
+  }
+
+  /**
+   * Constructor NSInfo
+   *
+   *
+   * @param namespace The namespace URI 
+   * @param hasXMLNSAttrs Flag indicating whether this node
+   * has XMLNS attributes.
+   */
+  public NSInfo(String namespace, boolean hasXMLNSAttrs)
+  {
+
+    m_hasProcessedNS = true;
+    m_hasXMLNSAttrs = hasXMLNSAttrs;
+    m_namespace = namespace;
+    m_ancestorHasXMLNSAttrs = ANCESTORXMLNSUNPROCESSED;
+  }
+
+  /** The namespace URI          */
+  public String m_namespace;
+
+  /** Flag indicating whether this node has an XMLNS attribute          */
+  public boolean m_hasXMLNSAttrs;
+
+  /** Flag indicating whether namespaces have been processed for this node */
+  public boolean m_hasProcessedNS;
+
+  /** Flag indicating whether one of this node's ancestor has an XMLNS attribute          */
+  public int m_ancestorHasXMLNSAttrs;
+
+  /** Constant for ancestors XMLNS atributes not processed          */
+  public static final int ANCESTORXMLNSUNPROCESSED = 0;
+
+  /** Constant indicating an ancestor has an XMLNS attribute           */
+  public static final int ANCESTORHASXMLNS = 1;
+
+  /** Constant indicating ancestors don't have an XMLNS attribute           */
+  public static final int ANCESTORNOXMLNS = 2;
+}
diff --git a/src/main/java/org/apache/xml/utils/NameSpace.java b/src/main/java/org/apache/xml/utils/NameSpace.java
new file mode 100644
index 0000000..0387df1
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/NameSpace.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NameSpace.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.io.Serializable;
+
+/**
+ * A representation of a namespace.  One of these will
+ * be pushed on the namespace stack for each
+ * element.
+ * @xsl.usage advanced
+ */
+public class NameSpace implements Serializable
+{
+    static final long serialVersionUID = 1471232939184881839L;
+
+  /** Next NameSpace element on the stack.
+   *  @serial             */
+  public NameSpace m_next = null;
+
+  /** Prefix of this NameSpace element.
+   *  @serial          */
+  public String m_prefix;
+
+  /** Namespace URI of this NameSpace element.
+   *  @serial           */
+  public String m_uri;  // if null, then Element namespace is empty.
+
+  /**
+   * Construct a namespace for placement on the
+   * result tree namespace stack.
+   *
+   * @param prefix Prefix of this element
+   * @param uri URI of  this element
+   */
+  public NameSpace(String prefix, String uri)
+  {
+    m_prefix = prefix;
+    m_uri = uri;
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/NamespaceSupport2.java b/src/main/java/org/apache/xml/utils/NamespaceSupport2.java
new file mode 100644
index 0000000..de7eb16
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/NamespaceSupport2.java
@@ -0,0 +1,749 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NamespaceSupport2.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.util.EmptyStackException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * Encapsulate Namespace tracking logic for use by SAX drivers.
+ *
+ * <p>This class is an attempt to rewrite the SAX NamespaceSupport
+ * "helper" class for improved efficiency. It can be used to track the
+ * namespace declarations currently in scope, providing lookup
+ * routines to map prefixes to URIs and vice versa.</p>
+ *
+ * <p>ISSUE: For testing purposes, I've extended NamespaceSupport even
+ * though I'm completely reasserting all behaviors and fields.
+ * Wasteful.... But SAX did not put an interface under that object and
+ * we seem to have written that SAX class into our APIs... and I don't
+ * want to argue with it right now. </p>
+ *
+ * @see org.xml.sax.helpers.NamespaceSupport
+ * */
+public class NamespaceSupport2
+    extends org.xml.sax.helpers.NamespaceSupport
+{
+    ////////////////////////////////////////////////////////////////////
+    // Internal state.
+    ////////////////////////////////////////////////////////////////////
+
+    private Context2 currentContext; // Current point on the double-linked stack
+
+
+    ////////////////////////////////////////////////////////////////////
+    // Constants.
+    ////////////////////////////////////////////////////////////////////
+
+
+    /**
+     * The XML Namespace as a constant.
+     *
+     * <p>This is the Namespace URI that is automatically mapped
+     * to the "xml" prefix.</p>
+     */
+    public final static String XMLNS =
+        "http://www.w3.org/XML/1998/namespace";
+
+
+    ////////////////////////////////////////////////////////////////////
+    // Constructor.
+    ////////////////////////////////////////////////////////////////////
+
+
+    /**
+     * Create a new Namespace support object.
+     */
+    public NamespaceSupport2 ()
+    {
+        reset();
+    }
+
+
+    ////////////////////////////////////////////////////////////////////
+    // Context management.
+    ////////////////////////////////////////////////////////////////////
+
+
+    /**
+     * Reset this Namespace support object for reuse.
+     *
+     * <p>It is necessary to invoke this method before reusing the
+     * Namespace support object for a new session.</p>
+     */
+    public void reset ()
+    {
+        // Discarding the whole stack doesn't save us a lot versus
+        // creating a new NamespaceSupport. Do we care, or should we
+        // change this to just reset the root context?
+        currentContext = new Context2(null);
+        currentContext.declarePrefix("xml", XMLNS);
+    }
+
+
+    /**
+     * Start a new Namespace context.
+     *
+     * <p>Normally, you should push a new context at the beginning
+     * of each XML element: the new context will automatically inherit
+     * the declarations of its parent context, but it will also keep
+     * track of which declarations were made within this context.</p>
+     *
+     * <p>The Namespace support object always starts with a base context
+     * already in force: in this context, only the "xml" prefix is
+     * declared.</p>
+     *
+     * @see #popContext
+     */
+    public void pushContext ()
+    {
+        // JJK: Context has a parent pointer.
+        // That means we don't need a stack to pop.
+        // We may want to retain for reuse, but that can be done via
+        // a child pointer.
+
+        Context2 parentContext=currentContext;
+        currentContext = parentContext.getChild();
+        if (currentContext == null){
+                currentContext = new Context2(parentContext);
+            }
+        else{
+            // JJK: This will wipe out any leftover data
+            // if we're reusing a previously allocated Context.
+            currentContext.setParent(parentContext);
+        }
+    }
+
+
+    /**
+     * Revert to the previous Namespace context.
+     *
+     * <p>Normally, you should pop the context at the end of each
+     * XML element.  After popping the context, all Namespace prefix
+     * mappings that were previously in force are restored.</p>
+     *
+     * <p>You must not attempt to declare additional Namespace
+     * prefixes after popping a context, unless you push another
+     * context first.</p>
+     *
+     * @see #pushContext
+     */
+    public void popContext ()
+    {
+        Context2 parentContext=currentContext.getParent();
+        if(parentContext==null)
+            throw new EmptyStackException();
+        else
+            currentContext = parentContext;
+    }
+
+
+
+    ////////////////////////////////////////////////////////////////////
+    // Operations within a context.
+    ////////////////////////////////////////////////////////////////////
+
+
+    /**
+     * Declare a Namespace prefix.
+     *
+     * <p>This method declares a prefix in the current Namespace
+     * context; the prefix will remain in force until this context
+     * is popped, unless it is shadowed in a descendant context.</p>
+     *
+     * <p>To declare a default Namespace, use the empty string.  The
+     * prefix must not be "xml" or "xmlns".</p>
+     *
+     * <p>Note that you must <em>not</em> declare a prefix after
+     * you've pushed and popped another Namespace.</p>
+     *
+     * <p>Note that there is an asymmetry in this library: while {@link
+     * #getPrefix getPrefix} will not return the default "" prefix,
+     * even if you have declared one; to check for a default prefix,
+     * you have to look it up explicitly using {@link #getURI getURI}.
+     * This asymmetry exists to make it easier to look up prefixes
+     * for attribute names, where the default prefix is not allowed.</p>
+     *
+     * @param prefix The prefix to declare, or null for the empty
+     *        string.
+     * @param uri The Namespace URI to associate with the prefix.
+     * @return true if the prefix was legal, false otherwise
+     * @see #processName
+     * @see #getURI
+     * @see #getPrefix
+     */
+    public boolean declarePrefix (String prefix, String uri)
+    {
+        if (prefix.equals("xml") || prefix.equals("xmlns")) {
+            return false;
+        } else {
+            currentContext.declarePrefix(prefix, uri);
+            return true;
+        }
+    }
+
+
+    /**
+     * Process a raw XML 1.0 name.
+     *
+     * <p>This method processes a raw XML 1.0 name in the current
+     * context by removing the prefix and looking it up among the
+     * prefixes currently declared.  The return value will be the
+     * array supplied by the caller, filled in as follows:</p>
+     *
+     * <dl>
+     * <dt>parts[0]</dt>
+     * <dd>The Namespace URI, or an empty string if none is
+     *  in use.</dd>
+     * <dt>parts[1]</dt>
+     * <dd>The local name (without prefix).</dd>
+     * <dt>parts[2]</dt>
+     * <dd>The original raw name.</dd>
+     * </dl>
+     *
+     * <p>All of the strings in the array will be internalized.  If
+     * the raw name has a prefix that has not been declared, then
+     * the return value will be null.</p>
+     *
+     * <p>Note that attribute names are processed differently than
+     * element names: an unprefixed element name will received the
+     * default Namespace (if any), while an unprefixed element name
+     * will not.</p>
+     *
+     * @param qName The raw XML 1.0 name to be processed.
+     * @param parts A string array supplied by the caller, capable of
+     *        holding at least three members.
+     * @param isAttribute A flag indicating whether this is an
+     *        attribute name (true) or an element name (false).
+     * @return The supplied array holding three internalized strings 
+     *        representing the Namespace URI (or empty string), the
+     *        local name, and the raw XML 1.0 name; or null if there
+     *        is an undeclared prefix.
+     * @see #declarePrefix
+     * @see java.lang.String#intern */
+    public String [] processName (String qName, String[] parts,
+                                  boolean isAttribute)
+    {
+        String[] name=currentContext.processName(qName, isAttribute);
+        if(name==null)
+            return null;
+
+        // JJK: This recopying is required because processName may return
+        // a cached result. I Don't Like It. *****
+        System.arraycopy(name,0,parts,0,3);
+        return parts;
+    }
+
+
+    /**
+     * Look up a prefix and get the currently-mapped Namespace URI.
+     *
+     * <p>This method looks up the prefix in the current context.
+     * Use the empty string ("") for the default Namespace.</p>
+     *
+     * @param prefix The prefix to look up.
+     * @return The associated Namespace URI, or null if the prefix
+     *         is undeclared in this context.
+     * @see #getPrefix
+     * @see #getPrefixes
+     */
+    public String getURI (String prefix)
+    {
+        return currentContext.getURI(prefix);
+    }
+
+
+    /**
+     * Return an enumeration of all prefixes currently declared.
+     *
+     * <p><strong>Note:</strong> if there is a default prefix, it will not be
+     * returned in this enumeration; check for the default prefix
+     * using the {@link #getURI getURI} with an argument of "".</p>
+     *
+     * @return An enumeration of all prefixes declared in the
+     *         current context except for the empty (default)
+     *         prefix.
+     * @see #getDeclaredPrefixes
+     * @see #getURI
+     */
+    public Enumeration getPrefixes ()
+    {
+        return currentContext.getPrefixes();
+    }
+
+
+    /**
+     * Return one of the prefixes mapped to a Namespace URI.
+     *
+     * <p>If more than one prefix is currently mapped to the same
+     * URI, this method will make an arbitrary selection; if you
+     * want all of the prefixes, use the {@link #getPrefixes}
+     * method instead.</p>
+     *
+     * <p><strong>Note:</strong> this will never return the empty
+     * (default) prefix; to check for a default prefix, use the {@link
+     * #getURI getURI} method with an argument of "".</p>
+     *
+     * @param uri The Namespace URI.
+     * @return One of the prefixes currently mapped to the URI supplied,
+     *         or null if none is mapped or if the URI is assigned to
+     *         the default Namespace.
+     * @see #getPrefixes(java.lang.String)
+     * @see #getURI */
+    public String getPrefix (String uri)
+    {
+        return currentContext.getPrefix(uri);
+    }
+
+
+    /**
+     * Return an enumeration of all prefixes currently declared for a URI.
+     *
+     * <p>This method returns prefixes mapped to a specific Namespace
+     * URI.  The xml: prefix will be included.  If you want only one
+     * prefix that's mapped to the Namespace URI, and you don't care 
+     * which one you get, use the {@link #getPrefix getPrefix}
+     *  method instead.</p>
+     *
+     * <p><strong>Note:</strong> the empty (default) prefix is
+     * <em>never</em> included in this enumeration; to check for the
+     * presence of a default Namespace, use the {@link #getURI getURI}
+     * method with an argument of "".</p>
+     *
+     * @param uri The Namespace URI.
+     * @return An enumeration of all prefixes declared in the
+     *         current context.
+     * @see #getPrefix
+     * @see #getDeclaredPrefixes
+     * @see #getURI */
+    public Enumeration getPrefixes (String uri)
+    {
+        // JJK: The old code involved creating a vector, filling it
+        // with all the matching prefixes, and then getting its
+        // elements enumerator. Wastes storage, wastes cycles if we
+        // don't actually need them all. Better to either implement
+        // a specific enumerator for these prefixes... or a filter
+        // around the all-prefixes enumerator, which comes out to
+        // roughly the same thing.
+        //
+        // **** Currently a filter. That may not be most efficient
+        // when I'm done restructuring storage!
+        return new PrefixForUriEnumerator(this,uri,getPrefixes());       
+    }
+    
+
+    /**
+     * Return an enumeration of all prefixes declared in this context.
+     *
+     * <p>The empty (default) prefix will be included in this 
+     * enumeration; note that this behaviour differs from that of
+     * {@link #getPrefix} and {@link #getPrefixes}.</p>
+     *
+     * @return An enumeration of all prefixes declared in this
+     *         context.
+     * @see #getPrefixes
+     * @see #getURI
+     */
+    public Enumeration getDeclaredPrefixes ()
+    {
+        return currentContext.getDeclaredPrefixes();
+    }
+
+
+
+}
+
+////////////////////////////////////////////////////////////////////
+// Local classes.
+// These were _internal_ classes... but in fact they don't have to be,
+// and may be more efficient if they aren't. 
+////////////////////////////////////////////////////////////////////
+
+/**
+ * Implementation of Enumeration filter, wrapped
+ * aroung the get-all-prefixes version of the operation. This is NOT
+ * necessarily the most efficient approach; finding the URI and then asking
+ * what prefixes apply to it might make much more sense.
+ */
+class PrefixForUriEnumerator implements Enumeration
+{
+    private Enumeration allPrefixes;
+    private String uri;
+    private String lookahead=null;
+    private NamespaceSupport2 nsup;
+     
+    // Kluge: Since one can't do a constructor on an
+    // anonymous class (as far as I know)...
+    PrefixForUriEnumerator(NamespaceSupport2 nsup,String uri, Enumeration allPrefixes)
+    {
+	this.nsup=nsup;
+        this.uri=uri;
+        this.allPrefixes=allPrefixes;
+    }
+        
+    public boolean hasMoreElements()
+    {
+        if(lookahead!=null)
+            return true;
+            
+        while(allPrefixes.hasMoreElements())
+            {
+                String prefix=(String)allPrefixes.nextElement();
+                if(uri.equals(nsup.getURI(prefix)))
+                    {
+                        lookahead=prefix;
+                        return true;
+                    }
+            }
+        return false;
+    }
+        
+    public Object nextElement()
+    {
+        if(hasMoreElements())
+            {
+                String tmp=lookahead;
+                lookahead=null;
+                return tmp;
+            }
+        else
+            throw new java.util.NoSuchElementException();
+    }
+}
+
+/**
+ * Internal class for a single Namespace context.
+ *
+ * <p>This module caches and reuses Namespace contexts, so the number allocated
+ * will be equal to the element depth of the document, not to the total
+ * number of elements (i.e. 5-10 rather than tens of thousands).</p>
+ */
+final class Context2 {
+
+    ////////////////////////////////////////////////////////////////
+    // Manefest Constants
+    ////////////////////////////////////////////////////////////////
+        
+    /**
+     * An empty enumeration.
+     */
+    private final static Enumeration EMPTY_ENUMERATION =
+        new Vector().elements();
+
+    ////////////////////////////////////////////////////////////////
+    // Protected state.
+    ////////////////////////////////////////////////////////////////
+        
+    Hashtable prefixTable;
+    Hashtable uriTable;
+    Hashtable elementNameTable;
+    Hashtable attributeNameTable;
+    String defaultNS = null;
+
+    ////////////////////////////////////////////////////////////////
+    // Internal state.
+    ////////////////////////////////////////////////////////////////
+        
+    private Vector declarations = null;
+    private boolean tablesDirty = false;
+    private Context2 parent = null;
+    private Context2 child = null;
+
+    /**
+     * Create a new Namespace context.
+     */
+    Context2 (Context2 parent)
+    {
+        if(parent==null)
+            {
+                prefixTable = new Hashtable();
+                uriTable = new Hashtable();
+                elementNameTable=null; 
+                attributeNameTable=null; 
+            }
+        else
+            setParent(parent);
+    }
+
+        
+    /**
+     * @returns The child Namespace context object, or null if this
+     * is the last currently on the chain.
+     */
+    Context2 getChild()
+    {
+        return child;
+    }
+        
+    /**
+     * @returns The parent Namespace context object, or null if this
+     * is the root.
+     */
+    Context2 getParent()
+    {
+        return parent;
+    }
+        
+    /**
+     * (Re)set the parent of this Namespace context.
+     * This is separate from the c'tor because it's re-applied
+     * when a Context2 is reused by push-after-pop.
+     *
+     * @param context The parent Namespace context object.
+     */
+    void setParent (Context2 parent)
+    {
+        this.parent = parent;
+        parent.child = this;        // JJK: Doubly-linked
+        declarations = null;
+        prefixTable = parent.prefixTable;
+        uriTable = parent.uriTable;
+        elementNameTable = parent.elementNameTable;
+        attributeNameTable = parent.attributeNameTable;
+        defaultNS = parent.defaultNS;
+        tablesDirty = false;
+    }
+        
+        
+    /**
+     * Declare a Namespace prefix for this context.
+     *
+     * @param prefix The prefix to declare.
+     * @param uri The associated Namespace URI.
+     * @see org.xml.sax.helpers.NamespaceSupport2#declarePrefix
+     */
+    void declarePrefix (String prefix, String uri)
+    {
+                                // Lazy processing...
+        if (!tablesDirty) {
+            copyTables();
+        }
+        if (declarations == null) {
+            declarations = new Vector();
+        }
+            
+        prefix = prefix.intern();
+        uri = uri.intern();
+        if ("".equals(prefix)) {
+            if ("".equals(uri)) {
+                defaultNS = null;
+            } else {
+                defaultNS = uri;
+            }
+        } else {
+            prefixTable.put(prefix, uri);
+            uriTable.put(uri, prefix); // may wipe out another prefix
+        }
+        declarations.addElement(prefix);
+    }
+
+
+    /**
+     * Process a raw XML 1.0 name in this context.
+     *
+     * @param qName The raw XML 1.0 name.
+     * @param isAttribute true if this is an attribute name.
+     * @return An array of three strings containing the
+     *         URI part (or empty string), the local part,
+     *         and the raw name, all internalized, or null
+     *         if there is an undeclared prefix.
+     * @see org.xml.sax.helpers.NamespaceSupport2#processName
+     */
+    String [] processName (String qName, boolean isAttribute)
+    {
+        String name[];
+        Hashtable table;
+            
+                                // Select the appropriate table.
+        if (isAttribute) {
+            if(elementNameTable==null)
+                elementNameTable=new Hashtable();
+            table = elementNameTable;
+        } else {
+            if(attributeNameTable==null)
+                attributeNameTable=new Hashtable();
+            table = attributeNameTable;
+        }
+            
+                                // Start by looking in the cache, and
+                                // return immediately if the name
+                                // is already known in this content
+        name = (String[])table.get(qName);
+        if (name != null) {
+            return name;
+        }
+            
+                                // We haven't seen this name in this
+                                // context before.
+        name = new String[3];
+        int index = qName.indexOf(':');
+            
+            
+                                // No prefix.
+        if (index == -1) {
+            if (isAttribute || defaultNS == null) {
+                name[0] = "";
+            } else {
+                name[0] = defaultNS;
+            }
+            name[1] = qName.intern();
+            name[2] = name[1];
+        }
+            
+                                // Prefix
+        else {
+            String prefix = qName.substring(0, index);
+            String local = qName.substring(index+1);
+            String uri;
+            if ("".equals(prefix)) {
+                uri = defaultNS;
+            } else {
+                uri = (String)prefixTable.get(prefix);
+            }
+            if (uri == null) {
+                return null;
+            }
+            name[0] = uri;
+            name[1] = local.intern();
+            name[2] = qName.intern();
+        }
+            
+                                // Save in the cache for future use.
+        table.put(name[2], name);
+        tablesDirty = true;
+        return name;
+    }
+        
+
+    /**
+     * Look up the URI associated with a prefix in this context.
+     *
+     * @param prefix The prefix to look up.
+     * @return The associated Namespace URI, or null if none is
+     *         declared.    
+     * @see org.xml.sax.helpers.NamespaceSupport2#getURI
+     */
+    String getURI (String prefix)
+    {
+        if ("".equals(prefix)) {
+            return defaultNS;
+        } else if (prefixTable == null) {
+            return null;
+        } else {
+            return (String)prefixTable.get(prefix);
+        }
+    }
+
+
+    /**
+     * Look up one of the prefixes associated with a URI in this context.
+     *
+     * <p>Since many prefixes may be mapped to the same URI,
+     * the return value may be unreliable.</p>
+     *
+     * @param uri The URI to look up.
+     * @return The associated prefix, or null if none is declared.
+     * @see org.xml.sax.helpers.NamespaceSupport2#getPrefix
+     */
+    String getPrefix (String uri)
+    {
+        if (uriTable == null) {
+            return null;
+        } else {
+            return (String)uriTable.get(uri);
+        }
+    }
+        
+        
+    /**
+     * Return an enumeration of prefixes declared in this context.
+     *
+     * @return An enumeration of prefixes (possibly empty).
+     * @see org.xml.sax.helpers.NamespaceSupport2#getDeclaredPrefixes
+     */
+    Enumeration getDeclaredPrefixes ()
+    {
+        if (declarations == null) {
+            return EMPTY_ENUMERATION;
+        } else {
+            return declarations.elements();
+        }
+    }
+        
+        
+    /**
+     * Return an enumeration of all prefixes currently in force.
+     *
+     * <p>The default prefix, if in force, is <em>not</em>
+     * returned, and will have to be checked for separately.</p>
+     *
+     * @return An enumeration of prefixes (never empty).
+     * @see org.xml.sax.helpers.NamespaceSupport2#getPrefixes
+     */
+    Enumeration getPrefixes ()
+    {
+        if (prefixTable == null) {
+            return EMPTY_ENUMERATION;
+        } else {
+            return prefixTable.keys();
+        }
+    }
+        
+    ////////////////////////////////////////////////////////////////
+    // Internal methods.
+    ////////////////////////////////////////////////////////////////
+
+    /**
+     * Copy on write for the internal tables in this context.
+     *
+     * <p>This class is optimized for the normal case where most
+     * elements do not contain Namespace declarations. In that case,
+     * the Context2 will share data structures with its parent.
+     * New tables are obtained only when new declarations are issued,
+     * so they can be popped off the stack.</p>
+     *
+     * <p> JJK: **** Alternative: each Context2 might declare
+     *  _only_ its local bindings, and delegate upward if not found.</p>
+     */     
+    private void copyTables ()
+    {
+        // Start by copying our parent's bindings
+        prefixTable = (Hashtable)prefixTable.clone();
+        uriTable = (Hashtable)uriTable.clone();
+
+        // Replace the caches with empty ones, rather than
+        // trying to determine which bindings should be flushed.
+        // As far as I can tell, these caches are never actually
+        // used in Xalan... More efficient to remove the whole
+        // cache system? ****
+        if(elementNameTable!=null)
+            elementNameTable=new Hashtable(); 
+        if(attributeNameTable!=null)
+            attributeNameTable=new Hashtable(); 
+        tablesDirty = true;
+    }
+
+}
+
+
+// end of NamespaceSupport2.java
diff --git a/src/main/java/org/apache/xml/utils/NodeConsumer.java b/src/main/java/org/apache/xml/utils/NodeConsumer.java
new file mode 100644
index 0000000..17e5c1c
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/NodeConsumer.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeConsumer.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import org.w3c.dom.Node;
+
+/**
+ * The tree walker will test for this interface, and call
+ * setOriginatingNode before calling the SAX event.  For creating
+ * DOM backpointers for things that are normally created via
+ * SAX events.
+ */
+public interface NodeConsumer
+{
+
+  /**
+   * Set the node that is originating the SAX event.
+   *
+   * @param n Reference to node that originated the current event.
+   */
+  public void setOriginatingNode(Node n);
+}
diff --git a/src/main/java/org/apache/xml/utils/NodeVector.java b/src/main/java/org/apache/xml/utils/NodeVector.java
new file mode 100644
index 0000000..c33d5c9
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/NodeVector.java
@@ -0,0 +1,739 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeVector.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.io.Serializable;
+
+import org.apache.xml.dtm.DTM;
+
+/**
+ * A very simple table that stores a list of Nodes.
+ * @xsl.usage internal
+ */
+public class NodeVector implements Serializable, Cloneable
+{
+    static final long serialVersionUID = -713473092200731870L;
+
+  /**
+   * Size of blocks to allocate.
+   *  @serial          
+   */
+  private int m_blocksize;
+
+  /**
+   * Array of nodes this points to.
+   *  @serial          
+   */
+  private int m_map[];
+
+  /**
+   * Number of nodes in this NodeVector.
+   *  @serial          
+   */
+  protected int m_firstFree = 0;
+
+  /**
+   * Size of the array this points to.
+   *  @serial           
+   */
+  private int m_mapSize;  // lazy initialization
+
+  /**
+   * Default constructor.
+   */
+  public NodeVector()
+  {
+    m_blocksize = 32;
+    m_mapSize = 0;
+  }
+
+  /**
+   * Construct a NodeVector, using the given block size.
+   *
+   * @param blocksize Size of blocks to allocate
+   */
+  public NodeVector(int blocksize)
+  {
+    m_blocksize = blocksize;
+    m_mapSize = 0;
+  }
+
+  /**
+   * Get a cloned LocPathIterator.
+   *
+   * @return A clone of this
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+
+    NodeVector clone = (NodeVector) super.clone();
+
+    if ((null != this.m_map) && (this.m_map == clone.m_map))
+    {
+      clone.m_map = new int[this.m_map.length];
+
+      System.arraycopy(this.m_map, 0, clone.m_map, 0, this.m_map.length);
+    }
+
+    return clone;
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return Number of nodes in this NodeVector
+   */
+  public int size()
+  {
+    return m_firstFree;
+  }
+
+  /**
+   * Append a Node onto the vector.
+   *
+   * @param value Node to add to the vector
+   */
+  public void addElement(int value)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      if (null == m_map)
+      {
+        m_map = new int[m_blocksize];
+        m_mapSize = m_blocksize;
+      }
+      else
+      {
+        m_mapSize += m_blocksize;
+
+        int newMap[] = new int[m_mapSize];
+
+        System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+        m_map = newMap;
+      }
+    }
+
+    m_map[m_firstFree] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Append a Node onto the vector.
+   *
+   * @param value Node to add to the vector
+   */
+  public final void push(int value)
+  {
+
+    int ff = m_firstFree;
+
+    if ((ff + 1) >= m_mapSize)
+    {
+      if (null == m_map)
+      {
+        m_map = new int[m_blocksize];
+        m_mapSize = m_blocksize;
+      }
+      else
+      {
+        m_mapSize += m_blocksize;
+
+        int newMap[] = new int[m_mapSize];
+
+        System.arraycopy(m_map, 0, newMap, 0, ff + 1);
+
+        m_map = newMap;
+      }
+    }
+
+    m_map[ff] = value;
+
+    ff++;
+
+    m_firstFree = ff;
+  }
+
+  /**
+   * Pop a node from the tail of the vector and return the result.
+   *
+   * @return the node at the tail of the vector
+   */
+  public final int pop()
+  {
+
+    m_firstFree--;
+
+    int n = m_map[m_firstFree];
+
+    m_map[m_firstFree] = DTM.NULL;
+
+    return n;
+  }
+
+  /**
+   * Pop a node from the tail of the vector and return the
+   * top of the stack after the pop.
+   *
+   * @return The top of the stack after it's been popped
+   */
+  public final int popAndTop()
+  {
+
+    m_firstFree--;
+
+    m_map[m_firstFree] = DTM.NULL;
+
+    return (m_firstFree == 0) ? DTM.NULL : m_map[m_firstFree - 1];
+  }
+
+  /**
+   * Pop a node from the tail of the vector.
+   */
+  public final void popQuick()
+  {
+
+    m_firstFree--;
+
+    m_map[m_firstFree] = DTM.NULL;
+  }
+
+  /**
+   * Return the node at the top of the stack without popping the stack.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @return Node at the top of the stack or null if stack is empty.
+   */
+  public final int peepOrNull()
+  {
+    return ((null != m_map) && (m_firstFree > 0))
+           ? m_map[m_firstFree - 1] : DTM.NULL;
+  }
+
+  /**
+   * Push a pair of nodes into the stack.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @param v1 First node to add to vector
+   * @param v2 Second node to add to vector
+   */
+  public final void pushPair(int v1, int v2)
+  {
+
+    if (null == m_map)
+    {
+      m_map = new int[m_blocksize];
+      m_mapSize = m_blocksize;
+    }
+    else
+    {
+      if ((m_firstFree + 2) >= m_mapSize)
+      {
+        m_mapSize += m_blocksize;
+
+        int newMap[] = new int[m_mapSize];
+
+        System.arraycopy(m_map, 0, newMap, 0, m_firstFree);
+
+        m_map = newMap;
+      }
+    }
+
+    m_map[m_firstFree] = v1;
+    m_map[m_firstFree + 1] = v2;
+    m_firstFree += 2;
+  }
+
+  /**
+   * Pop a pair of nodes from the tail of the stack.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   */
+  public final void popPair()
+  {
+
+    m_firstFree -= 2;
+    m_map[m_firstFree] = DTM.NULL;
+    m_map[m_firstFree + 1] = DTM.NULL;
+  }
+
+  /**
+   * Set the tail of the stack to the given node.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @param n Node to set at the tail of vector
+   */
+  public final void setTail(int n)
+  {
+    m_map[m_firstFree - 1] = n;
+  }
+
+  /**
+   * Set the given node one position from the tail.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @param n Node to set
+   */
+  public final void setTailSub1(int n)
+  {
+    m_map[m_firstFree - 2] = n;
+  }
+
+  /**
+   * Return the node at the tail of the vector without popping
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @return Node at the tail of the vector
+   */
+  public final int peepTail()
+  {
+    return m_map[m_firstFree - 1];
+  }
+
+  /**
+   * Return the node one position from the tail without popping.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @return Node one away from the tail
+   */
+  public final int peepTailSub1()
+  {
+    return m_map[m_firstFree - 2];
+  }
+
+  /**
+   * Insert a node in order in the list.
+   *
+   * @param value Node to insert
+   */
+  public void insertInOrder(int value)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (value < m_map[i])
+      {
+        insertElementAt(value, i);
+
+        return;
+      }
+    }
+
+    addElement(value);
+  }
+
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   *
+   * @param value Node to insert
+   * @param at Position where to insert
+   */
+  public void insertElementAt(int value, int at)
+  {
+
+    if (null == m_map)
+    {
+      m_map = new int[m_blocksize];
+      m_mapSize = m_blocksize;
+    }
+    else if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      int newMap[] = new int[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    if (at <= (m_firstFree - 1))
+    {
+      System.arraycopy(m_map, at, m_map, at + 1, m_firstFree - at);
+    }
+
+    m_map[at] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Append the nodes to the list.
+   *
+   * @param nodes NodeVector to append to this list
+   */
+  public void appendNodes(NodeVector nodes)
+  {
+
+    int nNodes = nodes.size();
+
+    if (null == m_map)
+    {
+      m_mapSize = nNodes + m_blocksize;
+      m_map = new int[m_mapSize];
+    }
+    else if ((m_firstFree + nNodes) >= m_mapSize)
+    {
+      m_mapSize += (nNodes + m_blocksize);
+
+      int newMap[] = new int[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + nNodes);
+
+      m_map = newMap;
+    }
+
+    System.arraycopy(nodes.m_map, 0, m_map, m_firstFree, nNodes);
+
+    m_firstFree += nNodes;
+  }
+
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   */
+  public void removeAllElements()
+  {
+
+    if (null == m_map)
+      return;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      m_map[i] = DTM.NULL;
+    }
+
+    m_firstFree = 0;
+  }
+  
+  /**
+   * Set the length to zero, but don't clear the array.
+   */
+  public void RemoveAllNoClear()
+  {
+
+    if (null == m_map)
+      return;
+
+    m_firstFree = 0;
+  }
+
+  /**
+   * Removes the first occurrence of the argument from this vector.
+   * If the object is found in this vector, each component in the vector
+   * with an index greater or equal to the object's index is shifted
+   * downward to have an index one smaller than the value it had
+   * previously.
+   *
+   * @param s Node to remove from the list
+   *
+   * @return True if the node was successfully removed
+   */
+  public boolean removeElement(int s)
+  {
+
+    if (null == m_map)
+      return false;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      int node = m_map[i];
+
+      if (node == s)
+      {
+        if (i > m_firstFree)
+          System.arraycopy(m_map, i + 1, m_map, i - 1, m_firstFree - i);
+        else
+          m_map[i] = DTM.NULL;
+
+        m_firstFree--;
+
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Deletes the component at the specified index. Each component in
+   * this vector with an index greater or equal to the specified
+   * index is shifted downward to have an index one smaller than
+   * the value it had previously.
+   *
+   * @param i Index of node to remove
+   */
+  public void removeElementAt(int i)
+  {
+
+    if (null == m_map)
+      return;
+
+    if (i > m_firstFree)
+      System.arraycopy(m_map, i + 1, m_map, i - 1, m_firstFree - i);
+    else
+      m_map[i] = DTM.NULL;
+  }
+
+  /**
+   * Sets the component at the specified index of this vector to be the
+   * specified object. The previous component at that position is discarded.
+   *
+   * The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.
+   *
+   * @param node Node to set
+   * @param index Index of where to set the node
+   */
+  public void setElementAt(int node, int index)
+  {
+
+    if (null == m_map)
+    {
+      m_map = new int[m_blocksize];
+      m_mapSize = m_blocksize;
+    }
+    
+    if(index == -1)
+    	addElement(node);
+
+    m_map[index] = node;
+  }
+
+  /**
+   * Get the nth element.
+   *
+   * @param i Index of node to get
+   *
+   * @return Node at specified index
+   */
+  public int elementAt(int i)
+  {
+
+    if (null == m_map)
+      return DTM.NULL;
+
+    return m_map[i];
+  }
+
+  /**
+   * Tell if the table contains the given node.
+   *
+   * @param s Node to look for
+   *
+   * @return True if the given node was found.
+   */
+  public boolean contains(int s)
+  {
+
+    if (null == m_map)
+      return false;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      int node = m_map[i];
+
+      if (node == s)
+        return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Node to look for
+   * @param index Index of where to start the search
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public int indexOf(int elem, int index)
+  {
+
+    if (null == m_map)
+      return -1;
+
+    for (int i = index; i < m_firstFree; i++)
+    {
+      int node = m_map[i];
+
+      if (node == elem)
+        return i;
+    }
+
+    return -1;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Node to look for
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public int indexOf(int elem)
+  {
+
+    if (null == m_map)
+      return -1;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      int node = m_map[i];
+
+      if (node == elem)
+        return i;
+    }
+
+    return -1;
+  }
+
+  /**
+   * Sort an array using a quicksort algorithm.
+   *
+   * @param a The array to be sorted.
+   * @param lo0  The low index.
+   * @param hi0  The high index.
+   *
+   * @throws Exception
+   */
+  public void sort(int a[], int lo0, int hi0) throws Exception
+  {
+
+    int lo = lo0;
+    int hi = hi0;
+
+    // pause(lo, hi);
+    if (lo >= hi)
+    {
+      return;
+    }
+    else if (lo == hi - 1)
+    {
+
+      /*
+       *  sort a two element list by swapping if necessary
+       */
+      if (a[lo] > a[hi])
+      {
+        int T = a[lo];
+
+        a[lo] = a[hi];
+        a[hi] = T;
+      }
+
+      return;
+    }
+
+    /*
+     *  Pick a pivot and move it out of the way
+     */
+    int pivot = a[(lo + hi) / 2];
+
+    a[(lo + hi) / 2] = a[hi];
+    a[hi] = pivot;
+
+    while (lo < hi)
+    {
+
+      /*
+       *  Search forward from a[lo] until an element is found that
+       *  is greater than the pivot or lo >= hi
+       */
+      while (a[lo] <= pivot && lo < hi)
+      {
+        lo++;
+      }
+
+      /*
+       *  Search backward from a[hi] until element is found that
+       *  is less than the pivot, or lo >= hi
+       */
+      while (pivot <= a[hi] && lo < hi)
+      {
+        hi--;
+      }
+
+      /*
+       *  Swap elements a[lo] and a[hi]
+       */
+      if (lo < hi)
+      {
+        int T = a[lo];
+
+        a[lo] = a[hi];
+        a[hi] = T;
+
+        // pause();
+      }
+
+      // if (stopRequested) {
+      //    return;
+      // }
+    }
+
+    /*
+     *  Put the median in the "center" of the list
+     */
+    a[hi0] = a[hi];
+    a[hi] = pivot;
+
+    /*
+     *  Recursive calls, elements a[lo0] to a[lo-1] are less than or
+     *  equal to pivot, elements a[hi+1] to a[hi0] are greater than
+     *  pivot.
+     */
+    sort(a, lo0, lo - 1);
+    sort(a, hi + 1, hi0);
+  }
+
+  /**
+   * Sort an array using a quicksort algorithm.
+   *
+   * @throws Exception
+   */
+  public void sort() throws Exception
+  {
+    sort(m_map, 0, m_firstFree - 1);
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/ObjectFactory.java b/src/main/java/org/apache/xml/utils/ObjectFactory.java
new file mode 100644
index 0000000..cd41234
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/ObjectFactory.java
@@ -0,0 +1,661 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xml.utils;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+
+import java.util.Properties;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ * This class is duplicated for each JAXP subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the JAXP
+ * API.
+ * <p>
+ * This code is designed to implement the JAXP 1.1 spec pluggability
+ * feature and is designed to run on JDK version 1.1 and
+ * later, and to compile on JDK 1.2 and onward.  
+ * The code also runs both as part of an unbundled jar file and
+ * when bundled as part of the JDK.
+ * <p>
+ * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
+ * class and modified to be used as a general utility for creating objects 
+ * dynamically.
+ *
+ * @version $Id:  $
+ */
+class ObjectFactory {
+
+    //
+    // Constants
+    //
+
+    // name of default properties file to look for in JDK's jre/lib directory
+    private static final String DEFAULT_PROPERTIES_FILENAME =
+                                                     "xalan.properties";
+
+    private static final String SERVICES_PATH = "META-INF/services/";
+
+    /** Set to true for debugging */
+    private static final boolean DEBUG = false;
+
+    /** cache the contents of the xalan.properties file.
+     *  Until an attempt has been made to read this file, this will
+     * be null; if the file does not exist or we encounter some other error
+     * during the read, this will be empty.
+     */
+    private static Properties fXalanProperties = null;
+
+    /***
+     * Cache the time stamp of the xalan.properties file so
+     * that we know if it's been modified and can invalidate
+     * the cache when necessary.
+     */
+    private static long fLastModified = -1;
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, String fallbackClassName)
+        throws ConfigurationError {
+        return createObject(factoryId, null, fallbackClassName);
+    } // createObject(String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, 
+                                      String propertiesFilename,
+                                      String fallbackClassName)
+        throws ConfigurationError
+    {
+        Class factoryClass = lookUpFactoryClass(factoryId,
+                                                propertiesFilename,
+                                                fallbackClassName);
+
+        if (factoryClass == null) {
+            throw new ConfigurationError(
+                "Provider for " + factoryId + " cannot be found", null);
+        }
+
+        try{
+            Object instance = factoryClass.newInstance();
+            debugPrintln("created new instance of factory " + factoryId);
+            return instance;
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider for factory " + factoryId
+                    + " could not be instantiated: " + x, x);
+        }
+    } // createObject(String,String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId) 
+        throws ConfigurationError
+    {
+        return lookUpFactoryClass(factoryId, null, null);
+    } // lookUpFactoryClass(String):Class
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId,
+                                           String propertiesFilename,
+                                           String fallbackClassName)
+        throws ConfigurationError
+    {
+        String factoryClassName = lookUpFactoryClassName(factoryId,
+                                                         propertiesFilename,
+                                                         fallbackClassName);
+        ClassLoader cl = findClassLoader();
+
+        if (factoryClassName == null) {
+            factoryClassName = fallbackClassName;
+        }
+
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(factoryClassName,
+                                                    cl,
+                                                    true);
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return providerClass;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + factoryClassName + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider "+factoryClassName+" could not be instantiated: "+x,
+                x);
+        }
+    } // lookUpFactoryClass(String,String,String):Class
+
+    /**
+     * Finds the name of the required implementation class in the specified
+     * order.  The specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return name of class that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static String lookUpFactoryClassName(String factoryId,
+                                                String propertiesFilename,
+                                                String fallbackClassName)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Use the system property first
+        try {
+            String systemProp = ss.getSystemProperty(factoryId);
+            if (systemProp != null) {
+                debugPrintln("found system property, value=" + systemProp);
+                return systemProp;
+            }
+        } catch (SecurityException se) {
+            // Ignore and continue w/ next location
+        }
+
+        // Try to read from propertiesFilename, or
+        // $java.home/lib/xalan.properties
+        String factoryClassName = null;
+        // no properties file name specified; use
+        // $JAVA_HOME/lib/xalan.properties:
+        if (propertiesFilename == null) {
+            File propertiesFile = null;
+            boolean propertiesFileExists = false;
+            try {
+                String javah = ss.getSystemProperty("java.home");
+                propertiesFilename = javah + File.separator +
+                    "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
+                propertiesFile = new File(propertiesFilename);
+                propertiesFileExists = ss.getFileExists(propertiesFile);
+            } catch (SecurityException e) {
+                // try again...
+                fLastModified = -1;
+                fXalanProperties = null;
+            }
+
+            synchronized (ObjectFactory.class) {
+                boolean loadProperties = false;
+                FileInputStream fis = null;
+                try {
+                    // file existed last time
+                    if(fLastModified >= 0) {
+                        if(propertiesFileExists &&
+                                (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
+                            loadProperties = true;
+                        } else {
+                            // file has stopped existing...
+                            if(!propertiesFileExists) {
+                                fLastModified = -1;
+                                fXalanProperties = null;
+                            } // else, file wasn't modified!
+                        }
+                    } else {
+                        // file has started to exist:
+                        if(propertiesFileExists) {
+                            loadProperties = true;
+                            fLastModified = ss.getLastModified(propertiesFile);
+                        } // else, nothing's changed
+                    }
+                    if(loadProperties) {
+                        // must never have attempted to read xalan.properties
+                        // before (or it's outdeated)
+                        fXalanProperties = new Properties();
+                        fis = ss.getFileInputStream(propertiesFile);
+                        fXalanProperties.load(fis);
+                    }
+	        } catch (Exception x) {
+	            fXalanProperties = null;
+	            fLastModified = -1;
+                    // assert(x instanceof FileNotFoundException
+	            //        || x instanceof SecurityException)
+	            // In both cases, ignore and continue w/ next location
+	        }
+                finally {
+                    // try to close the input stream if one was opened.
+                    if (fis != null) {
+                        try {
+                            fis.close();
+                        }
+                        // Ignore the exception.
+                        catch (IOException exc) {}
+                    }
+                }	            
+            }
+            if(fXalanProperties != null) {
+                factoryClassName = fXalanProperties.getProperty(factoryId);
+            }
+        } else {
+            FileInputStream fis = null;
+            try {
+                fis = ss.getFileInputStream(new File(propertiesFilename));
+                Properties props = new Properties();
+                props.load(fis);
+                factoryClassName = props.getProperty(factoryId);
+            } catch (Exception x) {
+                // assert(x instanceof FileNotFoundException
+                //        || x instanceof SecurityException)
+                // In both cases, ignore and continue w/ next location
+            }
+            finally {
+                // try to close the input stream if one was opened.
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    }
+                    // Ignore the exception.
+                    catch (IOException exc) {}
+                }
+            }               
+        }
+        if (factoryClassName != null) {
+            debugPrintln("found in " + propertiesFilename + ", value="
+                          + factoryClassName);
+            return factoryClassName;
+        }
+
+        // Try Jar Service Provider Mechanism
+        return findJarServiceProviderName(factoryId);
+    } // lookUpFactoryClass(String,String):String
+
+    //
+    // Private static methods
+    //
+
+    /** Prints a message to standard error if debugging is enabled. */
+    private static void debugPrintln(String msg) {
+        if (DEBUG) {
+            System.err.println("JAXP: " + msg);
+        }
+    } // debugPrintln(String)
+
+    /**
+     * Figure out which ClassLoader to use.  For JDK 1.2 and later use
+     * the context ClassLoader.
+     */
+    static ClassLoader findClassLoader()
+        throws ConfigurationError
+    { 
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Figure out which ClassLoader to use for loading the provider
+        // class.  If there is a Context ClassLoader then use it.
+        ClassLoader context = ss.getContextClassLoader();
+        ClassLoader system = ss.getSystemClassLoader();
+
+        ClassLoader chain = system;
+        while (true) {
+            if (context == chain) {
+                // Assert: we are on JDK 1.1 or we have no Context ClassLoader
+                // or any Context ClassLoader in chain of system classloader
+                // (including extension ClassLoader) so extend to widest
+                // ClassLoader (always look in system ClassLoader if Xalan
+                // is in boot/extension/system classpath and in current
+                // ClassLoader otherwise); normal classloaders delegate
+                // back to system ClassLoader first so this widening doesn't
+                // change the fact that context ClassLoader will be consulted
+                ClassLoader current = ObjectFactory.class.getClassLoader();
+
+                chain = system;
+                while (true) {
+                    if (current == chain) {
+                        // Assert: Current ClassLoader in chain of
+                        // boot/extension/system ClassLoaders
+                        return system;
+                    }
+                    if (chain == null) {
+                        break;
+                    }
+                    chain = ss.getParentClassLoader(chain);
+                }
+
+                // Assert: Current ClassLoader not in chain of
+                // boot/extension/system ClassLoaders
+                return current;
+            }
+
+            if (chain == null) {
+                // boot ClassLoader reached
+                break;
+            }
+
+            // Check for any extension ClassLoaders in chain up to
+            // boot ClassLoader
+            chain = ss.getParentClassLoader(chain);
+        };
+
+        // Assert: Context ClassLoader not in chain of
+        // boot/extension/system ClassLoaders
+        return context;
+    } // findClassLoader():ClassLoader
+
+    /**
+     * Create an instance of a class using the specified ClassLoader
+     */ 
+    static Object newInstance(String className, ClassLoader cl,
+                                      boolean doFallback)
+        throws ConfigurationError
+    {
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(className, cl, doFallback);
+            Object instance = providerClass.newInstance();
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return instance;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + className + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider " + className + " could not be instantiated: " + x,
+                x);
+        }
+    }
+
+    /**
+     * Find a Class using the specified ClassLoader
+     */ 
+    static Class findProviderClass(String className, ClassLoader cl,
+                                           boolean doFallback)
+        throws ClassNotFoundException, ConfigurationError
+    {   
+        //throw security exception if the calling thread is not allowed to access the
+        //class. Restrict the access to the package classes as specified in java.security policy.
+        SecurityManager security = System.getSecurityManager();
+        try{
+                if (security != null){
+                    final int lastDot = className.lastIndexOf(".");
+                    String packageName = className;
+                    if (lastDot != -1) packageName = className.substring(0, lastDot);
+                    security.checkPackageAccess(packageName);
+                 }   
+        }catch(SecurityException e){
+            throw e;
+        }
+        
+        Class providerClass;
+        if (cl == null) {
+            // XXX Use the bootstrap ClassLoader.  There is no way to
+            // load a class using the bootstrap ClassLoader that works
+            // in both JDK 1.1 and Java 2.  However, this should still
+            // work b/c the following should be true:
+            //
+            // (cl == null) iff current ClassLoader == null
+            //
+            // Thus Class.forName(String) will use the current
+            // ClassLoader which will be the bootstrap ClassLoader.
+            providerClass = Class.forName(className);
+        } else {
+            try {
+                providerClass = cl.loadClass(className);
+            } catch (ClassNotFoundException x) {
+                if (doFallback) {
+                    // Fall back to current classloader
+                    ClassLoader current = ObjectFactory.class.getClassLoader();
+                    if (current == null) {
+                        providerClass = Class.forName(className);
+                    } else if (cl != current) {
+                        cl = current;
+                        providerClass = cl.loadClass(className);
+                    } else {
+                        throw x;
+                    }
+                } else {
+                    throw x;
+                }
+            }
+        }
+
+        return providerClass;
+    }
+
+    /**
+     * Find the name of service provider using Jar Service Provider Mechanism
+     *
+     * @return instance of provider class if found or null
+     */
+    private static String findJarServiceProviderName(String factoryId)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+        String serviceId = SERVICES_PATH + factoryId;
+        InputStream is = null;
+
+        // First try the Context ClassLoader
+        ClassLoader cl = findClassLoader();
+
+        is = ss.getResourceAsStream(cl, serviceId);
+
+        // If no provider found then try the current ClassLoader
+        if (is == null) {
+            ClassLoader current = ObjectFactory.class.getClassLoader();
+            if (cl != current) {
+                cl = current;
+                is = ss.getResourceAsStream(cl, serviceId);
+            }
+        }
+
+        if (is == null) {
+            // No provider found
+            return null;
+        }
+
+        debugPrintln("found jar resource=" + serviceId +
+               " using ClassLoader: " + cl);
+
+        // Read the service provider name in UTF-8 as specified in
+        // the jar spec.  Unfortunately this fails in Microsoft
+        // VJ++, which does not implement the UTF-8
+        // encoding. Theoretically, we should simply let it fail in
+        // that case, since the JVM is obviously broken if it
+        // doesn't support such a basic standard.  But since there
+        // are still some users attempting to use VJ++ for
+        // development, we have dropped in a fallback which makes a
+        // second attempt using the platform's default encoding. In
+        // VJ++ this is apparently ASCII, which is a subset of
+        // UTF-8... and since the strings we'll be reading here are
+        // also primarily limited to the 7-bit ASCII range (at
+        // least, in English versions), this should work well
+        // enough to keep us on the air until we're ready to
+        // officially decommit from VJ++. [Edited comment from
+        // jkesselm]
+        BufferedReader rd;
+        try {
+            rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+        } catch (java.io.UnsupportedEncodingException e) {
+            rd = new BufferedReader(new InputStreamReader(is));
+        }
+        
+        String factoryClassName = null;
+        try {
+            // XXX Does not handle all possible input as specified by the
+            // Jar Service Provider specification
+            factoryClassName = rd.readLine();
+        } catch (IOException x) {
+            // No provider found
+            return null;
+        }
+        finally {
+            try {
+                // try to close the reader.
+                rd.close();
+            }
+            // Ignore the exception.
+            catch (IOException exc) {}
+        }          
+
+        if (factoryClassName != null &&
+            ! "".equals(factoryClassName)) {
+            debugPrintln("found in resource, value="
+                   + factoryClassName);
+
+            // Note: here we do not want to fall back to the current
+            // ClassLoader because we want to avoid the case where the
+            // resource file was found using one ClassLoader and the
+            // provider class was instantiated using a different one.
+            return factoryClassName;
+        }
+
+        // No provider found
+        return null;
+    }
+
+    //
+    // Classes
+    //
+
+    /**
+     * A configuration error.
+     */
+    static class ConfigurationError 
+        extends Error {
+                static final long serialVersionUID = 2036619216663421552L;
+        //
+        // Data
+        //
+
+        /** Exception. */
+        private Exception exception;
+
+        //
+        // Constructors
+        //
+
+        /**
+         * Construct a new instance with the specified detail string and
+         * exception.
+         */
+        ConfigurationError(String msg, Exception x) {
+            super(msg);
+            this.exception = x;
+        } // <init>(String,Exception)
+
+        //
+        // Public methods
+        //
+
+        /** Returns the exception associated to this error. */
+        Exception getException() {
+            return exception;
+        } // getException():Exception
+
+    } // class ConfigurationError
+
+} // class ObjectFactory
diff --git a/src/main/java/org/apache/xml/utils/ObjectPool.java b/src/main/java/org/apache/xml/utils/ObjectPool.java
new file mode 100644
index 0000000..43d71ab
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/ObjectPool.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ObjectPool.java 475981 2006-11-16 23:35:53Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.util.ArrayList;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+
+/**
+ * Pool of object of a given type to pick from to help memory usage
+ * @xsl.usage internal
+ */
+public class ObjectPool implements java.io.Serializable
+{
+    static final long serialVersionUID = -8519013691660936643L;
+
+  /** Type of objects in this pool.
+   *  @serial          */
+  private final Class objectType;
+
+  /** Stack of given objects this points to.
+   *  @serial          */
+  private final ArrayList freeStack;
+
+  /**
+   * Constructor ObjectPool
+   *
+   * @param type Type of objects for this pool
+   */
+  public ObjectPool(Class type)
+  {
+    objectType = type;
+    freeStack = new ArrayList();
+  }
+  
+  /**
+   * Constructor ObjectPool
+   *
+   * @param className Fully qualified name of the type of objects for this pool.
+   */
+  public ObjectPool(String className)
+  {
+    try
+    {
+      objectType = ObjectFactory.findProviderClass(
+        className, ObjectFactory.findClassLoader(), true);
+    }
+    catch(ClassNotFoundException cnfe)
+    {
+      throw new WrappedRuntimeException(cnfe);
+    }
+    freeStack = new ArrayList();
+  }
+
+
+  /**
+   * Constructor ObjectPool
+   *
+   *
+   * @param type Type of objects for this pool
+   * @param size Size of vector to allocate
+   */
+  public ObjectPool(Class type, int size)
+  {
+    objectType = type;
+    freeStack = new ArrayList(size);
+  }
+
+  /**
+   * Constructor ObjectPool
+   *
+   */
+  public ObjectPool()
+  {
+    objectType = null;
+    freeStack = new ArrayList();
+  }
+
+  /**
+   * Get an instance of the given object in this pool if available
+   *
+   *
+   * @return an instance of the given object if available or null
+   */
+  public synchronized Object getInstanceIfFree()
+  {
+
+    // Check if the pool is empty.
+    if (!freeStack.isEmpty())
+    {
+
+      // Remove object from end of free pool.
+      Object result = freeStack.remove(freeStack.size() - 1);
+      return result;
+    }
+
+    return null;
+  }
+
+  /**
+   * Get an instance of the given object in this pool 
+   *
+   *
+   * @return An instance of the given object
+   */
+  public synchronized Object getInstance()
+  {
+
+    // Check if the pool is empty.
+    if (freeStack.isEmpty())
+    {
+
+      // Create a new object if so.
+      try
+      {
+        return objectType.newInstance();
+      }
+      catch (InstantiationException ex){}
+      catch (IllegalAccessException ex){}
+
+      // Throw unchecked exception for error in pool configuration.
+      throw new RuntimeException(XMLMessages.createXMLMessage(XMLErrorResources.ER_EXCEPTION_CREATING_POOL, null)); //"exception creating new instance for pool");
+    }
+    else
+    {
+
+      // Remove object from end of free pool.
+      Object result = freeStack.remove(freeStack.size() - 1);
+
+      return result;
+    }
+  }
+
+  /**
+   * Add an instance of the given object to the pool  
+   *
+   *
+   * @param obj Object to add.
+   */
+  public synchronized void freeInstance(Object obj)
+  {
+
+    // Make sure the object is of the correct type.
+    // Remove safety.  -sb
+    // if (objectType.isInstance(obj))
+    // {
+    freeStack.add(obj);
+    // }
+    // else
+    // {
+    //  throw new IllegalArgumentException("argument type invalid for pool");
+    // }
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/ObjectStack.java b/src/main/java/org/apache/xml/utils/ObjectStack.java
new file mode 100644
index 0000000..7b406f6
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/ObjectStack.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+package org.apache.xml.utils;
+
+import java.util.EmptyStackException;
+
+/**
+ * Implement a stack of simple integers.
+ *
+ * %OPT%
+ * This is currently based on ObjectVector, which permits fast acess but pays a
+ * heavy recopying penalty if/when its size is increased. If we expect deep
+ * stacks, we should consider a version based on ChunkedObjectVector.
+ * @xsl.usage internal
+ */
+public class ObjectStack extends ObjectVector
+{
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is very small, for small lists.
+   */
+  public ObjectStack()
+  {
+    super();
+  }
+
+  /**
+   * Construct a ObjectVector, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public ObjectStack(int blocksize)
+  {
+    super(blocksize);
+  }
+
+  /**
+   * Copy constructor for ObjectStack
+   * 
+   * @param v ObjectStack to copy
+   */
+  public ObjectStack (ObjectStack v)
+  {
+  	super(v);
+  }
+  
+  /**
+   * Pushes an item onto the top of this stack.
+   *
+   * @param   i   the int to be pushed onto this stack.
+   * @return  the <code>item</code> argument.
+   */
+  public Object push(Object i)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      Object newMap[] = new Object[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    m_map[m_firstFree] = i;
+
+    m_firstFree++;
+
+    return i;
+  }
+
+  /**
+   * Removes the object at the top of this stack and returns that
+   * object as the value of this function.
+   *
+   * @return     The object at the top of this stack.
+   */
+  public Object pop()
+  {
+    Object val = m_map[--m_firstFree];
+    m_map[m_firstFree] = null;
+    
+    return val;
+  }
+
+  /**
+   * Quickly pops a number of items from the stack.
+   */
+
+  public void quickPop(int n)
+  {
+    m_firstFree -= n;
+  }
+
+  /**
+   * Looks at the object at the top of this stack without removing it
+   * from the stack.
+   *
+   * @return     the object at the top of this stack.
+   * @throws  EmptyStackException  if this stack is empty.
+   */
+  public Object peek()
+  {
+    try {
+      return m_map[m_firstFree - 1];
+    }
+    catch (ArrayIndexOutOfBoundsException e)
+    {
+      throw new EmptyStackException();
+    }
+  }
+
+  /**
+   * Looks at the object at the position the stack counting down n items.
+   *
+   * @param n The number of items down, indexed from zero.
+   * @return     the object at n items down.
+   * @throws  EmptyStackException  if this stack is empty.
+   */
+  public Object peek(int n)
+  {
+    try {
+      return m_map[m_firstFree-(1+n)];
+    }
+    catch (ArrayIndexOutOfBoundsException e)
+    {
+      throw new EmptyStackException();
+    }
+  }
+
+  /**
+   * Sets an object at a the top of the statck
+   *
+   *
+   * @param val object to set at the top
+   * @throws  EmptyStackException  if this stack is empty.
+   */
+  public void setTop(Object val)
+  {
+    try {
+      m_map[m_firstFree - 1] = val;
+    }
+    catch (ArrayIndexOutOfBoundsException e)
+    {
+      throw new EmptyStackException();
+    }
+  }
+
+  /**
+   * Tests if this stack is empty.
+   *
+   * @return  <code>true</code> if this stack is empty;
+   *          <code>false</code> otherwise.
+   * @since   JDK1.0
+   */
+  public boolean empty()
+  {
+    return m_firstFree == 0;
+  }
+
+  /**
+   * Returns where an object is on this stack.
+   *
+   * @param   o   the desired object.
+   * @return  the distance from the top of the stack where the object is]
+   *          located; the return value <code>-1</code> indicates that the
+   *          object is not on the stack.
+   * @since   JDK1.0
+   */
+  public int search(Object o)
+  {
+
+    int i = lastIndexOf(o);
+
+    if (i >= 0)
+    {
+      return size() - i;
+    }
+
+    return -1;
+  }
+
+  /**
+   * Returns clone of current ObjectStack
+   * 
+   * @return clone of current ObjectStack
+   */
+  public Object clone()
+    throws CloneNotSupportedException
+  {
+  	return (ObjectStack) super.clone();
+  }  
+  
+}
diff --git a/src/main/java/org/apache/xml/utils/ObjectVector.java b/src/main/java/org/apache/xml/utils/ObjectVector.java
new file mode 100644
index 0000000..9b6cbb2
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/ObjectVector.java
@@ -0,0 +1,431 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+package org.apache.xml.utils;
+
+/**
+ * A very simple table that stores a list of objects.
+ *
+ * This version is based on a "realloc" strategy -- a simle array is
+ * used, and when more storage is needed, a larger array is obtained
+ * and all existing data is recopied into it. As a result, read/write
+ * access to existing nodes is O(1) fast but appending may be O(N**2)
+ * slow. 
+ * @xsl.usage internal
+ */
+public class ObjectVector implements Cloneable
+{
+
+  /** Size of blocks to allocate          */
+  protected int m_blocksize;
+
+  /** Array of objects          */
+  protected Object m_map[]; 
+
+  /** Number of ints in array          */
+  protected int m_firstFree = 0;
+
+  /** Size of array          */
+  protected int m_mapSize;
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is very small, for small lists.
+   */
+  public ObjectVector()
+  {
+
+    m_blocksize = 32;
+    m_mapSize = m_blocksize;
+    m_map = new Object[m_blocksize];
+  }
+
+  /**
+   * Construct a IntVector, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public ObjectVector(int blocksize)
+  {
+
+    m_blocksize = blocksize;
+    m_mapSize = blocksize;
+    m_map = new Object[blocksize];
+  }
+  
+  /**
+   * Construct a IntVector, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public ObjectVector(int blocksize, int increaseSize)
+  {
+
+    m_blocksize = increaseSize;
+    m_mapSize = blocksize;
+    m_map = new Object[blocksize];
+  }
+
+  /**
+   * Copy constructor for ObjectVector
+   * 
+   * @param v Existing ObjectVector to copy
+   */
+  public ObjectVector(ObjectVector v)
+  {
+  	m_map = new Object[v.m_mapSize];
+    m_mapSize = v.m_mapSize;
+    m_firstFree = v.m_firstFree;
+  	m_blocksize = v.m_blocksize;
+  	System.arraycopy(v.m_map, 0, m_map, 0, m_firstFree);
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return length of the list
+   */
+  public final int size()
+  {
+    return m_firstFree;
+  }
+  
+  /**
+   * Get the length of the list.
+   *
+   * @return length of the list
+   */
+  public final void setSize(int sz)
+  {
+    m_firstFree = sz;
+  }
+
+
+  /**
+   * Append an object onto the vector.
+   *
+   * @param value Object to add to the list 
+   */
+  public final void addElement(Object value)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      Object newMap[] = new Object[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    m_map[m_firstFree] = value;
+
+    m_firstFree++;
+  }
+  
+  /**
+   * Append several Object values onto the vector.
+   *
+   * @param value Object to add to the list 
+   */
+  public final void addElements(Object value, int numberOfElements)
+  {
+
+    if ((m_firstFree + numberOfElements) >= m_mapSize)
+    {
+      m_mapSize += (m_blocksize+numberOfElements);
+
+      Object newMap[] = new Object[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    for (int i = 0; i < numberOfElements; i++) 
+    {
+      m_map[m_firstFree] = value;
+      m_firstFree++;
+    }
+  }
+  
+  /**
+   * Append several slots onto the vector, but do not set the values.
+   *
+   * @param numberOfElements number of slots to append
+   */
+  public final void addElements(int numberOfElements)
+  {
+
+    if ((m_firstFree + numberOfElements) >= m_mapSize)
+    {
+      m_mapSize += (m_blocksize+numberOfElements);
+
+      Object newMap[] = new Object[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+    
+    m_firstFree += numberOfElements;
+  }
+  
+
+  /**
+   * Inserts the specified object in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   *
+   * @param value Object to insert
+   * @param at Index of where to insert 
+   */
+  public final void insertElementAt(Object value, int at)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      Object newMap[] = new Object[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    if (at <= (m_firstFree - 1))
+    {
+      System.arraycopy(m_map, at, m_map, at + 1, m_firstFree - at);
+    }
+
+    m_map[at] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Remove all elements objects from the list. 
+   */
+  public final void removeAllElements()
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      m_map[i] = null;
+    }
+
+    m_firstFree = 0;
+  }
+
+  /**
+   * Removes the first occurrence of the argument from this vector.
+   * If the object is found in this vector, each component in the vector
+   * with an index greater or equal to the object's index is shifted
+   * downward to have an index one smaller than the value it had
+   * previously.
+   *
+   * @param s Object to remove from array
+   *
+   * @return True if the object was removed, false if it was not found
+   */
+  public final boolean removeElement(Object s)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i] == s)
+      {
+        if ((i + 1) < m_firstFree)
+          System.arraycopy(m_map, i + 1, m_map, i - 1, m_firstFree - i);
+        else
+          m_map[i] = null;
+
+        m_firstFree--;
+
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Deletes the component at the specified index. Each component in
+   * this vector with an index greater or equal to the specified
+   * index is shifted downward to have an index one smaller than
+   * the value it had previously.
+   *
+   * @param i index of where to remove an object
+   */
+  public final void removeElementAt(int i)
+  {
+
+    if (i > m_firstFree)
+      System.arraycopy(m_map, i + 1, m_map, i, m_firstFree);
+    else
+      m_map[i] = null;
+
+    m_firstFree--;
+  }
+
+  /**
+   * Sets the component at the specified index of this vector to be the
+   * specified object. The previous component at that position is discarded.
+   *
+   * The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.
+   *
+   * @param value object to set
+   * @param index Index of where to set the object
+   */
+  public final void setElementAt(Object value, int index)
+  {
+    m_map[index] = value;
+  }
+
+  /**
+   * Get the nth element.
+   *
+   * @param i index of object to get
+   *
+   * @return object at given index
+   */
+  public final Object elementAt(int i)
+  {
+    return m_map[i];
+  }
+
+  /**
+   * Tell if the table contains the given Object.
+   *
+   * @param s object to look for
+   *
+   * @return true if the object is in the list
+   */
+  public final boolean contains(Object s)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i] == s)
+        return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem object to look for
+   * @param index Index of where to begin search
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public final int indexOf(Object elem, int index)
+  {
+
+    for (int i = index; i < m_firstFree; i++)
+    {
+      if (m_map[i] == elem)
+        return i;
+    }
+
+    return java.lang.Integer.MIN_VALUE;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem object to look for
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public final int indexOf(Object elem)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i] == elem)
+        return i;
+    }
+
+    return java.lang.Integer.MIN_VALUE;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Object to look for
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public final int lastIndexOf(Object elem)
+  {
+
+    for (int i = (m_firstFree - 1); i >= 0; i--)
+    {
+      if (m_map[i] == elem)
+        return i;
+    }
+
+    return java.lang.Integer.MIN_VALUE;
+  }
+  
+  /*
+   * Reset the array to the supplied size.  
+   * 
+   * @param size 
+   */
+  public final void setToSize(int size) {
+    
+    Object newMap[] = new Object[size];
+    
+    System.arraycopy(m_map, 0, newMap, 0, m_firstFree);
+    m_mapSize = size;
+
+    m_map = newMap;
+    
+  }  
+  
+  /**
+   * Returns clone of current ObjectVector
+   * 
+   * @return clone of current ObjectVector
+   */
+  public Object clone()
+    throws CloneNotSupportedException
+  {
+  	return new ObjectVector(this);
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/PrefixResolver.java b/src/main/java/org/apache/xml/utils/PrefixResolver.java
new file mode 100644
index 0000000..bdeb08a
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/PrefixResolver.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: PrefixResolver.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * The class that implements this interface can resolve prefixes to
+ * namespaces. Examples would include resolving the meaning of a
+ * prefix at a particular point in a document, or mapping the prefixes
+ * used in an XPath expression.
+ * @xsl.usage advanced
+ */
+public interface PrefixResolver
+{
+
+  /**
+   * Given a namespace, get the corrisponding prefix.  This assumes that
+   * the PrefixResolver holds its own namespace context, or is a namespace
+   * context itself.
+   *
+   * @param prefix The prefix to look up, which may be an empty string ("") for the default Namespace.
+   *
+   * @return The associated Namespace URI, or null if the prefix
+   *         is undeclared in this context.
+   */
+  String getNamespaceForPrefix(String prefix);
+
+  /**
+   * Given a namespace, get the corresponding prefix, based on the context node.
+   *
+   * @param prefix The prefix to look up, which may be an empty string ("") for the default Namespace.
+   * @param context The node context from which to look up the URI.
+   *
+   * @return The associated Namespace URI as a string, or null if the prefix
+   *         is undeclared in this context.
+   */
+  String getNamespaceForPrefix(String prefix, org.w3c.dom.Node context);
+
+  /**
+   * Return the base identifier.
+   *
+   * @return The base identifier from where relative URIs should be absolutized, or null 
+   * if the base ID is unknown.
+   * <p>
+   * CAVEAT: Note that the base URI in an XML document may vary with where
+   * you are in the document, if part of the doc's contents were brought in
+   * via an external entity reference or if mechanisms such as xml:base have
+   * been used. Unless this PrefixResolver is bound to a specific portion of
+   * the document, or has been kept up to date via some other mechanism, it
+   * may not accurately reflect that context information.
+   */
+  public String getBaseIdentifier();
+  
+  public boolean handlesNullPrefixes();
+}
diff --git a/src/main/java/org/apache/xml/utils/PrefixResolverDefault.java b/src/main/java/org/apache/xml/utils/PrefixResolverDefault.java
new file mode 100644
index 0000000..38ced11
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/PrefixResolverDefault.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: PrefixResolverDefault.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * This class implements a generic PrefixResolver that
+ * can be used to perform prefix-to-namespace lookup
+ * for the XPath object.
+ * @xsl.usage general
+ */
+public class PrefixResolverDefault implements PrefixResolver
+{
+
+  /**
+   * The context to resolve the prefix from, if the context
+   * is not given.
+   */
+  Node m_context;
+
+  /**
+   * Construct a PrefixResolverDefault object.
+   * @param xpathExpressionContext The context from
+   * which XPath expression prefixes will be resolved.
+   * Warning: This will not work correctly if xpathExpressionContext
+   * is an attribute node.
+   */
+  public PrefixResolverDefault(Node xpathExpressionContext)
+  {
+    m_context = xpathExpressionContext;
+  }
+
+  /**
+   * Given a namespace, get the corrisponding prefix.  This assumes that
+   * the PrevixResolver hold's it's own namespace context, or is a namespace
+   * context itself.
+   * @param prefix Prefix to resolve.
+   * @return Namespace that prefix resolves to, or null if prefix
+   * is not bound.
+   */
+  public String getNamespaceForPrefix(String prefix)
+  {
+    return getNamespaceForPrefix(prefix, m_context);
+  }
+
+  /**
+   * Given a namespace, get the corrisponding prefix.
+   * Warning: This will not work correctly if namespaceContext
+   * is an attribute node.
+   * @param prefix Prefix to resolve.
+   * @param namespaceContext Node from which to start searching for a
+   * xmlns attribute that binds a prefix to a namespace.
+   * @return Namespace that prefix resolves to, or null if prefix
+   * is not bound.
+   */
+  public String getNamespaceForPrefix(String prefix,
+                                      org.w3c.dom.Node namespaceContext)
+  {
+
+    Node parent = namespaceContext;
+    String namespace = null;
+
+    if (prefix.equals("xml"))
+    {
+      namespace = Constants.S_XMLNAMESPACEURI;
+    }
+    else
+    {
+      int type;
+
+      while ((null != parent) && (null == namespace)
+             && (((type = parent.getNodeType()) == Node.ELEMENT_NODE)
+                 || (type == Node.ENTITY_REFERENCE_NODE)))
+      {
+        if (type == Node.ELEMENT_NODE)
+        {
+                if (parent.getNodeName().indexOf(prefix+":") == 0) 
+                        return parent.getNamespaceURI();                
+          NamedNodeMap nnm = parent.getAttributes();
+
+          for (int i = 0; i < nnm.getLength(); i++)
+          {
+            Node attr = nnm.item(i);
+            String aname = attr.getNodeName();
+            boolean isPrefix = aname.startsWith("xmlns:");
+
+            if (isPrefix || aname.equals("xmlns"))
+            {
+              int index = aname.indexOf(':');
+              String p = isPrefix ? aname.substring(index + 1) : "";
+
+              if (p.equals(prefix))
+              {
+                namespace = attr.getNodeValue();
+
+                break;
+              }
+            }
+          }
+        }
+
+        parent = parent.getParentNode();
+      }
+    }
+
+    return namespace;
+  }
+
+  /**
+   * Return the base identifier.
+   *
+   * @return null
+   */
+  public String getBaseIdentifier()
+  {
+    return null;
+  }
+	/**
+	 * @see PrefixResolver#handlesNullPrefixes()
+	 */
+	public boolean handlesNullPrefixes() {
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/apache/xml/utils/QName.java b/src/main/java/org/apache/xml/utils/QName.java
new file mode 100644
index 0000000..09fd90d
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/QName.java
@@ -0,0 +1,709 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: QName.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.util.Stack;
+import java.util.StringTokenizer;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+import org.w3c.dom.Element;
+
+/**
+ * Class to represent a qualified name: "The name of an internal XSLT object,
+ * specifically a named template (see [7 Named Templates]), a mode (see [6.7 Modes]),
+ * an attribute set (see [8.1.4 Named Attribute Sets]), a key (see [14.2 Keys]),
+ * a locale (see [14.3 Number Formatting]), a variable or a parameter (see
+ * [12 Variables and Parameters]) is specified as a QName. If it has a prefix,
+ * then the prefix is expanded into a URI reference using the namespace declarations
+ * in effect on the attribute in which the name occurs. The expanded name
+ * consisting of the local part of the name and the possibly null URI reference
+ * is used as the name of the object. The default namespace is not used for
+ * unprefixed names."
+ * @xsl.usage general
+ */
+public class QName implements java.io.Serializable
+{
+    static final long serialVersionUID = 467434581652829920L;
+
+  /**
+   * The local name.
+   * @serial
+   */
+  protected String _localName;
+
+  /**
+   * The namespace URI.
+   * @serial
+   */
+  protected String _namespaceURI;
+
+  /**
+   * The namespace prefix.
+   * @serial
+   */
+  protected String _prefix;
+
+  /**
+   * The XML namespace.
+   */
+  public static final String S_XMLNAMESPACEURI =
+    "http://www.w3.org/XML/1998/namespace";
+
+  /**
+   * The cached hashcode, which is calculated at construction time.
+   * @serial
+   */
+  private int m_hashCode;
+
+  /**
+   * Constructs an empty QName.
+   * 20001019: Try making this public, to support Serializable? -- JKESS
+   */
+  public QName(){}
+
+  /**
+   * Constructs a new QName with the specified namespace URI and
+   * local name.
+   *
+   * @param namespaceURI The namespace URI if known, or null
+   * @param localName The local name
+   */
+  public QName(String namespaceURI, String localName)
+  {
+    this(namespaceURI, localName, false); 
+  }
+
+  /**
+   * Constructs a new QName with the specified namespace URI and
+   * local name.
+   *
+   * @param namespaceURI The namespace URI if known, or null
+   * @param localName The local name
+   * @param validate If true the new QName will be validated and an IllegalArgumentException will
+   *                 be thrown if it is invalid.
+   */
+  public QName(String namespaceURI, String localName, boolean validate) 
+  {
+
+    // This check was already here.  So, for now, I will not add it to the validation
+    // that is done when the validate parameter is true.
+    if (localName == null)
+      throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null");
+
+    if (validate) 
+    {
+        if (!XML11Char.isXML11ValidNCName(localName))
+        {
+            throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName");
+        }
+    }
+    
+    _namespaceURI = namespaceURI;
+    _localName = localName;
+    m_hashCode = toString().hashCode();
+  }
+  
+  /**
+   * Constructs a new QName with the specified namespace URI, prefix
+   * and local name.
+   *
+   * @param namespaceURI The namespace URI if known, or null
+   * @param prefix The namespace prefix is known, or null
+   * @param localName The local name
+   * 
+   */
+  public QName(String namespaceURI, String prefix, String localName)
+  {
+     this(namespaceURI, prefix, localName, false);
+  }
+  
+ /**
+   * Constructs a new QName with the specified namespace URI, prefix
+   * and local name.
+   *
+   * @param namespaceURI The namespace URI if known, or null
+   * @param prefix The namespace prefix is known, or null
+   * @param localName The local name
+   * @param validate If true the new QName will be validated and an IllegalArgumentException will
+   *                 be thrown if it is invalid.
+   */
+  public QName(String namespaceURI, String prefix, String localName, boolean validate)
+  {
+
+    // This check was already here.  So, for now, I will not add it to the validation
+    // that is done when the validate parameter is true.
+    if (localName == null)
+      throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null");
+
+    if (validate)
+    {    
+        if (!XML11Char.isXML11ValidNCName(localName))
+        {
+            throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName");
+        }
+
+        if ((null != prefix) && (!XML11Char.isXML11ValidNCName(prefix)))
+        {
+            throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_PREFIX_INVALID,null )); //"Argument 'prefix' not a valid NCName");
+        }
+
+    }
+    _namespaceURI = namespaceURI;
+    _prefix = prefix;
+    _localName = localName;
+    m_hashCode = toString().hashCode();
+  }  
+
+  /**
+   * Construct a QName from a string, without namespace resolution.  Good
+   * for a few odd cases.
+   *
+   * @param localName Local part of qualified name
+   * 
+   */
+  public QName(String localName)
+  {
+    this(localName, false);
+  }
+  
+  /**
+   * Construct a QName from a string, without namespace resolution.  Good
+   * for a few odd cases.
+   *
+   * @param localName Local part of qualified name
+   * @param validate If true the new QName will be validated and an IllegalArgumentException will
+   *                 be thrown if it is invalid.
+   */
+  public QName(String localName, boolean validate)
+  {
+
+    // This check was already here.  So, for now, I will not add it to the validation
+    // that is done when the validate parameter is true.
+    if (localName == null)
+      throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null");
+
+    if (validate)
+    {    
+        if (!XML11Char.isXML11ValidNCName(localName))
+        {
+            throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName");
+        }
+    }
+    _namespaceURI = null;
+    _localName = localName;
+    m_hashCode = toString().hashCode();
+  }  
+
+  /**
+   * Construct a QName from a string, resolving the prefix
+   * using the given namespace stack. The default namespace is
+   * not resolved.
+   *
+   * @param qname Qualified name to resolve
+   * @param namespaces Namespace stack to use to resolve namespace
+   */
+  public QName(String qname, Stack namespaces)
+  {
+    this(qname, namespaces, false);
+  }
+
+  /**
+   * Construct a QName from a string, resolving the prefix
+   * using the given namespace stack. The default namespace is
+   * not resolved.
+   *
+   * @param qname Qualified name to resolve
+   * @param namespaces Namespace stack to use to resolve namespace
+   * @param validate If true the new QName will be validated and an IllegalArgumentException will
+   *                 be thrown if it is invalid.
+   */
+  public QName(String qname, Stack namespaces, boolean validate)
+  {
+
+    String namespace = null;
+    String prefix = null;
+    int indexOfNSSep = qname.indexOf(':');
+
+    if (indexOfNSSep > 0)
+    {
+      prefix = qname.substring(0, indexOfNSSep);
+
+      if (prefix.equals("xml"))
+      {
+        namespace = S_XMLNAMESPACEURI;
+      }
+      // Do we want this?
+      else if (prefix.equals("xmlns"))
+      {
+        return;
+      }
+      else
+      {
+        int depth = namespaces.size();
+
+        for (int i = depth - 1; i >= 0; i--)
+        {
+          NameSpace ns = (NameSpace) namespaces.elementAt(i);
+
+          while (null != ns)
+          {
+            if ((null != ns.m_prefix) && prefix.equals(ns.m_prefix))
+            {
+              namespace = ns.m_uri;
+              i = -1;
+
+              break;
+            }
+
+            ns = ns.m_next;
+          }
+        }
+      }
+
+      if (null == namespace)
+      {
+        throw new RuntimeException(
+          XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_PREFIX_MUST_RESOLVE,
+            new Object[]{ prefix }));  //"Prefix must resolve to a namespace: "+prefix);
+      }
+    }
+
+    _localName = (indexOfNSSep < 0)
+                 ? qname : qname.substring(indexOfNSSep + 1);
+                 
+    if (validate)
+    {
+        if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) 
+        {
+           throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName");
+        }
+    }                 
+    _namespaceURI = namespace;
+    _prefix = prefix;
+    m_hashCode = toString().hashCode();
+  }
+
+  /**
+   * Construct a QName from a string, resolving the prefix
+   * using the given namespace context and prefix resolver. 
+   * The default namespace is not resolved.
+   * 
+   * @param qname Qualified name to resolve
+   * @param namespaceContext Namespace Context to use
+   * @param resolver Prefix resolver for this context
+   */
+  public QName(String qname, Element namespaceContext,
+               PrefixResolver resolver)
+  {
+      this(qname, namespaceContext, resolver, false);
+  }
+
+  /**
+   * Construct a QName from a string, resolving the prefix
+   * using the given namespace context and prefix resolver. 
+   * The default namespace is not resolved.
+   * 
+   * @param qname Qualified name to resolve
+   * @param namespaceContext Namespace Context to use
+   * @param resolver Prefix resolver for this context
+   * @param validate If true the new QName will be validated and an IllegalArgumentException will
+   *                 be thrown if it is invalid.
+   */
+  public QName(String qname, Element namespaceContext,
+               PrefixResolver resolver, boolean validate)
+  {
+
+    _namespaceURI = null;
+
+    int indexOfNSSep = qname.indexOf(':');
+
+    if (indexOfNSSep > 0)
+    {
+      if (null != namespaceContext)
+      {
+        String prefix = qname.substring(0, indexOfNSSep);
+
+        _prefix = prefix;
+
+        if (prefix.equals("xml"))
+        {
+          _namespaceURI = S_XMLNAMESPACEURI;
+        }
+        
+        // Do we want this?
+        else if (prefix.equals("xmlns"))
+        {
+          return;
+        }
+        else
+        {
+          _namespaceURI = resolver.getNamespaceForPrefix(prefix,
+                  namespaceContext);
+        }
+
+        if (null == _namespaceURI)
+        {
+          throw new RuntimeException(
+            XMLMessages.createXMLMessage(
+              XMLErrorResources.ER_PREFIX_MUST_RESOLVE,
+              new Object[]{ prefix }));  //"Prefix must resolve to a namespace: "+prefix);
+        }
+      }
+      else
+      {
+
+        // TODO: error or warning...
+      }
+    }
+
+    _localName = (indexOfNSSep < 0)
+                 ? qname : qname.substring(indexOfNSSep + 1);
+
+    if (validate)
+    {
+        if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) 
+        {
+           throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName");
+        }
+    }                 
+                 
+    m_hashCode = toString().hashCode();
+  }
+
+
+  /**
+   * Construct a QName from a string, resolving the prefix
+   * using the given namespace stack. The default namespace is
+   * not resolved.
+   *
+   * @param qname Qualified name to resolve
+   * @param resolver Prefix resolver for this context
+   */
+  public QName(String qname, PrefixResolver resolver)
+  {
+    this(qname, resolver, false);
+  }
+
+  /**
+   * Construct a QName from a string, resolving the prefix
+   * using the given namespace stack. The default namespace is
+   * not resolved.
+   *
+   * @param qname Qualified name to resolve
+   * @param resolver Prefix resolver for this context
+   * @param validate If true the new QName will be validated and an IllegalArgumentException will
+   *                 be thrown if it is invalid.
+   */
+  public QName(String qname, PrefixResolver resolver, boolean validate)
+  {
+
+	String prefix = null;
+    _namespaceURI = null;
+
+    int indexOfNSSep = qname.indexOf(':');
+
+    if (indexOfNSSep > 0)
+    {
+      prefix = qname.substring(0, indexOfNSSep);
+
+      if (prefix.equals("xml"))
+      {
+        _namespaceURI = S_XMLNAMESPACEURI;
+      }
+      else
+      {
+        _namespaceURI = resolver.getNamespaceForPrefix(prefix);
+      }
+
+      if (null == _namespaceURI)
+      {
+        throw new RuntimeException(
+          XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_PREFIX_MUST_RESOLVE,
+            new Object[]{ prefix }));  //"Prefix must resolve to a namespace: "+prefix);
+      }
+      _localName = qname.substring(indexOfNSSep + 1);
+    }
+    else if (indexOfNSSep == 0) 
+    {
+      throw new RuntimeException(
+         XMLMessages.createXMLMessage(
+           XMLErrorResources.ER_NAME_CANT_START_WITH_COLON,
+           null));
+    }
+    else
+    {
+      _localName = qname;
+    }   
+                 
+    if (validate)
+    {
+        if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) 
+        {
+           throw new IllegalArgumentException(XMLMessages.createXMLMessage(
+            XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName");
+        }
+    }                 
+
+              
+    m_hashCode = toString().hashCode();
+    _prefix = prefix;
+  }
+
+  /**
+   * Returns the namespace URI. Returns null if the namespace URI
+   * is not known.
+   *
+   * @return The namespace URI, or null
+   */
+  public String getNamespaceURI()
+  {
+    return _namespaceURI;
+  }
+
+  /**
+   * Returns the namespace prefix. Returns null if the namespace
+   * prefix is not known.
+   *
+   * @return The namespace prefix, or null
+   */
+  public String getPrefix()
+  {
+    return _prefix;
+  }
+
+  /**
+   * Returns the local part of the qualified name.
+   *
+   * @return The local part of the qualified name
+   */
+  public String getLocalName()
+  {
+    return _localName;
+  }
+
+  /**
+   * Return the string representation of the qualified name, using the 
+   * prefix if available, or the '{ns}foo' notation if not. Performs
+   * string concatenation, so beware of performance issues.
+   *
+   * @return the string representation of the namespace
+   */
+  public String toString()
+  {
+
+    return _prefix != null
+           ? (_prefix + ":" + _localName)
+           : (_namespaceURI != null
+              ? ("{"+_namespaceURI + "}" + _localName) : _localName);
+  }
+  
+  /**
+   * Return the string representation of the qualified name using the 
+   * the '{ns}foo' notation. Performs
+   * string concatenation, so beware of performance issues.
+   *
+   * @return the string representation of the namespace
+   */
+  public String toNamespacedString()
+  {
+
+    return (_namespaceURI != null
+              ? ("{"+_namespaceURI + "}" + _localName) : _localName);
+  }
+
+
+  /**
+   * Get the namespace of the qualified name.
+   *
+   * @return the namespace URI of the qualified name
+   */
+  public String getNamespace()
+  {
+    return getNamespaceURI();
+  }
+
+  /**
+   * Get the local part of the qualified name.
+   *
+   * @return the local part of the qualified name
+   */
+  public String getLocalPart()
+  {
+    return getLocalName();
+  }
+
+  /**
+   * Return the cached hashcode of the qualified name.
+   *
+   * @return the cached hashcode of the qualified name
+   */
+  public int hashCode()
+  {
+    return m_hashCode;
+  }
+
+  /**
+   * Override equals and agree that we're equal if
+   * the passed object is a string and it matches
+   * the name of the arg.
+   *
+   * @param ns Namespace URI to compare to
+   * @param localPart Local part of qualified name to compare to 
+   *
+   * @return True if the local name and uri match 
+   */
+  public boolean equals(String ns, String localPart)
+  {
+
+    String thisnamespace = getNamespaceURI();
+
+    return getLocalName().equals(localPart)
+           && (((null != thisnamespace) && (null != ns))
+               ? thisnamespace.equals(ns)
+               : ((null == thisnamespace) && (null == ns)));
+  }
+
+  /**
+   * Override equals and agree that we're equal if
+   * the passed object is a QName and it matches
+   * the name of the arg.
+   *
+   * @return True if the qualified names are equal
+   */
+  public boolean equals(Object object)
+  {
+
+    if (object == this)
+      return true;
+
+    if (object instanceof QName) {
+      QName qname = (QName) object;
+      String thisnamespace = getNamespaceURI();
+      String thatnamespace = qname.getNamespaceURI();
+
+      return getLocalName().equals(qname.getLocalName())
+             && (((null != thisnamespace) && (null != thatnamespace))
+                 ? thisnamespace.equals(thatnamespace)
+                 : ((null == thisnamespace) && (null == thatnamespace)));
+    }
+    else
+      return false;
+  }
+
+  /**
+   * Given a string, create and return a QName object  
+   *
+   *
+   * @param name String to use to create QName
+   *
+   * @return a QName object
+   */
+  public static QName getQNameFromString(String name)
+  {
+
+    StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
+    QName qname;
+    String s1 = tokenizer.nextToken();
+    String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+
+    if (null == s2)
+      qname = new QName(null, s1);
+    else
+      qname = new QName(s1, s2);
+
+    return qname;
+  }
+
+  /**
+   * This function tells if a raw attribute name is a
+   * xmlns attribute.
+   *
+   * @param attRawName Raw name of attribute
+   *
+   * @return True if the attribute starts with or is equal to xmlns 
+   */
+  public static boolean isXMLNSDecl(String attRawName)
+  {
+
+    return (attRawName.startsWith("xmlns")
+            && (attRawName.equals("xmlns")
+                || attRawName.startsWith("xmlns:")));
+  }
+
+  /**
+   * This function tells if a raw attribute name is a
+   * xmlns attribute.
+   *
+   * @param attRawName Raw name of attribute
+   *
+   * @return Prefix of attribute
+   */
+  public static String getPrefixFromXMLNSDecl(String attRawName)
+  {
+
+    int index = attRawName.indexOf(':');
+
+    return (index >= 0) ? attRawName.substring(index + 1) : "";
+  }
+
+  /**
+   * Returns the local name of the given node.
+   *
+   * @param qname Input name
+   *
+   * @return Local part of the name if prefixed, or the given name if not
+   */
+  public static String getLocalPart(String qname)
+  {
+
+    int index = qname.indexOf(':');
+
+    return (index < 0) ? qname : qname.substring(index + 1);
+  }
+
+  /**
+   * Returns the local name of the given node.
+   *
+   * @param qname Input name 
+   *
+   * @return Prefix of name or empty string if none there   
+   */
+  public static String getPrefixPart(String qname)
+  {
+
+    int index = qname.indexOf(':');
+
+    return (index >= 0) ? qname.substring(0, index) : "";
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/SAXSourceLocator.java b/src/main/java/org/apache/xml/utils/SAXSourceLocator.java
new file mode 100644
index 0000000..39fe2a7
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/SAXSourceLocator.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SAXSourceLocator.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.io.Serializable;
+
+import javax.xml.transform.SourceLocator;
+
+import org.xml.sax.Locator;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.LocatorImpl;
+
+/**
+ * Class SAXSourceLocator extends org.xml.sax.helpers.LocatorImpl
+ * for the purpose of implementing the SourceLocator interface, 
+ * and thus can be both a SourceLocator and a SAX Locator.
+ */
+public class SAXSourceLocator extends LocatorImpl
+        implements SourceLocator, Serializable
+{
+    static final long serialVersionUID = 3181680946321164112L;
+  /** The SAX Locator object.
+   *  @serial
+   */
+  Locator m_locator;
+
+  /**
+   * Constructor SAXSourceLocator
+   *
+   */
+  public SAXSourceLocator(){}
+
+  /**
+   * Constructor SAXSourceLocator
+   *
+   *
+   * @param locator Source locator
+   */
+  public SAXSourceLocator(Locator locator)
+  {
+    m_locator = locator;
+    this.setColumnNumber(locator.getColumnNumber());
+    this.setLineNumber(locator.getLineNumber());
+    this.setPublicId(locator.getPublicId());
+    this.setSystemId(locator.getSystemId());
+  }
+  
+  /**
+   * Constructor SAXSourceLocator
+   *
+   *
+   * @param locator Source locator
+   */
+  public SAXSourceLocator(javax.xml.transform.SourceLocator locator)
+  {
+    m_locator = null;
+    this.setColumnNumber(locator.getColumnNumber());
+    this.setLineNumber(locator.getLineNumber());
+    this.setPublicId(locator.getPublicId());
+    this.setSystemId(locator.getSystemId());
+  }
+
+  
+  /**
+   * Constructor SAXSourceLocator
+   *
+   *
+   * @param spe SAXParseException exception.
+   */
+  public SAXSourceLocator(SAXParseException spe)
+  {
+    this.setLineNumber( spe.getLineNumber() );
+    this.setColumnNumber( spe.getColumnNumber() );
+    this.setPublicId( spe.getPublicId() );
+    this.setSystemId( spe.getSystemId() );
+  }
+  
+  /**
+   * Return the public identifier for the current document event.
+   *
+   * <p>The return value is the public identifier of the document
+   * entity or of the external parsed entity in which the markup
+   * triggering the event appears.</p>
+   *
+   * @return A string containing the public identifier, or
+   *         null if none is available.
+   * @see #getSystemId
+   */
+  public String getPublicId()
+  {
+    return (null == m_locator) ? super.getPublicId() : m_locator.getPublicId();
+  }
+
+  /**
+   * Return the system identifier for the current document event.
+   *
+   * <p>The return value is the system identifier of the document
+   * entity or of the external parsed entity in which the markup
+   * triggering the event appears.</p>
+   *
+   * <p>If the system identifier is a URL, the parser must resolve it
+   * fully before passing it to the application.</p>
+   *
+   * @return A string containing the system identifier, or null
+   *         if none is available.
+   * @see #getPublicId
+   */
+  public String getSystemId()
+  {
+    return (null == m_locator) ? super.getSystemId() : m_locator.getSystemId();
+  }
+  
+  /**
+   * Return the line number where the current document event ends.
+   *
+   * <p><strong>Warning:</strong> The return value from the method
+   * is intended only as an approximation for the sake of error
+   * reporting; it is not intended to provide sufficient information
+   * to edit the character content of the original XML document.</p>
+   *
+   * <p>The return value is an approximation of the line number
+   * in the document entity or external parsed entity where the
+   * markup triggering the event appears.</p>
+   *
+   * @return The line number, or -1 if none is available.
+   * @see #getColumnNumber
+   */
+  public int getLineNumber()
+  {
+    return (null == m_locator) ? super.getLineNumber() : m_locator.getLineNumber();
+  }
+
+  /**
+   * Return the column number where the current document event ends.
+   *
+   * <p><strong>Warning:</strong> The return value from the method
+   * is intended only as an approximation for the sake of error
+   * reporting; it is not intended to provide sufficient information
+   * to edit the character content of the original XML document.</p>
+   *
+   * <p>The return value is an approximation of the column number
+   * in the document entity or external parsed entity where the
+   * markup triggering the event appears.</p>
+   *
+   * @return The column number, or -1 if none is available.
+   * @see #getLineNumber
+   */
+  public int getColumnNumber()
+  {
+    return (null == m_locator) ? super.getColumnNumber() : m_locator.getColumnNumber();
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/SecuritySupport.java b/src/main/java/org/apache/xml/utils/SecuritySupport.java
new file mode 100644
index 0000000..c847fab
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/SecuritySupport.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xml.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Base class with security related methods that work on JDK 1.1.
+ */
+class SecuritySupport {
+
+    /*
+     * Make this of type Object so that the verifier won't try to
+     * prove its type, thus possibly trying to load the SecuritySupport12
+     * class.
+     */
+    private static final Object securitySupport;
+
+    static {
+	SecuritySupport ss = null;
+	try {
+	    Class c = Class.forName("java.security.AccessController");
+	    // if that worked, we're on 1.2.
+	    /*
+	    // don't reference the class explicitly so it doesn't
+	    // get dragged in accidentally.
+	    c = Class.forName("javax.mail.SecuritySupport12");
+	    Constructor cons = c.getConstructor(new Class[] { });
+	    ss = (SecuritySupport)cons.newInstance(new Object[] { });
+	    */
+	    /*
+	     * Unfortunately, we can't load the class using reflection
+	     * because the class is package private.  And the class has
+	     * to be package private so the APIs aren't exposed to other
+	     * code that could use them to circumvent security.  Thus,
+	     * we accept the risk that the direct reference might fail
+	     * on some JDK 1.1 JVMs, even though we would never execute
+	     * this code in such a case.  Sigh...
+	     */
+	    ss = new SecuritySupport12();
+	} catch (Exception ex) {
+	    // ignore it
+	} finally {
+	    if (ss == null)
+		ss = new SecuritySupport();
+	    securitySupport = ss;
+	}
+    }
+
+    /**
+     * Return an appropriate instance of this class, depending on whether
+     * we're on a JDK 1.1 or J2SE 1.2 (or later) system.
+     */
+    static SecuritySupport getInstance() {
+	return (SecuritySupport)securitySupport;
+    }
+
+    ClassLoader getContextClassLoader() {
+	return null;
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return null;
+    }
+
+    ClassLoader getParentClassLoader(ClassLoader cl) {
+        return null;
+    }
+
+    String getSystemProperty(String propName) {
+        return System.getProperty(propName);
+    }
+
+    FileInputStream getFileInputStream(File file)
+        throws FileNotFoundException
+    {
+        return new FileInputStream(file);
+    }
+
+    InputStream getResourceAsStream(ClassLoader cl, String name) {
+        InputStream ris;
+        if (cl == null) {
+            ris = ClassLoader.getSystemResourceAsStream(name);
+        } else {
+            ris = cl.getResourceAsStream(name);
+        }
+        return ris;
+    }
+    
+    boolean getFileExists(File f) {
+        return f.exists();
+    }
+    
+    long getLastModified(File f) {
+        return f.lastModified();
+    }    
+}
diff --git a/src/main/java/org/apache/xml/utils/SecuritySupport12.java b/src/main/java/org/apache/xml/utils/SecuritySupport12.java
new file mode 100644
index 0000000..52954b5
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/SecuritySupport12.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xml.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Security related methods that only work on J2SE 1.2 and newer.
+ */
+class SecuritySupport12 extends SecuritySupport {
+
+    ClassLoader getContextClassLoader() {
+        return (ClassLoader)
+                AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                ClassLoader cl = null;
+                try {
+                    cl = Thread.currentThread().getContextClassLoader();
+                } catch (SecurityException ex) { }
+                return cl;
+            }
+        });
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader cl = null;
+                    try {
+                        cl = ClassLoader.getSystemClassLoader();
+                    } catch (SecurityException ex) {}
+                    return cl;
+                }
+            });
+    }
+
+    ClassLoader getParentClassLoader(final ClassLoader cl) {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader parent = null;
+                    try {
+                        parent = cl.getParent();
+                    } catch (SecurityException ex) {}
+
+                    // eliminate loops in case of the boot
+                    // ClassLoader returning itself as a parent
+                    return (parent == cl) ? null : parent;
+                }
+            });
+    }
+
+    String getSystemProperty(final String propName) {
+        return (String)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return System.getProperty(propName);
+                }
+            });
+    }
+
+    FileInputStream getFileInputStream(final File file)
+        throws FileNotFoundException
+    {
+        try {
+            return (FileInputStream)
+                AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    public Object run() throws FileNotFoundException {
+                        return new FileInputStream(file);
+                    }
+                });
+        } catch (PrivilegedActionException e) {
+            throw (FileNotFoundException)e.getException();
+        }
+    }
+
+    InputStream getResourceAsStream(final ClassLoader cl,
+                                           final String name)
+    {
+        return (InputStream)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    InputStream ris;
+                    if (cl == null) {
+                        ris = ClassLoader.getSystemResourceAsStream(name);
+                    } else {
+                        ris = cl.getResourceAsStream(name);
+                    }
+                    return ris;
+                }
+            });
+    }
+    
+    boolean getFileExists(final File f) {
+    return ((Boolean)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Boolean(f.exists());
+                }
+            })).booleanValue();
+    }
+    
+    long getLastModified(final File f) {
+    return ((Long)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Long(f.lastModified());
+                }
+            })).longValue();
+    }
+        
+}
diff --git a/src/main/java/org/apache/xml/utils/StopParseException.java b/src/main/java/org/apache/xml/utils/StopParseException.java
new file mode 100644
index 0000000..2f809da
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/StopParseException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StopParseException.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * This is a special exception that is used to stop parsing when 
+ * search for an element.  For instance, when searching for xml:stylesheet 
+ * PIs, it is used to stop the parse once the document element is found.
+ * @see StylesheetPIHandler
+ * @xsl.usage internal
+ */
+public class StopParseException extends org.xml.sax.SAXException
+{
+        static final long serialVersionUID = 210102479218258961L;
+  /**
+   * Constructor StopParseException.
+   */
+  StopParseException()
+  {
+    super("Stylesheet PIs found, stop the parse");
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/StringBufferPool.java b/src/main/java/org/apache/xml/utils/StringBufferPool.java
new file mode 100644
index 0000000..d70e6de
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/StringBufferPool.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StringBufferPool.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * This class pools string buffers, since they are reused so often.
+ * String buffers are good candidates for pooling, because of 
+ * their supporting character arrays.
+ * @xsl.usage internal
+ */
+public class StringBufferPool
+{
+
+  /** The global pool of string buffers.   */
+  private static ObjectPool m_stringBufPool =
+    new ObjectPool(org.apache.xml.utils.FastStringBuffer.class);
+
+  /**
+   * Get the first free instance of a string buffer, or create one 
+   * if there are no free instances.
+   *
+   * @return A string buffer ready for use.
+   */
+  public synchronized static FastStringBuffer get()
+  {
+    return (FastStringBuffer) m_stringBufPool.getInstance();
+  }
+
+  /**
+   * Return a string buffer back to the pool.
+   *
+   * @param sb Must be a non-null reference to a string buffer.
+   */
+  public synchronized static void free(FastStringBuffer sb)
+  {
+    // Since this isn't synchronized, setLength must be 
+    // done before the instance is freed.
+    // Fix attributed to Peter Speck <speck@ruc.dk>.
+    sb.setLength(0);
+    m_stringBufPool.freeInstance(sb);
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/StringToIntTable.java b/src/main/java/org/apache/xml/utils/StringToIntTable.java
new file mode 100644
index 0000000..9287d86
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/StringToIntTable.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StringToIntTable.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * A very simple lookup table that stores a list of strings, the even
+ * number strings being keys, and the odd number strings being values.
+ * @xsl.usage internal
+ */
+public class StringToIntTable
+{
+
+  public static final int INVALID_KEY = -10000;
+  
+  /** Block size to allocate          */
+  private int m_blocksize;
+
+  /** Array of strings this table points to. Associated with ints
+   * in m_values         */
+  private String m_map[];
+
+  /** Array of ints this table points. Associated with strings from
+   * m_map.         */
+  private int m_values[];
+
+  /** Number of ints in the table          */
+  private int m_firstFree = 0;
+
+  /** Size of this table         */
+  private int m_mapSize;
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is very small, for small lists.
+   */
+  public StringToIntTable()
+  {
+
+    m_blocksize = 8;
+    m_mapSize = m_blocksize;
+    m_map = new String[m_blocksize];
+    m_values = new int[m_blocksize];
+  }
+
+  /**
+   * Construct a StringToIntTable, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public StringToIntTable(int blocksize)
+  {
+
+    m_blocksize = blocksize;
+    m_mapSize = blocksize;
+    m_map = new String[blocksize];
+    m_values = new int[m_blocksize];
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return the length of the list 
+   */
+  public final int getLength()
+  {
+    return m_firstFree;
+  }
+
+  /**
+   * Append a string onto the vector.
+   *
+   * @param key String to append
+   * @param value The int value of the string
+   */
+  public final void put(String key, int value)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      String newMap[] = new String[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+
+      int newValues[] = new int[m_mapSize];
+
+      System.arraycopy(m_values, 0, newValues, 0, m_firstFree + 1);
+
+      m_values = newValues;
+    }
+
+    m_map[m_firstFree] = key;
+    m_values[m_firstFree] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Tell if the table contains the given string.
+   *
+   * @param key String to look for
+   *
+   * @return The String's int value
+   * 
+   */
+  public final int get(String key)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i].equals(key))
+        return m_values[i];
+    }
+
+	return INVALID_KEY;
+  }
+
+  /**
+   * Tell if the table contains the given string. Ignore case.
+   *
+   * @param key String to look for
+   *
+   * @return The string's int value
+   */
+  public final int getIgnoreCase(String key)
+  {
+
+    if (null == key)
+        return INVALID_KEY;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i].equalsIgnoreCase(key))
+        return m_values[i];
+    }
+
+    return INVALID_KEY;
+  }
+
+  /**
+   * Tell if the table contains the given string.
+   *
+   * @param key String to look for
+   *
+   * @return True if the string is in the table
+   */
+  public final boolean contains(String key)
+  {
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i].equals(key))
+        return true;
+    }
+
+    return false;
+  }
+  
+  /**
+   * Return array of keys in the table.
+   * 
+   * @return Array of strings
+   */
+  public final String[] keys()
+  {
+    String [] keysArr = new String[m_firstFree];
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      keysArr[i] = m_map[i];
+    }
+
+    return keysArr;
+  }  
+}
diff --git a/src/main/java/org/apache/xml/utils/StringVector.java b/src/main/java/org/apache/xml/utils/StringVector.java
new file mode 100644
index 0000000..79dee1d
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/StringVector.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StringVector.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * A very simple table that stores a list of strings, optimized
+ * for small lists.
+ * @xsl.usage internal
+ */
+public class StringVector implements java.io.Serializable
+{
+    static final long serialVersionUID = 4995234972032919748L;
+
+  /** @serial Size of blocks to allocate           */
+  protected int m_blocksize;
+
+  /** @serial Array of strings this contains          */
+  protected String m_map[];
+
+  /** @serial Number of strings this contains          */
+  protected int m_firstFree = 0;
+
+  /** @serial Size of the array          */
+  protected int m_mapSize;
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is very small, for small lists.
+   */
+  public StringVector()
+  {
+
+    m_blocksize = 8;
+    m_mapSize = m_blocksize;
+    m_map = new String[m_blocksize];
+  }
+
+  /**
+   * Construct a StringVector, using the given block size.
+   *
+   * @param blocksize Size of the blocks to allocate 
+   */
+  public StringVector(int blocksize)
+  {
+
+    m_blocksize = blocksize;
+    m_mapSize = blocksize;
+    m_map = new String[blocksize];
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return Number of strings in the list 
+   */
+  public int getLength()
+  {
+    return m_firstFree;
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return Number of strings in the list
+   */
+  public final int size()
+  {
+    return m_firstFree;
+  }
+
+  /**
+   * Append a string onto the vector.
+   *
+   * @param value Sting to add to the vector
+   */
+  public final void addElement(String value)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      String newMap[] = new String[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    m_map[m_firstFree] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Get the nth element.
+   *
+   * @param i Index of string to find
+   *
+   * @return String at given index
+   */
+  public final String elementAt(int i)
+  {
+    return m_map[i];
+  }
+
+  /**
+   * Tell if the table contains the given string.
+   *
+   * @param s String to look for
+   *
+   * @return True if the string is in this table  
+   */
+  public final boolean contains(String s)
+  {
+
+    if (null == s)
+      return false;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i].equals(s))
+        return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Tell if the table contains the given string. Ignore case.
+   *
+   * @param s String to find
+   *
+   * @return True if the String is in this vector
+   */
+  public final boolean containsIgnoreCase(String s)
+  {
+
+    if (null == s)
+      return false;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      if (m_map[i].equalsIgnoreCase(s))
+        return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Tell if the table contains the given string.
+   *
+   * @param s String to push into the vector
+   */
+  public final void push(String s)
+  {
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      String newMap[] = new String[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    m_map[m_firstFree] = s;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Pop the tail of this vector.
+   *
+   * @return The String last added to this vector or null not found.
+   * The string is removed from the vector.
+   */
+  public final String pop()
+  {
+
+    if (m_firstFree <= 0)
+      return null;
+
+    m_firstFree--;
+
+    String s = m_map[m_firstFree];
+
+    m_map[m_firstFree] = null;
+
+    return s;
+  }
+
+  /**
+   * Get the string at the tail of this vector without popping.
+   *
+   * @return The string at the tail of this vector.
+   */
+  public final String peek()
+  {
+    return (m_firstFree <= 0) ? null : m_map[m_firstFree - 1];
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/StylesheetPIHandler.java b/src/main/java/org/apache/xml/utils/StylesheetPIHandler.java
new file mode 100644
index 0000000..8005d06
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/StylesheetPIHandler.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StylesheetPIHandler.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+
+import org.apache.xml.utils.SystemIDResolver;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Search for the xml-stylesheet processing instructions in an XML document.
+ * @see <a href="http://www.w3.org/TR/xml-stylesheet/">Associating Style Sheets with XML documents, Version 1.0</a>
+ */
+public class StylesheetPIHandler extends DefaultHandler
+{
+  /** The baseID of the document being processed.  */
+  String m_baseID;
+
+  /** The desired media criteria. */
+  String m_media;
+
+  /** The desired title criteria.  */
+  String m_title;
+
+  /** The desired character set criteria.   */
+  String m_charset;
+
+  /** A list of SAXSource objects that match the criteria.  */
+  Vector m_stylesheets = new Vector();
+  
+  // Add code to use a URIResolver. Patch from Dmitri Ilyin. 
+  
+  /**
+   * The object that implements the URIResolver interface,
+   * or null.
+   */
+  URIResolver m_uriResolver;
+
+  /**
+   * Get the object that will be used to resolve URIs in href 
+   * in xml-stylesheet processing instruction.
+   *
+   * @param resolver An object that implements the URIResolver interface,
+   * or null.
+   */
+  public void setURIResolver(URIResolver resolver)
+  {
+    m_uriResolver = resolver;
+  }
+
+  /**
+   * Get the object that will be used to resolve URIs in href 
+   * in xml-stylesheet processing instruction.
+   *
+   * @return The URIResolver that was set with setURIResolver.
+   */
+  public URIResolver getURIResolver()
+  {
+    return m_uriResolver;
+  }
+
+  /**
+   * Construct a StylesheetPIHandler instance that will search 
+   * for xml-stylesheet PIs based on the given criteria.
+   *
+   * @param baseID The base ID of the XML document, needed to resolve 
+   *               relative IDs.
+   * @param media The desired media criteria.
+   * @param title The desired title criteria.
+   * @param charset The desired character set criteria.
+   */
+  public StylesheetPIHandler(String baseID, String media, String title,
+                             String charset)
+  {
+
+    m_baseID = baseID;
+    m_media = media;
+    m_title = title;
+    m_charset = charset;
+  }
+
+  /**
+   * Return the last stylesheet found that match the constraints.
+   *
+   * @return Source object that references the last stylesheet reference 
+   *         that matches the constraints.
+   */
+  public Source getAssociatedStylesheet()
+  {
+
+    int sz = m_stylesheets.size();
+
+    if (sz > 0)
+    {
+      Source source = (Source) m_stylesheets.elementAt(sz-1);
+      return source;      
+    }
+    else
+      return null;
+  }
+
+  /**
+   * Handle the xml-stylesheet processing instruction.
+   *
+   * @param target The processing instruction target.
+   * @param data The processing instruction data, or null if
+   *             none is supplied.
+   * @throws org.xml.sax.SAXException Any SAX exception, possibly
+   *            wrapping another exception.
+   * @see org.xml.sax.ContentHandler#processingInstruction
+   * @see <a href="http://www.w3.org/TR/xml-stylesheet/">Associating Style Sheets with XML documents, Version 1.0</a>
+   */
+  public void processingInstruction(String target, String data)
+          throws org.xml.sax.SAXException
+  {
+
+    if (target.equals("xml-stylesheet"))
+    {
+      String href = null;  // CDATA #REQUIRED
+      String type = null;  // CDATA #REQUIRED
+      String title = null;  // CDATA #IMPLIED
+      String media = null;  // CDATA #IMPLIED
+      String charset = null;  // CDATA #IMPLIED
+      boolean alternate = false;  // (yes|no) "no"
+      StringTokenizer tokenizer = new StringTokenizer(data, " \t=\n", true);
+      boolean lookedAhead = false; 
+      Source source = null;
+
+      String token = "";
+      while (tokenizer.hasMoreTokens())
+      {        
+        if (!lookedAhead)
+          token = tokenizer.nextToken();
+        else
+          lookedAhead = false;
+        if (tokenizer.hasMoreTokens() && 
+               (token.equals(" ") || token.equals("\t") || token.equals("=")))
+          continue;
+          
+        String name = token;  
+        if (name.equals("type"))
+        { 
+          token = tokenizer.nextToken();
+          while (tokenizer.hasMoreTokens() && 
+               (token.equals(" " ) || token.equals("\t") || token.equals("=")))
+            token = tokenizer.nextToken();
+          type = token.substring(1, token.length() - 1);
+          
+        }
+        else if (name.equals("href"))
+        {
+          token = tokenizer.nextToken();
+          while (tokenizer.hasMoreTokens() && 
+               (token.equals(" " ) || token.equals("\t") || token.equals("=")))
+            token = tokenizer.nextToken();
+          href = token;
+          if (tokenizer.hasMoreTokens())
+          {
+            token = tokenizer.nextToken();
+            // If the href value has parameters to be passed to a 
+            // servlet(something like "foobar?id=12..."), 
+            // we want to make sure we get them added to
+            // the href value. Without this check, we would move on 
+            // to try to process another attribute and that would be
+            // wrong.
+            // We need to set lookedAhead here to flag that we
+            // already have the next token. 
+            while ( token.equals("=") && tokenizer.hasMoreTokens())
+            {  
+              href = href + token + tokenizer.nextToken();
+              if (tokenizer.hasMoreTokens())
+              {  
+                token = tokenizer.nextToken();
+                lookedAhead = true;
+              }
+              else
+              {
+                break;
+              }
+            }
+          }
+          href = href.substring(1, href.length() - 1);
+          try
+          { 
+            // Add code to use a URIResolver. Patch from Dmitri Ilyin. 
+            if (m_uriResolver != null) 
+            {
+              source = m_uriResolver.resolve(href, m_baseID);
+            } 
+           else 
+            {
+              href = SystemIDResolver.getAbsoluteURI(href, m_baseID);
+              source = new SAXSource(new InputSource(href));
+            }            
+          }
+          catch(TransformerException te)
+          {
+            throw new org.xml.sax.SAXException(te);
+          }
+        }
+        else if (name.equals("title"))
+        {
+          token = tokenizer.nextToken();
+          while (tokenizer.hasMoreTokens() && 
+               (token.equals(" " ) || token.equals("\t") || token.equals("=")))
+            token = tokenizer.nextToken();
+          title = token.substring(1, token.length() - 1);
+        }
+        else if (name.equals("media"))
+        {
+          token = tokenizer.nextToken();
+          while (tokenizer.hasMoreTokens() && 
+               (token.equals(" " ) || token.equals("\t") || token.equals("=")))
+            token = tokenizer.nextToken();
+          media = token.substring(1, token.length() - 1);
+        }
+        else if (name.equals("charset"))
+        {
+          token = tokenizer.nextToken();
+          while (tokenizer.hasMoreTokens() && 
+              (token.equals(" " ) || token.equals("\t") || token.equals("=")))
+            token = tokenizer.nextToken();
+          charset = token.substring(1, token.length() - 1);
+        }
+        else if (name.equals("alternate"))
+        {
+          token = tokenizer.nextToken();
+          while (tokenizer.hasMoreTokens() && 
+               (token.equals(" " ) || token.equals("\t") || token.equals("=")))
+            token = tokenizer.nextToken();
+          alternate = token.substring(1, token.length()
+                                             - 1).equals("yes");
+        }
+        
+      }
+
+      if ((null != type) 
+          && (type.equals("text/xsl") || type.equals("text/xml") || type.equals("application/xml+xslt"))  
+          && (null != href))
+      {
+        if (null != m_media)
+        {
+          if (null != media)
+          {
+            if (!media.equals(m_media))
+              return;
+          }
+          else
+            return;
+        }
+
+        if (null != m_charset)
+        {
+          if (null != charset)
+          {
+            if (!charset.equals(m_charset))
+              return;
+          }
+          else
+            return;
+        }
+
+        if (null != m_title)
+        {
+          if (null != title)
+          {
+            if (!title.equals(m_title))
+              return;
+          }
+          else
+            return;
+        }
+
+        m_stylesheets.addElement(source);
+      }
+    }
+  }
+  
+  
+  /**
+   * The spec notes that "The xml-stylesheet processing instruction is allowed only in the prolog of an XML document.",
+   * so, at least for right now, I'm going to go ahead an throw a TransformerException
+   * in order to stop the parse.
+   *
+   * @param namespaceURI The Namespace URI, or an empty string.
+   * @param localName The local name (without prefix), or empty string if not namespace processing.
+   * @param qName The qualified name (with prefix).
+   * @param atts  The specified or defaulted attributes.
+   *
+   * @throws StopParseException since there can be no valid xml-stylesheet processing 
+   *                            instructions past the first element.
+   */
+  public void startElement(
+          String namespaceURI, String localName, String qName, Attributes atts)
+            throws org.xml.sax.SAXException
+  {
+    throw new StopParseException();
+  }
+
+  /**
+    * Added additional getter and setter methods for the Base Id
+    * to fix bugzilla bug 24187
+    * 
+    */
+   public void setBaseId(String baseId) {
+       m_baseID = baseId;
+ 
+   }
+   public String  getBaseId() {
+       return m_baseID ;
+   }
+
+}
diff --git a/src/main/java/org/apache/xml/utils/SuballocatedIntVector.java b/src/main/java/org/apache/xml/utils/SuballocatedIntVector.java
new file mode 100644
index 0000000..3104939
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/SuballocatedIntVector.java
@@ -0,0 +1,557 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SuballocatedIntVector.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * A very simple table that stores a list of int. Very similar API to our
+ * IntVector class (same API); different internal storage.
+ * 
+ * This version uses an array-of-arrays solution. Read/write access is thus
+ * a bit slower than the simple IntVector, and basic storage is a trifle
+ * higher due to the top-level array -- but appending is O(1) fast rather
+ * than O(N**2) slow, which will swamp those costs in situations where
+ * long vectors are being built up.
+ * 
+ * Known issues:
+ * 
+ * Some methods are private because they haven't yet been tested properly.
+ *
+ * Retrieval performance is critical, since this is used at the core
+ * of the DTM model. (Append performance is almost as important.)
+ * That's pushing me toward just letting reads from unset indices
+ * throw exceptions or return stale data; safer behavior would have
+ * performance costs.
+ * */
+public class SuballocatedIntVector
+{
+  /** Size of blocks to allocate          */
+  protected int m_blocksize;
+
+  /** Bitwise addressing (much faster than div/remainder */
+  protected int m_SHIFT, m_MASK;
+  
+  /** The default number of blocks to (over)allocate by */
+  protected static final int NUMBLOCKS_DEFAULT = 32;
+  
+  /** The number of blocks to (over)allocate by */
+  protected int m_numblocks = NUMBLOCKS_DEFAULT;
+  
+  /** Array of arrays of ints          */
+  protected int m_map[][];
+
+  /** Number of ints in array          */
+  protected int m_firstFree = 0;
+
+  /** "Shortcut" handle to m_map[0]. Surprisingly helpful for short vectors. */
+  protected int m_map0[];
+
+  /** "Shortcut" handle to most recently added row of m_map.
+   * Very helpful during construction.
+   * @xsl.usage internal
+   */
+  protected int m_buildCache[];
+  protected int m_buildCacheStartIndex;
+
+
+  /**
+   * Default constructor.  Note that the default
+   * block size is currently 2K, which may be overkill for
+   * small lists and undershootng for large ones.
+   */
+  public SuballocatedIntVector()
+  {
+    this(2048);
+  }
+
+  /**
+   * Construct a IntVector, using the given block size and number
+   * of blocks. For efficiency, we will round the requested size 
+   * off to a power of two.
+   *
+   * @param blocksize Size of block to allocate
+   * @param numblocks Number of blocks to allocate
+   * */
+  public SuballocatedIntVector(int blocksize, int numblocks)
+  {
+    //m_blocksize = blocksize;
+    for(m_SHIFT=0;0!=(blocksize>>>=1);++m_SHIFT)
+      ;
+    m_blocksize=1<<m_SHIFT;
+    m_MASK=m_blocksize-1;
+    m_numblocks = numblocks;
+    	
+    m_map0=new int[m_blocksize];
+    m_map = new int[numblocks][];
+    m_map[0]=m_map0;
+    m_buildCache = m_map0;
+    m_buildCacheStartIndex = 0;
+  }
+	
+  /** Construct a IntVector, using the given block size and
+   * the default number of blocks (32).
+   *
+   * @param blocksize Size of block to allocate
+   * */
+  public SuballocatedIntVector(int blocksize)
+  {
+    this(blocksize, NUMBLOCKS_DEFAULT);
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return length of the list
+   */
+  public int size()
+  {
+    return m_firstFree;
+  }
+  
+  /**
+   * Set the length of the list. This will only work to truncate the list, and
+   * even then it has not been heavily tested and may not be trustworthy.
+   *
+   * @return length of the list
+   */
+  public void setSize(int sz)
+  {
+    if(m_firstFree>sz) // Whups; had that backward!
+      m_firstFree = sz;
+  }
+
+  /**
+   * Append a int onto the vector.
+   *
+   * @param value Int to add to the list 
+   */
+  public  void addElement(int value)
+  {
+    int indexRelativeToCache = m_firstFree - m_buildCacheStartIndex;
+
+    // Is the new index an index into the cache row of m_map?
+    if(indexRelativeToCache >= 0 && indexRelativeToCache < m_blocksize) {
+      m_buildCache[indexRelativeToCache]=value;
+      ++m_firstFree;
+    } else {
+      // Growing the outer array should be rare. We initialize to a
+      // total of m_blocksize squared elements, which at the default
+      // size is 4M integers... and we grow by at least that much each
+      // time.  However, attempts to microoptimize for this (assume
+      // long enough and catch exceptions) yield no noticable
+      // improvement.
+
+      int index=m_firstFree>>>m_SHIFT;
+      int offset=m_firstFree&m_MASK;
+
+      if(index>=m_map.length)
+      {
+	int newsize=index+m_numblocks;
+	int[][] newMap=new int[newsize][];
+	System.arraycopy(m_map, 0, newMap, 0, m_map.length);
+	m_map=newMap;
+      }
+      int[] block=m_map[index];
+      if(null==block)
+	block=m_map[index]=new int[m_blocksize];
+      block[offset]=value;
+
+      // Cache the current row of m_map.  Next m_blocksize-1
+      // values added will go to this row.
+      m_buildCache = block;
+      m_buildCacheStartIndex = m_firstFree-offset;
+
+      ++m_firstFree;
+    }
+  }
+
+  /**
+   * Append several int values onto the vector.
+   *
+   * @param value Int to add to the list 
+   */
+  private  void addElements(int value, int numberOfElements)
+  {
+    if(m_firstFree+numberOfElements<m_blocksize)
+      for (int i = 0; i < numberOfElements; i++) 
+      {
+        m_map0[m_firstFree++]=value;
+      }
+    else
+    {
+      int index=m_firstFree>>>m_SHIFT;
+      int offset=m_firstFree&m_MASK;
+      m_firstFree+=numberOfElements;
+      while( numberOfElements>0)
+      {
+        if(index>=m_map.length)
+        {
+          int newsize=index+m_numblocks;
+          int[][] newMap=new int[newsize][];
+          System.arraycopy(m_map, 0, newMap, 0, m_map.length);
+          m_map=newMap;
+        }
+        int[] block=m_map[index];
+        if(null==block)
+          block=m_map[index]=new int[m_blocksize];
+        int copied=(m_blocksize-offset < numberOfElements)
+          ? m_blocksize-offset : numberOfElements;
+        numberOfElements-=copied;
+        while(copied-- > 0)
+          block[offset++]=value;
+
+        ++index;offset=0;
+      }
+    }
+  }
+  
+  /**
+   * Append several slots onto the vector, but do not set the values.
+   * Note: "Not Set" means the value is unspecified.
+   *
+   * @param numberOfElements Int to add to the list 
+   */
+  private  void addElements(int numberOfElements)
+  {
+    int newlen=m_firstFree+numberOfElements;
+    if(newlen>m_blocksize)
+    {
+      int index=m_firstFree>>>m_SHIFT;
+      int newindex=(m_firstFree+numberOfElements)>>>m_SHIFT;
+      for(int i=index+1;i<=newindex;++i)
+        m_map[i]=new int[m_blocksize];
+    }
+    m_firstFree=newlen;
+  }
+  
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   *
+   * Insertion may be an EXPENSIVE operation!
+   *
+   * @param value Int to insert
+   * @param at Index of where to insert 
+   */
+  private  void insertElementAt(int value, int at)
+  {
+    if(at==m_firstFree)
+      addElement(value);
+    else if (at>m_firstFree)
+    {
+      int index=at>>>m_SHIFT;
+      if(index>=m_map.length)
+      {
+        int newsize=index+m_numblocks;
+        int[][] newMap=new int[newsize][];
+        System.arraycopy(m_map, 0, newMap, 0, m_map.length);
+        m_map=newMap;
+      }
+      int[] block=m_map[index];
+      if(null==block)
+        block=m_map[index]=new int[m_blocksize];
+      int offset=at&m_MASK;
+          block[offset]=value;
+          m_firstFree=offset+1;
+        }
+    else
+    {
+      int index=at>>>m_SHIFT;
+      int maxindex=m_firstFree>>>m_SHIFT; // %REVIEW% (m_firstFree+1?)
+      ++m_firstFree;
+      int offset=at&m_MASK;
+      int push;
+      
+      // ***** Easier to work down from top?
+      while(index<=maxindex)
+      {
+        int copylen=m_blocksize-offset-1;
+        int[] block=m_map[index];
+        if(null==block)
+        {
+          push=0;
+          block=m_map[index]=new int[m_blocksize];
+        }
+        else
+        {
+          push=block[m_blocksize-1];
+          System.arraycopy(block, offset , block, offset+1, copylen);
+        }
+        block[offset]=value;
+        value=push;
+        offset=0;
+        ++index;
+      }
+    }
+  }
+
+  /**
+   * Wipe it out. Currently defined as equivalent to setSize(0).
+   */
+  public void removeAllElements()
+  {
+    m_firstFree = 0;
+    m_buildCache = m_map0;
+    m_buildCacheStartIndex = 0;
+  }
+
+  /**
+   * Removes the first occurrence of the argument from this vector.
+   * If the object is found in this vector, each component in the vector
+   * with an index greater or equal to the object's index is shifted
+   * downward to have an index one smaller than the value it had
+   * previously.
+   *
+   * @param s Int to remove from array
+   *
+   * @return True if the int was removed, false if it was not found
+   */
+  private  boolean removeElement(int s)
+  {
+    int at=indexOf(s,0);
+    if(at<0)
+      return false;
+    removeElementAt(at);
+    return true;
+  }
+
+  /**
+   * Deletes the component at the specified index. Each component in
+   * this vector with an index greater or equal to the specified
+   * index is shifted downward to have an index one smaller than
+   * the value it had previously.
+   *
+   * @param i index of where to remove and int
+   */
+  private  void removeElementAt(int at)
+  {
+        // No point in removing elements that "don't exist"...  
+    if(at<m_firstFree)
+    {
+      int index=at>>>m_SHIFT;
+      int maxindex=m_firstFree>>>m_SHIFT;
+      int offset=at&m_MASK;
+      
+      while(index<=maxindex)
+      {
+        int copylen=m_blocksize-offset-1;
+        int[] block=m_map[index];
+        if(null==block)
+          block=m_map[index]=new int[m_blocksize];
+        else
+          System.arraycopy(block, offset+1, block, offset, copylen);
+        if(index<maxindex)
+        {
+          int[] next=m_map[index+1];
+          if(next!=null)
+            block[m_blocksize-1]=(next!=null) ? next[0] : 0;
+        }
+        else
+          block[m_blocksize-1]=0;
+        offset=0;
+        ++index;
+      }
+    }
+    --m_firstFree;
+  }
+
+  /**
+   * Sets the component at the specified index of this vector to be the
+   * specified object. The previous component at that position is discarded.
+   *
+   * The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.
+   *
+   * @param value object to set
+   * @param at    Index of where to set the object
+   */
+  public void setElementAt(int value, int at)
+  {
+    if(at<m_blocksize)
+      m_map0[at]=value;
+    else
+    {
+      int index=at>>>m_SHIFT;
+      int offset=at&m_MASK;
+        
+      if(index>=m_map.length)
+      {
+	int newsize=index+m_numblocks;
+	int[][] newMap=new int[newsize][];
+	System.arraycopy(m_map, 0, newMap, 0, m_map.length);
+	m_map=newMap;
+      }
+
+      int[] block=m_map[index];
+      if(null==block)
+	block=m_map[index]=new int[m_blocksize];
+      block[offset]=value;
+    }
+
+    if(at>=m_firstFree)
+      m_firstFree=at+1;
+  }
+  
+
+  /**
+   * Get the nth element. This is often at the innermost loop of an
+   * application, so performance is critical.
+   *
+   * @param i index of value to get
+   *
+   * @return value at given index. If that value wasn't previously set,
+   * the result is undefined for performance reasons. It may throw an
+   * exception (see below), may return zero, or (if setSize has previously
+   * been used) may return stale data.
+   *
+   * @throws ArrayIndexOutOfBoundsException if the index was _clearly_
+   * unreasonable (negative, or past the highest block).
+   *
+   * @throws NullPointerException if the index points to a block that could
+   * have existed (based on the highest index used) but has never had anything
+   * set into it.
+   * %REVIEW% Could add a catch to create the block in that case, or return 0.
+   * Try/Catch is _supposed_ to be nearly free when not thrown to. Do we
+   * believe that? Should we have a separate safeElementAt?
+   */
+  public int elementAt(int i)
+  {
+    // This is actually a significant optimization!
+    if(i<m_blocksize)
+      return m_map0[i];
+
+    return m_map[i>>>m_SHIFT][i&m_MASK];
+  }
+
+  /**
+   * Tell if the table contains the given node.
+   *
+   * @param s object to look for
+   *
+   * @return true if the object is in the list
+   */
+  private  boolean contains(int s)
+  {
+    return (indexOf(s,0) >= 0);
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem object to look for
+   * @param index Index of where to begin search
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public int indexOf(int elem, int index)
+  {
+        if(index>=m_firstFree)
+                return -1;
+          
+    int bindex=index>>>m_SHIFT;
+    int boffset=index&m_MASK;
+    int maxindex=m_firstFree>>>m_SHIFT;
+    int[] block;
+    
+    for(;bindex<maxindex;++bindex)
+    {
+      block=m_map[bindex];
+      if(block!=null)
+        for(int offset=boffset;offset<m_blocksize;++offset)
+          if(block[offset]==elem)
+            return offset+bindex*m_blocksize;
+      boffset=0; // after first
+    }
+    // Last block may need to stop before end
+    int maxoffset=m_firstFree&m_MASK;
+    block=m_map[maxindex];
+    for(int offset=boffset;offset<maxoffset;++offset)
+      if(block[offset]==elem)
+        return offset+maxindex*m_blocksize;
+
+    return -1;    
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem object to look for
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public int indexOf(int elem)
+  {
+    return indexOf(elem,0);
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Object to look for
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  private  int lastIndexOf(int elem)
+  {
+    int boffset=m_firstFree&m_MASK;
+    for(int index=m_firstFree>>>m_SHIFT;
+        index>=0;
+        --index)
+    {
+      int[] block=m_map[index];
+      if(block!=null)
+        for(int offset=boffset; offset>=0; --offset)
+          if(block[offset]==elem)
+            return offset+index*m_blocksize;
+      boffset=0; // after first
+    }
+    return -1;
+  }
+  
+  /**
+   * Return the internal m_map0 array
+   * @return the m_map0 array
+   */
+  public final int[] getMap0()
+  {
+    return m_map0;
+  }
+  
+  /**
+   * Return the m_map double array
+   * @return the internal map of array of arrays 
+   */
+  public final int[][] getMap()
+  {
+    return m_map;
+  }
+  
+}
diff --git a/src/main/java/org/apache/xml/utils/SystemIDResolver.java b/src/main/java/org/apache/xml/utils/SystemIDResolver.java
new file mode 100644
index 0000000..7fe63f5
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/SystemIDResolver.java
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SystemIDResolver.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.io.File;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.utils.URI.MalformedURIException;
+
+/**
+ * This class is used to resolve relative URIs and SystemID 
+ * strings into absolute URIs.
+ *
+ * <p>This is a generic utility for resolving URIs, other than the 
+ * fact that it's declared to throw TransformerException.  Please 
+ * see code comments for details on how resolution is performed.</p>
+ * @xsl.usage internal
+ */
+public class SystemIDResolver
+{
+
+  /**
+   * Get an absolute URI from a given relative URI (local path). 
+   * 
+   * <p>The relative URI is a local filesystem path. The path can be
+   * absolute or relative. If it is a relative path, it is resolved relative 
+   * to the system property "user.dir" if it is available; if not (i.e. in an 
+   * Applet perhaps which throws SecurityException) then we just return the
+   * relative path. The space and backslash characters are also replaced to
+   * generate a good absolute URI.</p>
+   *
+   * @param localPath The relative URI to resolve
+   *
+   * @return Resolved absolute URI
+   */
+  public static String getAbsoluteURIFromRelative(String localPath)
+  {
+    if (localPath == null || localPath.length() == 0)
+      return "";
+      
+    // If the local path is a relative path, then it is resolved against
+    // the "user.dir" system property.
+    String absolutePath = localPath;
+    if (!isAbsolutePath(localPath))
+    {
+      try 
+      {
+        absolutePath = getAbsolutePathFromRelativePath(localPath);
+      }
+      // user.dir not accessible from applet
+      catch (SecurityException se) 
+      {
+        return "file:" + localPath;
+      }
+    }
+
+    String urlString;
+    if (null != absolutePath)
+    {
+      if (absolutePath.startsWith(File.separator))
+        urlString = "file://" + absolutePath;
+      else
+        urlString = "file:///" + absolutePath;        
+    }
+    else
+      urlString = "file:" + localPath;
+    
+    return replaceChars(urlString);
+  }
+  
+  /**
+   * Return an absolute path from a relative path.
+   *
+   * @param relativePath A relative path
+   * @return The absolute path
+   */
+  private static String getAbsolutePathFromRelativePath(String relativePath)
+  {
+    return new File(relativePath).getAbsolutePath();
+  }
+  
+  /**
+   * Return true if the systemId denotes an absolute URI .
+   *
+   * @param systemId The systemId string
+   * @return true if the systemId is an an absolute URI
+   */
+  public static boolean isAbsoluteURI(String systemId)
+  {
+     /** http://www.ietf.org/rfc/rfc2396.txt
+      *   Authors should be aware that a path segment which contains a colon
+      * character cannot be used as the first segment of a relative URI path
+      * (e.g., "this:that"), because it would be mistaken for a scheme name.
+     **/
+     /** 
+      * %REVIEW% Can we assume here that systemId is a valid URI?
+      * It looks like we cannot ( See discussion of this common problem in 
+      * Bugzilla Bug 22777 ). 
+     **/
+     //"fix" for Bugzilla Bug 22777
+    if(isWindowsAbsolutePath(systemId)){
+        return false;
+     }
+    
+    final int fragmentIndex = systemId.indexOf('#');
+    final int queryIndex = systemId.indexOf('?');
+    final int slashIndex = systemId.indexOf('/');
+    final int colonIndex = systemId.indexOf(':');
+    
+    //finding substring  before '#', '?', and '/' 
+    int index = systemId.length() -1;
+    if(fragmentIndex > 0) 
+        index = fragmentIndex;
+    if((queryIndex > 0) && (queryIndex <index)) 
+        index = queryIndex;
+    if((slashIndex > 0) && (slashIndex <index))
+        index = slashIndex; 
+    // return true if there is ':' before '#', '?', and '/'
+    return ((colonIndex >0) && (colonIndex<index));
+    
+  }
+  
+  /**
+   * Return true if the local path is an absolute path.
+   *
+   * @param systemId The path string
+   * @return true if the path is absolute
+   */
+  public static boolean isAbsolutePath(String systemId)
+  {
+    if(systemId == null)
+        return false;
+    final File file = new File(systemId);
+    return file.isAbsolute();
+    
+  }
+  
+   /**
+   * Return true if the local path is a Windows absolute path.
+   *
+   * @param systemId The path string
+   * @return true if the path is a Windows absolute path
+   */
+    private static boolean isWindowsAbsolutePath(String systemId)
+  {
+    if(!isAbsolutePath(systemId))
+      return false;
+    // On Windows, an absolute path starts with "[drive_letter]:\".
+    if (systemId.length() > 2 
+        && systemId.charAt(1) == ':'
+        && Character.isLetter(systemId.charAt(0))
+        && (systemId.charAt(2) == '\\' || systemId.charAt(2) == '/'))
+      return true;
+    else
+      return false;
+  }
+  
+  /**
+   * Replace spaces with "%20" and backslashes with forward slashes in 
+   * the input string to generate a well-formed URI string.
+   *
+   * @param str The input string
+   * @return The string after conversion
+   */
+  private static String replaceChars(String str)
+  {
+    StringBuffer buf = new StringBuffer(str);
+    int length = buf.length();
+    for (int i = 0; i < length; i++)
+    {
+      char currentChar = buf.charAt(i);
+      // Replace space with "%20"
+      if (currentChar == ' ')
+      {
+        buf.setCharAt(i, '%');
+        buf.insert(i+1, "20");
+        length = length + 2;
+        i = i + 2;
+      }
+      // Replace backslash with forward slash
+      else if (currentChar == '\\')
+      {
+        buf.setCharAt(i, '/');
+      }
+    }
+    
+    return buf.toString();
+  }
+  
+  /**
+   * Take a SystemID string and try to turn it into a good absolute URI.
+   *
+   * @param systemId A URI string, which may be absolute or relative.
+   *
+   * @return The resolved absolute URI
+   */
+  public static String getAbsoluteURI(String systemId)
+  {
+    String absoluteURI = systemId;
+    if (isAbsoluteURI(systemId))
+    {
+      // Only process the systemId if it starts with "file:".
+      if (systemId.startsWith("file:"))
+      {
+        String str = systemId.substring(5);
+        
+        // Resolve the absolute path if the systemId starts with "file:///"
+        // or "file:/". Don't do anything if it only starts with "file://".
+        if (str != null && str.startsWith("/"))
+        {
+          if (str.startsWith("///") || !str.startsWith("//"))
+          {
+            // A Windows path containing a drive letter can be relative.
+            // A Unix path starting with "file:/" is always absolute.
+            int secondColonIndex = systemId.indexOf(':', 5);
+            if (secondColonIndex > 0)
+            {
+              String localPath = systemId.substring(secondColonIndex-1);
+              try {
+                if (!isAbsolutePath(localPath))
+                  absoluteURI = systemId.substring(0, secondColonIndex-1) + 
+                                getAbsolutePathFromRelativePath(localPath);
+              }
+              catch (SecurityException se) {
+                return systemId;
+              }
+            }
+          }          
+        }
+        else
+        {
+          return getAbsoluteURIFromRelative(systemId.substring(5));
+        }
+                
+        return replaceChars(absoluteURI);
+      }
+      else
+        return systemId;
+    }
+    else
+      return getAbsoluteURIFromRelative(systemId);
+    
+  }
+
+
+  /**
+   * Take a SystemID string and try to turn it into a good absolute URI.
+   *
+   * @param urlString SystemID string
+   * @param base The URI string used as the base for resolving the systemID
+   *
+   * @return The resolved absolute URI
+   * @throws TransformerException thrown if the string can't be turned into a URI.
+   */
+  public static String getAbsoluteURI(String urlString, String base)
+          throws TransformerException
+  {    
+    if (base == null)
+      return getAbsoluteURI(urlString);
+    
+    String absoluteBase = getAbsoluteURI(base);
+    URI uri = null;
+    try 
+    {
+      URI baseURI = new URI(absoluteBase);
+      uri = new URI(baseURI, urlString);
+    }
+    catch (MalformedURIException mue)
+    {
+      throw new TransformerException(mue);
+    }
+    
+    return replaceChars(uri.toString());
+  }
+  
+}
diff --git a/src/main/java/org/apache/xml/utils/ThreadControllerWrapper.java b/src/main/java/org/apache/xml/utils/ThreadControllerWrapper.java
new file mode 100644
index 0000000..4a314ea
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/ThreadControllerWrapper.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ThreadControllerWrapper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * A utility class that wraps the ThreadController, which is used
+ * by IncrementalSAXSource for the incremental building of DTM.
+ */
+public class ThreadControllerWrapper
+{
+  
+  /** The ThreadController pool   */
+  private static ThreadController m_tpool = new ThreadController();
+  
+  public static Thread runThread(Runnable runnable, int priority)
+  {
+    return m_tpool.run(runnable, priority);
+  }
+  
+  public static void waitThread(Thread worker, Runnable task)
+    throws InterruptedException
+  {
+    m_tpool.waitThread(worker, task);
+  }
+  
+  /**
+   * Thread controller utility class for incremental SAX source. Must 
+   * be overriden with a derived class to support thread pooling.
+   *
+   * All thread-related stuff is in this class.
+   */
+  public static class ThreadController
+  {
+
+    /**
+     * Will get a thread from the pool, execute the task
+     *  and return the thread to the pool.
+     *
+     *  The return value is used only to wait for completion
+     *
+     *
+     * NEEDSDOC @param task
+     * @param priority if >0 the task will run with the given priority
+     *  ( doesn't seem to be used in xalan, since it's allways the default )
+     * @return  The thread that is running the task, can be used
+     *          to wait for completion
+     */
+    public Thread run(Runnable task, int priority)
+    {
+
+      Thread t = new Thread(task);
+
+      t.start();
+
+      //       if( priority > 0 )
+      //      t.setPriority( priority );
+      return t;
+    }
+
+    /**
+     *  Wait until the task is completed on the worker
+     *  thread.
+     *
+     * NEEDSDOC @param worker
+     * NEEDSDOC @param task
+     *
+     * @throws InterruptedException
+     */
+    public void waitThread(Thread worker, Runnable task)
+            throws InterruptedException
+    {
+
+      // This should wait until the transformThread is considered not alive.
+      worker.join();
+    }
+  }
+ 
+}
diff --git a/src/main/java/org/apache/xml/utils/TreeWalker.java b/src/main/java/org/apache/xml/utils/TreeWalker.java
new file mode 100644
index 0000000..2ebfe63
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/TreeWalker.java
@@ -0,0 +1,508 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: TreeWalker.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.io.File;
+
+import org.w3c.dom.Comment;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.LocatorImpl;
+
+/**
+ * This class does a pre-order walk of the DOM tree, calling a ContentHandler
+ * interface as it goes.
+ * @xsl.usage advanced
+ */
+
+public class TreeWalker
+{
+
+  /** Local reference to a ContentHandler          */
+  private ContentHandler m_contentHandler = null;
+
+  // ARGHH!!  JAXP Uses Xerces without setting the namespace processing to ON!
+  // DOM2Helper m_dh = new DOM2Helper();
+
+  /** DomHelper for this TreeWalker          */
+  protected DOMHelper m_dh;
+        
+        /** Locator object for this TreeWalker          */
+        private LocatorImpl m_locator = new LocatorImpl();
+
+  /**
+   * Get the ContentHandler used for the tree walk.
+   *
+   * @return the ContentHandler used for the tree walk
+   */
+  public ContentHandler getContentHandler()
+  {
+    return m_contentHandler;
+  }
+
+  /**
+   * Get the ContentHandler used for the tree walk.
+   *
+   * @return the ContentHandler used for the tree walk
+   */
+  public void setContentHandler(ContentHandler ch)
+  {
+    m_contentHandler = ch;
+  }
+        
+        /**
+   * Constructor.
+   * @param   contentHandler The implemention of the
+   * @param   systemId System identifier for the document.
+   * contentHandler operation (toXMLString, digest, ...)
+   */
+  public TreeWalker(ContentHandler contentHandler, DOMHelper dh, String systemId)
+  {
+    this.m_contentHandler = contentHandler;
+    m_contentHandler.setDocumentLocator(m_locator);
+    if (systemId != null)
+        m_locator.setSystemId(systemId);
+    else {
+        try {
+          // Bug see Bugzilla  26741
+          m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
+         }
+         catch (SecurityException se) {// user.dir not accessible from applet             
+         }
+    }
+    m_dh = dh;
+  }
+
+  /**
+   * Constructor.
+   * @param   contentHandler The implemention of the
+   * contentHandler operation (toXMLString, digest, ...)
+   */
+  public TreeWalker(ContentHandler contentHandler, DOMHelper dh)
+  {
+    this.m_contentHandler = contentHandler;
+    m_contentHandler.setDocumentLocator(m_locator);
+    try {
+       // Bug see Bugzilla  26741
+      m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
+    } 
+    catch (SecurityException se){// user.dir not accessible from applet      
+    }
+    m_dh = dh;
+  }
+  
+  /**
+   * Constructor.
+   * @param   contentHandler The implemention of the
+   * contentHandler operation (toXMLString, digest, ...)
+   */
+  public TreeWalker(ContentHandler contentHandler)
+  {
+    this.m_contentHandler = contentHandler;
+                if (m_contentHandler != null)
+                        m_contentHandler.setDocumentLocator(m_locator);
+                try {
+                   // Bug see Bugzilla  26741
+                  m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
+                } 
+                catch (SecurityException se){// user.dir not accessible from applet
+                  
+    }
+    m_dh = new DOM2Helper();
+  }
+
+  /**
+   * Perform a pre-order traversal non-recursive style.  
+   *
+   * Note that TreeWalker assumes that the subtree is intended to represent 
+   * a complete (though not necessarily well-formed) document and, during a 
+   * traversal, startDocument and endDocument will always be issued to the 
+   * SAX listener.
+   *  
+   * @param pos Node in the tree where to start traversal
+   *
+   * @throws TransformerException
+   */
+  public void traverse(Node pos) throws org.xml.sax.SAXException
+  {
+        this.m_contentHandler.startDocument();
+        
+        traverseFragment(pos);
+
+        this.m_contentHandler.endDocument();
+  }
+    
+  /**
+   * Perform a pre-order traversal non-recursive style.  
+   *
+   * In contrast to the traverse() method this method will not issue 
+   * startDocument() and endDocument() events to the SAX listener.
+   *  
+   * @param pos Node in the tree where to start traversal
+   *
+   * @throws TransformerException
+   */
+  public void traverseFragment(Node pos) throws org.xml.sax.SAXException
+  {
+    Node top = pos;
+
+    while (null != pos)
+    {
+      startNode(pos);
+
+      Node nextNode = pos.getFirstChild();
+
+      while (null == nextNode)
+      {
+        endNode(pos);
+
+        if (top.equals(pos))
+          break;
+
+        nextNode = pos.getNextSibling();
+
+        if (null == nextNode)
+        {
+          pos = pos.getParentNode();
+
+          if ((null == pos) || (top.equals(pos)))
+          {
+            if (null != pos)
+              endNode(pos);
+
+            nextNode = null;
+
+            break;
+          }
+        }
+      }
+
+      pos = nextNode;
+    }
+  }
+
+  /**
+   * Perform a pre-order traversal non-recursive style.
+
+   * Note that TreeWalker assumes that the subtree is intended to represent 
+   * a complete (though not necessarily well-formed) document and, during a 
+   * traversal, startDocument and endDocument will always be issued to the 
+   * SAX listener.
+   *
+   * @param pos Node in the tree where to start traversal
+   * @param top Node in the tree where to end traversal
+   *
+   * @throws TransformerException
+   */
+  public void traverse(Node pos, Node top) throws org.xml.sax.SAXException
+  {
+
+	this.m_contentHandler.startDocument();
+	
+    while (null != pos)
+    {
+      startNode(pos);
+
+      Node nextNode = pos.getFirstChild();
+
+      while (null == nextNode)
+      {
+        endNode(pos);
+
+        if ((null != top) && top.equals(pos))
+          break;
+
+        nextNode = pos.getNextSibling();
+
+        if (null == nextNode)
+        {
+          pos = pos.getParentNode();
+
+          if ((null == pos) || ((null != top) && top.equals(pos)))
+          {
+            nextNode = null;
+
+            break;
+          }
+        }
+      }
+
+      pos = nextNode;
+    }
+    this.m_contentHandler.endDocument();
+  }
+
+  /** Flag indicating whether following text to be processed is raw text          */
+  boolean nextIsRaw = false;
+  
+  /**
+   * Optimized dispatch of characters.
+   */
+  private final void dispatachChars(Node node)
+     throws org.xml.sax.SAXException
+  {
+    if(m_contentHandler instanceof org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)
+    {
+      ((org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)m_contentHandler).characters(node);
+    }
+    else
+    {
+      String data = ((Text) node).getData();
+      this.m_contentHandler.characters(data.toCharArray(), 0, data.length());
+    }
+  }
+
+  /**
+   * Start processing given node
+   *
+   *
+   * @param node Node to process
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  protected void startNode(Node node) throws org.xml.sax.SAXException
+  {
+
+    if (m_contentHandler instanceof NodeConsumer)
+    {
+      ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
+    }
+                
+                if (node instanceof Locator)
+                {
+                        Locator loc = (Locator)node;
+                        m_locator.setColumnNumber(loc.getColumnNumber());
+                        m_locator.setLineNumber(loc.getLineNumber());
+                        m_locator.setPublicId(loc.getPublicId());
+                        m_locator.setSystemId(loc.getSystemId());
+                }
+                else
+                {
+                        m_locator.setColumnNumber(0);
+      m_locator.setLineNumber(0);
+                }
+
+    switch (node.getNodeType())
+    {
+    case Node.COMMENT_NODE :
+    {
+      String data = ((Comment) node).getData();
+
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
+
+        lh.comment(data.toCharArray(), 0, data.length());
+      }
+    }
+    break;
+    case Node.DOCUMENT_FRAGMENT_NODE :
+
+      // ??;
+      break;
+    case Node.DOCUMENT_NODE :
+    
+      break;
+    case Node.ELEMENT_NODE :
+      NamedNodeMap atts = ((Element) node).getAttributes();
+      int nAttrs = atts.getLength();
+      // System.out.println("TreeWalker#startNode: "+node.getNodeName());
+
+      for (int i = 0; i < nAttrs; i++)
+      {
+        Node attr = atts.item(i);
+        String attrName = attr.getNodeName();
+
+        // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
+        if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
+        {
+          // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
+          int index;
+          // Use "" instead of null, as Xerces likes "" for the 
+          // name of the default namespace.  Fix attributed 
+          // to "Steven Murray" <smurray@ebt.com>.
+          String prefix = (index = attrName.indexOf(":")) < 0
+                          ? "" : attrName.substring(index + 1);
+
+          this.m_contentHandler.startPrefixMapping(prefix,
+                                                   attr.getNodeValue());
+        }
+        
+      }
+
+      // System.out.println("m_dh.getNamespaceOfNode(node): "+m_dh.getNamespaceOfNode(node));
+      // System.out.println("m_dh.getLocalNameOfNode(node): "+m_dh.getLocalNameOfNode(node));
+      String ns = m_dh.getNamespaceOfNode(node);
+      if(null == ns)
+        ns = "";
+      this.m_contentHandler.startElement(ns,
+                                         m_dh.getLocalNameOfNode(node),
+                                         node.getNodeName(),
+                                         new AttList(atts, m_dh));
+      break;
+    case Node.PROCESSING_INSTRUCTION_NODE :
+    {
+      ProcessingInstruction pi = (ProcessingInstruction) node;
+      String name = pi.getNodeName();
+
+      // String data = pi.getData();
+      if (name.equals("xslt-next-is-raw"))
+      {
+        nextIsRaw = true;
+      }
+      else
+      {
+        this.m_contentHandler.processingInstruction(pi.getNodeName(),
+                                                    pi.getData());
+      }
+    }
+    break;
+    case Node.CDATA_SECTION_NODE :
+    {
+      boolean isLexH = (m_contentHandler instanceof LexicalHandler);
+      LexicalHandler lh = isLexH
+                          ? ((LexicalHandler) this.m_contentHandler) : null;
+
+      if (isLexH)
+      {
+        lh.startCDATA();
+      }
+      
+      dispatachChars(node);
+
+      {
+        if (isLexH)
+        {
+          lh.endCDATA();
+        }
+      }
+    }
+    break;
+    case Node.TEXT_NODE :
+    {
+      //String data = ((Text) node).getData();
+
+      if (nextIsRaw)
+      {
+        nextIsRaw = false;
+
+        m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
+        dispatachChars(node);
+        m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
+      }
+      else
+      {
+        dispatachChars(node);
+      }
+    }
+    break;
+    case Node.ENTITY_REFERENCE_NODE :
+    {
+      EntityReference eref = (EntityReference) node;
+
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        ((LexicalHandler) this.m_contentHandler).startEntity(
+          eref.getNodeName());
+      }
+      else
+      {
+
+        // warning("Can not output entity to a pure SAX ContentHandler");
+      }
+    }
+    break;
+    default :
+    }
+  }
+
+  /**
+   * End processing of given node 
+   *
+   *
+   * @param node Node we just finished processing
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  protected void endNode(Node node) throws org.xml.sax.SAXException
+  {
+
+    switch (node.getNodeType())
+    {
+    case Node.DOCUMENT_NODE :
+      break;
+      
+    case Node.ELEMENT_NODE :
+      String ns = m_dh.getNamespaceOfNode(node);
+      if(null == ns)
+        ns = "";
+      this.m_contentHandler.endElement(ns,
+                                         m_dh.getLocalNameOfNode(node),
+                                         node.getNodeName());
+
+      NamedNodeMap atts = ((Element) node).getAttributes();
+      int nAttrs = atts.getLength();
+
+      for (int i = 0; i < nAttrs; i++)
+      {
+        Node attr = atts.item(i);
+        String attrName = attr.getNodeName();
+
+        if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
+        {
+          int index;
+          // Use "" instead of null, as Xerces likes "" for the 
+          // name of the default namespace.  Fix attributed 
+          // to "Steven Murray" <smurray@ebt.com>.
+          String prefix = (index = attrName.indexOf(":")) < 0
+                          ? "" : attrName.substring(index + 1);
+
+          this.m_contentHandler.endPrefixMapping(prefix);
+        }
+      }
+      break;
+    case Node.CDATA_SECTION_NODE :
+      break;
+    case Node.ENTITY_REFERENCE_NODE :
+    {
+      EntityReference eref = (EntityReference) node;
+
+      if (m_contentHandler instanceof LexicalHandler)
+      {
+        LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
+
+        lh.endEntity(eref.getNodeName());
+      }
+    }
+    break;
+    default :
+    }
+  }
+}  //TreeWalker
+
diff --git a/src/main/java/org/apache/xml/utils/URI.java b/src/main/java/org/apache/xml/utils/URI.java
new file mode 100644
index 0000000..4bee2d1
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/URI.java
@@ -0,0 +1,1673 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: URI.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+/**
+ * A class to represent a Uniform Resource Identifier (URI). This class
+ * is designed to handle the parsing of URIs and provide access to
+ * the various components (scheme, host, port, userinfo, path, query
+ * string and fragment) that may constitute a URI.
+ * <p>
+ * Parsing of a URI specification is done according to the URI
+ * syntax described in RFC 2396
+ * <http://www.ietf.org/rfc/rfc2396.txt?number=2396>. Every URI consists
+ * of a scheme, followed by a colon (':'), followed by a scheme-specific
+ * part. For URIs that follow the "generic URI" syntax, the scheme-
+ * specific part begins with two slashes ("//") and may be followed
+ * by an authority segment (comprised of user information, host, and
+ * port), path segment, query segment and fragment. Note that RFC 2396
+ * no longer specifies the use of the parameters segment and excludes
+ * the "user:password" syntax as part of the authority segment. If
+ * "user:password" appears in a URI, the entire user/password string
+ * is stored as userinfo.
+ * <p>
+ * For URIs that do not follow the "generic URI" syntax (e.g. mailto),
+ * the entire scheme-specific part is treated as the "path" portion
+ * of the URI.
+ * <p>
+ * Note that, unlike the java.net.URL class, this class does not provide
+ * any built-in network access functionality nor does it provide any
+ * scheme-specific functionality (for example, it does not know a
+ * default port for a specific scheme). Rather, it only knows the
+ * grammar and basic set of operations that can be applied to a URI.
+ *
+ *
+ */
+public class URI implements Serializable
+{
+    static final long serialVersionUID = 7096266377907081897L;
+
+  /**
+   * MalformedURIExceptions are thrown in the process of building a URI
+   * or setting fields on a URI when an operation would result in an
+   * invalid URI specification.
+   *
+   */
+  public static class MalformedURIException extends IOException
+  {
+
+    /**
+     * Constructs a <code>MalformedURIException</code> with no specified
+     * detail message.
+     */
+    public MalformedURIException()
+    {
+      super();
+    }
+
+    /**
+     * Constructs a <code>MalformedURIException</code> with the
+     * specified detail message.
+     *
+     * @param p_msg the detail message.
+     */
+    public MalformedURIException(String p_msg)
+    {
+      super(p_msg);
+    }
+  }
+
+  /** reserved characters */
+  private static final String RESERVED_CHARACTERS = ";/?:@&=+$,";
+
+  /**
+   * URI punctuation mark characters - these, combined with
+   *   alphanumerics, constitute the "unreserved" characters 
+   */
+  private static final String MARK_CHARACTERS = "-_.!~*'() ";
+
+  /** scheme can be composed of alphanumerics and these characters */
+  private static final String SCHEME_CHARACTERS = "+-.";
+
+  /**
+   * userinfo can be composed of unreserved, escaped and these
+   *   characters 
+   */
+  private static final String USERINFO_CHARACTERS = ";:&=+$,";
+
+  /** Stores the scheme (usually the protocol) for this URI.
+   *  @serial */
+  private String m_scheme = null;
+
+  /** If specified, stores the userinfo for this URI; otherwise null.
+   *  @serial */
+  private String m_userinfo = null;
+
+  /** If specified, stores the host for this URI; otherwise null.
+   *  @serial */
+  private String m_host = null;
+
+  /** If specified, stores the port for this URI; otherwise -1.
+   *  @serial */
+  private int m_port = -1;
+
+  /** If specified, stores the path for this URI; otherwise null.
+   *  @serial */
+  private String m_path = null;
+
+  /**
+   * If specified, stores the query string for this URI; otherwise
+   *   null. 
+   * @serial 
+   */
+  private String m_queryString = null;
+
+  /** If specified, stores the fragment for this URI; otherwise null.
+   *  @serial */
+  private String m_fragment = null;
+
+  /** Indicate whether in DEBUG mode          */
+  private static boolean DEBUG = false;
+
+  /**
+   * Construct a new and uninitialized URI.
+   */
+  public URI(){}
+
+  /**
+   * Construct a new URI from another URI. All fields for this URI are
+   * set equal to the fields of the URI passed in.
+   *
+   * @param p_other the URI to copy (cannot be null)
+   */
+  public URI(URI p_other)
+  {
+    initialize(p_other);
+  }
+
+  /**
+   * Construct a new URI from a URI specification string. If the
+   * specification follows the "generic URI" syntax, (two slashes
+   * following the first colon), the specification will be parsed
+   * accordingly - setting the scheme, userinfo, host,port, path, query
+   * string and fragment fields as necessary. If the specification does
+   * not follow the "generic URI" syntax, the specification is parsed
+   * into a scheme and scheme-specific part (stored as the path) only.
+   *
+   * @param p_uriSpec the URI specification string (cannot be null or
+   *                  empty)
+   *
+   * @throws MalformedURIException if p_uriSpec violates any syntax
+   *                                   rules
+   */
+  public URI(String p_uriSpec) throws MalformedURIException
+  {
+    this((URI) null, p_uriSpec);
+  }
+
+  /**
+   * Construct a new URI from a base URI and a URI specification string.
+   * The URI specification string may be a relative URI.
+   *
+   * @param p_base the base URI (cannot be null if p_uriSpec is null or
+   *               empty)
+   * @param p_uriSpec the URI specification string (cannot be null or
+   *                  empty if p_base is null)
+   *
+   * @throws MalformedURIException if p_uriSpec violates any syntax
+   *                                  rules
+   */
+  public URI(URI p_base, String p_uriSpec) throws MalformedURIException
+  {
+    initialize(p_base, p_uriSpec);
+  }
+
+  /**
+   * Construct a new URI that does not follow the generic URI syntax.
+   * Only the scheme and scheme-specific part (stored as the path) are
+   * initialized.
+   *
+   * @param p_scheme the URI scheme (cannot be null or empty)
+   * @param p_schemeSpecificPart the scheme-specific part (cannot be
+   *                             null or empty)
+   *
+   * @throws MalformedURIException if p_scheme violates any
+   *                                  syntax rules
+   */
+  public URI(String p_scheme, String p_schemeSpecificPart)
+          throws MalformedURIException
+  {
+
+    if (p_scheme == null || p_scheme.trim().length() == 0)
+    {
+      throw new MalformedURIException(
+        "Cannot construct URI with null/empty scheme!");
+    }
+
+    if (p_schemeSpecificPart == null
+            || p_schemeSpecificPart.trim().length() == 0)
+    {
+      throw new MalformedURIException(
+        "Cannot construct URI with null/empty scheme-specific part!");
+    }
+
+    setScheme(p_scheme);
+    setPath(p_schemeSpecificPart);
+  }
+
+  /**
+   * Construct a new URI that follows the generic URI syntax from its
+   * component parts. Each component is validated for syntax and some
+   * basic semantic checks are performed as well.  See the individual
+   * setter methods for specifics.
+   *
+   * @param p_scheme the URI scheme (cannot be null or empty)
+   * @param p_host the hostname or IPv4 address for the URI
+   * @param p_path the URI path - if the path contains '?' or '#',
+   *               then the query string and/or fragment will be
+   *               set from the path; however, if the query and
+   *               fragment are specified both in the path and as
+   *               separate parameters, an exception is thrown
+   * @param p_queryString the URI query string (cannot be specified
+   *                      if path is null)
+   * @param p_fragment the URI fragment (cannot be specified if path
+   *                   is null)
+   *
+   * @throws MalformedURIException if any of the parameters violates
+   *                                  syntax rules or semantic rules
+   */
+  public URI(String p_scheme, String p_host, String p_path, String p_queryString, String p_fragment)
+          throws MalformedURIException
+  {
+    this(p_scheme, null, p_host, -1, p_path, p_queryString, p_fragment);
+  }
+
+  /**
+   * Construct a new URI that follows the generic URI syntax from its
+   * component parts. Each component is validated for syntax and some
+   * basic semantic checks are performed as well.  See the individual
+   * setter methods for specifics.
+   *
+   * @param p_scheme the URI scheme (cannot be null or empty)
+   * @param p_userinfo the URI userinfo (cannot be specified if host
+   *                   is null)
+   * @param p_host the hostname or IPv4 address for the URI
+   * @param p_port the URI port (may be -1 for "unspecified"; cannot
+   *               be specified if host is null)
+   * @param p_path the URI path - if the path contains '?' or '#',
+   *               then the query string and/or fragment will be
+   *               set from the path; however, if the query and
+   *               fragment are specified both in the path and as
+   *               separate parameters, an exception is thrown
+   * @param p_queryString the URI query string (cannot be specified
+   *                      if path is null)
+   * @param p_fragment the URI fragment (cannot be specified if path
+   *                   is null)
+   *
+   * @throws MalformedURIException if any of the parameters violates
+   *                                  syntax rules or semantic rules
+   */
+  public URI(String p_scheme, String p_userinfo, String p_host, int p_port, String p_path, String p_queryString, String p_fragment)
+          throws MalformedURIException
+  {
+
+    if (p_scheme == null || p_scheme.trim().length() == 0)
+    {
+      throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_SCHEME_REQUIRED, null)); //"Scheme is required!");
+    }
+
+    if (p_host == null)
+    {
+      if (p_userinfo != null)
+      {
+        throw new MalformedURIException(
+          XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_USERINFO_IF_NO_HOST, null)); //"Userinfo may not be specified if host is not specified!");
+      }
+
+      if (p_port != -1)
+      {
+        throw new MalformedURIException(
+          XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_PORT_IF_NO_HOST, null)); //"Port may not be specified if host is not specified!");
+      }
+    }
+
+    if (p_path != null)
+    {
+      if (p_path.indexOf('?') != -1 && p_queryString != null)
+      {
+        throw new MalformedURIException(
+          XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_QUERY_STRING_IN_PATH, null)); //"Query string cannot be specified in path and query string!");
+      }
+
+      if (p_path.indexOf('#') != -1 && p_fragment != null)
+      {
+        throw new MalformedURIException(
+          XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_FRAGMENT_STRING_IN_PATH, null)); //"Fragment cannot be specified in both the path and fragment!");
+      }
+    }
+
+    setScheme(p_scheme);
+    setHost(p_host);
+    setPort(p_port);
+    setUserinfo(p_userinfo);
+    setPath(p_path);
+    setQueryString(p_queryString);
+    setFragment(p_fragment);
+  }
+
+  /**
+   * Initialize all fields of this URI from another URI.
+   *
+   * @param p_other the URI to copy (cannot be null)
+   */
+  private void initialize(URI p_other)
+  {
+
+    m_scheme = p_other.getScheme();
+    m_userinfo = p_other.getUserinfo();
+    m_host = p_other.getHost();
+    m_port = p_other.getPort();
+    m_path = p_other.getPath();
+    m_queryString = p_other.getQueryString();
+    m_fragment = p_other.getFragment();
+  }
+
+  /**
+   * Initializes this URI from a base URI and a URI specification string.
+   * See RFC 2396 Section 4 and Appendix B for specifications on parsing
+   * the URI and Section 5 for specifications on resolving relative URIs
+   * and relative paths.
+   *
+   * @param p_base the base URI (may be null if p_uriSpec is an absolute
+   *               URI)
+   * @param p_uriSpec the URI spec string which may be an absolute or
+   *                  relative URI (can only be null/empty if p_base
+   *                  is not null)
+   *
+   * @throws MalformedURIException if p_base is null and p_uriSpec
+   *                                  is not an absolute URI or if
+   *                                  p_uriSpec violates syntax rules
+   */
+  private void initialize(URI p_base, String p_uriSpec)
+          throws MalformedURIException
+  {
+
+    if (p_base == null
+            && (p_uriSpec == null || p_uriSpec.trim().length() == 0))
+    {
+      throw new MalformedURIException(
+        XMLMessages.createXMLMessage(XMLErrorResources.ER_CANNOT_INIT_URI_EMPTY_PARMS, null)); //"Cannot initialize URI with empty parameters.");
+    }
+
+    // just make a copy of the base if spec is empty
+    if (p_uriSpec == null || p_uriSpec.trim().length() == 0)
+    {
+      initialize(p_base);
+
+      return;
+    }
+
+    String uriSpec = p_uriSpec.trim();
+    int uriSpecLen = uriSpec.length();
+    int index = 0;
+
+    // check for scheme
+    int colonIndex = uriSpec.indexOf(':');
+    if (colonIndex < 0)
+    {
+      if (p_base == null)
+      {
+        throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_SCHEME_IN_URI, new Object[]{uriSpec})); //"No scheme found in URI: "+uriSpec);
+      }
+    }
+    else
+    {
+      initializeScheme(uriSpec);
+      uriSpec = uriSpec.substring(colonIndex+1);
+      // This is a fix for XALANJ-2059.
+      if(m_scheme != null && p_base != null)
+      {	  	
+        // a) If <uriSpec> starts with a slash (/), it means <uriSpec> is absolute 
+        //    and p_base can be ignored.
+        //    For example,
+        //    uriSpec = file:/myDIR/myXSLFile.xsl
+        //    p_base = file:/myWork/
+        //
+        //    Here, uriSpec has absolute path after scheme file and :
+        //    Hence p_base can be ignored.
+        // 
+        // b) Similarily, according to RFC 2396, uri is resolved for <uriSpec> relative to <p_base>
+        //    if scheme in <uriSpec> is same as scheme in <p_base>, else p_base can be ignored.
+        // 
+        // c) if <p_base> is not hierarchical, it can be ignored.
+        //
+        if(uriSpec.startsWith("/") || !m_scheme.equals(p_base.m_scheme) || !p_base.getSchemeSpecificPart().startsWith("/"))
+        {
+          p_base = null;
+        }
+      }
+      // Fix for XALANJ-2059  
+      uriSpecLen = uriSpec.length();
+    }
+
+    // two slashes means generic URI syntax, so we get the authority
+    if (uriSpec.startsWith("//"))
+    {
+      index += 2;
+
+      int startPos = index;
+
+      // get authority - everything up to path, query or fragment
+      char testChar = '\0';
+
+      while (index < uriSpecLen)
+      {
+        testChar = uriSpec.charAt(index);
+
+        if (testChar == '/' || testChar == '?' || testChar == '#')
+        {
+          break;
+        }
+
+        index++;
+      }
+
+      // if we found authority, parse it out, otherwise we set the
+      // host to empty string
+      if (index > startPos)
+      {
+        initializeAuthority(uriSpec.substring(startPos, index));
+      }
+      else
+      {
+        m_host = "";
+      }
+    }
+
+    initializePath(uriSpec.substring(index));
+
+    // Resolve relative URI to base URI - see RFC 2396 Section 5.2
+    // In some cases, it might make more sense to throw an exception
+    // (when scheme is specified is the string spec and the base URI
+    // is also specified, for example), but we're just following the
+    // RFC specifications 
+    if (p_base != null)
+    {
+
+      // check to see if this is the current doc - RFC 2396 5.2 #2
+      // note that this is slightly different from the RFC spec in that
+      // we don't include the check for query string being null
+      // - this handles cases where the urispec is just a query
+      // string or a fragment (e.g. "?y" or "#s") - 
+      // see <http://www.ics.uci.edu/~fielding/url/test1.html> which
+      // identified this as a bug in the RFC
+      if (m_path.length() == 0 && m_scheme == null && m_host == null)
+      {
+        m_scheme = p_base.getScheme();
+        m_userinfo = p_base.getUserinfo();
+        m_host = p_base.getHost();
+        m_port = p_base.getPort();
+        m_path = p_base.getPath();
+
+        if (m_queryString == null)
+        {
+          m_queryString = p_base.getQueryString();
+        }
+
+        return;
+      }
+
+      // check for scheme - RFC 2396 5.2 #3
+      // if we found a scheme, it means absolute URI, so we're done
+      if (m_scheme == null)
+      {
+        m_scheme = p_base.getScheme();
+      }
+
+      // check for authority - RFC 2396 5.2 #4
+      // if we found a host, then we've got a network path, so we're done
+      if (m_host == null)
+      {
+        m_userinfo = p_base.getUserinfo();
+        m_host = p_base.getHost();
+        m_port = p_base.getPort();
+      }
+      else
+      {
+        return;
+      }
+
+      // check for absolute path - RFC 2396 5.2 #5
+      if (m_path.length() > 0 && m_path.startsWith("/"))
+      {
+        return;
+      }
+
+      // if we get to this point, we need to resolve relative path
+      // RFC 2396 5.2 #6
+      String path = new String();
+      String basePath = p_base.getPath();
+
+      // 6a - get all but the last segment of the base URI path
+      if (basePath != null)
+      {
+        int lastSlash = basePath.lastIndexOf('/');
+
+        if (lastSlash != -1)
+        {
+          path = basePath.substring(0, lastSlash + 1);
+        }
+      }
+
+      // 6b - append the relative URI path
+      path = path.concat(m_path);
+
+      // 6c - remove all "./" where "." is a complete path segment
+      index = -1;
+
+      while ((index = path.indexOf("/./")) != -1)
+      {
+        path = path.substring(0, index + 1).concat(path.substring(index + 3));
+      }
+
+      // 6d - remove "." if path ends with "." as a complete path segment
+      if (path.endsWith("/."))
+      {
+        path = path.substring(0, path.length() - 1);
+      }
+
+      // 6e - remove all "<segment>/../" where "<segment>" is a complete 
+      // path segment not equal to ".."
+      index = -1;
+
+      int segIndex = -1;
+      String tempString = null;
+
+      while ((index = path.indexOf("/../")) > 0)
+      {
+        tempString = path.substring(0, path.indexOf("/../"));
+        segIndex = tempString.lastIndexOf('/');
+
+        if (segIndex != -1)
+        {
+          if (!tempString.substring(segIndex++).equals(".."))
+          {
+            path = path.substring(0, segIndex).concat(path.substring(index
+                    + 4));
+          }
+        }
+      }
+
+      // 6f - remove ending "<segment>/.." where "<segment>" is a 
+      // complete path segment
+      if (path.endsWith("/.."))
+      {
+        tempString = path.substring(0, path.length() - 3);
+        segIndex = tempString.lastIndexOf('/');
+
+        if (segIndex != -1)
+        {
+          path = path.substring(0, segIndex + 1);
+        }
+      }
+
+      m_path = path;
+    }
+  }
+
+  /**
+   * Initialize the scheme for this URI from a URI string spec.
+   *
+   * @param p_uriSpec the URI specification (cannot be null)
+   *
+   * @throws MalformedURIException if URI does not have a conformant
+   *                                  scheme
+   */
+  private void initializeScheme(String p_uriSpec) throws MalformedURIException
+  {
+
+    int uriSpecLen = p_uriSpec.length();
+    int index = 0;
+    String scheme = null;
+    char testChar = '\0';
+
+    while (index < uriSpecLen)
+    {
+      testChar = p_uriSpec.charAt(index);
+
+      if (testChar == ':' || testChar == '/' || testChar == '?'
+              || testChar == '#')
+      {
+        break;
+      }
+
+      index++;
+    }
+
+    scheme = p_uriSpec.substring(0, index);
+
+    if (scheme.length() == 0)
+    {
+      throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_SCHEME_INURI, null)); //"No scheme found in URI.");
+    }
+    else
+    {
+      setScheme(scheme);
+    }
+  }
+
+  /**
+   * Initialize the authority (userinfo, host and port) for this
+   * URI from a URI string spec.
+   *
+   * @param p_uriSpec the URI specification (cannot be null)
+   *
+   * @throws MalformedURIException if p_uriSpec violates syntax rules
+   */
+  private void initializeAuthority(String p_uriSpec)
+          throws MalformedURIException
+  {
+
+    int index = 0;
+    int start = 0;
+    int end = p_uriSpec.length();
+    char testChar = '\0';
+    String userinfo = null;
+
+    // userinfo is everything up @
+    if (p_uriSpec.indexOf('@', start) != -1)
+    {
+      while (index < end)
+      {
+        testChar = p_uriSpec.charAt(index);
+
+        if (testChar == '@')
+        {
+          break;
+        }
+
+        index++;
+      }
+
+      userinfo = p_uriSpec.substring(start, index);
+
+      index++;
+    }
+
+    // host is everything up to ':'
+    String host = null;
+
+    start = index;
+
+    while (index < end)
+    {
+      testChar = p_uriSpec.charAt(index);
+
+      if (testChar == ':')
+      {
+        break;
+      }
+
+      index++;
+    }
+
+    host = p_uriSpec.substring(start, index);
+
+    int port = -1;
+
+    if (host.length() > 0)
+    {
+
+      // port
+      if (testChar == ':')
+      {
+        index++;
+
+        start = index;
+
+        while (index < end)
+        {
+          index++;
+        }
+
+        String portStr = p_uriSpec.substring(start, index);
+
+        if (portStr.length() > 0)
+        {
+          for (int i = 0; i < portStr.length(); i++)
+          {
+            if (!isDigit(portStr.charAt(i)))
+            {
+              throw new MalformedURIException(
+                portStr + " is invalid. Port should only contain digits!");
+            }
+          }
+
+          try
+          {
+            port = Integer.parseInt(portStr);
+          }
+          catch (NumberFormatException nfe)
+          {
+
+            // can't happen
+          }
+        }
+      }
+    }
+
+    setHost(host);
+    setPort(port);
+    setUserinfo(userinfo);
+  }
+
+  /**
+   * Initialize the path for this URI from a URI string spec.
+   *
+   * @param p_uriSpec the URI specification (cannot be null)
+   *
+   * @throws MalformedURIException if p_uriSpec violates syntax rules
+   */
+  private void initializePath(String p_uriSpec) throws MalformedURIException
+  {
+
+    if (p_uriSpec == null)
+    {
+      throw new MalformedURIException(
+        "Cannot initialize path from null string!");
+    }
+
+    int index = 0;
+    int start = 0;
+    int end = p_uriSpec.length();
+    char testChar = '\0';
+
+    // path - everything up to query string or fragment
+    while (index < end)
+    {
+      testChar = p_uriSpec.charAt(index);
+
+      if (testChar == '?' || testChar == '#')
+      {
+        break;
+      }
+
+      // check for valid escape sequence
+      if (testChar == '%')
+      {
+        if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1))
+                ||!isHex(p_uriSpec.charAt(index + 2)))
+        {
+          throw new MalformedURIException(
+            XMLMessages.createXMLMessage(XMLErrorResources.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE, null)); //"Path contains invalid escape sequence!");
+        }
+      }
+      else if (!isReservedCharacter(testChar)
+               &&!isUnreservedCharacter(testChar))
+      {
+        if ('\\' != testChar)
+          throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_PATH_INVALID_CHAR, new Object[]{String.valueOf(testChar)})); //"Path contains invalid character: "
+                                          //+ testChar);
+      }
+
+      index++;
+    }
+
+    m_path = p_uriSpec.substring(start, index);
+
+    // query - starts with ? and up to fragment or end
+    if (testChar == '?')
+    {
+      index++;
+
+      start = index;
+
+      while (index < end)
+      {
+        testChar = p_uriSpec.charAt(index);
+
+        if (testChar == '#')
+        {
+          break;
+        }
+
+        if (testChar == '%')
+        {
+          if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1))
+                  ||!isHex(p_uriSpec.charAt(index + 2)))
+          {
+            throw new MalformedURIException(
+              "Query string contains invalid escape sequence!");
+          }
+        }
+        else if (!isReservedCharacter(testChar)
+                 &&!isUnreservedCharacter(testChar))
+        {
+          throw new MalformedURIException(
+            "Query string contains invalid character:" + testChar);
+        }
+
+        index++;
+      }
+
+      m_queryString = p_uriSpec.substring(start, index);
+    }
+
+    // fragment - starts with #
+    if (testChar == '#')
+    {
+      index++;
+
+      start = index;
+
+      while (index < end)
+      {
+        testChar = p_uriSpec.charAt(index);
+
+        if (testChar == '%')
+        {
+          if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1))
+                  ||!isHex(p_uriSpec.charAt(index + 2)))
+          {
+            throw new MalformedURIException(
+              "Fragment contains invalid escape sequence!");
+          }
+        }
+        else if (!isReservedCharacter(testChar)
+                 &&!isUnreservedCharacter(testChar))
+        {
+          throw new MalformedURIException(
+            "Fragment contains invalid character:" + testChar);
+        }
+
+        index++;
+      }
+
+      m_fragment = p_uriSpec.substring(start, index);
+    }
+  }
+
+  /**
+   * Get the scheme for this URI.
+   *
+   * @return the scheme for this URI
+   */
+  public String getScheme()
+  {
+    return m_scheme;
+  }
+
+  /**
+   * Get the scheme-specific part for this URI (everything following the
+   * scheme and the first colon). See RFC 2396 Section 5.2 for spec.
+   *
+   * @return the scheme-specific part for this URI
+   */
+  public String getSchemeSpecificPart()
+  {
+
+    StringBuffer schemespec = new StringBuffer();
+
+    if (m_userinfo != null || m_host != null || m_port != -1)
+    {
+      schemespec.append("//");
+    }
+
+    if (m_userinfo != null)
+    {
+      schemespec.append(m_userinfo);
+      schemespec.append('@');
+    }
+
+    if (m_host != null)
+    {
+      schemespec.append(m_host);
+    }
+
+    if (m_port != -1)
+    {
+      schemespec.append(':');
+      schemespec.append(m_port);
+    }
+
+    if (m_path != null)
+    {
+      schemespec.append((m_path));
+    }
+
+    if (m_queryString != null)
+    {
+      schemespec.append('?');
+      schemespec.append(m_queryString);
+    }
+
+    if (m_fragment != null)
+    {
+      schemespec.append('#');
+      schemespec.append(m_fragment);
+    }
+
+    return schemespec.toString();
+  }
+
+  /**
+   * Get the userinfo for this URI.
+   *
+   * @return the userinfo for this URI (null if not specified).
+   */
+  public String getUserinfo()
+  {
+    return m_userinfo;
+  }
+
+  /**
+   * Get the host for this URI.
+   *
+   * @return the host for this URI (null if not specified).
+   */
+  public String getHost()
+  {
+    return m_host;
+  }
+
+  /**
+   * Get the port for this URI.
+   *
+   * @return the port for this URI (-1 if not specified).
+   */
+  public int getPort()
+  {
+    return m_port;
+  }
+
+  /**
+   * Get the path for this URI (optionally with the query string and
+   * fragment).
+   *
+   * @param p_includeQueryString if true (and query string is not null),
+   *                             then a "?" followed by the query string
+   *                             will be appended
+   * @param p_includeFragment if true (and fragment is not null),
+   *                             then a "#" followed by the fragment
+   *                             will be appended
+   *
+   * @return the path for this URI possibly including the query string
+   *         and fragment
+   */
+  public String getPath(boolean p_includeQueryString,
+                        boolean p_includeFragment)
+  {
+
+    StringBuffer pathString = new StringBuffer(m_path);
+
+    if (p_includeQueryString && m_queryString != null)
+    {
+      pathString.append('?');
+      pathString.append(m_queryString);
+    }
+
+    if (p_includeFragment && m_fragment != null)
+    {
+      pathString.append('#');
+      pathString.append(m_fragment);
+    }
+
+    return pathString.toString();
+  }
+
+  /**
+   * Get the path for this URI. Note that the value returned is the path
+   * only and does not include the query string or fragment.
+   *
+   * @return the path for this URI.
+   */
+  public String getPath()
+  {
+    return m_path;
+  }
+
+  /**
+   * Get the query string for this URI.
+   *
+   * @return the query string for this URI. Null is returned if there
+   *         was no "?" in the URI spec, empty string if there was a
+   *         "?" but no query string following it.
+   */
+  public String getQueryString()
+  {
+    return m_queryString;
+  }
+
+  /**
+   * Get the fragment for this URI.
+   *
+   * @return the fragment for this URI. Null is returned if there
+   *         was no "#" in the URI spec, empty string if there was a
+   *         "#" but no fragment following it.
+   */
+  public String getFragment()
+  {
+    return m_fragment;
+  }
+
+  /**
+   * Set the scheme for this URI. The scheme is converted to lowercase
+   * before it is set.
+   *
+   * @param p_scheme the scheme for this URI (cannot be null)
+   *
+   * @throws MalformedURIException if p_scheme is not a conformant
+   *                                  scheme name
+   */
+  public void setScheme(String p_scheme) throws MalformedURIException
+  {
+
+    if (p_scheme == null)
+    {
+      throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_SCHEME_FROM_NULL_STRING, null)); //"Cannot set scheme from null string!");
+    }
+
+    if (!isConformantSchemeName(p_scheme))
+    {
+      throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_SCHEME_NOT_CONFORMANT, null)); //"The scheme is not conformant.");
+    }
+
+    m_scheme = p_scheme.toLowerCase();
+  }
+
+  /**
+   * Set the userinfo for this URI. If a non-null value is passed in and
+   * the host value is null, then an exception is thrown.
+   *
+   * @param p_userinfo the userinfo for this URI
+   *
+   * @throws MalformedURIException if p_userinfo contains invalid
+   *                                  characters
+   */
+  public void setUserinfo(String p_userinfo) throws MalformedURIException
+  {
+
+    if (p_userinfo == null)
+    {
+      m_userinfo = null;
+    }
+    else
+    {
+      if (m_host == null)
+      {
+        throw new MalformedURIException(
+          "Userinfo cannot be set when host is null!");
+      }
+
+      // userinfo can contain alphanumerics, mark characters, escaped
+      // and ';',':','&','=','+','$',','
+      int index = 0;
+      int end = p_userinfo.length();
+      char testChar = '\0';
+
+      while (index < end)
+      {
+        testChar = p_userinfo.charAt(index);
+
+        if (testChar == '%')
+        {
+          if (index + 2 >= end ||!isHex(p_userinfo.charAt(index + 1))
+                  ||!isHex(p_userinfo.charAt(index + 2)))
+          {
+            throw new MalformedURIException(
+              "Userinfo contains invalid escape sequence!");
+          }
+        }
+        else if (!isUnreservedCharacter(testChar)
+                 && USERINFO_CHARACTERS.indexOf(testChar) == -1)
+        {
+          throw new MalformedURIException(
+            "Userinfo contains invalid character:" + testChar);
+        }
+
+        index++;
+      }
+    }
+
+    m_userinfo = p_userinfo;
+  }
+
+  /**
+   * Set the host for this URI. If null is passed in, the userinfo
+   * field is also set to null and the port is set to -1.
+   *
+   * @param p_host the host for this URI
+   *
+   * @throws MalformedURIException if p_host is not a valid IP
+   *                                  address or DNS hostname.
+   */
+  public void setHost(String p_host) throws MalformedURIException
+  {
+
+    if (p_host == null || p_host.trim().length() == 0)
+    {
+      m_host = p_host;
+      m_userinfo = null;
+      m_port = -1;
+    }
+    else if (!isWellFormedAddress(p_host))
+    {
+      throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_HOST_ADDRESS_NOT_WELLFORMED, null)); //"Host is not a well formed address!");
+    }
+
+    m_host = p_host;
+  }
+
+  /**
+   * Set the port for this URI. -1 is used to indicate that the port is
+   * not specified, otherwise valid port numbers are  between 0 and 65535.
+   * If a valid port number is passed in and the host field is null,
+   * an exception is thrown.
+   *
+   * @param p_port the port number for this URI
+   *
+   * @throws MalformedURIException if p_port is not -1 and not a
+   *                                  valid port number
+   */
+  public void setPort(int p_port) throws MalformedURIException
+  {
+
+    if (p_port >= 0 && p_port <= 65535)
+    {
+      if (m_host == null)
+      {
+        throw new MalformedURIException(
+          XMLMessages.createXMLMessage(XMLErrorResources.ER_PORT_WHEN_HOST_NULL, null)); //"Port cannot be set when host is null!");
+      }
+    }
+    else if (p_port != -1)
+    {
+      throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_INVALID_PORT, null)); //"Invalid port number!");
+    }
+
+    m_port = p_port;
+  }
+
+  /**
+   * Set the path for this URI. If the supplied path is null, then the
+   * query string and fragment are set to null as well. If the supplied
+   * path includes a query string and/or fragment, these fields will be
+   * parsed and set as well. Note that, for URIs following the "generic
+   * URI" syntax, the path specified should start with a slash.
+   * For URIs that do not follow the generic URI syntax, this method
+   * sets the scheme-specific part.
+   *
+   * @param p_path the path for this URI (may be null)
+   *
+   * @throws MalformedURIException if p_path contains invalid
+   *                                  characters
+   */
+  public void setPath(String p_path) throws MalformedURIException
+  {
+
+    if (p_path == null)
+    {
+      m_path = null;
+      m_queryString = null;
+      m_fragment = null;
+    }
+    else
+    {
+      initializePath(p_path);
+    }
+  }
+
+  /**
+   * Append to the end of the path of this URI. If the current path does
+   * not end in a slash and the path to be appended does not begin with
+   * a slash, a slash will be appended to the current path before the
+   * new segment is added. Also, if the current path ends in a slash
+   * and the new segment begins with a slash, the extra slash will be
+   * removed before the new segment is appended.
+   *
+   * @param p_addToPath the new segment to be added to the current path
+   *
+   * @throws MalformedURIException if p_addToPath contains syntax
+   *                                  errors
+   */
+  public void appendPath(String p_addToPath) throws MalformedURIException
+  {
+
+    if (p_addToPath == null || p_addToPath.trim().length() == 0)
+    {
+      return;
+    }
+
+    if (!isURIString(p_addToPath))
+    {
+      throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_PATH_INVALID_CHAR, new Object[]{p_addToPath})); //"Path contains invalid character!");
+    }
+
+    if (m_path == null || m_path.trim().length() == 0)
+    {
+      if (p_addToPath.startsWith("/"))
+      {
+        m_path = p_addToPath;
+      }
+      else
+      {
+        m_path = "/" + p_addToPath;
+      }
+    }
+    else if (m_path.endsWith("/"))
+    {
+      if (p_addToPath.startsWith("/"))
+      {
+        m_path = m_path.concat(p_addToPath.substring(1));
+      }
+      else
+      {
+        m_path = m_path.concat(p_addToPath);
+      }
+    }
+    else
+    {
+      if (p_addToPath.startsWith("/"))
+      {
+        m_path = m_path.concat(p_addToPath);
+      }
+      else
+      {
+        m_path = m_path.concat("/" + p_addToPath);
+      }
+    }
+  }
+
+  /**
+   * Set the query string for this URI. A non-null value is valid only
+   * if this is an URI conforming to the generic URI syntax and
+   * the path value is not null.
+   *
+   * @param p_queryString the query string for this URI
+   *
+   * @throws MalformedURIException if p_queryString is not null and this
+   *                                  URI does not conform to the generic
+   *                                  URI syntax or if the path is null
+   */
+  public void setQueryString(String p_queryString)
+          throws MalformedURIException
+  {
+
+    if (p_queryString == null)
+    {
+      m_queryString = null;
+    }
+    else if (!isGenericURI())
+    {
+      throw new MalformedURIException(
+        "Query string can only be set for a generic URI!");
+    }
+    else if (getPath() == null)
+    {
+      throw new MalformedURIException(
+        "Query string cannot be set when path is null!");
+    }
+    else if (!isURIString(p_queryString))
+    {
+      throw new MalformedURIException(
+        "Query string contains invalid character!");
+    }
+    else
+    {
+      m_queryString = p_queryString;
+    }
+  }
+
+  /**
+   * Set the fragment for this URI. A non-null value is valid only
+   * if this is a URI conforming to the generic URI syntax and
+   * the path value is not null.
+   *
+   * @param p_fragment the fragment for this URI
+   *
+   * @throws MalformedURIException if p_fragment is not null and this
+   *                                  URI does not conform to the generic
+   *                                  URI syntax or if the path is null
+   */
+  public void setFragment(String p_fragment) throws MalformedURIException
+  {
+
+    if (p_fragment == null)
+    {
+      m_fragment = null;
+    }
+    else if (!isGenericURI())
+    {
+      throw new MalformedURIException(
+        XMLMessages.createXMLMessage(XMLErrorResources.ER_FRAG_FOR_GENERIC_URI, null)); //"Fragment can only be set for a generic URI!");
+    }
+    else if (getPath() == null)
+    {
+      throw new MalformedURIException(
+        XMLMessages.createXMLMessage(XMLErrorResources.ER_FRAG_WHEN_PATH_NULL, null)); //"Fragment cannot be set when path is null!");
+    }
+    else if (!isURIString(p_fragment))
+    {
+      throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_FRAG_INVALID_CHAR, null)); //"Fragment contains invalid character!");
+    }
+    else
+    {
+      m_fragment = p_fragment;
+    }
+  }
+
+  /**
+   * Determines if the passed-in Object is equivalent to this URI.
+   *
+   * @param p_test the Object to test for equality.
+   *
+   * @return true if p_test is a URI with all values equal to this
+   *         URI, false otherwise
+   */
+  public boolean equals(Object p_test)
+  {
+
+    if (p_test instanceof URI)
+    {
+      URI testURI = (URI) p_test;
+
+      if (((m_scheme == null && testURI.m_scheme == null) || (m_scheme != null && testURI.m_scheme != null && m_scheme.equals(
+              testURI.m_scheme))) && ((m_userinfo == null && testURI.m_userinfo == null) || (m_userinfo != null && testURI.m_userinfo != null && m_userinfo.equals(
+              testURI.m_userinfo))) && ((m_host == null && testURI.m_host == null) || (m_host != null && testURI.m_host != null && m_host.equals(
+              testURI.m_host))) && m_port == testURI.m_port && ((m_path == null && testURI.m_path == null) || (m_path != null && testURI.m_path != null && m_path.equals(
+              testURI.m_path))) && ((m_queryString == null && testURI.m_queryString == null) || (m_queryString != null && testURI.m_queryString != null && m_queryString.equals(
+              testURI.m_queryString))) && ((m_fragment == null && testURI.m_fragment == null) || (m_fragment != null && testURI.m_fragment != null && m_fragment.equals(
+              testURI.m_fragment))))
+      {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Get the URI as a string specification. See RFC 2396 Section 5.2.
+   *
+   * @return the URI string specification
+   */
+  public String toString()
+  {
+
+    StringBuffer uriSpecString = new StringBuffer();
+
+    if (m_scheme != null)
+    {
+      uriSpecString.append(m_scheme);
+      uriSpecString.append(':');
+    }
+
+    uriSpecString.append(getSchemeSpecificPart());
+
+    return uriSpecString.toString();
+  }
+
+  /**
+   * Get the indicator as to whether this URI uses the "generic URI"
+   * syntax.
+   *
+   * @return true if this URI uses the "generic URI" syntax, false
+   *         otherwise
+   */
+  public boolean isGenericURI()
+  {
+
+    // presence of the host (whether valid or empty) means 
+    // double-slashes which means generic uri
+    return (m_host != null);
+  }
+
+  /**
+   * Determine whether a scheme conforms to the rules for a scheme name.
+   * A scheme is conformant if it starts with an alphanumeric, and
+   * contains only alphanumerics, '+','-' and '.'.
+   *
+   *
+   * @param p_scheme The sheme name to check
+   * @return true if the scheme is conformant, false otherwise
+   */
+  public static boolean isConformantSchemeName(String p_scheme)
+  {
+
+    if (p_scheme == null || p_scheme.trim().length() == 0)
+    {
+      return false;
+    }
+
+    if (!isAlpha(p_scheme.charAt(0)))
+    {
+      return false;
+    }
+
+    char testChar;
+
+    for (int i = 1; i < p_scheme.length(); i++)
+    {
+      testChar = p_scheme.charAt(i);
+
+      if (!isAlphanum(testChar) && SCHEME_CHARACTERS.indexOf(testChar) == -1)
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Determine whether a string is syntactically capable of representing
+   * a valid IPv4 address or the domain name of a network host. A valid
+   * IPv4 address consists of four decimal digit groups separated by a
+   * '.'. A hostname consists of domain labels (each of which must
+   * begin and end with an alphanumeric but may contain '-') separated
+   * & by a '.'. See RFC 2396 Section 3.2.2.
+   *
+   *
+   * @param p_address The address string to check
+   * @return true if the string is a syntactically valid IPv4 address
+   *              or hostname
+   */
+  public static boolean isWellFormedAddress(String p_address)
+  {
+
+    if (p_address == null)
+    {
+      return false;
+    }
+
+    String address = p_address.trim();
+    int addrLength = address.length();
+
+    if (addrLength == 0 || addrLength > 255)
+    {
+      return false;
+    }
+
+    if (address.startsWith(".") || address.startsWith("-"))
+    {
+      return false;
+    }
+
+    // rightmost domain label starting with digit indicates IP address
+    // since top level domain label can only start with an alpha
+    // see RFC 2396 Section 3.2.2
+    int index = address.lastIndexOf('.');
+
+    if (address.endsWith("."))
+    {
+      index = address.substring(0, index).lastIndexOf('.');
+    }
+
+    if (index + 1 < addrLength && isDigit(p_address.charAt(index + 1)))
+    {
+      char testChar;
+      int numDots = 0;
+
+      // make sure that 1) we see only digits and dot separators, 2) that
+      // any dot separator is preceded and followed by a digit and 
+      // 3) that we find 3 dots
+      for (int i = 0; i < addrLength; i++)
+      {
+        testChar = address.charAt(i);
+
+        if (testChar == '.')
+        {
+          if (!isDigit(address.charAt(i - 1))
+                  || (i + 1 < addrLength &&!isDigit(address.charAt(i + 1))))
+          {
+            return false;
+          }
+
+          numDots++;
+        }
+        else if (!isDigit(testChar))
+        {
+          return false;
+        }
+      }
+
+      if (numDots != 3)
+      {
+        return false;
+      }
+    }
+    else
+    {
+
+      // domain labels can contain alphanumerics and '-"
+      // but must start and end with an alphanumeric
+      char testChar;
+
+      for (int i = 0; i < addrLength; i++)
+      {
+        testChar = address.charAt(i);
+
+        if (testChar == '.')
+        {
+          if (!isAlphanum(address.charAt(i - 1)))
+          {
+            return false;
+          }
+
+          if (i + 1 < addrLength &&!isAlphanum(address.charAt(i + 1)))
+          {
+            return false;
+          }
+        }
+        else if (!isAlphanum(testChar) && testChar != '-')
+        {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Determine whether a char is a digit.
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is betweeen '0' and '9', false otherwise
+   */
+  private static boolean isDigit(char p_char)
+  {
+    return p_char >= '0' && p_char <= '9';
+  }
+
+  /**
+   * Determine whether a character is a hexadecimal character.
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is betweeen '0' and '9', 'a' and 'f'
+   *         or 'A' and 'F', false otherwise
+   */
+  private static boolean isHex(char p_char)
+  {
+    return (isDigit(p_char) || (p_char >= 'a' && p_char <= 'f')
+            || (p_char >= 'A' && p_char <= 'F'));
+  }
+
+  /**
+   * Determine whether a char is an alphabetic character: a-z or A-Z
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is alphabetic, false otherwise
+   */
+  private static boolean isAlpha(char p_char)
+  {
+    return ((p_char >= 'a' && p_char <= 'z')
+            || (p_char >= 'A' && p_char <= 'Z'));
+  }
+
+  /**
+   * Determine whether a char is an alphanumeric: 0-9, a-z or A-Z
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is alphanumeric, false otherwise
+   */
+  private static boolean isAlphanum(char p_char)
+  {
+    return (isAlpha(p_char) || isDigit(p_char));
+  }
+
+  /**
+   * Determine whether a character is a reserved character:
+   * ';', '/', '?', ':', '@', '&', '=', '+', '$' or ','
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the string contains any reserved characters
+   */
+  private static boolean isReservedCharacter(char p_char)
+  {
+    return RESERVED_CHARACTERS.indexOf(p_char) != -1;
+  }
+
+  /**
+   * Determine whether a char is an unreserved character.
+   *
+   *
+   * @param p_char the character to check
+   * @return true if the char is unreserved, false otherwise
+   */
+  private static boolean isUnreservedCharacter(char p_char)
+  {
+    return (isAlphanum(p_char) || MARK_CHARACTERS.indexOf(p_char) != -1);
+  }
+
+  /**
+   * Determine whether a given string contains only URI characters (also
+   * called "uric" in RFC 2396). uric consist of all reserved
+   * characters, unreserved characters and escaped characters.
+   *
+   *
+   * @param p_uric URI string
+   * @return true if the string is comprised of uric, false otherwise
+   */
+  private static boolean isURIString(String p_uric)
+  {
+
+    if (p_uric == null)
+    {
+      return false;
+    }
+
+    int end = p_uric.length();
+    char testChar = '\0';
+
+    for (int i = 0; i < end; i++)
+    {
+      testChar = p_uric.charAt(i);
+
+      if (testChar == '%')
+      {
+        if (i + 2 >= end ||!isHex(p_uric.charAt(i + 1))
+                ||!isHex(p_uric.charAt(i + 2)))
+        {
+          return false;
+        }
+        else
+        {
+          i += 2;
+
+          continue;
+        }
+      }
+
+      if (isReservedCharacter(testChar) || isUnreservedCharacter(testChar))
+      {
+        continue;
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/UnImplNode.java b/src/main/java/org/apache/xml/utils/UnImplNode.java
new file mode 100644
index 0000000..88ed983
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/UnImplNode.java
@@ -0,0 +1,1977 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: UnImplNode.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+import org.w3c.dom.UserDataHandler;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.TypeInfo;
+/**
+ * To be subclassed by classes that wish to fake being nodes.
+ * @xsl.usage internal
+ */
+public class UnImplNode implements Node, Element, NodeList, Document
+{
+
+  /**
+   * Constructor UnImplNode
+   *
+   */
+  public UnImplNode(){}
+
+  /**
+   * Throw an error.
+   *
+   * @param msg Message Key for the error
+   */
+  public void error(String msg)
+  {
+
+    System.out.println("DOM ERROR! class: " + this.getClass().getName());
+
+    throw new RuntimeException(XMLMessages.createXMLMessage(msg, null));
+  }
+
+  /**
+   * Throw an error.
+   *
+   * @param msg Message Key for the error
+   * @param args Array of arguments to be used in the error message
+   */
+  public void error(String msg, Object[] args)
+  {
+
+    System.out.println("DOM ERROR! class: " + this.getClass().getName());
+
+    throw new RuntimeException(XMLMessages.createXMLMessage(msg, args));  //"UnImplNode error: "+msg);
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param newChild New node to append to the list of this node's children
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Node appendChild(Node newChild) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"appendChild not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return false
+   */
+  public boolean hasChildNodes()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"hasChildNodes not supported!");
+
+    return false;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return 0
+   */
+  public short getNodeType()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getNodeType not supported!");
+
+    return 0;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public Node getParentNode()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getParentNode not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public NodeList getChildNodes()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getChildNodes not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public Node getFirstChild()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getFirstChild not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public Node getLastChild()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getLastChild not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public Node getNextSibling()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getNextSibling not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.NodeList
+   *
+   * @return 0
+   */
+  public int getLength()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getLength not supported!");
+
+    return 0;
+  }  // getLength():int
+
+  /**
+   * Unimplemented. See org.w3c.dom.NodeList
+   *
+   * @param index index of a child of this node in its list of children
+   *
+   * @return null
+   */
+  public Node item(int index)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"item not supported!");
+
+    return null;
+  }  // item(int):Node
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public Document getOwnerDocument()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getOwnerDocument not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public String getTagName()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getTagName not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public String getNodeName()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getNodeName not supported!");
+
+    return null;
+  }
+
+  /** Unimplemented. See org.w3c.dom.Node */
+  public void normalize()
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"normalize not supported!");
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param name Name of the element
+   *
+   * @return null
+   */
+  public NodeList getElementsByTagName(String name)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getElementsByTagName not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param oldAttr Attribute to be removed from this node's list of attributes
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Attr removeAttributeNode(Attr oldAttr) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"removeAttributeNode not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param newAttr Attribute node to be added to this node's list of attributes
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Attr setAttributeNode(Attr newAttr) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"setAttributeNode not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   *
+   * @param name Name of an attribute
+   *
+   * @return false
+   */
+  public boolean hasAttribute(String name)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"hasAttribute not supported!");
+
+    return false;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   *
+   * @param name
+   * @param x
+   *
+   * @return false
+   */
+  public boolean hasAttributeNS(String name, String x)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"hasAttributeNS not supported!");
+
+    return false;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   *
+   * @param name Attribute node name
+   *
+   * @return null
+   */
+  public Attr getAttributeNode(String name)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getAttributeNode not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param name Attribute node name to remove from list of attributes
+   *
+   * @throws DOMException
+   */
+  public void removeAttribute(String name) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"removeAttribute not supported!");
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param name Name of attribute to set
+   * @param value Value of attribute
+   *
+   * @throws DOMException
+   */
+  public void setAttribute(String name, String value) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"setAttribute not supported!");
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param name Name of attribute to get
+   *
+   * @return null
+   */
+  public String getAttribute(String name)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getAttribute not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. Introduced in DOM Level 2.
+   *
+   * @return false
+   */
+  public boolean hasAttributes()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"hasAttributes not supported!");
+
+    return false;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param namespaceURI Namespace URI of the element
+   * @param localName Local part of qualified name of the element
+   *
+   * @return null
+   */
+  public NodeList getElementsByTagNameNS(String namespaceURI,
+                                         String localName)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getElementsByTagNameNS not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param newAttr Attribute to set
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Attr setAttributeNodeNS(Attr newAttr) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"setAttributeNodeNS not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param namespaceURI Namespace URI of attribute node to get
+   * @param localName Local part of qualified name of attribute node to get
+   *
+   * @return null
+   */
+  public Attr getAttributeNodeNS(String namespaceURI, String localName)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getAttributeNodeNS not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param namespaceURI Namespace URI of attribute node to remove
+   * @param localName Local part of qualified name of attribute node to remove
+   *
+   * @throws DOMException
+   */
+  public void removeAttributeNS(String namespaceURI, String localName)
+          throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"removeAttributeNS not supported!");
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param namespaceURI Namespace URI of attribute node to set
+   * NEEDSDOC @param qualifiedName
+   * @param value value of attribute
+   *
+   * @throws DOMException
+   */
+  public void setAttributeNS(
+          String namespaceURI, String qualifiedName, String value)
+            throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"setAttributeNS not supported!");
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Element
+   *
+   * @param namespaceURI Namespace URI of attribute node to get
+   * @param localName Local part of qualified name of attribute node to get
+   *
+   * @return null
+   */
+  public String getAttributeNS(String namespaceURI, String localName)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getAttributeNS not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public Node getPreviousSibling()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getPreviousSibling not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param deep Flag indicating whether to clone deep (clone member variables)
+   *
+   * @return null
+   */
+  public Node cloneNode(boolean deep)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"cloneNode not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public String getNodeValue() throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getNodeValue not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param nodeValue Value to set this node to
+   *
+   * @throws DOMException
+   */
+  public void setNodeValue(String nodeValue) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"setNodeValue not supported!");
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   *
+   * NEEDSDOC @param value
+   * @return value Node value
+   *
+   * @throws DOMException
+   */
+
+  // public String getValue ()
+  // {      
+  //  error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED); //"getValue not supported!");
+  //  return null;
+  // } 
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param value Value to set this node to
+   *
+   * @throws DOMException
+   */
+  public void setValue(String value) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"setValue not supported!");
+  }
+
+  /**
+   *  Returns the name of this attribute.
+   *
+   * @return the name of this attribute.
+   */
+
+  // public String getName()
+  // {
+  //  return this.getNodeName();
+  // }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public Element getOwnerElement()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getOwnerElement not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return False
+   */
+  public boolean getSpecified()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"setValue not supported!");
+
+    return false;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public NamedNodeMap getAttributes()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getAttributes not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param newChild New child node to insert
+   * @param refChild Insert in front of this child
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Node insertBefore(Node newChild, Node refChild) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"insertBefore not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param newChild Replace existing child with this one
+   * @param oldChild Existing child to be replaced
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Node replaceChild(Node newChild, Node oldChild) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"replaceChild not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param oldChild Child to be removed
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Node removeChild(Node oldChild) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"replaceChild not supported!");
+
+    return null;
+  }
+
+  /**
+   * Tests whether the DOM implementation implements a specific feature and
+   * that feature is supported by this node.
+   * @param feature The name of the feature to test. This is the same name
+   *   which can be passed to the method <code>hasFeature</code> on
+   *   <code>DOMImplementation</code>.
+   * @param version This is the version number of the feature to test. In
+   *   Level 2, version 1, this is the string "2.0". If the version is not
+   *   specified, supporting any version of the feature will cause the
+   *   method to return <code>true</code>.
+   *
+   * @return Returns <code>false</code>
+   * @since DOM Level 2
+   */
+  public boolean isSupported(String feature, String version)
+  {
+    return false;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public String getNamespaceURI()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getNamespaceURI not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public String getPrefix()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getPrefix not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @param prefix Prefix to set for this node
+   *
+   * @throws DOMException
+   */
+  public void setPrefix(String prefix) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"setPrefix not supported!");
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Node
+   *
+   * @return null
+   */
+  public String getLocalName()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);  //"getLocalName not supported!");
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @return null
+   */
+  public DocumentType getDoctype()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @return null
+   */
+  public DOMImplementation getImplementation()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @return null
+   */
+  public Element getDocumentElement()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param tagName Element tag name
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Element createElement(String tagName) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @return null
+   */
+  public DocumentFragment createDocumentFragment()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param data Data for text node
+   *
+   * @return null
+   */
+  public Text createTextNode(String data)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param data Data for comment
+   *
+   * @return null
+   */
+  public Comment createComment(String data)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param data Data for CDATA section
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public CDATASection createCDATASection(String data) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param target Target for Processing instruction
+   * @param data Data for Processing instruction
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public ProcessingInstruction createProcessingInstruction(
+          String target, String data) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param name Attribute name
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Attr createAttribute(String name) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param name Entity Reference name
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public EntityReference createEntityReference(String name)
+          throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param importedNode The node to import.
+   * @param deep         If <code>true</code>, recursively import the subtree under
+   *   the specified node; if <code>false</code>, import only the node
+   *   itself, as explained above. This has no effect on <code>Attr</code>
+   *   , <code>EntityReference</code>, and <code>Notation</code> nodes.
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Node importNode(Node importedNode, boolean deep) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param namespaceURI Namespace URI for the element
+   * @param qualifiedName Qualified name of the element
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Element createElementNS(String namespaceURI, String qualifiedName)
+          throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param namespaceURI Namespace URI of the attribute
+   * @param qualifiedName Qualified name of the attribute
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public Attr createAttributeNS(String namespaceURI, String qualifiedName)
+          throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented. See org.w3c.dom.Document
+   *
+   * @param elementId ID of the element to get
+   *
+   * @return null
+   */
+  public Element getElementById(String elementId)
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Set Node data
+   *
+   *
+   * @param data data to set for this node
+   *
+   * @throws DOMException
+   */
+  public void setData(String data) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+  }
+
+  /**
+   * Unimplemented.
+   *
+   * @param offset Start offset of substring to extract.
+   * @param count The length of the substring to extract.
+   *
+   * @return null
+   *
+   * @throws DOMException
+   */
+  public String substringData(int offset, int count) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * Unimplemented.
+   *
+   * @param arg String data to append
+   *
+   * @throws DOMException
+   */
+  public void appendData(String arg) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+  }
+
+  /**
+   * Unimplemented.
+   *
+   * @param offset Start offset of substring to insert.
+   * NEEDSDOC @param arg
+   *
+   * @throws DOMException
+   */
+  public void insertData(int offset, String arg) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+  }
+
+  /**
+   * Unimplemented.
+   *
+   * @param offset Start offset of substring to delete.
+   * @param count The length of the substring to delete.
+   *
+   * @throws DOMException
+   */
+  public void deleteData(int offset, int count) throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+  }
+
+  /**
+   * Unimplemented.
+   *
+   * @param offset Start offset of substring to replace.
+   * @param count The length of the substring to replace.
+   * @param arg substring to replace with
+   *
+   * @throws DOMException
+   */
+  public void replaceData(int offset, int count, String arg)
+          throws DOMException
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+  }
+
+  /**
+   * Unimplemented.
+   *
+   * @param offset Offset into text to split
+   *
+   * @return null, unimplemented
+   *
+   * @throws DOMException
+   */
+  public Text splitText(int offset) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * NEEDSDOC Method adoptNode 
+   *
+   *
+   * NEEDSDOC @param source
+   *
+   * NEEDSDOC (adoptNode) @return
+   *
+   * @throws DOMException
+   */
+  public Node adoptNode(Node source) throws DOMException
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * <p>Based on the <a
+   * href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document
+   * Object Model (DOM) Level 3 Core Specification of 07 April 2004.</a>.
+   * <p>
+   * An attribute specifying, as part of the XML declaration, the encoding
+   * of this document. This is <code>null</code> when unspecified.
+   * @since DOM Level 3
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public String getInputEncoding()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return null;
+  }
+
+  /**
+   * <p>Based on the <a
+   * href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document
+   * Object Model (DOM) Level 3 Core Specification of 07 April 2004.</a>.
+   * <p>
+   * An attribute specifying, as part of the XML declaration, the encoding
+   * of this document. This is <code>null</code> when unspecified.
+   * @since DOM Level 3
+   *
+   * NEEDSDOC @param encoding
+   */
+  public void setInputEncoding(String encoding)
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+  }
+
+  /**
+   * <p>Based on the <a
+   * href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document
+   * Object Model (DOM) Level 3 Core Specification of 07 April 2004.</a>.
+   * <p>
+   * An attribute specifying whether errors checking is enforced or not.
+   * When set to <code>false</code>, the implementation is free to not
+   * test every possible error case normally defined on DOM operations,
+   * and not raise any <code>DOMException</code>. In case of error, the
+   * behavior is undefined. This attribute is <code>true</code> by
+   * defaults.
+   * @since DOM Level 3
+   *
+   * NEEDSDOC ($objectName$) @return
+   */
+  public boolean getStrictErrorChecking()
+  {
+
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+
+    return false;
+  }
+
+  /**
+   * <p>Based on the <a
+   * href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document
+   * Object Model (DOM) Level 3 Core Specification of 07 April 2004.</a>.
+   * <p>
+   * An attribute specifying whether errors checking is enforced or not.
+   * When set to <code>false</code>, the implementation is free to not
+   * test every possible error case normally defined on DOM operations,
+   * and not raise any <code>DOMException</code>. In case of error, the
+   * behavior is undefined. This attribute is <code>true</code> by
+   * defaults.
+   * @since DOM Level 3
+   *
+   * NEEDSDOC @param strictErrorChecking
+   */
+  public void setStrictErrorChecking(boolean strictErrorChecking)
+  {
+    error(XMLErrorResources.ER_FUNCTION_NOT_SUPPORTED);
+  }
+
+    // RAMESH : Pending proper implementation of DOM Level 3    
+    public Object setUserData(String key,
+                              Object data,
+                              UserDataHandler handler) {
+        return getOwnerDocument().setUserData( key, data, handler);
+    }
+
+    /**
+     * Retrieves the object associated to a key on a this node. The object
+     * must first have been set to this node by calling
+     * <code>setUserData</code> with the same key.
+     * @param key The key the object is associated to.
+     * @return Returns the <code>DOMObject</code> associated to the given key
+     *   on this node, or <code>null</code> if there was none.
+     * @since DOM Level 3
+     */
+    public Object getUserData(String key) {
+        return getOwnerDocument().getUserData( key);
+    } 
+
+      /**
+     *  This method returns a specialized object which implements the
+     * specialized APIs of the specified feature and version. The
+     * specialized object may also be obtained by using binding-specific
+     * casting methods but is not necessarily expected to, as discussed in Mixed DOM implementations.
+     * @param feature The name of the feature requested (case-insensitive).
+     * @param version  This is the version number of the feature to test. If
+     *   the version is <code>null</code> or the empty string, supporting
+     *   any version of the feature will cause the method to return an
+     *   object that supports at least one version of the feature.
+     * @return  Returns an object which implements the specialized APIs of
+     *   the specified feature and version, if any, or <code>null</code> if
+     *   there is no object which implements interfaces associated with that
+     *   feature. If the <code>DOMObject</code> returned by this method
+     *   implements the <code>Node</code> interface, it must delegate to the
+     *   primary core <code>Node</code> and not return results inconsistent
+     *   with the primary core <code>Node</code> such as attributes,
+     *   childNodes, etc.
+     * @since DOM Level 3
+     */
+    public Object getFeature(String feature, String version) {
+        // we don't have any alternate node, either this node does the job
+        // or we don't have anything that does
+        return isSupported(feature, version) ? this : null;
+    }
+
+    /**
+     * Tests whether two nodes are equal.
+     * <br>This method tests for equality of nodes, not sameness (i.e.,
+     * whether the two nodes are references to the same object) which can be
+     * tested with <code>Node.isSameNode</code>. All nodes that are the same
+     * will also be equal, though the reverse may not be true.
+     * <br>Two nodes are equal if and only if the following conditions are
+     * satisfied: The two nodes are of the same type.The following string
+     * attributes are equal: <code>nodeName</code>, <code>localName</code>,
+     * <code>namespaceURI</code>, <code>prefix</code>, <code>nodeValue</code>
+     * , <code>baseURI</code>. This is: they are both <code>null</code>, or
+     * they have the same length and are character for character identical.
+     * The <code>attributes</code> <code>NamedNodeMaps</code> are equal.
+     * This is: they are both <code>null</code>, or they have the same
+     * length and for each node that exists in one map there is a node that
+     * exists in the other map and is equal, although not necessarily at the
+     * same index.The <code>childNodes</code> <code>NodeLists</code> are
+     * equal. This is: they are both <code>null</code>, or they have the
+     * same length and contain equal nodes at the same index. This is true
+     * for <code>Attr</code> nodes as for any other type of node. Note that
+     * normalization can affect equality; to avoid this, nodes should be
+     * normalized before being compared.
+     * <br>For two <code>DocumentType</code> nodes to be equal, the following
+     * conditions must also be satisfied: The following string attributes
+     * are equal: <code>publicId</code>, <code>systemId</code>,
+     * <code>internalSubset</code>.The <code>entities</code>
+     * <code>NamedNodeMaps</code> are equal.The <code>notations</code>
+     * <code>NamedNodeMaps</code> are equal.
+     * <br>On the other hand, the following do not affect equality: the
+     * <code>ownerDocument</code> attribute, the <code>specified</code>
+     * attribute for <code>Attr</code> nodes, the
+     * <code>isWhitespaceInElementContent</code> attribute for
+     * <code>Text</code> nodes, as well as any user data or event listeners
+     * registered on the nodes.
+     * @param arg The node to compare equality with.
+     * @param deep If <code>true</code>, recursively compare the subtrees; if
+     *   <code>false</code>, compare only the nodes themselves (and its
+     *   attributes, if it is an <code>Element</code>).
+     * @return If the nodes, and possibly subtrees are equal,
+     *   <code>true</code> otherwise <code>false</code>.
+     * @since DOM Level 3
+     */
+    public boolean isEqualNode(Node arg) {
+        if (arg == this) {
+            return true;
+        }
+        if (arg.getNodeType() != getNodeType()) {
+            return false;
+        }
+        // in theory nodeName can't be null but better be careful
+        // who knows what other implementations may be doing?...
+        if (getNodeName() == null) {
+            if (arg.getNodeName() != null) {
+                return false;
+            }
+        }
+        else if (!getNodeName().equals(arg.getNodeName())) {
+            return false;
+        }
+
+        if (getLocalName() == null) {
+            if (arg.getLocalName() != null) {
+                return false;
+            }
+        }
+        else if (!getLocalName().equals(arg.getLocalName())) {
+            return false;
+        }
+
+        if (getNamespaceURI() == null) {
+            if (arg.getNamespaceURI() != null) {
+                return false;
+            }
+        }
+        else if (!getNamespaceURI().equals(arg.getNamespaceURI())) {
+            return false;
+        }
+
+        if (getPrefix() == null) {
+            if (arg.getPrefix() != null) {
+                return false;
+            }
+        }
+        else if (!getPrefix().equals(arg.getPrefix())) {
+            return false;
+        }
+
+        if (getNodeValue() == null) {
+            if (arg.getNodeValue() != null) {
+                return false;
+            }
+        }
+        else if (!getNodeValue().equals(arg.getNodeValue())) {
+            return false;
+        }
+    /*
+        if (getBaseURI() == null) {
+            if (((NodeImpl) arg).getBaseURI() != null) {
+                return false;
+            }
+        }
+        else if (!getBaseURI().equals(((NodeImpl) arg).getBaseURI())) {
+            return false;
+        }
+*/
+
+             return true;
+    }
+
+    /**
+     * DOM Level 3:
+     * Look up the namespace URI associated to the given prefix, starting from this node.
+     * Use lookupNamespaceURI(null) to lookup the default namespace
+     *
+     * @param namespaceURI
+     * @return th URI for the namespace
+     * @since DOM Level 3
+     */
+    public String lookupNamespaceURI(String specifiedPrefix) {
+        short type = this.getNodeType();
+        switch (type) {
+        case Node.ELEMENT_NODE : {
+
+                String namespace = this.getNamespaceURI();
+                String prefix = this.getPrefix();
+                if (namespace !=null) {
+                    // REVISIT: is it possible that prefix is empty string?
+                    if (specifiedPrefix== null && prefix==specifiedPrefix) {
+                        // looking for default namespace
+                        return namespace;
+                    } else if (prefix != null && prefix.equals(specifiedPrefix)) {
+                        // non default namespace
+                        return namespace;
+                    }
+                }
+                if (this.hasAttributes()) {
+                    NamedNodeMap map = this.getAttributes();
+                    int length = map.getLength();
+                    for (int i=0;i<length;i++) {
+                        Node attr = map.item(i);
+                        String attrPrefix = attr.getPrefix();
+                        String value = attr.getNodeValue();
+                        namespace = attr.getNamespaceURI();
+                        if (namespace !=null && namespace.equals("http://www.w3.org/2000/xmlns/")) {
+                            // at this point we are dealing with DOM Level 2 nodes only
+                            if (specifiedPrefix == null &&
+                                attr.getNodeName().equals("xmlns")) {
+                                // default namespace
+                                return value;
+                            } else if (attrPrefix !=null &&
+                                       attrPrefix.equals("xmlns") &&
+                                       attr.getLocalName().equals(specifiedPrefix)) {
+                 // non default namespace
+                                return value;
+                            }
+                        }
+                    }
+                }
+		/*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupNamespaceURI(specifiedPrefix);
+                }
+		*/
+
+                return null;
+
+
+            }
+/*
+        case Node.DOCUMENT_NODE : {
+                return((NodeImpl)((Document)this).getDocumentElement()).lookupNamespaceURI(specifiedPrefix) ;
+            }
+*/
+        case Node.ENTITY_NODE :
+        case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return null;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
+                    return getOwnerElement().lookupNamespaceURI(specifiedPrefix);
+
+                }
+                return null;
+            }
+        default:{
+	   /*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupNamespaceURI(specifiedPrefix);
+                }
+             */
+                return null;
+            }
+
+        }
+    }
+    
+    /**
+     *  DOM Level 3:
+     *  This method checks if the specified <code>namespaceURI</code> is the
+     *  default namespace or not.
+     *  @param namespaceURI The namespace URI to look for.
+     *  @return  <code>true</code> if the specified <code>namespaceURI</code>
+     *   is the default namespace, <code>false</code> otherwise.
+     * @since DOM Level 3
+     */
+    public boolean isDefaultNamespace(String namespaceURI){
+       /*
+        // REVISIT: remove casts when DOM L3 becomes REC.
+        short type = this.getNodeType();
+        switch (type) {
+        case Node.ELEMENT_NODE: {
+            String namespace = this.getNamespaceURI();
+            String prefix = this.getPrefix();
+
+            // REVISIT: is it possible that prefix is empty string?
+            if (prefix == null || prefix.length() == 0) {
+                if (namespaceURI == null) {
+                    return (namespace == namespaceURI);
+                }
+                return namespaceURI.equals(namespace);
+            }
+            if (this.hasAttributes()) {
+                ElementImpl elem = (ElementImpl)this;
+                NodeImpl attr = (NodeImpl)elem.getAttributeNodeNS("http://www.w3.org/2000/xmlns/", "xmlns");
+                if (attr != null) {
+                    String value = attr.getNodeValue();
+                    if (namespaceURI == null) {
+                        return (namespace == value);
+                    }
+                    return namespaceURI.equals(value);
+                }
+            }
+
+            NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+            if (ancestor != null) {
+                return ancestor.isDefaultNamespace(namespaceURI);
+            }
+            return false;
+        }
+        case Node.DOCUMENT_NODE:{
+                return((NodeImpl)((Document)this).getDocumentElement()).isDefaultNamespace(namespaceURI);
+            }
+
+        case Node.ENTITY_NODE :
+          case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return false;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.ownerNode.getNodeType() == Node.ELEMENT_NODE) {
+                    return ownerNode.isDefaultNamespace(namespaceURI);
+
+                }
+                return false;
+            }
+        default:{  
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.isDefaultNamespace(namespaceURI);
+                }
+                return false;
+            }
+
+        }
+*/
+        return false;
+
+
+    }
+
+    /**
+     *
+     * DOM Level 3:
+     * Look up the prefix associated to the given namespace URI, starting from this node.
+     *
+     * @param namespaceURI
+     * @return the prefix for the namespace
+     */
+    public String lookupPrefix(String namespaceURI){
+
+        // REVISIT: When Namespaces 1.1 comes out this may not be true
+        // Prefix can't be bound to null namespace
+        if (namespaceURI == null) {
+            return null;
+        }
+
+        short type = this.getNodeType();
+
+        switch (type) {
+/*
+        case Node.ELEMENT_NODE: {
+
+                String namespace = this.getNamespaceURI(); // to flip out children
+                return lookupNamespacePrefix(namespaceURI, (ElementImpl)this);
+            }
+
+        case Node.DOCUMENT_NODE:{
+                return((NodeImpl)((Document)this).getDocumentElement()).lookupPrefix(namespaceURI);
+            }
+*/
+        case Node.ENTITY_NODE :
+        case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return null;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
+                    return getOwnerElement().lookupPrefix(namespaceURI);
+
+                }
+                return null;
+            }
+        default:{ 
+/*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupPrefix(namespaceURI);
+                }
+*/
+                return null;
+            }
+         }
+    }
+
+    /**
+     * Returns whether this node is the same node as the given one.
+     * <br>This method provides a way to determine whether two
+     * <code>Node</code> references returned by the implementation reference
+     * the same object. When two <code>Node</code> references are references
+     * to the same object, even if through a proxy, the references may be
+     * used completely interchangably, such that all attributes have the
+     * same values and calling the same DOM method on either reference
+     * always has exactly the same effect.
+     * @param other The node to test against.
+     * @return Returns <code>true</code> if the nodes are the same,
+     *   <code>false</code> otherwise.
+     * @since DOM Level 3
+     */
+    public boolean isSameNode(Node other) {
+        // we do not use any wrapper so the answer is obvious
+        return this == other;
+    }
+
+    /**
+     * This attribute returns the text content of this node and its
+     * descendants. When it is defined to be null, setting it has no effect.
+     * When set, any possible children this node may have are removed and
+     * replaced by a single <code>Text</code> node containing the string
+     * this attribute is set to. On getting, no serialization is performed,
+     * the returned string does not contain any markup. No whitespace
+     * normalization is performed, the returned string does not contain the
+     * element content whitespaces . Similarly, on setting, no parsing is
+     * performed either, the input string is taken as pure textual content.
+     * <br>The string returned is made of the text content of this node
+     * depending on its type, as defined below:
+     * <table border='1'>
+     * <tr>
+     * <th>Node type</th>
+     * <th>Content</th>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
+     * DOCUMENT_FRAGMENT_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
+     * attribute value of every child node, excluding COMMENT_NODE and
+     * PROCESSING_INSTRUCTION_NODE nodes</td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
+     * CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * <code>nodeValue</code></td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * null</td>
+     * </tr>
+     * </table>
+     * @exception DOMException
+     *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+     * @exception DOMException
+     *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
+     *   fit in a <code>DOMString</code> variable on the implementation
+     *   platform.
+     * @since DOM Level 3
+     */
+    public void setTextContent(String textContent)
+        throws DOMException {
+        setNodeValue(textContent);
+    }
+    
+    /**
+     * This attribute returns the text content of this node and its
+     * descendants. When it is defined to be null, setting it has no effect.
+     * When set, any possible children this node may have are removed and
+     * replaced by a single <code>Text</code> node containing the string
+     * this attribute is set to. On getting, no serialization is performed,
+     * the returned string does not contain any markup. No whitespace
+     * normalization is performed, the returned string does not contain the
+     * element content whitespaces . Similarly, on setting, no parsing is
+     * performed either, the input string is taken as pure textual content.
+     * <br>The string returned is made of the text content of this node
+     * depending on its type, as defined below:
+     * <table border='1'>
+     * <tr>
+     * <th>Node type</th>
+     * <th>Content</th>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
+     * DOCUMENT_FRAGMENT_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
+     * attribute value of every child node, excluding COMMENT_NODE and
+     * PROCESSING_INSTRUCTION_NODE nodes</td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
+     * CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * <code>nodeValue</code></td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * null</td>
+     * </tr>
+     * </table>
+     * @exception DOMException
+     *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+     * @exception DOMException
+     *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
+     *   fit in a <code>DOMString</code> variable on the implementation
+     *   platform.
+     * @since DOM Level 3
+     */
+    public String getTextContent() throws DOMException {
+        return getNodeValue();  // overriden in some subclasses
+    }
+
+    /**
+     * Compares a node with this node with regard to their position in the
+     * document.
+     * @param other The node to compare against this node.
+     * @return Returns how the given node is positioned relatively to this
+     *   node.
+     * @since DOM Level 3
+     */
+    public short compareDocumentPosition(Node other) throws DOMException {
+        return 0;
+    }
+
+    /**
+     * The absolute base URI of this node or <code>null</code> if undefined.
+     * This value is computed according to . However, when the
+     * <code>Document</code> supports the feature "HTML" , the base URI is
+     * computed using first the value of the href attribute of the HTML BASE
+     * element if any, and the value of the <code>documentURI</code>
+     * attribute from the <code>Document</code> interface otherwise.
+     * <br> When the node is an <code>Element</code>, a <code>Document</code>
+     * or a a <code>ProcessingInstruction</code>, this attribute represents
+     * the properties [base URI] defined in . When the node is a
+     * <code>Notation</code>, an <code>Entity</code>, or an
+     * <code>EntityReference</code>, this attribute represents the
+     * properties [declaration base URI] in the . How will this be affected
+     * by resolution of relative namespace URIs issue?It's not.Should this
+     * only be on Document, Element, ProcessingInstruction, Entity, and
+     * Notation nodes, according to the infoset? If not, what is it equal to
+     * on other nodes? Null? An empty string? I think it should be the
+     * parent's.No.Should this be read-only and computed or and actual
+     * read-write attribute?Read-only and computed (F2F 19 Jun 2000 and
+     * teleconference 30 May 2001).If the base HTML element is not yet
+     * attached to a document, does the insert change the Document.baseURI?
+     * Yes. (F2F 26 Sep 2001)
+     * @since DOM Level 3
+     */
+    public String getBaseURI() {
+        return null;
+    }
+
+    /**
+     * DOM Level 3
+     * Renaming node
+     */
+    public Node renameNode(Node n,
+                           String namespaceURI,
+                           String name)
+                           throws DOMException{
+        return n;
+    }
+    
+    /**
+     *  DOM Level 3
+     *  Normalize document.
+     */
+    public void normalizeDocument(){   
+
+    }
+    
+    /**
+     *  The configuration used when <code>Document.normalizeDocument</code> is
+     * invoked.
+     * @since DOM Level 3
+     */
+    public DOMConfiguration getDomConfig(){
+       return null;
+    }
+
+    
+    /** DOM Level 3 feature: documentURI */
+    protected String fDocumentURI;
+
+    /**
+     * DOM Level 3
+     */
+    public void setDocumentURI(String documentURI){
+        
+        fDocumentURI= documentURI;
+    }
+
+    /**
+     * DOM Level 3
+     * The location of the document or <code>null</code> if undefined.
+     * <br>Beware that when the <code>Document</code> supports the feature
+     * "HTML" , the href attribute of the HTML BASE element takes precedence
+     * over this attribute.
+     * @since DOM Level 3
+     */
+    public String getDocumentURI(){
+        return fDocumentURI;
+    }
+
+    /** DOM Level 3 feature: Document actualEncoding */
+    protected String actualEncoding;
+
+    /**
+     * DOM Level 3
+     * An attribute specifying the actual encoding of this document. This is
+     * <code>null</code> otherwise.
+     * <br> This attribute represents the property [character encoding scheme]
+     * defined in .
+     * @since DOM Level 3
+     */
+    public String getActualEncoding() {
+        return actualEncoding;
+    }
+
+    /**
+     * DOM Level 3
+     * An attribute specifying the actual encoding of this document. This is
+     * <code>null</code> otherwise.
+     * <br> This attribute represents the property [character encoding scheme]
+     * defined in .
+     * @since DOM Level 3
+     */
+    public void setActualEncoding(String value) {
+        actualEncoding = value;
+    }
+
+    /**
+     * DOM Level 3
+     */
+    public Text replaceWholeText(String content)
+                                 throws DOMException{
+/*
+
+        if (needsSyncData()) {
+            synchronizeData();
+        }
+
+        // make sure we can make the replacement
+        if (!canModify(nextSibling)) {
+            throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
+                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
+        }
+
+        Node parent = this.getParentNode();
+        if (content == null || content.length() == 0) {
+            // remove current node
+            if (parent !=null) { // check if node in the tree
+                parent.removeChild(this);
+                return null;
+            }
+        }
+        Text currentNode = null;
+        if (isReadOnly()){
+            Text newNode = this.ownerDocument().createTextNode(content);
+            if (parent !=null) { // check if node in the tree
+                parent.insertBefore(newNode, this);
+                parent.removeChild(this);
+                currentNode = newNode;
+            } else {
+                return newNode;
+            }
+        }  else {
+            this.setData(content);
+            currentNode = this;
+        }
+        Node sibling =  currentNode.getNextSibling();
+        while ( sibling !=null) {
+            parent.removeChild(sibling);
+            sibling = currentNode.getNextSibling();
+        }
+
+        return currentNode;
+*/
+        return null; //Pending
+    }
+
+    /**
+     * DOM Level 3
+     * Returns all text of <code>Text</code> nodes logically-adjacent text
+     * nodes to this node, concatenated in document order.
+     * @since DOM Level 3
+     */
+    public String getWholeText(){
+
+/*
+        if (needsSyncData()) {
+            synchronizeData();
+        }
+        if (nextSibling == null) {
+            return data;
+        }
+        StringBuffer buffer = new StringBuffer();
+        if (data != null && data.length() != 0) {
+            buffer.append(data);
+        }
+        getWholeText(nextSibling, buffer);
+        return buffer.toString();
+*/
+        return null; // PENDING
+
+    }
+
+    /**
+     * DOM Level 3
+     * Returns whether this text node contains whitespace in element content,
+     * often abusively called "ignorable whitespace".
+     */
+    public boolean isWhitespaceInElementContent(){
+        return false;
+    }
+
+    /**
+     * NON-DOM: set the type of this attribute to be ID type.
+     *
+     * @param id
+     */
+    public void setIdAttribute(boolean id){
+        //PENDING
+    }
+
+    /**
+     * DOM Level 3: register the given attribute node as an ID attribute
+     */
+    public void setIdAttribute(String name, boolean makeId) {
+        //PENDING
+    }
+       
+    /**
+     * DOM Level 3: register the given attribute node as an ID attribute
+     */
+    public void setIdAttributeNode(Attr at, boolean makeId) {
+        //PENDING
+    }
+
+    /**
+     * DOM Level 3: register the given attribute node as an ID attribute
+     */
+    public void setIdAttributeNS(String namespaceURI, String localName,
+                                    boolean makeId) {
+        //PENDING
+    }
+    
+    /**
+     * Method getSchemaTypeInfo.
+     * @return TypeInfo
+     */
+    public TypeInfo getSchemaTypeInfo(){
+        return null; //PENDING
+    }
+
+    public boolean isId() {
+        return false; //PENDING
+    }
+  
+    private String xmlEncoding;
+    public String getXmlEncoding ( ) {
+        return xmlEncoding;
+    }
+    public void setXmlEncoding ( String xmlEncoding ) {
+        this.xmlEncoding = xmlEncoding;
+    }
+  
+    private boolean xmlStandalone;
+    public boolean getXmlStandalone() {
+        return xmlStandalone;
+    }
+
+    public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
+        this.xmlStandalone = xmlStandalone;
+    }
+
+    private String xmlVersion;
+    public String getXmlVersion() {
+        return xmlVersion;
+    }
+
+    public void setXmlVersion(String xmlVersion) throws DOMException {
+        this.xmlVersion = xmlVersion;
+    }
+
+}
diff --git a/src/main/java/org/apache/xml/utils/WrappedRuntimeException.java b/src/main/java/org/apache/xml/utils/WrappedRuntimeException.java
new file mode 100644
index 0000000..ded920c
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/WrappedRuntimeException.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WrappedRuntimeException.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * This class is for throwing important checked exceptions
+ * over non-checked methods.  It should be used with care,
+ * and in limited circumstances.
+ */
+public class WrappedRuntimeException extends RuntimeException
+{
+    static final long serialVersionUID = 7140414456714658073L;
+
+  /** Primary checked exception.
+   *  @serial          */
+  private Exception m_exception;
+
+  /**
+   * Construct a WrappedRuntimeException from a
+   * checked exception.
+   *
+   * @param e Primary checked exception
+   */
+  public WrappedRuntimeException(Exception e)
+  {
+
+    super(e.getMessage());
+
+    m_exception = e;
+  }
+
+  /**
+   * Constructor WrappedRuntimeException
+   *
+   *
+   * @param msg Exception information.
+   * @param e Primary checked exception
+   */
+  public WrappedRuntimeException(String msg, Exception e)
+  {
+
+    super(msg);
+
+    m_exception = e;
+  }
+  
+  /**
+   * Get the checked exception that this runtime exception wraps.
+   *
+   * @return The primary checked exception
+   */
+  public Exception getException()
+  {
+    return m_exception;
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/XML11Char.java b/src/main/java/org/apache/xml/utils/XML11Char.java
new file mode 100644
index 0000000..950d485
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/XML11Char.java
@@ -0,0 +1,432 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xml.utils;
+
+import java.util.Arrays;
+
+
+/**
+ * THIS IS A COPY OF THE XERCES-2J CLASS org.apache.xerces.utls.XMLChar
+ *  
+ * This class defines the basic properties of characters in XML 1.1. The data
+ * in this class can be used to verify that a character is a valid
+ * XML 1.1 character or if the character is a space, name start, or name
+ * character.
+ * <p>
+ * A series of convenience methods are supplied to ease the burden
+ * of the developer.  Using the character as an index into the <code>XML11CHARS</code>
+ * array and applying the appropriate mask flag (e.g.
+ * <code>MASK_VALID</code>), yields the same results as calling the
+ * convenience methods. There is one exception: check the comments
+ * for the <code>isValid</code> method for details.
+ *
+ * @version $Id: XML11Char.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+public class XML11Char {
+
+    //
+    // Constants
+    //
+
+    /** Character flags for XML 1.1. */
+    private static final byte XML11CHARS [] = new byte [1 << 16];
+
+    /** XML 1.1 Valid character mask. */
+    public static final int MASK_XML11_VALID = 0x01;
+
+    /** XML 1.1 Space character mask. */
+    public static final int MASK_XML11_SPACE = 0x02;
+
+    /** XML 1.1 Name start character mask. */
+    public static final int MASK_XML11_NAME_START = 0x04;
+
+    /** XML 1.1 Name character mask. */
+    public static final int MASK_XML11_NAME = 0x08;
+
+    /** XML 1.1 control character mask */
+    public static final int MASK_XML11_CONTROL = 0x10;
+
+    /** XML 1.1 content for external entities (valid - "special" chars - control chars) */
+    public static final int MASK_XML11_CONTENT = 0x20;
+
+    /** XML namespaces 1.1 NCNameStart */
+    public static final int MASK_XML11_NCNAME_START = 0x40;
+
+    /** XML namespaces 1.1 NCName */
+    public static final int MASK_XML11_NCNAME = 0x80;
+    
+    /** XML 1.1 content for internal entities (valid - "special" chars) */
+    public static final int MASK_XML11_CONTENT_INTERNAL = MASK_XML11_CONTROL | MASK_XML11_CONTENT; 
+
+    //
+    // Static initialization
+    //
+
+    static {
+    	
+        // Initializing the Character Flag Array
+        // Code generated by: XML11CharGenerator.
+        
+        Arrays.fill(XML11CHARS, 1, 9, (byte) 17 ); // Fill 8 of value (byte) 17
+        XML11CHARS[9] = 35;
+        XML11CHARS[10] = 3;
+        Arrays.fill(XML11CHARS, 11, 13, (byte) 17 ); // Fill 2 of value (byte) 17
+        XML11CHARS[13] = 3;
+        Arrays.fill(XML11CHARS, 14, 32, (byte) 17 ); // Fill 18 of value (byte) 17
+        XML11CHARS[32] = 35;
+        Arrays.fill(XML11CHARS, 33, 38, (byte) 33 ); // Fill 5 of value (byte) 33
+        XML11CHARS[38] = 1;
+        Arrays.fill(XML11CHARS, 39, 45, (byte) 33 ); // Fill 6 of value (byte) 33
+        Arrays.fill(XML11CHARS, 45, 47, (byte) -87 ); // Fill 2 of value (byte) -87
+        XML11CHARS[47] = 33;
+        Arrays.fill(XML11CHARS, 48, 58, (byte) -87 ); // Fill 10 of value (byte) -87
+        XML11CHARS[58] = 45;
+        XML11CHARS[59] = 33;
+        XML11CHARS[60] = 1;
+        Arrays.fill(XML11CHARS, 61, 65, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(XML11CHARS, 65, 91, (byte) -19 ); // Fill 26 of value (byte) -19
+        Arrays.fill(XML11CHARS, 91, 93, (byte) 33 ); // Fill 2 of value (byte) 33
+        XML11CHARS[93] = 1;
+        XML11CHARS[94] = 33;
+        XML11CHARS[95] = -19;
+        XML11CHARS[96] = 33;
+        Arrays.fill(XML11CHARS, 97, 123, (byte) -19 ); // Fill 26 of value (byte) -19
+        Arrays.fill(XML11CHARS, 123, 127, (byte) 33 ); // Fill 4 of value (byte) 33
+        Arrays.fill(XML11CHARS, 127, 133, (byte) 17 ); // Fill 6 of value (byte) 17
+        XML11CHARS[133] = 35;
+        Arrays.fill(XML11CHARS, 134, 160, (byte) 17 ); // Fill 26 of value (byte) 17
+        Arrays.fill(XML11CHARS, 160, 183, (byte) 33 ); // Fill 23 of value (byte) 33
+        XML11CHARS[183] = -87;
+        Arrays.fill(XML11CHARS, 184, 192, (byte) 33 ); // Fill 8 of value (byte) 33
+        Arrays.fill(XML11CHARS, 192, 215, (byte) -19 ); // Fill 23 of value (byte) -19
+        XML11CHARS[215] = 33;
+        Arrays.fill(XML11CHARS, 216, 247, (byte) -19 ); // Fill 31 of value (byte) -19
+        XML11CHARS[247] = 33;
+        Arrays.fill(XML11CHARS, 248, 768, (byte) -19 ); // Fill 520 of value (byte) -19
+        Arrays.fill(XML11CHARS, 768, 880, (byte) -87 ); // Fill 112 of value (byte) -87
+        Arrays.fill(XML11CHARS, 880, 894, (byte) -19 ); // Fill 14 of value (byte) -19
+        XML11CHARS[894] = 33;
+        Arrays.fill(XML11CHARS, 895, 8192, (byte) -19 ); // Fill 7297 of value (byte) -19
+        Arrays.fill(XML11CHARS, 8192, 8204, (byte) 33 ); // Fill 12 of value (byte) 33
+        Arrays.fill(XML11CHARS, 8204, 8206, (byte) -19 ); // Fill 2 of value (byte) -19
+        Arrays.fill(XML11CHARS, 8206, 8232, (byte) 33 ); // Fill 26 of value (byte) 33
+        XML11CHARS[8232] = 35;
+        Arrays.fill(XML11CHARS, 8233, 8255, (byte) 33 ); // Fill 22 of value (byte) 33
+        Arrays.fill(XML11CHARS, 8255, 8257, (byte) -87 ); // Fill 2 of value (byte) -87
+        Arrays.fill(XML11CHARS, 8257, 8304, (byte) 33 ); // Fill 47 of value (byte) 33
+        Arrays.fill(XML11CHARS, 8304, 8592, (byte) -19 ); // Fill 288 of value (byte) -19
+        Arrays.fill(XML11CHARS, 8592, 11264, (byte) 33 ); // Fill 2672 of value (byte) 33
+        Arrays.fill(XML11CHARS, 11264, 12272, (byte) -19 ); // Fill 1008 of value (byte) -19
+        Arrays.fill(XML11CHARS, 12272, 12289, (byte) 33 ); // Fill 17 of value (byte) 33
+        Arrays.fill(XML11CHARS, 12289, 55296, (byte) -19 ); // Fill 43007 of value (byte) -19
+        Arrays.fill(XML11CHARS, 57344, 63744, (byte) 33 ); // Fill 6400 of value (byte) 33
+        Arrays.fill(XML11CHARS, 63744, 64976, (byte) -19 ); // Fill 1232 of value (byte) -19
+        Arrays.fill(XML11CHARS, 64976, 65008, (byte) 33 ); // Fill 32 of value (byte) 33
+        Arrays.fill(XML11CHARS, 65008, 65534, (byte) -19 ); // Fill 526 of value (byte) -19
+
+    } // <clinit>()
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Returns true if the specified character is a space character
+     * as amdended in the XML 1.1 specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11Space(int c) {
+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_SPACE) != 0);
+    } // isXML11Space(int):boolean
+
+    /**
+     * Returns true if the specified character is valid. This method
+     * also checks the surrogate character range from 0x10000 to 0x10FFFF.
+     * <p>
+     * If the program chooses to apply the mask directly to the
+     * <code>XML11CHARS</code> array, then they are responsible for checking
+     * the surrogate character range.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11Valid(int c) {
+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_VALID) != 0) 
+                || (0x10000 <= c && c <= 0x10FFFF);
+    } // isXML11Valid(int):boolean
+
+    /**
+     * Returns true if the specified character is invalid.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11Invalid(int c) {
+        return !isXML11Valid(c);
+    } // isXML11Invalid(int):boolean
+
+    /**
+     * Returns true if the specified character is valid and permitted outside
+     * of a character reference.  
+     * That is, this method will return false for the same set as
+     * isXML11Valid, except it also reports false for "control characters".
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11ValidLiteral(int c) {
+        return ((c < 0x10000 && ((XML11CHARS[c] & MASK_XML11_VALID) != 0 && (XML11CHARS[c] & MASK_XML11_CONTROL) == 0))
+            || (0x10000 <= c && c <= 0x10FFFF)); 
+    } // isXML11ValidLiteral(int):boolean
+
+    /**
+     * Returns true if the specified character can be considered 
+     * content in an external parsed entity.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11Content(int c) {
+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_CONTENT) != 0) ||
+               (0x10000 <= c && c <= 0x10FFFF);
+    } // isXML11Content(int):boolean
+    
+    /**
+     * Returns true if the specified character can be considered 
+     * content in an internal parsed entity.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11InternalEntityContent(int c) {
+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_CONTENT_INTERNAL) != 0) ||
+               (0x10000 <= c && c <= 0x10FFFF);
+    } // isXML11InternalEntityContent(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid name start
+     * character as defined by production [4] in the XML 1.1
+     * specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11NameStart(int c) {
+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_NAME_START) != 0)
+            || (0x10000 <= c && c < 0xF0000);
+    } // isXML11NameStart(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid name
+     * character as defined by production [4a] in the XML 1.1
+     * specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11Name(int c) {
+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_NAME) != 0) 
+            || (c >= 0x10000 && c < 0xF0000);
+    } // isXML11Name(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid NCName start
+     * character as defined by production [4] in Namespaces in XML
+     * 1.1 recommendation.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11NCNameStart(int c) {
+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_NCNAME_START) != 0)
+            || (0x10000 <= c && c < 0xF0000);
+    } // isXML11NCNameStart(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid NCName
+     * character as defined by production [5] in Namespaces in XML
+     * 1.1 recommendation.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11NCName(int c) {
+        return (c < 0x10000 && (XML11CHARS[c] & MASK_XML11_NCNAME) != 0)
+            || (0x10000 <= c && c < 0xF0000);
+    } // isXML11NCName(int):boolean
+    
+    /**
+     * Returns whether the given character is a valid 
+     * high surrogate for a name character. This includes
+     * all high surrogates for characters [0x10000-0xEFFFF].
+     * In other words everything excluding planes 15 and 16.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isXML11NameHighSurrogate(int c) {
+        return (0xD800 <= c && c <= 0xDB7F);
+    }
+
+    /*
+     * [5] Name ::= NameStartChar NameChar*
+     */
+    /**
+     * Check to see if a string is a valid Name according to [5]
+     * in the XML 1.1 Recommendation
+     *
+     * @param name string to check
+     * @return true if name is a valid Name
+     */
+    public static boolean isXML11ValidName(String name) {
+        int length = name.length();
+        if (length == 0)
+            return false;
+        int i = 1;
+        char ch = name.charAt(0);
+        if( !isXML11NameStart(ch) ) {
+            if ( length > 1 && isXML11NameHighSurrogate(ch) ) {
+                char ch2 = name.charAt(1);
+                if ( !XMLChar.isLowSurrogate(ch2) || 
+                     !isXML11NameStart(XMLChar.supplemental(ch, ch2)) ) {
+                    return false;
+                }
+                i = 2;
+            }
+            else {
+                return false;
+            }
+        }
+        while (i < length) {
+            ch = name.charAt(i);
+            if ( !isXML11Name(ch) ) {
+                if ( ++i < length && isXML11NameHighSurrogate(ch) ) {
+                    char ch2 = name.charAt(i);
+                    if ( !XMLChar.isLowSurrogate(ch2) || 
+                         !isXML11Name(XMLChar.supplemental(ch, ch2)) ) {
+                        return false;
+                    }
+                }
+                else {
+                    return false;
+                }
+            }
+            ++i;
+        }
+        return true;
+    } // isXML11ValidName(String):boolean
+    
+
+    /*
+     * from the namespace 1.1 rec
+     * [4] NCName ::= NCNameStartChar NCNameChar*
+     */
+    /**
+     * Check to see if a string is a valid NCName according to [4]
+     * from the XML Namespaces 1.1 Recommendation
+     *
+     * @param ncName string to check
+     * @return true if name is a valid NCName
+     */
+    public static boolean isXML11ValidNCName(String ncName) {
+        int length = ncName.length();
+        if (length == 0)
+            return false;
+        int i = 1;
+        char ch = ncName.charAt(0);
+        if( !isXML11NCNameStart(ch) ) {
+            if ( length > 1 && isXML11NameHighSurrogate(ch) ) {
+                char ch2 = ncName.charAt(1);
+                if ( !XMLChar.isLowSurrogate(ch2) || 
+                     !isXML11NCNameStart(XMLChar.supplemental(ch, ch2)) ) {
+                    return false;
+                }
+                i = 2;
+            }
+            else {
+                return false;
+            }
+        }
+        while (i < length) {
+            ch = ncName.charAt(i);
+            if ( !isXML11NCName(ch) ) {
+                if ( ++i < length && isXML11NameHighSurrogate(ch) ) {
+                    char ch2 = ncName.charAt(i);
+                    if ( !XMLChar.isLowSurrogate(ch2) || 
+                         !isXML11NCName(XMLChar.supplemental(ch, ch2)) ) {
+                        return false;
+                    }
+                }
+                else {
+                    return false;
+                }
+            }
+            ++i;
+        }
+        return true;
+    } // isXML11ValidNCName(String):boolean
+
+    /*
+     * [7] Nmtoken ::= (NameChar)+
+     */
+    /**
+     * Check to see if a string is a valid Nmtoken according to [7]
+     * in the XML 1.1 Recommendation
+     *
+     * @param nmtoken string to check
+     * @return true if nmtoken is a valid Nmtoken 
+     */
+    public static boolean isXML11ValidNmtoken(String nmtoken) {
+        int length = nmtoken.length();
+        if (length == 0)
+            return false;
+        for (int i = 0; i < length; ++i ) {
+            char ch = nmtoken.charAt(i);
+            if( !isXML11Name(ch) ) {
+                if ( ++i < length && isXML11NameHighSurrogate(ch) ) {
+                    char ch2 = nmtoken.charAt(i);
+                    if ( !XMLChar.isLowSurrogate(ch2) || 
+                         !isXML11Name(XMLChar.supplemental(ch, ch2)) ) {
+                        return false;
+                    }
+                }
+                else {
+                    return false;
+                }
+            }
+        }
+        return true;
+    } // isXML11ValidName(String):boolean
+    
+    /**
+      * Simple check to determine if qname is legal. If it returns false
+      * then <param>str</param> is illegal; if it returns true then 
+      * <param>str</param> is legal.
+      */
+     public static boolean isXML11ValidQName(String str) {
+
+        final int colon = str.indexOf(':');
+
+        if (colon == 0 || colon == str.length() - 1) {
+            return false;
+        }
+       
+        if (colon > 0) {
+            final String prefix = str.substring(0,colon);
+            final String localPart = str.substring(colon+1);
+            return isXML11ValidNCName(prefix) && isXML11ValidNCName(localPart);
+        }
+        else {
+            return isXML11ValidNCName(str);
+        }
+     }
+
+} // class XML11Char
+
diff --git a/src/main/java/org/apache/xml/utils/XMLChar.java b/src/main/java/org/apache/xml/utils/XMLChar.java
new file mode 100644
index 0000000..779a4c0
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/XMLChar.java
@@ -0,0 +1,668 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xml.utils;
+
+/**
+ * This class defines the basic XML character properties. The data
+ * in this class can be used to verify that a character is a valid
+ * XML character or if the character is a space, name start, or name
+ * character.
+ * <p>
+ * A series of convenience methods are supplied to ease the burden
+ * of the developer. Because inlining the checks can improve per
+ * character performance, the tables of character properties are
+ * public. Using the character as an index into the <code>CHARS</code>
+ * array and applying the appropriate mask flag (e.g.
+ * <code>MASK_VALID</code>), yields the same results as calling the
+ * convenience methods. There is one exception: check the comments
+ * for the <code>isValid</code> method for details.
+ *
+ * @author Glenn Marcy, IBM
+ * @author Andy Clark, IBM
+ * @author Eric Ye, IBM
+ * @author Arnaud  Le Hors, IBM
+ * @author Rahul Srivastava, Sun Microsystems Inc.
+ *
+ * @version $Id: XMLChar.java,v 1.7 2002/01/29 01:15:18 lehors Exp $
+ */
+public class XMLChar {
+
+    //
+    // Constants
+    //
+
+    /** Character flags. */
+    private static final byte[] CHARS = new byte[1 << 16];
+
+    /** Valid character mask. */
+    public static final int MASK_VALID = 0x01;
+
+    /** Space character mask. */
+    public static final int MASK_SPACE = 0x02;
+
+    /** Name start character mask. */
+    public static final int MASK_NAME_START = 0x04;
+
+    /** Name character mask. */
+    public static final int MASK_NAME = 0x08;
+
+    /** Pubid character mask. */
+    public static final int MASK_PUBID = 0x10;
+    
+    /** 
+     * Content character mask. Special characters are those that can
+     * be considered the start of markup, such as '&lt;' and '&amp;'. 
+     * The various newline characters are considered special as well.
+     * All other valid XML characters can be considered content.
+     * <p>
+     * This is an optimization for the inner loop of character scanning.
+     */
+    public static final int MASK_CONTENT = 0x20;
+
+    /** NCName start character mask. */
+    public static final int MASK_NCNAME_START = 0x40;
+
+    /** NCName character mask. */
+    public static final int MASK_NCNAME = 0x80;
+
+    //
+    // Static initialization
+    //
+
+    static {
+        
+        //
+        // [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] |
+        //              [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+        //
+
+        int charRange[] = { 
+            0x0009, 0x000A, 0x000D, 0x000D, 0x0020, 0xD7FF, 0xE000, 0xFFFD,
+        };
+
+        //
+        // [3] S ::= (#x20 | #x9 | #xD | #xA)+
+        //
+
+        int spaceChar[] = { 
+            0x0020, 0x0009, 0x000D, 0x000A,
+        };
+
+        //
+        // [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
+        //                  CombiningChar | Extender
+        //
+
+        int nameChar[] = { 
+            0x002D, 0x002E, // '-' and '.'
+        };
+
+        //
+        // [5] Name ::= (Letter | '_' | ':') (NameChar)*
+        //
+
+        int nameStartChar[] = { 
+            0x003A, 0x005F, // ':' and '_'
+        };
+
+        //
+        // [13] PubidChar ::= #x20 | 0xD | 0xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
+        //
+
+        int pubidChar[] = {
+            0x000A, 0x000D, 0x0020, 0x0021, 0x0023, 0x0024, 0x0025, 0x003D,
+            0x005F
+        };
+
+        int pubidRange[] = {
+            0x0027, 0x003B, 0x003F, 0x005A, 0x0061, 0x007A
+        };
+
+        //
+        // [84] Letter ::= BaseChar | Ideographic
+        //
+
+        int letterRange[] = {
+            // BaseChar
+            0x0041, 0x005A, 0x0061, 0x007A, 0x00C0, 0x00D6, 0x00D8, 0x00F6,
+            0x00F8, 0x0131, 0x0134, 0x013E, 0x0141, 0x0148, 0x014A, 0x017E,
+            0x0180, 0x01C3, 0x01CD, 0x01F0, 0x01F4, 0x01F5, 0x01FA, 0x0217,
+            0x0250, 0x02A8, 0x02BB, 0x02C1, 0x0388, 0x038A, 0x038E, 0x03A1,
+            0x03A3, 0x03CE, 0x03D0, 0x03D6, 0x03E2, 0x03F3, 0x0401, 0x040C,
+            0x040E, 0x044F, 0x0451, 0x045C, 0x045E, 0x0481, 0x0490, 0x04C4,
+            0x04C7, 0x04C8, 0x04CB, 0x04CC, 0x04D0, 0x04EB, 0x04EE, 0x04F5,
+            0x04F8, 0x04F9, 0x0531, 0x0556, 0x0561, 0x0586, 0x05D0, 0x05EA,
+            0x05F0, 0x05F2, 0x0621, 0x063A, 0x0641, 0x064A, 0x0671, 0x06B7,
+            0x06BA, 0x06BE, 0x06C0, 0x06CE, 0x06D0, 0x06D3, 0x06E5, 0x06E6,
+            0x0905, 0x0939, 0x0958, 0x0961, 0x0985, 0x098C, 0x098F, 0x0990,
+            0x0993, 0x09A8, 0x09AA, 0x09B0, 0x09B6, 0x09B9, 0x09DC, 0x09DD,
+            0x09DF, 0x09E1, 0x09F0, 0x09F1, 0x0A05, 0x0A0A, 0x0A0F, 0x0A10,
+            0x0A13, 0x0A28, 0x0A2A, 0x0A30, 0x0A32, 0x0A33, 0x0A35, 0x0A36,
+            0x0A38, 0x0A39, 0x0A59, 0x0A5C, 0x0A72, 0x0A74, 0x0A85, 0x0A8B,
+            0x0A8F, 0x0A91, 0x0A93, 0x0AA8, 0x0AAA, 0x0AB0, 0x0AB2, 0x0AB3,
+            0x0AB5, 0x0AB9, 0x0B05, 0x0B0C, 0x0B0F, 0x0B10, 0x0B13, 0x0B28,
+            0x0B2A, 0x0B30, 0x0B32, 0x0B33, 0x0B36, 0x0B39, 0x0B5C, 0x0B5D,
+            0x0B5F, 0x0B61, 0x0B85, 0x0B8A, 0x0B8E, 0x0B90, 0x0B92, 0x0B95,
+            0x0B99, 0x0B9A, 0x0B9E, 0x0B9F, 0x0BA3, 0x0BA4, 0x0BA8, 0x0BAA,
+            0x0BAE, 0x0BB5, 0x0BB7, 0x0BB9, 0x0C05, 0x0C0C, 0x0C0E, 0x0C10,
+            0x0C12, 0x0C28, 0x0C2A, 0x0C33, 0x0C35, 0x0C39, 0x0C60, 0x0C61,
+            0x0C85, 0x0C8C, 0x0C8E, 0x0C90, 0x0C92, 0x0CA8, 0x0CAA, 0x0CB3,
+            0x0CB5, 0x0CB9, 0x0CE0, 0x0CE1, 0x0D05, 0x0D0C, 0x0D0E, 0x0D10,
+            0x0D12, 0x0D28, 0x0D2A, 0x0D39, 0x0D60, 0x0D61, 0x0E01, 0x0E2E,
+            0x0E32, 0x0E33, 0x0E40, 0x0E45, 0x0E81, 0x0E82, 0x0E87, 0x0E88,
+            0x0E94, 0x0E97, 0x0E99, 0x0E9F, 0x0EA1, 0x0EA3, 0x0EAA, 0x0EAB,
+            0x0EAD, 0x0EAE, 0x0EB2, 0x0EB3, 0x0EC0, 0x0EC4, 0x0F40, 0x0F47,
+            0x0F49, 0x0F69, 0x10A0, 0x10C5, 0x10D0, 0x10F6, 0x1102, 0x1103,
+            0x1105, 0x1107, 0x110B, 0x110C, 0x110E, 0x1112, 0x1154, 0x1155,
+            0x115F, 0x1161, 0x116D, 0x116E, 0x1172, 0x1173, 0x11AE, 0x11AF,
+            0x11B7, 0x11B8, 0x11BC, 0x11C2, 0x1E00, 0x1E9B, 0x1EA0, 0x1EF9,
+            0x1F00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, 0x1F48, 0x1F4D,
+            0x1F50, 0x1F57, 0x1F5F, 0x1F7D, 0x1F80, 0x1FB4, 0x1FB6, 0x1FBC,
+            0x1FC2, 0x1FC4, 0x1FC6, 0x1FCC, 0x1FD0, 0x1FD3, 0x1FD6, 0x1FDB,
+            0x1FE0, 0x1FEC, 0x1FF2, 0x1FF4, 0x1FF6, 0x1FFC, 0x212A, 0x212B,
+            0x2180, 0x2182, 0x3041, 0x3094, 0x30A1, 0x30FA, 0x3105, 0x312C,
+            0xAC00, 0xD7A3,
+            // Ideographic
+            0x3021, 0x3029, 0x4E00, 0x9FA5,
+        };
+        int letterChar[] = {
+            // BaseChar
+            0x0386, 0x038C, 0x03DA, 0x03DC, 0x03DE, 0x03E0, 0x0559, 0x06D5,
+            0x093D, 0x09B2, 0x0A5E, 0x0A8D, 0x0ABD, 0x0AE0, 0x0B3D, 0x0B9C,
+            0x0CDE, 0x0E30, 0x0E84, 0x0E8A, 0x0E8D, 0x0EA5, 0x0EA7, 0x0EB0,
+            0x0EBD, 0x1100, 0x1109, 0x113C, 0x113E, 0x1140, 0x114C, 0x114E,
+            0x1150, 0x1159, 0x1163, 0x1165, 0x1167, 0x1169, 0x1175, 0x119E,
+            0x11A8, 0x11AB, 0x11BA, 0x11EB, 0x11F0, 0x11F9, 0x1F59, 0x1F5B,
+            0x1F5D, 0x1FBE, 0x2126, 0x212E,
+            // Ideographic
+            0x3007,
+        };
+
+        //
+        // [87] CombiningChar ::= ...
+        //
+
+        int combiningCharRange[] = {
+            0x0300, 0x0345, 0x0360, 0x0361, 0x0483, 0x0486, 0x0591, 0x05A1,
+            0x05A3, 0x05B9, 0x05BB, 0x05BD, 0x05C1, 0x05C2, 0x064B, 0x0652,
+            0x06D6, 0x06DC, 0x06DD, 0x06DF, 0x06E0, 0x06E4, 0x06E7, 0x06E8,
+            0x06EA, 0x06ED, 0x0901, 0x0903, 0x093E, 0x094C, 0x0951, 0x0954,
+            0x0962, 0x0963, 0x0981, 0x0983, 0x09C0, 0x09C4, 0x09C7, 0x09C8,
+            0x09CB, 0x09CD, 0x09E2, 0x09E3, 0x0A40, 0x0A42, 0x0A47, 0x0A48,
+            0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A83, 0x0ABE, 0x0AC5,
+            0x0AC7, 0x0AC9, 0x0ACB, 0x0ACD, 0x0B01, 0x0B03, 0x0B3E, 0x0B43,
+            0x0B47, 0x0B48, 0x0B4B, 0x0B4D, 0x0B56, 0x0B57, 0x0B82, 0x0B83,
+            0x0BBE, 0x0BC2, 0x0BC6, 0x0BC8, 0x0BCA, 0x0BCD, 0x0C01, 0x0C03,
+            0x0C3E, 0x0C44, 0x0C46, 0x0C48, 0x0C4A, 0x0C4D, 0x0C55, 0x0C56,
+            0x0C82, 0x0C83, 0x0CBE, 0x0CC4, 0x0CC6, 0x0CC8, 0x0CCA, 0x0CCD,
+            0x0CD5, 0x0CD6, 0x0D02, 0x0D03, 0x0D3E, 0x0D43, 0x0D46, 0x0D48,
+            0x0D4A, 0x0D4D, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB4, 0x0EB9,
+            0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19, 0x0F71, 0x0F84,
+            0x0F86, 0x0F8B, 0x0F90, 0x0F95, 0x0F99, 0x0FAD, 0x0FB1, 0x0FB7,
+            0x20D0, 0x20DC, 0x302A, 0x302F,
+        };
+
+        int combiningCharChar[] = {
+            0x05BF, 0x05C4, 0x0670, 0x093C, 0x094D, 0x09BC, 0x09BE, 0x09BF,
+            0x09D7, 0x0A02, 0x0A3C, 0x0A3E, 0x0A3F, 0x0ABC, 0x0B3C, 0x0BD7,
+            0x0D57, 0x0E31, 0x0EB1, 0x0F35, 0x0F37, 0x0F39, 0x0F3E, 0x0F3F,
+            0x0F97, 0x0FB9, 0x20E1, 0x3099, 0x309A,
+        };
+
+        //
+        // [88] Digit ::= ...
+        //
+
+        int digitRange[] = {
+            0x0030, 0x0039, 0x0660, 0x0669, 0x06F0, 0x06F9, 0x0966, 0x096F,
+            0x09E6, 0x09EF, 0x0A66, 0x0A6F, 0x0AE6, 0x0AEF, 0x0B66, 0x0B6F,
+            0x0BE7, 0x0BEF, 0x0C66, 0x0C6F, 0x0CE6, 0x0CEF, 0x0D66, 0x0D6F,
+            0x0E50, 0x0E59, 0x0ED0, 0x0ED9, 0x0F20, 0x0F29,
+        };
+
+        //
+        // [89] Extender ::= ...
+        //
+
+        int extenderRange[] = {
+            0x3031, 0x3035, 0x309D, 0x309E, 0x30FC, 0x30FE,
+        };
+
+        int extenderChar[] = {
+            0x00B7, 0x02D0, 0x02D1, 0x0387, 0x0640, 0x0E46, 0x0EC6, 0x3005,
+        };
+
+        //
+        // SpecialChar ::= '<', '&', '\n', '\r', ']'
+        //
+
+        int specialChar[] = {
+            '<', '&', '\n', '\r', ']',
+        };
+
+        //
+        // Initialize
+        //
+
+        // set valid characters
+        for (int i = 0; i < charRange.length; i += 2) {
+            for (int j = charRange[i]; j <= charRange[i + 1]; j++) {
+                CHARS[j] |= MASK_VALID | MASK_CONTENT;
+            }
+        }
+
+        // remove special characters
+        for (int i = 0; i < specialChar.length; i++) {
+            CHARS[specialChar[i]] = (byte)(CHARS[specialChar[i]] & ~MASK_CONTENT);
+        }
+
+        // set space characters
+        for (int i = 0; i < spaceChar.length; i++) {
+            CHARS[spaceChar[i]] |= MASK_SPACE;
+        }
+
+        // set name start characters
+        for (int i = 0; i < nameStartChar.length; i++) {
+            CHARS[nameStartChar[i]] |= MASK_NAME_START | MASK_NAME | 
+                                       MASK_NCNAME_START | MASK_NCNAME;
+        }
+        for (int i = 0; i < letterRange.length; i += 2) {
+            for (int j = letterRange[i]; j <= letterRange[i + 1]; j++) {
+                CHARS[j] |= MASK_NAME_START | MASK_NAME |
+                            MASK_NCNAME_START | MASK_NCNAME;
+            }
+        }
+        for (int i = 0; i < letterChar.length; i++) {
+            CHARS[letterChar[i]] |= MASK_NAME_START | MASK_NAME |
+                                    MASK_NCNAME_START | MASK_NCNAME;
+        }
+
+        // set name characters
+        for (int i = 0; i < nameChar.length; i++) {
+            CHARS[nameChar[i]] |= MASK_NAME | MASK_NCNAME;
+        }
+        for (int i = 0; i < digitRange.length; i += 2) {
+            for (int j = digitRange[i]; j <= digitRange[i + 1]; j++) {
+                CHARS[j] |= MASK_NAME | MASK_NCNAME;
+            }
+        }
+        for (int i = 0; i < combiningCharRange.length; i += 2) {
+            for (int j = combiningCharRange[i]; j <= combiningCharRange[i + 1]; j++) {
+                CHARS[j] |= MASK_NAME | MASK_NCNAME;
+            }
+        }
+        for (int i = 0; i < combiningCharChar.length; i++) {
+            CHARS[combiningCharChar[i]] |= MASK_NAME | MASK_NCNAME;
+        }
+        for (int i = 0; i < extenderRange.length; i += 2) {
+            for (int j = extenderRange[i]; j <= extenderRange[i + 1]; j++) {
+                CHARS[j] |= MASK_NAME | MASK_NCNAME;
+            }
+        }
+        for (int i = 0; i < extenderChar.length; i++) {
+            CHARS[extenderChar[i]] |= MASK_NAME | MASK_NCNAME;
+        }
+
+        // remove ':' from allowable MASK_NCNAME_START and MASK_NCNAME chars
+        CHARS[':'] &= ~(MASK_NCNAME_START | MASK_NCNAME);
+
+        // set Pubid characters
+        for (int i = 0; i < pubidChar.length; i++) {
+            CHARS[pubidChar[i]] |= MASK_PUBID;
+        }
+        for (int i = 0; i < pubidRange.length; i += 2) {
+            for (int j = pubidRange[i]; j <= pubidRange[i + 1]; j++) {
+                CHARS[j] |= MASK_PUBID;
+            }
+        }
+
+    } // <clinit>()
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Returns true if the specified character is a supplemental character.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isSupplemental(int c) {
+        return (c >= 0x10000 && c <= 0x10FFFF);
+    }
+
+    /**
+     * Returns true the supplemental character corresponding to the given
+     * surrogates.
+     *
+     * @param h The high surrogate.
+     * @param l The low surrogate.
+     */
+    public static int supplemental(char h, char l) {
+        return (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000;
+    }
+
+    /**
+     * Returns the high surrogate of a supplemental character
+     *
+     * @param c The supplemental character to "split".
+     */
+    public static char highSurrogate(int c) {
+        return (char) (((c - 0x00010000) >> 10) + 0xD800);
+    }
+
+    /**
+     * Returns the low surrogate of a supplemental character
+     *
+     * @param c The supplemental character to "split".
+     */
+    public static char lowSurrogate(int c) {
+        return (char) (((c - 0x00010000) & 0x3FF) + 0xDC00);
+    }
+
+    /**
+     * Returns whether the given character is a high surrogate
+     *
+     * @param c The character to check.
+     */
+    public static boolean isHighSurrogate(int c) {
+        return (0xD800 <= c && c <= 0xDBFF);
+    }
+
+    /**
+     * Returns whether the given character is a low surrogate
+     *
+     * @param c The character to check.
+     */
+    public static boolean isLowSurrogate(int c) {
+        return (0xDC00 <= c && c <= 0xDFFF);
+    }
+
+
+    /**
+     * Returns true if the specified character is valid. This method
+     * also checks the surrogate character range from 0x10000 to 0x10FFFF.
+     * <p>
+     * If the program chooses to apply the mask directly to the
+     * <code>CHARS</code> array, then they are responsible for checking
+     * the surrogate character range.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isValid(int c) {
+        return (c < 0x10000 && (CHARS[c] & MASK_VALID) != 0) ||
+               (0x10000 <= c && c <= 0x10FFFF);
+    } // isValid(int):boolean
+
+    /**
+     * Returns true if the specified character is invalid.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isInvalid(int c) {
+        return !isValid(c);
+    } // isInvalid(int):boolean
+
+    /**
+     * Returns true if the specified character can be considered content.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isContent(int c) {
+        return (c < 0x10000 && (CHARS[c] & MASK_CONTENT) != 0) ||
+               (0x10000 <= c && c <= 0x10FFFF);
+    } // isContent(int):boolean
+
+    /**
+     * Returns true if the specified character can be considered markup.
+     * Markup characters include '&lt;', '&amp;', and '%'.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isMarkup(int c) {
+        return c == '<' || c == '&' || c == '%';
+    } // isMarkup(int):boolean
+
+    /**
+     * Returns true if the specified character is a space character
+     * as defined by production [3] in the XML 1.0 specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isSpace(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_SPACE) != 0;
+    } // isSpace(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid name start
+     * character as defined by production [5] in the XML 1.0
+     * specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isNameStart(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_NAME_START) != 0;
+    } // isNameStart(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid name
+     * character as defined by production [4] in the XML 1.0
+     * specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isName(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_NAME) != 0;
+    } // isName(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid NCName start
+     * character as defined by production [4] in Namespaces in XML
+     * recommendation.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isNCNameStart(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_NCNAME_START) != 0;
+    } // isNCNameStart(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid NCName
+     * character as defined by production [5] in Namespaces in XML
+     * recommendation.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isNCName(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_NCNAME) != 0;
+    } // isNCName(int):boolean
+
+    /**
+     * Returns true if the specified character is a valid Pubid
+     * character as defined by production [13] in the XML 1.0
+     * specification.
+     *
+     * @param c The character to check.
+     */
+    public static boolean isPubid(int c) {
+        return c < 0x10000 && (CHARS[c] & MASK_PUBID) != 0;
+    } // isPubid(int):boolean
+
+    /*
+     * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+     */
+    /**
+     * Check to see if a string is a valid Name according to [5]
+     * in the XML 1.0 Recommendation
+     *
+     * @param name string to check
+     * @return true if name is a valid Name
+     */
+    public static boolean isValidName(String name) {
+        if (name.length() == 0)
+            return false;
+        char ch = name.charAt(0);
+        if( isNameStart(ch) == false)
+           return false;
+        for (int i = 1; i < name.length(); i++ ) {
+           ch = name.charAt(i);
+           if( isName( ch ) == false ){
+              return false;
+           }
+        }
+        return true;
+    } // isValidName(String):boolean
+    
+
+    /*
+     * from the namespace rec
+     * [4] NCName ::= (Letter | '_') (NCNameChar)*
+     */
+    /**
+     * Check to see if a string is a valid NCName according to [4]
+     * from the XML Namespaces 1.0 Recommendation
+     *
+     * @param ncName string to check
+     * @return true if name is a valid NCName
+     */
+    public static boolean isValidNCName(String ncName) {
+        if (ncName.length() == 0)
+            return false;
+        char ch = ncName.charAt(0);
+        if( isNCNameStart(ch) == false)
+           return false;
+        for (int i = 1; i < ncName.length(); i++ ) {
+           ch = ncName.charAt(i);
+           if( isNCName( ch ) == false ){
+              return false;
+           }
+        }
+        return true;
+    } // isValidNCName(String):boolean
+
+    /*
+     * [7] Nmtoken ::= (NameChar)+
+     */
+    /**
+     * Check to see if a string is a valid Nmtoken according to [7]
+     * in the XML 1.0 Recommendation
+     *
+     * @param nmtoken string to check
+     * @return true if nmtoken is a valid Nmtoken 
+     */
+    public static boolean isValidNmtoken(String nmtoken) {
+        if (nmtoken.length() == 0)
+            return false;
+        for (int i = 0; i < nmtoken.length(); i++ ) {
+           char ch = nmtoken.charAt(i);
+           if(  ! isName( ch ) ){
+              return false;
+           }
+        }
+        return true;
+    } // isValidName(String):boolean
+
+
+
+
+
+    // encodings
+
+    /**
+     * Returns true if the encoding name is a valid IANA encoding.
+     * This method does not verify that there is a decoder available
+     * for this encoding, only that the characters are valid for an
+     * IANA encoding name.
+     *
+     * @param ianaEncoding The IANA encoding name.
+     */
+    public static boolean isValidIANAEncoding(String ianaEncoding) {
+        if (ianaEncoding != null) {
+            int length = ianaEncoding.length();
+            if (length > 0) {
+                char c = ianaEncoding.charAt(0);
+                if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
+                    for (int i = 1; i < length; i++) {
+                        c = ianaEncoding.charAt(i);
+                        if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&
+                            (c < '0' || c > '9') && c != '.' && c != '_' &&
+                            c != '-') {
+                            return false;
+                        }
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    } // isValidIANAEncoding(String):boolean
+
+    /**
+     * Returns true if the encoding name is a valid Java encoding.
+     * This method does not verify that there is a decoder available
+     * for this encoding, only that the characters are valid for an
+     * Java encoding name.
+     *
+     * @param javaEncoding The Java encoding name.
+     */
+    public static boolean isValidJavaEncoding(String javaEncoding) {
+        if (javaEncoding != null) {
+            int length = javaEncoding.length();
+            if (length > 0) {
+                for (int i = 1; i < length; i++) {
+                    char c = javaEncoding.charAt(i);
+                    if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&
+                        (c < '0' || c > '9') && c != '.' && c != '_' &&
+                        c != '-') {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    } // isValidIANAEncoding(String):boolean
+    
+   /**
+     * Simple check to determine if qname is legal. If it returns false
+     * then <param>str</param> is illegal; if it returns true then 
+     * <param>str</param> is legal.
+     */
+    public static boolean isValidQName(String str) {
+       
+       final int colon = str.indexOf(':');
+       
+       if (colon == 0 || colon == str.length() - 1) {
+           return false;
+       }       
+       
+       if (colon > 0) {
+           final String prefix = str.substring(0,colon);
+           final String localPart = str.substring(colon+1);
+           return isValidNCName(prefix) && isValidNCName(localPart);
+       }
+       else {
+           return isValidNCName(str);
+       }       
+    }      
+
+} // class XMLChar
diff --git a/src/main/java/org/apache/xml/utils/XMLCharacterRecognizer.java b/src/main/java/org/apache/xml/utils/XMLCharacterRecognizer.java
new file mode 100644
index 0000000..9d879cb
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/XMLCharacterRecognizer.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLCharacterRecognizer.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * Class used to verify whether the specified <var>ch</var> 
+ * conforms to the XML 1.0 definition of whitespace. 
+ * @xsl.usage internal
+ */
+public class XMLCharacterRecognizer
+{
+
+  /**
+   * Returns whether the specified <var>ch</var> conforms to the XML 1.0 definition
+   * of whitespace.  Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S">
+   * the definition of <CODE>S</CODE></A> for details.
+   * @param ch Character to check as XML whitespace.
+   * @return =true if <var>ch</var> is XML whitespace; otherwise =false.
+   */
+  public static boolean isWhiteSpace(char ch)
+  {
+    return (ch == 0x20) || (ch == 0x09) || (ch == 0xD) || (ch == 0xA);
+  }
+
+  /**
+   * Tell if the string is whitespace.
+   *
+   * @param ch Character array to check as XML whitespace.
+   * @param start Start index of characters in the array
+   * @param length Number of characters in the array 
+   * @return True if the characters in the array are 
+   * XML whitespace; otherwise, false.
+   */
+  public static boolean isWhiteSpace(char ch[], int start, int length)
+  {
+
+    int end = start + length;
+
+    for (int s = start; s < end; s++)
+    {
+      if (!isWhiteSpace(ch[s]))
+        return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Tell if the string is whitespace.
+   *
+   * @param buf StringBuffer to check as XML whitespace.
+   * @return True if characters in buffer are XML whitespace, false otherwise
+   */
+  public static boolean isWhiteSpace(StringBuffer buf)
+  {
+
+    int n = buf.length();
+
+    for (int i = 0; i < n; i++)
+    {
+      if (!isWhiteSpace(buf.charAt(i)))
+        return false;
+    }
+
+    return true;
+  }
+  
+  /**
+   * Tell if the string is whitespace.
+   *
+   * @param s String to check as XML whitespace.
+   * @return True if characters in buffer are XML whitespace, false otherwise
+   */
+  public static boolean isWhiteSpace(String s)
+  {
+
+    if(null != s)
+    {
+      int n = s.length();
+  
+      for (int i = 0; i < n; i++)
+      {
+        if (!isWhiteSpace(s.charAt(i)))
+          return false;
+      }
+    }
+
+    return true;
+  }
+
+}
diff --git a/src/main/java/org/apache/xml/utils/XMLReaderManager.java b/src/main/java/org/apache/xml/utils/XMLReaderManager.java
new file mode 100644
index 0000000..8cc2d33
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/XMLReaderManager.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLReaderManager.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+import java.util.Hashtable;
+
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+import org.xml.sax.SAXException;
+
+/**
+ * Creates XMLReader objects and caches them for re-use.
+ * This class follows the singleton pattern.
+ */
+public class XMLReaderManager {
+
+    private static final String NAMESPACES_FEATURE =
+                             "http://xml.org/sax/features/namespaces";
+    private static final String NAMESPACE_PREFIXES_FEATURE =
+                             "http://xml.org/sax/features/namespace-prefixes";
+    private static final XMLReaderManager m_singletonManager =
+                                                     new XMLReaderManager();
+
+    /**
+     * Parser factory to be used to construct XMLReader objects
+     */
+    private static SAXParserFactory m_parserFactory;
+
+    /**
+     * Cache of XMLReader objects
+     */
+    private ThreadLocal m_readers;
+
+    /**
+     * Keeps track of whether an XMLReader object is in use.
+     */
+    private Hashtable m_inUse;
+
+    /**
+     * Hidden constructor
+     */
+    private XMLReaderManager() {
+    }
+
+    /**
+     * Retrieves the singleton reader manager
+     */
+    public static XMLReaderManager getInstance() {
+        return m_singletonManager;
+    }
+
+    /**
+     * Retrieves a cached XMLReader for this thread, or creates a new
+     * XMLReader, if the existing reader is in use.  When the caller no
+     * longer needs the reader, it must release it with a call to
+     * {@link #releaseXMLReader}.
+     */
+    public synchronized XMLReader getXMLReader() throws SAXException {
+        XMLReader reader;
+        boolean readerInUse;
+
+        if (m_readers == null) {
+            // When the m_readers.get() method is called for the first time
+            // on a thread, a new XMLReader will automatically be created.
+            m_readers = new ThreadLocal();
+        }
+
+        if (m_inUse == null) {
+            m_inUse = new Hashtable();
+        }
+
+        // If the cached reader for this thread is in use, construct a new
+        // one; otherwise, return the cached reader.
+        reader = (XMLReader) m_readers.get();
+        boolean threadHasReader = (reader != null);
+        if (!threadHasReader || m_inUse.get(reader) == Boolean.TRUE) {
+            try {
+                try {
+                    // According to JAXP 1.2 specification, if a SAXSource
+                    // is created using a SAX InputSource the Transformer or
+                    // TransformerFactory creates a reader via the
+                    // XMLReaderFactory if setXMLReader is not used
+                    reader = XMLReaderFactory.createXMLReader();
+                } catch (Exception e) {
+                   try {
+                        // If unable to create an instance, let's try to use
+                        // the XMLReader from JAXP
+                        if (m_parserFactory == null) {
+                            m_parserFactory = SAXParserFactory.newInstance();
+                            m_parserFactory.setNamespaceAware(true);
+                        }
+
+                        reader = m_parserFactory.newSAXParser().getXMLReader();
+                   } catch (ParserConfigurationException pce) {
+                       throw pce;   // pass along pce
+                   }
+                }
+                try {
+                    reader.setFeature(NAMESPACES_FEATURE, true);
+                    reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false);
+                } catch (SAXException se) {
+                    // Try to carry on if we've got a parser that
+                    // doesn't know about namespace prefixes.
+                }
+            } catch (ParserConfigurationException ex) {
+                throw new SAXException(ex);
+            } catch (FactoryConfigurationError ex1) {
+                throw new SAXException(ex1.toString());
+            } catch (NoSuchMethodError ex2) {
+            } catch (AbstractMethodError ame) {
+            }
+
+            // Cache the XMLReader if this is the first time we've created
+            // a reader for this thread.
+            if (!threadHasReader) {
+                m_readers.set(reader);
+                m_inUse.put(reader, Boolean.TRUE);
+            }
+        } else {
+            m_inUse.put(reader, Boolean.TRUE);
+        }
+
+        return reader;
+    }
+
+    /**
+     * Mark the cached XMLReader as available.  If the reader was not
+     * actually in the cache, do nothing.
+     *
+     * @param reader The XMLReader that's being released.
+     */
+    public synchronized void releaseXMLReader(XMLReader reader) {
+        // If the reader that's being released is the cached reader
+        // for this thread, remove it from the m_isUse list.
+        if (m_readers.get() == reader && reader != null) {
+            m_inUse.remove(reader);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/xml/utils/XMLString.java b/src/main/java/org/apache/xml/utils/XMLString.java
new file mode 100644
index 0000000..c968d80
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/XMLString.java
@@ -0,0 +1,689 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLString.java 570109 2007-08-27 13:31:35Z zongaro $
+ */
+package org.apache.xml.utils;
+
+import java.util.Locale;
+
+/**
+ * This class is meant to be an interface to character strings, whether they
+ * be java Strings or <code>org.apache.xml.utils.FastStringBuffer</code>s, or
+ * other character data.  By using XMLString, character copies can be reduced
+ * in the XML pipeline.
+ */
+public interface XMLString
+{
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value. Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public abstract void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
+    throws org.xml.sax.SAXException;
+
+  /**
+   * Directly call the
+   * comment method on the passed LexicalHandler for the
+   * string-value.
+   *
+   * @param lh A non-null reference to a LexicalHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public abstract void dispatchAsComment(org.xml.sax.ext.LexicalHandler lh)
+    throws org.xml.sax.SAXException;
+    
+  /**
+   * Conditionally trim all leading and trailing whitespace in the specified String.
+   * All strings of white space are
+   * replaced by a single space character (#x20), except spaces after punctuation which
+   * receive double spaces if doublePunctuationSpaces is true.
+   * This function may be useful to a formatter, but to get first class
+   * results, the formatter should probably do it's own white space handling
+   * based on the semantics of the formatting object.
+   * 
+   * @param   trimHead    Trim leading whitespace?
+   * @param   trimTail    Trim trailing whitespace?
+   * @param   doublePunctuationSpaces    Use double spaces for punctuation?
+   * @return              The trimmed string.
+   */
+  public XMLString fixWhiteSpace(boolean trimHead,
+                                 boolean trimTail,
+                                 boolean doublePunctuationSpaces);
+
+  /**
+   * Returns the length of this string.
+   *
+   * @return  the length of the sequence of characters represented by this
+   *          object.
+   */
+  public abstract int length();
+
+  /**
+   * Returns the character at the specified index. An index ranges
+   * from <code>0</code> to <code>length() - 1</code>. The first character
+   * of the sequence is at index <code>0</code>, the next at index
+   * <code>1</code>, and so on, as for array indexing.
+   *
+   * @param      index   the index of the character.
+   * @return     the character at the specified index of this string.
+   *             The first character is at index <code>0</code>.
+   * @exception  IndexOutOfBoundsException  if the <code>index</code>
+   *             argument is negative or not less than the length of this
+   *             string.
+   */
+  public abstract char charAt(int index);
+
+  /**
+   * Copies characters from this string into the destination character
+   * array.
+   *
+   * @param      srcBegin   index of the first character in the string
+   *                        to copy.
+   * @param      srcEnd     index after the last character in the string
+   *                        to copy.
+   * @param      dst        the destination array.
+   * @param      dstBegin   the start offset in the destination array.
+   * @exception IndexOutOfBoundsException If any of the following
+   *            is true:
+   *            <ul><li><code>srcBegin</code> is negative.
+   *            <li><code>srcBegin</code> is greater than <code>srcEnd</code>
+   *            <li><code>srcEnd</code> is greater than the length of this
+   *                string
+   *            <li><code>dstBegin</code> is negative
+   *            <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
+   *                <code>dst.length</code></ul>
+   * @exception NullPointerException if <code>dst</code> is <code>null</code>
+   */
+  public abstract void getChars(int srcBegin, int srcEnd, char dst[],
+                                int dstBegin);
+                                
+  /**
+   * Compares this string to the specified object.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is an <code>XMLString</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   anObject   the object to compare this <code>String</code>
+   *                     against.
+   * @return  <code>true</code> if the <code>String </code>are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public abstract boolean equals(XMLString anObject);
+
+  /**
+   * Compares this string to the specified <code>String</code>.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   anotherString   the object to compare this <code>String</code>
+   *                          against.
+   * @return  <code>true</code> if the <code>String</code>s are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public abstract boolean equals(String anotherString);
+
+  /**
+   * Compares this string to the specified object.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   anObject   the object to compare this <code>String</code>
+   *                     against.
+   * @return  <code>true</code> if the <code>String </code>are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public abstract boolean equals(Object anObject);
+
+  /**
+   * Compares this <code>String</code> to another <code>String</code>,
+   * ignoring case considerations.  Two strings are considered equal
+   * ignoring case if they are of the same length, and corresponding
+   * characters in the two strings are equal ignoring case.
+   *
+   * @param   anotherString   the <code>String</code> to compare this
+   *                          <code>String</code> against.
+   * @return  <code>true</code> if the argument is not <code>null</code>
+   *          and the <code>String</code>s are equal,
+   *          ignoring case; <code>false</code> otherwise.
+   * @see     #equals(Object)
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see java.lang.Character#toUpperCase(char)
+   */
+  public abstract boolean equalsIgnoreCase(String anotherString);
+
+  /**
+   * Compares two strings lexicographically.
+   *
+   * @param   anotherString   the <code>String</code> to be compared.
+   * @return  the value <code>0</code> if the argument string is equal to
+   *          this string; a value less than <code>0</code> if this string
+   *          is lexicographically less than the string argument; and a
+   *          value greater than <code>0</code> if this string is
+   *          lexicographically greater than the string argument.
+   * @exception java.lang.NullPointerException if <code>anotherString</code>
+   *          is <code>null</code>.
+   */
+  public abstract int compareTo(XMLString anotherString);
+
+  /**
+   * Compares two strings lexicographically, ignoring case considerations.
+   * This method returns an integer whose sign is that of
+   * <code>this.toUpperCase().toLowerCase().compareTo(
+   * str.toUpperCase().toLowerCase())</code>.
+   * <p>
+   * Note that this method does <em>not</em> take locale into account,
+   * and will result in an unsatisfactory ordering for certain locales.
+   * The java.text package provides <em>collators</em> to allow
+   * locale-sensitive ordering.
+   *
+   * @param   str   the <code>String</code> to be compared.
+   * @return  a negative integer, zero, or a positive integer as the
+   *          the specified String is greater than, equal to, or less
+   *          than this String, ignoring case considerations.
+   * @see     java.text.Collator#compare(String, String)
+   * @since   1.2
+   */
+  public abstract int compareToIgnoreCase(XMLString str);
+
+  /**
+   * Tests if this string starts with the specified prefix beginning
+   * a specified index.
+   *
+   * @param   prefix    the prefix.
+   * @param   toffset   where to begin looking in the string.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the substring of this object starting
+   *          at index <code>toffset</code>; <code>false</code> otherwise.
+   *          The result is <code>false</code> if <code>toffset</code> is
+   *          negative or greater than the length of this
+   *          <code>String</code> object; otherwise the result is the same
+   *          as the result of the expression
+   *          <pre>
+   *          this.subString(toffset).startsWith(prefix)
+   *          </pre>
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public abstract boolean startsWith(String prefix, int toffset);
+
+  /**
+   * Tests if this string starts with the specified prefix beginning
+   * a specified index.
+   *
+   * @param   prefix    the prefix.
+   * @param   toffset   where to begin looking in the string.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the substring of this object starting
+   *          at index <code>toffset</code>; <code>false</code> otherwise.
+   *          The result is <code>false</code> if <code>toffset</code> is
+   *          negative or greater than the length of this
+   *          <code>String</code> object; otherwise the result is the same
+   *          as the result of the expression
+   *          <pre>
+   *          this.subString(toffset).startsWith(prefix)
+   *          </pre>
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public abstract boolean startsWith(XMLString prefix, int toffset);
+
+  /**
+   * Tests if this string starts with the specified prefix.
+   *
+   * @param   prefix   the prefix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the character sequence represented by
+   *          this string; <code>false</code> otherwise.
+   *          Note also that <code>true</code> will be returned if the
+   *          argument is an empty string or is equal to this
+   *          <code>String</code> object as determined by the
+   *          {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   * @since   JDK1. 0
+   */
+  public abstract boolean startsWith(String prefix);
+
+  /**
+   * Tests if this string starts with the specified prefix.
+   *
+   * @param   prefix   the prefix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the character sequence represented by
+   *          this string; <code>false</code> otherwise.
+   *          Note also that <code>true</code> will be returned if the
+   *          argument is an empty string or is equal to this
+   *          <code>String</code> object as determined by the
+   *          {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   * @since   JDK1. 0
+   */
+  public abstract boolean startsWith(XMLString prefix);
+
+  /**
+   * Tests if this string ends with the specified suffix.
+   *
+   * @param   suffix   the suffix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a suffix of the character sequence represented by
+   *          this object; <code>false</code> otherwise. Note that the
+   *          result will be <code>true</code> if the argument is the
+   *          empty string or is equal to this <code>String</code> object
+   *          as determined by the {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>suffix</code> is
+   *          <code>null</code>.
+   */
+  public abstract boolean endsWith(String suffix);
+
+  /**
+   * Returns a hashcode for this string. The hashcode for a
+   * <code>String</code> object is computed as
+   * <blockquote><pre>
+   * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
+   * </pre></blockquote>
+   * using <code>int</code> arithmetic, where <code>s[i]</code> is the
+   * <i>i</i>th character of the string, <code>n</code> is the length of
+   * the string, and <code>^</code> indicates exponentiation.
+   * (The hash value of the empty string is zero.)
+   *
+   * @return  a hash code value for this object.
+   */
+  public abstract int hashCode();
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified character. If a character with value <code>ch</code> occurs
+   * in the character sequence represented by this <code>String</code>
+   * object, then the index of the first such occurrence is returned --
+   * that is, the smallest value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.charAt(<i>k</i>) == ch
+   * </pre></blockquote>
+   * is <code>true</code>. If no such character occurs in this string,
+   * then <code>-1</code> is returned.
+   *
+   * @param   ch   a character.
+   * @return  the index of the first occurrence of the character in the
+   *          character sequence represented by this object, or
+   *          <code>-1</code> if the character does not occur.
+   */
+  public abstract int indexOf(int ch);
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified character, starting the search at the specified index.
+   * <p>
+   * If a character with value <code>ch</code> occurs in the character
+   * sequence represented by this <code>String</code> object at an index
+   * no smaller than <code>fromIndex</code>, then the index of the first
+   * such occurrence is returned--that is, the smallest value <i>k</i>
+   * such that:
+   * <blockquote><pre>
+   * (this.charAt(<i>k</i>) == ch) && (<i>k</i> >= fromIndex)
+   * </pre></blockquote>
+   * is true. If no such character occurs in this string at or after
+   * position <code>fromIndex</code>, then <code>-1</code> is returned.
+   * <p>
+   * There is no restriction on the value of <code>fromIndex</code>. If it
+   * is negative, it has the same effect as if it were zero: this entire
+   * string may be searched. If it is greater than the length of this
+   * string, it has the same effect as if it were equal to the length of
+   * this string: <code>-1</code> is returned.
+   *
+   * @param   ch          a character.
+   * @param   fromIndex   the index to start the search from.
+   * @return  the index of the first occurrence of the character in the
+   *          character sequence represented by this object that is greater
+   *          than or equal to <code>fromIndex</code>, or <code>-1</code>
+   *          if the character does not occur.
+   */
+  public abstract int indexOf(int ch, int fromIndex);
+
+  /**
+   * Returns the index within this string of the last occurrence of the
+   * specified character. That is, the index returned is the largest
+   * value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.charAt(<i>k</i>) == ch
+   * </pre></blockquote>
+   * is true.
+   * The String is searched backwards starting at the last character.
+   *
+   * @param   ch   a character.
+   * @return  the index of the last occurrence of the character in the
+   *          character sequence represented by this object, or
+   *          <code>-1</code> if the character does not occur.
+   */
+  public abstract int lastIndexOf(int ch);
+
+  /**
+   * Returns the index within this string of the last occurrence of the
+   * specified character, searching backward starting at the specified
+   * index. That is, the index returned is the largest value <i>k</i>
+   * such that:
+   * <blockquote><pre>
+   * this.charAt(k) == ch) && (k <= fromIndex)
+   * </pre></blockquote>
+   * is true.
+   *
+   * @param   ch          a character.
+   * @param   fromIndex   the index to start the search from. There is no
+   *          restriction on the value of <code>fromIndex</code>. If it is
+   *          greater than or equal to the length of this string, it has
+   *          the same effect as if it were equal to one less than the
+   *          length of this string: this entire string may be searched.
+   *          If it is negative, it has the same effect as if it were -1:
+   *          -1 is returned.
+   * @return  the index of the last occurrence of the character in the
+   *          character sequence represented by this object that is less
+   *          than or equal to <code>fromIndex</code>, or <code>-1</code>
+   *          if the character does not occur before that point.
+   */
+  public abstract int lastIndexOf(int ch, int fromIndex);
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring. The integer returned is the smallest value
+   * <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   *
+   * @param   str   any string.
+   * @return  if the string argument occurs as a substring within this
+   *          object, then the index of the first character of the first
+   *          such substring is returned; if it does not occur as a
+   *          substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public abstract int indexOf(String str);
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring. The integer returned is the smallest value
+   * <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   *
+   * @param   str   any string.
+   * @return  if the string argument occurs as a substring within this
+   *          object, then the index of the first character of the first
+   *          such substring is returned; if it does not occur as a
+   *          substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public abstract int indexOf(XMLString str);
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring, starting at the specified index. The integer
+   * returned is the smallest value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>) && (<i>k</i> >= fromIndex)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   * <p>
+   * There is no restriction on the value of <code>fromIndex</code>. If
+   * it is negative, it has the same effect as if it were zero: this entire
+   * string may be searched. If it is greater than the length of this
+   * string, it has the same effect as if it were equal to the length of
+   * this string: <code>-1</code> is returned.
+   *
+   * @param   str         the substring to search for.
+   * @param   fromIndex   the index to start the search from.
+   * @return  If the string argument occurs as a substring within this
+   *          object at a starting index no smaller than
+   *          <code>fromIndex</code>, then the index of the first character
+   *          of the first such substring is returned. If it does not occur
+   *          as a substring starting at <code>fromIndex</code> or beyond,
+   *          <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>
+   */
+  public abstract int indexOf(String str, int fromIndex);
+
+  /**
+   * Returns the index within this string of the rightmost occurrence
+   * of the specified substring.  The rightmost empty string "" is
+   * considered to occur at the index value <code>this.length()</code>.
+   * The returned index is the largest value <i>k</i> such that
+   * <blockquote><pre>
+   * this.startsWith(str, k)
+   * </pre></blockquote>
+   * is true.
+   *
+   * @param   str   the substring to search for.
+   * @return  if the string argument occurs one or more times as a substring
+   *          within this object, then the index of the first character of
+   *          the last such substring is returned. If it does not occur as
+   *          a substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException  if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public abstract int lastIndexOf(String str);
+
+  /**
+   * Returns the index within this string of the last occurrence of
+   * the specified substring.
+   *
+   * @param   str         the substring to search for.
+   * @param   fromIndex   the index to start the search from. There is no
+   *          restriction on the value of fromIndex. If it is greater than
+   *          the length of this string, it has the same effect as if it
+   *          were equal to the length of this string: this entire string
+   *          may be searched. If it is negative, it has the same effect
+   *          as if it were -1: -1 is returned.
+   * @return  If the string argument occurs one or more times as a substring
+   *          within this object at a starting index no greater than
+   *          <code>fromIndex</code>, then the index of the first character of
+   *          the last such substring is returned. If it does not occur as a
+   *          substring starting at <code>fromIndex</code> or earlier,
+   *          <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public abstract int lastIndexOf(String str, int fromIndex);
+
+  /**
+   * Returns a new string that is a substring of this string. The
+   * substring begins with the character at the specified index and
+   * extends to the end of this string. <p>
+   * Examples:
+   * <blockquote><pre>
+   * "unhappy".substring(2) returns "happy"
+   * "Harbison".substring(3) returns "bison"
+   * "emptiness".substring(9) returns "" (an empty string)
+   * </pre></blockquote>
+   *
+   * @param      beginIndex   the beginning index, inclusive.
+   * @return     the specified substring.
+   * @exception  IndexOutOfBoundsException  if
+   *             <code>beginIndex</code> is negative or larger than the
+   *             length of this <code>String</code> object.
+   */
+  public abstract XMLString substring(int beginIndex);
+
+  /**
+   * Returns a new string that is a substring of this string. The
+   * substring begins at the specified <code>beginIndex</code> and
+   * extends to the character at index <code>endIndex - 1</code>.
+   * Thus the length of the substring is <code>endIndex-beginIndex</code>.
+   *
+   * @param      beginIndex   the beginning index, inclusive.
+   * @param      endIndex     the ending index, exclusive.
+   * @return     the specified substring.
+   * @exception  IndexOutOfBoundsException  if the
+   *             <code>beginIndex</code> is negative, or
+   *             <code>endIndex</code> is larger than the length of
+   *             this <code>String</code> object, or
+   *             <code>beginIndex</code> is larger than
+   *             <code>endIndex</code>.
+   */
+  public abstract XMLString substring(int beginIndex, int endIndex);
+
+  /**
+   * Concatenates the specified string to the end of this string.
+   *
+   * @param   str   the <code>String</code> that is concatenated to the end
+   *                of this <code>String</code>.
+   * @return  a string that represents the concatenation of this object's
+   *          characters followed by the string argument's characters.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public abstract XMLString concat(String str);
+
+  /**
+   * Converts all of the characters in this <code>String</code> to lower
+   * case using the rules of the given <code>Locale</code>.
+   *
+   * @param locale use the case transformation rules for this locale
+   * @return the String, converted to lowercase.
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see     java.lang.String#toUpperCase(Locale)
+   */
+  public abstract XMLString toLowerCase(Locale locale);
+
+  /**
+   * Converts all of the characters in this <code>String</code> to lower
+   * case using the rules of the default locale, which is returned
+   * by <code>Locale.getDefault</code>.
+   * <p>
+   *
+   * @return  the string, converted to lowercase.
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see     java.lang.String#toLowerCase(Locale)
+   */
+  public abstract XMLString toLowerCase();
+
+  /**
+   * Converts all of the characters in this <code>String</code> to upper
+   * case using the rules of the given locale.
+   * @param locale use the case transformation rules for this locale
+   * @return the String, converted to uppercase.
+   * @see     java.lang.Character#toUpperCase(char)
+   * @see     java.lang.String#toLowerCase(Locale)
+   */
+  public abstract XMLString toUpperCase(Locale locale);
+
+  /**
+   * Converts all of the characters in this <code>String</code> to upper
+   * case using the rules of the default locale, which is returned
+   * by <code>Locale.getDefault</code>.
+   *
+   * <p>
+   * If no character in this string has a different uppercase version,
+   * based on calling the <code>toUpperCase</code> method defined by
+   * <code>Character</code>, then the original string is returned.
+   * <p>
+   * Otherwise, this method creates a new <code>String</code> object
+   * representing a character sequence identical in length to the
+   * character sequence represented by this <code>String</code> object and
+   * with every character equal to the result of applying the method
+   * <code>Character.toUpperCase</code> to the corresponding character of
+   * this <code>String</code> object. <p>
+   * Examples:
+   * <blockquote><pre>
+   * "Fahrvergn&uuml;gen".toUpperCase() returns "FAHRVERGN&Uuml;GEN"
+   * "Visit Ljubinje!".toUpperCase() returns "VISIT LJUBINJE!"
+   * </pre></blockquote>
+   *
+   * @return  the string, converted to uppercase.
+   * @see     java.lang.Character#toUpperCase(char)
+   * @see     java.lang.String#toUpperCase(Locale)
+   */
+  public abstract XMLString toUpperCase();
+
+  /**
+   * Removes white space from both ends of this string.
+   * <p>
+   * If this <code>String</code> object represents an empty character
+   * sequence, or the first and last characters of character sequence
+   * represented by this <code>String</code> object both have codes
+   * greater than <code>'&#92;u0020'</code> (the space character), then a
+   * reference to this <code>String</code> object is returned.
+   * <p>
+   * Otherwise, if there is no character with a code greater than
+   * <code>'&#92;u0020'</code> in the string, then a new
+   * <code>String</code> object representing an empty string is created
+   * and returned.
+   * <p>
+   * Otherwise, let <i>k</i> be the index of the first character in the
+   * string whose code is greater than <code>'&#92;u0020'</code>, and let
+   * <i>m</i> be the index of the last character in the string whose code
+   * is greater than <code>'&#92;u0020'</code>. A new <code>String</code>
+   * object is created, representing the substring of this string that
+   * begins with the character at index <i>k</i> and ends with the
+   * character at index <i>m</i>-that is, the result of
+   * <code>this.substring(<i>k</i>,&nbsp;<i>m</i>+1)</code>.
+   * <p>
+   * This method may be used to trim
+   * {@link Character#isSpace(char) whitespace} from the beginning and end
+   * of a string; in fact, it trims all ASCII control characters as well.
+   *
+   * @return  this string, with white space removed from the front and end.
+   */
+  public abstract XMLString trim();
+
+  /**
+   * This object (which is already a string!) is itself returned.
+   *
+   * @return  the string itself.
+   */
+  public abstract String toString();
+  
+  /**
+   * Tell if this object contains a java String object.
+   * 
+   * @return true if this XMLString can return a string without creating one.
+   */
+  public abstract boolean hasString();
+  
+  /**
+   * Convert a string to a double -- Allowed input is in fixed
+   * notation ddd.fff.
+   *
+   * @return A double value representation of the string, or return Double.NaN 
+   * if the string can not be converted.
+   */
+  public double toDouble();
+}
diff --git a/src/main/java/org/apache/xml/utils/XMLStringDefault.java b/src/main/java/org/apache/xml/utils/XMLStringDefault.java
new file mode 100644
index 0000000..746d25a
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/XMLStringDefault.java
@@ -0,0 +1,818 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLStringDefault.java 570109 2007-08-27 13:31:35Z zongaro $
+ */
+package org.apache.xml.utils;
+
+import java.util.Locale;
+
+/**
+ * The default implementation of the XMLString interface,
+ * which is just a simple wrapper of a String object.
+ */
+public class XMLStringDefault implements XMLString
+{
+
+  private String m_str;
+  
+  /**
+   * Create a XMLStringDefault object from a String
+   */
+  public XMLStringDefault(String str)
+  {
+    m_str = str;
+  }
+  
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value. Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
+    throws org.xml.sax.SAXException
+  {
+  }
+
+  /**
+   * Directly call the
+   * comment method on the passed LexicalHandler for the
+   * string-value.
+   *
+   * @param lh A non-null reference to a LexicalHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchAsComment(org.xml.sax.ext.LexicalHandler lh)
+    throws org.xml.sax.SAXException
+  {
+  }
+    
+  /**
+   * Conditionally trim all leading and trailing whitespace in the specified String.
+   * All strings of white space are
+   * replaced by a single space character (#x20), except spaces after punctuation which
+   * receive double spaces if doublePunctuationSpaces is true.
+   * This function may be useful to a formatter, but to get first class
+   * results, the formatter should probably do it's own white space handling
+   * based on the semantics of the formatting object.
+   * 
+   * @param   trimHead    Trim leading whitespace?
+   * @param   trimTail    Trim trailing whitespace?
+   * @param   doublePunctuationSpaces    Use double spaces for punctuation?
+   * @return              The trimmed string.
+   */
+  public XMLString fixWhiteSpace(boolean trimHead,
+                                 boolean trimTail,
+                                 boolean doublePunctuationSpaces)
+  {
+    return new XMLStringDefault(m_str.trim());
+  }
+
+  /**
+   * Returns the length of this string.
+   *
+   * @return  the length of the sequence of characters represented by this
+   *          object.
+   */
+  public int length()
+  {
+    return m_str.length();
+  }
+
+  /**
+   * Returns the character at the specified index. An index ranges
+   * from <code>0</code> to <code>length() - 1</code>. The first character
+   * of the sequence is at index <code>0</code>, the next at index
+   * <code>1</code>, and so on, as for array indexing.
+   *
+   * @param      index   the index of the character.
+   * @return     the character at the specified index of this string.
+   *             The first character is at index <code>0</code>.
+   * @exception  IndexOutOfBoundsException  if the <code>index</code>
+   *             argument is negative or not less than the length of this
+   *             string.
+   */
+  public char charAt(int index)
+  {
+    return m_str.charAt(index);
+  }
+
+  /**
+   * Copies characters from this string into the destination character
+   * array.
+   *
+   * @param      srcBegin   index of the first character in the string
+   *                        to copy.
+   * @param      srcEnd     index after the last character in the string
+   *                        to copy.
+   * @param      dst        the destination array.
+   * @param      dstBegin   the start offset in the destination array.
+   * @exception IndexOutOfBoundsException If any of the following
+   *            is true:
+   *            <ul><li><code>srcBegin</code> is negative.
+   *            <li><code>srcBegin</code> is greater than <code>srcEnd</code>
+   *            <li><code>srcEnd</code> is greater than the length of this
+   *                string
+   *            <li><code>dstBegin</code> is negative
+   *            <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
+   *                <code>dst.length</code></ul>
+   * @exception NullPointerException if <code>dst</code> is <code>null</code>
+   */
+  public void getChars(int srcBegin, int srcEnd, char dst[],
+                                int dstBegin)
+  {
+    int destIndex = dstBegin;
+    for (int i = srcBegin; i < srcEnd; i++)
+    {
+      dst[destIndex++] = m_str.charAt(i);
+    }
+  }
+
+  /**
+   * Compares this string to the specified <code>String</code>.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   obj2   the object to compare this <code>String</code> against.
+   * @return  <code>true</code> if the <code>String</code>s are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public boolean equals(String obj2) {
+      return m_str.equals(obj2);
+  }
+
+  /**
+   * Compares this string to the specified object.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   anObject   the object to compare this <code>String</code>
+   *                     against.
+   * @return  <code>true</code> if the <code>String </code>are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public boolean equals(XMLString anObject)
+  {
+    return m_str.equals(anObject.toString());
+  }
+
+
+  /**
+   * Compares this string to the specified object.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   anObject   the object to compare this <code>String</code>
+   *                     against.
+   * @return  <code>true</code> if the <code>String </code>are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public boolean equals(Object anObject)
+  {
+    return m_str.equals(anObject);
+  }
+
+  /**
+   * Compares this <code>String</code> to another <code>String</code>,
+   * ignoring case considerations.  Two strings are considered equal
+   * ignoring case if they are of the same length, and corresponding
+   * characters in the two strings are equal ignoring case.
+   *
+   * @param   anotherString   the <code>String</code> to compare this
+   *                          <code>String</code> against.
+   * @return  <code>true</code> if the argument is not <code>null</code>
+   *          and the <code>String</code>s are equal,
+   *          ignoring case; <code>false</code> otherwise.
+   * @see     #equals(Object)
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see java.lang.Character#toUpperCase(char)
+   */
+  public boolean equalsIgnoreCase(String anotherString)
+  {
+    return m_str.equalsIgnoreCase(anotherString);
+  }
+
+  /**
+   * Compares two strings lexicographically.
+   *
+   * @param   anotherString   the <code>String</code> to be compared.
+   * @return  the value <code>0</code> if the argument string is equal to
+   *          this string; a value less than <code>0</code> if this string
+   *          is lexicographically less than the string argument; and a
+   *          value greater than <code>0</code> if this string is
+   *          lexicographically greater than the string argument.
+   * @exception java.lang.NullPointerException if <code>anotherString</code>
+   *          is <code>null</code>.
+   */
+  public int compareTo(XMLString anotherString)
+  {
+    return m_str.compareTo(anotherString.toString());
+  }
+
+  /**
+   * Compares two strings lexicographically, ignoring case considerations.
+   * This method returns an integer whose sign is that of
+   * <code>this.toUpperCase().toLowerCase().compareTo(
+   * str.toUpperCase().toLowerCase())</code>.
+   * <p>
+   * Note that this method does <em>not</em> take locale into account,
+   * and will result in an unsatisfactory ordering for certain locales.
+   * The java.text package provides <em>collators</em> to allow
+   * locale-sensitive ordering.
+   *
+   * @param   str   the <code>String</code> to be compared.
+   * @return  a negative integer, zero, or a positive integer as the
+   *          the specified String is greater than, equal to, or less
+   *          than this String, ignoring case considerations.
+   * @see     java.text.Collator#compare(String, String)
+   * @since   1.2
+   */
+  public int compareToIgnoreCase(XMLString str)
+  {
+    return m_str.compareToIgnoreCase(str.toString());
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix beginning
+   * a specified index.
+   *
+   * @param   prefix    the prefix.
+   * @param   toffset   where to begin looking in the string.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the substring of this object starting
+   *          at index <code>toffset</code>; <code>false</code> otherwise.
+   *          The result is <code>false</code> if <code>toffset</code> is
+   *          negative or greater than the length of this
+   *          <code>String</code> object; otherwise the result is the same
+   *          as the result of the expression
+   *          <pre>
+   *          this.subString(toffset).startsWith(prefix)
+   *          </pre>
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public boolean startsWith(String prefix, int toffset)
+  {
+    return m_str.startsWith(prefix, toffset);
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix beginning
+   * a specified index.
+   *
+   * @param   prefix    the prefix.
+   * @param   toffset   where to begin looking in the string.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the substring of this object starting
+   *          at index <code>toffset</code>; <code>false</code> otherwise.
+   *          The result is <code>false</code> if <code>toffset</code> is
+   *          negative or greater than the length of this
+   *          <code>String</code> object; otherwise the result is the same
+   *          as the result of the expression
+   *          <pre>
+   *          this.subString(toffset).startsWith(prefix)
+   *          </pre>
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public boolean startsWith(XMLString prefix, int toffset)
+  {
+    return m_str.startsWith(prefix.toString(), toffset);
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix.
+   *
+   * @param   prefix   the prefix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the character sequence represented by
+   *          this string; <code>false</code> otherwise.
+   *          Note also that <code>true</code> will be returned if the
+   *          argument is an empty string or is equal to this
+   *          <code>String</code> object as determined by the
+   *          {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   * @since   JDK1. 0
+   */
+  public boolean startsWith(String prefix)
+  {
+    return m_str.startsWith(prefix);
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix.
+   *
+   * @param   prefix   the prefix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the character sequence represented by
+   *          this string; <code>false</code> otherwise.
+   *          Note also that <code>true</code> will be returned if the
+   *          argument is an empty string or is equal to this
+   *          <code>String</code> object as determined by the
+   *          {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   * @since   JDK1. 0
+   */
+  public boolean startsWith(XMLString prefix)
+  {
+    return m_str.startsWith(prefix.toString());
+  }
+
+  /**
+   * Tests if this string ends with the specified suffix.
+   *
+   * @param   suffix   the suffix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a suffix of the character sequence represented by
+   *          this object; <code>false</code> otherwise. Note that the
+   *          result will be <code>true</code> if the argument is the
+   *          empty string or is equal to this <code>String</code> object
+   *          as determined by the {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>suffix</code> is
+   *          <code>null</code>.
+   */
+  public boolean endsWith(String suffix)
+  {
+    return m_str.endsWith(suffix);
+  }
+
+  /**
+   * Returns a hashcode for this string. The hashcode for a
+   * <code>String</code> object is computed as
+   * <blockquote><pre>
+   * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
+   * </pre></blockquote>
+   * using <code>int</code> arithmetic, where <code>s[i]</code> is the
+   * <i>i</i>th character of the string, <code>n</code> is the length of
+   * the string, and <code>^</code> indicates exponentiation.
+   * (The hash value of the empty string is zero.)
+   *
+   * @return  a hash code value for this object.
+   */
+  public int hashCode()
+  {
+    return m_str.hashCode();
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified character. If a character with value <code>ch</code> occurs
+   * in the character sequence represented by this <code>String</code>
+   * object, then the index of the first such occurrence is returned --
+   * that is, the smallest value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.charAt(<i>k</i>) == ch
+   * </pre></blockquote>
+   * is <code>true</code>. If no such character occurs in this string,
+   * then <code>-1</code> is returned.
+   *
+   * @param   ch   a character.
+   * @return  the index of the first occurrence of the character in the
+   *          character sequence represented by this object, or
+   *          <code>-1</code> if the character does not occur.
+   */
+  public int indexOf(int ch)
+  {
+    return m_str.indexOf(ch);
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified character, starting the search at the specified index.
+   * <p>
+   * If a character with value <code>ch</code> occurs in the character
+   * sequence represented by this <code>String</code> object at an index
+   * no smaller than <code>fromIndex</code>, then the index of the first
+   * such occurrence is returned--that is, the smallest value <i>k</i>
+   * such that:
+   * <blockquote><pre>
+   * (this.charAt(<i>k</i>) == ch) && (<i>k</i> >= fromIndex)
+   * </pre></blockquote>
+   * is true. If no such character occurs in this string at or after
+   * position <code>fromIndex</code>, then <code>-1</code> is returned.
+   * <p>
+   * There is no restriction on the value of <code>fromIndex</code>. If it
+   * is negative, it has the same effect as if it were zero: this entire
+   * string may be searched. If it is greater than the length of this
+   * string, it has the same effect as if it were equal to the length of
+   * this string: <code>-1</code> is returned.
+   *
+   * @param   ch          a character.
+   * @param   fromIndex   the index to start the search from.
+   * @return  the index of the first occurrence of the character in the
+   *          character sequence represented by this object that is greater
+   *          than or equal to <code>fromIndex</code>, or <code>-1</code>
+   *          if the character does not occur.
+   */
+  public int indexOf(int ch, int fromIndex)
+  {
+    return m_str.indexOf(ch, fromIndex);
+  }
+
+  /**
+   * Returns the index within this string of the last occurrence of the
+   * specified character. That is, the index returned is the largest
+   * value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.charAt(<i>k</i>) == ch
+   * </pre></blockquote>
+   * is true.
+   * The String is searched backwards starting at the last character.
+   *
+   * @param   ch   a character.
+   * @return  the index of the last occurrence of the character in the
+   *          character sequence represented by this object, or
+   *          <code>-1</code> if the character does not occur.
+   */
+  public int lastIndexOf(int ch)
+  {
+    return m_str.lastIndexOf(ch);
+  }
+
+  /**
+   * Returns the index within this string of the last occurrence of the
+   * specified character, searching backward starting at the specified
+   * index. That is, the index returned is the largest value <i>k</i>
+   * such that:
+   * <blockquote><pre>
+   * this.charAt(k) == ch) && (k <= fromIndex)
+   * </pre></blockquote>
+   * is true.
+   *
+   * @param   ch          a character.
+   * @param   fromIndex   the index to start the search from. There is no
+   *          restriction on the value of <code>fromIndex</code>. If it is
+   *          greater than or equal to the length of this string, it has
+   *          the same effect as if it were equal to one less than the
+   *          length of this string: this entire string may be searched.
+   *          If it is negative, it has the same effect as if it were -1:
+   *          -1 is returned.
+   * @return  the index of the last occurrence of the character in the
+   *          character sequence represented by this object that is less
+   *          than or equal to <code>fromIndex</code>, or <code>-1</code>
+   *          if the character does not occur before that point.
+   */
+  public int lastIndexOf(int ch, int fromIndex)
+  {
+    return m_str.lastIndexOf(ch, fromIndex);
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring. The integer returned is the smallest value
+   * <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   *
+   * @param   str   any string.
+   * @return  if the string argument occurs as a substring within this
+   *          object, then the index of the first character of the first
+   *          such substring is returned; if it does not occur as a
+   *          substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public int indexOf(String str)
+  {
+    return m_str.indexOf(str);
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring. The integer returned is the smallest value
+   * <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   *
+   * @param   str   any string.
+   * @return  if the string argument occurs as a substring within this
+   *          object, then the index of the first character of the first
+   *          such substring is returned; if it does not occur as a
+   *          substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public int indexOf(XMLString str)
+  {
+    return m_str.indexOf(str.toString());
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring, starting at the specified index. The integer
+   * returned is the smallest value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>) && (<i>k</i> >= fromIndex)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   * <p>
+   * There is no restriction on the value of <code>fromIndex</code>. If
+   * it is negative, it has the same effect as if it were zero: this entire
+   * string may be searched. If it is greater than the length of this
+   * string, it has the same effect as if it were equal to the length of
+   * this string: <code>-1</code> is returned.
+   *
+   * @param   str         the substring to search for.
+   * @param   fromIndex   the index to start the search from.
+   * @return  If the string argument occurs as a substring within this
+   *          object at a starting index no smaller than
+   *          <code>fromIndex</code>, then the index of the first character
+   *          of the first such substring is returned. If it does not occur
+   *          as a substring starting at <code>fromIndex</code> or beyond,
+   *          <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>
+   */
+  public int indexOf(String str, int fromIndex)
+  {
+    return m_str.indexOf(str, fromIndex);
+  }
+
+  /**
+   * Returns the index within this string of the rightmost occurrence
+   * of the specified substring.  The rightmost empty string "" is
+   * considered to occur at the index value <code>this.length()</code>.
+   * The returned index is the largest value <i>k</i> such that
+   * <blockquote><pre>
+   * this.startsWith(str, k)
+   * </pre></blockquote>
+   * is true.
+   *
+   * @param   str   the substring to search for.
+   * @return  if the string argument occurs one or more times as a substring
+   *          within this object, then the index of the first character of
+   *          the last such substring is returned. If it does not occur as
+   *          a substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException  if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public int lastIndexOf(String str)
+  {
+    return m_str.lastIndexOf(str);
+  }
+
+  /**
+   * Returns the index within this string of the last occurrence of
+   * the specified substring.
+   *
+   * @param   str         the substring to search for.
+   * @param   fromIndex   the index to start the search from. There is no
+   *          restriction on the value of fromIndex. If it is greater than
+   *          the length of this string, it has the same effect as if it
+   *          were equal to the length of this string: this entire string
+   *          may be searched. If it is negative, it has the same effect
+   *          as if it were -1: -1 is returned.
+   * @return  If the string argument occurs one or more times as a substring
+   *          within this object at a starting index no greater than
+   *          <code>fromIndex</code>, then the index of the first character of
+   *          the last such substring is returned. If it does not occur as a
+   *          substring starting at <code>fromIndex</code> or earlier,
+   *          <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public int lastIndexOf(String str, int fromIndex)
+  {
+    return m_str.lastIndexOf(str, fromIndex);
+  }
+
+  /**
+   * Returns a new string that is a substring of this string. The
+   * substring begins with the character at the specified index and
+   * extends to the end of this string. <p>
+   * Examples:
+   * <blockquote><pre>
+   * "unhappy".substring(2) returns "happy"
+   * "Harbison".substring(3) returns "bison"
+   * "emptiness".substring(9) returns "" (an empty string)
+   * </pre></blockquote>
+   *
+   * @param      beginIndex   the beginning index, inclusive.
+   * @return     the specified substring.
+   * @exception  IndexOutOfBoundsException  if
+   *             <code>beginIndex</code> is negative or larger than the
+   *             length of this <code>String</code> object.
+   */
+  public XMLString substring(int beginIndex)
+  {
+    return new XMLStringDefault(m_str.substring(beginIndex));
+  }
+
+  /**
+   * Returns a new string that is a substring of this string. The
+   * substring begins at the specified <code>beginIndex</code> and
+   * extends to the character at index <code>endIndex - 1</code>.
+   * Thus the length of the substring is <code>endIndex-beginIndex</code>.
+   *
+   * @param      beginIndex   the beginning index, inclusive.
+   * @param      endIndex     the ending index, exclusive.
+   * @return     the specified substring.
+   * @exception  IndexOutOfBoundsException  if the
+   *             <code>beginIndex</code> is negative, or
+   *             <code>endIndex</code> is larger than the length of
+   *             this <code>String</code> object, or
+   *             <code>beginIndex</code> is larger than
+   *             <code>endIndex</code>.
+   */
+  public XMLString substring(int beginIndex, int endIndex)
+  {
+    return new XMLStringDefault(m_str.substring(beginIndex, endIndex));
+  }
+
+  /**
+   * Concatenates the specified string to the end of this string.
+   *
+   * @param   str   the <code>String</code> that is concatenated to the end
+   *                of this <code>String</code>.
+   * @return  a string that represents the concatenation of this object's
+   *          characters followed by the string argument's characters.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public XMLString concat(String str)
+  {
+    return new XMLStringDefault(m_str.concat(str));
+  }
+
+  /**
+   * Converts all of the characters in this <code>String</code> to lower
+   * case using the rules of the given <code>Locale</code>.
+   *
+   * @param locale use the case transformation rules for this locale
+   * @return the String, converted to lowercase.
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see     java.lang.String#toUpperCase(Locale)
+   */
+  public XMLString toLowerCase(Locale locale)
+  {
+    return new XMLStringDefault(m_str.toLowerCase(locale));
+  }
+
+  /**
+   * Converts all of the characters in this <code>String</code> to lower
+   * case using the rules of the default locale, which is returned
+   * by <code>Locale.getDefault</code>.
+   * <p>
+   *
+   * @return  the string, converted to lowercase.
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see     java.lang.String#toLowerCase(Locale)
+   */
+  public XMLString toLowerCase()
+  {
+    return new XMLStringDefault(m_str.toLowerCase());
+  }
+
+  /**
+   * Converts all of the characters in this <code>String</code> to upper
+   * case using the rules of the given locale.
+   * @param locale use the case transformation rules for this locale
+   * @return the String, converted to uppercase.
+   * @see     java.lang.Character#toUpperCase(char)
+   * @see     java.lang.String#toLowerCase(Locale)
+   */
+  public XMLString toUpperCase(Locale locale)
+  {
+    return new XMLStringDefault(m_str.toUpperCase(locale));
+  }
+
+  /**
+   * Converts all of the characters in this <code>String</code> to upper
+   * case using the rules of the default locale, which is returned
+   * by <code>Locale.getDefault</code>.
+   *
+   * <p>
+   * If no character in this string has a different uppercase version,
+   * based on calling the <code>toUpperCase</code> method defined by
+   * <code>Character</code>, then the original string is returned.
+   * <p>
+   * Otherwise, this method creates a new <code>String</code> object
+   * representing a character sequence identical in length to the
+   * character sequence represented by this <code>String</code> object and
+   * with every character equal to the result of applying the method
+   * <code>Character.toUpperCase</code> to the corresponding character of
+   * this <code>String</code> object. <p>
+   * Examples:
+   * <blockquote><pre>
+   * "Fahrvergn&uuml;gen".toUpperCase() returns "FAHRVERGN&Uuml;GEN"
+   * "Visit Ljubinje!".toUpperCase() returns "VISIT LJUBINJE!"
+   * </pre></blockquote>
+   *
+   * @return  the string, converted to uppercase.
+   * @see     java.lang.Character#toUpperCase(char)
+   * @see     java.lang.String#toUpperCase(Locale)
+   */
+  public XMLString toUpperCase()
+  {
+    return new XMLStringDefault(m_str.toUpperCase());
+  }
+
+  /**
+   * Removes white space from both ends of this string.
+   * <p>
+   * If this <code>String</code> object represents an empty character
+   * sequence, or the first and last characters of character sequence
+   * represented by this <code>String</code> object both have codes
+   * greater than <code>'&#92;u0020'</code> (the space character), then a
+   * reference to this <code>String</code> object is returned.
+   * <p>
+   * Otherwise, if there is no character with a code greater than
+   * <code>'&#92;u0020'</code> in the string, then a new
+   * <code>String</code> object representing an empty string is created
+   * and returned.
+   * <p>
+   * Otherwise, let <i>k</i> be the index of the first character in the
+   * string whose code is greater than <code>'&#92;u0020'</code>, and let
+   * <i>m</i> be the index of the last character in the string whose code
+   * is greater than <code>'&#92;u0020'</code>. A new <code>String</code>
+   * object is created, representing the substring of this string that
+   * begins with the character at index <i>k</i> and ends with the
+   * character at index <i>m</i>-that is, the result of
+   * <code>this.substring(<i>k</i>,&nbsp;<i>m</i>+1)</code>.
+   * <p>
+   * This method may be used to trim
+   * {@link Character#isSpace(char) whitespace} from the beginning and end
+   * of a string; in fact, it trims all ASCII control characters as well.
+   *
+   * @return  this string, with white space removed from the front and end.
+   */
+  public XMLString trim()
+  {
+    return new XMLStringDefault(m_str.trim());
+  }
+
+  /**
+   * This object (which is already a string!) is itself returned.
+   *
+   * @return  the string itself.
+   */
+  public String toString()
+  {
+    return m_str;
+  }
+  
+  /**
+   * Tell if this object contains a java String object.
+   * 
+   * @return true if this XMLString can return a string without creating one.
+   */
+  public boolean hasString()
+  {
+    return true;
+  }
+  
+  /**
+   * Convert a string to a double -- Allowed input is in fixed
+   * notation ddd.fff.
+   *
+   * @return A double value representation of the string, or return Double.NaN 
+   * if the string can not be converted.
+   */
+  public double toDouble()
+  {
+    try {
+      return Double.valueOf(m_str).doubleValue();
+    }
+    catch (NumberFormatException nfe)
+    {
+      return Double.NaN;
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/XMLStringFactory.java b/src/main/java/org/apache/xml/utils/XMLStringFactory.java
new file mode 100644
index 0000000..3656fa6
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/XMLStringFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLStringFactory.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils;
+
+/**
+ * A concrete class that implements this interface creates XMLString objects.
+ */
+public abstract class XMLStringFactory
+{
+
+  /**
+   * Create a new XMLString from a Java string.
+   *
+   *
+   * @param string Java String reference, which must be non-null.
+   *
+   * @return An XMLString object that wraps the String reference.
+   */
+  public abstract XMLString newstr(String string);
+
+  /**
+   * Create a XMLString from a FastStringBuffer.
+   *
+   *
+   * @param string FastStringBuffer reference, which must be non-null.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   *
+   * @return An XMLString object that wraps the FastStringBuffer reference.
+   */
+  public abstract XMLString newstr(FastStringBuffer string, int start, 
+                                   int length);
+
+  /**
+   * Create a XMLString from a FastStringBuffer.
+   *
+   *
+   * @param string FastStringBuffer reference, which must be non-null.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   *
+   * @return An XMLString object that wraps the FastStringBuffer reference.
+   */
+  public abstract XMLString newstr(char[] string, int start, 
+                                   int length);
+                                   
+  /**
+   * Get a cheap representation of an empty string.
+   * 
+   * @return An non-null reference to an XMLString that represents "".
+   */
+  public abstract XMLString emptystr();
+}
diff --git a/src/main/java/org/apache/xml/utils/package.html b/src/main/java/org/apache/xml/utils/package.html
new file mode 100644
index 0000000..c943bbc
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/package.html
@@ -0,0 +1,27 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468655 2006-10-28 07:12:06Z minchau $ -->
+<html>
+  <title>Xalan utilities.</title>
+  <body>
+    <p>Implementation of Xalan utility classes.  This package is also shared by XPath.
+       There *should* be no outward dependencies to XPath or Xalan by classes in this package.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xml/utils/res/CharArrayWrapper.java b/src/main/java/org/apache/xml/utils/res/CharArrayWrapper.java
new file mode 100755
index 0000000..62aa7ba
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/CharArrayWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: CharArrayWrapper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+/**
+ *
+ * It is a mutable object to wrap the char[] used in
+ * the contents of the XResourceBundle class
+ */
+public class CharArrayWrapper {
+    private char[] m_char;
+    
+    public CharArrayWrapper(char[] arg){
+        m_char = arg;
+    }
+    
+    public char getChar(int index){
+        return m_char[index];
+    }
+    
+    public int getLength(){
+        return m_char.length;
+    }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/IntArrayWrapper.java b/src/main/java/org/apache/xml/utils/res/IntArrayWrapper.java
new file mode 100755
index 0000000..b9794a7
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/IntArrayWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: IntArrayWrapper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+/**
+ *
+ * It is a mutable object to wrap the int[] used in
+ * the contents of the XResourceBundle class
+ */
+public class IntArrayWrapper {
+    private int[] m_int;
+    
+    public IntArrayWrapper(int[] arg){
+        m_int = arg;
+    }
+    
+    public int getInt(int index){
+        return m_int[index];
+    }
+    
+    public int getLength(){
+        return m_int.length;
+    }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/LongArrayWrapper.java b/src/main/java/org/apache/xml/utils/res/LongArrayWrapper.java
new file mode 100755
index 0000000..a98bbb2
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/LongArrayWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: LongArrayWrapper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+/**
+ *
+ * It is a mutable object to wrap the long[] used in
+ * the contents of the XResourceBundle class
+ */
+public class LongArrayWrapper {
+    private long[] m_long;
+    
+    public LongArrayWrapper(long[] arg){
+        m_long = arg;
+    }
+    
+    public long getLong(int index){
+        return m_long[index];
+    }
+    
+    public int getLength(){
+        return m_long.length;
+    }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/StringArrayWrapper.java b/src/main/java/org/apache/xml/utils/res/StringArrayWrapper.java
new file mode 100755
index 0000000..e669e8e
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/StringArrayWrapper.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StringArrayWrapper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+/**
+ *
+ * It is a mutable object to wrap the String[] used in
+ * the contents of the XResourceBundle class
+ */
+public class StringArrayWrapper {
+    private String[] m_string;
+    
+    public StringArrayWrapper(String[] arg){
+        m_string = arg;
+    }
+    
+    public String getString(int index){
+        return m_string[index];
+    }
+    
+    public int getLength(){
+        return m_string.length;
+    }
+}
+
diff --git a/src/main/java/org/apache/xml/utils/res/XResourceBundle.java b/src/main/java/org/apache/xml/utils/res/XResourceBundle.java
new file mode 100644
index 0000000..72e829c
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResourceBundle.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResourceBundle.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * The default (english) resource bundle.
+ * @xsl.usage internal
+ */
+public class XResourceBundle extends ListResourceBundle
+{
+
+  /** Error resource constants */
+  public static final String ERROR_RESOURCES =
+    "org.apache.xalan.res.XSLTErrorResources", XSLT_RESOURCE =
+    "org.apache.xml.utils.res.XResourceBundle", LANG_BUNDLE_NAME =
+    "org.apache.xml.utils.res.XResources", MULT_ORDER =
+    "multiplierOrder", MULT_PRECEDES = "precedes", MULT_FOLLOWS =
+    "follows", LANG_ORIENTATION = "orientation", LANG_RIGHTTOLEFT =
+    "rightToLeft", LANG_LEFTTORIGHT = "leftToRight", LANG_NUMBERING =
+    "numbering", LANG_ADDITIVE = "additive", LANG_MULT_ADD =
+    "multiplicative-additive", LANG_MULTIPLIER =
+    "multiplier", LANG_MULTIPLIER_CHAR =
+    "multiplierChar", LANG_NUMBERGROUPS = "numberGroups", LANG_NUM_TABLES =
+    "tables", LANG_ALPHABET = "alphabet", LANG_TRAD_ALPHABET = "tradAlphabet";
+
+  /**
+   * Return a named ResourceBundle for a particular locale.  This method mimics the behavior
+   * of ResourceBundle.getBundle().
+   *
+   * @param className Name of local-specific subclass.
+   * @param locale the locale to prefer when searching for the bundle
+   */
+  public static final XResourceBundle loadResourceBundle(
+          String className, Locale locale) throws MissingResourceException
+  {
+
+    String suffix = getResourceSuffix(locale);
+
+    //System.out.println("resource " + className + suffix);
+    try
+    {
+      
+      // first try with the given locale
+      String resourceName = className + suffix;
+      return (XResourceBundle) ResourceBundle.getBundle(resourceName, locale);
+    }
+    catch (MissingResourceException e)
+    {
+      try  // try to fall back to en_US if we can't load
+      {
+
+        // Since we can't find the localized property file,
+        // fall back to en_US.
+        return (XResourceBundle) ResourceBundle.getBundle(
+          XSLT_RESOURCE, new Locale("en", "US"));
+      }
+      catch (MissingResourceException e2)
+      {
+
+        // Now we are really in trouble.
+        // very bad, definitely very bad...not going to get very far
+        throw new MissingResourceException(
+          "Could not load any resource bundles.", className, "");
+      }
+    }
+  }
+
+  /**
+   * Return the resource file suffic for the indicated locale
+   * For most locales, this will be based the language code.  However
+   * for Chinese, we do distinguish between Taiwan and PRC
+   *
+   * @param locale the locale
+   * @return an String suffix which canbe appended to a resource name
+   */
+  private static final String getResourceSuffix(Locale locale)
+  {
+
+    String lang = locale.getLanguage();
+    String country = locale.getCountry();
+    String variant = locale.getVariant();
+    String suffix = "_" + locale.getLanguage();
+
+    if (lang.equals("zh"))
+      suffix += "_" + country;
+
+    if (country.equals("JP"))
+      suffix += "_" + country + "_" + variant;
+
+    return suffix;
+  }
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "en" }, { "help_language", "en" }, { "language", "en" },
+    { "alphabet", new CharArrayWrapper(new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+         'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 
+         'V', 'W', 'X', 'Y', 'Z' })},
+    { "tradAlphabet", new CharArrayWrapper(new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 
+         'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
+         'U', 'V', 'W', 'X', 'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResourceBundleBase.java b/src/main/java/org/apache/xml/utils/res/XResourceBundleBase.java
new file mode 100644
index 0000000..fd49302
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResourceBundleBase.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResourceBundleBase.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+import java.util.ListResourceBundle;
+
+/**
+ * This is an interface for error messages.  This class is misnamed,
+ * and should be called XalanResourceBundle, or some such.
+ * @xsl.usage internal
+ */
+abstract public class XResourceBundleBase extends ListResourceBundle
+{
+
+  /**
+   * Get the error string associated with the error code
+   *
+   * @param errorCode Error code
+   *
+   * @return error string associated with the given error code
+   */
+  abstract public String getMessageKey(int errorCode);
+
+  /**
+   * Get the warning string associated with the error code
+   *
+   * @param errorCode Error code
+   * 
+   * @return warning string associated with the given error code
+   */
+  abstract public String getWarningKey(int errorCode);
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_cy.java b/src/main/java/org/apache/xml/utils/res/XResources_cy.java
new file mode 100644
index 0000000..2c8b42f
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_cy.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_cy.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+
+/**
+ * The Cyrillic resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_cy extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "cy" }, { "help_language", "cy" }, { "language", "cy" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x0430, 0x0432, 0x0433, 0x0434, 0x0435, 0x0437, 0x0438,
+                  0x0439, 0x04A9, 0x0457, 0x043A, 0x043B, 0x043C, 0x043D,
+                  0x046F, 0x043E, 0x043F, 0x0447, 0x0440, 0x0441, 0x0442,
+                  0x0443, 0x0444, 0x0445, 0x0470, 0x0460, 0x0446 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering 
+    //{"numbering", "additive"},
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "precedes" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer(10000000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 100, 10, 1 }) },
+
+    //These only used for mutiplicative-additive numbering
+    { "multiplier", new LongArrayWrapper(new long[]{ 1000 }) },
+    { "multiplierChar", new CharArrayWrapper(new char[]{ 0x03D9 }) },
+
+    // chinese only??
+    { "zero", new CharArrayWrapper(new char[0]) },
+
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x0430, 0x0432, 0x0433, 0x0434, 0x0435, 0x0437, 0x0438,
+                  0x0439, 0x04A9 }) },
+    { "tens", new CharArrayWrapper(
+      new char[]{ 0x0457, 0x043A, 0x043B, 0x043C, 0x043D, 0x046F, 0x043E,
+                  0x043F, 0x0447 }) },
+    { "hundreds", new CharArrayWrapper(
+      new char[]{ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0470,
+                  0x0460, 0x0446 }) },
+    { "tables", new StringArrayWrapper(new String[]{ "hundreds", "tens", "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_de.java b/src/main/java/org/apache/xml/utils/res/XResources_de.java
new file mode 100644
index 0000000..9a13c37
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_de.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_de.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_de.properties
+//
+
+/**
+ * The German resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_de extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "de" }, { "help_language", "de" }, { "language", "de" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    //{"numberGroups", new int[]{10,1}},
+    //These only used for mutiplicative-additive numbering
+    //{"multiplier", "10"},
+    //{"multiplierChar", "M"}, 
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    //{"digits", new char[]{0x10D0,0x10D1,0x10D2,0x10D3,0x10D4,0x10D5,0x10D6,0x10D7,0x10D8}},
+    //{"tens", new char[]{0x10D9,0x10DA,0x10DB,0x10DC,0x10DD,0x10DE,0x10DF,0x10E0,0x10E1}},  
+    //hundreds, etc...
+    //{"tables", new String[]{"tens", "digits"}}
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_el.java b/src/main/java/org/apache/xml/utils/res/XResources_el.java
new file mode 100644
index 0000000..d4bc82b
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_el.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_el.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Greek resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_el extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "el" }, { "help_language", "el" }, { "language", "el" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+                  0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be,
+                  0x03bf, 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5,
+                  0x03c6, 0x03c7, 0x03c8, 0x03c9 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering 
+    //{"numbering", "additive"},
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "precedes" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 100, 10, 1 }) },
+
+    //These only used for mutiplicative-additive numbering
+    { "multiplier", new LongArrayWrapper(new long[]{ 1000 }) },
+    { "multiplierChar", new CharArrayWrapper(new char[]{ 0x03d9 }) },
+
+    // chinese only??
+    { "zero", new CharArrayWrapper(new char[0]) },
+
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03db, 0x03b6,
+                  0x03b7, 0x03b8 }) },
+    { "tens", new CharArrayWrapper(
+      new char[]{ 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+                  0x03c0, 0x03df }) },
+    { "hundreds", new CharArrayWrapper(
+      new char[]{ 0x03c1, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8,
+                  0x03c9, 0x03e1 }) },
+
+    //{"thousands", new char[]{0x10D9,0x10DA,0x10DB,0x10DC,0x10DD,0x10DE,0x10DF,0x10E0,0x10E1}},  
+    //hundreds, etc...
+    { "tables", new StringArrayWrapper(new String[]{ "hundreds", "tens", "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_en.java b/src/main/java/org/apache/xml/utils/res/XResources_en.java
new file mode 100644
index 0000000..7116607
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_en.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_en.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The English resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_en extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][] 
+  {
+    { "ui_language", "en" }, { "help_language", "en" }, { "language", "en" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' })},
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    //{"numberGroups", new int[]{10,1}},
+    //These only used for mutiplicative-additive numbering
+    //{"multiplier", "10"},
+    //{"multiplierChar", "M"}, 
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    //{"digits", new char[]{0x10D0,0x10D1,0x10D2,0x10D3,0x10D4,0x10D5,0x10D6,0x10D7,0x10D8}},
+    //{"tens", new char[]{0x10D9,0x10DA,0x10DB,0x10DC,0x10DD,0x10DE,0x10DF,0x10E0,0x10E1}},  
+    //hundreds, etc...
+    //{"tables", new String[]{"tens", "digits"}}
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_es.java b/src/main/java/org/apache/xml/utils/res/XResources_es.java
new file mode 100644
index 0000000..18ecebc
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_es.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_es.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_es.properties
+//
+
+/**
+ * The Spanish resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_es extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "es" }, { "help_language", "es" }, { "language", "es" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    //{"numberGroups", new int[]{10,1}},
+    //These only used for mutiplicative-additive numbering
+    //{"multiplier", "10"},
+    //{"multiplierChar", "M"}, 
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    //{"digits", new char[]{0x10D0,0x10D1,0x10D2,0x10D3,0x10D4,0x10D5,0x10D6,0x10D7,0x10D8}},
+    //{"tens", new char[]{0x10D9,0x10DA,0x10DB,0x10DC,0x10DD,0x10DE,0x10DF,0x10E0,0x10E1}},  
+    //hundreds, etc...
+    //{"tables", new String[]{"tens", "digits"}}
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_fr.java b/src/main/java/org/apache/xml/utils/res/XResources_fr.java
new file mode 100644
index 0000000..55b6c88
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_fr.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_fr.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_fr.properties
+//
+
+/**
+ * The French resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_fr extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "fr" }, { "help_language", "fr" }, { "language", "fr" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    //{"numberGroups", new int[]{10,1}},
+    //These only used for mutiplicative-additive numbering
+    //{"multiplier", "10"},
+    //{"multiplierChar", "M"}, 
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    //{"digits", new char[]{0x10D0,0x10D1,0x10D2,0x10D3,0x10D4,0x10D5,0x10D6,0x10D7,0x10D8}},
+    //{"tens", new char[]{0x10D9,0x10DA,0x10DB,0x10DC,0x10DD,0x10DE,0x10DF,0x10E0,0x10E1}},  
+    //hundreds, etc...
+    //{"tables", new String[]{"tens", "digits"}}
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_he.java b/src/main/java/org/apache/xml/utils/res/XResources_he.java
new file mode 100644
index 0000000..1089eb7
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_he.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_he.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Hebrew resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_he extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "he" }, { "help_language", "he" }, { "language", "he" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6,
+                  0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD,
+                  0x05DE, 0x05DF, 0x05E0, 0x05E1 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "RightToLeft" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 10, 1 }) },
+
+    //These only used for mutiplicative-additive numbering
+    //{"multiplier", "10"},
+    //{"multiplierChar", "M"}, 
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6,
+                  0x05D7, 0x05D8 }) },
+    { "tens", new CharArrayWrapper(
+      new char[]{ 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+                  0x05E0, 0x05E1 }) },
+
+    //hundreds, etc...
+    { "tables", new StringArrayWrapper(new String[]{ "tens", "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_hy.java b/src/main/java/org/apache/xml/utils/res/XResources_hy.java
new file mode 100644
index 0000000..e23154d
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_hy.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_hy.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Armenian resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_hy extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "hy" }, { "help_language", "hy" }, { "language", "hy" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+                  0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E,
+                  0x056F, 0x0567, 0x0568, 0x0572, 0x0573, 0x0574, 0x0575,
+                  0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C,
+                  0x057D, 0x057E, 0x057F, 0x0580, 0x0581, 0x0582, 0x0583,
+                  0x0584 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1000, 100, 10, 1 }) },
+
+    //These only used for mutiplicative-additive numbering
+    //{"multiplier", "10"},
+    //{"multiplierChar", "M"}, 
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+                  0x0568, 0x0569 }) },
+    { "tens", new CharArrayWrapper(
+      new char[]{ 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, 0x0567,
+                  0x0568, 0x0572 }) },
+    { "hundreds", new CharArrayWrapper(
+      new char[]{ 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579,
+                  0x057A, 0x057B }) },
+    { "thousands", new CharArrayWrapper(
+      new char[]{ 0x057C, 0x057D, 0x057E, 0x057F, 0x0580, 0x0581, 0x0582,
+                  0x0583, 0x0584 }) },
+    { "tables", new StringArrayWrapper(new String[]{ "thousands", "hundreds", "tens", "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_it.java b/src/main/java/org/apache/xml/utils/res/XResources_it.java
new file mode 100644
index 0000000..8808e37
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_it.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_it.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_it.properties
+//
+
+/**
+ * The Italian resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_it extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "it" }, { "help_language", "it" }, { "language", "it" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    //{"numberGroups", new int[]{10,1}},
+    //These only used for mutiplicative-additive numbering
+    //{"multiplier", "10"},
+    //{"multiplierChar", "M"}, 
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    //{"digits", new char[]{0x10D0,0x10D1,0x10D2,0x10D3,0x10D4,0x10D5,0x10D6,0x10D7,0x10D8}},
+    //{"tens", new char[]{0x10D9,0x10DA,0x10DB,0x10DC,0x10DD,0x10DE,0x10DF,0x10E0,0x10E1}},  
+    //hundreds, etc...
+    //{"tables", new String[]{"tens", "digits"}}
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_A.java b/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_A.java
new file mode 100644
index 0000000..3f41216
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_A.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_ja_JP_A.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Japanese (Katakana) resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_ja_JP_A extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "ja" }, { "help_language", "ja" }, { "language", "ja" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, 0x30ab, 0x30ad,
+                  0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, 0x30bb,
+                  0x30bd, 0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca,
+                  0x30cb, 0x30cc, 0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5,
+                  0x30d8, 0x30db, 0x30de, 0x30df, 0x30e0, 0x30e1, 0x30e2,
+                  0x30e4, 0x30e6, 0x30e8, 0x30e9, 0x30ea, 0x30eb, 0x30ec,
+                  0x30ed, 0x30ef, 0x30f0, 0x30f1, 0x30f2, 0x30f3 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation 
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "follows" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer(10000000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1 }) },
+
+    //These only used for mutiplicative-additive numbering
+    // Note that we are using longs and that the last two 
+    // multipliers are not supported. This is a known limitation.
+    { "multiplier", new LongArrayWrapper(
+      new long[]{ Long.MAX_VALUE, Long.MAX_VALUE, 100000000, 10000, 1000, 
+          100, 10 }) },
+    { "multiplierChar", new CharArrayWrapper(
+      new char[]{ 0x4EAC, 0x5146, 0x5104, 0x4E07, 0x5343, 0x767e, 0x5341 }) },
+
+    // chinese only? 
+    { "zero", new CharArrayWrapper(new char[0]) },
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E94, 0x516D, 0x4E03,
+                  0x516B, 0x4E5D }) }, { "tables", new StringArrayWrapper(
+                      new String[]{ "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_HA.java b/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_HA.java
new file mode 100644
index 0000000..7934665
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_HA.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_ja_JP_HA.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Japanese (Hiragana) resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_ja_JP_HA extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "ja" }, { "help_language", "ja" }, { "language", "ja" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x3042, 0x3044, 0x3046, 0x3048, 0x304a, 0x304b, 0x304d,
+                  0x304f, 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305b,
+                  0x305d, 0x305f, 0x3061, 0x3064, 0x3066, 0x3068, 0x306a,
+                  0x306b, 0x306c, 0x306d, 0x306e, 0x306f, 0x3072, 0x3075,
+                  0x3078, 0x307b, 0x307e, 0x307f, 0x3080, 0x3081, 0x3082,
+                  0x3084, 0x3086, 0x3088, 0x3089, 0x308a, 0x308b, 0x308c,
+                  0x308d, 0x308f, 0x3090, 0x3091, 0x3092, 0x3093 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation 
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "follows" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer(10000000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1 }) },
+
+    //These only used for mutiplicative-additive numbering
+    // Note that we are using longs and that the last two 
+    // multipliers are not supported. This is a known limitation.
+    { "multiplier", new LongArrayWrapper(
+      new long[]{ Long.MAX_VALUE, Long.MAX_VALUE, 100000000, 10000, 1000, 
+          100, 10 }) },
+    { "multiplierChar", new CharArrayWrapper(
+      new char[]{ 0x4EAC, 0x5146, 0x5104, 0x4E07, 0x5343, 0x767e, 0x5341 }) },
+
+    // chinese only? 
+    { "zero", new CharArrayWrapper(new char[0]) },
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E94, 0x516D, 0x4E03,
+                  0x516B, 0x4E5D }) }, { "tables", new StringArrayWrapper(
+                      new String[]{ "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_HI.java b/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_HI.java
new file mode 100644
index 0000000..306fcb2
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_HI.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_ja_JP_HI.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Japanese (Hiragana) resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_ja_JP_HI extends XResourceBundle
+{
+
+  /**
+   * Get the association table for this resource.
+   *
+   *
+   * @return the association table for this resource.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "ja" }, { "help_language", "ja" }, { "language", "ja" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x3044, 0x308d, 0x306f, 0x306b, 0x307b, 0x3078, 0x3068,
+                  0x3061, 0x308a, 0x306c, 0x308b, 0x3092, 0x308f, 0x304b,
+                  0x3088, 0x305f, 0x308c, 0x305d, 0x3064, 0x306d, 0x306a,
+                  0x3089, 0x3080, 0x3046, 0x3090, 0x306e, 0x304a, 0x304f,
+                  0x3084, 0x307e, 0x3051, 0x3075, 0x3053, 0x3048, 0x3066,
+                  0x3042, 0x3055, 0x304d, 0x3086, 0x3081, 0x307f, 0x3057,
+                  0x3091, 0x3072, 0x3082, 0x305b, 0x3059 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "follows" },
+
+    // largest numerical value 
+    //{"MaxNumericalValue", new Integer(10000000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1 }) },
+
+    //These only used for mutiplicative-additive numbering
+    // Note that we are using longs and that the last two 
+    // multipliers are not supported. This is a known limitation.
+    { "multiplier", new LongArrayWrapper(
+      new long[]{ Long.MAX_VALUE, Long.MAX_VALUE, 100000000, 10000, 1000, 
+          100, 10 }) },
+    { "multiplierChar", new CharArrayWrapper(
+      new char[]{ 0x4EAC, 0x5146, 0x5104, 0x4E07, 0x5343, 0x767e, 0x5341 }) },
+
+    // chinese only??
+    { "zero", new CharArrayWrapper(new char[0]) },
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E94, 0x516D, 0x4E03,
+                  0x516B, 0x4E5D }) }, { "tables", new StringArrayWrapper(
+                      new String[]{ "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_I.java b/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_I.java
new file mode 100644
index 0000000..b2b6558
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_ja_JP_I.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_ja_JP_I.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Japanese (Katakana) resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_ja_JP_I extends XResourceBundle
+{
+
+  /**
+   * Get the association table for this resource.
+   *
+   *
+   * @return the association table for this resource.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "ja" }, { "help_language", "ja" }, { "language", "ja" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x30a4, 0x30ed, 0x30cf, 0x30cb, 0x30db, 0x30d8, 0x30c8,
+                  0x30c1, 0x30ea, 0x30cc, 0x30eb, 0x30f2, 0x30ef, 0x30ab,
+                  0x30e8, 0x30bf, 0x30ec, 0x30bd, 0x30c4, 0x30cd, 0x30ca,
+                  0x30e9, 0x30e0, 0x30a6, 0x30f0, 0x30ce, 0x30aa, 0x30af,
+                  0x30e4, 0x30de, 0x30b1, 0x30d5, 0x30b3, 0x30a8, 0x30c6,
+                  0x30a2, 0x30b5, 0x30ad, 0x30e6, 0x30e1, 0x30df, 0x30b7,
+                  0x30f1, 0x30d2, 0x30e2, 0x30bb, 0x30b9 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "follows" },
+
+    // largest numerical value 
+    //{"MaxNumericalValue", new Integer(10000000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1 }) },
+
+    //These only used for mutiplicative-additive numbering
+    // Note that we are using longs and that the last two 
+    // multipliers are not supported. This is a known limitation.
+    { "multiplier", new LongArrayWrapper(
+      new long[]{ Long.MAX_VALUE, Long.MAX_VALUE, 100000000, 10000, 1000, 100, 10 }) },
+    { "multiplierChar", new CharArrayWrapper(
+      new char[]{ 0x4EAC, 0x5146, 0x5104, 0x4E07, 0x5343, 0x767e, 0x5341 }) },
+
+    // chinese only??
+    { "zero", new CharArrayWrapper(new char[0]) },
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E94, 0x516D, 0x4E03,
+                  0x516B, 0x4E5D }) }, { "tables", new StringArrayWrapper(
+                      new String[]{ "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_ka.java b/src/main/java/org/apache/xml/utils/res/XResources_ka.java
new file mode 100644
index 0000000..53dd551
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_ka.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_ka.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Georgian resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_ka extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "ka" }, { "help_language", "ka" }, { "language", "ka" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6,
+                  0x10f1, 0x10D7, 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC,
+                  0x10f2, 0x10DD, 0x10DE, 0x10DF, 0x10E0, 0x10E1, 0x10E2,
+                  0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8, 0x10E9,
+                  0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF,
+                  0x10F0 }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer(10000000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1000, 100, 10, 1 }) },
+
+    //These used for additive numbering
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6,
+                  0x10f1, 0x10D7 }) },
+    { "tens", new CharArrayWrapper(
+      new char[]{ 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10f2, 0x10DD,
+                  0x10DE, 0x10DF }) },
+    { "hundreds", new CharArrayWrapper(
+      new char[]{ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6,
+                  0x10E7, 0x10E8 }) },
+    { "thousands", new CharArrayWrapper(
+      new char[]{ 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4,
+                  0x10EF, 0x10F0 }) },
+    { "tables", new StringArrayWrapper(new String[]{ "thousands", "hundreds", 
+            "tens", "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_ko.java b/src/main/java/org/apache/xml/utils/res/XResources_ko.java
new file mode 100644
index 0000000..c6a622b
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_ko.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_ko.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_ko.properties
+//
+
+/**
+ * The Korean resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_ko extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "ko" }, { "help_language", "ko" }, { "language", "ko" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 
+                  0x3147, 0x3148, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 
+                  0x314f, 0x3151, 0x3153, 0x3155, 0x3157, 0x315b, 0x315c, 
+                  0x3160, 0x3161, 0x3163})},
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation 
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "follows" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer(100000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1 }) },
+
+    // chinese only ??
+    { "zero", new CharArrayWrapper(new char[0]) },
+
+    //These only used for mutiplicative-additive numbering
+    { "multiplier", new LongArrayWrapper(new long[]{ 100000000, 10000, 1000, 
+        100, 10 }) },
+    { "multiplierChar", new CharArrayWrapper(
+      new char[]{  0xc5b5, 0xb9cc, 0xcc9c, 0xbc31, 0xc2ed }) },
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0xc77c, 0xc774, 0xc0bc, 0xc0ac, 0xc624, 0xc721, 0xce60, 
+          0xd314, 0xad6c}) }, { "tables", new StringArrayWrapper(
+              new String[]{ "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_sv.java b/src/main/java/org/apache/xml/utils/res/XResources_sv.java
new file mode 100644
index 0000000..a50766d
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_sv.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_sv.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_sv.properties
+//
+
+/**
+ * The Swedish resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_sv extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "sv" }, { "help_language", "sv" }, { "language", "sv" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "additive" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer()},
+    //These would not be used for EN. Only used for traditional numbering   
+    //{"numberGroups", new int[]{10,1}},
+    //These only used for mutiplicative-additive numbering
+    //{"multiplier", "10"},
+    //{"multiplierChar", "M"}, 
+    //{"digits", new char[]{'a','b','c','d','e','f','g','h','i'}},
+    //{"digits", new char[]{0x10D0,0x10D1,0x10D2,0x10D3,0x10D4,0x10D5,0x10D6,0x10D7,0x10D8}},
+    //{"tens", new char[]{0x10D9,0x10DA,0x10DB,0x10DC,0x10DD,0x10DE,0x10DF,0x10E0,0x10E1}},  
+    //hundreds, etc...
+    //{"tables", new String[]{"tens", "digits"}}
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_zh_CN.java b/src/main/java/org/apache/xml/utils/res/XResources_zh_CN.java
new file mode 100644
index 0000000..03fdd79
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_zh_CN.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_zh_CN.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Chinese resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_zh_CN extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "zh" }, { "help_language", "zh" }, { "language", "zh" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27,
+                  0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e,
+                  0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35,
+                  0xff36, 0xff37, 0xff38, 0xff39, 0xff3a }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation 
+    { "orientation", "LeftToRight" },
+
+    //language numbering   
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "follows" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer(100000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1 }) },
+
+    // simplified chinese  
+    { "zero", new CharArrayWrapper(new char[]{ 0x96f6 }) },
+
+    //These only used for mutiplicative-additive numbering
+    { "multiplier", new LongArrayWrapper(new long[]{ 100000000, 10000, 1000, 
+        100, 10 }) },
+    { "multiplierChar", new CharArrayWrapper(
+      new char[]{ 0x4ebf, 0x4e07, 0x5343, 0x767e, 0x5341 }) },
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03,
+                  0x516b, 0x4e5d }) }, { "tables", new StringArrayWrapper(
+                      new String[]{ "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xml/utils/res/XResources_zh_TW.java b/src/main/java/org/apache/xml/utils/res/XResources_zh_TW.java
new file mode 100644
index 0000000..db531a0
--- /dev/null
+++ b/src/main/java/org/apache/xml/utils/res/XResources_zh_TW.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XResources_zh_TW.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xml.utils.res;
+
+//
+//  LangResources_en.properties
+//
+
+/**
+ * The Chinese(Taiwan) resource bundle.
+ * @xsl.usage internal
+ */
+public class XResources_zh_TW extends XResourceBundle
+{
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]
+  {
+    { "ui_language", "zh" }, { "help_language", "zh" }, { "language", "zh" },
+    { "alphabet", new CharArrayWrapper(
+      new char[]{ 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27,
+                  0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e,
+                  0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35,
+                  0xff36, 0xff37, 0xff38, 0xff39, 0xff3a }) },
+    { "tradAlphabet", new CharArrayWrapper(
+      new char[]{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+                  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                  'Y', 'Z' }) },
+
+    //language orientation 
+    { "orientation", "LeftToRight" },
+
+    //language numbering    
+    { "numbering", "multiplicative-additive" },
+    { "multiplierOrder", "follows" },
+
+    // largest numerical value
+    //{"MaxNumericalValue", new Integer(100000000)},
+    //These would not be used for EN. Only used for traditional numbering   
+    { "numberGroups", new IntArrayWrapper(new int[]{ 1 }) },
+
+    // simplified chinese 
+    { "zero", new CharArrayWrapper(new char[]{ 0x96f6 }) },
+
+    //These only used for mutiplicative-additive numbering 
+    { "multiplier", new LongArrayWrapper(new long[]{ 100000000, 10000, 1000, 
+        100, 10 }) },
+    { "multiplierChar", new CharArrayWrapper(
+      new char[]{ 0x5104, 0x842c, 0x4edf, 0x4f70, 0x62fe }) },
+    { "digits", new CharArrayWrapper(
+      new char[]{ 0x58f9, 0x8cb3, 0x53c3, 0x8086, 0x4f0d, 0x9678, 0x67d2,
+                  0x634c, 0x7396 }) }, { "tables", new StringArrayWrapper(
+                      new String[]{ "digits" }) }
+  };
+  }
+}
diff --git a/src/main/java/org/apache/xpath/Arg.java b/src/main/java/org/apache/xpath/Arg.java
new file mode 100644
index 0000000..5582038
--- /dev/null
+++ b/src/main/java/org/apache/xpath/Arg.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Arg.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import org.apache.xml.utils.QName;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * This class holds an instance of an argument on
+ * the stack. The value of the argument can be either an
+ * XObject or a String containing an expression.
+ * @xsl.usage internal
+ */
+public class Arg
+{
+
+  /** Field m_qname: The name of this argument, expressed as a QName
+   * (Qualified Name) object.
+   * @see getQName
+   * @see setQName
+   *  */
+  private QName m_qname;
+
+  /**
+   * Get the qualified name for this argument.
+   *
+   * @return QName object containing the qualified name
+   */
+  public final QName getQName()
+  {
+    return m_qname;
+  }
+
+  /**
+   * Set the qualified name for this argument.
+   *
+   * @param name QName object representing the new Qualified Name.
+   */
+  public final void setQName(QName name)
+  {
+    m_qname = name;
+  }
+
+  /** Field m_val: Stored XObject value of this argument
+   * @see #getVal()
+   * @see #setVal()
+   */
+  private XObject m_val;
+
+  /**
+   * Get the value for this argument.
+   *
+   * @return the argument's stored XObject value.
+   * @see #setVal(XObject)
+   */
+  public final XObject getVal()
+  {
+    return m_val;
+  }
+
+  /**
+   * Set the value of this argument.
+   *
+   * @param val an XObject representing the arguments's value.
+   * @see #getVal()
+   */
+  public final void setVal(XObject val)
+  {
+    m_val = val;
+  }
+  
+  /**
+   * Have the object release it's resources.
+   * Call only when the variable or argument is going out of scope.
+   */
+  public void detach()
+  {
+    if(null != m_val)
+    {
+      m_val.allowDetachToRelease(true);
+      m_val.detach();
+    }
+  }
+
+
+  /** Field m_expression: Stored expression value of this argument.
+   * @see #setExpression
+   * @see #getExpression
+   * */
+  private String m_expression;
+
+  /**
+   * Get the value expression for this argument.
+   *
+   * @return String containing the expression previously stored into this
+   * argument
+   * @see #setExpression
+   */
+  public String getExpression()
+  {
+    return m_expression;
+  }
+
+  /**
+   * Set the value expression for this argument.
+   *
+   * @param expr String containing the expression to be stored as this
+   * argument's value.
+   * @see #getExpression
+   */
+  public void setExpression(String expr)
+  {
+    m_expression = expr;
+  }
+
+  /** 
+   * True if this variable was added with an xsl:with-param or
+   * is added via setParameter.
+   */
+  private boolean m_isFromWithParam;
+
+  /**
+   * Tell if this variable is a parameter passed with a with-param or as 
+   * a top-level parameter.
+   */
+   public boolean isFromWithParam()
+   {
+    return m_isFromWithParam;
+   }
+
+  /** 
+   * True if this variable is currently visible.  To be visible,
+   * a variable needs to come either from xsl:variable or be 
+   * a "received" parameter, ie one for which an xsl:param has
+   * been encountered.
+   * Set at the time the object is constructed and updated as needed.
+   */
+  private boolean m_isVisible;
+
+  /**
+   * Tell if this variable is currently visible.
+   */
+   public boolean isVisible()
+   {
+    return m_isVisible;
+   }
+   
+  /**
+   * Update visibility status of this variable.
+   */
+   public void setIsVisible(boolean b)
+   {
+    m_isVisible = b;
+   }
+
+  /**
+   * Construct a dummy parameter argument, with no QName and no
+   * value (either expression string or value XObject). isVisible
+   * defaults to true.
+   */
+  public Arg()
+  {
+
+    m_qname = new QName("");
+    ;  // so that string compares can be done.
+    m_val = null;
+    m_expression = null;
+    m_isVisible = true;
+    m_isFromWithParam = false;
+  }
+
+  /**
+   * Construct a parameter argument that contains an expression.
+   *
+   * @param qname Name of the argument, expressed as a QName object.
+   * @param expression String to be stored as this argument's value expression.
+   * @param isFromWithParam True if this is a parameter variable.
+   */
+  public Arg(QName qname, String expression, boolean isFromWithParam)
+  {
+
+    m_qname = qname;
+    m_val = null;
+    m_expression = expression;
+    m_isFromWithParam = isFromWithParam;
+    m_isVisible = !isFromWithParam;
+  }
+
+  /**
+   * Construct a parameter argument which has an XObject value.
+   * isVisible defaults to true.
+   *
+   * @param qname Name of the argument, expressed as a QName object.
+   * @param val Value of the argument, expressed as an XObject
+   */
+  public Arg(QName qname, XObject val)
+  {
+
+    m_qname = qname;
+    m_val = val;
+    m_isVisible = true;
+    m_isFromWithParam = false;
+    m_expression = null;
+  }
+  
+  /**
+   * Equality function specialized for the variable name.  If the argument 
+   * is not a qname, it will deligate to the super class.
+   * 
+   * @param   obj   the reference object with which to compare.
+   * @return  <code>true</code> if this object is the same as the obj
+   *          argument; <code>false</code> otherwise.
+   */
+  public boolean equals(Object obj) 
+  {
+    if(obj instanceof QName)
+    {
+      return m_qname.equals(obj);
+    }
+    else
+      return super.equals(obj);
+  }
+
+  /**
+   * Construct a parameter argument.
+   *
+   * @param qname Name of the argument, expressed as a QName object.
+   * @param val Value of the argument, expressed as an XObject
+   * @param isFromWithParam True if this is a parameter variable.
+   */
+  public Arg(QName qname, XObject val, boolean isFromWithParam)
+  {
+
+    m_qname = qname;
+    m_val = val;
+    m_isFromWithParam = isFromWithParam;
+    m_isVisible = !isFromWithParam;
+    m_expression = null;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/CachedXPathAPI.java b/src/main/java/org/apache/xpath/CachedXPathAPI.java
new file mode 100644
index 0000000..7ecfdfd
--- /dev/null
+++ b/src/main/java/org/apache/xpath/CachedXPathAPI.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: CachedXPathAPI.java 524811 2007-04-02 15:51:59Z zongaro $
+ */
+package org.apache.xpath;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.PrefixResolverDefault;
+import org.apache.xpath.objects.XObject;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * The methods in this class are convenience methods into the
+ * low-level XPath API.
+ *
+ * These functions tend to be a little slow, since a number of objects must be
+ * created for each evaluation.  A faster way is to precompile the
+ * XPaths using the low-level API, and then just use the XPaths
+ * over and over.
+ *
+ * This is an alternative for the old XPathAPI class, which provided
+ * static methods for the purpose but had the drawback of
+ * instantiating a new XPathContext (and thus building a new DTMManager,
+ * and new DTMs) each time it was called. XPathAPIObject instead retains
+ * its context as long as the object persists, reusing the DTMs. This
+ * does have a downside: if you've changed your source document, you should
+ * obtain a new XPathAPIObject to continue searching it, since trying to use
+ * the old DTMs will probably yield bad results or malfunction outright... and
+ * the cached DTMs may consume memory until this object and its context are
+ * returned to the heap. Essentially, it's the caller's responsibility to
+ * decide when to discard the cache.
+ *
+ * @see <a href="http://www.w3.org/TR/xpath">XPath Specification</a>
+ * */
+public class CachedXPathAPI
+{
+  /** XPathContext, and thus the document model system (DTMs), persists through multiple
+      calls to this object. This is set in the constructor.
+  */
+  protected XPathContext xpathSupport;
+
+  /**
+   * <p>Default constructor. Establishes its own {@link XPathContext}, and hence
+   * its own {@link org.apache.xml.dtm.DTMManager}.
+   * Good choice for simple uses.</p>
+   * <p>Note that any particular instance of {@link CachedXPathAPI} must not be
+   * operated upon by multiple threads without synchronization; we do
+   * not currently support multithreaded access to a single
+   * {@link org.apache.xml.dtm.DTM}.</p>
+   */
+  public CachedXPathAPI()
+  {
+    // Create an XPathContext that doesn't support pushing and popping of
+    // variable resolution scopes.  Sufficient for simple XPath 1.0 expressions.
+    xpathSupport = new XPathContext(false);
+  }
+  
+  /**
+   * <p>This constructor shares its {@link XPathContext} with a pre-existing
+   * {@link CachedXPathAPI}.  That allows sharing document models
+   * ({@link org.apache.xml.dtm.DTM}) and previously established location
+   * state.</p>
+   * <p>Note that the original {@link CachedXPathAPI} and the new one should
+   * not be operated upon concurrently; we do not support multithreaded access
+   * to a single {@link org.apache.xml.dtm.DTM} at this time.  Similarly,
+   * any particular instance of {@link CachedXPathAPI} must not be operated
+   * upon by multiple threads without synchronization.</p>
+   * <p>%REVIEW% Should this instead do a clone-and-reset on the XPathSupport object?</p>
+   *
+   */
+  public CachedXPathAPI(CachedXPathAPI priorXPathAPI)
+  {
+    xpathSupport = priorXPathAPI.xpathSupport;
+  }
+
+
+  /** Returns the XPathSupport object used in this CachedXPathAPI
+   *
+   * %REVIEW% I'm somewhat concerned about the loss of encapsulation
+   * this causes, but the xml-security folks say they need it.
+   * */
+  public XPathContext getXPathContext()
+  {
+    return this.xpathSupport;
+  }
+  
+
+  /**
+   * Use an XPath string to select a single node. XPath namespace
+   * prefixes are resolved from the context node, which may not
+   * be what you want (see the next method).
+   *
+   * @param contextNode The node to start searching from.
+   * @param str A valid XPath string.
+   * @return The first node found that matches the XPath, or null.
+   *
+   * @throws TransformerException
+   */
+  public  Node selectSingleNode(Node contextNode, String str)
+          throws TransformerException
+  {
+    return selectSingleNode(contextNode, str, contextNode);
+  }
+
+  /**
+   * Use an XPath string to select a single node.
+   * XPath namespace prefixes are resolved from the namespaceNode.
+   *
+   * @param contextNode The node to start searching from.
+   * @param str A valid XPath string.
+   * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
+   * @return The first node found that matches the XPath, or null.
+   *
+   * @throws TransformerException
+   */
+  public  Node selectSingleNode(
+          Node contextNode, String str, Node namespaceNode)
+            throws TransformerException
+  {
+
+    // Have the XObject return its result as a NodeSetDTM.
+    NodeIterator nl = selectNodeIterator(contextNode, str, namespaceNode);
+
+    // Return the first node, or null
+    return nl.nextNode();
+  }
+
+  /**
+   *  Use an XPath string to select a nodelist.
+   *  XPath namespace prefixes are resolved from the contextNode.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @return A NodeIterator, should never be null.
+   *
+   * @throws TransformerException
+   */
+  public  NodeIterator selectNodeIterator(Node contextNode, String str)
+          throws TransformerException
+  {
+    return selectNodeIterator(contextNode, str, contextNode);
+  }
+
+  /**
+   *  Use an XPath string to select a nodelist.
+   *  XPath namespace prefixes are resolved from the namespaceNode.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
+   *  @return A NodeIterator, should never be null.
+   *
+   * @throws TransformerException
+   */
+  public  NodeIterator selectNodeIterator(
+          Node contextNode, String str, Node namespaceNode)
+            throws TransformerException
+  {
+
+    // Execute the XPath, and have it return the result
+    XObject list = eval(contextNode, str, namespaceNode);
+
+    // Have the XObject return its result as a NodeSetDTM.                
+    return list.nodeset();
+  }
+
+  /**
+   *  Use an XPath string to select a nodelist.
+   *  XPath namespace prefixes are resolved from the contextNode.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @return A NodeIterator, should never be null.
+   *
+   * @throws TransformerException
+   */
+  public  NodeList selectNodeList(Node contextNode, String str)
+          throws TransformerException
+  {
+    return selectNodeList(contextNode, str, contextNode);
+  }
+
+  /**
+   *  Use an XPath string to select a nodelist.
+   *  XPath namespace prefixes are resolved from the namespaceNode.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
+   *  @return A NodeIterator, should never be null.
+   *
+   * @throws TransformerException
+   */
+  public  NodeList selectNodeList(
+          Node contextNode, String str, Node namespaceNode)
+            throws TransformerException
+  {
+
+    // Execute the XPath, and have it return the result
+    XObject list = eval(contextNode, str, namespaceNode);
+
+    // Return a NodeList.
+    return list.nodelist();
+  }
+
+  /**
+   *  Evaluate XPath string to an XObject.  Using this method,
+   *  XPath namespace prefixes will be resolved from the namespaceNode.
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
+   *  @see org.apache.xpath.objects.XObject
+   *  @see org.apache.xpath.objects.XNull
+   *  @see org.apache.xpath.objects.XBoolean
+   *  @see org.apache.xpath.objects.XNumber
+   *  @see org.apache.xpath.objects.XString
+   *  @see org.apache.xpath.objects.XRTreeFrag
+   *
+   * @throws TransformerException
+   */
+  public  XObject eval(Node contextNode, String str)
+          throws TransformerException
+  {
+    return eval(contextNode, str, contextNode);
+  }
+
+  /**
+   *  Evaluate XPath string to an XObject. 
+   *  XPath namespace prefixes are resolved from the namespaceNode.
+   *  The implementation of this is a little slow, since it creates
+   *  a number of objects each time it is called.  This could be optimized
+   *  to keep the same objects around, but then thread-safety issues would arise.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
+   *  @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
+   *  @see org.apache.xpath.objects.XObject
+   *  @see org.apache.xpath.objects.XNull
+   *  @see org.apache.xpath.objects.XBoolean
+   *  @see org.apache.xpath.objects.XNumber
+   *  @see org.apache.xpath.objects.XString
+   *  @see org.apache.xpath.objects.XRTreeFrag
+   *
+   * @throws TransformerException
+   */
+  public  XObject eval(Node contextNode, String str, Node namespaceNode)
+          throws TransformerException
+  {
+
+    // Since we don't have a XML Parser involved here, install some default support
+    // for things like namespaces, etc.
+    // (Changed from: XPathContext xpathSupport = new XPathContext();
+    //    because XPathContext is weak in a number of areas... perhaps
+    //    XPathContext should be done away with.)
+
+    // Create an object to resolve namespace prefixes.
+    // XPath namespaces are resolved from the input context node's document element
+    // if it is a root node, or else the current context node (for lack of a better
+    // resolution space, given the simplicity of this sample code).
+    PrefixResolverDefault prefixResolver = new PrefixResolverDefault(
+      (namespaceNode.getNodeType() == Node.DOCUMENT_NODE)
+      ? ((Document) namespaceNode).getDocumentElement() : namespaceNode);
+
+    // Create the XPath object.
+    XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
+
+    // Execute the XPath, and have it return the result
+    // return xpath.execute(xpathSupport, contextNode, prefixResolver);
+    int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
+
+    return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
+  }
+
+  /**
+   *   Evaluate XPath string to an XObject.
+   *   XPath namespace prefixes are resolved from the namespaceNode.
+   *   The implementation of this is a little slow, since it creates
+   *   a number of objects each time it is called.  This could be optimized
+   *   to keep the same objects around, but then thread-safety issues would arise.
+   *
+   *   @param contextNode The node to start searching from.
+   *   @param str A valid XPath string.
+   *   @param prefixResolver Will be called if the parser encounters namespace
+   *                         prefixes, to resolve the prefixes to URLs.
+   *   @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
+   *   @see org.apache.xpath.objects.XObject
+   *   @see org.apache.xpath.objects.XNull
+   *   @see org.apache.xpath.objects.XBoolean
+   *   @see org.apache.xpath.objects.XNumber
+   *   @see org.apache.xpath.objects.XString
+   *   @see org.apache.xpath.objects.XRTreeFrag
+   *
+   * @throws TransformerException
+   */
+  public  XObject eval(
+          Node contextNode, String str, PrefixResolver prefixResolver)
+            throws TransformerException
+  {
+
+    // Since we don't have a XML Parser involved here, install some default support
+    // for things like namespaces, etc.
+    // (Changed from: XPathContext xpathSupport = new XPathContext();
+    //    because XPathContext is weak in a number of areas... perhaps
+    //    XPathContext should be done away with.)
+    // Create the XPath object.
+    XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
+
+    // Create an XPathContext that doesn't support pushing and popping of
+    // variable resolution scopes.  Sufficient for simple XPath 1.0 expressions.
+    XPathContext xpathSupport = new XPathContext(false);
+
+    // Execute the XPath, and have it return the result
+    int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
+
+    return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/Expression.java b/src/main/java/org/apache/xpath/Expression.java
new file mode 100644
index 0000000..2b38290
--- /dev/null
+++ b/src/main/java/org/apache/xpath/Expression.java
@@ -0,0 +1,589 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Expression.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+
+import org.xml.sax.ContentHandler;
+
+/**
+ * This abstract class serves as the base for all expression objects.  An
+ * Expression can be executed to return a {@link org.apache.xpath.objects.XObject},
+ * normally has a location within a document or DOM, can send error and warning
+ * events, and normally do not hold state and are meant to be immutable once
+ * construction has completed.  An exception to the immutibility rule is iterators
+ * and walkers, which must be cloned in order to be used -- the original must
+ * still be immutable.
+ */
+public abstract class Expression implements java.io.Serializable, ExpressionNode, XPathVisitable
+{
+    static final long serialVersionUID = 565665869777906902L;
+  /**
+   * The location where this expression was built from.  Need for diagnostic
+   *  messages. May be null.
+   *  @serial
+   */
+  private ExpressionNode m_parent;
+
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside
+   * the current subtree.
+   *
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+  public boolean canTraverseOutsideSubtree()
+  {
+    return false;
+  }
+  
+//  /**
+//   * Set the location where this expression was built from.
+//   *
+//   *
+//   * @param locator the location where this expression was built from, may be
+//   *                null.
+//   */
+//  public void setSourceLocator(SourceLocator locator)
+//  {
+//    m_slocator = locator;
+//  }
+
+  /**
+   * Execute an expression in the XPath runtime context, and return the
+   * result of the expression.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @param currentNode The currentNode.
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception
+   *         occurs.
+   */
+  public XObject execute(XPathContext xctxt, int currentNode)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // For now, the current node is already pushed.
+    return execute(xctxt);
+  }
+
+  /**
+   * Execute an expression in the XPath runtime context, and return the
+   * result of the expression.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @param currentNode The currentNode.
+   * @param dtm The DTM of the current node.
+   * @param expType The expanded type ID of the current node.
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception
+   *         occurs.
+   */
+  public XObject execute(
+          XPathContext xctxt, int currentNode, DTM dtm, int expType)
+            throws javax.xml.transform.TransformerException
+  {
+
+    // For now, the current node is already pushed.
+    return execute(xctxt);
+  }
+
+  /**
+   * Execute an expression in the XPath runtime context, and return the
+   * result of the expression.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception
+   *         occurs.
+   */
+  public abstract XObject execute(XPathContext xctxt)
+    throws javax.xml.transform.TransformerException;
+
+  /**
+   * Execute an expression in the XPath runtime context, and return the
+   * result of the expression, but tell that a "safe" object doesn't have 
+   * to be returned.  The default implementation just calls execute(xctxt).
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @param destructiveOK true if a "safe" object doesn't need to be returned.
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception
+   *         occurs.
+   */
+  public XObject execute(XPathContext xctxt, boolean destructiveOK)
+    throws javax.xml.transform.TransformerException
+  {
+  	return execute(xctxt);
+  }
+
+
+  /**
+   * Evaluate expression to a number.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @return The expression evaluated as a double.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return execute(xctxt).num();
+  }
+
+  /**
+   * Evaluate expression to a boolean.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @return false
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean bool(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return execute(xctxt).bool();
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @return The string this wraps or the empty string if null
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XMLString xstr(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return execute(xctxt).xstr();
+  }
+
+  /**
+   * Tell if the expression is a nodeset expression.  In other words, tell
+   * if you can execute {@link #asNode(XPathContext) asNode} without an exception.
+   * @return true if the expression can be represented as a nodeset.
+   */
+  public boolean isNodesetExpr()
+  {
+    return false;
+  }
+
+  /**
+   * Return the first node out of the nodeset, if this expression is
+   * a nodeset expression.
+   * @param xctxt The XPath runtime context.
+   * @return the first node out of the nodeset, or DTM.NULL.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public int asNode(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+  	DTMIterator iter = execute(xctxt).iter();
+    return iter.nextNode();
+  }
+
+  /**
+   * Given an select expression and a context, evaluate the XPath
+   * and return the resulting iterator.
+   *
+   * @param xctxt The execution context.
+   * @param contextNode The node that "." expresses.
+   *
+   *
+   * @return A valid DTMIterator.
+   * @throws TransformerException thrown if the active ProblemListener decides
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage experimental
+   */
+  public DTMIterator asIterator(XPathContext xctxt, int contextNode)
+          throws javax.xml.transform.TransformerException
+  {
+
+    try
+    {
+      xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
+
+      return execute(xctxt).iter();
+    }
+    finally
+    {
+      xctxt.popCurrentNodeAndExpression();
+    }
+  }
+  
+  /**
+   * Given an select expression and a context, evaluate the XPath
+   * and return the resulting iterator, but do not clone.
+   *
+   * @param xctxt The execution context.
+   * @param contextNode The node that "." expresses.
+   *
+   *
+   * @return A valid DTMIterator.
+   * @throws TransformerException thrown if the active ProblemListener decides
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage experimental
+   */
+  public DTMIterator asIteratorRaw(XPathContext xctxt, int contextNode)
+          throws javax.xml.transform.TransformerException
+  {
+
+    try
+    {
+      xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
+
+      XNodeSet nodeset = (XNodeSet)execute(xctxt);
+      return nodeset.iterRaw();
+    }
+    finally
+    {
+      xctxt.popCurrentNodeAndExpression();
+    }
+  }
+
+
+  /**
+   * Execute an expression in the XPath runtime context, and return the
+   * result of the expression.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * NEEDSDOC @param handler
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception
+   *         occurs.
+   * @throws org.xml.sax.SAXException
+   */
+  public void executeCharsToContentHandler(
+          XPathContext xctxt, ContentHandler handler)
+            throws javax.xml.transform.TransformerException,
+                   org.xml.sax.SAXException
+  {
+
+    XObject obj = execute(xctxt);
+
+    obj.dispatchCharactersEvents(handler);
+    obj.detach();
+  }
+
+  /**
+   * Tell if this expression returns a stable number that will not change during 
+   * iterations within the expression.  This is used to determine if a proximity 
+   * position predicate can indicate that no more searching has to occur.
+   * 
+   *
+   * @return true if the expression represents a stable number.
+   */
+  public boolean isStableNumber()
+  {
+    return false;
+  }
+
+  /**
+   * This function is used to fixup variables from QNames to stack frame
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list
+   * should be searched backwards for the first qualified name that
+   * corresponds to the variable reference qname.  The position of the
+   * QName in the vector from the start of the vector will be its position
+   * in the stack frame (but variables above the globalsTop value will need
+   * to be offset to the current stack frame).
+   * NEEDSDOC @param globalsSize
+   */
+  public abstract void fixupVariables(java.util.Vector vars, int globalsSize);
+  
+  /**
+   * Compare this object with another object and see 
+   * if they are equal, include the sub heararchy.
+   * 
+   * @param expr Another expression object.
+   * @return true if this objects class and the expr
+   * object's class are the same, and the data contained 
+   * within both objects are considered equal.
+   */
+  public abstract boolean deepEquals(Expression expr);
+  
+  /**
+   * This is a utility method to tell if the passed in 
+   * class is the same class as this.  It is to be used by
+   * the deepEquals method.  I'm bottlenecking it here 
+   * because I'm not totally confident that comparing the 
+   * class objects is the best way to do this.
+   * @return true of the passed in class is the exact same 
+   * class as this class.
+   */
+  protected final boolean isSameClass(Expression expr)
+  {
+  	if(null == expr)
+  	  return false;
+  	  
+  	return (getClass() == expr.getClass());
+  }
+
+  /**
+   * Warn the user of an problem.
+   *
+   * @param xctxt The XPath runtime context.
+   * @param msg An error msgkey that corresponds to one of the conststants found
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to
+   *                              throw an exception.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void warn(XPathContext xctxt, String msg, Object[] args)
+          throws javax.xml.transform.TransformerException
+  {
+
+    java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args);
+
+    if (null != xctxt)
+    {
+      ErrorListener eh = xctxt.getErrorListener();
+
+      // TO DO: Need to get stylesheet Locator from here.
+      eh.warning(new TransformerException(fmsg, xctxt.getSAXLocator()));
+    }
+  }
+
+  /**
+   * Tell the user of an assertion error, and probably throw an
+   * exception.
+   *
+   * @param b  If false, a runtime exception will be thrown.
+   * @param msg The assertion message, which should be informative.
+   *
+   * @throws RuntimeException if the b argument is false.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void assertion(boolean b, java.lang.String msg)
+  {
+
+    if (!b)
+    {
+      java.lang.String fMsg = XSLMessages.createXPATHMessage(
+        XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
+        new Object[]{ msg });
+
+      throw new RuntimeException(fMsg);
+    }
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param xctxt The XPath runtime context.
+   * @param msg An error msgkey that corresponds to one of the constants found
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to
+   *                              throw an exception.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void error(XPathContext xctxt, String msg, Object[] args)
+          throws javax.xml.transform.TransformerException
+  {
+
+    java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args);
+
+    if (null != xctxt)
+    {
+      ErrorListener eh = xctxt.getErrorListener();
+      TransformerException te = new TransformerException(fmsg, this);
+
+      eh.fatalError(te);
+    }
+  }
+  
+  /**
+   * Get the first non-Expression parent of this node.
+   * @return null or first ancestor that is not an Expression.
+   */
+  public ExpressionNode getExpressionOwner()
+  {
+  	ExpressionNode parent = exprGetParent();
+  	while((null != parent) && (parent instanceof Expression))
+  		parent = parent.exprGetParent();
+  	return parent;
+  }
+  
+  //=============== ExpressionNode methods ================
+  
+  /** This pair of methods are used to inform the node of its
+    parent. */
+  public void exprSetParent(ExpressionNode n)
+  {
+  	assertion(n != this, "Can not parent an expression to itself!");
+  	m_parent = n;
+  }
+  
+  public ExpressionNode exprGetParent()
+  {
+  	return m_parent;
+  }
+
+  /** This method tells the node to add its argument to the node's
+    list of children.  */
+  public void exprAddChild(ExpressionNode n, int i)
+  {
+  	assertion(false, "exprAddChild method not implemented!");
+  }
+
+  /** This method returns a child node.  The children are numbered
+     from zero, left to right. */
+  public ExpressionNode exprGetChild(int i)
+  {
+  	return null;
+  }
+
+  /** Return the number of children the node has. */
+  public int exprGetNumChildren()
+  {
+  	return 0;
+  }
+  
+  //=============== SourceLocator methods ================
+
+  /**
+   * Return the public identifier for the current document event.
+   *
+   * <p>The return value is the public identifier of the document
+   * entity or of the external parsed entity in which the markup that
+   * triggered the event appears.</p>
+   *
+   * @return A string containing the public identifier, or
+   *         null if none is available.
+   * @see #getSystemId
+   */
+  public String getPublicId()
+  {
+  	if(null == m_parent)
+  	  return null;
+  	return m_parent.getPublicId();
+  }
+
+  /**
+   * Return the system identifier for the current document event.
+   *
+   * <p>The return value is the system identifier of the document
+   * entity or of the external parsed entity in which the markup that
+   * triggered the event appears.</p>
+   *
+   * <p>If the system identifier is a URL, the parser must resolve it
+   * fully before passing it to the application.</p>
+   *
+   * @return A string containing the system identifier, or null
+   *         if none is available.
+   * @see #getPublicId
+   */
+  public String getSystemId()
+  {
+  	if(null == m_parent)
+  	  return null;
+  	return m_parent.getSystemId();
+  }
+
+  /**
+   * Return the line number where the current document event ends.
+   *
+   * <p><strong>Warning:</strong> The return value from the method
+   * is intended only as an approximation for the sake of error
+   * reporting; it is not intended to provide sufficient information
+   * to edit the character content of the original XML document.</p>
+   *
+   * <p>The return value is an approximation of the line number
+   * in the document entity or external parsed entity where the
+   * markup that triggered the event appears.</p>
+   *
+   * @return The line number, or -1 if none is available.
+   * @see #getColumnNumber
+   */
+  public int getLineNumber()
+  {
+  	if(null == m_parent)
+  	  return 0;
+  	return m_parent.getLineNumber();
+  }
+
+  /**
+   * Return the character position where the current document event ends.
+   *
+   * <p><strong>Warning:</strong> The return value from the method
+   * is intended only as an approximation for the sake of error
+   * reporting; it is not intended to provide sufficient information
+   * to edit the character content of the original XML document.</p>
+   *
+   * <p>The return value is an approximation of the column number
+   * in the document entity or external parsed entity where the
+   * markup that triggered the event appears.</p>
+   *
+   * @return The column number, or -1 if none is available.
+   * @see #getLineNumber
+   */
+  public int getColumnNumber()
+  {
+  	if(null == m_parent)
+  	  return 0;
+  	return m_parent.getColumnNumber();
+  }
+}
diff --git a/src/main/java/org/apache/xpath/ExpressionNode.java b/src/main/java/org/apache/xpath/ExpressionNode.java
new file mode 100644
index 0000000..d415867
--- /dev/null
+++ b/src/main/java/org/apache/xpath/ExpressionNode.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExpressionNode.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * A class that implements this interface can construct expressions, 
+ * give information about child and parent expressions,
+ * and give the originating source information.  A class that implements 
+ * this interface does not lay any claim to being directly executable.
+ * 
+ * <p>Note: This interface should not be considered stable.  Only exprSetParent 
+ * and exprGetParent can be counted on to work reliably.  Work in progress.</p>
+ */
+public interface ExpressionNode extends SourceLocator
+{
+  /** This pair of methods are used to inform the node of its
+    parent. */
+  public void exprSetParent(ExpressionNode n);
+  public ExpressionNode exprGetParent();
+
+  /** This method tells the node to add its argument to the node's
+    list of children.  */
+  public void exprAddChild(ExpressionNode n, int i);
+
+  /** This method returns a child node.  The children are numbered
+     from zero, left to right. */
+  public ExpressionNode exprGetChild(int i);
+
+  /** Return the number of children the node has. */
+  public int exprGetNumChildren();
+}
+
diff --git a/src/main/java/org/apache/xpath/ExpressionOwner.java b/src/main/java/org/apache/xpath/ExpressionOwner.java
new file mode 100644
index 0000000..54c1b13
--- /dev/null
+++ b/src/main/java/org/apache/xpath/ExpressionOwner.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExpressionOwner.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+/**
+ * Classes that implement this interface own an expression, which 
+ * can be rewritten.
+ */
+public interface ExpressionOwner
+{
+  /**
+   * Get the raw Expression object that this class wraps.
+   *
+   * @return the raw Expression object, which should not normally be null.
+   */
+  public Expression getExpression();
+
+  /**
+   * Set the raw expression object for this object.
+   *
+   * @param exp the raw Expression object, which should not normally be null.
+   */
+  public void setExpression(Expression exp);
+
+
+}
+
diff --git a/src/main/java/org/apache/xpath/ExtensionsProvider.java b/src/main/java/org/apache/xpath/ExtensionsProvider.java
new file mode 100644
index 0000000..c49d14e
--- /dev/null
+++ b/src/main/java/org/apache/xpath/ExtensionsProvider.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ExtensionsProvider.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import java.util.Vector;
+
+import org.apache.xpath.functions.FuncExtFunction;
+
+/**
+ * Interface that XPath objects can call to obtain access to an 
+ * ExtensionsTable.
+ * 
+ */
+public interface ExtensionsProvider
+{
+  /**
+   * Is the extension function available?
+   */
+  
+  public boolean functionAvailable(String ns, String funcName)
+          throws javax.xml.transform.TransformerException;
+  
+  /**
+   * Is the extension element available?
+   */
+  public boolean elementAvailable(String ns, String elemName)
+          throws javax.xml.transform.TransformerException;
+   
+  /**
+   * Execute the extension function.
+   */
+  public Object extFunction(String ns, String funcName, 
+                            Vector argVec, Object methodKey)
+            throws javax.xml.transform.TransformerException;
+
+  /**
+   * Execute the extension function.
+   */
+  public Object extFunction(FuncExtFunction extFunction, 
+                            Vector argVec)
+            throws javax.xml.transform.TransformerException;
+}
diff --git a/src/main/java/org/apache/xpath/FoundIndex.java b/src/main/java/org/apache/xpath/FoundIndex.java
new file mode 100644
index 0000000..67ec811
--- /dev/null
+++ b/src/main/java/org/apache/xpath/FoundIndex.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FoundIndex.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+/**
+ * Class to let us know when it's time to do
+ * a search from the parent because of indexing.
+ * @xsl.usage internal
+ */
+public class FoundIndex extends RuntimeException
+{
+    static final long serialVersionUID = -4643975335243078270L;
+
+  /**
+   * Constructor FoundIndex
+   *
+   */
+  public FoundIndex(){}
+}
diff --git a/src/main/java/org/apache/xpath/NodeSet.java b/src/main/java/org/apache/xpath/NodeSet.java
new file mode 100644
index 0000000..d1c7f80
--- /dev/null
+++ b/src/main/java/org/apache/xpath/NodeSet.java
@@ -0,0 +1,1370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeSet.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.utils.DOM2Helper;
+import org.apache.xpath.axes.ContextNodeList;
+import org.apache.xpath.res.XPATHErrorResources;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.traversal.NodeFilter;
+import org.w3c.dom.traversal.NodeIterator;
+
+
+/**
+ * <p>The NodeSet class can act as either a NodeVector,
+ * NodeList, or NodeIterator.  However, in order for it to
+ * act as a NodeVector or NodeList, it's required that
+ * setShouldCacheNodes(true) be called before the first
+ * nextNode() is called, in order that nodes can be added
+ * as they are fetched.  Derived classes that implement iterators
+ * must override runTo(int index), in order that they may
+ * run the iteration to the given index. </p>
+ * 
+ * <p>Note that we directly implement the DOM's NodeIterator
+ * interface. We do not emulate all the behavior of the
+ * standard NodeIterator. In particular, we do not guarantee
+ * to present a "live view" of the document ... but in XSLT,
+ * the source document should never be mutated, so this should
+ * never be an issue.</p>
+ * 
+ * <p>Thought: Should NodeSet really implement NodeList and NodeIterator,
+ * or should there be specific subclasses of it which do so? The
+ * advantage of doing it all here is that all NodeSets will respond
+ * to the same calls; the disadvantage is that some of them may return
+ * less-than-enlightening results when you do so.</p>
+ * @xsl.usage advanced
+ */
+public class NodeSet
+        implements NodeList, NodeIterator, Cloneable, ContextNodeList
+{
+
+  /**
+   * Create an empty nodelist.
+   */
+  public NodeSet()
+  {
+    m_blocksize = 32;
+    m_mapSize = 0;
+  }
+
+  /**
+   * Create an empty, using the given block size.
+   *
+   * @param blocksize Size of blocks to allocate 
+   */
+  public NodeSet(int blocksize)
+  {
+    m_blocksize = blocksize;
+    m_mapSize = 0;
+  }
+
+  /**
+   * Create a NodeSet, and copy the members of the
+   * given nodelist into it.
+   *
+   * @param nodelist List of Nodes to be made members of the new set.
+   */
+  public NodeSet(NodeList nodelist)
+  {
+
+    this(32);
+
+    addNodes(nodelist);
+  }
+
+  /**
+   * Create a NodeSet, and copy the members of the
+   * given NodeSet into it.
+   *
+   * @param nodelist Set of Nodes to be made members of the new set.
+   */
+  public NodeSet(NodeSet nodelist)
+  {
+
+    this(32);
+
+    addNodes((NodeIterator) nodelist);
+  }
+
+  /**
+   * Create a NodeSet, and copy the members of the
+   * given NodeIterator into it.
+   *
+   * @param ni Iterator which yields Nodes to be made members of the new set.
+   */
+  public NodeSet(NodeIterator ni)
+  {
+
+    this(32);
+
+    addNodes(ni);
+  }
+
+  /**
+   * Create a NodeSet which contains the given Node.
+   *
+   * @param node Single node to be added to the new set.
+   */
+  public NodeSet(Node node)
+  {
+
+    this(32);
+
+    addNode(node);
+  }
+
+  /**
+   * @return The root node of the Iterator, as specified when it was created.
+   * For non-Iterator NodeSets, this will be null.
+   */
+  public Node getRoot()
+  {
+    return null;
+  }
+
+  /**
+   * Get a cloned Iterator, and reset its state to the beginning of the
+   * iteration.
+   *
+   * @return a new NodeSet of the same type, having the same state...
+   * except that the reset() operation has been called.
+   *
+   * @throws CloneNotSupportedException if this subclass of NodeSet
+   * does not support the clone() operation.
+   */
+  public NodeIterator cloneWithReset() throws CloneNotSupportedException
+  {
+
+    NodeSet clone = (NodeSet) clone();
+
+    clone.reset();
+
+    return clone;
+  }
+
+  /**
+   * Reset the iterator. May have no effect on non-iterator Nodesets.
+   */
+  public void reset()
+  {
+    m_next = 0;
+  }
+
+  /**
+   *  This attribute determines which node types are presented via the
+   * iterator. The available set of constants is defined in the
+   * <code>NodeFilter</code> interface. For NodeSets, the mask has been
+   * hardcoded to show all nodes except EntityReference nodes, which have
+   * no equivalent in the XPath data model.
+   *
+   * @return integer used as a bit-array, containing flags defined in
+   * the DOM's NodeFilter class. The value will be 
+   * <code>SHOW_ALL & ~SHOW_ENTITY_REFERENCE</code>, meaning that
+   * only entity references are suppressed.
+   */
+  public int getWhatToShow()
+  {
+    return NodeFilter.SHOW_ALL & ~NodeFilter.SHOW_ENTITY_REFERENCE;
+  }
+
+  /**
+   * The filter object used to screen nodes. Filters are applied to
+   * further reduce (and restructure) the NodeIterator's view of the
+   * document. In our case, we will be using hardcoded filters built
+   * into our iterators... but getFilter() is part of the DOM's 
+   * NodeIterator interface, so we have to support it.
+   *
+   * @return null, which is slightly misleading. True, there is no
+   * user-written filter object, but in fact we are doing some very
+   * sophisticated custom filtering. A DOM purist might suggest
+   * returning a placeholder object just to indicate that this is
+   * not going to return all nodes selected by whatToShow.
+   */
+  public NodeFilter getFilter()
+  {
+    return null;
+  }
+
+  /**
+   *  The value of this flag determines whether the children of entity
+   * reference nodes are visible to the iterator. If false, they will be
+   * skipped over.
+   * <br> To produce a view of the document that has entity references
+   * expanded and does not expose the entity reference node itself, use the
+   * whatToShow flags to hide the entity reference node and set
+   * expandEntityReferences to true when creating the iterator. To produce
+   * a view of the document that has entity reference nodes but no entity
+   * expansion, use the whatToShow flags to show the entity reference node
+   * and set expandEntityReferences to false.
+   *
+   * @return true for all iterators based on NodeSet, meaning that the
+   * contents of EntityRefrence nodes may be returned (though whatToShow
+   * says that the EntityReferences themselves are not shown.)
+   */
+  public boolean getExpandEntityReferences()
+  {
+    return true;
+  }
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   * @throws DOMException
+   *    INVALID_STATE_ERR: Raised if this method is called after the
+   *   <code>detach</code> method was invoked.
+   */
+  public Node nextNode() throws DOMException
+  {
+
+    if ((m_next) < this.size())
+    {
+      Node next = this.elementAt(m_next);
+
+      m_next++;
+
+      return next;
+    }
+    else
+      return null;
+  }
+
+  /**
+   *  Returns the previous node in the set and moves the position of the
+   * iterator backwards in the set.
+   * @return  The previous <code>Node</code> in the set being iterated over,
+   *   or<code>null</code> if there are no more members in that set.
+   * @throws DOMException
+   *    INVALID_STATE_ERR: Raised if this method is called after the
+   *   <code>detach</code> method was invoked.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a cached type, and hence doesn't know what the previous node was.
+   */
+  public Node previousNode() throws DOMException
+  {
+
+    if (!m_cacheNodes)
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_CANNOT_ITERATE, null)); //"This NodeSet can not iterate to a previous node!");
+
+    if ((m_next - 1) > 0)
+    {
+      m_next--;
+
+      return this.elementAt(m_next);
+    }
+    else
+      return null;
+  }
+
+  /**
+   * Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   * <p>
+   * This operation is a no-op in NodeSet, and will not cause 
+   * INVALID_STATE_ERR to be raised by later operations.
+   * </p>
+   */
+  public void detach(){}
+
+  /**
+   * Tells if this NodeSet is "fresh", in other words, if
+   * the first nextNode() that is called will return the
+   * first node in the set.
+   *
+   * @return true if nextNode() would return the first node in the set,
+   * false if it would return a later one.
+   */
+  public boolean isFresh()
+  {
+    return (m_next == 0);
+  }
+
+  /**
+   * If an index is requested, NodeSet will call this method
+   * to run the iterator to the index.  By default this sets
+   * m_next to the index.  If the index argument is -1, this
+   * signals that the iterator should be run to the end.
+   *
+   * @param index Position to advance (or retreat) to, with
+   * 0 requesting the reset ("fresh") position and -1 (or indeed
+   * any out-of-bounds value) requesting the final position.
+   * @throws RuntimeException thrown if this NodeSet is not
+   * one of the types which supports indexing/counting.
+   */
+  public void runTo(int index)
+  {
+
+    if (!m_cacheNodes)
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_CANNOT_INDEX, null)); //"This NodeSet can not do indexing or counting functions!");
+
+    if ((index >= 0) && (m_next < m_firstFree))
+      m_next = index;
+    else
+      m_next = m_firstFree - 1;
+  }
+
+  /**
+   * Returns the <code>index</code>th item in the collection. If
+   * <code>index</code> is greater than or equal to the number of nodes in
+   * the list, this returns <code>null</code>.
+   * 
+   * TODO: What happens if index is out of range?
+   * 
+   * @param index Index into the collection.
+   * @return The node at the <code>index</code>th position in the
+   *   <code>NodeList</code>, or <code>null</code> if that is not a valid
+   *   index.
+   */
+  public Node item(int index)
+  {
+
+    runTo(index);
+
+    return (Node) this.elementAt(index);
+  }
+
+  /**
+   * The number of nodes in the list. The range of valid child node indices is
+   * 0 to <code>length-1</code> inclusive. Note that this operation requires
+   * finding all the matching nodes, which may defeat attempts to defer
+   * that work.
+   *
+   * @return integer indicating how many nodes are represented by this list.
+   */
+  public int getLength()
+  {
+
+    runTo(-1);
+
+    return this.size();
+  }
+
+  /**
+   * Add a node to the NodeSet. Not all types of NodeSets support this
+   * operation
+   *
+   * @param n Node to be added
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public void addNode(Node n)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    this.addElement(n);
+  }
+
+  /**
+   * Insert a node at a given position.
+   *
+   * @param n Node to be added
+   * @param pos Offset at which the node is to be inserted,
+   * with 0 being the first position.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public void insertNode(Node n, int pos)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    insertElementAt(n, pos);
+  }
+
+  /**
+   * Remove a node.
+   *
+   * @param n Node to be added
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public void removeNode(Node n)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    this.removeElement(n);
+  }
+
+  /**
+   * Copy NodeList members into this nodelist, adding in
+   * document order.  If a node is null, don't add it.
+   *
+   * @param nodelist List of nodes which should now be referenced by
+   * this NodeSet.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public void addNodes(NodeList nodelist)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    if (null != nodelist)  // defensive to fix a bug that Sanjiva reported.
+    {
+      int nChildren = nodelist.getLength();
+
+      for (int i = 0; i < nChildren; i++)
+      {
+        Node obj = nodelist.item(i);
+
+        if (null != obj)
+        {
+          addElement(obj);
+        }
+      }
+    }
+
+    // checkDups();
+  }
+
+  /**
+   * <p>Copy NodeList members into this nodelist, adding in
+   * document order.  Only genuine node references will be copied;
+   * nulls appearing in the source NodeSet will
+   * not be added to this one. </p>
+   * 
+   * <p> In case you're wondering why this function is needed: NodeSet
+   * implements both NodeIterator and NodeList. If this method isn't
+   * provided, Java can't decide which of those to use when addNodes()
+   * is invoked. Providing the more-explicit match avoids that
+   * ambiguity.)</p>
+   *
+   * @param ns NodeSet whose members should be merged into this NodeSet.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public void addNodes(NodeSet ns)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    addNodes((NodeIterator) ns);
+  }
+
+  /**
+   * Copy NodeList members into this nodelist, adding in
+   * document order.  Null references are not added.
+   *
+   * @param iterator NodeIterator which yields the nodes to be added.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public void addNodes(NodeIterator iterator)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    if (null != iterator)  // defensive to fix a bug that Sanjiva reported.
+    {
+      Node obj;
+
+      while (null != (obj = iterator.nextNode()))
+      {
+        addElement(obj);
+      }
+    }
+
+    // checkDups();
+  }
+
+  /**
+   * Copy NodeList members into this nodelist, adding in
+   * document order.  If a node is null, don't add it.
+   *
+   * @param nodelist List of nodes to be added
+   * @param support The XPath runtime context.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public void addNodesInDocOrder(NodeList nodelist, XPathContext support)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    int nChildren = nodelist.getLength();
+
+    for (int i = 0; i < nChildren; i++)
+    {
+      Node node = nodelist.item(i);
+
+      if (null != node)
+      {
+        addNodeInDocOrder(node, support);
+      }
+    }
+  }
+
+  /**
+   * Copy NodeList members into this nodelist, adding in
+   * document order.  If a node is null, don't add it.
+   *
+   * @param iterator NodeIterator which yields the nodes to be added.
+   * @param support The XPath runtime context.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public void addNodesInDocOrder(NodeIterator iterator, XPathContext support)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    Node node;
+
+    while (null != (node = iterator.nextNode()))
+    {
+      addNodeInDocOrder(node, support);
+    }
+  }
+
+  /**
+   * Add the node list to this node set in document order.
+   *
+   * @param start index.
+   * @param end index.
+   * @param testIndex index.
+   * @param nodelist The nodelist to add.
+   * @param support The XPath runtime context.
+   *
+   * @return false always.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  private boolean addNodesInDocOrder(int start, int end, int testIndex,
+                                     NodeList nodelist, XPathContext support)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    boolean foundit = false;
+    int i;
+    Node node = nodelist.item(testIndex);
+
+    for (i = end; i >= start; i--)
+    {
+      Node child = (Node) elementAt(i);
+
+      if (child == node)
+      {
+        i = -2;  // Duplicate, suppress insert
+
+        break;
+      }
+
+      if (!DOM2Helper.isNodeAfter(node, child))
+      {
+        insertElementAt(node, i + 1);
+
+        testIndex--;
+
+        if (testIndex > 0)
+        {
+          boolean foundPrev = addNodesInDocOrder(0, i, testIndex, nodelist,
+                                                 support);
+
+          if (!foundPrev)
+          {
+            addNodesInDocOrder(i, size() - 1, testIndex, nodelist, support);
+          }
+        }
+
+        break;
+      }
+    }
+
+    if (i == -1)
+    {
+      insertElementAt(node, 0);
+    }
+
+    return foundit;
+  }
+
+  /**
+   * Add the node into a vector of nodes where it should occur in
+   * document order.
+   * @param node The node to be added.
+   * @param test true if we should test for doc order
+   * @param support The XPath runtime context.
+   * @return insertIndex.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public int addNodeInDocOrder(Node node, boolean test, XPathContext support)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    int insertIndex = -1;
+
+    if (test)
+    {
+
+      // This needs to do a binary search, but a binary search 
+      // is somewhat tough because the sequence test involves 
+      // two nodes.
+      int size = size(), i;
+
+      for (i = size - 1; i >= 0; i--)
+      {
+        Node child = (Node) elementAt(i);
+
+        if (child == node)
+        {
+          i = -2;  // Duplicate, suppress insert
+
+          break;
+        }
+
+        if (!DOM2Helper.isNodeAfter(node, child))
+        {
+          break;
+        }
+      }
+
+      if (i != -2)
+      {
+        insertIndex = i + 1;
+
+        insertElementAt(node, insertIndex);
+      }
+    }
+    else
+    {
+      insertIndex = this.size();
+
+      boolean foundit = false;
+
+      for (int i = 0; i < insertIndex; i++)
+      {
+        if (this.item(i).equals(node))
+        {
+          foundit = true;
+
+          break;
+        }
+      }
+
+      if (!foundit)
+        addElement(node);
+    }
+
+    // checkDups();
+    return insertIndex;
+  }  // end addNodeInDocOrder(Vector v, Object obj)
+
+  /**
+   * Add the node into a vector of nodes where it should occur in
+   * document order.
+   * @param node The node to be added.
+   * @param support The XPath runtime context.
+   *
+   * @return The index where it was inserted.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a mutable type.
+   */
+  public int addNodeInDocOrder(Node node, XPathContext support)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    return addNodeInDocOrder(node, true, support);
+  }  // end addNodeInDocOrder(Vector v, Object obj)
+
+
+  /** If this node is being used as an iterator, the next index that nextNode()
+   *  will return.  */
+  transient protected int m_next = 0;
+
+  /**
+   * Get the current position, which is one less than
+   * the next nextNode() call will retrieve.  i.e. if
+   * you call getCurrentPos() and the return is 0, the next
+   * fetch will take place at index 1.
+   *
+   * @return The the current position index.
+   */
+  public int getCurrentPos()
+  {
+    return m_next;
+  }
+
+  /**
+   * Set the current position in the node set.
+   * @param i Must be a valid index.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a cached type, and thus doesn't permit indexed access.
+   */
+  public void setCurrentPos(int i)
+  {
+
+    if (!m_cacheNodes)
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_CANNOT_INDEX, null)); //"This NodeSet can not do indexing or counting functions!");
+
+    m_next = i;
+  }
+
+  /**
+   * Return the last fetched node.  Needed to support the UnionPathIterator.
+   *
+   * @return the last fetched node.
+   * @throws RuntimeException thrown if this NodeSet is not of 
+   * a cached type, and thus doesn't permit indexed access.
+   */
+  public Node getCurrentNode()
+  {
+
+    if (!m_cacheNodes)
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_CANNOT_INDEX, null)); //"This NodeSet can not do indexing or counting functions!");
+
+    int saved = m_next;
+    Node n = (m_next < m_firstFree) ? elementAt(m_next) : null;
+    m_next = saved; // HACK: I think this is a bit of a hack.  -sb
+    return n;
+  }
+
+  /** True if this list can be mutated.  */
+  transient protected boolean m_mutable = true;
+
+  /** True if this list is cached.
+   *  @serial  */
+  transient protected boolean m_cacheNodes = true;
+
+  /**
+   * Get whether or not this is a cached node set.
+   *
+   *
+   * @return True if this list is cached.
+   */
+  public boolean getShouldCacheNodes()
+  {
+    return m_cacheNodes;
+  }
+
+  /**
+   * If setShouldCacheNodes(true) is called, then nodes will
+   * be cached.  They are not cached by default. This switch must
+   * be set before the first call to nextNode is made, to ensure
+   * that all nodes are cached.
+   *
+   * @param b true if this node set should be cached.
+   * @throws RuntimeException thrown if an attempt is made to
+   * request caching after we've already begun stepping through the
+   * nodes in this set.
+  */
+  public void setShouldCacheNodes(boolean b)
+  {
+
+    if (!isFresh())
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_CALL_SETSHOULDCACHENODE, null)); //"Can not call setShouldCacheNodes after nextNode has been called!");
+
+    m_cacheNodes = b;
+    m_mutable = true;
+  }
+  
+  
+  transient private int m_last = 0;
+  
+  public int getLast()
+  {
+    return m_last;
+  }
+  
+  public void setLast(int last)
+  {
+    m_last = last;
+  }
+  
+  /** Size of blocks to allocate.
+   *  @serial          */
+  private int m_blocksize;
+
+  /** Array of nodes this points to.
+   *  @serial          */
+  Node m_map[];
+
+  /** Number of nodes in this NodeVector.
+   *  @serial          */
+  protected int m_firstFree = 0;
+
+  /** Size of the array this points to.
+   *  @serial           */
+  private int m_mapSize;  // lazy initialization
+
+  /**
+   * Get a cloned LocPathIterator.
+   *
+   * @return A clone of this
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+
+    NodeSet clone = (NodeSet) super.clone();
+
+    if ((null != this.m_map) && (this.m_map == clone.m_map))
+    {
+      clone.m_map = new Node[this.m_map.length];
+
+      System.arraycopy(this.m_map, 0, clone.m_map, 0, this.m_map.length);
+    }
+
+    return clone;
+  }
+
+  /**
+   * Get the length of the list.
+   *
+   * @return Number of nodes in this NodeVector
+   */
+  public int size()
+  {
+    return m_firstFree;
+  }
+
+  /**
+   * Append a Node onto the vector.
+   *
+   * @param value Node to add to the vector
+   */
+  public void addElement(Node value)
+  {
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    if ((m_firstFree + 1) >= m_mapSize)
+    {
+      if (null == m_map)
+      {
+        m_map = new Node[m_blocksize];
+        m_mapSize = m_blocksize;
+      }
+      else
+      {
+        m_mapSize += m_blocksize;
+
+        Node newMap[] = new Node[m_mapSize];
+
+        System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+        m_map = newMap;
+      }
+    }
+
+    m_map[m_firstFree] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Append a Node onto the vector.
+   *
+   * @param value Node to add to the vector
+   */
+  public final void push(Node value)
+  {
+
+    int ff = m_firstFree;
+
+    if ((ff + 1) >= m_mapSize)
+    {
+      if (null == m_map)
+      {
+        m_map = new Node[m_blocksize];
+        m_mapSize = m_blocksize;
+      }
+      else
+      {
+        m_mapSize += m_blocksize;
+
+        Node newMap[] = new Node[m_mapSize];
+
+        System.arraycopy(m_map, 0, newMap, 0, ff + 1);
+
+        m_map = newMap;
+      }
+    }
+
+    m_map[ff] = value;
+
+    ff++;
+
+    m_firstFree = ff;
+  }
+
+  /**
+   * Pop a node from the tail of the vector and return the result.
+   *
+   * @return the node at the tail of the vector
+   */
+  public final Node pop()
+  {
+
+    m_firstFree--;
+
+    Node n = m_map[m_firstFree];
+
+    m_map[m_firstFree] = null;
+
+    return n;
+  }
+
+  /**
+   * Pop a node from the tail of the vector and return the
+   * top of the stack after the pop.
+   *
+   * @return The top of the stack after it's been popped 
+   */
+  public final Node popAndTop()
+  {
+
+    m_firstFree--;
+
+    m_map[m_firstFree] = null;
+
+    return (m_firstFree == 0) ? null : m_map[m_firstFree - 1];
+  }
+
+  /**
+   * Pop a node from the tail of the vector.
+   */
+  public final void popQuick()
+  {
+
+    m_firstFree--;
+
+    m_map[m_firstFree] = null;
+  }
+
+  /**
+   * Return the node at the top of the stack without popping the stack.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @return Node at the top of the stack or null if stack is empty.  
+   */
+  public final Node peepOrNull()
+  {
+    return ((null != m_map) && (m_firstFree > 0))
+           ? m_map[m_firstFree - 1] : null;
+  }
+
+  /**
+   * Push a pair of nodes into the stack.  
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @param v1 First node to add to vector
+   * @param v2 Second node to add to vector
+   */
+  public final void pushPair(Node v1, Node v2)
+  {
+
+    if (null == m_map)
+    {
+      m_map = new Node[m_blocksize];
+      m_mapSize = m_blocksize;
+    }
+    else
+    {
+      if ((m_firstFree + 2) >= m_mapSize)
+      {
+        m_mapSize += m_blocksize;
+
+        Node newMap[] = new Node[m_mapSize];
+
+        System.arraycopy(m_map, 0, newMap, 0, m_firstFree);
+
+        m_map = newMap;
+      }
+    }
+
+    m_map[m_firstFree] = v1;
+    m_map[m_firstFree + 1] = v2;
+    m_firstFree += 2;
+  }
+
+  /**
+   * Pop a pair of nodes from the tail of the stack. 
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   */
+  public final void popPair()
+  {
+
+    m_firstFree -= 2;
+    m_map[m_firstFree] = null;
+    m_map[m_firstFree + 1] = null;
+  }
+
+  /**
+   * Set the tail of the stack to the given node.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @param n Node to set at the tail of vector
+   */
+  public final void setTail(Node n)
+  {
+    m_map[m_firstFree - 1] = n;
+  }
+
+  /**
+   * Set the given node one position from the tail.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @param n Node to set
+   */
+  public final void setTailSub1(Node n)
+  {
+    m_map[m_firstFree - 2] = n;
+  }
+
+  /**
+   * Return the node at the tail of the vector without popping
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @return Node at the tail of the vector
+   */
+  public final Node peepTail()
+  {
+    return m_map[m_firstFree - 1];
+  }
+
+  /**
+   * Return the node one position from the tail without popping.
+   * Special purpose method for TransformerImpl, pushElemTemplateElement.
+   * Performance critical.
+   *
+   * @return Node one away from the tail
+   */
+  public final Node peepTailSub1()
+  {
+    return m_map[m_firstFree - 2];
+  }
+
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   *
+   * @param value Node to insert
+   * @param at Position where to insert
+   */
+  public void insertElementAt(Node value, int at)
+  {
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    if (null == m_map)
+    {
+      m_map = new Node[m_blocksize];
+      m_mapSize = m_blocksize;
+    }
+    else if ((m_firstFree + 1) >= m_mapSize)
+    {
+      m_mapSize += m_blocksize;
+
+      Node newMap[] = new Node[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
+
+      m_map = newMap;
+    }
+
+    if (at <= (m_firstFree - 1))
+    {
+      System.arraycopy(m_map, at, m_map, at + 1, m_firstFree - at);
+    }
+
+    m_map[at] = value;
+
+    m_firstFree++;
+  }
+
+  /**
+   * Append the nodes to the list.
+   *
+   * @param nodes NodeVector to append to this list
+   */
+  public void appendNodes(NodeSet nodes)
+  {
+
+    int nNodes = nodes.size();
+
+    if (null == m_map)
+    {
+      m_mapSize = nNodes + m_blocksize;
+      m_map = new Node[m_mapSize];
+    }
+    else if ((m_firstFree + nNodes) >= m_mapSize)
+    {
+      m_mapSize += (nNodes + m_blocksize);
+
+      Node newMap[] = new Node[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + nNodes);
+
+      m_map = newMap;
+    }
+
+    System.arraycopy(nodes.m_map, 0, m_map, m_firstFree, nNodes);
+
+    m_firstFree += nNodes;
+  }
+
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   */
+  public void removeAllElements()
+  {
+
+    if (null == m_map)
+      return;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      m_map[i] = null;
+    }
+
+    m_firstFree = 0;
+  }
+
+  /**
+   * Removes the first occurrence of the argument from this vector.
+   * If the object is found in this vector, each component in the vector
+   * with an index greater or equal to the object's index is shifted
+   * downward to have an index one smaller than the value it had
+   * previously.
+   *
+   * @param s Node to remove from the list
+   *
+   * @return True if the node was successfully removed
+   */
+  public boolean removeElement(Node s)
+  {
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    if (null == m_map)
+      return false;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      Node node = m_map[i];
+
+      if ((null != node) && node.equals(s))
+      {
+        if (i < m_firstFree - 1)
+          System.arraycopy(m_map, i + 1, m_map, i, m_firstFree - i - 1);
+
+        m_firstFree--;
+        m_map[m_firstFree] = null;
+
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Deletes the component at the specified index. Each component in
+   * this vector with an index greater or equal to the specified
+   * index is shifted downward to have an index one smaller than
+   * the value it had previously.
+   *
+   * @param i Index of node to remove
+   */
+  public void removeElementAt(int i)
+  {
+
+    if (null == m_map)
+      return;
+      
+    if (i >= m_firstFree)
+      throw new ArrayIndexOutOfBoundsException(i + " >= " + m_firstFree);
+    else if (i < 0)
+      throw new ArrayIndexOutOfBoundsException(i);
+
+    if (i < m_firstFree - 1)
+      System.arraycopy(m_map, i + 1, m_map, i, m_firstFree - i - 1);
+
+    m_firstFree--;
+    m_map[m_firstFree] = null;
+  }
+
+  /**
+   * Sets the component at the specified index of this vector to be the
+   * specified object. The previous component at that position is discarded.
+   *
+   * The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.
+   *
+   * @param node Node to set
+   * @param index Index of where to set the node
+   */
+  public void setElementAt(Node node, int index)
+  {
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
+
+    if (null == m_map)
+    {
+      m_map = new Node[m_blocksize];
+      m_mapSize = m_blocksize;
+    }
+
+    m_map[index] = node;
+  }
+
+  /**
+   * Get the nth element.
+   *
+   * @param i Index of node to get
+   *
+   * @return Node at specified index
+   */
+  public Node elementAt(int i)
+  {
+
+    if (null == m_map)
+      return null;
+
+    return m_map[i];
+  }
+
+  /**
+   * Tell if the table contains the given node.
+   *
+   * @param s Node to look for
+   *
+   * @return True if the given node was found.
+   */
+  public boolean contains(Node s)
+  {
+    runTo(-1);
+
+    if (null == m_map)
+      return false;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      Node node = m_map[i];
+
+      if ((null != node) && node.equals(s))
+        return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Node to look for
+   * @param index Index of where to start the search
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public int indexOf(Node elem, int index)
+  {
+    runTo(-1);
+
+    if (null == m_map)
+      return -1;
+
+    for (int i = index; i < m_firstFree; i++)
+    {
+      Node node = m_map[i];
+
+      if ((null != node) && node.equals(elem))
+        return i;
+    }
+
+    return -1;
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Node to look for 
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public int indexOf(Node elem)
+  {
+    runTo(-1);
+
+    if (null == m_map)
+      return -1;
+
+    for (int i = 0; i < m_firstFree; i++)
+    {
+      Node node = m_map[i];
+
+      if ((null != node) && node.equals(elem))
+        return i;
+    }
+
+    return -1;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/NodeSetDTM.java b/src/main/java/org/apache/xpath/NodeSetDTM.java
new file mode 100644
index 0000000..f92d505
--- /dev/null
+++ b/src/main/java/org/apache/xpath/NodeSetDTM.java
@@ -0,0 +1,1247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeSetDTM.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.utils.NodeVector;
+import org.apache.xpath.res.XPATHErrorResources;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.traversal.NodeIterator;
+
+
+/**
+ * <p>The NodeSetDTM class can act as either a NodeVector,
+ * NodeList, or NodeIterator.  However, in order for it to
+ * act as a NodeVector or NodeList, it's required that
+ * setShouldCacheNodes(true) be called before the first
+ * nextNode() is called, in order that nodes can be added
+ * as they are fetched.  Derived classes that implement iterators
+ * must override runTo(int index), in order that they may
+ * run the iteration to the given index. </p>
+ * 
+ * <p>Note that we directly implement the DOM's NodeIterator
+ * interface. We do not emulate all the behavior of the
+ * standard NodeIterator. In particular, we do not guarantee
+ * to present a "live view" of the document ... but in XSLT,
+ * the source document should never be mutated, so this should
+ * never be an issue.</p>
+ * 
+ * <p>Thought: Should NodeSetDTM really implement NodeList and NodeIterator,
+ * or should there be specific subclasses of it which do so? The
+ * advantage of doing it all here is that all NodeSetDTMs will respond
+ * to the same calls; the disadvantage is that some of them may return
+ * less-than-enlightening results when you do so.</p>
+ * @xsl.usage advanced
+ */
+public class NodeSetDTM extends NodeVector
+        implements /* NodeList, NodeIterator, */ DTMIterator, 
+        Cloneable
+{
+    static final long serialVersionUID = 7686480133331317070L;
+
+  /**
+   * Create an empty nodelist.
+   */
+  public NodeSetDTM(DTMManager dtmManager)
+  {
+    super();
+    m_manager = dtmManager;
+  }
+
+  /**
+   * Create an empty, using the given block size.
+   *
+   * @param blocksize Size of blocks to allocate 
+   * @param dummy pass zero for right now...
+   */
+  public NodeSetDTM(int blocksize, int dummy, DTMManager dtmManager)
+  {
+    super(blocksize);
+    m_manager = dtmManager;
+  }
+
+  // %TBD%
+//  /**
+//   * Create a NodeSetDTM, and copy the members of the
+//   * given nodelist into it.
+//   *
+//   * @param nodelist List of Nodes to be made members of the new set.
+//   */
+//  public NodeSetDTM(NodeList nodelist)
+//  {
+//
+//    super();
+//
+//    addNodes(nodelist);
+//  }
+
+  /**
+   * Create a NodeSetDTM, and copy the members of the
+   * given NodeSetDTM into it.
+   *
+   * @param nodelist Set of Nodes to be made members of the new set.
+   */
+  public NodeSetDTM(NodeSetDTM nodelist)
+  {
+
+    super();
+    m_manager = nodelist.getDTMManager();
+    m_root = nodelist.getRoot();
+
+    addNodes((DTMIterator) nodelist);
+  }
+
+  /**
+   * Create a NodeSetDTM, and copy the members of the
+   * given DTMIterator into it.
+   *
+   * @param ni Iterator which yields Nodes to be made members of the new set.
+   */
+  public NodeSetDTM(DTMIterator ni)
+  {
+
+    super();
+
+    m_manager = ni.getDTMManager();
+    m_root = ni.getRoot();
+    addNodes(ni);
+  }
+  
+  /**
+   * Create a NodeSetDTM, and copy the members of the
+   * given DTMIterator into it.
+   *
+   * @param iterator Iterator which yields Nodes to be made members of the new set.
+   */
+  public NodeSetDTM(NodeIterator iterator, XPathContext xctxt)
+  {
+
+    super();
+
+    Node node;
+    m_manager = xctxt.getDTMManager();
+
+    while (null != (node = iterator.nextNode()))
+    {
+      int handle = xctxt.getDTMHandleFromNode(node);
+      addNodeInDocOrder(handle, xctxt);
+    }
+  }
+  
+  /**
+   * Create a NodeSetDTM, and copy the members of the
+   * given DTMIterator into it.
+   *
+   */
+  public NodeSetDTM(NodeList nodeList, XPathContext xctxt)
+  {
+
+    super();
+
+    m_manager = xctxt.getDTMManager();
+
+    int n = nodeList.getLength();
+    for (int i = 0; i < n; i++) 
+    {
+      Node node = nodeList.item(i);
+      int handle = xctxt.getDTMHandleFromNode(node);
+      // Do not reorder or strip duplicate nodes from the given DOM nodelist
+      addNode(handle); // addNodeInDocOrder(handle, xctxt);  
+    } 
+  }
+
+
+  /**
+   * Create a NodeSetDTM which contains the given Node.
+   *
+   * @param node Single node to be added to the new set.
+   */
+  public NodeSetDTM(int node, DTMManager dtmManager)
+  {
+
+    super();
+    m_manager = dtmManager;
+
+    addNode(node);
+  }
+  
+  /**
+   * Set the environment in which this iterator operates, which should provide:
+   * a node (the context node... same value as "root" defined below) 
+   * a pair of non-zero positive integers (the context position and the context size) 
+   * a set of variable bindings 
+   * a function library 
+   * the set of namespace declarations in scope for the expression.
+   * 
+   * <p>At this time the exact implementation of this environment is application 
+   * dependent.  Probably a proper interface will be created fairly soon.</p>
+   * 
+   * @param environment The environment object.
+   */
+  public void setEnvironment(Object environment)
+  {
+    // no-op
+  }
+
+
+  /**
+   * @return The root node of the Iterator, as specified when it was created.
+   * For non-Iterator NodeSetDTMs, this will be null.
+   */
+  public int getRoot()
+  {
+    if(DTM.NULL == m_root)
+    {
+      if(size() > 0)
+        return item(0);
+      else
+        return DTM.NULL;
+    }
+    else
+      return m_root;
+  }
+  
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+    // no-op, I guess...  (-sb)
+  }
+
+  /**
+   * Clone this NodeSetDTM.
+   * At this time, we only expect this to be used with LocPathIterators;
+   * it may not work with other kinds of NodeSetDTMs.
+   *
+   * @return a new NodeSetDTM of the same type, having the same state...
+   * though unless overridden in the subclasses, it may not copy all
+   * the state information.
+   *
+   * @throws CloneNotSupportedException if this subclass of NodeSetDTM
+   * does not support the clone() operation.
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+
+    NodeSetDTM clone = (NodeSetDTM) super.clone();
+
+    return clone;
+  }
+
+  /**
+   * Get a cloned Iterator, and reset its state to the beginning of the
+   * iteration.
+   *
+   * @return a new NodeSetDTM of the same type, having the same state...
+   * except that the reset() operation has been called.
+   *
+   * @throws CloneNotSupportedException if this subclass of NodeSetDTM
+   * does not support the clone() operation.
+   */
+  public DTMIterator cloneWithReset() throws CloneNotSupportedException
+  {
+
+    NodeSetDTM clone = (NodeSetDTM) clone();
+
+    clone.reset();
+
+    return clone;
+  }
+
+  /**
+   * Reset the iterator. May have no effect on non-iterator Nodesets.
+   */
+  public void reset()
+  {
+    m_next = 0;
+  }
+
+  /**
+   *  This attribute determines which node types are presented via the
+   * iterator. The available set of constants is defined in the
+   * <code>DTMFilter</code> interface. For NodeSetDTMs, the mask has been
+   * hardcoded to show all nodes except EntityReference nodes, which have
+   * no equivalent in the XPath data model.
+   *
+   * @return integer used as a bit-array, containing flags defined in
+   * the DOM's DTMFilter class. The value will be 
+   * <code>SHOW_ALL & ~SHOW_ENTITY_REFERENCE</code>, meaning that
+   * only entity references are suppressed.
+   */
+  public int getWhatToShow()
+  {
+    return DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE;
+  }
+
+  /**
+   * The filter object used to screen nodes. Filters are applied to
+   * further reduce (and restructure) the DTMIterator's view of the
+   * document. In our case, we will be using hardcoded filters built
+   * into our iterators... but getFilter() is part of the DOM's 
+   * DTMIterator interface, so we have to support it.
+   *
+   * @return null, which is slightly misleading. True, there is no
+   * user-written filter object, but in fact we are doing some very
+   * sophisticated custom filtering. A DOM purist might suggest
+   * returning a placeholder object just to indicate that this is
+   * not going to return all nodes selected by whatToShow.
+   */
+  public DTMFilter getFilter()
+  {
+    return null;
+  }
+
+  /**
+   *  The value of this flag determines whether the children of entity
+   * reference nodes are visible to the iterator. If false, they will be
+   * skipped over.
+   * <br> To produce a view of the document that has entity references
+   * expanded and does not expose the entity reference node itself, use the
+   * whatToShow flags to hide the entity reference node and set
+   * expandEntityReferences to true when creating the iterator. To produce
+   * a view of the document that has entity reference nodes but no entity
+   * expansion, use the whatToShow flags to show the entity reference node
+   * and set expandEntityReferences to false.
+   *
+   * @return true for all iterators based on NodeSetDTM, meaning that the
+   * contents of EntityRefrence nodes may be returned (though whatToShow
+   * says that the EntityReferences themselves are not shown.)
+   */
+  public boolean getExpandEntityReferences()
+  {
+    return true;
+  }
+  
+  /**
+   * Get an instance of a DTM that "owns" a node handle.  Since a node 
+   * iterator may be passed without a DTMManager, this allows the 
+   * caller to easily get the DTM using just the iterator.
+   *
+   * @param nodeHandle the nodeHandle.
+   *
+   * @return a non-null DTM reference.
+   */
+  public DTM getDTM(int nodeHandle)
+  {
+    
+    return m_manager.getDTM(nodeHandle);
+  }
+  
+  /* An instance of the DTMManager. */
+  DTMManager m_manager;
+  
+  /**
+   * Get an instance of the DTMManager.  Since a node 
+   * iterator may be passed without a DTMManager, this allows the 
+   * caller to easily get the DTMManager using just the iterator.
+   *
+   * @return a non-null DTMManager reference.
+   */
+  public DTMManager getDTMManager()
+  {
+    
+    return m_manager;
+  }
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a DTMIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>DTM.NULL</code> if there are no more members in that set.
+   * @throws DOMException
+   *    INVALID_STATE_ERR: Raised if this method is called after the
+   *   <code>detach</code> method was invoked.
+   */
+  public int nextNode()
+  {
+
+    if ((m_next) < this.size())
+    {
+      int next = this.elementAt(m_next);
+
+      m_next++;
+
+      return next;
+    }
+    else
+      return DTM.NULL;
+  }
+
+  /**
+   *  Returns the previous node in the set and moves the position of the
+   * iterator backwards in the set.
+   * @return  The previous <code>Node</code> in the set being iterated over,
+   *   or<code>DTM.NULL</code> if there are no more members in that set.
+   * @throws DOMException
+   *    INVALID_STATE_ERR: Raised if this method is called after the
+   *   <code>detach</code> method was invoked.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a cached type, and hence doesn't know what the previous node was.
+   */
+  public int previousNode()
+  {
+
+    if (!m_cacheNodes)
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_CANNOT_ITERATE, null)); //"This NodeSetDTM can not iterate to a previous node!");
+
+    if ((m_next - 1) > 0)
+    {
+      m_next--;
+
+      return this.elementAt(m_next);
+    }
+    else
+      return DTM.NULL;
+  }
+
+  /**
+   * Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   * <p>
+   * This operation is a no-op in NodeSetDTM, and will not cause 
+   * INVALID_STATE_ERR to be raised by later operations.
+   * </p>
+   */
+  public void detach(){}
+  
+  /**
+   * Specify if it's OK for detach to release the iterator for reuse.
+   * 
+   * @param allowRelease true if it is OK for detach to release this iterator 
+   * for pooling.
+   */
+  public void allowDetachToRelease(boolean allowRelease)
+  {
+    // no action for right now.
+  }
+
+
+  /**
+   * Tells if this NodeSetDTM is "fresh", in other words, if
+   * the first nextNode() that is called will return the
+   * first node in the set.
+   *
+   * @return true if nextNode() would return the first node in the set,
+   * false if it would return a later one.
+   */
+  public boolean isFresh()
+  {
+    return (m_next == 0);
+  }
+
+  /**
+   * If an index is requested, NodeSetDTM will call this method
+   * to run the iterator to the index.  By default this sets
+   * m_next to the index.  If the index argument is -1, this
+   * signals that the iterator should be run to the end.
+   *
+   * @param index Position to advance (or retreat) to, with
+   * 0 requesting the reset ("fresh") position and -1 (or indeed
+   * any out-of-bounds value) requesting the final position.
+   * @throws RuntimeException thrown if this NodeSetDTM is not
+   * one of the types which supports indexing/counting.
+   */
+  public void runTo(int index)
+  {
+
+    if (!m_cacheNodes)
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_CANNOT_INDEX, null)); //"This NodeSetDTM can not do indexing or counting functions!");
+
+    if ((index >= 0) && (m_next < m_firstFree))
+      m_next = index;
+    else
+      m_next = m_firstFree - 1;
+  }
+
+  /**
+   * Returns the <code>index</code>th item in the collection. If
+   * <code>index</code> is greater than or equal to the number of nodes in
+   * the list, this returns <code>null</code>.
+   * 
+   * TODO: What happens if index is out of range?
+   * 
+   * @param index Index into the collection.
+   * @return The node at the <code>index</code>th position in the
+   *   <code>NodeList</code>, or <code>null</code> if that is not a valid
+   *   index.
+   */
+  public int item(int index)
+  {
+
+    runTo(index);
+
+    return this.elementAt(index);
+  }
+
+  /**
+   * The number of nodes in the list. The range of valid child node indices is
+   * 0 to <code>length-1</code> inclusive. Note that this operation requires
+   * finding all the matching nodes, which may defeat attempts to defer
+   * that work.
+   *
+   * @return integer indicating how many nodes are represented by this list.
+   */
+  public int getLength()
+  {
+
+    runTo(-1);
+
+    return this.size();
+  }
+
+  /**
+   * Add a node to the NodeSetDTM. Not all types of NodeSetDTMs support this
+   * operation
+   *
+   * @param n Node to be added
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void addNode(int n)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    this.addElement(n);
+  }
+
+  /**
+   * Insert a node at a given position.
+   *
+   * @param n Node to be added
+   * @param pos Offset at which the node is to be inserted,
+   * with 0 being the first position.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void insertNode(int n, int pos)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    insertElementAt(n, pos);
+  }
+
+  /**
+   * Remove a node.
+   *
+   * @param n Node to be added
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void removeNode(int n)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    this.removeElement(n);
+  }
+
+  // %TBD%
+//  /**
+//   * Copy NodeList members into this nodelist, adding in
+//   * document order.  If a node is null, don't add it.
+//   *
+//   * @param nodelist List of nodes which should now be referenced by
+//   * this NodeSetDTM.
+//   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+//   * a mutable type.
+//   */
+//  public void addNodes(NodeList nodelist)
+//  {
+//
+//    if (!m_mutable)
+//      throw new RuntimeException("This NodeSetDTM is not mutable!");
+//
+//    if (null != nodelist)  // defensive to fix a bug that Sanjiva reported.
+//    {
+//      int nChildren = nodelist.getLength();
+//
+//      for (int i = 0; i < nChildren; i++)
+//      {
+//        int obj = nodelist.item(i);
+//
+//        if (null != obj)
+//        {
+//          addElement(obj);
+//        }
+//      }
+//    }
+//
+//    // checkDups();
+//  }
+
+  // %TBD%
+//  /**
+//   * <p>Copy NodeList members into this nodelist, adding in
+//   * document order.  Only genuine node references will be copied;
+//   * nulls appearing in the source NodeSetDTM will
+//   * not be added to this one. </p>
+//   * 
+//   * <p> In case you're wondering why this function is needed: NodeSetDTM
+//   * implements both DTMIterator and NodeList. If this method isn't
+//   * provided, Java can't decide which of those to use when addNodes()
+//   * is invoked. Providing the more-explicit match avoids that
+//   * ambiguity.)</p>
+//   *
+//   * @param ns NodeSetDTM whose members should be merged into this NodeSetDTM.
+//   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+//   * a mutable type.
+//   */
+//  public void addNodes(NodeSetDTM ns)
+//  {
+//
+//    if (!m_mutable)
+//      throw new RuntimeException("This NodeSetDTM is not mutable!");
+//
+//    addNodes((DTMIterator) ns);
+//  }
+
+  /**
+   * Copy NodeList members into this nodelist, adding in
+   * document order.  Null references are not added.
+   *
+   * @param iterator DTMIterator which yields the nodes to be added.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void addNodes(DTMIterator iterator)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    if (null != iterator)  // defensive to fix a bug that Sanjiva reported.
+    {
+      int obj;
+
+      while (DTM.NULL != (obj = iterator.nextNode()))
+      {
+        addElement(obj);
+      }
+    }
+
+    // checkDups();
+  }
+
+  // %TBD%
+//  /**
+//   * Copy NodeList members into this nodelist, adding in
+//   * document order.  If a node is null, don't add it.
+//   *
+//   * @param nodelist List of nodes to be added
+//   * @param support The XPath runtime context.
+//   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+//   * a mutable type.
+//   */
+//  public void addNodesInDocOrder(NodeList nodelist, XPathContext support)
+//  {
+//
+//    if (!m_mutable)
+//      throw new RuntimeException("This NodeSetDTM is not mutable!");
+//
+//    int nChildren = nodelist.getLength();
+//
+//    for (int i = 0; i < nChildren; i++)
+//    {
+//      int node = nodelist.item(i);
+//
+//      if (null != node)
+//      {
+//        addNodeInDocOrder(node, support);
+//      }
+//    }
+//  }
+
+  /**
+   * Copy NodeList members into this nodelist, adding in
+   * document order.  If a node is null, don't add it.
+   *
+   * @param iterator DTMIterator which yields the nodes to be added.
+   * @param support The XPath runtime context.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void addNodesInDocOrder(DTMIterator iterator, XPathContext support)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    int node;
+
+    while (DTM.NULL != (node = iterator.nextNode()))
+    {
+      addNodeInDocOrder(node, support);
+    }
+  }
+
+  // %TBD%
+//  /**
+//   * Add the node list to this node set in document order.
+//   *
+//   * @param start index.
+//   * @param end index.
+//   * @param testIndex index.
+//   * @param nodelist The nodelist to add.
+//   * @param support The XPath runtime context.
+//   *
+//   * @return false always.
+//   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+//   * a mutable type.
+//   */
+//  private boolean addNodesInDocOrder(int start, int end, int testIndex,
+//                                     NodeList nodelist, XPathContext support)
+//  {
+//
+//    if (!m_mutable)
+//      throw new RuntimeException("This NodeSetDTM is not mutable!");
+//
+//    boolean foundit = false;
+//    int i;
+//    int node = nodelist.item(testIndex);
+//
+//    for (i = end; i >= start; i--)
+//    {
+//      int child = elementAt(i);
+//
+//      if (child == node)
+//      {
+//        i = -2;  // Duplicate, suppress insert
+//
+//        break;
+//      }
+//
+//      if (!support.getDOMHelper().isNodeAfter(node, child))
+//      {
+//        insertElementAt(node, i + 1);
+//
+//        testIndex--;
+//
+//        if (testIndex > 0)
+//        {
+//          boolean foundPrev = addNodesInDocOrder(0, i, testIndex, nodelist,
+//                                                 support);
+//
+//          if (!foundPrev)
+//          {
+//            addNodesInDocOrder(i, size() - 1, testIndex, nodelist, support);
+//          }
+//        }
+//
+//        break;
+//      }
+//    }
+//
+//    if (i == -1)
+//    {
+//      insertElementAt(node, 0);
+//    }
+//
+//    return foundit;
+//  }
+
+  /**
+   * Add the node into a vector of nodes where it should occur in
+   * document order.
+   * @param node The node to be added.
+   * @param test true if we should test for doc order
+   * @param support The XPath runtime context.
+   * @return insertIndex.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public int addNodeInDocOrder(int node, boolean test, XPathContext support)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    int insertIndex = -1;
+
+    if (test)
+    {
+
+      // This needs to do a binary search, but a binary search 
+      // is somewhat tough because the sequence test involves 
+      // two nodes.
+      int size = size(), i;
+
+      for (i = size - 1; i >= 0; i--)
+      {
+        int child = elementAt(i);
+
+        if (child == node)
+        {
+          i = -2;  // Duplicate, suppress insert
+
+          break;
+        }
+
+        DTM dtm = support.getDTM(node);
+        if (!dtm.isNodeAfter(node, child))
+        {
+          break;
+        }
+      }
+
+      if (i != -2)
+      {
+        insertIndex = i + 1;
+
+        insertElementAt(node, insertIndex);
+      }
+    }
+    else
+    {
+      insertIndex = this.size();
+
+      boolean foundit = false;
+
+      for (int i = 0; i < insertIndex; i++)
+      {
+        if (i == node)
+        {
+          foundit = true;
+
+          break;
+        }
+      }
+
+      if (!foundit)
+        addElement(node);
+    }
+
+    // checkDups();
+    return insertIndex;
+  }  // end addNodeInDocOrder(Vector v, Object obj)
+
+  /**
+   * Add the node into a vector of nodes where it should occur in
+   * document order.
+   * @param node The node to be added.
+   * @param support The XPath runtime context.
+   *
+   * @return The index where it was inserted.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public int addNodeInDocOrder(int node, XPathContext support)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    return addNodeInDocOrder(node, true, support);
+  }  // end addNodeInDocOrder(Vector v, Object obj)
+
+  /**
+   * Get the length of the list.
+   *
+   * @return The size of this node set.
+   */
+  public int size()
+  {
+    return super.size();
+  }
+
+  /**
+   * Append a Node onto the vector.
+   *
+   * @param value The node to be added.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void addElement(int value)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    super.addElement(value);
+  }
+
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   *
+   * @param value The node to be inserted.
+   * @param at The index where the insert should occur.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void insertElementAt(int value, int at)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    super.insertElementAt(value, at);
+  }
+
+  /**
+   * Append the nodes to the list.
+   *
+   * @param nodes The nodes to be appended to this node set.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void appendNodes(NodeVector nodes)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    super.appendNodes(nodes);
+  }
+
+  /**
+   * Inserts the specified node in this vector at the specified index.
+   * Each component in this vector with an index greater or equal to
+   * the specified index is shifted upward to have an index one greater
+   * than the value it had previously.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void removeAllElements()
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    super.removeAllElements();
+  }
+
+  /**
+   * Removes the first occurrence of the argument from this vector.
+   * If the object is found in this vector, each component in the vector
+   * with an index greater or equal to the object's index is shifted
+   * downward to have an index one smaller than the value it had
+   * previously.
+   *
+   * @param s The node to be removed.
+   *
+   * @return True if the node was successfully removed
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public boolean removeElement(int s)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    return super.removeElement(s);
+  }
+
+  /**
+   * Deletes the component at the specified index. Each component in
+   * this vector with an index greater or equal to the specified
+   * index is shifted downward to have an index one smaller than
+   * the value it had previously.
+   *
+   * @param i The index of the node to be removed.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void removeElementAt(int i)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    super.removeElementAt(i);
+  }
+
+  /**
+   * Sets the component at the specified index of this vector to be the
+   * specified object. The previous component at that position is discarded.
+   *
+   * The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.
+   *
+   * @param node  The node to be set.
+   * @param index The index of the node to be replaced.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void setElementAt(int node, int index)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    super.setElementAt(node, index);
+  }
+  
+  /**
+   * Same as setElementAt.
+   *
+   * @param node  The node to be set.
+   * @param index The index of the node to be replaced.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+  public void setItem(int node, int index)
+  {
+
+    if (!m_mutable)
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
+
+    super.setElementAt(node, index);
+  }
+
+  /**
+   * Get the nth element.
+   *
+   * @param i The index of the requested node.
+   *
+   * @return Node at specified index.
+   */
+  public int elementAt(int i)
+  {
+
+    runTo(i);
+
+    return super.elementAt(i);
+  }
+  
+  /**
+   * Tell if the table contains the given node.
+   *
+   * @param s Node to look for
+   *
+   * @return True if the given node was found.
+   */
+  public boolean contains(int s)
+  {
+
+    runTo(-1);
+
+    return super.contains(s);
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Node to look for
+   * @param index Index of where to start the search
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public int indexOf(int elem, int index)
+  {
+
+    runTo(-1);
+
+    return super.indexOf(elem, index);
+  }
+
+  /**
+   * Searches for the first occurence of the given argument,
+   * beginning the search at index, and testing for equality
+   * using the equals method.
+   *
+   * @param elem Node to look for 
+   * @return the index of the first occurrence of the object
+   * argument in this vector at position index or later in the
+   * vector; returns -1 if the object is not found.
+   */
+  public int indexOf(int elem)
+  {
+
+    runTo(-1);
+
+    return super.indexOf(elem);
+  }
+
+  /** If this node is being used as an iterator, the next index that nextNode()
+   *  will return.  */
+  transient protected int m_next = 0;
+
+  /**
+   * Get the current position, which is one less than
+   * the next nextNode() call will retrieve.  i.e. if
+   * you call getCurrentPos() and the return is 0, the next
+   * fetch will take place at index 1.
+   *
+   * @return The the current position index.
+   */
+  public int getCurrentPos()
+  {
+    return m_next;
+  }
+
+  /**
+   * Set the current position in the node set.
+   * @param i Must be a valid index.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a cached type, and thus doesn't permit indexed access.
+   */
+  public void setCurrentPos(int i)
+  {
+
+    if (!m_cacheNodes)
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_CANNOT_INDEX, null)); //"This NodeSetDTM can not do indexing or counting functions!");
+
+    m_next = i;
+  }
+
+  /**
+   * Return the last fetched node.  Needed to support the UnionPathIterator.
+   *
+   * @return the last fetched node.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a cached type, and thus doesn't permit indexed access.
+   */
+  public int getCurrentNode()
+  {
+
+    if (!m_cacheNodes)
+      throw new RuntimeException(
+        "This NodeSetDTM can not do indexing or counting functions!");
+
+    int saved = m_next;
+    // because nextNode always increments
+    // But watch out for copy29, where the root iterator didn't
+    // have nextNode called on it.
+    int current = (m_next > 0) ? m_next-1 : m_next; 
+    int n = (current < m_firstFree) ? elementAt(current) : DTM.NULL;
+    m_next = saved; // HACK: I think this is a bit of a hack.  -sb
+    return n;
+  }
+
+  /** True if this list can be mutated.  */
+  transient protected boolean m_mutable = true;
+
+  /** True if this list is cached.
+   *  @serial  */
+  transient protected boolean m_cacheNodes = true;
+  
+  /** The root of the iteration, if available. */
+  protected int m_root = DTM.NULL;
+
+  /**
+   * Get whether or not this is a cached node set.
+   *
+   *
+   * @return True if this list is cached.
+   */
+  public boolean getShouldCacheNodes()
+  {
+    return m_cacheNodes;
+  }
+
+  /**
+   * If setShouldCacheNodes(true) is called, then nodes will
+   * be cached.  They are not cached by default. This switch must
+   * be set before the first call to nextNode is made, to ensure
+   * that all nodes are cached.
+   *
+   * @param b true if this node set should be cached.
+   * @throws RuntimeException thrown if an attempt is made to
+   * request caching after we've already begun stepping through the
+   * nodes in this set.
+  */
+  public void setShouldCacheNodes(boolean b)
+  {
+
+    if (!isFresh())
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_CALL_SETSHOULDCACHENODE, null)); //"Can not call setShouldCacheNodes after nextNode has been called!");
+
+    m_cacheNodes = b;
+    m_mutable = true;
+  }
+  
+  /**
+   * Tells if this iterator can have nodes added to it or set via 
+   * the <code>setItem(int node, int index)</code> method.
+   * 
+   * @return True if the nodelist can be mutated.
+   */
+  public boolean isMutable()
+  {
+    return m_mutable;
+  }
+  
+  transient private int m_last = 0;
+  
+  public int getLast()
+  {
+    return m_last;
+  }
+  
+  public void setLast(int last)
+  {
+    m_last = last;
+  }
+  
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * 
+   * @return true as a default.
+   */
+  public boolean isDocOrdered()
+  {
+    return true;
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return -1;
+  }
+  
+
+}
diff --git a/src/main/java/org/apache/xpath/SourceTree.java b/src/main/java/org/apache/xpath/SourceTree.java
new file mode 100644
index 0000000..6492810
--- /dev/null
+++ b/src/main/java/org/apache/xpath/SourceTree.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SourceTree.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+
+/**
+ * This object represents a Source Tree, and any associated
+ * information.
+ * @xsl.usage internal
+ */
+public class SourceTree
+{
+
+  /**
+   * Constructor SourceTree
+   *
+   *
+   * @param root The root of the source tree, which may or may not be a 
+   * {@link org.w3c.dom.Document} node.
+   * @param url The URI of the source tree.
+   */
+  public SourceTree(int root, String url)
+  {
+    m_root = root;
+    m_url = url;
+  }
+
+  /** The URI of the source tree.   */
+  public String m_url;
+
+  /** The root of the source tree, which may or may not be a 
+   * {@link org.w3c.dom.Document} node.  */
+  public int m_root;
+}
diff --git a/src/main/java/org/apache/xpath/SourceTreeManager.java b/src/main/java/org/apache/xpath/SourceTreeManager.java
new file mode 100644
index 0000000..0ba44cf
--- /dev/null
+++ b/src/main/java/org/apache/xpath/SourceTreeManager.java
@@ -0,0 +1,390 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SourceTreeManager.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import java.io.IOException;
+import java.util.Vector;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.SystemIDResolver;
+
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * This class bottlenecks all management of source trees.  The methods
+ * in this class should allow easy garbage collection of source
+ * trees (not yet!), and should centralize parsing for those source trees.
+ */
+public class SourceTreeManager
+{
+
+  /** Vector of SourceTree objects that this manager manages. */
+  private Vector m_sourceTree = new Vector();
+
+  /**
+   * Reset the list of SourceTree objects that this manager manages.
+   *
+   */
+  public void reset()
+  {
+    m_sourceTree = new Vector();
+  }
+
+  /** The TrAX URI resolver used to obtain source trees. */
+  URIResolver m_uriResolver;
+
+  /**
+   * Set an object that will be used to resolve URIs used in
+   * document(), etc.
+   * @param resolver An object that implements the URIResolver interface,
+   * or null.
+   */
+  public void setURIResolver(URIResolver resolver)
+  {
+    m_uriResolver = resolver;
+  }
+
+  /**
+   * Get the object that will be used to resolve URIs used in
+   * document(), etc.
+   * @return An object that implements the URIResolver interface,
+   * or null.
+   */
+  public URIResolver getURIResolver()
+  {
+    return m_uriResolver;
+  }
+
+  /**
+   * Given a document, find the URL associated with that document.
+   * @param owner Document that was previously processed by this liaison.
+   *
+   * @return The base URI of the owner argument.
+   */
+  public String findURIFromDoc(int owner)
+  {
+    int n = m_sourceTree.size();
+
+    for (int i = 0; i < n; i++)
+    {
+      SourceTree sTree = (SourceTree) m_sourceTree.elementAt(i);
+
+      if (owner == sTree.m_root)
+        return sTree.m_url;
+    }
+
+    return null;
+  }
+
+  /**
+   * This will be called by the processor when it encounters
+   * an xsl:include, xsl:import, or document() function.
+   *
+   * @param base The base URI that should be used.
+   * @param urlString Value from an xsl:import or xsl:include's href attribute,
+   * or a URI specified in the document() function.
+   * 
+   * @return a Source that can be used to process the resource.
+   *
+   * @throws IOException
+   * @throws TransformerException
+   */
+  public Source resolveURI(
+          String base, String urlString, SourceLocator locator)
+            throws TransformerException, IOException
+  {
+
+    Source source = null;
+
+    if (null != m_uriResolver)
+    {
+      source = m_uriResolver.resolve(urlString, base);
+    }
+
+    if (null == source)
+    {
+      String uri = SystemIDResolver.getAbsoluteURI(urlString, base);
+
+      source = new StreamSource(uri);
+    }
+
+    return source;
+  }
+
+  /** JJK: Support  <?xalan:doc_cache_off?> kluge in ElemForEach.
+   * TODO: This function is highly dangerous. Cache management must be improved.
+   *
+   * @param n The node to remove.
+   */
+  public void removeDocumentFromCache(int n)
+  {
+    if(DTM.NULL ==n)
+      return;
+    for(int i=m_sourceTree.size()-1;i>=0;--i)
+    {
+      SourceTree st=(SourceTree)m_sourceTree.elementAt(i);
+      if(st!=null && st.m_root==n)
+      {
+	m_sourceTree.removeElementAt(i);
+	return;
+      }
+    }
+  }
+  
+
+
+  /**
+   * Put the source tree root node in the document cache.
+   * TODO: This function needs to be a LOT more sophisticated.
+   *
+   * @param n The node to cache.
+   * @param source The Source object to cache.
+   */
+  public void putDocumentInCache(int n, Source source)
+  {
+
+    int cachedNode = getNode(source);
+
+    if (DTM.NULL != cachedNode)
+    {
+      if (!(cachedNode == n))
+        throw new RuntimeException(
+          "Programmer's Error!  "
+          + "putDocumentInCache found reparse of doc: "
+          + source.getSystemId());
+      return;
+    }
+    if (null != source.getSystemId())
+    {
+      m_sourceTree.addElement(new SourceTree(n, source.getSystemId()));
+    }
+  }
+
+  /**
+   * Given a Source object, find the node associated with it.
+   *
+   * @param source The Source object to act as the key.
+   *
+   * @return The node that is associated with the Source, or null if not found.
+   */
+  public int getNode(Source source)
+  {
+
+//    if (source instanceof DOMSource)
+//      return ((DOMSource) source).getNode();
+
+    // TODO: Not sure if the BaseID is really the same thing as the ID.
+    String url = source.getSystemId();
+
+    if (null == url)
+      return DTM.NULL;
+
+    int n = m_sourceTree.size();
+
+    // System.out.println("getNode: "+n);
+    for (int i = 0; i < n; i++)
+    {
+      SourceTree sTree = (SourceTree) m_sourceTree.elementAt(i);
+
+      // System.out.println("getNode -         url: "+url);
+      // System.out.println("getNode - sTree.m_url: "+sTree.m_url);
+      if (url.equals(sTree.m_url))
+        return sTree.m_root;
+    }
+
+    // System.out.println("getNode - returning: "+node);
+    return DTM.NULL;
+  }
+
+  /**
+   * Get the source tree from the a base URL and a URL string.
+   *
+   * @param base The base URI to use if the urlString is relative.
+   * @param urlString An absolute or relative URL string.
+   * @param locator The location of the caller, for diagnostic purposes.
+   *
+   * @return should be a non-null reference to the node identified by the 
+   * base and urlString.
+   *
+   * @throws TransformerException If the URL can not resolve to a node.
+   */
+  public int getSourceTree(
+          String base, String urlString, SourceLocator locator, XPathContext xctxt)
+            throws TransformerException
+  {
+
+    // System.out.println("getSourceTree");
+    try
+    {
+      Source source = this.resolveURI(base, urlString, locator);
+
+      // System.out.println("getSourceTree - base: "+base+", urlString: "+urlString+", source: "+source.getSystemId());
+      return getSourceTree(source, locator, xctxt);
+    }
+    catch (IOException ioe)
+    {
+      throw new TransformerException(ioe.getMessage(), locator, ioe);
+    }
+
+    /* catch (TransformerException te)
+     {
+       throw new TransformerException(te.getMessage(), locator, te);
+     }*/
+  }
+
+  /**
+   * Get the source tree from the input source.
+   *
+   * @param source The Source object that should identify the desired node.
+   * @param locator The location of the caller, for diagnostic purposes.
+   *
+   * @return non-null reference to a node.
+   *
+   * @throws TransformerException if the Source argument can't be resolved to 
+   *         a node.
+   */
+  public int getSourceTree(Source source, SourceLocator locator, XPathContext xctxt)
+          throws TransformerException
+  {
+
+    int n = getNode(source);
+
+    if (DTM.NULL != n)
+      return n;
+
+    n = parseToNode(source, locator, xctxt);
+
+    if (DTM.NULL != n)
+      putDocumentInCache(n, source);
+
+    return n;
+  }
+
+  /**
+   * Try to create a DOM source tree from the input source.
+   *
+   * @param source The Source object that identifies the source node.
+   * @param locator The location of the caller, for diagnostic purposes.
+   *
+   * @return non-null reference to node identified by the source argument.
+   *
+   * @throws TransformerException if the source argument can not be resolved 
+   *         to a source node.
+   */
+  public int parseToNode(Source source, SourceLocator locator, XPathContext xctxt)
+          throws TransformerException
+  {
+
+    try
+    {      
+      Object xowner = xctxt.getOwnerObject();
+      DTM dtm;
+      if(null != xowner && xowner instanceof org.apache.xml.dtm.DTMWSFilter)
+      {
+        dtm = xctxt.getDTM(source, false, 
+                          (org.apache.xml.dtm.DTMWSFilter)xowner, false, true);
+      }
+      else
+      {
+        dtm = xctxt.getDTM(source, false, null, false, true);
+      }
+      return dtm.getDocument();
+    }
+    catch (Exception e)
+    {
+      //e.printStackTrace();
+      throw new TransformerException(e.getMessage(), locator, e);
+    }
+
+  }
+
+  /**
+   * This method returns the SAX2 parser to use with the InputSource
+   * obtained from this URI.
+   * It may return null if any SAX2-conformant XML parser can be used,
+   * or if getInputSource() will also return null. The parser must
+   * be free for use (i.e.
+   * not currently in use for another parse().
+   *
+   * @param inputSource The value returned from the URIResolver.
+   * @return a SAX2 XMLReader to use to resolve the inputSource argument.
+   * @param locator The location of the original caller, for diagnostic purposes.
+   *
+   * @throws TransformerException if the reader can not be created.
+   */
+  public static XMLReader getXMLReader(Source inputSource, SourceLocator locator)
+          throws TransformerException
+  {
+
+    try
+    {
+      XMLReader reader = (inputSource instanceof SAXSource)
+                         ? ((SAXSource) inputSource).getXMLReader() : null;
+                         
+      if (null == reader)
+      {
+        try {
+          javax.xml.parsers.SAXParserFactory factory=
+              javax.xml.parsers.SAXParserFactory.newInstance();
+          factory.setNamespaceAware( true );
+          javax.xml.parsers.SAXParser jaxpParser=
+              factory.newSAXParser();
+          reader=jaxpParser.getXMLReader();
+          
+        } catch( javax.xml.parsers.ParserConfigurationException ex ) {
+          throw new org.xml.sax.SAXException( ex );
+        } catch( javax.xml.parsers.FactoryConfigurationError ex1 ) {
+            throw new org.xml.sax.SAXException( ex1.toString() );
+        } catch( NoSuchMethodError ex2 ) {
+        }
+        catch (AbstractMethodError ame){}
+        if(null == reader)
+          reader = XMLReaderFactory.createXMLReader();
+      }
+
+      try
+      {
+        reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
+                          true);
+      }
+      catch (org.xml.sax.SAXException se)
+      {
+
+        // What can we do?
+        // TODO: User diagnostics.
+      }
+
+      return reader;
+    }
+    catch (org.xml.sax.SAXException se)
+    {
+      throw new TransformerException(se.getMessage(), locator, se);
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xpath/VariableStack.java b/src/main/java/org/apache/xpath/VariableStack.java
new file mode 100644
index 0000000..e72c265
--- /dev/null
+++ b/src/main/java/org/apache/xpath/VariableStack.java
@@ -0,0 +1,547 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: VariableStack.java 524812 2007-04-02 15:52:03Z zongaro $
+ */
+package org.apache.xpath;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Defines a class to keep track of a stack for
+ * template arguments and variables.
+ *
+ * <p>This has been changed from the previous incarnations of this
+ * class to be fairly low level.</p>
+ * @xsl.usage internal
+ */
+public class VariableStack implements Cloneable
+{
+  /**
+   * limitation for 1K
+   */
+  public static final int CLEARLIMITATION= 1024;
+
+  /**
+   * Constructor for a variable stack.
+   */
+  public VariableStack()
+  {
+    reset();
+  }
+
+  /**
+   * Constructor for a variable stack.
+   * @param initStackSize The initial stack size.  Must be at least one.  The
+   *                      stack can grow if needed.
+   */
+  public VariableStack(int initStackSize)
+  {
+    // Allow for twice as many variables as stack link entries
+    reset(initStackSize, initStackSize*2);
+  }
+
+  /**
+   * Returns a clone of this variable stack.
+   *
+   * @return  a clone of this variable stack.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public synchronized Object clone() throws CloneNotSupportedException
+  {
+
+    VariableStack vs = (VariableStack) super.clone();
+
+    // I *think* I can get away with a shallow clone here?
+    vs._stackFrames = (XObject[]) _stackFrames.clone();
+    vs._links = (int[]) _links.clone();
+
+    return vs;
+  }
+
+  /**
+   * The stack frame where all variables and params will be kept.
+   * @serial
+   */
+  XObject[] _stackFrames;
+
+  /**
+   * The top of the stack frame (<code>_stackFrames</code>).
+   * @serial
+   */
+  int _frameTop;
+
+  /**
+   * The bottom index of the current frame (relative to <code>_stackFrames</code>).
+   * @serial
+   */
+  private int _currentFrameBottom;
+
+  /**
+   * The stack of frame positions.  I call 'em links because of distant
+   * <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
+   * Motorola 68000 assembler</a> memories.  :-)
+   * @serial
+   */
+  int[] _links;
+
+  /**
+   * The top of the links stack.
+   */
+  int _linksTop;
+
+  /**
+   * Get the element at the given index, regardless of stackframe.
+   *
+   * @param i index from zero.
+   *
+   * @return The item at the given index.
+   */
+  public XObject elementAt(final int i)
+  {
+    return _stackFrames[i];
+  }
+
+  /**
+   * Get size of the stack.
+   *
+   * @return the total size of the execution stack.
+   */
+  public int size()
+  {
+    return _frameTop;
+  }
+
+  /**
+   * Reset the stack to a start position.
+   */
+  public void reset()
+  {
+    // If the stack was previously allocated, assume that about the same
+    // amount of stack space will be needed again; otherwise, use a very
+    // large stack size.
+    int linksSize = (_links == null) ? XPathContext.RECURSIONLIMIT
+                                     : _links.length;
+    int varArraySize = (_stackFrames == null) ? XPathContext.RECURSIONLIMIT * 2
+                                              : _stackFrames.length;
+    reset(linksSize, varArraySize);
+  }
+
+  /**
+   * Reset the stack to a start position.
+   * @param linksSize Initial stack size to use
+   * @param varArraySize Initial variable array size to use
+   */
+  protected void reset(int linksSize, int varArraySize) {
+    _frameTop = 0;
+    _linksTop = 0;
+
+    // Don't bother reallocating _links array if it exists already
+    if (_links == null) {
+      _links = new int[linksSize];
+    }
+
+    // Adding one here to the stack of frame positions will allow us always 
+    // to look one under without having to check if we're at zero.
+    // (As long as the caller doesn't screw up link/unlink.)
+    _links[_linksTop++] = 0;
+
+    // Get a clean _stackFrames array and discard the old one.
+    _stackFrames = new XObject[varArraySize]; 
+  }
+
+  /**
+   * Set the current stack frame.
+   *
+   * @param sf The new stack frame position.
+   */
+  public void setStackFrame(int sf)
+  {
+    _currentFrameBottom = sf;
+  }
+
+  /**
+   * Get the position from where the search should start,
+   * which is either the searchStart property, or the top
+   * of the stack if that value is -1.
+   *
+   * @return The current stack frame position.
+   */
+  public int getStackFrame()
+  {
+    return _currentFrameBottom;
+  }
+
+  /**
+   * Allocates memory (called a stackframe) on the stack; used to store
+   * local variables and parameter arguments.
+   *
+   * <p>I use the link/unlink concept because of distant
+   * <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
+   * Motorola 68000 assembler</a> memories.</p>
+   *
+   * @param size The size of the stack frame allocation.  This ammount should
+   * normally be the maximum number of variables that you can have allocated
+   * at one time in the new stack frame.
+   *
+   * @return The bottom of the stack frame, from where local variable addressing
+   * should start from.
+   */
+  public int link(final int size)
+  {
+
+    _currentFrameBottom = _frameTop;
+    _frameTop += size;
+
+    if (_frameTop >= _stackFrames.length)
+    {
+      XObject newsf[] = new XObject[_stackFrames.length + XPathContext.RECURSIONLIMIT + size];
+
+      System.arraycopy(_stackFrames, 0, newsf, 0, _stackFrames.length);
+
+      _stackFrames = newsf;
+    }
+
+    if (_linksTop + 1 >= _links.length)
+    {
+      int newlinks[] = new int[_links.length + (CLEARLIMITATION * 2)];
+
+      System.arraycopy(_links, 0, newlinks, 0, _links.length);
+
+      _links = newlinks;
+    }
+
+    _links[_linksTop++] = _currentFrameBottom;
+
+    return _currentFrameBottom;
+  }
+
+  /**
+   * Free up the stack frame that was last allocated with
+   * {@link #link(int size)}.
+   */
+  public  void unlink()
+  {
+    _frameTop = _links[--_linksTop];
+    _currentFrameBottom = _links[_linksTop - 1];
+  }
+  
+  /**
+   * Free up the stack frame that was last allocated with
+   * {@link #link(int size)}.
+   * @param currentFrame The current frame to set to 
+   * after the unlink.
+   */
+  public  void unlink(int currentFrame)
+  {
+    _frameTop = _links[--_linksTop];
+    _currentFrameBottom = currentFrame; 
+  }
+
+  /**
+   * Set a local variable or parameter in the current stack frame.
+   *
+   *
+   * @param index Local variable index relative to the current stack
+   * frame bottom.
+   *
+   * @param val The value of the variable that is being set.
+   */
+  public void setLocalVariable(int index, XObject val)
+  {
+    _stackFrames[index + _currentFrameBottom] = val;
+  }
+
+  /**
+   * Set a local variable or parameter in the specified stack frame.
+   *
+   *
+   * @param index Local variable index relative to the current stack
+   * frame bottom.
+   * NEEDSDOC @param stackFrame
+   *
+   * @param val The value of the variable that is being set.
+   */
+  public void setLocalVariable(int index, XObject val, int stackFrame)
+  {
+    _stackFrames[index + stackFrame] = val;
+  }
+
+  /**
+   * Get a local variable or parameter in the current stack frame.
+   *
+   *
+   * @param xctxt The XPath context, which must be passed in order to
+   * lazy evaluate variables.
+   *
+   * @param index Local variable index relative to the current stack
+   * frame bottom.
+   *
+   * @return The value of the variable.
+   *
+   * @throws TransformerException
+   */
+  public XObject getLocalVariable(XPathContext xctxt, int index)
+          throws TransformerException
+  {
+
+    index += _currentFrameBottom;
+
+    XObject val = _stackFrames[index];
+    
+    if(null == val)
+      throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND, null),
+                     xctxt.getSAXLocator());
+      // "Variable accessed before it is bound!", xctxt.getSAXLocator());
+
+    // Lazy execution of variables.
+    if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
+      return (_stackFrames[index] = val.execute(xctxt));
+
+    return val;
+  }
+
+  /**
+   * Get a local variable or parameter in the current stack frame.
+   *
+   *
+   * @param index Local variable index relative to the given
+   * frame bottom.
+   * NEEDSDOC @param frame
+   *
+   * @return The value of the variable.
+   *
+   * @throws TransformerException
+   */
+  public XObject getLocalVariable(int index, int frame)
+          throws TransformerException
+  {
+
+    index += frame;
+
+    XObject val = _stackFrames[index];
+
+    return val;
+  }
+  
+  /**
+   * Get a local variable or parameter in the current stack frame.
+   *
+   *
+   * @param xctxt The XPath context, which must be passed in order to
+   * lazy evaluate variables.
+   *
+   * @param index Local variable index relative to the current stack
+   * frame bottom.
+   *
+   * @return The value of the variable.
+   *
+   * @throws TransformerException
+   */
+  public XObject getLocalVariable(XPathContext xctxt, int index, boolean destructiveOK)
+          throws TransformerException
+  {
+
+    index += _currentFrameBottom;
+
+    XObject val = _stackFrames[index];
+    
+    if(null == val)
+      throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND, null),
+                     xctxt.getSAXLocator());
+      // "Variable accessed before it is bound!", xctxt.getSAXLocator());
+
+    // Lazy execution of variables.
+    if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
+      return (_stackFrames[index] = val.execute(xctxt));
+
+    return destructiveOK ? val : val.getFresh();
+  }
+
+  /**
+   * Tell if a local variable has been set or not.
+   *
+   * @param index Local variable index relative to the current stack
+   * frame bottom.
+   *
+   * @return true if the value at the index is not null.
+   *
+   * @throws TransformerException
+   */
+  public boolean isLocalSet(int index) throws TransformerException
+  {
+    return (_stackFrames[index + _currentFrameBottom] != null);
+  }
+
+  /** NEEDSDOC Field m_nulls          */
+  private static XObject[] m_nulls = new XObject[CLEARLIMITATION];
+
+  /**
+   * Use this to clear the variables in a section of the stack.  This is
+   * used to clear the parameter section of the stack, so that default param
+   * values can tell if they've already been set.  It is important to note that
+   * this function has a 1K limitation.
+   *
+   * @param start The start position, relative to the current local stack frame.
+   * @param len The number of slots to be cleared.
+   */
+  public void clearLocalSlots(int start, int len)
+  {
+
+    start += _currentFrameBottom;
+
+    System.arraycopy(m_nulls, 0, _stackFrames, start, len);
+  }
+
+  /**
+   * Set a global variable or parameter in the global stack frame.
+   *
+   *
+   * @param index Local variable index relative to the global stack frame
+   * bottom.
+   *
+   * @param val The value of the variable that is being set.
+   */
+  public void setGlobalVariable(final int index, final XObject val)
+  {
+    _stackFrames[index] = val;
+  }
+
+  /**
+   * Get a global variable or parameter from the global stack frame.
+   *
+   *
+   * @param xctxt The XPath context, which must be passed in order to
+   * lazy evaluate variables.
+   *
+   * @param index Global variable index relative to the global stack
+   * frame bottom.
+   *
+   * @return The value of the variable.
+   *
+   * @throws TransformerException
+   */
+  public XObject getGlobalVariable(XPathContext xctxt, final int index)
+          throws TransformerException
+  {
+
+    XObject val = _stackFrames[index];
+
+    // Lazy execution of variables.
+    if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
+      return (_stackFrames[index] = val.execute(xctxt));
+
+    return val;
+  }
+  
+  /**
+   * Get a global variable or parameter from the global stack frame.
+   *
+   *
+   * @param xctxt The XPath context, which must be passed in order to
+   * lazy evaluate variables.
+   *
+   * @param index Global variable index relative to the global stack
+   * frame bottom.
+   *
+   * @return The value of the variable.
+   *
+   * @throws TransformerException
+   */
+  public XObject getGlobalVariable(XPathContext xctxt, final int index, boolean destructiveOK)
+          throws TransformerException
+  {
+
+    XObject val = _stackFrames[index];
+
+    // Lazy execution of variables.
+    if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
+      return (_stackFrames[index] = val.execute(xctxt));
+
+    return destructiveOK ? val : val.getFresh();
+  }
+
+  /**
+   * Get a variable based on it's qualified name.
+   * This is for external use only.
+   *
+   * @param xctxt The XPath context, which must be passed in order to
+   * lazy evaluate variables.
+   * 
+   * @param qname The qualified name of the variable.
+   *
+   * @return The evaluated value of the variable.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject getVariableOrParam(
+          XPathContext xctxt, org.apache.xml.utils.QName qname)
+            throws javax.xml.transform.TransformerException
+  {
+
+    org.apache.xml.utils.PrefixResolver prefixResolver =
+      xctxt.getNamespaceContext();
+
+    // Get the current ElemTemplateElement, which must be pushed in as the 
+    // prefix resolver, and then walk backwards in document order, searching 
+    // for an xsl:param element or xsl:variable element that matches our 
+    // qname.  If we reach the top level, use the StylesheetRoot's composed
+    // list of top level variables and parameters.
+
+    if (prefixResolver instanceof org.apache.xalan.templates.ElemTemplateElement)
+    {
+      
+      org.apache.xalan.templates.ElemVariable vvar;
+
+      org.apache.xalan.templates.ElemTemplateElement prev =
+        (org.apache.xalan.templates.ElemTemplateElement) prefixResolver;
+
+      if (!(prev instanceof org.apache.xalan.templates.Stylesheet))
+      {
+        while ( !(prev.getParentNode() instanceof org.apache.xalan.templates.Stylesheet) )
+        {
+          org.apache.xalan.templates.ElemTemplateElement savedprev = prev;
+
+          while (null != (prev = prev.getPreviousSiblingElem()))
+          {
+            if (prev instanceof org.apache.xalan.templates.ElemVariable)
+            {
+              vvar = (org.apache.xalan.templates.ElemVariable) prev;
+
+              if (vvar.getName().equals(qname))
+                return getLocalVariable(xctxt, vvar.getIndex());
+            }
+          }
+          prev = savedprev.getParentElem();
+        }
+      }
+
+      vvar = prev.getStylesheetRoot().getVariableOrParamComposed(qname);
+      if (null != vvar)
+        return getGlobalVariable(xctxt, vvar.getIndex());
+    }
+
+    throw new javax.xml.transform.TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VAR_NOT_RESOLVABLE, new Object[]{qname.toString()})); //"Variable not resolvable: " + qname);
+  }
+}  // end VariableStack
+
diff --git a/src/main/java/org/apache/xpath/WhitespaceStrippingElementMatcher.java b/src/main/java/org/apache/xpath/WhitespaceStrippingElementMatcher.java
new file mode 100644
index 0000000..2315b0c
--- /dev/null
+++ b/src/main/java/org/apache/xpath/WhitespaceStrippingElementMatcher.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WhitespaceStrippingElementMatcher.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import javax.xml.transform.TransformerException;
+
+import org.w3c.dom.Element;
+
+/**
+ * A class that implements this interface can tell if a given element should 
+ * strip whitespace nodes from it's children.
+ */
+public interface WhitespaceStrippingElementMatcher
+{
+  /**
+   * Get information about whether or not an element should strip whitespace.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @param support The XPath runtime state.
+   * @param targetElement Element to check
+   *
+   * @return true if the whitespace should be stripped.
+   *
+   * @throws TransformerException
+   */
+  public boolean shouldStripWhiteSpace(
+          XPathContext support, Element targetElement) throws TransformerException;
+  
+  /**
+   * Get information about whether or not whitespace can be stripped.
+   * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
+   *
+   * @return true if the whitespace can be stripped.
+   */
+  public boolean canStripWhiteSpace();
+}
diff --git a/src/main/java/org/apache/xpath/XPath.java b/src/main/java/org/apache/xpath/XPath.java
new file mode 100644
index 0000000..56c2b73
--- /dev/null
+++ b/src/main/java/org/apache/xpath/XPath.java
@@ -0,0 +1,643 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPath.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import java.io.Serializable;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.SAXSourceLocator;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.FunctionTable;
+import org.apache.xpath.compiler.XPathParser;
+import org.apache.xpath.functions.Function;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * The XPath class wraps an expression object and provides general services 
+ * for execution of that expression.
+ * @xsl.usage advanced
+ */
+public class XPath implements Serializable, ExpressionOwner
+{
+    static final long serialVersionUID = 3976493477939110553L;
+
+  /** The top of the expression tree. 
+   *  @serial */
+  private Expression m_mainExp;
+  
+  /**
+   * The function table for xpath build-in functions
+   */
+  private transient FunctionTable m_funcTable = null;
+
+  /**
+   * initial the function table
+   */
+  private void initFunctionTable(){
+  	      m_funcTable = new FunctionTable();
+  }
+
+  /**
+   * Get the raw Expression object that this class wraps.
+   *
+   *
+   * @return the raw Expression object, which should not normally be null.
+   */
+  public Expression getExpression()
+  {
+    return m_mainExp;
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    m_mainExp.fixupVariables(vars, globalsSize);
+  }
+
+  /**
+   * Set the raw expression object for this object.
+   *
+   *
+   * @param exp the raw Expression object, which should not normally be null.
+   */
+  public void setExpression(Expression exp)
+  {
+  	if(null != m_mainExp)
+    	exp.exprSetParent(m_mainExp.exprGetParent()); // a bit bogus
+    m_mainExp = exp;
+  }
+
+  /**
+   * Get the SourceLocator on the expression object.
+   *
+   *
+   * @return the SourceLocator on the expression object, which may be null.
+   */
+  public SourceLocator getLocator()
+  {
+    return m_mainExp;
+  }
+
+//  /**
+//   * Set the SourceLocator on the expression object.
+//   *
+//   *
+//   * @param l the SourceLocator on the expression object, which may be null.
+//   */
+//  public void setLocator(SourceLocator l)
+//  {
+//    // Note potential hazards -- l may not be serializable, or may be changed
+//      // after being assigned here.
+//    m_mainExp.setSourceLocator(l);
+//  }
+
+  /** The pattern string, mainly kept around for diagnostic purposes.
+   *  @serial  */
+  String m_patternString;
+
+  /**
+   * Return the XPath string associated with this object.
+   *
+   *
+   * @return the XPath string associated with this object.
+   */
+  public String getPatternString()
+  {
+    return m_patternString;
+  }
+
+  /** Represents a select type expression. */
+  public static final int SELECT = 0;
+
+  /** Represents a match type expression.  */
+  public static final int MATCH = 1;
+
+  /**
+   * Construct an XPath object.  
+   *
+   * (Needs review -sc) This method initializes an XPathParser/
+   * Compiler and compiles the expression.
+   * @param exprString The XPath expression.
+   * @param locator The location of the expression, may be null.
+   * @param prefixResolver A prefix resolver to use to resolve prefixes to 
+   *                       namespace URIs.
+   * @param type one of {@link #SELECT} or {@link #MATCH}.
+   * @param errorListener The error listener, or null if default should be used.
+   *
+   * @throws javax.xml.transform.TransformerException if syntax or other error.
+   */
+  public XPath(
+          String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type,
+          ErrorListener errorListener)
+            throws javax.xml.transform.TransformerException
+  { 
+    initFunctionTable();     
+    if(null == errorListener)
+      errorListener = new org.apache.xml.utils.DefaultErrorHandler();
+    
+    m_patternString = exprString;
+
+    XPathParser parser = new XPathParser(errorListener, locator);
+    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
+
+    if (SELECT == type)
+      parser.initXPath(compiler, exprString, prefixResolver);
+    else if (MATCH == type)
+      parser.initMatchPattern(compiler, exprString, prefixResolver);
+    else
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, new Object[]{Integer.toString(type)})); //"Can not deal with XPath type: " + type);
+
+    // System.out.println("----------------");
+    Expression expr = compiler.compile(0);
+
+    // System.out.println("expr: "+expr);
+    this.setExpression(expr);
+    
+    if((null != locator) && locator instanceof ExpressionNode)
+    {
+    	expr.exprSetParent((ExpressionNode)locator);
+    }
+
+  }
+
+  /**
+   * Construct an XPath object.  
+   *
+   * (Needs review -sc) This method initializes an XPathParser/
+   * Compiler and compiles the expression.
+   * @param exprString The XPath expression.
+   * @param locator The location of the expression, may be null.
+   * @param prefixResolver A prefix resolver to use to resolve prefixes to 
+   *                       namespace URIs.
+   * @param type one of {@link #SELECT} or {@link #MATCH}.
+   * @param errorListener The error listener, or null if default should be used.
+   *
+   * @throws javax.xml.transform.TransformerException if syntax or other error.
+   */
+  public XPath(
+          String exprString, SourceLocator locator, 
+          PrefixResolver prefixResolver, int type,
+          ErrorListener errorListener, FunctionTable aTable)
+            throws javax.xml.transform.TransformerException
+  { 
+    m_funcTable = aTable;     
+    if(null == errorListener)
+      errorListener = new org.apache.xml.utils.DefaultErrorHandler();
+    
+    m_patternString = exprString;
+
+    XPathParser parser = new XPathParser(errorListener, locator);
+    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
+
+    if (SELECT == type)
+      parser.initXPath(compiler, exprString, prefixResolver);
+    else if (MATCH == type)
+      parser.initMatchPattern(compiler, exprString, prefixResolver);
+    else
+      throw new RuntimeException(XSLMessages.createXPATHMessage(
+            XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, 
+            new Object[]{Integer.toString(type)})); 
+            //"Can not deal with XPath type: " + type);
+
+    // System.out.println("----------------");
+    Expression expr = compiler.compile(0);
+
+    // System.out.println("expr: "+expr);
+    this.setExpression(expr);
+    
+    if((null != locator) && locator instanceof ExpressionNode)
+    {
+    	expr.exprSetParent((ExpressionNode)locator);
+    }
+
+  }
+  
+  /**
+   * Construct an XPath object.  
+   *
+   * (Needs review -sc) This method initializes an XPathParser/
+   * Compiler and compiles the expression.
+   * @param exprString The XPath expression.
+   * @param locator The location of the expression, may be null.
+   * @param prefixResolver A prefix resolver to use to resolve prefixes to 
+   *                       namespace URIs.
+   * @param type one of {@link #SELECT} or {@link #MATCH}.
+   *
+   * @throws javax.xml.transform.TransformerException if syntax or other error.
+   */
+  public XPath(
+          String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)
+            throws javax.xml.transform.TransformerException
+  {  
+    this(exprString, locator, prefixResolver, type, null);    
+  }
+
+  /**
+   * Construct an XPath object.
+   *
+   * @param expr The Expression object.
+   *
+   * @throws javax.xml.transform.TransformerException if syntax or other error.
+   */
+  public XPath(Expression expr)
+  {  
+    this.setExpression(expr);
+    initFunctionTable();   
+  }
+  
+  /**
+   * Given an expression and a context, evaluate the XPath
+   * and return the result.
+   * 
+   * @param xctxt The execution context.
+   * @param contextNode The node that "." expresses.
+   * @param namespaceContext The context in which namespaces in the
+   * XPath are supposed to be expanded.
+   *
+   * @return The result of the XPath or null if callbacks are used.
+   * @throws TransformerException thrown if
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage experimental
+   */
+  public XObject execute(
+          XPathContext xctxt, org.w3c.dom.Node contextNode, 
+          PrefixResolver namespaceContext)
+            throws javax.xml.transform.TransformerException
+  {
+    return execute(
+          xctxt, xctxt.getDTMHandleFromNode(contextNode), 
+          namespaceContext);
+  }
+  
+
+  /**
+   * Given an expression and a context, evaluate the XPath
+   * and return the result.
+   * 
+   * @param xctxt The execution context.
+   * @param contextNode The node that "." expresses.
+   * @param namespaceContext The context in which namespaces in the
+   * XPath are supposed to be expanded.
+   * 
+   * @throws TransformerException thrown if the active ProblemListener decides
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage experimental
+   */
+  public XObject execute(
+          XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
+            throws javax.xml.transform.TransformerException
+  {
+
+    xctxt.pushNamespaceContext(namespaceContext);
+
+    xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
+
+    XObject xobj = null;
+
+    try
+    {
+      xobj = m_mainExp.execute(xctxt);
+    }
+    catch (TransformerException te)
+    {
+      te.setLocator(this.getLocator());
+      ErrorListener el = xctxt.getErrorListener();
+      if(null != el) // defensive, should never happen.
+      {
+        el.error(te);
+      }
+      else
+        throw te;
+    }
+    catch (Exception e)
+    {
+      while (e instanceof org.apache.xml.utils.WrappedRuntimeException)
+      {
+        e = ((org.apache.xml.utils.WrappedRuntimeException) e).getException();
+      }
+      // e.printStackTrace();
+
+      String msg = e.getMessage();
+      
+      if (msg == null || msg.length() == 0) {
+           msg = XSLMessages.createXPATHMessage(
+               XPATHErrorResources.ER_XPATH_ERROR, null);
+     
+      }  
+      TransformerException te = new TransformerException(msg,
+              getLocator(), e);
+      ErrorListener el = xctxt.getErrorListener();
+      // te.printStackTrace();
+      if(null != el) // defensive, should never happen.
+      {
+        el.fatalError(te);
+      }
+      else
+        throw te;
+    }
+    finally
+    {
+      xctxt.popNamespaceContext();
+
+      xctxt.popCurrentNodeAndExpression();
+    }
+
+    return xobj;
+  }
+  
+  /**
+   * Given an expression and a context, evaluate the XPath
+   * and return the result.
+   * 
+   * @param xctxt The execution context.
+   * @param contextNode The node that "." expresses.
+   * @param namespaceContext The context in which namespaces in the
+   * XPath are supposed to be expanded.
+   * 
+   * @throws TransformerException thrown if the active ProblemListener decides
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage experimental
+   */
+  public boolean bool(
+          XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
+            throws javax.xml.transform.TransformerException
+  {
+
+    xctxt.pushNamespaceContext(namespaceContext);
+
+    xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
+
+    try
+    {
+      return m_mainExp.bool(xctxt);
+    }
+    catch (TransformerException te)
+    {
+      te.setLocator(this.getLocator());
+      ErrorListener el = xctxt.getErrorListener();
+      if(null != el) // defensive, should never happen.
+      {
+        el.error(te);
+      }
+      else
+        throw te;
+    }
+    catch (Exception e)
+    {
+      while (e instanceof org.apache.xml.utils.WrappedRuntimeException)
+      {
+        e = ((org.apache.xml.utils.WrappedRuntimeException) e).getException();
+      }
+      // e.printStackTrace();
+
+      String msg = e.getMessage();
+      
+      if (msg == null || msg.length() == 0) {
+           msg = XSLMessages.createXPATHMessage(
+               XPATHErrorResources.ER_XPATH_ERROR, null);
+     
+      }        
+      
+      TransformerException te = new TransformerException(msg,
+              getLocator(), e);
+      ErrorListener el = xctxt.getErrorListener();
+      // te.printStackTrace();
+      if(null != el) // defensive, should never happen.
+      {
+        el.fatalError(te);
+      }
+      else
+        throw te;
+    }
+    finally
+    {
+      xctxt.popNamespaceContext();
+
+      xctxt.popCurrentNodeAndExpression();
+    }
+
+    return false;
+  }
+
+  /** Set to true to get diagnostic messages about the result of 
+   *  match pattern testing.  */
+  private static final boolean DEBUG_MATCHES = false;
+
+  /**
+   * Get the match score of the given node.
+   *
+   * @param xctxt XPath runtime context.
+   * @param context The current source tree context node.
+   * 
+   * @return score, one of {@link #MATCH_SCORE_NODETEST},
+   * {@link #MATCH_SCORE_NONE}, {@link #MATCH_SCORE_OTHER}, 
+   * or {@link #MATCH_SCORE_QNAME}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double getMatchScore(XPathContext xctxt, int context)
+          throws javax.xml.transform.TransformerException
+  {
+
+    xctxt.pushCurrentNode(context);
+    xctxt.pushCurrentExpressionNode(context);
+
+    try
+    {
+      XObject score = m_mainExp.execute(xctxt);
+
+      if (DEBUG_MATCHES)
+      {
+        DTM dtm = xctxt.getDTM(context);
+        System.out.println("score: " + score.num() + " for "
+                           + dtm.getNodeName(context) + " for xpath "
+                           + this.getPatternString());
+      }
+
+      return score.num();
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+      xctxt.popCurrentExpressionNode();
+    }
+
+    // return XPath.MATCH_SCORE_NONE;
+  }
+
+
+  /**
+   * Warn the user of an problem.
+   *
+   * @param xctxt The XPath runtime context.
+   * @param sourceNode Not used.
+   * @param msg An error msgkey that corresponds to one of the constants found 
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which 
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to 
+   *                              throw an exception.
+   */
+  public void warn(
+          XPathContext xctxt, int sourceNode, String msg, Object[] args)
+            throws javax.xml.transform.TransformerException
+  {
+
+    String fmsg = XSLMessages.createXPATHWarning(msg, args);
+    ErrorListener ehandler = xctxt.getErrorListener();
+
+    if (null != ehandler)
+    {
+
+      // TO DO: Need to get stylesheet Locator from here.
+      ehandler.warning(new TransformerException(fmsg, (SAXSourceLocator)xctxt.getSAXLocator()));
+    }
+  }
+
+  /**
+   * Tell the user of an assertion error, and probably throw an
+   * exception.
+   *
+   * @param b  If false, a runtime exception will be thrown.
+   * @param msg The assertion message, which should be informative.
+   * 
+   * @throws RuntimeException if the b argument is false.
+   */
+  public void assertion(boolean b, String msg)
+  {
+
+    if (!b)
+    {
+      String fMsg = XSLMessages.createXPATHMessage(
+        XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
+        new Object[]{ msg });
+
+      throw new RuntimeException(fMsg);
+    }
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param xctxt The XPath runtime context.
+   * @param sourceNode Not used.
+   * @param msg An error msgkey that corresponds to one of the constants found 
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which 
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to 
+   *                              throw an exception.
+   */
+  public void error(
+          XPathContext xctxt, int sourceNode, String msg, Object[] args)
+            throws javax.xml.transform.TransformerException
+  {
+
+    String fmsg = XSLMessages.createXPATHMessage(msg, args);
+    ErrorListener ehandler = xctxt.getErrorListener();
+
+    if (null != ehandler)
+    {
+      ehandler.fatalError(new TransformerException(fmsg,
+                              (SAXSourceLocator)xctxt.getSAXLocator()));
+    }
+    else
+    {
+      SourceLocator slocator = xctxt.getSAXLocator();
+      System.out.println(fmsg + "; file " + slocator.getSystemId()
+                         + "; line " + slocator.getLineNumber() + "; column "
+                         + slocator.getColumnNumber());
+    }
+  }
+  
+  /**
+   * This will traverse the heararchy, calling the visitor for 
+   * each member.  If the called visitor method returns 
+   * false, the subtree should not be called.
+   * 
+   * @param owner The owner of the visitor, where that path may be 
+   *              rewritten if needed.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	m_mainExp.callVisitors(this, visitor);
+  }
+
+  /**
+   * The match score if no match is made.
+   * @xsl.usage advanced
+   */
+  public static final double MATCH_SCORE_NONE = Double.NEGATIVE_INFINITY;
+
+  /**
+   * The match score if the pattern has the form
+   * of a QName optionally preceded by an @ character.
+   * @xsl.usage advanced
+   */
+  public static final double MATCH_SCORE_QNAME = 0.0;
+
+  /**
+   * The match score if the pattern pattern has the form NCName:*.
+   * @xsl.usage advanced
+   */
+  public static final double MATCH_SCORE_NSWILD = -0.25;
+
+  /**
+   * The match score if the pattern consists of just a NodeTest.
+   * @xsl.usage advanced
+   */
+  public static final double MATCH_SCORE_NODETEST = -0.5;
+
+  /**
+   * The match score if the pattern consists of something
+   * other than just a NodeTest or just a qname.
+   * @xsl.usage advanced
+   */
+  public static final double MATCH_SCORE_OTHER = 0.5;
+}
diff --git a/src/main/java/org/apache/xpath/XPathAPI.java b/src/main/java/org/apache/xpath/XPathAPI.java
new file mode 100644
index 0000000..01d1741
--- /dev/null
+++ b/src/main/java/org/apache/xpath/XPathAPI.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathAPI.java 524807 2007-04-02 15:51:43Z zongaro $
+ */
+package org.apache.xpath;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.PrefixResolverDefault;
+import org.apache.xpath.objects.XObject;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * The methods in this class are convenience methods into the
+ * low-level XPath API.
+ * These functions tend to be a little slow, since a number of objects must be
+ * created for each evaluation.  A faster way is to precompile the
+ * XPaths using the low-level API, and then just use the XPaths
+ * over and over.
+ *
+ * NOTE: In particular, each call to this method will create a new
+ * XPathContext, a new DTMManager... and thus a new DTM. That's very
+ * safe, since it guarantees that you're always processing against a
+ * fully up-to-date view of your document. But it's also portentially
+ * very expensive, since you're rebuilding the DTM every time. You should
+ * consider using an instance of CachedXPathAPI rather than these static
+ * methods.
+ *
+ * @see <a href="http://www.w3.org/TR/xpath">XPath Specification</a> 
+ * */
+public class XPathAPI
+{
+
+  /**
+   * Use an XPath string to select a single node. XPath namespace
+   * prefixes are resolved from the context node, which may not
+   * be what you want (see the next method).
+   *
+   * @param contextNode The node to start searching from.
+   * @param str A valid XPath string.
+   * @return The first node found that matches the XPath, or null.
+   *
+   * @throws TransformerException
+   */
+  public static Node selectSingleNode(Node contextNode, String str)
+          throws TransformerException
+  {
+    return selectSingleNode(contextNode, str, contextNode);
+  }
+
+  /**
+   * Use an XPath string to select a single node.
+   * XPath namespace prefixes are resolved from the namespaceNode.
+   *
+   * @param contextNode The node to start searching from.
+   * @param str A valid XPath string.
+   * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
+   * @return The first node found that matches the XPath, or null.
+   *
+   * @throws TransformerException
+   */
+  public static Node selectSingleNode(
+          Node contextNode, String str, Node namespaceNode)
+            throws TransformerException
+  {
+
+    // Have the XObject return its result as a NodeSetDTM.
+    NodeIterator nl = selectNodeIterator(contextNode, str, namespaceNode);
+
+    // Return the first node, or null
+    return nl.nextNode();
+  }
+
+  /**
+   *  Use an XPath string to select a nodelist.
+   *  XPath namespace prefixes are resolved from the contextNode.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @return A NodeIterator, should never be null.
+   *
+   * @throws TransformerException
+   */
+  public static NodeIterator selectNodeIterator(Node contextNode, String str)
+          throws TransformerException
+  {
+    return selectNodeIterator(contextNode, str, contextNode);
+  }
+
+  /**
+   *  Use an XPath string to select a nodelist.
+   *  XPath namespace prefixes are resolved from the namespaceNode.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
+   *  @return A NodeIterator, should never be null.
+   *
+   * @throws TransformerException
+   */
+  public static NodeIterator selectNodeIterator(
+          Node contextNode, String str, Node namespaceNode)
+            throws TransformerException
+  {
+
+    // Execute the XPath, and have it return the result
+    XObject list = eval(contextNode, str, namespaceNode);
+
+    // Have the XObject return its result as a NodeSetDTM.                
+    return list.nodeset();
+  }
+
+  /**
+   *  Use an XPath string to select a nodelist.
+   *  XPath namespace prefixes are resolved from the contextNode.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @return A NodeIterator, should never be null.
+   *
+   * @throws TransformerException
+   */
+  public static NodeList selectNodeList(Node contextNode, String str)
+          throws TransformerException
+  {
+    return selectNodeList(contextNode, str, contextNode);
+  }
+
+  /**
+   *  Use an XPath string to select a nodelist.
+   *  XPath namespace prefixes are resolved from the namespaceNode.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
+   *  @return A NodeIterator, should never be null.
+   *
+   * @throws TransformerException
+   */
+  public static NodeList selectNodeList(
+          Node contextNode, String str, Node namespaceNode)
+            throws TransformerException
+  {
+
+    // Execute the XPath, and have it return the result
+    XObject list = eval(contextNode, str, namespaceNode);
+
+    // Return a NodeList.
+    return list.nodelist();
+  }
+
+  /**
+   *  Evaluate XPath string to an XObject.  Using this method,
+   *  XPath namespace prefixes will be resolved from the namespaceNode.
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
+   *  @see org.apache.xpath.objects.XObject
+   *  @see org.apache.xpath.objects.XNull
+   *  @see org.apache.xpath.objects.XBoolean
+   *  @see org.apache.xpath.objects.XNumber
+   *  @see org.apache.xpath.objects.XString
+   *  @see org.apache.xpath.objects.XRTreeFrag
+   *
+   * @throws TransformerException
+   */
+  public static XObject eval(Node contextNode, String str)
+          throws TransformerException
+  {
+    return eval(contextNode, str, contextNode);
+  }
+
+  /**
+   *  Evaluate XPath string to an XObject.
+   *  XPath namespace prefixes are resolved from the namespaceNode.
+   *  The implementation of this is a little slow, since it creates
+   *  a number of objects each time it is called.  This could be optimized
+   *  to keep the same objects around, but then thread-safety issues would arise.
+   *
+   *  @param contextNode The node to start searching from.
+   *  @param str A valid XPath string.
+   *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
+   *  @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
+   *  @see org.apache.xpath.objects.XObject
+   *  @see org.apache.xpath.objects.XNull
+   *  @see org.apache.xpath.objects.XBoolean
+   *  @see org.apache.xpath.objects.XNumber
+   *  @see org.apache.xpath.objects.XString
+   *  @see org.apache.xpath.objects.XRTreeFrag
+   *
+   * @throws TransformerException
+   */
+  public static XObject eval(Node contextNode, String str, Node namespaceNode)
+          throws TransformerException
+  {
+
+    // Since we don't have a XML Parser involved here, install some default support
+    // for things like namespaces, etc.
+    // (Changed from: XPathContext xpathSupport = new XPathContext();
+    //    because XPathContext is weak in a number of areas... perhaps
+    //    XPathContext should be done away with.)
+    // Create an XPathContext that doesn't support pushing and popping of
+    // variable resolution scopes.  Sufficient for simple XPath 1.0 expressions.
+    XPathContext xpathSupport = new XPathContext(false);
+
+    // Create an object to resolve namespace prefixes.
+    // XPath namespaces are resolved from the input context node's document element
+    // if it is a root node, or else the current context node (for lack of a better
+    // resolution space, given the simplicity of this sample code).
+    PrefixResolverDefault prefixResolver = new PrefixResolverDefault(
+      (namespaceNode.getNodeType() == Node.DOCUMENT_NODE)
+      ? ((Document) namespaceNode).getDocumentElement() : namespaceNode);
+
+    // Create the XPath object.
+    XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
+
+    // Execute the XPath, and have it return the result
+    // return xpath.execute(xpathSupport, contextNode, prefixResolver);
+    int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
+
+    return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
+  }
+
+  /**
+   *   Evaluate XPath string to an XObject.
+   *   XPath namespace prefixes are resolved from the namespaceNode.
+   *   The implementation of this is a little slow, since it creates
+   *   a number of objects each time it is called.  This could be optimized
+   *   to keep the same objects around, but then thread-safety issues would arise.
+   *
+   *   @param contextNode The node to start searching from.
+   *   @param str A valid XPath string.
+   *   @param prefixResolver Will be called if the parser encounters namespace
+   *                         prefixes, to resolve the prefixes to URLs.
+   *   @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
+   *   @see org.apache.xpath.objects.XObject
+   *   @see org.apache.xpath.objects.XNull
+   *   @see org.apache.xpath.objects.XBoolean
+   *   @see org.apache.xpath.objects.XNumber
+   *   @see org.apache.xpath.objects.XString
+   *   @see org.apache.xpath.objects.XRTreeFrag
+   *
+   * @throws TransformerException
+   */
+  public static XObject eval(
+          Node contextNode, String str, PrefixResolver prefixResolver)
+            throws TransformerException
+  {
+
+    // Since we don't have a XML Parser involved here, install some default support
+    // for things like namespaces, etc.
+    // (Changed from: XPathContext xpathSupport = new XPathContext();
+    //    because XPathContext is weak in a number of areas... perhaps
+    //    XPathContext should be done away with.)
+    // Create the XPath object.
+    XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
+
+    // Create an XPathContext that doesn't support pushing and popping of
+    // variable resolution scopes.  Sufficient for simple XPath 1.0 expressions.
+    XPathContext xpathSupport = new XPathContext(false);
+
+    // Execute the XPath, and have it return the result
+    int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode);
+
+    return xpath.execute(xpathSupport, ctxtNode, prefixResolver);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/XPathContext.java b/src/main/java/org/apache/xpath/XPathContext.java
new file mode 100644
index 0000000..5da437e
--- /dev/null
+++ b/src/main/java/org/apache/xpath/XPathContext.java
@@ -0,0 +1,1352 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathContext.java 524809 2007-04-02 15:51:51Z zongaro $
+ */
+package org.apache.xpath;
+
+import java.lang.reflect.Method;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+
+import org.apache.xalan.extensions.ExpressionContext;
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.dtm.DTMWSFilter;
+import org.apache.xml.dtm.ref.sax2dtm.SAX2RTFDTM;
+import org.apache.xml.utils.IntStack;
+import org.apache.xml.utils.NodeVector;
+import org.apache.xml.utils.ObjectStack;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.SAXSourceLocator;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.axes.SubContextList;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.DTMXRTreeFrag;
+import org.apache.xpath.objects.XString;
+import org.apache.xpath.res.XPATHErrorResources;
+
+import org.xml.sax.XMLReader;
+
+/**
+ * Default class for the runtime execution context for XPath.
+ * 
+ * <p>This class extends DTMManager but does not directly implement it.</p>
+ * @xsl.usage advanced
+ */
+public class XPathContext extends DTMManager // implements ExpressionContext
+{
+	IntStack m_last_pushed_rtfdtm=new IntStack();	
+  /**
+   * Stack of cached "reusable" DTMs for Result Tree Fragments.
+   * This is a kluge to handle the problem of starting an RTF before
+   * the old one is complete.
+   * 
+   * %REVIEW% I'm using a Vector rather than Stack so we can reuse
+   * the DTMs if the problem occurs multiple times. I'm not sure that's
+   * really a net win versus discarding the DTM and starting a new one...
+   * but the retained RTF DTM will have been tail-pruned so should be small.
+   */
+  private Vector m_rtfdtm_stack=null;
+  /** Index of currently active RTF DTM in m_rtfdtm_stack */
+  private int m_which_rtfdtm=-1;
+  
+ /**
+   * Most recent "reusable" DTM for Global Result Tree Fragments. No stack is
+   * required since we're never going to pop these.
+   */
+  private SAX2RTFDTM m_global_rtfdtm=null;
+  
+  /**
+   * HashMap of cached the DTMXRTreeFrag objects, which are identified by DTM IDs.
+   * The object are just wrappers for DTMs which are used in  XRTreeFrag.
+   */
+  private HashMap m_DTMXRTreeFrags = null;
+  
+  /**
+   * state of the secure processing feature.
+   */
+  private boolean m_isSecureProcessing = false;
+	
+  /**
+   * Though XPathContext context extends 
+   * the DTMManager, it really is a proxy for this object, which 
+   * is the real DTMManager.
+   */
+  protected DTMManager m_dtmManager = DTMManager.newInstance(
+                   org.apache.xpath.objects.XMLStringFactoryImpl.getFactory());
+  
+  /**
+   * Return the DTMManager object.  Though XPathContext context extends 
+   * the DTMManager, it really is a proxy for the real DTMManager.  If a 
+   * caller needs to make a lot of calls to the DTMManager, it is faster 
+   * if it gets the real one from this function.
+   */
+   public DTMManager getDTMManager()
+   {
+     return m_dtmManager;
+   }
+  
+  /**
+   * Set the state of the secure processing feature
+   */
+  public void setSecureProcessing(boolean flag)
+  {
+    m_isSecureProcessing = flag;
+  }
+  
+  /**
+   * Return the state of the secure processing feature
+   */
+  public boolean isSecureProcessing()
+  {
+    return m_isSecureProcessing;
+  }
+  
+  /**
+   * Get an instance of a DTM, loaded with the content from the
+   * specified source.  If the unique flag is true, a new instance will
+   * always be returned.  Otherwise it is up to the DTMManager to return a
+   * new instance or an instance that it already created and may be being used
+   * by someone else.
+   * (I think more parameters will need to be added for error handling, and entity
+   * resolution).
+   *
+   * @param source the specification of the source object, which may be null, 
+   *               in which case it is assumed that node construction will take 
+   *               by some other means.
+   * @param unique true if the returned DTM must be unique, probably because it
+   * is going to be mutated.
+   * @param wsfilter Enables filtering of whitespace nodes, and may be null.
+   * @param incremental true if the construction should try and be incremental.
+   * @param doIndexing true if the caller considers it worth it to use 
+   *                   indexing schemes.
+   *
+   * @return a non-null DTM reference.
+   */
+  public DTM getDTM(javax.xml.transform.Source source, boolean unique, 
+                    DTMWSFilter wsfilter,
+                    boolean incremental,
+                    boolean doIndexing)
+  {
+    return m_dtmManager.getDTM(source, unique, wsfilter, 
+                               incremental, doIndexing);
+  }
+                             
+  /**
+   * Get an instance of a DTM that "owns" a node handle. 
+   *
+   * @param nodeHandle the nodeHandle.
+   *
+   * @return a non-null DTM reference.
+   */
+  public DTM getDTM(int nodeHandle)
+  {
+    return m_dtmManager.getDTM(nodeHandle);
+  }
+
+  /**
+   * Given a W3C DOM node, try and return a DTM handle.
+   * Note: calling this may be non-optimal.
+   * 
+   * @param node Non-null reference to a DOM node.
+   * 
+   * @return a valid DTM handle.
+   */
+  public int getDTMHandleFromNode(org.w3c.dom.Node node)
+  {
+    return m_dtmManager.getDTMHandleFromNode(node);
+  }
+//
+//  
+  /**
+   * %TBD% Doc
+   */
+  public int getDTMIdentity(DTM dtm)
+  {
+    return m_dtmManager.getDTMIdentity(dtm);
+  }
+//  
+  /**
+   * Creates an empty <code>DocumentFragment</code> object. 
+   * @return A new <code>DocumentFragment handle</code>.
+   */
+  public DTM createDocumentFragment()
+  {
+    return m_dtmManager.createDocumentFragment();
+  }
+//  
+  /**
+   * Release a DTM either to a lru pool, or completely remove reference.
+   * DTMs without system IDs are always hard deleted.
+   * State: experimental.
+   * 
+   * @param dtm The DTM to be released.
+   * @param shouldHardDelete True if the DTM should be removed no matter what.
+   * @return true if the DTM was removed, false if it was put back in a lru pool.
+   */
+  public boolean release(DTM dtm, boolean shouldHardDelete)
+  {
+    // %REVIEW% If it's a DTM which may contain multiple Result Tree
+    // Fragments, we can't discard it unless we know not only that it
+    // is empty, but that the XPathContext itself is going away. So do
+    // _not_ accept the request. (May want to do it as part of
+    // reset(), though.)
+    if(m_rtfdtm_stack!=null && m_rtfdtm_stack.contains(dtm))
+    {
+      return false;
+    }
+  	
+    return m_dtmManager.release(dtm, shouldHardDelete);
+  }
+
+  /**
+   * Create a new <code>DTMIterator</code> based on an XPath
+   * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
+   * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
+   *
+   * @param xpathCompiler ??? Somehow we need to pass in a subpart of the
+   * expression.  I hate to do this with strings, since the larger expression
+   * has already been parsed.
+   *
+   * @param pos The position in the expression.
+   * @return The newly created <code>DTMIterator</code>.
+   */
+  public DTMIterator createDTMIterator(Object xpathCompiler, int pos)
+  {
+    return m_dtmManager.createDTMIterator(xpathCompiler, pos);
+  }
+//
+  /**
+   * Create a new <code>DTMIterator</code> based on an XPath
+   * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
+   * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
+   *
+   * @param xpathString Must be a valid string expressing a
+   * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
+   * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
+   *
+   * @param presolver An object that can resolve prefixes to namespace URLs.
+   *
+   * @return The newly created <code>DTMIterator</code>.
+   */
+  public DTMIterator createDTMIterator(String xpathString,
+          PrefixResolver presolver)
+  {
+    return m_dtmManager.createDTMIterator(xpathString, presolver);
+  }
+//
+  /**
+   * Create a new <code>DTMIterator</code> based only on a whatToShow and
+   * a DTMFilter.  The traversal semantics are defined as the descendant
+   * access.
+   *
+   * @param whatToShow This flag specifies which node types may appear in
+   *   the logical view of the tree presented by the iterator. See the
+   *   description of <code>NodeFilter</code> for the set of possible
+   *   <code>SHOW_</code> values.These flags can be combined using
+   *   <code>OR</code>.
+   * @param filter The <code>NodeFilter</code> to be used with this
+   *   <code>TreeWalker</code>, or <code>null</code> to indicate no filter.
+   * @param entityReferenceExpansion The value of this flag determines
+   *   whether entity reference nodes are expanded.
+   *
+   * @return The newly created <code>NodeIterator</code>.
+   */
+  public DTMIterator createDTMIterator(int whatToShow,
+          DTMFilter filter, boolean entityReferenceExpansion)
+  {
+    return m_dtmManager.createDTMIterator(whatToShow, filter, entityReferenceExpansion);
+  }
+  
+  /**
+   * Create a new <code>DTMIterator</code> that holds exactly one node.
+   *
+   * @param node The node handle that the DTMIterator will iterate to.
+   *
+   * @return The newly created <code>DTMIterator</code>.
+   */
+  public DTMIterator createDTMIterator(int node)
+  {
+    // DescendantIterator iter = new DescendantIterator();
+    DTMIterator iter = new org.apache.xpath.axes.OneStepIteratorForward(Axis.SELF);
+    iter.setRoot(node, this);
+    return iter;
+    // return m_dtmManager.createDTMIterator(node);
+  }
+
+  /**
+   * Create an XPathContext instance.  This is equivalent to calling
+   * the {@link #XPathContext(boolean)} constructor with the value
+   * <code>true</code>.
+   */
+  public XPathContext() {
+    this(true);
+  }
+
+  /**
+   * Create an XPathContext instance.
+   * @param recursiveVarContext A <code>boolean</code> value indicating whether
+   *             the XPath context needs to support pushing of scopes for
+   *             variable resolution
+   */
+  public XPathContext(boolean recursiveVarContext) {
+    m_prefixResolvers.push(null);
+    m_currentNodes.push(DTM.NULL);
+    m_currentExpressionNodes.push(DTM.NULL);
+    m_saxLocations.push(null);
+    m_variableStacks = recursiveVarContext ? new VariableStack()
+                                           : new VariableStack(1);
+  }
+
+  /**
+   * Create an XPathContext instance.  This is equivalent to calling the
+   * constructor {@link #XPathContext(java.lang.Object,boolean)} with the
+   * value of the second parameter set to <code>true</code>.
+   * @param owner Value that can be retrieved via the getOwnerObject() method.
+   * @see #getOwnerObject
+   */
+  public XPathContext(Object owner)
+  {
+    this(owner, true);
+  }
+
+  /**
+   * Create an XPathContext instance.
+   * @param owner Value that can be retrieved via the getOwnerObject() method.
+   * @see #getOwnerObject
+   * @param recursiveVarContext A <code>boolean</code> value indicating whether
+   *             the XPath context needs to support pushing of scopes for
+   *             variable resolution
+   */
+  public XPathContext(Object owner, boolean recursiveVarContext) {
+    this(recursiveVarContext);
+    m_owner = owner;
+    try {
+      m_ownerGetErrorListener = m_owner.getClass().getMethod("getErrorListener", new Class[] {});
+    }
+    catch (NoSuchMethodException nsme) {}
+  }
+
+  /**
+   * Reset for new run.
+   */
+  public void reset()
+  {
+    releaseDTMXRTreeFrags();
+  	// These couldn't be disposed of earlier (see comments in release()); zap them now.
+  	if(m_rtfdtm_stack!=null)
+  		 for (java.util.Enumeration e = m_rtfdtm_stack.elements() ; e.hasMoreElements() ;) 
+  		 	m_dtmManager.release((DTM)e.nextElement(), true);
+
+    m_rtfdtm_stack=null; // drop our references too
+    m_which_rtfdtm=-1;
+    
+    if(m_global_rtfdtm!=null)
+  		 	m_dtmManager.release(m_global_rtfdtm,true);
+    m_global_rtfdtm=null;
+    
+  	
+    m_dtmManager = DTMManager.newInstance(
+                   org.apache.xpath.objects.XMLStringFactoryImpl.getFactory());
+                   
+    m_saxLocations.removeAllElements();   
+	m_axesIteratorStack.removeAllElements();
+	m_contextNodeLists.removeAllElements();
+	m_currentExpressionNodes.removeAllElements();
+	m_currentNodes.removeAllElements();
+	m_iteratorRoots.RemoveAllNoClear();
+	m_predicatePos.removeAllElements();
+	m_predicateRoots.RemoveAllNoClear();
+	m_prefixResolvers.removeAllElements();
+	
+	m_prefixResolvers.push(null);
+    m_currentNodes.push(DTM.NULL);
+    m_currentExpressionNodes.push(DTM.NULL);
+    m_saxLocations.push(null);
+  }
+
+  /** The current stylesheet locator. */
+  ObjectStack m_saxLocations = new ObjectStack(RECURSIONLIMIT);
+
+  /**
+   * Set the current locater in the stylesheet.
+   *
+   * @param location The location within the stylesheet.
+   */
+  public void setSAXLocator(SourceLocator location)
+  {
+    m_saxLocations.setTop(location);
+  }
+  
+  /**
+   * Set the current locater in the stylesheet.
+   *
+   * @param location The location within the stylesheet.
+   */
+  public void pushSAXLocator(SourceLocator location)
+  {
+    m_saxLocations.push(location);
+  }
+  
+  /**
+   * Push a slot on the locations stack so that setSAXLocator can be 
+   * repeatedly called.
+   *
+   */
+  public void pushSAXLocatorNull()
+  {
+    m_saxLocations.push(null);
+  }
+
+
+  /**
+   * Pop the current locater.
+   */
+  public void popSAXLocator()
+  {
+    m_saxLocations.pop();
+  }
+
+  /**
+   * Get the current locater in the stylesheet.
+   *
+   * @return The location within the stylesheet, or null if not known.
+   */
+  public SourceLocator getSAXLocator()
+  {
+    return (SourceLocator) m_saxLocations.peek();
+  }
+
+  /** The owner context of this XPathContext.  In the case of XSLT, this will be a
+   *  Transformer object.
+   */
+  private Object m_owner;
+
+  /** The owner context of this XPathContext.  In the case of XSLT, this will be a
+   *  Transformer object.
+   */
+  private Method m_ownerGetErrorListener;
+
+  /**
+   * Get the "owner" context of this context, which should be,
+   * in the case of XSLT, the Transformer object.  This is needed
+   * so that XSLT functions can get the Transformer.
+   * @return The owner object passed into the constructor, or null.
+   */
+  public Object getOwnerObject()
+  {
+    return m_owner;
+  }
+
+  // ================ VarStack ===================
+
+  /**
+   * The stack of Variable stacks.  A VariableStack will be
+   * pushed onto this stack for each template invocation.
+   */
+  private VariableStack m_variableStacks;
+
+  /**
+   * Get the variable stack, which is in charge of variables and
+   * parameters.
+   *
+   * @return the variable stack, which should not be null.
+   */
+  public final VariableStack getVarStack()
+  {
+    return m_variableStacks;
+  }
+
+  /** 
+   * Get the variable stack, which is in charge of variables and
+   * parameters.
+   *
+   * @param varStack non-null reference to the variable stack.
+   */
+  public final void setVarStack(VariableStack varStack)
+  {
+    m_variableStacks = varStack;
+  }
+
+  // ================ SourceTreeManager ===================
+
+  /** The source tree manager, which associates Source objects to source 
+   *  tree nodes. */
+  private SourceTreeManager m_sourceTreeManager = new SourceTreeManager();
+
+  /**
+   * Get the SourceTreeManager associated with this execution context.
+   *
+   * @return the SourceTreeManager associated with this execution context.
+   */
+  public final SourceTreeManager getSourceTreeManager()
+  {
+    return m_sourceTreeManager;
+  }
+
+  /**
+   * Set the SourceTreeManager associated with this execution context.
+   *
+   * @param mgr the SourceTreeManager to be associated with this 
+   *        execution context.
+   */
+  public void setSourceTreeManager(SourceTreeManager mgr)
+  {
+    m_sourceTreeManager = mgr;
+  }
+  
+  // =================================================
+
+  /** The ErrorListener where errors and warnings are to be reported.   */
+  private ErrorListener m_errorListener;
+
+  /** A default ErrorListener in case our m_errorListener was not specified and our
+   *  owner either does not have an ErrorListener or has a null one.
+   */
+  private ErrorListener m_defaultErrorListener;
+
+  /**
+   * Get the ErrorListener where errors and warnings are to be reported.
+   *
+   * @return A non-null ErrorListener reference.
+   */
+  public final ErrorListener getErrorListener()
+  {
+
+    if (null != m_errorListener)
+        return m_errorListener;
+
+    ErrorListener retval = null;
+
+    try {
+      if (null != m_ownerGetErrorListener)
+        retval = (ErrorListener) m_ownerGetErrorListener.invoke(m_owner, new Object[] {});
+    }
+    catch (Exception e) {}
+
+    if (null == retval)
+    {
+      if (null == m_defaultErrorListener) 
+        m_defaultErrorListener = new org.apache.xml.utils.DefaultErrorHandler();
+      retval = m_defaultErrorListener;
+    }
+
+    return retval;
+  }
+
+  /**
+   * Set the ErrorListener where errors and warnings are to be reported.
+   *
+   * @param listener A non-null ErrorListener reference.
+   */
+  public void setErrorListener(ErrorListener listener) throws IllegalArgumentException
+  {
+    if (listener == null) 
+      throw new IllegalArgumentException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler");
+    m_errorListener = listener;
+  }
+
+
+  // =================================================
+
+  /** The TrAX URI Resolver for resolving URIs from the document(...)
+   *  function to source tree nodes.  */
+  private URIResolver m_uriResolver;
+
+  /**
+   * Get the URIResolver associated with this execution context.
+   *
+   * @return a URI resolver, which may be null.
+   */
+  public final URIResolver getURIResolver()
+  {
+    return m_uriResolver;
+  }
+
+  /**
+   * Set the URIResolver associated with this execution context.
+   *
+   * @param resolver the URIResolver to be associated with this 
+   *        execution context, may be null to clear an already set resolver.
+   */
+  public void setURIResolver(URIResolver resolver)
+  {
+    m_uriResolver = resolver;
+  }
+
+  // =================================================
+
+  /** The reader of the primary source tree.    */
+  public XMLReader m_primaryReader;
+
+  /**
+   * Get primary XMLReader associated with this execution context.
+   *
+   * @return The reader of the primary source tree.
+   */
+  public final XMLReader getPrimaryReader()
+  {
+    return m_primaryReader;
+  }
+
+  /**
+   * Set primary XMLReader associated with this execution context.
+   *
+   * @param reader The reader of the primary source tree.
+   */
+  public void setPrimaryReader(XMLReader reader)
+  {
+    m_primaryReader = reader;
+  }
+
+  // =================================================
+
+
+  /** Misnamed string manager for XPath messages.  */
+  // private static XSLMessages m_XSLMessages = new XSLMessages();
+
+  /**
+   * Tell the user of an assertion error, and probably throw an
+   * exception.
+   *
+   * @param b  If false, a TransformerException will be thrown.
+   * @param msg The assertion message, which should be informative.
+   * 
+   * @throws javax.xml.transform.TransformerException if b is false.
+   */
+  private void assertion(boolean b, String msg) throws javax.xml.transform.TransformerException
+  {
+    if (!b) 
+    {
+      ErrorListener errorHandler = getErrorListener();
+
+      if (errorHandler != null)
+      {
+        errorHandler.fatalError(
+          new TransformerException(
+            XSLMessages.createMessage(
+              XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
+              new Object[]{ msg }), (SAXSourceLocator)this.getSAXLocator()));
+      }
+    }
+  }
+
+  //==========================================================
+  // SECTION: Execution context state tracking
+  //==========================================================
+  
+  /**
+   * The current context node list.
+   */
+  private Stack m_contextNodeLists = new Stack();
+  
+  public Stack getContextNodeListsStack() { return m_contextNodeLists; }
+  public void setContextNodeListsStack(Stack s) { m_contextNodeLists = s; }
+
+  /**
+   * Get the current context node list.
+   *
+   * @return  the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>,
+   * also refered to here as a <term>context node list</term>.
+   */
+  public final DTMIterator getContextNodeList()
+  {
+
+    if (m_contextNodeLists.size() > 0)
+      return (DTMIterator) m_contextNodeLists.peek();
+    else
+      return null;
+  }
+
+  /**
+   * Set the current context node list.
+   *
+   * @param nl the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>,
+   * also refered to here as a <term>context node list</term>.
+   * @xsl.usage internal
+   */
+  public final void pushContextNodeList(DTMIterator nl)
+  {
+    m_contextNodeLists.push(nl);
+  }
+
+  /**
+   * Pop the current context node list.
+   * @xsl.usage internal
+   */
+  public final void popContextNodeList()
+  {
+  	if(m_contextNodeLists.isEmpty())
+  	  System.err.println("Warning: popContextNodeList when stack is empty!");
+  	else
+      m_contextNodeLists.pop();
+  }
+
+  /**
+   * The ammount to use for stacks that record information during the 
+   * recursive execution.
+   */
+  public static final int RECURSIONLIMIT = (1024*4);
+
+  /** The stack of <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a> objects.
+   *  Not to be confused with the current node list.  %REVIEW% Note that there 
+   *  are no bounds check and resize for this stack, so if it is blown, it's all 
+   *  over.  */
+  private IntStack m_currentNodes = new IntStack(RECURSIONLIMIT);
+   
+//  private NodeVector m_currentNodes = new NodeVector();
+  
+  public IntStack getCurrentNodeStack() {return m_currentNodes; }
+  public void setCurrentNodeStack(IntStack nv) { m_currentNodes = nv; }
+
+  /**
+   * Get the current context node.
+   *
+   * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
+   */
+  public final int getCurrentNode()
+  {
+    return m_currentNodes.peek();
+  }
+  
+  /**
+   * Set the current context node and expression node.
+   *
+   * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
+   * @param en the sub-expression context node.
+   */
+  public final void pushCurrentNodeAndExpression(int cn, int en)
+  {
+    m_currentNodes.push(cn);
+    m_currentExpressionNodes.push(cn);
+  }
+
+  /**
+   * Set the current context node.
+   */
+  public final void popCurrentNodeAndExpression()
+  {
+    m_currentNodes.quickPop(1);
+    m_currentExpressionNodes.quickPop(1);
+  }
+  
+  /**
+   * Push the current context node, expression node, and prefix resolver.
+   *
+   * @param cn the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
+   * @param en the sub-expression context node.
+   * @param nc the namespace context (prefix resolver.
+   */
+  public final void pushExpressionState(int cn, int en, PrefixResolver nc)
+  {
+    m_currentNodes.push(cn);
+    m_currentExpressionNodes.push(cn);
+    m_prefixResolvers.push(nc);
+  }
+  
+  /**
+   * Pop the current context node, expression node, and prefix resolver.
+   */
+  public final void popExpressionState()
+  {
+    m_currentNodes.quickPop(1);
+    m_currentExpressionNodes.quickPop(1);
+    m_prefixResolvers.pop();
+  }
+
+
+
+  /**
+   * Set the current context node.
+   *
+   * @param n the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
+   */
+  public final void pushCurrentNode(int n)
+  {
+    m_currentNodes.push(n);
+  }
+  
+  /**
+   * Pop the current context node.
+   */
+  public final void popCurrentNode()
+  {
+    m_currentNodes.quickPop(1);
+  }
+  
+  /**
+   * Set the current predicate root.
+   */
+  public final void pushPredicateRoot(int n)
+  {
+    m_predicateRoots.push(n);
+  }
+
+  /**
+   * Pop the current predicate root.
+   */
+  public final void popPredicateRoot()
+  {
+    m_predicateRoots.popQuick();
+  }
+
+  /**
+   * Get the current predicate root.
+   */
+  public final int getPredicateRoot()
+  {
+    return m_predicateRoots.peepOrNull();
+  }
+  
+  /**
+   * Set the current location path iterator root.
+   */
+  public final void pushIteratorRoot(int n)
+  {
+    m_iteratorRoots.push(n);
+  }
+
+  /**
+   * Pop the current location path iterator root.
+   */
+  public final void popIteratorRoot()
+  {
+    m_iteratorRoots.popQuick();
+  }
+
+  /**
+   * Get the current location path iterator root.
+   */
+  public final int getIteratorRoot()
+  {
+    return m_iteratorRoots.peepOrNull();
+  }
+  
+  /** A stack of the current sub-expression nodes.  */
+  private NodeVector m_iteratorRoots = new NodeVector();
+
+  /** A stack of the current sub-expression nodes.  */
+  private NodeVector m_predicateRoots = new NodeVector();
+
+  /** A stack of the current sub-expression nodes.  */
+  private IntStack m_currentExpressionNodes = new IntStack(RECURSIONLIMIT);
+  
+     
+  public IntStack getCurrentExpressionNodeStack() { return m_currentExpressionNodes; }
+  public void setCurrentExpressionNodeStack(IntStack nv) { m_currentExpressionNodes = nv; }
+  
+  private IntStack m_predicatePos = new IntStack();
+  
+  public final int getPredicatePos()
+  {
+    return m_predicatePos.peek();
+  }
+
+  public final void pushPredicatePos(int n)
+  {
+    m_predicatePos.push(n);
+  }
+
+  public final void popPredicatePos()
+  {
+    m_predicatePos.pop();
+  }
+
+  /**
+   * Get the current node that is the expression's context (i.e. for current() support).
+   *
+   * @return The current sub-expression node.
+   */
+  public final int getCurrentExpressionNode()
+  {
+    return m_currentExpressionNodes.peek();
+  }
+
+  /**
+   * Set the current node that is the expression's context (i.e. for current() support).
+   *
+   * @param n The sub-expression node to be current.
+   */
+  public final void pushCurrentExpressionNode(int n)
+  {
+    m_currentExpressionNodes.push(n);
+  }
+
+  /**
+   * Pop the current node that is the expression's context 
+   * (i.e. for current() support).
+   */
+  public final void popCurrentExpressionNode()
+  {
+    m_currentExpressionNodes.quickPop(1);
+  }
+  
+  private ObjectStack m_prefixResolvers 
+                                   = new ObjectStack(RECURSIONLIMIT);
+
+  /**
+   * Get the current namespace context for the xpath.
+   *
+   * @return the current prefix resolver for resolving prefixes to 
+   *         namespace URLs.
+   */
+  public final PrefixResolver getNamespaceContext()
+  {
+    return (PrefixResolver) m_prefixResolvers.peek();
+  }
+
+  /**
+   * Get the current namespace context for the xpath.
+   *
+   * @param pr the prefix resolver to be used for resolving prefixes to 
+   *         namespace URLs.
+   */
+  public final void setNamespaceContext(PrefixResolver pr)
+  {
+    m_prefixResolvers.setTop(pr);
+  }
+
+  /**
+   * Push a current namespace context for the xpath.
+   *
+   * @param pr the prefix resolver to be used for resolving prefixes to 
+   *         namespace URLs.
+   */
+  public final void pushNamespaceContext(PrefixResolver pr)
+  {
+    m_prefixResolvers.push(pr);
+  }
+  
+  /**
+   * Just increment the namespace contest stack, so that setNamespaceContext
+   * can be used on the slot.
+   */
+  public final void pushNamespaceContextNull()
+  {
+    m_prefixResolvers.push(null);
+  }
+
+  /**
+   * Pop the current namespace context for the xpath.
+   */
+  public final void popNamespaceContext()
+  {
+    m_prefixResolvers.pop();
+  }
+
+  //==========================================================
+  // SECTION: Current TreeWalker contexts (for internal use)
+  //==========================================================
+
+  /**
+   * Stack of AxesIterators.
+   */
+  private Stack m_axesIteratorStack = new Stack();
+  
+  public Stack getAxesIteratorStackStacks() { return m_axesIteratorStack; }
+  public void setAxesIteratorStackStacks(Stack s) { m_axesIteratorStack = s; }
+
+  /**
+   * Push a TreeWalker on the stack.
+   *
+   * @param iter A sub-context AxesWalker.
+   * @xsl.usage internal
+   */
+  public final void pushSubContextList(SubContextList iter)
+  {
+    m_axesIteratorStack.push(iter);
+  }
+
+  /**
+   * Pop the last pushed axes iterator.
+   * @xsl.usage internal
+   */
+  public final void popSubContextList()
+  {
+    m_axesIteratorStack.pop();
+  }
+
+  /**
+   * Get the current axes iterator, or return null if none.
+   *
+   * @return the sub-context node list.
+   * @xsl.usage internal
+   */
+  public SubContextList getSubContextList()
+  {
+    return m_axesIteratorStack.isEmpty()
+           ? null : (SubContextList) m_axesIteratorStack.peek();
+  }
+  
+  /**
+   * Get the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a> 
+   * as defined by the XSLT spec.
+   *
+   * @return the <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>.
+   * @xsl.usage internal
+   */
+
+  public org.apache.xpath.axes.SubContextList getCurrentNodeList()
+  {
+    return m_axesIteratorStack.isEmpty()
+           ? null : (SubContextList) m_axesIteratorStack.elementAt(0);
+  }
+  //==========================================================
+  // SECTION: Implementation of ExpressionContext interface
+  //==========================================================
+
+  /**
+   * Get the current context node.
+   * @return The current context node.
+   */
+  public final int getContextNode()
+  {
+    return this.getCurrentNode();
+  }
+
+  /**
+   * Get the current context node list.
+   * @return An iterator for the current context list, as
+   * defined in XSLT.
+   */
+  public final DTMIterator getContextNodes()
+  {
+
+    try
+    {
+      DTMIterator cnl = getContextNodeList();
+
+      if (null != cnl)
+        return cnl.cloneWithReset();
+      else
+        return null;  // for now... this might ought to be an empty iterator.
+    }
+    catch (CloneNotSupportedException cnse)
+    {
+      return null;  // error reporting?
+    }
+  }
+  
+  XPathExpressionContext expressionContext = new XPathExpressionContext();
+  
+  /**
+   * The the expression context for extensions for this context.
+   * 
+   * @return An object that implements the ExpressionContext.
+   */
+  public ExpressionContext getExpressionContext()
+  {
+    return expressionContext;
+  }
+  
+  public class XPathExpressionContext implements ExpressionContext
+  {
+    /**
+     * Return the XPathContext associated with this XPathExpressionContext.
+     * Extensions should use this judiciously and only when special processing
+     * requirements cannot be met another way.  Consider requesting an enhancement
+     * to the ExpressionContext interface to avoid having to call this method.
+     * @return the XPathContext associated with this XPathExpressionContext.
+     */
+     public XPathContext getXPathContext()
+     {
+       return XPathContext.this;
+     }
+
+    /**
+     * Return the DTMManager object.  Though XPathContext context extends 
+     * the DTMManager, it really is a proxy for the real DTMManager.  If a 
+     * caller needs to make a lot of calls to the DTMManager, it is faster 
+     * if it gets the real one from this function.
+     */
+     public DTMManager getDTMManager()
+     {
+       return m_dtmManager;
+     }
+    
+    /**
+     * Get the current context node.
+     * @return The current context node.
+     */
+    public org.w3c.dom.Node getContextNode()
+    {
+      int context = getCurrentNode();
+      
+      return getDTM(context).getNode(context);
+    }
+  
+    /**
+     * Get the current context node list.
+     * @return An iterator for the current context list, as
+     * defined in XSLT.
+     */
+    public org.w3c.dom.traversal.NodeIterator getContextNodes()
+    {
+      return new org.apache.xml.dtm.ref.DTMNodeIterator(getContextNodeList());
+    }
+
+    /**
+     * Get the error listener.
+     * @return The registered error listener.
+     */
+    public ErrorListener getErrorListener()
+    {
+      return XPathContext.this.getErrorListener();
+    }
+  
+    /**
+     * Get the value of a node as a number.
+     * @param n Node to be converted to a number.  May be null.
+     * @return value of n as a number.
+     */
+    public double toNumber(org.w3c.dom.Node n)
+    {
+      // %REVIEW% You can't get much uglier than this...
+      int nodeHandle = getDTMHandleFromNode(n);
+      DTM dtm = getDTM(nodeHandle);
+      XString xobj = (XString)dtm.getStringValue(nodeHandle);
+      return xobj.num();
+    }
+  
+    /**
+     * Get the value of a node as a string.
+     * @param n Node to be converted to a string.  May be null.
+     * @return value of n as a string, or an empty string if n is null.
+     */
+    public String toString(org.w3c.dom.Node n)
+    {
+      // %REVIEW% You can't get much uglier than this...
+      int nodeHandle = getDTMHandleFromNode(n);
+      DTM dtm = getDTM(nodeHandle);
+      XMLString strVal = dtm.getStringValue(nodeHandle);
+      return strVal.toString();
+    }
+
+    /**
+     * Get a variable based on it's qualified name.
+     * @param qname The qualified name of the variable.
+     * @return The evaluated value of the variable.
+     * @throws javax.xml.transform.TransformerException
+     */
+
+    public final XObject getVariableOrParam(org.apache.xml.utils.QName qname)
+              throws javax.xml.transform.TransformerException
+    {
+      return m_variableStacks.getVariableOrParam(XPathContext.this, qname);
+    }
+
+  }
+
+ /**
+   * Get a DTM to be used as a container for a global Result Tree
+   * Fragment. This will always be an instance of (derived from? equivalent to?) 
+   * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX 
+   * output to it. It may be a single DTM containing for multiple fragments, 
+   * if the implementation supports that.
+   * 
+   * Note: The distinction between this method and getRTFDTM() is that the latter
+   * allocates space from the dynamic variable stack (m_rtfdtm_stack), which may
+   * be pruned away again as the templates which defined those variables are exited.
+   * Global variables may be bound late (see XUnresolvedVariable), and never want to
+   * be discarded, hence we need to allocate them separately and don't actually need
+   * a stack to track them.
+   * 
+   * @return a non-null DTM reference.
+   */
+  public DTM getGlobalRTFDTM()
+  {
+  	// We probably should _NOT_ be applying whitespace filtering at this stage!
+  	//
+  	// Some magic has been applied in DTMManagerDefault to recognize this set of options
+  	// and generate an instance of DTM which can contain multiple documents
+  	// (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but
+  	// I didn't want to change the manager API at this time, or expose 
+  	// too many dependencies on its internals. (Ideally, I'd like to move
+  	// isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly
+  	// specify the subclass here.)
+
+	// If it doesn't exist, or if the one already existing is in the middle of
+	// being constructed, we need to obtain a new DTM to write into. I'm not sure
+	// the latter will ever arise, but I'd rather be just a bit paranoid..
+	if( m_global_rtfdtm==null || m_global_rtfdtm.isTreeIncomplete() )
+	{
+  		m_global_rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false);
+	}
+    return m_global_rtfdtm;
+  }
+  
+
+
+
+  /**
+   * Get a DTM to be used as a container for a dynamic Result Tree
+   * Fragment. This will always be an instance of (derived from? equivalent to?) 
+   * SAX2DTM, since each RTF is constructed by temporarily redirecting our SAX 
+   * output to it. It may be a single DTM containing for multiple fragments, 
+   * if the implementation supports that.
+   * 
+   * @return a non-null DTM reference.
+   */
+  public DTM getRTFDTM()
+  {
+  	SAX2RTFDTM rtfdtm;
+
+  	// We probably should _NOT_ be applying whitespace filtering at this stage!
+  	//
+  	// Some magic has been applied in DTMManagerDefault to recognize this set of options
+  	// and generate an instance of DTM which can contain multiple documents
+  	// (SAX2RTFDTM). Perhaps not the optimal way of achieving that result, but
+  	// I didn't want to change the manager API at this time, or expose 
+  	// too many dependencies on its internals. (Ideally, I'd like to move
+  	// isTreeIncomplete all the way up to DTM, so we wouldn't need to explicitly
+  	// specify the subclass here.)
+
+	if(m_rtfdtm_stack==null)
+	{
+		m_rtfdtm_stack=new Vector();
+  		rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false);
+    m_rtfdtm_stack.addElement(rtfdtm);
+		++m_which_rtfdtm;
+	}
+	else if(m_which_rtfdtm<0)
+	{
+		rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(++m_which_rtfdtm);
+	}
+	else
+	{
+		rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(m_which_rtfdtm);
+  		
+	  	// It might already be under construction -- the classic example would be
+ 	 	// an xsl:variable which uses xsl:call-template as part of its value. To
+  		// handle this recursion, we have to start a new RTF DTM, pushing the old
+  		// one onto a stack so we can return to it. This is not as uncommon a case
+  		// as we might wish, unfortunately, as some folks insist on coding XSLT
+  		// as if it were a procedural language...
+  		if(rtfdtm.isTreeIncomplete())
+	  	{
+	  		if(++m_which_rtfdtm < m_rtfdtm_stack.size())
+				rtfdtm=(SAX2RTFDTM)m_rtfdtm_stack.elementAt(m_which_rtfdtm);
+	  		else
+	  		{
+		  		rtfdtm=(SAX2RTFDTM)m_dtmManager.getDTM(null,true,null,false,false);
+          m_rtfdtm_stack.addElement(rtfdtm); 	
+	  		}
+ 	 	}
+	}
+		
+    return rtfdtm;
+  }
+  
+  /** Push the RTFDTM's context mark, to allows discarding RTFs added after this
+   * point. (If it doesn't exist we don't push, since we might still be able to 
+   * get away with not creating it. That requires that excessive pops be harmless.)
+   * */
+  public void pushRTFContext()
+  {
+  	m_last_pushed_rtfdtm.push(m_which_rtfdtm);
+  	if(null!=m_rtfdtm_stack)
+	  	((SAX2RTFDTM)(getRTFDTM())).pushRewindMark();
+  }
+  
+  /** Pop the RTFDTM's context mark. This discards any RTFs added after the last
+   * mark was set. 
+   * 
+   * If there is no RTF DTM, there's nothing to pop so this
+   * becomes a no-op. If pushes were issued before this was called, we count on
+   * the fact that popRewindMark is defined such that overpopping just resets
+   * to empty.
+   * 
+   * Complicating factor: We need to handle the case of popping back to a previous
+   * RTF DTM, if one of the weird produce-an-RTF-to-build-an-RTF cases arose.
+   * Basically: If pop says this DTM is now empty, then return to the previous
+   * if one exists, in whatever state we left it in. UGLY, but hopefully the
+   * situation which forces us to consider this will arise exceedingly rarely.
+   * */
+  public void popRTFContext()
+  {
+  	int previous=m_last_pushed_rtfdtm.pop();
+  	if(null==m_rtfdtm_stack)
+  		return;
+  
+  	if(m_which_rtfdtm==previous)
+  	{
+  		if(previous>=0) // guard against none-active
+  		{
+	  		boolean isEmpty=((SAX2RTFDTM)(m_rtfdtm_stack.elementAt(previous))).popRewindMark();
+  		}
+  	}
+  	else while(m_which_rtfdtm!=previous)
+  	{
+  		// Empty each DTM before popping, so it's ready for reuse
+  		// _DON'T_ pop the previous, since it's still open (which is why we
+  		// stacked up more of these) and did not receive a mark.
+  		boolean isEmpty=((SAX2RTFDTM)(m_rtfdtm_stack.elementAt(m_which_rtfdtm))).popRewindMark();
+  		--m_which_rtfdtm; 
+  	}
+  }
+  
+  /**
+   * Gets DTMXRTreeFrag object if one has already been created.
+   * Creates new DTMXRTreeFrag object and adds to m_DTMXRTreeFrags  HashMap,
+   * otherwise.  
+   * @param dtmIdentity
+   * @return DTMXRTreeFrag
+   */
+  public DTMXRTreeFrag getDTMXRTreeFrag(int dtmIdentity){
+    if(m_DTMXRTreeFrags == null){
+      m_DTMXRTreeFrags = new HashMap();     
+    }
+    
+    if(m_DTMXRTreeFrags.containsKey(new Integer(dtmIdentity))){
+       return (DTMXRTreeFrag)m_DTMXRTreeFrags.get(new Integer(dtmIdentity));
+    }else{
+      final DTMXRTreeFrag frag = new DTMXRTreeFrag(dtmIdentity,this);
+      m_DTMXRTreeFrags.put(new Integer(dtmIdentity),frag);
+      return frag ;
+    }   
+  }
+ 
+  /**
+   * Cleans DTMXRTreeFrag objects by removing references 
+   * to DTM and XPathContext objects.   
+   */
+  private final void releaseDTMXRTreeFrags(){
+    if(m_DTMXRTreeFrags == null){
+      return;
+    }
+    final Iterator iter = (m_DTMXRTreeFrags.values()).iterator();
+    while(iter.hasNext()){
+      DTMXRTreeFrag frag = (DTMXRTreeFrag)iter.next();
+      frag.destruct();
+      iter.remove();
+    }
+    m_DTMXRTreeFrags = null;
+ }
+}
diff --git a/src/main/java/org/apache/xpath/XPathException.java b/src/main/java/org/apache/xpath/XPathException.java
new file mode 100644
index 0000000..315a5d6
--- /dev/null
+++ b/src/main/java/org/apache/xpath/XPathException.java
@@ -0,0 +1,328 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathException.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import javax.xml.transform.TransformerException;
+
+import org.w3c.dom.Node;
+
+/**
+ * This class implements an exception object that all
+ * XPath classes will throw in case of an error.  This class
+ * extends TransformerException, and may hold other exceptions. In the
+ * case of nested exceptions, printStackTrace will dump
+ * all the traces of the nested exceptions, not just the trace
+ * of this object.
+ * @xsl.usage general
+ */
+public class XPathException extends TransformerException
+{
+    static final long serialVersionUID = 4263549717619045963L;
+
+  /** The home of the expression that caused the error.
+   *  @serial  */
+  Object m_styleNode = null;
+
+  /**
+   * Get the stylesheet node from where this error originated.
+   * @return The stylesheet node from where this error originated, or null.
+   */
+  public Object getStylesheetNode()
+  {
+    return m_styleNode;
+  }
+  
+  /**
+   * Set the stylesheet node from where this error originated.
+   * @param styleNode The stylesheet node from where this error originated, or null.
+   */
+  public void setStylesheetNode(Object styleNode)
+  {
+    m_styleNode = styleNode;
+  }
+
+
+  /** A nested exception.
+   *  @serial   */
+  protected Exception m_exception;
+
+  /**
+   * Create an XPathException object that holds
+   * an error message.
+   * @param message The error message.
+   */
+  public XPathException(String message, ExpressionNode ex)
+  {
+    super(message);
+    this.setLocator(ex);
+    setStylesheetNode(getStylesheetNode(ex));
+  }
+  
+  /**
+   * Create an XPathException object that holds
+   * an error message.
+   * @param message The error message.
+   */
+  public XPathException(String message)
+  {
+    super(message);
+  }
+
+  
+  /**
+   * Get the XSLT ElemVariable that this sub-expression references.  In order for 
+   * this to work, the SourceLocator must be the owning ElemTemplateElement.
+   * @return The dereference to the ElemVariable, or null if not found.
+   */
+  public org.w3c.dom.Node getStylesheetNode(ExpressionNode ex)
+  {
+  	
+    ExpressionNode owner = getExpressionOwner(ex);
+
+    if (null != owner && owner instanceof org.w3c.dom.Node)
+    {
+		return ((org.w3c.dom.Node)owner);
+    }
+    return null;
+
+  }
+  
+  /**
+   * Get the first non-Expression parent of this node.
+   * @return null or first ancestor that is not an Expression.
+   */
+  protected ExpressionNode getExpressionOwner(ExpressionNode ex)
+  {
+  	ExpressionNode parent = ex.exprGetParent();
+  	while((null != parent) && (parent instanceof Expression))
+  		parent = parent.exprGetParent();
+  	return parent;
+  }
+
+
+
+  /**
+   * Create an XPathException object that holds
+   * an error message and the stylesheet node that
+   * the error originated from.
+   * @param message The error message.
+   * @param styleNode The stylesheet node that the error originated from.
+   */
+  public XPathException(String message, Object styleNode)
+  {
+
+    super(message);
+
+    m_styleNode = styleNode;
+  }
+
+  /**
+   * Create an XPathException object that holds
+   * an error message, the stylesheet node that
+   * the error originated from, and another exception
+   * that caused this exception.
+   * @param message The error message.
+   * @param styleNode The stylesheet node that the error originated from.
+   * @param e The exception that caused this exception.
+   */
+  public XPathException(String message, Node styleNode, Exception e)
+  {
+
+    super(message);
+
+    m_styleNode = styleNode;
+    this.m_exception = e;
+  }
+
+  /**
+   * Create an XPathException object that holds
+   * an error message, and another exception
+   * that caused this exception.
+   * @param message The error message.
+   * @param e The exception that caused this exception.
+   */
+  public XPathException(String message, Exception e)
+  {
+
+    super(message);
+
+    this.m_exception = e;
+  }
+
+  /**
+   * Print the the trace of methods from where the error
+   * originated.  This will trace all nested exception
+   * objects, as well as this object.
+   * @param s The stream where the dump will be sent to.
+   */
+  public void printStackTrace(java.io.PrintStream s)
+  {
+
+    if (s == null)
+      s = System.err;
+
+    try
+    {
+      super.printStackTrace(s);
+    }
+    catch (Exception e){}
+
+    Throwable exception = m_exception;
+
+    for (int i = 0; (i < 10) && (null != exception); i++)
+    {
+      s.println("---------");
+      exception.printStackTrace(s);
+
+      if (exception instanceof TransformerException)
+      {
+        TransformerException se = (TransformerException) exception;
+        Throwable prev = exception;
+
+        exception = se.getException();
+
+        if (prev == exception)
+          break;
+      }
+      else
+      {
+        exception = null;
+      }
+    }
+  }
+
+  /**
+   * Find the most contained message.
+   *
+   * @return The error message of the originating exception.
+   */
+  public String getMessage()
+  {
+
+    String lastMessage = super.getMessage();
+    Throwable exception = m_exception;
+
+    while (null != exception)
+    {
+      String nextMessage = exception.getMessage();
+
+      if (null != nextMessage)
+        lastMessage = nextMessage;
+
+      if (exception instanceof TransformerException)
+      {
+        TransformerException se = (TransformerException) exception;
+        Throwable prev = exception;
+
+        exception = se.getException();
+
+        if (prev == exception)
+          break;
+      }
+      else
+      {
+        exception = null;
+      }
+    }
+
+    return (null != lastMessage) ? lastMessage : "";
+  }
+
+  /**
+   * Print the the trace of methods from where the error
+   * originated.  This will trace all nested exception
+   * objects, as well as this object.
+   * @param s The writer where the dump will be sent to.
+   */
+  public void printStackTrace(java.io.PrintWriter s)
+  {
+
+    if (s == null)
+      s = new java.io.PrintWriter(System.err);
+        
+    try
+    {
+      super.printStackTrace(s);
+    }
+    catch (Exception e){}
+    
+    
+    boolean isJdk14OrHigher = false;
+    try {
+        Throwable.class.getMethod("getCause", (Class<?>) null);
+        isJdk14OrHigher = true;
+    } catch (NoSuchMethodException nsme) {
+        // do nothing
+    }
+        
+    // The printStackTrace method of the Throwable class in jdk 1.4 
+    // and higher will include the cause when printing the backtrace.
+    // The following code is only required when using jdk 1.3 or lower               
+    if (!isJdk14OrHigher) {
+    	        
+      Throwable exception = m_exception;
+
+      for (int i = 0; (i < 10) && (null != exception); i++)
+      {
+        s.println("---------");
+
+        try
+        {
+          exception.printStackTrace(s);
+        }
+        catch (Exception e)
+        {
+          s.println("Could not print stack trace...");
+        }
+
+        if (exception instanceof TransformerException)
+        {
+          TransformerException se = (TransformerException) exception;
+          Throwable prev = exception;
+
+          exception = se.getException();
+
+          if (prev == exception)
+          {
+            exception = null;
+
+            break;
+          }
+        }
+        else
+        {
+          exception = null;
+        }
+      }
+    }
+  }
+
+  /**
+   *  Return the embedded exception, if any.
+   *  Overrides javax.xml.transform.TransformerException.getException().
+   * 
+   *  @return The embedded exception, or null if there is none.
+   */
+  public Throwable getException()
+  {
+    return m_exception;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/XPathFactory.java b/src/main/java/org/apache/xpath/XPathFactory.java
new file mode 100644
index 0000000..df7df4a
--- /dev/null
+++ b/src/main/java/org/apache/xpath/XPathFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathFactory.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import javax.xml.transform.SourceLocator;
+
+import org.apache.xml.utils.PrefixResolver;
+
+/**
+ * Factory class for creating an XPath.  Implementors of XPath derivatives
+ * will need to make a derived class of this.
+ * @xsl.usage advanced
+ */
+public interface XPathFactory
+{
+
+  /**
+   * Create an XPath.
+   *
+   * @param exprString The XPath expression string.
+   * @param locator The location of the expression string, mainly for diagnostic
+   *                purposes.
+   * @param prefixResolver This will be called in order to resolve prefixes 
+   *        to namespace URIs.
+   * @param type One of {@link org.apache.xpath.XPath#SELECT} or 
+   *             {@link org.apache.xpath.XPath#MATCH}.
+   *
+   * @return an XPath ready for execution.
+   */
+  XPath create(String exprString, SourceLocator locator,
+               PrefixResolver prefixResolver, int type);
+}
diff --git a/src/main/java/org/apache/xpath/XPathProcessorException.java b/src/main/java/org/apache/xpath/XPathProcessorException.java
new file mode 100644
index 0000000..e3dcfea
--- /dev/null
+++ b/src/main/java/org/apache/xpath/XPathProcessorException.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathProcessorException.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+/**
+ * Derived from XPathException in order that XPath processor
+ * exceptions may be specifically caught.
+ * @xsl.usage general
+ */
+public class XPathProcessorException extends XPathException
+{
+    static final long serialVersionUID = 1215509418326642603L;
+
+  /**
+   * Create an XPathProcessorException object that holds
+   * an error message.
+   * @param message The error message.
+   */
+  public XPathProcessorException(String message)
+  {
+    super(message);
+  }
+  
+
+  /**
+   * Create an XPathProcessorException object that holds
+   * an error message, and another exception
+   * that caused this exception.
+   * @param message The error message.
+   * @param e The exception that caused this exception.
+   */
+  public XPathProcessorException(String message, Exception e)
+  {
+    super(message, e);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/XPathVisitable.java b/src/main/java/org/apache/xpath/XPathVisitable.java
new file mode 100644
index 0000000..271d4c2
--- /dev/null
+++ b/src/main/java/org/apache/xpath/XPathVisitable.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathVisitable.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+/**
+ * A class that implements this interface will call a XPathVisitor 
+ * for itself and members within it's heararchy.  If the XPathVisitor's 
+ * method returns false, the sub-member heararchy will not be 
+ * traversed.
+ */
+public interface XPathVisitable
+{
+	/**
+	 * This will traverse the heararchy, calling the visitor for 
+	 * each member.  If the called visitor method returns 
+	 * false, the subtree should not be called.
+	 * 
+	 * @param owner The owner of the visitor, where that path may be 
+	 *              rewritten if needed.
+	 * @param visitor The visitor whose appropriate method will be called.
+	 */
+	public void callVisitors(ExpressionOwner owner, XPathVisitor visitor);
+}
+
diff --git a/src/main/java/org/apache/xpath/XPathVisitor.java b/src/main/java/org/apache/xpath/XPathVisitor.java
new file mode 100644
index 0000000..b5375b4
--- /dev/null
+++ b/src/main/java/org/apache/xpath/XPathVisitor.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathVisitor.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath;
+
+import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.UnionPathIterator;
+import org.apache.xpath.functions.Function;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XString;
+import org.apache.xpath.operations.Operation;
+import org.apache.xpath.operations.UnaryOperation;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.patterns.NodeTest;
+import org.apache.xpath.patterns.StepPattern;
+import org.apache.xpath.patterns.UnionPattern;
+
+/**
+ * A derivation from this class can be passed to a class that implements 
+ * the XPathVisitable interface, to have the appropriate method called 
+ * for each component of the XPath.  Aside from possible other uses, the 
+ * main intention is to provide a reasonable means to perform expression 
+ * rewriting.
+ * 
+ * <p>Each method has the form 
+ * <code>boolean visitComponentType(ExpressionOwner owner, ComponentType compType)</code>. 
+ * The ExpressionOwner argument is the owner of the component, and can 
+ * be used to reset the expression for rewriting.  If a method returns 
+ * false, the sub hierarchy will not be traversed.</p>
+ * 
+ * <p>This class is meant to be a base class that will be derived by concrete classes, 
+ * and doesn't much except return true for each method.</p>
+ */
+public class XPathVisitor
+{
+	/**
+	 * Visit a LocationPath.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param path The LocationPath object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitLocationPath(ExpressionOwner owner, LocPathIterator path)
+	{
+		return true;
+	}
+
+	/**
+	 * Visit a UnionPath.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param path The UnionPath object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitUnionPath(ExpressionOwner owner, UnionPathIterator path)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit a step within a location path.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param step The Step object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitStep(ExpressionOwner owner, NodeTest step)
+	{
+		return true;
+	}
+		
+	/**
+	 * Visit a predicate within a location path.  Note that there isn't a 
+	 * proper unique component for predicates, and that the expression will 
+	 * be called also for whatever type Expression is.
+	 * 
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param pred The predicate object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitPredicate(ExpressionOwner owner, Expression pred)
+	{
+		return true;
+	}
+
+	/**
+	 * Visit a binary operation.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param op The operation object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitBinaryOperation(ExpressionOwner owner, Operation op)
+	{
+		return true;
+	}
+
+	/**
+	 * Visit a unary operation.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param op The operation object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitUnaryOperation(ExpressionOwner owner, UnaryOperation op)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit a variable reference.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param var The variable reference object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitVariableRef(ExpressionOwner owner, Variable var)
+	{
+		return true;
+	}
+
+	/**
+	 * Visit a function.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param func The function reference object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitFunction(ExpressionOwner owner, Function func)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit a match pattern.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param pattern The match pattern object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitMatchPattern(ExpressionOwner owner, StepPattern pattern)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit a union pattern.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param pattern The union pattern object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitUnionPattern(ExpressionOwner owner, UnionPattern pattern)
+	{
+		return true;
+	}
+	
+	/**
+	 * Visit a string literal.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param str The string literal object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitStringLiteral(ExpressionOwner owner, XString str)
+	{
+		return true;
+	}
+
+
+	/**
+	 * Visit a number literal.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param num The number literal object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitNumberLiteral(ExpressionOwner owner, XNumber num)
+	{
+		return true;
+	}
+
+
+}
+
diff --git a/src/main/java/org/apache/xpath/axes/AttributeIterator.java b/src/main/java/org/apache/xpath/axes/AttributeIterator.java
new file mode 100644
index 0000000..b3cc1cc
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/AttributeIterator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AttributeIterator.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.compiler.Compiler;
+
+/**
+ * This class implements an optimized iterator for
+ * attribute axes patterns.
+ * @see org.apache.xpath.axes#ChildTestIterator
+ * @xsl.usage advanced
+ */
+public class AttributeIterator extends ChildTestIterator
+{
+    static final long serialVersionUID = -8417986700712229686L;
+
+  /**
+   * Create a AttributeIterator object.
+   *
+   * @param compiler A reference to the Compiler that contains the op map.
+   * @param opPos The position within the op map, which contains the
+   * location path expression for this itterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  AttributeIterator(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis);
+  }
+    
+  /**
+   * Get the next node via getFirstAttribute && getNextAttribute.
+   */
+  protected int getNextNode()
+  {
+    m_lastFetched = (DTM.NULL == m_lastFetched)
+                     ? m_cdtm.getFirstAttribute(m_context)
+                     : m_cdtm.getNextAttribute(m_lastFetched);
+    return m_lastFetched;
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return org.apache.xml.dtm.Axis.ATTRIBUTE;
+  }
+
+
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/AxesWalker.java b/src/main/java/org/apache/xpath/axes/AxesWalker.java
new file mode 100644
index 0000000..524d004
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/AxesWalker.java
@@ -0,0 +1,590 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: AxesWalker.java 513117 2007-03-01 03:28:52Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import java.util.Vector;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisTraverser;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Serves as common interface for axes Walkers, and stores common
+ * state variables.
+ */
+public class AxesWalker extends PredicatedNodeTest
+        implements Cloneable, PathComponent, ExpressionOwner
+{
+    static final long serialVersionUID = -2966031951306601247L;
+  
+  /**
+   * Construct an AxesWalker using a LocPathIterator.
+   *
+   * @param locPathIterator non-null reference to the parent iterator.
+   */
+  public AxesWalker(LocPathIterator locPathIterator, int axis)
+  {
+    super( locPathIterator );
+    m_axis = axis;
+  }
+  
+  public final WalkingIterator wi()
+  {
+    return (WalkingIterator)m_lpi;
+  }
+
+  /**
+   * Initialize an AxesWalker during the parse of the XPath expression.
+   *
+   * @param compiler The Compiler object that has information about this 
+   *                 walker in the op map.
+   * @param opPos The op code position of this location step.
+   * @param stepType  The type of location step.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void init(Compiler compiler, int opPos, int stepType)
+          throws javax.xml.transform.TransformerException
+  {
+
+    initPredicateInfo(compiler, opPos);
+
+    // int testType = compiler.getOp(nodeTestOpPos);
+  }
+
+  /**
+   * Get a cloned AxesWalker.
+   *
+   * @return A new AxesWalker that can be used without mutating this one.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+    // Do not access the location path itterator during this operation!
+    
+    AxesWalker clone = (AxesWalker) super.clone();
+
+    //clone.setCurrentNode(clone.m_root);
+
+    // clone.m_isFresh = true;
+
+    return clone;
+  }
+  
+  /**
+   * Do a deep clone of this walker, including next and previous walkers.
+   * If the this AxesWalker is on the clone list, don't clone but 
+   * return the already cloned version.
+   * 
+   * @param cloneOwner non-null reference to the cloned location path 
+   *                   iterator to which this clone will be added.
+   * @param cloneList non-null vector of sources in odd elements, and the 
+   *                  corresponding clones in even vectors.
+   * 
+   * @return non-null clone, which may be a new clone, or may be a clone 
+   *         contained on the cloneList.
+   */
+  AxesWalker cloneDeep(WalkingIterator cloneOwner, Vector cloneList)
+     throws CloneNotSupportedException
+  {
+    AxesWalker clone = findClone(this, cloneList);
+    if(null != clone)
+      return clone;
+    clone = (AxesWalker)this.clone();
+    clone.setLocPathIterator(cloneOwner);
+    if(null != cloneList)
+    {
+      cloneList.addElement(this);
+      cloneList.addElement(clone);
+    }
+    
+    if(wi().m_lastUsedWalker == this)
+      cloneOwner.m_lastUsedWalker = clone;
+      
+    if(null != m_nextWalker)
+      clone.m_nextWalker = m_nextWalker.cloneDeep(cloneOwner, cloneList);
+      
+    // If you don't check for the cloneList here, you'll go into an 
+    // recursive infinate loop.  
+    if(null != cloneList)
+    {
+      if(null != m_prevWalker)
+        clone.m_prevWalker = m_prevWalker.cloneDeep(cloneOwner, cloneList);
+    }
+    else
+    {
+      if(null != m_nextWalker)
+        clone.m_nextWalker.m_prevWalker = clone;
+    }
+    return clone;
+  }
+  
+  /**
+   * Find a clone that corresponds to the key argument.
+   * 
+   * @param key The original AxesWalker for which there may be a clone.
+   * @param cloneList vector of sources in odd elements, and the 
+   *                  corresponding clones in even vectors, may be null.
+   * 
+   * @return A clone that corresponds to the key, or null if key not found.
+   */
+  static AxesWalker findClone(AxesWalker key, Vector cloneList)
+  {
+    if(null != cloneList)
+    {
+      // First, look for clone on list.
+      int n = cloneList.size();
+      for (int i = 0; i < n; i+=2) 
+      {
+        if(key == cloneList.elementAt(i))
+          return (AxesWalker)cloneList.elementAt(i+1);
+      }
+    }
+    return null;    
+  }
+  
+  /**
+   * Detaches the walker from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state.
+   */
+  public void detach()
+  { 
+  	m_currentNode = DTM.NULL;
+  	m_dtm = null;
+  	m_traverser = null;
+  	m_isFresh = true;
+  	m_root = DTM.NULL;
+  }
+  
+  //=============== TreeWalker Implementation ===============
+
+  /**
+   * The root node of the TreeWalker, as specified in setRoot(int root).
+   * Note that this may actually be below the current node.
+   *
+   * @return The context node of the step.
+   */
+  public int getRoot()
+  {
+    return m_root;
+  }
+  
+  /** 
+   * Get the analysis bits for this walker, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits()
+  {
+  	int axis = getAxis();
+  	int bit = WalkerFactory.getAnalysisBitFromAxes(axis);
+  	return bit;
+  }
+
+  /**
+   * Set the root node of the TreeWalker.
+   * (Not part of the DOM2 TreeWalker interface).
+   *
+   * @param root The context node of this step.
+   */
+  public void setRoot(int root)
+  {
+    // %OPT% Get this directly from the lpi.
+    XPathContext xctxt = wi().getXPathContext();
+    m_dtm = xctxt.getDTM(root);
+    m_traverser = m_dtm.getAxisTraverser(m_axis);
+    m_isFresh = true;
+    m_foundLast = false;
+    m_root = root;
+    m_currentNode = root;
+
+    if (DTM.NULL == root)
+    {
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_SETTING_WALKER_ROOT_TO_NULL, null)); //"\n !!!! Error! Setting the root of a walker to null!!!");
+    }
+
+    resetProximityPositions();
+  }
+
+  /**
+   * The node at which the TreeWalker is currently positioned.
+   * <br> The value must not be null. Alterations to the DOM tree may cause
+   * the current node to no longer be accepted by the TreeWalker's
+   * associated filter. currentNode may also be explicitly set to any node,
+   * whether or not it is within the subtree specified by the root node or
+   * would be accepted by the filter and whatToShow flags. Further
+   * traversal occurs relative to currentNode even if it is not part of the
+   * current view by applying the filters in the requested direction (not
+   * changing currentNode where no traversal is possible).
+   *
+   * @return The node at which the TreeWalker is currently positioned, only null 
+   * if setRoot has not yet been called.
+   */
+  public final int getCurrentNode()
+  {
+    return m_currentNode;
+  }
+
+  /**
+   * Set the next walker in the location step chain.
+   *
+   *
+   * @param walker Reference to AxesWalker derivative, or may be null.
+   */
+  public void setNextWalker(AxesWalker walker)
+  {
+    m_nextWalker = walker;
+  }
+
+  /**
+   * Get the next walker in the location step chain.
+   *
+   *
+   * @return Reference to AxesWalker derivative, or null.
+   */
+  public AxesWalker getNextWalker()
+  {
+    return m_nextWalker;
+  }
+
+  /**
+   * Set or clear the previous walker reference in the location step chain.
+   *
+   *
+   * @param walker Reference to previous walker reference in the location 
+   *               step chain, or null.
+   */
+  public void setPrevWalker(AxesWalker walker)
+  {
+    m_prevWalker = walker;
+  }
+
+  /**
+   * Get the previous walker reference in the location step chain.
+   *
+   *
+   * @return Reference to previous walker reference in the location 
+   *               step chain, or null.
+   */
+  public AxesWalker getPrevWalker()
+  {
+    return m_prevWalker;
+  }
+
+  /**
+   * This is simply a way to bottle-neck the return of the next node, for 
+   * diagnostic purposes.
+   *
+   * @param n Node to return, or null.
+   *
+   * @return The argument.
+   */
+  private int returnNextNode(int n)
+  {
+
+    return n;
+  }
+
+  /**
+   * Get the next node in document order on the axes.
+   *
+   * @return the next node in document order on the axes, or null.
+   */
+  protected int getNextNode()
+  {
+    if (m_foundLast)
+      return DTM.NULL;
+
+    if (m_isFresh)
+    {
+      m_currentNode = m_traverser.first(m_root);
+      m_isFresh = false;
+    }
+    // I shouldn't have to do this the check for current node, I think.
+    // numbering\numbering24.xsl fails if I don't do this.  I think 
+    // it occurs as the walkers are backing up. -sb
+    else if(DTM.NULL != m_currentNode) 
+    {
+      m_currentNode = m_traverser.next(m_root, m_currentNode);
+    }
+
+    if (DTM.NULL == m_currentNode)
+      this.m_foundLast = true;
+
+    return m_currentNode;
+  }
+
+  /**
+   *  Moves the <code>TreeWalker</code> to the next visible node in document
+   * order relative to the current node, and returns the new node. If the
+   * current node has no next node,  or if the search for nextNode attempts
+   * to step upward from the TreeWalker's root node, returns
+   * <code>null</code> , and retains the current node.
+   * @return  The new node, or <code>null</code> if the current node has no
+   *   next node  in the TreeWalker's logical view.
+   */
+  public int nextNode()
+  {
+    int nextNode = DTM.NULL;
+    AxesWalker walker = wi().getLastUsedWalker();
+
+    while (true)
+    {
+      if (null == walker)
+        break;
+
+      nextNode = walker.getNextNode();
+
+      if (DTM.NULL == nextNode)
+      {
+
+        walker = walker.m_prevWalker;
+      }
+      else
+      {
+        if (walker.acceptNode(nextNode) != DTMIterator.FILTER_ACCEPT)
+        {
+          continue;
+        }
+
+        if (null == walker.m_nextWalker)
+        {
+          wi().setLastUsedWalker(walker);
+
+          // return walker.returnNextNode(nextNode);
+          break;
+        }
+        else
+        {
+          AxesWalker prev = walker;
+
+          walker = walker.m_nextWalker;
+
+          walker.setRoot(nextNode);
+
+          walker.m_prevWalker = prev;
+
+          continue;
+        }
+      }  // if(null != nextNode)
+    }  // while(null != walker)
+
+    return nextNode;
+  }
+
+  //============= End TreeWalker Implementation =============
+
+  /**
+   * Get the index of the last node that can be itterated to.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return the index of the last node that can be itterated to.
+   */
+  public int getLastPos(XPathContext xctxt)
+  {
+
+    int pos = getProximityPosition();
+    
+    AxesWalker walker;
+
+    try
+    {
+      walker = (AxesWalker) clone();
+    }
+    catch (CloneNotSupportedException cnse)
+    {
+      return -1;
+    }
+
+    walker.setPredicateCount(m_predicateIndex);
+    walker.setNextWalker(null);
+    walker.setPrevWalker(null);
+
+    WalkingIterator lpi = wi();
+    AxesWalker savedWalker = lpi.getLastUsedWalker();
+
+    try
+    {
+      lpi.setLastUsedWalker(walker);
+
+      int next;
+
+      while (DTM.NULL != (next = walker.nextNode()))
+      {
+        pos++;
+      }
+
+      // TODO: Should probably save this in the iterator.
+    }
+    finally
+    {
+      lpi.setLastUsedWalker(savedWalker);
+    }
+
+    // System.out.println("pos: "+pos);
+    return pos;
+  }
+  
+  //============= State Data =============
+  
+  /**
+   * The DTM for the root.  This can not be used, or must be changed, 
+   * for the filter walker, or any walker that can have nodes 
+   * from multiple documents.
+   * Never, ever, access this value without going through getDTM(int node).
+   */
+  private DTM m_dtm;
+  
+  /**
+   * Set the DTM for this walker.
+   * 
+   * @param dtm Non-null reference to a DTM.
+   */
+  public void setDefaultDTM(DTM dtm)
+  {
+    m_dtm = dtm;
+  }
+  
+  /**
+   * Get the DTM for this walker.
+   * 
+   * @return Non-null reference to a DTM.
+   */
+  public DTM getDTM(int node)
+  {
+    //
+    return wi().getXPathContext().getDTM(node);
+  }
+  
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * Warning: This can only be called after setRoot has been called!
+   * 
+   * @return true as a default.
+   */
+  public boolean isDocOrdered()
+  {
+    return true;
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return m_axis;
+  }
+  
+  /**
+   * This will traverse the heararchy, calling the visitor for 
+   * each member.  If the called visitor method returns 
+   * false, the subtree should not be called.
+   * 
+   * @param owner The owner of the visitor, where that path may be 
+   *              rewritten if needed.
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	if(visitor.visitStep(owner, this))
+  	{
+  		callPredicateVisitors(visitor);
+  		if(null != m_nextWalker)
+  		{
+  			m_nextWalker.callVisitors(this, visitor);
+  		}
+  	}
+  }
+  
+  /**
+   * @see ExpressionOwner#getExpression()
+   */
+  public Expression getExpression()
+  {
+    return m_nextWalker;
+  }
+
+  /**
+   * @see ExpressionOwner#setExpression(Expression)
+   */
+  public void setExpression(Expression exp)
+  {
+  	exp.exprSetParent(this);
+  	m_nextWalker = (AxesWalker)exp;
+  }
+  
+    /**
+     * @see Expression#deepEquals(Expression)
+     */
+    public boolean deepEquals(Expression expr)
+    {
+      if (!super.deepEquals(expr))
+                return false;
+
+      AxesWalker walker = (AxesWalker)expr;
+      if(this.m_axis != walker.m_axis)
+      	return false;
+
+      return true;
+    }
+
+  /**
+   *  The root node of the TreeWalker, as specified when it was created.
+   */
+  transient int m_root = DTM.NULL;
+
+  /**
+   *  The node at which the TreeWalker is currently positioned.
+   */
+  private transient int m_currentNode = DTM.NULL;
+  
+  /** True if an itteration has not begun.  */
+  transient boolean m_isFresh;
+
+  /** The next walker in the location step chain.
+   *  @serial  */
+  protected AxesWalker m_nextWalker;
+  
+  /** The previous walker in the location step chain, or null.
+   *  @serial   */
+  AxesWalker m_prevWalker;
+  
+  /** The traversal axis from where the nodes will be filtered. */
+  protected int m_axis = -1;
+
+  /** The DTM inner traversal class, that corresponds to the super axis. */
+  protected DTMAxisTraverser m_traverser; 
+}
diff --git a/src/main/java/org/apache/xpath/axes/BasicTestIterator.java b/src/main/java/org/apache/xpath/axes/BasicTestIterator.java
new file mode 100644
index 0000000..75a8135
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/BasicTestIterator.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: BasicTestIterator.java 469314 2006-10-30 23:31:59Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.OpMap;
+
+/**
+ * Base for iterators that handle predicates.  Does the basic next 
+ * node logic, so all the derived iterator has to do is get the 
+ * next node.
+ */
+public abstract class BasicTestIterator extends LocPathIterator
+{
+    static final long serialVersionUID = 3505378079378096623L;
+  /**
+   * Create a LocPathIterator object.
+   *
+   * @param nscontext The namespace context for this iterator,
+   * should be OK if null.
+   */
+  protected BasicTestIterator()
+  {
+  }
+
+
+  /**
+   * Create a LocPathIterator object.
+   *
+   * @param nscontext The namespace context for this iterator,
+   * should be OK if null.
+   */
+  protected BasicTestIterator(PrefixResolver nscontext)
+  {
+
+    super(nscontext);
+  }
+
+  /**
+   * Create a LocPathIterator object, including creation
+   * of step walkers from the opcode list, and call back
+   * into the Compiler to create predicate expressions.
+   *
+   * @param compiler The Compiler which is creating
+   * this expression.
+   * @param opPos The position of this iterator in the
+   * opcode list from the compiler.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected BasicTestIterator(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis, false);
+    
+    int firstStepPos = OpMap.getFirstChildPos(opPos);
+    int whatToShow = compiler.getWhatToShow(firstStepPos);
+
+    if ((0 == (whatToShow
+               & (DTMFilter.SHOW_ATTRIBUTE 
+               | DTMFilter.SHOW_NAMESPACE 
+               | DTMFilter.SHOW_ELEMENT
+               | DTMFilter.SHOW_PROCESSING_INSTRUCTION))) 
+               || (whatToShow == DTMFilter.SHOW_ALL))
+      initNodeTest(whatToShow);
+    else
+    {
+      initNodeTest(whatToShow, compiler.getStepNS(firstStepPos),
+                              compiler.getStepLocalName(firstStepPos));
+    }
+    initPredicateInfo(compiler, firstStepPos);
+  }
+
+  /**
+   * Create a LocPathIterator object, including creation
+   * of step walkers from the opcode list, and call back
+   * into the Compiler to create predicate expressions.
+   *
+   * @param compiler The Compiler which is creating
+   * this expression.
+   * @param opPos The position of this iterator in the
+   * opcode list from the compiler.
+   * @param shouldLoadWalkers True if walkers should be
+   * loaded, or false if this is a derived iterator and
+   * it doesn't wish to load child walkers.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected BasicTestIterator(
+          Compiler compiler, int opPos, int analysis, boolean shouldLoadWalkers)
+            throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis, shouldLoadWalkers);
+  }
+
+	
+  /**
+   * Get the next node via getNextXXX.  Bottlenecked for derived class override.
+   * @return The next node on the axis, or DTM.NULL.
+   */
+  protected abstract int getNextNode();
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   *
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   */
+  public int nextNode()
+  {      
+  	if(m_foundLast)
+  	{
+  		m_lastFetched = DTM.NULL;
+  		return DTM.NULL;
+  	}
+  		
+    if(DTM.NULL == m_lastFetched)
+    {
+      resetProximityPositions();
+    }
+
+    int next;
+    
+    org.apache.xpath.VariableStack vars;
+    int savedStart;
+    if (-1 != m_stackFrame)
+    {
+      vars = m_execContext.getVarStack();
+
+      // These three statements need to be combined into one operation.
+      savedStart = vars.getStackFrame();
+
+      vars.setStackFrame(m_stackFrame);
+    }
+    else
+    {
+      // Yuck.  Just to shut up the compiler!
+      vars = null;
+      savedStart = 0;
+    }
+    
+    try
+    {
+      do
+      {
+        next = getNextNode();
+  
+        if (DTM.NULL != next)
+        {
+          if(DTMIterator.FILTER_ACCEPT == acceptNode(next))
+            break;
+          else
+            continue;
+        }
+        else
+          break;
+      }
+      while (next != DTM.NULL);
+  
+      if (DTM.NULL != next)
+      {
+      	m_pos++;
+        return next;
+      }
+      else
+      {
+        m_foundLast = true;
+  
+        return DTM.NULL;
+      }
+    }
+    finally
+    {
+      if (-1 != m_stackFrame)
+      {
+        // These two statements need to be combined into one operation.
+        vars.setStackFrame(savedStart);
+      }
+    }
+  }
+  
+  /**
+   *  Get a cloned Iterator that is reset to the beginning
+   *  of the query.
+   * 
+   *  @return A cloned NodeIterator set of the start of the query.
+   * 
+   *  @throws CloneNotSupportedException
+   */
+  public DTMIterator cloneWithReset() throws CloneNotSupportedException
+  {
+
+    ChildTestIterator clone = (ChildTestIterator) super.cloneWithReset();
+
+    clone.resetProximityPositions();
+
+    return clone;
+  }
+
+
+}
+
diff --git a/src/main/java/org/apache/xpath/axes/ChildIterator.java b/src/main/java/org/apache/xpath/axes/ChildIterator.java
new file mode 100644
index 0000000..4ce7733
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/ChildIterator.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ChildIterator.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.compiler.Compiler;
+
+/**
+ * This class implements an optimized iterator for
+ * "node()" patterns, that is, any children of the
+ * context node.
+ * @see org.apache.xpath.axes.LocPathIterator
+ * @xsl.usage advanced
+ */
+public class ChildIterator extends LocPathIterator
+{
+    static final long serialVersionUID = -6935428015142993583L;
+
+  /**
+   * Create a ChildIterator object.
+   *
+   * @param compiler A reference to the Compiler that contains the op map.
+   * @param opPos The position within the op map, which contains the
+   * location path expression for this itterator.
+   * @param analysis Analysis bits of the entire pattern.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  ChildIterator(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis, false);
+
+    // This iterator matches all kinds of nodes
+    initNodeTest(DTMFilter.SHOW_ALL);
+  }
+  
+  /**
+   * Return the first node out of the nodeset, if this expression is 
+   * a nodeset expression.  This is the default implementation for 
+   * nodesets.
+   * <p>WARNING: Do not mutate this class from this function!</p>
+   * @param xctxt The XPath runtime context.
+   * @return the first node out of the nodeset, or DTM.NULL.
+   */
+  public int asNode(XPathContext xctxt)
+    throws javax.xml.transform.TransformerException
+  {
+    int current = xctxt.getCurrentNode();
+    
+    DTM dtm = xctxt.getDTM(current);
+    
+    return dtm.getFirstChild(current);
+  }
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   *
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   */
+  public int nextNode()
+  {
+  	if(m_foundLast)
+  		return DTM.NULL;
+
+    int next;
+
+    m_lastFetched = next = (DTM.NULL == m_lastFetched)
+                           ? m_cdtm.getFirstChild(m_context)
+                           : m_cdtm.getNextSibling(m_lastFetched);
+
+    // m_lastFetched = next;
+    if (DTM.NULL != next)
+    {
+      m_pos++;
+      return next;
+    }
+    else
+    {
+      m_foundLast = true;
+
+      return DTM.NULL;
+    }
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return org.apache.xml.dtm.Axis.CHILD;
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/ChildTestIterator.java b/src/main/java/org/apache/xpath/axes/ChildTestIterator.java
new file mode 100644
index 0000000..ee33a4e
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/ChildTestIterator.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ChildTestIterator.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisTraverser;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.compiler.Compiler;
+
+/**
+ * This class implements an optimized iterator for
+ * children patterns that have a node test, and possibly a predicate.
+ * @see org.apache.xpath.axes.BasicTestIterator
+ * @xsl.usage advanced
+ */
+public class ChildTestIterator extends BasicTestIterator
+{
+    static final long serialVersionUID = -7936835957960705722L;
+  /** The traverser to use to navigate over the descendants. */
+  transient protected DTMAxisTraverser m_traverser;
+  
+  /** The extended type ID, not set until setRoot. */
+//  protected int m_extendedTypeID;
+
+
+  /**
+   * Create a ChildTestIterator object.
+   *
+   * @param compiler A reference to the Compiler that contains the op map.
+   * @param opPos The position within the op map, which contains the
+   * location path expression for this itterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  ChildTestIterator(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis);
+  }
+  
+  /**
+   * Create a ChildTestIterator object.
+   *
+   * @param traverser Traverser that tells how the KeyIterator is to be handled.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public ChildTestIterator(DTMAxisTraverser traverser)
+  {
+
+    super(null);
+
+    m_traverser = traverser;
+  }
+
+  /**
+   * Get the next node via getNextXXX.  Bottlenecked for derived class override.
+   * @return The next node on the axis, or DTM.NULL.
+   */
+  protected int getNextNode()
+  {                     
+    if(true /* 0 == m_extendedTypeID */)
+    {
+      m_lastFetched = (DTM.NULL == m_lastFetched)
+                   ? m_traverser.first(m_context)
+                   : m_traverser.next(m_context, m_lastFetched);
+    }
+//    else
+//    {
+//      m_lastFetched = (DTM.NULL == m_lastFetched)
+//                   ? m_traverser.first(m_context, m_extendedTypeID)
+//                   : m_traverser.next(m_context, m_lastFetched, 
+//                                      m_extendedTypeID);
+//    }
+
+    return m_lastFetched;
+  }
+
+  
+  /**
+   *  Get a cloned Iterator that is reset to the beginning
+   *  of the query.
+   * 
+   *  @return A cloned NodeIterator set of the start of the query.
+   * 
+   *  @throws CloneNotSupportedException
+   */
+  public DTMIterator cloneWithReset() throws CloneNotSupportedException
+  {
+
+    ChildTestIterator clone = (ChildTestIterator) super.cloneWithReset();
+    clone.m_traverser = m_traverser;
+
+    return clone;
+  }
+  
+
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+    super.setRoot(context, environment);
+    m_traverser = m_cdtm.getAxisTraverser(Axis.CHILD);
+    
+//    String localName = getLocalName();
+//    String namespace = getNamespace();
+//    int what = m_whatToShow;
+//    // System.out.println("what: ");
+//    // NodeTest.debugWhatToShow(what);
+//    if(DTMFilter.SHOW_ALL == what ||
+//       ((DTMFilter.SHOW_ELEMENT & what) == 0)
+//       || localName == NodeTest.WILD
+//       || namespace == NodeTest.WILD)
+//    {
+//      m_extendedTypeID = 0;
+//    }
+//    else
+//    {
+//      int type = getNodeTypeTest(what);
+//      m_extendedTypeID = m_cdtm.getExpandedTypeID(namespace, localName, type);
+//    }
+    
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return org.apache.xml.dtm.Axis.CHILD;
+  }
+
+  /**
+   *  Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   */
+  public void detach()
+  {   
+    if(m_allowDetach)
+    {
+      m_traverser = null;
+      
+      // Always call the superclass detach last!
+      super.detach();
+    }
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/ContextNodeList.java b/src/main/java/org/apache/xpath/axes/ContextNodeList.java
new file mode 100644
index 0000000..f5e24ce
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/ContextNodeList.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ContextNodeList.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * Classes who implement this interface can be a
+ * <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>,
+ * also refered to here as a <term>context node list</term>.
+ * @xsl.usage advanced
+ */
+public interface ContextNodeList
+{
+
+  /**
+   * Get the <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>.
+   *
+   *
+   * @return The current node, or null.
+   */
+  public Node getCurrentNode();
+
+  /**
+   * Get the current position, which is one less than
+   * the next nextNode() call will retrieve.  i.e. if
+   * you call getCurrentPos() and the return is 0, the next
+   * fetch will take place at index 1.
+   *
+   * @return The position of the
+   * <a href="http://www.w3.org/TR/xslt#dt-current-node">current node</a>
+   * in the  <a href="http://www.w3.org/TR/xslt#dt-current-node-list">current node list</a>.
+   */
+  public int getCurrentPos();
+
+  /**
+   * Reset the iterator.
+   */
+  public void reset();
+
+  /**
+   * If setShouldCacheNodes(true) is called, then nodes will
+   * be cached.  They are not cached by default.
+   *
+   * @param b true if the nodes should be cached.
+   */
+  public void setShouldCacheNodes(boolean b);
+
+  /**
+   * If an index is requested, NodeSetDTM will call this method
+   * to run the iterator to the index.  By default this sets
+   * m_next to the index.  If the index argument is -1, this
+   * signals that the iterator should be run to the end.
+   *
+   * @param index The index to run to, or -1 if the iterator should be run
+   *              to the end.
+   */
+  public void runTo(int index);
+
+  /**
+   * Set the current position in the node set.
+   * @param i Must be a valid index.
+   */
+  public void setCurrentPos(int i);
+
+  /**
+   * Get the length of the list.
+   *
+   * @return The number of nodes in this node list.
+   */
+  public int size();
+
+  /**
+   * Tells if this NodeSetDTM is "fresh", in other words, if
+   * the first nextNode() that is called will return the
+   * first node in the set.
+   *
+   * @return true if the iteration of this list has not yet begun.
+   */
+  public boolean isFresh();
+
+  /**
+   * Get a cloned Iterator that is reset to the start of the iteration.
+   *
+   * @return A clone of this iteration that has been reset.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public NodeIterator cloneWithReset() throws CloneNotSupportedException;
+
+  /**
+   * Get a clone of this iterator.  Be aware that this operation may be
+   * somewhat expensive.
+   *
+   *
+   * @return A clone of this object.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException;
+
+  /**
+   * Get the index of the last node in this list.
+   *
+   *
+   * @return the index of the last node in this list.
+   */
+  public int getLast();
+
+  /**
+   * Set the index of the last node in this list.
+   *
+   *
+   * @param last the index of the last node in this list.
+   */
+  public void setLast(int last);
+}
diff --git a/src/main/java/org/apache/xpath/axes/DescendantIterator.java b/src/main/java/org/apache/xpath/axes/DescendantIterator.java
new file mode 100644
index 0000000..5706125
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/DescendantIterator.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DescendantIterator.java 469314 2006-10-30 23:31:59Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisTraverser;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.OpCodes;
+import org.apache.xpath.compiler.OpMap;
+import org.apache.xpath.patterns.NodeTest;
+
+/**
+ * This class implements an optimized iterator for
+ * descendant, descendant-or-self, or "//foo" patterns.
+ * @see org.apache.xpath.axes.LocPathIterator
+ * @xsl.usage advanced
+ */
+public class DescendantIterator extends LocPathIterator
+{
+    static final long serialVersionUID = -1190338607743976938L;
+  /**
+   * Create a DescendantIterator object.
+   *
+   * @param compiler A reference to the Compiler that contains the op map.
+   * @param opPos The position within the op map, which contains the
+   * location path expression for this itterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  DescendantIterator(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+
+    super(compiler, opPos, analysis, false);
+
+    int firstStepPos = OpMap.getFirstChildPos(opPos);
+    int stepType = compiler.getOp(firstStepPos);
+
+    boolean orSelf = (OpCodes.FROM_DESCENDANTS_OR_SELF == stepType);
+    boolean fromRoot = false;
+    if (OpCodes.FROM_SELF == stepType)
+    {
+      orSelf = true;
+      // firstStepPos += 8;
+    }
+    else if(OpCodes.FROM_ROOT == stepType)
+    {
+      fromRoot = true;
+      // Ugly code... will go away when AST work is done.
+      int nextStepPos = compiler.getNextStepPos(firstStepPos);
+      if(compiler.getOp(nextStepPos) == OpCodes.FROM_DESCENDANTS_OR_SELF)
+        orSelf = true;
+      // firstStepPos += 8;
+    }
+    
+    // Find the position of the last step.
+    int nextStepPos = firstStepPos;
+    while(true)
+    {
+      nextStepPos = compiler.getNextStepPos(nextStepPos);
+      if(nextStepPos > 0)
+      {
+        int stepOp = compiler.getOp(nextStepPos);
+        if(OpCodes.ENDOP != stepOp)
+          firstStepPos = nextStepPos;
+        else
+          break;
+      }
+      else
+        break;
+      
+    }
+    
+    // Fix for http://nagoya.apache.org/bugzilla/show_bug.cgi?id=1336
+    if((analysis & WalkerFactory.BIT_CHILD) != 0)
+      orSelf = false;
+      
+    if(fromRoot)
+    {
+      if(orSelf)
+        m_axis = Axis.DESCENDANTSORSELFFROMROOT;
+      else
+        m_axis = Axis.DESCENDANTSFROMROOT;
+    }
+    else if(orSelf)
+      m_axis = Axis.DESCENDANTORSELF;
+    else
+      m_axis = Axis.DESCENDANT;
+
+    int whatToShow = compiler.getWhatToShow(firstStepPos);
+
+    if ((0 == (whatToShow
+               & (DTMFilter.SHOW_ATTRIBUTE | DTMFilter.SHOW_ELEMENT
+                  | DTMFilter.SHOW_PROCESSING_INSTRUCTION))) || 
+                   (whatToShow == DTMFilter.SHOW_ALL))
+      initNodeTest(whatToShow);
+    else
+    {
+      initNodeTest(whatToShow, compiler.getStepNS(firstStepPos),
+                              compiler.getStepLocalName(firstStepPos));
+    }
+    initPredicateInfo(compiler, firstStepPos);
+  }
+  
+  /**
+   * Create a DescendantIterator object.
+   *
+   */
+  public DescendantIterator()
+  {
+    super(null);
+    m_axis = Axis.DESCENDANTSORSELFFROMROOT;
+    int whatToShow = DTMFilter.SHOW_ALL;
+    initNodeTest(whatToShow);
+  }
+
+  
+  /**
+   *  Get a cloned Iterator that is reset to the beginning
+   *  of the query.
+   * 
+   *  @return A cloned NodeIterator set of the start of the query.
+   * 
+   *  @throws CloneNotSupportedException
+   */
+  public DTMIterator cloneWithReset() throws CloneNotSupportedException
+  {
+
+    DescendantIterator clone = (DescendantIterator) super.cloneWithReset();
+    clone.m_traverser = m_traverser;
+
+    clone.resetProximityPositions();
+
+    return clone;
+  }
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   *
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   *
+   * @throws DOMException
+   *    INVALID_STATE_ERR: Raised if this method is called after the
+   *   <code>detach</code> method was invoked.
+   */
+  public int nextNode()
+  {
+   	if(m_foundLast)
+  		return DTM.NULL;
+
+    if(DTM.NULL == m_lastFetched)
+    {
+      resetProximityPositions();
+    }
+
+    int next;
+    
+    org.apache.xpath.VariableStack vars;
+    int savedStart;
+    if (-1 != m_stackFrame)
+    {
+      vars = m_execContext.getVarStack();
+
+      // These three statements need to be combined into one operation.
+      savedStart = vars.getStackFrame();
+
+      vars.setStackFrame(m_stackFrame);
+    }
+    else
+    {
+      // Yuck.  Just to shut up the compiler!
+      vars = null;
+      savedStart = 0;
+    }
+    
+    try
+    {
+      do
+      {
+        if(0 == m_extendedTypeID)
+        {
+          next = m_lastFetched = (DTM.NULL == m_lastFetched)
+                       ? m_traverser.first(m_context)
+                       : m_traverser.next(m_context, m_lastFetched);
+        }
+        else
+        {
+          next = m_lastFetched = (DTM.NULL == m_lastFetched)
+                       ? m_traverser.first(m_context, m_extendedTypeID)
+                       : m_traverser.next(m_context, m_lastFetched, 
+                                          m_extendedTypeID);
+        }
+  
+        if (DTM.NULL != next)
+        {
+          if(DTMIterator.FILTER_ACCEPT == acceptNode(next))
+            break;
+          else
+            continue;
+        }
+        else
+          break;
+      }
+      while (next != DTM.NULL);
+  
+      if (DTM.NULL != next)
+      {
+      	m_pos++;
+        return next;
+      }
+      else
+      {
+        m_foundLast = true;
+  
+        return DTM.NULL;
+      }
+    }
+    finally
+    {
+      if (-1 != m_stackFrame)
+      {
+        // These two statements need to be combined into one operation.
+        vars.setStackFrame(savedStart);
+      }
+    }
+  }
+  
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+    super.setRoot(context, environment);
+    m_traverser = m_cdtm.getAxisTraverser(m_axis);
+    
+    String localName = getLocalName();
+    String namespace = getNamespace();
+    int what = m_whatToShow;
+    // System.out.println("what: ");
+    // NodeTest.debugWhatToShow(what);
+    if(DTMFilter.SHOW_ALL == what
+       || NodeTest.WILD.equals(localName)
+       || NodeTest.WILD.equals(namespace))
+    {
+      m_extendedTypeID = 0;
+    }
+    else
+    {
+      int type = getNodeTypeTest(what);
+      m_extendedTypeID = m_cdtm.getExpandedTypeID(namespace, localName, type);
+    }
+    
+  }
+  
+  /**
+   * Return the first node out of the nodeset, if this expression is 
+   * a nodeset expression.  This is the default implementation for 
+   * nodesets.
+   * <p>WARNING: Do not mutate this class from this function!</p>
+   * @param xctxt The XPath runtime context.
+   * @return the first node out of the nodeset, or DTM.NULL.
+   */
+  public int asNode(XPathContext xctxt)
+    throws javax.xml.transform.TransformerException
+  {
+    if(getPredicateCount() > 0)
+      return super.asNode(xctxt);
+
+    int current = xctxt.getCurrentNode();
+    
+    DTM dtm = xctxt.getDTM(current);
+    DTMAxisTraverser traverser = dtm.getAxisTraverser(m_axis);
+    
+    String localName = getLocalName();
+    String namespace = getNamespace();
+    int what = m_whatToShow;
+    
+    // System.out.print(" (DescendantIterator) ");
+    
+    // System.out.println("what: ");
+    // NodeTest.debugWhatToShow(what);
+    if(DTMFilter.SHOW_ALL == what
+       || localName == NodeTest.WILD
+       || namespace == NodeTest.WILD)
+    {
+      return traverser.first(current);
+    }
+    else
+    {
+      int type = getNodeTypeTest(what);
+      int extendedType = dtm.getExpandedTypeID(namespace, localName, type);
+      return traverser.first(current, extendedType);
+    }
+  }
+  
+  /**
+   *  Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   */
+  public void detach()
+  {
+    if (m_allowDetach) {
+      m_traverser = null;    
+      m_extendedTypeID = 0;
+
+      // Always call the superclass detach last!
+      super.detach();
+    }
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return m_axis;
+  }
+  
+  
+  /** The traverser to use to navigate over the descendants. */
+  transient protected DTMAxisTraverser m_traverser;
+  
+  /** The axis that we are traversing. */
+  protected int m_axis;
+  
+  /** The extended type ID, not set until setRoot. */
+  protected int m_extendedTypeID;
+  
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!super.deepEquals(expr))
+  		return false;
+  		
+  	if(m_axis != ((DescendantIterator)expr).m_axis)
+  		return false;
+  		
+  	return true;
+  }
+
+  
+}
diff --git a/src/main/java/org/apache/xpath/axes/FilterExprIterator.java b/src/main/java/org/apache/xpath/axes/FilterExprIterator.java
new file mode 100644
index 0000000..60776d1
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/FilterExprIterator.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FilterExprIterator.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.XNodeSet;
+
+public class FilterExprIterator extends BasicTestIterator
+{
+    static final long serialVersionUID = 2552176105165737614L;
+  /** The contained expression. Should be non-null.
+   *  @serial   */
+  private Expression m_expr;
+
+  /** The result of executing m_expr.  Needs to be deep cloned on clone op.  */
+  transient private XNodeSet m_exprObj;
+
+  private boolean m_mustHardReset = false;
+  private boolean m_canDetachNodeset = true;
+
+  /**
+   * Create a FilterExprIterator object.
+   *
+   */
+  public FilterExprIterator()
+  {
+    super(null);
+  }
+  
+  /**
+   * Create a FilterExprIterator object.
+   *
+   */
+  public FilterExprIterator(Expression expr)
+  {
+    super(null);
+    m_expr = expr;
+  }
+
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+  	super.setRoot(context, environment);
+ 	
+  	m_exprObj = FilterExprIteratorSimple.executeFilterExpr(context, 
+  	                  m_execContext, getPrefixResolver(), 
+  	                  getIsTopLevel(), m_stackFrame, m_expr);
+   }
+
+
+  /**
+   * Get the next node via getNextXXX.  Bottlenecked for derived class override.
+   * @return The next node on the axis, or DTM.NULL.
+   */
+  protected int getNextNode()
+  {
+    if (null != m_exprObj)
+    {
+      m_lastFetched = m_exprObj.nextNode();
+    }
+    else
+      m_lastFetched = DTM.NULL;
+
+    return m_lastFetched;
+  }
+  
+  /**
+   * Detaches the walker from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state.
+   */
+  public void detach()
+  {  
+  	super.detach();
+  	m_exprObj.detach();
+  	m_exprObj = null;
+  }
+
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+    m_expr.fixupVariables(vars, globalsSize);
+  }
+
+  /**
+   * Get the inner contained expression of this filter.
+   */
+  public Expression getInnerExpression()
+  {
+    return m_expr;
+  }
+
+  /**
+   * Set the inner contained expression of this filter.
+   */
+  public void setInnerExpression(Expression expr)
+  {
+    expr.exprSetParent(this);
+    m_expr = expr;
+  }
+
+  /** 
+   * Get the analysis bits for this walker, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits()
+  {
+    if (null != m_expr && m_expr instanceof PathComponent)
+    {
+      return ((PathComponent) m_expr).getAnalysisBits();
+    }
+    return WalkerFactory.BIT_FILTER;
+  }
+
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * Warning: This can only be called after setRoot has been called!
+   * 
+   * @return true as a default.
+   */
+  public boolean isDocOrdered()
+  {
+    return m_exprObj.isDocOrdered();
+  }
+
+  class filterExprOwner implements ExpressionOwner
+  {
+    /**
+    * @see ExpressionOwner#getExpression()
+    */
+    public Expression getExpression()
+    {
+      return m_expr;
+    }
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+      exp.exprSetParent(FilterExprIterator.this);
+      m_expr = exp;
+    }
+
+  }
+
+  /**
+   * This will traverse the heararchy, calling the visitor for 
+   * each member.  If the called visitor method returns 
+   * false, the subtree should not be called.
+   * 
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  public void callPredicateVisitors(XPathVisitor visitor)
+  {
+    m_expr.callVisitors(new filterExprOwner(), visitor);
+
+    super.callPredicateVisitors(visitor);
+  }
+
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+    if (!super.deepEquals(expr))
+      return false;
+
+    FilterExprIterator fet = (FilterExprIterator) expr;
+    if (!m_expr.deepEquals(fet.m_expr))
+      return false;
+
+    return true;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/FilterExprIteratorSimple.java b/src/main/java/org/apache/xpath/axes/FilterExprIteratorSimple.java
new file mode 100644
index 0000000..9f11944
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/FilterExprIteratorSimple.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FilterExprIteratorSimple.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.XNodeSet;
+
+/**
+ * Class to use for one-step iteration that doesn't have a predicate, and 
+ * doesn't need to set the context.
+ */
+public class FilterExprIteratorSimple extends LocPathIterator
+{
+    static final long serialVersionUID = -6978977187025375579L;
+  /** The contained expression. Should be non-null.
+   *  @serial   */
+  private Expression m_expr;
+
+  /** The result of executing m_expr.  Needs to be deep cloned on clone op.  */
+  transient private XNodeSet m_exprObj;
+
+  private boolean m_mustHardReset = false;
+  private boolean m_canDetachNodeset = true;
+
+  /**
+   * Create a FilterExprIteratorSimple object.
+   *
+   */
+  public FilterExprIteratorSimple()
+  {
+    super(null);
+  }
+  
+  /**
+   * Create a FilterExprIteratorSimple object.
+   *
+   */
+  public FilterExprIteratorSimple(Expression expr)
+  {
+    super(null);
+    m_expr = expr;
+  }
+  
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+  	super.setRoot(context, environment);
+  	m_exprObj = executeFilterExpr(context, m_execContext, getPrefixResolver(), 
+  	                  getIsTopLevel(), m_stackFrame, m_expr);
+  }
+
+  /**
+   * Execute the expression.  Meant for reuse by other FilterExpr iterators 
+   * that are not derived from this object.
+   */
+  public static XNodeSet executeFilterExpr(int context, XPathContext xctxt, 
+  												PrefixResolver prefixResolver,
+  												boolean isTopLevel,
+  												int stackFrame,
+  												Expression expr )
+    throws org.apache.xml.utils.WrappedRuntimeException
+  {
+    PrefixResolver savedResolver = xctxt.getNamespaceContext();
+    XNodeSet result = null;
+
+    try
+    {
+      xctxt.pushCurrentNode(context);
+      xctxt.setNamespaceContext(prefixResolver);
+
+      // The setRoot operation can take place with a reset operation, 
+      // and so we may not be in the context of LocPathIterator#nextNode, 
+      // so we have to set up the variable context, execute the expression, 
+      // and then restore the variable context.
+
+      if (isTopLevel)
+      {
+        // System.out.println("calling m_expr.execute(getXPathContext())");
+        VariableStack vars = xctxt.getVarStack();
+
+        // These three statements need to be combined into one operation.
+        int savedStart = vars.getStackFrame();
+        vars.setStackFrame(stackFrame);
+
+        result = (org.apache.xpath.objects.XNodeSet) expr.execute(xctxt);
+        result.setShouldCacheNodes(true);
+
+        // These two statements need to be combined into one operation.
+        vars.setStackFrame(savedStart);
+      }
+      else
+          result = (org.apache.xpath.objects.XNodeSet) expr.execute(xctxt);
+
+    }
+    catch (javax.xml.transform.TransformerException se)
+    {
+
+      // TODO: Fix...
+      throw new org.apache.xml.utils.WrappedRuntimeException(se);
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+      xctxt.setNamespaceContext(savedResolver);
+    }
+    return result;
+  }
+  
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   *
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   */
+  public int nextNode()
+  {
+  	if(m_foundLast)
+  		return DTM.NULL;
+
+    int next;
+
+    if (null != m_exprObj)
+    {
+      m_lastFetched = next = m_exprObj.nextNode();
+    }
+    else
+      m_lastFetched = next = DTM.NULL;
+
+    // m_lastFetched = next;
+    if (DTM.NULL != next)
+    {
+      m_pos++;
+      return next;
+    }
+    else
+    {
+      m_foundLast = true;
+
+      return DTM.NULL;
+    }
+  }
+  
+  /**
+   * Detaches the walker from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state.
+   */
+  public void detach()
+  {  
+    if(m_allowDetach)
+    {
+  		super.detach();
+  		m_exprObj.detach();
+  		m_exprObj = null;
+    }
+  }
+
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+    m_expr.fixupVariables(vars, globalsSize);
+  }
+
+  /**
+   * Get the inner contained expression of this filter.
+   */
+  public Expression getInnerExpression()
+  {
+    return m_expr;
+  }
+
+  /**
+   * Set the inner contained expression of this filter.
+   */
+  public void setInnerExpression(Expression expr)
+  {
+    expr.exprSetParent(this);
+    m_expr = expr;
+  }
+
+  /** 
+   * Get the analysis bits for this walker, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits()
+  {
+    if (null != m_expr && m_expr instanceof PathComponent)
+    {
+      return ((PathComponent) m_expr).getAnalysisBits();
+    }
+    return WalkerFactory.BIT_FILTER;
+  }
+
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * Warning: This can only be called after setRoot has been called!
+   * 
+   * @return true as a default.
+   */
+  public boolean isDocOrdered()
+  {
+    return m_exprObj.isDocOrdered();
+  }
+
+  class filterExprOwner implements ExpressionOwner
+  {
+    /**
+    * @see ExpressionOwner#getExpression()
+    */
+    public Expression getExpression()
+    {
+      return m_expr;
+    }
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+      exp.exprSetParent(FilterExprIteratorSimple.this);
+      m_expr = exp;
+    }
+
+  }
+
+  /**
+   * This will traverse the heararchy, calling the visitor for 
+   * each member.  If the called visitor method returns 
+   * false, the subtree should not be called.
+   * 
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  public void callPredicateVisitors(XPathVisitor visitor)
+  {
+    m_expr.callVisitors(new filterExprOwner(), visitor);
+
+    super.callPredicateVisitors(visitor);
+  }
+
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+    if (!super.deepEquals(expr))
+      return false;
+
+    FilterExprIteratorSimple fet = (FilterExprIteratorSimple) expr;
+    if (!m_expr.deepEquals(fet.m_expr))
+      return false;
+
+    return true;
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+  	if(null != m_exprObj)
+    	return m_exprObj.getAxis();
+    else
+    	return Axis.FILTEREDLIST;
+  }
+
+
+}
+
diff --git a/src/main/java/org/apache/xpath/axes/FilterExprWalker.java b/src/main/java/org/apache/xpath/axes/FilterExprWalker.java
new file mode 100644
index 0000000..cc7e7db
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/FilterExprWalker.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FilterExprWalker.java 469367 2006-10-31 04:41:08Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.OpCodes;
+import org.apache.xpath.objects.XNodeSet;
+
+/**
+ * Walker for the OP_VARIABLE, or OP_EXTFUNCTION, or OP_FUNCTION, or OP_GROUP,
+ * op codes.
+ * @see <a href="http://www.w3.org/TR/xpath#NT-FilterExpr">XPath FilterExpr descriptions</a>
+ */
+public class FilterExprWalker extends AxesWalker
+{
+    static final long serialVersionUID = 5457182471424488375L;
+
+  /**
+   * Construct a FilterExprWalker using a LocPathIterator.
+   *
+   * @param locPathIterator non-null reference to the parent iterator.
+   */
+  public FilterExprWalker(WalkingIterator locPathIterator)
+  {
+    super(locPathIterator, Axis.FILTEREDLIST);
+  }
+
+  /**
+   * Init a FilterExprWalker.
+   *
+   * @param compiler non-null reference to the Compiler that is constructing.
+   * @param opPos positive opcode position for this step.
+   * @param stepType The type of step.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void init(Compiler compiler, int opPos, int stepType)
+          throws javax.xml.transform.TransformerException
+  {
+
+    super.init(compiler, opPos, stepType);
+
+    // Smooth over an anomily in the opcode map...
+    switch (stepType)
+    {
+    case OpCodes.OP_FUNCTION :
+    case OpCodes.OP_EXTFUNCTION :
+    	m_mustHardReset = true;
+    case OpCodes.OP_GROUP :
+    case OpCodes.OP_VARIABLE :
+      m_expr = compiler.compile(opPos);
+      m_expr.exprSetParent(this);
+      //if((OpCodes.OP_FUNCTION == stepType) && (m_expr instanceof org.apache.xalan.templates.FuncKey))
+      if(m_expr instanceof org.apache.xpath.operations.Variable)
+      {
+      	// hack/temp workaround
+      	m_canDetachNodeset = false;
+      }
+      break;
+    default :
+      m_expr = compiler.compile(opPos + 2);
+      m_expr.exprSetParent(this);
+    }
+//    if(m_expr instanceof WalkingIterator)
+//    {
+//      WalkingIterator wi = (WalkingIterator)m_expr;
+//      if(wi.getFirstWalker() instanceof FilterExprWalker)
+//      {
+//      	FilterExprWalker fw = (FilterExprWalker)wi.getFirstWalker();
+//      	if(null == fw.getNextWalker())
+//      	{
+//      		m_expr = fw.m_expr;
+//      		m_expr.exprSetParent(this);
+//      	}
+//      }
+//      		
+//    }
+  }
+  
+  /**
+   * Detaches the walker from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state.
+   */
+  public void detach()
+  {  
+  	super.detach();
+  	if (m_canDetachNodeset)
+  	{
+  	  m_exprObj.detach();
+  	}
+  	m_exprObj = null;
+  }
+
+  /**
+   *  Set the root node of the TreeWalker.
+   *
+   * @param root non-null reference to the root, or starting point of 
+   *        the query.
+   */
+  public void setRoot(int root)
+  {
+
+    super.setRoot(root);
+
+  	m_exprObj = FilterExprIteratorSimple.executeFilterExpr(root, 
+  	                  m_lpi.getXPathContext(), m_lpi.getPrefixResolver(), 
+  	                  m_lpi.getIsTopLevel(), m_lpi.m_stackFrame, m_expr);
+
+  }
+
+  /**
+   * Get a cloned FilterExprWalker.
+   *
+   * @return A new FilterExprWalker that can be used without mutating this one.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+
+    FilterExprWalker clone = (FilterExprWalker) super.clone();
+
+    if (null != m_exprObj)
+      clone.m_exprObj = (XNodeSet) m_exprObj.clone();
+
+    return clone;
+  }
+  
+  /**
+   * This method needs to override AxesWalker.acceptNode because FilterExprWalkers
+   * don't need to, and shouldn't, do a node test.
+   * @param n  The node to check to see if it passes the filter or not.
+   * @return  a constant to determine whether the node is accepted,
+   *   rejected, or skipped, as defined  above .
+   */
+  public short acceptNode(int n)
+  {
+
+    try
+    {
+      if (getPredicateCount() > 0)
+      {
+        countProximityPosition(0);
+
+        if (!executePredicates(n, m_lpi.getXPathContext()))
+          return DTMIterator.FILTER_SKIP;
+      }
+
+      return DTMIterator.FILTER_ACCEPT;
+    }
+    catch (javax.xml.transform.TransformerException se)
+    {
+      throw new RuntimeException(se.getMessage());
+    }
+  }
+
+  /**
+   *  Moves the <code>TreeWalker</code> to the next visible node in document
+   * order relative to the current node, and returns the new node. If the
+   * current node has no next node,  or if the search for nextNode attempts
+   * to step upward from the TreeWalker's root node, returns
+   * <code>null</code> , and retains the current node.
+   * @return  The new node, or <code>null</code> if the current node has no
+   *   next node  in the TreeWalker's logical view.
+   */
+  public int getNextNode()
+  {
+
+    if (null != m_exprObj)
+    {
+       int next = m_exprObj.nextNode();
+       return next;
+    }
+    else
+      return DTM.NULL;
+  }
+  
+  /**
+   * Get the index of the last node that can be itterated to.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return the index of the last node that can be itterated to.
+   */
+  public int getLastPos(XPathContext xctxt)
+  {
+    return m_exprObj.getLength();
+  }
+  
+  /** The contained expression. Should be non-null.
+   *  @serial   */
+  private Expression m_expr;
+
+  /** The result of executing m_expr.  Needs to be deep cloned on clone op.  */
+  transient private XNodeSet m_exprObj;
+  
+  private boolean m_mustHardReset = false;
+  private boolean m_canDetachNodeset = true;
+
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+    m_expr.fixupVariables(vars, globalsSize);
+  }
+  
+  /**
+   * Get the inner contained expression of this filter.
+   */
+  public Expression getInnerExpression()
+  {
+  	return m_expr;
+  }
+  
+  /**
+   * Set the inner contained expression of this filter.
+   */
+  public void setInnerExpression(Expression expr)
+  {
+  	expr.exprSetParent(this);
+  	m_expr = expr;
+  }
+
+  
+  /** 
+   * Get the analysis bits for this walker, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits()
+  {
+      if (null != m_expr && m_expr instanceof PathComponent)
+      {
+        return ((PathComponent) m_expr).getAnalysisBits();
+      }
+      return WalkerFactory.BIT_FILTER;
+  }
+  
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * Warning: This can only be called after setRoot has been called!
+   * 
+   * @return true as a default.
+   */
+  public boolean isDocOrdered()
+  {
+    return m_exprObj.isDocOrdered();
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return m_exprObj.getAxis();
+  }
+  
+  class filterExprOwner implements ExpressionOwner
+  {
+      /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_expr;
+    }
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(FilterExprWalker.this);
+    	m_expr = exp;
+    }
+  }
+  
+	/**
+	 * This will traverse the heararchy, calling the visitor for 
+	 * each member.  If the called visitor method returns 
+	 * false, the subtree should not be called.
+	 * 
+	 * @param visitor The visitor whose appropriate method will be called.
+	 */
+	public void callPredicateVisitors(XPathVisitor visitor)
+	{
+	  m_expr.callVisitors(new filterExprOwner(), visitor);
+	  
+	  super.callPredicateVisitors(visitor);
+	} 
+
+
+    /**
+     * @see Expression#deepEquals(Expression)
+     */
+    public boolean deepEquals(Expression expr)
+    {
+      if (!super.deepEquals(expr))
+                return false;
+
+      FilterExprWalker walker = (FilterExprWalker)expr;
+      if(!m_expr.deepEquals(walker.m_expr))
+      	return false;
+
+      return true;
+    }
+
+	
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/HasPositionalPredChecker.java b/src/main/java/org/apache/xpath/axes/HasPositionalPredChecker.java
new file mode 100644
index 0000000..3fc6424
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/HasPositionalPredChecker.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: HasPositionalPredChecker.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.functions.FuncLast;
+import org.apache.xpath.functions.FuncPosition;
+import org.apache.xpath.functions.Function;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.operations.Div;
+import org.apache.xpath.operations.Minus;
+import org.apache.xpath.operations.Mod;
+import org.apache.xpath.operations.Mult;
+import org.apache.xpath.operations.Plus;
+import org.apache.xpath.operations.Quo;
+import org.apache.xpath.operations.Variable;
+
+public class HasPositionalPredChecker extends XPathVisitor
+{
+	private boolean m_hasPositionalPred = false;
+	private int m_predDepth = 0;
+	
+	/**
+	 * Process the LocPathIterator to see if it contains variables 
+	 * or functions that may make it context dependent.
+	 * @param path LocPathIterator that is assumed to be absolute, but needs checking.
+	 * @return true if the path is confirmed to be absolute, false if it 
+	 * may contain context dependencies.
+	 */
+	public static boolean check(LocPathIterator path)
+	{
+		HasPositionalPredChecker hppc = new HasPositionalPredChecker();
+		path.callVisitors(null, hppc);
+		return hppc.m_hasPositionalPred;
+	}
+	
+	/**
+	 * Visit a function.
+	 * @param owner The owner of the expression, to which the expression can 
+	 *              be reset if rewriting takes place.
+	 * @param func The function reference object.
+	 * @return true if the sub expressions should be traversed.
+	 */
+	public boolean visitFunction(ExpressionOwner owner, Function func)
+	{
+		if((func instanceof FuncPosition) ||
+		   (func instanceof FuncLast))
+			m_hasPositionalPred = true;
+		return true;
+	}
+	
+//	/**
+//	 * Visit a variable reference.
+//	 * @param owner The owner of the expression, to which the expression can 
+//	 *              be reset if rewriting takes place.
+//	 * @param var The variable reference object.
+//	 * @return true if the sub expressions should be traversed.
+//	 */
+//	public boolean visitVariableRef(ExpressionOwner owner, Variable var)
+//	{
+//		m_hasPositionalPred = true;
+//		return true;
+//	}
+	
+  /**
+   * Visit a predicate within a location path.  Note that there isn't a 
+   * proper unique component for predicates, and that the expression will 
+   * be called also for whatever type Expression is.
+   * 
+   * @param owner The owner of the expression, to which the expression can 
+   *              be reset if rewriting takes place.
+   * @param pred The predicate object.
+   * @return true if the sub expressions should be traversed.
+   */
+  public boolean visitPredicate(ExpressionOwner owner, Expression pred)
+  {
+    m_predDepth++;
+
+    if(m_predDepth == 1)
+    {
+      if((pred instanceof Variable) || 
+         (pred instanceof XNumber) ||
+         (pred instanceof Div) ||
+         (pred instanceof Plus) ||
+         (pred instanceof Minus) ||
+         (pred instanceof Mod) ||
+         (pred instanceof Quo) ||
+         (pred instanceof Mult) ||
+         (pred instanceof org.apache.xpath.operations.Number) ||
+         (pred instanceof Function))
+          m_hasPositionalPred = true;
+      else
+      	pred.callVisitors(owner, this);
+    }
+
+    m_predDepth--;
+
+    // Don't go have the caller go any further down the subtree.
+    return false;
+  }
+
+
+}
+
diff --git a/src/main/java/org/apache/xpath/axes/IteratorPool.java b/src/main/java/org/apache/xpath/axes/IteratorPool.java
new file mode 100644
index 0000000..ef97c44
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/IteratorPool.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: IteratorPool.java 475981 2006-11-16 23:35:53Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import java.util.ArrayList;
+
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.WrappedRuntimeException;
+
+/**
+ * Pool of object of a given type to pick from to help memory usage
+ * @xsl.usage internal
+ */
+public final class IteratorPool implements java.io.Serializable
+{
+    static final long serialVersionUID = -460927331149566998L;
+
+  /** 
+   * Type of objects in this pool.
+   */
+  private final DTMIterator m_orig;
+
+  /** 
+   * Stack of given objects this points to.
+   */
+  private final ArrayList m_freeStack;
+
+  /**
+   * Constructor IteratorPool
+   *
+   * @param original The original iterator from which all others will be cloned.
+   */
+  public IteratorPool(DTMIterator original)
+  {
+    m_orig = original;
+    m_freeStack = new ArrayList();
+  }
+  
+  /**
+   * Get an instance of the given object in this pool 
+   *
+   * @return An instance of the given object
+   */
+  public synchronized DTMIterator getInstanceOrThrow()
+    throws CloneNotSupportedException
+  {
+    // Check if the pool is empty.
+    if (m_freeStack.isEmpty())
+    {
+
+      // Create a new object if so.
+      return (DTMIterator)m_orig.clone();
+    }
+    else
+    {
+      // Remove object from end of free pool.
+      DTMIterator result = (DTMIterator)m_freeStack.remove(m_freeStack.size() - 1);
+      return result;
+    }
+  }
+  
+  /**
+   * Get an instance of the given object in this pool 
+   *
+   * @return An instance of the given object
+   */
+  public synchronized DTMIterator getInstance()
+  {
+    // Check if the pool is empty.
+    if (m_freeStack.isEmpty())
+    {
+
+      // Create a new object if so.
+      try
+      {
+        return (DTMIterator)m_orig.clone();
+      }
+      catch (Exception ex)
+      {
+        throw new WrappedRuntimeException(ex);
+      }
+    }
+    else
+    {
+      // Remove object from end of free pool.
+      DTMIterator result = (DTMIterator)m_freeStack.remove(m_freeStack.size() - 1);
+      return result;
+    }
+  }
+
+  /**
+   * Add an instance of the given object to the pool  
+   *
+   *
+   * @param obj Object to add.
+   */
+  public synchronized void freeInstance(DTMIterator obj)
+  {
+    m_freeStack.add(obj);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/axes/LocPathIterator.java b/src/main/java/org/apache/xpath/axes/LocPathIterator.java
new file mode 100644
index 0000000..214eba1
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/LocPathIterator.java
@@ -0,0 +1,1033 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: LocPathIterator.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * This class extends NodeSetDTM, which implements NodeIterator,
+ * and fetches nodes one at a time in document order based on a XPath
+ * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a>.
+ *
+ * <p>If setShouldCacheNodes(true) is called,
+ * as each node is iterated via nextNode(), the node is also stored
+ * in the NodeVector, so that previousNode() can easily be done, except in
+ * the case where the LocPathIterator is "owned" by a UnionPathIterator,
+ * in which case the UnionPathIterator will cache the nodes.</p>
+ * @xsl.usage advanced
+ */
+public abstract class LocPathIterator extends PredicatedNodeTest
+        implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
+{
+    static final long serialVersionUID = -4602476357268405754L;
+	
+  /**
+   * Create a LocPathIterator object.
+   *
+   */
+  protected LocPathIterator()
+  {
+  }
+
+
+  /**
+   * Create a LocPathIterator object.
+   *
+   * @param nscontext The namespace context for this iterator,
+   * should be OK if null.
+   */
+  protected LocPathIterator(PrefixResolver nscontext)
+  {
+
+    setLocPathIterator(this);
+    m_prefixResolver = nscontext;
+  }
+
+  /**
+   * Create a LocPathIterator object, including creation
+   * of step walkers from the opcode list, and call back
+   * into the Compiler to create predicate expressions.
+   *
+   * @param compiler The Compiler which is creating
+   * this expression.
+   * @param opPos The position of this iterator in the
+   * opcode list from the compiler.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected LocPathIterator(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+    this(compiler, opPos, analysis, true);
+  }
+
+  /**
+   * Create a LocPathIterator object, including creation
+   * of step walkers from the opcode list, and call back
+   * into the Compiler to create predicate expressions.
+   *
+   * @param compiler The Compiler which is creating
+   * this expression.
+   * @param opPos The position of this iterator in the
+   * opcode list from the compiler.
+   * @param shouldLoadWalkers True if walkers should be
+   * loaded, or false if this is a derived iterator and
+   * it doesn't wish to load child walkers.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected LocPathIterator(
+          Compiler compiler, int opPos, int analysis, boolean shouldLoadWalkers)
+            throws javax.xml.transform.TransformerException
+  {
+    setLocPathIterator(this);
+  }
+  
+  /** 
+   * Get the analysis bits for this walker, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits()
+  {
+  	int axis = getAxis();
+  	int bit = WalkerFactory.getAnalysisBitFromAxes(axis);
+  	return bit;
+  }
+  
+  /**
+   * Read the object from a serialization stream.
+   *
+   * @param stream Input stream to read from
+   *
+   * @throws java.io.IOException
+   * @throws javax.xml.transform.TransformerException
+   */
+  private void readObject(java.io.ObjectInputStream stream)
+          throws java.io.IOException, javax.xml.transform.TransformerException
+  {
+    try
+    {
+      stream.defaultReadObject();
+      m_clones =  new IteratorPool(this);
+    }
+    catch (ClassNotFoundException cnfe)
+    {
+      throw new javax.xml.transform.TransformerException(cnfe);
+    }
+  }
+  
+  /**
+   * Set the environment in which this iterator operates, which should provide:
+   * a node (the context node... same value as "root" defined below) 
+   * a pair of non-zero positive integers (the context position and the context size) 
+   * a set of variable bindings 
+   * a function library 
+   * the set of namespace declarations in scope for the expression.
+   * 
+   * <p>At this time the exact implementation of this environment is application 
+   * dependent.  Probably a proper interface will be created fairly soon.</p>
+   * 
+   * @param environment The environment object.
+   */
+  public void setEnvironment(Object environment)
+  {
+    // no-op for now.
+  }
+  
+  /**
+   * Get an instance of a DTM that "owns" a node handle.  Since a node 
+   * iterator may be passed without a DTMManager, this allows the 
+   * caller to easily get the DTM using just the iterator.
+   *
+   * @param nodeHandle the nodeHandle.
+   *
+   * @return a non-null DTM reference.
+   */
+  public DTM getDTM(int nodeHandle)
+  {
+    // %OPT%
+    return m_execContext.getDTM(nodeHandle);
+  }
+  
+  /**
+   * Get an instance of the DTMManager.  Since a node 
+   * iterator may be passed without a DTMManager, this allows the 
+   * caller to easily get the DTMManager using just the iterator.
+   *
+   * @return a non-null DTMManager reference.
+   */
+  public DTMManager getDTMManager()
+  {
+    return m_execContext.getDTMManager();
+  }
+  
+  /**
+   * Execute this iterator, meaning create a clone that can
+   * store state, and initialize it for fast execution from
+   * the current runtime state.  When this is called, no actual
+   * query from the current context node is performed.
+   *
+   * @param xctxt The XPath execution context.
+   *
+   * @return An XNodeSet reference that holds this iterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    XNodeSet iter = new XNodeSet((LocPathIterator)m_clones.getInstance());
+
+    iter.setRoot(xctxt.getCurrentNode(), xctxt);
+
+    return iter;
+  }
+    
+  /**
+   * Execute an expression in the XPath runtime context, and return the
+   * result of the expression.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @param handler The target content handler.
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception
+   *         occurs.
+   * @throws org.xml.sax.SAXException
+   */
+  public void executeCharsToContentHandler(
+          XPathContext xctxt, org.xml.sax.ContentHandler handler)
+            throws javax.xml.transform.TransformerException,
+                   org.xml.sax.SAXException
+  {
+    LocPathIterator clone = (LocPathIterator)m_clones.getInstance();
+
+    int current = xctxt.getCurrentNode();
+    clone.setRoot(current, xctxt);
+    
+    int node = clone.nextNode();
+    DTM dtm = clone.getDTM(node);
+    clone.detach();
+	
+    if(node != DTM.NULL)
+    {
+      dtm.dispatchCharactersEvents(node, handler, false);
+    }
+  }
+  
+  /**
+   * Given an select expression and a context, evaluate the XPath
+   * and return the resulting iterator.
+   * 
+   * @param xctxt The execution context.
+   * @param contextNode The node that "." expresses.
+   * @throws TransformerException thrown if the active ProblemListener decides
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage experimental
+   */
+  public DTMIterator asIterator(
+          XPathContext xctxt, int contextNode)
+            throws javax.xml.transform.TransformerException
+  {
+    XNodeSet iter = new XNodeSet((LocPathIterator)m_clones.getInstance());
+
+    iter.setRoot(contextNode, xctxt);
+
+    return iter;
+  }
+
+  
+  /**
+   * Tell if the expression is a nodeset expression.
+   * 
+   * @return true if the expression can be represented as a nodeset.
+   */
+  public boolean isNodesetExpr()
+  {
+    return true;
+  }
+  
+  /**
+   * Return the first node out of the nodeset, if this expression is 
+   * a nodeset expression.  This is the default implementation for 
+   * nodesets.  Derived classes should try and override this and return a 
+   * value without having to do a clone operation.
+   * @param xctxt The XPath runtime context.
+   * @return the first node out of the nodeset, or DTM.NULL.
+   */
+  public int asNode(XPathContext xctxt)
+    throws javax.xml.transform.TransformerException
+  {
+    DTMIterator iter = (DTMIterator)m_clones.getInstance();
+    
+    int current = xctxt.getCurrentNode();
+    
+    iter.setRoot(current, xctxt);
+
+    int next = iter.nextNode();
+    // m_clones.freeInstance(iter);
+    iter.detach();
+    return next;
+  }
+  
+  /**
+   * Evaluate this operation directly to a boolean.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a boolean.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean bool(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return (asNode(xctxt) != DTM.NULL);
+  }
+
+
+  /**
+   * Set if this is an iterator at the upper level of
+   * the XPath.
+   *
+   * @param b true if this location path is at the top level of the
+   *          expression.
+   * @xsl.usage advanced
+   */
+  public void setIsTopLevel(boolean b)
+  {
+    m_isTopLevel = b;
+  }
+
+  /**
+   * Get if this is an iterator at the upper level of
+   * the XPath.
+   *
+   * @return true if this location path is at the top level of the
+   *          expression.
+   * @xsl.usage advanced
+   */
+  public boolean getIsTopLevel()
+  {
+    return m_isTopLevel;
+  }
+  
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+
+    m_context = context;
+    
+    XPathContext xctxt = (XPathContext)environment;
+    m_execContext = xctxt;
+    m_cdtm = xctxt.getDTM(context);
+    
+    m_currentContextNode = context; // only if top level?
+    
+    // Yech, shouldn't have to do this.  -sb
+    if(null == m_prefixResolver)
+    	m_prefixResolver = xctxt.getNamespaceContext();
+        
+    m_lastFetched = DTM.NULL;
+    m_foundLast = false;
+    m_pos = 0;
+    m_length = -1;
+
+    if (m_isTopLevel)
+      this.m_stackFrame = xctxt.getVarStack().getStackFrame();
+      
+    // reset();
+  }
+
+  /**
+   * Set the next position index of this iterator.
+   *
+   * @param next A value greater than or equal to zero that indicates the next
+   * node position to fetch.
+   */
+  protected void setNextPosition(int next)
+  {
+    assertion(false, "setNextPosition not supported in this iterator!");
+  }
+
+  /**
+   * Get the current position, which is one less than
+   * the next nextNode() call will retrieve.  i.e. if
+   * you call getCurrentPos() and the return is 0, the next
+   * fetch will take place at index 1.
+   *
+   * @return A value greater than or equal to zero that indicates the next
+   * node position to fetch.
+   */
+  public final int getCurrentPos()
+  {
+    return m_pos;
+  }
+
+
+  /**
+   * If setShouldCacheNodes(true) is called, then nodes will
+   * be cached.  They are not cached by default.
+   *
+   * @param b True if this iterator should cache nodes.
+   */
+  public void setShouldCacheNodes(boolean b)
+  {
+
+    assertion(false, "setShouldCacheNodes not supported by this iterater!");
+  }
+  
+  /**
+   * Tells if this iterator can have nodes added to it or set via 
+   * the <code>setItem(int node, int index)</code> method.
+   * 
+   * @return True if the nodelist can be mutated.
+   */
+  public boolean isMutable()
+  {
+    return false;
+  }
+
+  /**
+   * Set the current position in the node set.
+   *
+   * @param i Must be a valid index greater
+   * than or equal to zero and less than m_cachedNodes.size().
+   */
+  public void setCurrentPos(int i)
+  {
+  	assertion(false, "setCurrentPos not supported by this iterator!");
+  }
+  
+  /**
+   * Increment the current position in the node set.
+   */
+  public void incrementCurrentPos()
+  {
+  	m_pos++;
+  }
+
+
+  /**
+   * Get the length of the cached nodes.
+   *
+   * <p>Note: for the moment at least, this only returns
+   * the size of the nodes that have been fetched to date,
+   * it doesn't attempt to run to the end to make sure we
+   * have found everything.  This should be reviewed.</p>
+   *
+   * @return The size of the current cache list.
+   */
+  public int size()
+  {
+	assertion(false, "size() not supported by this iterator!");
+	return 0;
+  }
+
+  /**
+   *  Returns the <code>index</code> th item in the collection. If
+   * <code>index</code> is greater than or equal to the number of nodes in
+   * the list, this returns <code>null</code> .
+   * @param index  Index into the collection.
+   * @return  The node at the <code>index</code> th position in the
+   *   <code>NodeList</code> , or <code>null</code> if that is not a valid
+   *   index.
+   */
+  public int item(int index)
+  {
+	assertion(false, "item(int index) not supported by this iterator!");
+	return 0;
+  }
+  
+  /**
+   * Sets the node at the specified index of this vector to be the
+   * specified node. The previous component at that position is discarded.
+   *
+   * <p>The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.  
+   * The iterator must be in cached mode.</p>
+   * 
+   * <p>Meant to be used for sorted iterators.</p>
+   *
+   * @param node Node to set
+   * @param index Index of where to set the node
+   */
+  public void setItem(int node, int index)
+  {
+	assertion(false, "setItem not supported by this iterator!");
+  }
+
+  /**
+   *  The number of nodes in the list. The range of valid child node indices
+   * is 0 to <code>length-1</code> inclusive.
+   *
+   * @return The number of nodes in the list, always greater or equal to zero.
+   */
+  public int getLength()
+  {      
+    // Tell if this is being called from within a predicate.
+  	boolean isPredicateTest = (this == m_execContext.getSubContextList());
+
+    // And get how many total predicates are part of this step.
+  	int predCount = getPredicateCount();
+  	
+    // If we have already calculated the length, and the current predicate 
+    // is the first predicate, then return the length.  We don't cache 
+    // the anything but the length of the list to the first predicate.
+    if (-1 != m_length && isPredicateTest && m_predicateIndex < 1)
+  		return m_length;
+  	
+    // I'm a bit worried about this one, since it doesn't have the 
+    // checks found above.  I suspect it's fine.  -sb
+    if (m_foundLast)
+  		return m_pos;
+  		
+    // Create a clone, and count from the current position to the end 
+    // of the list, not taking into account the current predicate and 
+    // predicates after the current one.
+    int pos = (m_predicateIndex >= 0) ? getProximityPosition() : m_pos;
+              
+    LocPathIterator clone;
+
+    try
+    {
+      clone = (LocPathIterator) clone();        
+    }
+    catch (CloneNotSupportedException cnse)
+    {
+      return -1;
+    }
+
+    // We want to clip off the last predicate, but only if we are a sub 
+    // context node list, NOT if we are a context list.  See pos68 test, 
+    // also test against bug4638.
+    if (predCount > 0 && isPredicateTest)
+    {
+      // Don't call setPredicateCount, because it clones and is slower.
+      clone.m_predCount = m_predicateIndex;
+      // The line above used to be:
+      // clone.m_predCount = predCount - 1;
+      // ...which looks like a dumb bug to me. -sb
+    }
+
+    int next;
+
+    while (DTM.NULL != (next = clone.nextNode()))
+    {
+      pos++;
+    }
+    
+    if (isPredicateTest && m_predicateIndex < 1)
+      m_length = pos;
+    
+    return pos;
+  }
+
+  /**
+   * Tells if this NodeSetDTM is "fresh", in other words, if
+   * the first nextNode() that is called will return the
+   * first node in the set.
+   *
+   * @return true of nextNode has not been called.
+   */
+  public boolean isFresh()
+  {
+    return (m_pos == 0);
+  }
+
+  /**
+   *  Returns the previous node in the set and moves the position of the
+   * iterator backwards in the set.
+   * @return  The previous <code>Node</code> in the set being iterated over,
+   *   or<code>null</code> if there are no more members in that set.
+   */
+  public int previousNode()
+  {
+    throw new RuntimeException(
+      XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_CANNOT_ITERATE, null)); //"This NodeSetDTM can not iterate to a previous node!");
+  }
+
+  /**
+   * This attribute determines which node types are presented via the
+   * iterator. The available set of constants is defined in the
+   * <code>NodeFilter</code> interface.
+   *
+   * <p>This is somewhat useless at this time, since it doesn't
+   * really return information that tells what this iterator will
+   * show.  It is here only to fullfill the DOM NodeIterator
+   * interface.</p>
+   *
+   * @return For now, always NodeFilter.SHOW_ALL & ~NodeFilter.SHOW_ENTITY_REFERENCE.
+   * @see org.w3c.dom.traversal.NodeIterator
+   */
+  public int getWhatToShow()
+  {
+
+    // TODO: ??
+    return DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE;
+  }
+
+  /**
+   *  The filter used to screen nodes.  Not used at this time,
+   * this is here only to fullfill the DOM NodeIterator
+   * interface.
+   *
+   * @return Always null.
+   * @see org.w3c.dom.traversal.NodeIterator
+   */
+  public DTMFilter getFilter()
+  {
+    return null;
+  }
+
+  /**
+   * The root node of the Iterator, as specified when it was created.
+   *
+   * @return The "root" of this iterator, which, in XPath terms,
+   * is the node context for this iterator.
+   */
+  public int getRoot()
+  {
+    return m_context;
+  }
+
+  /**
+   *  The value of this flag determines whether the children of entity
+   * reference nodes are visible to the iterator. If false, they will be
+   * skipped over.
+   * <br> To produce a view of the document that has entity references
+   * expanded and does not expose the entity reference node itself, use the
+   * whatToShow flags to hide the entity reference node and set
+   * expandEntityReferences to true when creating the iterator. To produce
+   * a view of the document that has entity reference nodes but no entity
+   * expansion, use the whatToShow flags to show the entity reference node
+   * and set expandEntityReferences to false.
+   *
+   * @return Always true, since entity reference nodes are not
+   * visible in the XPath model.
+   */
+  public boolean getExpandEntityReferences()
+  {
+    return true;
+  }
+  
+  /** Control over whether it is OK for detach to reset the iterator. */
+  protected boolean m_allowDetach = true;
+  
+  /**
+   * Specify if it's OK for detach to release the iterator for reuse.
+   * 
+   * @param allowRelease true if it is OK for detach to release this iterator 
+   * for pooling.
+   */
+  public void allowDetachToRelease(boolean allowRelease)
+  {
+    m_allowDetach = allowRelease;
+  }
+
+  /**
+   *  Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   */
+  public void detach()
+  {    
+    if(m_allowDetach)
+    {
+      // sb: allow reusing of cached nodes when possible?
+      // m_cachedNodes = null;
+      m_execContext = null;
+      // m_prefixResolver = null;  sb: Why would this ever want to be null?
+      m_cdtm = null;
+      m_length = -1;
+      m_pos = 0;
+      m_lastFetched = DTM.NULL;
+      m_context = DTM.NULL;
+      m_currentContextNode = DTM.NULL;
+      
+      m_clones.freeInstance(this);
+    }
+  }
+  
+  /**
+   * Reset the iterator.
+   */
+  public void reset()
+  {
+  	assertion(false, "This iterator can not reset!");
+  }
+
+  /**
+   * Get a cloned Iterator that is reset to the beginning
+   * of the query.
+   *
+   * @return A cloned NodeIterator set of the start of the query.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public DTMIterator cloneWithReset() throws CloneNotSupportedException
+  {
+    LocPathIterator clone;
+//    clone = (LocPathIterator) clone();
+    clone = (LocPathIterator)m_clones.getInstanceOrThrow();
+    clone.m_execContext = m_execContext;
+    clone.m_cdtm = m_cdtm;
+    
+    clone.m_context = m_context;
+    clone.m_currentContextNode = m_currentContextNode;
+    clone.m_stackFrame = m_stackFrame;
+
+    // clone.reset();
+
+    return clone;
+  }
+
+//  /**
+//   * Get a cloned LocPathIterator that holds the same
+//   * position as this iterator.
+//   *
+//   * @return A clone of this iterator that holds the same node position.
+//   *
+//   * @throws CloneNotSupportedException
+//   */
+//  public Object clone() throws CloneNotSupportedException
+//  {
+//
+//    LocPathIterator clone = (LocPathIterator) super.clone();
+//
+//    return clone;
+//  }
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   */
+  public abstract int nextNode();
+
+  /**
+   * Bottleneck the return of a next node, to make returns
+   * easier from nextNode().
+   *
+   * @param nextNode The next node found, may be null.
+   *
+   * @return The same node that was passed as an argument.
+   */
+  protected int returnNextNode(int nextNode)
+  {
+
+    if (DTM.NULL != nextNode)
+    {
+      m_pos++;
+    }
+
+    m_lastFetched = nextNode;
+
+    if (DTM.NULL == nextNode)
+      m_foundLast = true;
+
+    return nextNode;
+  }
+
+  /**
+   * Return the last fetched node.  Needed to support the UnionPathIterator.
+   *
+   * @return The last fetched node, or null if the last fetch was null.
+   */
+  public int getCurrentNode()
+  {
+    return m_lastFetched;
+  }
+
+  /**
+   * If an index is requested, NodeSetDTM will call this method
+   * to run the iterator to the index.  By default this sets
+   * m_next to the index.  If the index argument is -1, this
+   * signals that the iterator should be run to the end.
+   *
+   * @param index The index to run to, or -1 if the iterator
+   * should run to the end.
+   */
+  public void runTo(int index)
+  {
+
+    if (m_foundLast || ((index >= 0) && (index <= getCurrentPos())))
+      return;
+
+    int n;
+
+    if (-1 == index)
+    {
+      while (DTM.NULL != (n = nextNode()));
+    }
+    else
+    {
+      while (DTM.NULL != (n = nextNode()))
+      {
+        if (getCurrentPos() >= index)
+          break;
+      }
+    }
+  }
+
+  /**
+   * Tells if we've found the last node yet.
+   *
+   * @return true if the last nextNode returned null.
+   */
+  public final boolean getFoundLast()
+  {
+    return m_foundLast;
+  }
+
+  /**
+   * The XPath execution context we are operating on.
+   *
+   * @return XPath execution context this iterator is operating on,
+   * or null if setRoot has not been called.
+   */
+  public final XPathContext getXPathContext()
+  {
+    return m_execContext;
+  }
+
+  /**
+   * The node context for the iterator.
+   *
+   * @return The node context, same as getRoot().
+   */
+  public final int getContext()
+  {
+    return m_context;
+  }
+
+  /**
+   * The node context from where the expression is being
+   * executed from (i.e. for current() support).
+   *
+   * @return The top-level node context of the entire expression.
+   */
+  public final int getCurrentContextNode()
+  {
+    return m_currentContextNode;
+  }
+
+  /**
+   * Set the current context node for this iterator.
+   *
+   * @param n Must be a non-null reference to the node context.
+   */
+  public final void setCurrentContextNode(int n)
+  {
+    m_currentContextNode = n;
+  }
+  
+//  /**
+//   * Set the current context node for this iterator.
+//   *
+//   * @param n Must be a non-null reference to the node context.
+//   */
+//  public void setRoot(int n)
+//  {
+//    m_context = n;
+//    m_cdtm = m_execContext.getDTM(n);
+//  }
+
+  /**
+   * Return the saved reference to the prefix resolver that
+   * was in effect when this iterator was created.
+   *
+   * @return The prefix resolver or this iterator, which may be null.
+   */
+  public final PrefixResolver getPrefixResolver()
+  {
+  	if(null == m_prefixResolver)
+  	{
+    	m_prefixResolver = (PrefixResolver)getExpressionOwner();
+  	}
+
+    return m_prefixResolver;
+  }
+        
+//  /**
+//   * Get the analysis pattern built by the WalkerFactory.
+//   *
+//   * @return The analysis pattern built by the WalkerFactory.
+//   */
+//  int getAnalysis()
+//  {
+//    return m_analysis;
+//  }
+
+//  /**
+//   * Set the analysis pattern built by the WalkerFactory.
+//   *
+//   * @param a The analysis pattern built by the WalkerFactory.
+//   */
+//  void setAnalysis(int a)
+//  {
+//    m_analysis = a;
+//  }
+
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	 	if(visitor.visitLocationPath(owner, this))
+  	 	{
+  	 		visitor.visitStep(owner, this);
+  	 		callPredicateVisitors(visitor);
+  	 	}
+  }  
+
+  
+  //============= State Data =============
+  
+  /** 
+   * The pool for cloned iterators.  Iterators need to be cloned
+   * because the hold running state, and thus the original iterator
+   * expression from the stylesheet pool can not be used.          
+   */
+  transient protected IteratorPool m_clones = new IteratorPool(this);
+  
+  /** 
+   * The dtm of the context node.  Careful about using this... it may not 
+   * be the dtm of the current node.
+   */
+  transient protected DTM m_cdtm;
+  
+  /**
+   * The stack frame index for this iterator.
+   */
+  transient int m_stackFrame = -1;
+
+  /**
+   * Value determined at compile time, indicates that this is an
+   * iterator at the top level of the expression, rather than inside
+   * a predicate.
+   * @serial
+   */
+  private boolean m_isTopLevel = false;
+
+  /** The last node that was fetched, usually by nextNode. */
+  transient public int m_lastFetched = DTM.NULL;
+
+  /**
+   * The context node for this iterator, which doesn't change through
+   * the course of the iteration.
+   */
+  transient protected int m_context = DTM.NULL;
+
+  /**
+   * The node context from where the expression is being
+   * executed from (i.e. for current() support).  Different
+   * from m_context in that this is the context for the entire
+   * expression, rather than the context for the subexpression.
+   */
+  transient protected int m_currentContextNode = DTM.NULL;
+  
+  /**
+   * The current position of the context node.
+   */
+  transient protected int m_pos = 0;
+  
+  transient protected int m_length = -1;
+
+  /**
+   * Fast access to the current prefix resolver.  It isn't really
+   * clear that this is needed.
+   * @serial
+   */
+  private PrefixResolver m_prefixResolver;
+
+  /**
+   * The XPathContext reference, needed for execution of many
+   * operations.
+   */
+  transient protected XPathContext m_execContext;
+  
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * 
+   * @return true as a default.
+   */
+  public boolean isDocOrdered()
+  {
+    return true;
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return -1;
+  }
+
+
+//  /**
+//   * The analysis pattern built by the WalkerFactory.
+//   * TODO: Move to LocPathIterator.
+//   * @see org.apache.xpath.axes.WalkerFactory
+//   * @serial
+//   */
+//  protected int m_analysis = 0x00000000;
+  /**
+   * @see PredicatedNodeTest#getLastPos(XPathContext)
+   */
+  public int getLastPos(XPathContext xctxt)
+  {
+    return getLength();
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/MatchPatternIterator.java b/src/main/java/org/apache/xpath/axes/MatchPatternIterator.java
new file mode 100644
index 0000000..2647d82
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/MatchPatternIterator.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: MatchPatternIterator.java 469314 2006-10-30 23:31:59Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisTraverser;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.OpMap;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.patterns.NodeTest;
+import org.apache.xpath.patterns.StepPattern;
+
+/**
+ * This class treats a 
+ * <a href="http://www.w3.org/TR/xpath#location-paths">LocationPath</a> as a 
+ * filtered iteration over the tree, evaluating each node in a super axis 
+ * traversal against the LocationPath interpreted as a match pattern.  This 
+ * class is useful to find nodes in document order that are complex paths 
+ * whose steps probably criss-cross each other.
+ */
+public class MatchPatternIterator extends LocPathIterator
+{
+    static final long serialVersionUID = -5201153767396296474L;
+
+  /** This is the select pattern, translated into a match pattern. */
+  protected StepPattern m_pattern;
+
+  /** The traversal axis from where the nodes will be filtered. */
+  protected int m_superAxis = -1;
+
+  /** The DTM inner traversal class, that corresponds to the super axis. */
+  protected DTMAxisTraverser m_traverser;
+  
+  /** DEBUG flag for diagnostic dumps. */
+  private static final boolean DEBUG = false;
+  
+//  protected int m_nsElemBase = DTM.NULL;
+
+  /**
+   * Create a LocPathIterator object, including creation
+   * of step walkers from the opcode list, and call back
+   * into the Compiler to create predicate expressions.
+   *
+   * @param compiler The Compiler which is creating
+   * this expression.
+   * @param opPos The position of this iterator in the
+   * opcode list from the compiler.
+   * @param analysis Analysis bits that give general information about the 
+   * LocationPath.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  MatchPatternIterator(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+
+    super(compiler, opPos, analysis, false);
+
+    int firstStepPos = OpMap.getFirstChildPos(opPos);
+
+    m_pattern = WalkerFactory.loadSteps(this, compiler, firstStepPos, 0); 
+
+    boolean fromRoot = false;
+    boolean walkBack = false;
+    boolean walkDescendants = false;
+    boolean walkAttributes = false;
+
+    if (0 != (analysis & (WalkerFactory.BIT_ROOT | 
+                          WalkerFactory.BIT_ANY_DESCENDANT_FROM_ROOT)))
+      fromRoot = true;
+      
+    if (0 != (analysis
+              & (WalkerFactory.BIT_ANCESTOR
+                 | WalkerFactory.BIT_ANCESTOR_OR_SELF
+                 | WalkerFactory.BIT_PRECEDING
+                 | WalkerFactory.BIT_PRECEDING_SIBLING 
+                 | WalkerFactory.BIT_FOLLOWING
+                 | WalkerFactory.BIT_FOLLOWING_SIBLING
+                 | WalkerFactory.BIT_PARENT | WalkerFactory.BIT_FILTER)))
+      walkBack = true;
+
+    if (0 != (analysis
+              & (WalkerFactory.BIT_DESCENDANT_OR_SELF
+                 | WalkerFactory.BIT_DESCENDANT
+                 | WalkerFactory.BIT_CHILD)))
+      walkDescendants = true;
+
+    if (0 != (analysis
+              & (WalkerFactory.BIT_ATTRIBUTE | WalkerFactory.BIT_NAMESPACE)))
+      walkAttributes = true;
+      
+    if(false || DEBUG)
+    {
+      System.out.print("analysis: "+Integer.toBinaryString(analysis));
+      System.out.println(", "+WalkerFactory.getAnalysisString(analysis));
+    }
+      
+    if(fromRoot || walkBack)
+    {
+      if(walkAttributes)
+      {
+        m_superAxis = Axis.ALL;
+      }
+      else
+      {
+        m_superAxis = Axis.DESCENDANTSFROMROOT;
+      }
+    }
+    else if(walkDescendants)
+    {
+      if(walkAttributes)
+      {
+        m_superAxis = Axis.ALLFROMNODE;
+      }
+      else
+      {
+        m_superAxis = Axis.DESCENDANTORSELF;
+      }
+    }
+    else
+    {
+      m_superAxis = Axis.ALL;
+    }
+    if(false || DEBUG)
+    {
+      System.out.println("axis: "+Axis.getNames(m_superAxis));
+    }
+    
+  }
+  
+  
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+    super.setRoot(context, environment);
+    m_traverser = m_cdtm.getAxisTraverser(m_superAxis);
+  }
+
+  /**
+   *  Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   */
+  public void detach()
+  {    
+    if(m_allowDetach)
+    {
+      m_traverser = null;
+      
+      // Always call the superclass detach last!
+      super.detach();
+    }
+  }
+  
+  /**
+   * Get the next node via getNextXXX.  Bottlenecked for derived class override.
+   * @return The next node on the axis, or DTM.NULL.
+   */
+  protected int getNextNode()
+  {
+    m_lastFetched = (DTM.NULL == m_lastFetched)
+                     ? m_traverser.first(m_context)
+                     : m_traverser.next(m_context, m_lastFetched);
+    return m_lastFetched;
+  }
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   */
+  public int nextNode()
+  {      
+  	if(m_foundLast)
+  		return DTM.NULL;
+
+    int next;
+    
+    org.apache.xpath.VariableStack vars;
+    int savedStart;
+    if (-1 != m_stackFrame)
+    {
+      vars = m_execContext.getVarStack();
+
+      // These three statements need to be combined into one operation.
+      savedStart = vars.getStackFrame();
+
+      vars.setStackFrame(m_stackFrame);
+    }
+    else
+    {
+      // Yuck.  Just to shut up the compiler!
+      vars = null;
+      savedStart = 0;
+    }
+    
+    try
+    {
+      if(DEBUG)
+        System.out.println("m_pattern"+m_pattern.toString());
+
+      do
+      {
+        next = getNextNode();
+  
+        if (DTM.NULL != next)
+        {
+          if(DTMIterator.FILTER_ACCEPT == acceptNode(next, m_execContext))
+            break;
+          else
+            continue;
+        }
+        else
+          break;
+      }
+      while (next != DTM.NULL);
+      
+      if (DTM.NULL != next)
+      {
+        if(DEBUG)
+        {
+          System.out.println("next: "+next);
+          System.out.println("name: "+m_cdtm.getNodeName(next));
+        }
+        incrementCurrentPos();
+  
+        return next;
+      }
+      else
+      {
+        m_foundLast = true;
+  
+        return DTM.NULL;
+      }
+    }
+    finally
+    {
+      if (-1 != m_stackFrame)
+      {
+        // These two statements need to be combined into one operation.
+        vars.setStackFrame(savedStart);
+      }
+    }
+
+  }
+  
+  /**
+   *  Test whether a specified node is visible in the logical view of a
+   * TreeWalker or NodeIterator. This function will be called by the
+   * implementation of TreeWalker and NodeIterator; it is not intended to
+   * be called directly from user code.
+   * @param n  The node to check to see if it passes the filter or not.
+   * @return  a constant to determine whether the node is accepted,
+   *   rejected, or skipped, as defined  above .
+   */
+  public short acceptNode(int n, XPathContext xctxt)
+  {
+
+    try
+    {
+      xctxt.pushCurrentNode(n);
+      xctxt.pushIteratorRoot(m_context);
+      if(DEBUG)
+      {
+        System.out.println("traverser: "+m_traverser);
+        System.out.print("node: "+n);
+        System.out.println(", "+m_cdtm.getNodeName(n));
+        // if(m_cdtm.getNodeName(n).equals("near-east"))
+        System.out.println("pattern: "+m_pattern.toString());
+        m_pattern.debugWhatToShow(m_pattern.getWhatToShow());
+      }
+      
+      XObject score = m_pattern.execute(xctxt);
+      
+      if(DEBUG)
+      {
+        // System.out.println("analysis: "+Integer.toBinaryString(m_analysis));
+        System.out.println("score: "+score);
+        System.out.println("skip: "+(score == NodeTest.SCORE_NONE));
+      }
+
+      // System.out.println("\n::acceptNode - score: "+score.num()+"::");
+      return (score == NodeTest.SCORE_NONE) ? DTMIterator.FILTER_SKIP 
+                    : DTMIterator.FILTER_ACCEPT;
+    }
+    catch (javax.xml.transform.TransformerException se)
+    {
+
+      // TODO: Fix this.
+      throw new RuntimeException(se.getMessage());
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+      xctxt.popIteratorRoot();
+    }
+
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/NodeSequence.java b/src/main/java/org/apache/xpath/axes/NodeSequence.java
new file mode 100644
index 0000000..9b3e398
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/NodeSequence.java
@@ -0,0 +1,953 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeSequence.java 469367 2006-10-31 04:41:08Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import java.util.Vector;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.utils.NodeVector;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * This class is the dynamic wrapper for a Xalan DTMIterator instance, and 
+ * provides random access capabilities.
+ */
+public class NodeSequence extends XObject
+  implements DTMIterator, Cloneable, PathComponent
+{
+    static final long serialVersionUID = 3866261934726581044L;
+  /** The index of the last node in the iteration. */
+  protected int m_last = -1;
+  
+  /**
+   * The index of the next node to be fetched.  Useful if this
+   * is a cached iterator, and is being used as random access
+   * NodeList.
+   */
+  protected int m_next = 0;
+    
+  /**
+   * A cache of a list of nodes obtained from the iterator so far.
+   * This list is appended to until the iterator is exhausted and
+   * the cache is complete.
+   * <p>
+   * Multiple NodeSequence objects may share the same cache.
+   */
+  private IteratorCache m_cache;
+  
+  /**
+   * If this iterator needs to cache nodes that are fetched, they
+   * are stored in the Vector in the generic object.
+   */
+  protected NodeVector getVector() {
+      NodeVector nv = (m_cache != null) ?  m_cache.getVector() : null;
+      return nv;
+  }
+  
+  /**
+   * Get the cache (if any) of nodes obtained from
+   * the iterator so far. Note that the cache keeps
+   * growing until the iterator is walked to exhaustion,
+   * at which point the cache is "complete".
+   */
+  private IteratorCache getCache() {
+      return m_cache;
+  }
+  
+  /**
+   * Set the vector where nodes will be cached.
+   */
+  protected void SetVector(NodeVector v)
+  {
+  	setObject(v);
+  }
+
+  
+  /**
+   * If the iterator needs to cache nodes as they are fetched,
+   * then this method returns true. 
+   */
+  public boolean hasCache()
+  {
+    final NodeVector nv = getVector();
+  	return (nv != null);
+  }
+  
+  /**
+   * If this NodeSequence has a cache, and that cache is 
+   * fully populated then this method returns true, otherwise
+   * if there is no cache or it is not complete it returns false.
+   */
+  private boolean cacheComplete() {
+      final boolean complete;
+      if (m_cache != null) {
+          complete = m_cache.isComplete();
+      } else {
+          complete = false;
+      }
+      return complete;
+  }
+  
+  /**
+   * If this NodeSequence has a cache, mark that it is complete.
+   * This method should be called after the iterator is exhausted.
+   */
+  private void markCacheComplete() {
+      NodeVector nv = getVector();
+      if (nv != null) {
+          m_cache.setCacheComplete(true);
+      }      
+  }
+
+
+  /**
+   * The functional iterator that fetches nodes.
+   */
+  protected DTMIterator m_iter;
+  
+  /**
+   * Set the functional iterator that fetches nodes.
+   * @param iter The iterator that is to be contained.
+   */
+  public final void setIter(DTMIterator iter)
+  {
+  	m_iter = iter;
+  }
+  
+  /**
+   * Get the functional iterator that fetches nodes.
+   * @return The contained iterator.
+   */
+  public final DTMIterator getContainedIter()
+  {
+  	return m_iter;
+  }
+  
+  /**
+   * The DTMManager to use if we're using a NodeVector only.
+   * We may well want to do away with this, and store it in the NodeVector.
+   */
+  protected DTMManager m_dtmMgr;
+  
+  // ==== Constructors ====
+  
+  /**
+   * Create a new NodeSequence from a (already cloned) iterator.
+   * 
+   * @param iter Cloned (not static) DTMIterator.
+   * @param context The initial context node.
+   * @param xctxt The execution context.
+   * @param shouldCacheNodes True if this sequence can random access.
+   */
+  private NodeSequence(DTMIterator iter, int context, XPathContext xctxt, boolean shouldCacheNodes)
+  {
+  	setIter(iter);
+  	setRoot(context, xctxt);
+  	setShouldCacheNodes(shouldCacheNodes);
+  }
+  
+  /**
+   * Create a new NodeSequence from a (already cloned) iterator.
+   * 
+   * @param nodeVector
+   */
+  public NodeSequence(Object nodeVector)
+  {
+  	super(nodeVector);
+    if (nodeVector instanceof NodeVector) {
+        SetVector((NodeVector) nodeVector);
+    }
+  	if(null != nodeVector)
+  	{
+  		assertion(nodeVector instanceof NodeVector, 
+  			"Must have a NodeVector as the object for NodeSequence!");
+  		if(nodeVector instanceof DTMIterator)
+  		{
+  			setIter((DTMIterator)nodeVector);
+  			m_last = ((DTMIterator)nodeVector).getLength();
+  		}
+  		
+  	}
+  }
+  
+  /**
+   * Construct an empty XNodeSet object.  This is used to create a mutable 
+   * nodeset to which random nodes may be added.
+   */
+  private NodeSequence(DTMManager dtmMgr)
+  {
+    super(new NodeVector());
+    m_last = 0;
+    m_dtmMgr = dtmMgr;
+  }
+
+  
+  /**
+   * Create a new NodeSequence in an invalid (null) state.
+   */
+  public NodeSequence()
+  {
+      return;
+  }
+
+
+  /**
+   * @see DTMIterator#getDTM(int)
+   */
+  public DTM getDTM(int nodeHandle)
+  {
+  	DTMManager mgr = getDTMManager();
+  	if(null != mgr)
+    	return getDTMManager().getDTM(nodeHandle);
+    else
+    {
+    	assertion(false, "Can not get a DTM Unless a DTMManager has been set!");
+    	return null;
+    }
+  }
+
+  /**
+   * @see DTMIterator#getDTMManager()
+   */
+  public DTMManager getDTMManager()
+  {
+    return m_dtmMgr;
+  }
+
+  /**
+   * @see DTMIterator#getRoot()
+   */
+  public int getRoot()
+  {
+  	if(null != m_iter)
+    	return m_iter.getRoot();
+  	else
+  	{
+  		// NodeSetDTM will call this, and so it's not a good thing to throw 
+  		// an assertion here.
+  		// assertion(false, "Can not get the root from a non-iterated NodeSequence!");
+  		return DTM.NULL;
+  	}
+  }
+
+  /**
+   * @see DTMIterator#setRoot(int, Object)
+   */
+  public void setRoot(int nodeHandle, Object environment)
+  {
+  	if(null != m_iter)
+  	{
+  		XPathContext xctxt = (XPathContext)environment;
+  		m_dtmMgr = xctxt.getDTMManager();
+  		m_iter.setRoot(nodeHandle, environment);
+  		if(!m_iter.isDocOrdered())
+  		{
+  			if(!hasCache())
+  				setShouldCacheNodes(true);
+  			runTo(-1);
+  			m_next=0;
+  		}
+  	}
+  	else
+  		assertion(false, "Can not setRoot on a non-iterated NodeSequence!");
+  }
+
+  /**
+   * @see DTMIterator#reset()
+   */
+  public void reset()
+  {
+  	m_next = 0;
+  	// not resetting the iterator on purpose!!!
+  }
+
+  /**
+   * @see DTMIterator#getWhatToShow()
+   */
+  public int getWhatToShow()
+  {
+    return hasCache() ? (DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE) 
+    	: m_iter.getWhatToShow();
+  }
+
+  /**
+   * @see DTMIterator#getExpandEntityReferences()
+   */
+  public boolean getExpandEntityReferences()
+  {
+  	if(null != m_iter)
+  		return m_iter.getExpandEntityReferences();
+  	else
+    	return true;
+  }
+
+  /**
+   * @see DTMIterator#nextNode()
+   */
+  public int nextNode()
+  {
+    // If the cache is on, and the node has already been found, then 
+    // just return from the list.
+    NodeVector vec = getVector();
+    if (null != vec)
+    {	
+        // There is a cache
+    	if(m_next < vec.size())
+    	{
+            // The node is in the cache, so just return it.
+			int next = vec.elementAt(m_next);
+	    	m_next++;
+	    	return next;
+    	}
+    	else if(cacheComplete() || (-1 != m_last) || (null == m_iter))
+    	{
+    		m_next++;
+    		return DTM.NULL;
+    	}
+    }
+    
+  if (null == m_iter)
+    return DTM.NULL;
+  
+ 	int next = m_iter.nextNode();
+    if(DTM.NULL != next)
+    {
+    	if(hasCache())
+    	{
+    		if(m_iter.isDocOrdered())
+    	    {
+    			getVector().addElement(next);
+    			m_next++;
+    		}
+    		else
+    		{
+    			int insertIndex = addNodeInDocOrder(next);
+    			if(insertIndex >= 0)
+    				m_next++;
+    		}
+    	}
+    	else
+    		m_next++;
+    }
+    else
+    {
+        // We have exhausted the iterator, and if there is a cache
+        // it must have all nodes in it by now, so let the cache
+        // know that it is complete.
+        markCacheComplete();
+        
+    	m_last = m_next;
+    	m_next++;
+    }
+    	
+    return next;
+  }
+
+  /**
+   * @see DTMIterator#previousNode()
+   */
+  public int previousNode()
+  {
+  	if(hasCache())
+  	{
+  		if(m_next <= 0)
+  			return DTM.NULL;
+  		else
+  		{
+  			m_next--;
+  			return item(m_next);
+  		}
+  	}
+  	else
+  	{
+	    int n = m_iter.previousNode();
+	    m_next = m_iter.getCurrentPos();
+	    return m_next;
+  	}
+  }
+
+  /**
+   * @see DTMIterator#detach()
+   */
+  public void detach()
+  {
+  	if(null != m_iter)
+  		m_iter.detach();
+  	super.detach();
+  }
+
+  /**
+   * Calling this with a value of false will cause the nodeset 
+   * to be cached.
+   * @see DTMIterator#allowDetachToRelease(boolean)
+   */
+  public void allowDetachToRelease(boolean allowRelease)
+  {
+  	if((false == allowRelease) && !hasCache())
+  	{
+  		setShouldCacheNodes(true);
+  	}
+  	
+  	if(null != m_iter)
+  		m_iter.allowDetachToRelease(allowRelease);
+  	super.allowDetachToRelease(allowRelease);
+  }
+
+  /**
+   * @see DTMIterator#getCurrentNode()
+   */
+  public int getCurrentNode()
+  {
+  	if(hasCache())
+  	{
+  		int currentIndex = m_next-1;
+  		NodeVector vec = getVector();
+  		if((currentIndex >= 0) && (currentIndex < vec.size()))
+  			return vec.elementAt(currentIndex);
+  		else
+  			return DTM.NULL;
+  	}
+  	
+  	if(null != m_iter)
+  	{
+    	return m_iter.getCurrentNode();
+  	}
+  	else
+  		return DTM.NULL;
+  }
+
+  /**
+   * @see DTMIterator#isFresh()
+   */
+  public boolean isFresh()
+  {
+    return (0 == m_next);
+  }
+
+  /**
+   * @see DTMIterator#setShouldCacheNodes(boolean)
+   */
+  public void setShouldCacheNodes(boolean b)
+  {
+    if (b)
+    {
+      if(!hasCache())
+      {
+        SetVector(new NodeVector());
+      }
+//	  else
+//	    getVector().RemoveAllNoClear();  // Is this good?
+    }
+    else
+      SetVector(null);
+  }
+
+  /**
+   * @see DTMIterator#isMutable()
+   */
+  public boolean isMutable()
+  {
+    return hasCache(); // though may be surprising if it also has an iterator!
+  }
+
+  /**
+   * @see DTMIterator#getCurrentPos()
+   */
+  public int getCurrentPos()
+  {
+    return m_next;
+  }
+
+  /**
+   * @see DTMIterator#runTo(int)
+   */
+  public void runTo(int index)
+  {
+    int n;
+    
+    if (-1 == index)
+    {
+      int pos = m_next;
+      while (DTM.NULL != (n = nextNode()));
+      m_next = pos;
+    }
+    else if(m_next == index)
+    {
+      return;
+    }
+    else if(hasCache() && m_next < getVector().size())
+    {
+      m_next = index;
+    }
+    else if((null == getVector()) && (index < m_next))
+    {
+      while ((m_next >= index) && DTM.NULL != (n = previousNode()));
+    }
+    else
+    {   
+      while ((m_next < index) && DTM.NULL != (n = nextNode()));
+    }
+    
+  }
+
+  /**
+   * @see DTMIterator#setCurrentPos(int)
+   */
+  public void setCurrentPos(int i)
+  {
+  	runTo(i);
+  }
+
+  /**
+   * @see DTMIterator#item(int)
+   */
+  public int item(int index)
+  {
+  	setCurrentPos(index);
+  	int n = nextNode();
+  	m_next = index;
+  	return n;
+  }
+
+  /**
+   * @see DTMIterator#setItem(int, int)
+   */
+  public void setItem(int node, int index)
+  {
+  	NodeVector vec = getVector();
+  	if(null != vec)
+  	{
+        int oldNode = vec.elementAt(index);
+        if (oldNode != node && m_cache.useCount() > 1) {
+            /* If we are going to set the node at the given index
+             * to a different value, and the cache is shared
+             * (has a use count greater than 1)
+             * then make a copy of the cache and use it
+             * so we don't overwrite the value for other
+             * users of the cache.
+             */
+            IteratorCache newCache = new IteratorCache();
+            final NodeVector nv;
+            try {
+                nv = (NodeVector) vec.clone();
+            } catch (CloneNotSupportedException e) {
+                // This should never happen
+                e.printStackTrace();
+                RuntimeException rte = new RuntimeException(e.getMessage()); 
+                throw rte;
+            }
+            newCache.setVector(nv);
+            newCache.setCacheComplete(true);
+            m_cache = newCache;
+            vec = nv;
+            
+            // Keep our superclass informed of the current NodeVector
+            super.setObject(nv); 
+            
+            /* When we get to here the new cache has
+             * a use count of 1 and when setting a
+             * bunch of values on the same NodeSequence,
+             * such as when sorting, we will keep setting
+             * values in that same copy which has a use count of 1.
+             */
+        }
+  		vec.setElementAt(node, index);
+  		m_last = vec.size();
+  	}
+  	else
+  		m_iter.setItem(node, index);
+  }
+
+  /**
+   * @see DTMIterator#getLength()
+   */
+  public int getLength()
+  {
+    IteratorCache cache = getCache();
+    
+  	if(cache != null)
+  	{
+        // Nodes from the iterator are cached
+        if (cache.isComplete()) {
+            // All of the nodes from the iterator are cached
+            // so just return the number of nodes in the cache
+            NodeVector nv = cache.getVector();
+            return nv.size();
+        }
+        
+        // If this NodeSequence wraps a mutable nodeset, then
+        // m_last will not reflect the size of the nodeset if
+        // it has been mutated...
+        if (m_iter instanceof NodeSetDTM)
+        {
+            return m_iter.getLength();
+        }    
+        
+	  	if(-1 == m_last)
+	  	{
+	  		int pos = m_next;
+	  		runTo(-1);
+	  		m_next = pos;
+	  	}
+	    return m_last;
+  	}
+  	else
+  	{
+  		return (-1 == m_last) ? (m_last = m_iter.getLength()) : m_last;
+  	}
+  }
+
+  /**
+   * Note: Not a deep clone.
+   * @see DTMIterator#cloneWithReset()
+   */
+  public DTMIterator cloneWithReset() throws CloneNotSupportedException
+  {
+  	NodeSequence seq = (NodeSequence)super.clone();
+    seq.m_next = 0;
+    if (m_cache != null) {
+        // In making this clone of an iterator we are making
+        // another NodeSequence object it has a reference
+        // to the same IteratorCache object as the original
+        // so we need to remember that more than one
+        // NodeSequence object shares the cache.
+        m_cache.increaseUseCount();
+    }
+    
+    return seq;
+  }
+  
+  /**
+   * Get a clone of this iterator, but don't reset the iteration in the 
+   * process, so that it may be used from the current position.
+   * Note: Not a deep clone.
+   *
+   * @return A clone of this object.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+          NodeSequence clone = (NodeSequence) super.clone();
+          if (null != m_iter) clone.m_iter = (DTMIterator) m_iter.clone();
+          if (m_cache != null) {
+              // In making this clone of an iterator we are making
+              // another NodeSequence object it has a reference
+              // to the same IteratorCache object as the original
+              // so we need to remember that more than one
+              // NodeSequence object shares the cache.
+              m_cache.increaseUseCount();
+          }
+          
+          return clone;
+  }
+
+
+  /**
+   * @see DTMIterator#isDocOrdered()
+   */
+  public boolean isDocOrdered()
+  {
+  	if(null != m_iter)
+  		return m_iter.isDocOrdered();
+  	else
+    	return true; // can't be sure?
+  }
+
+  /**
+   * @see DTMIterator#getAxis()
+   */
+  public int getAxis()
+  {
+  	if(null != m_iter)
+    	return m_iter.getAxis();
+    else
+    {
+    	assertion(false, "Can not getAxis from a non-iterated node sequence!");
+    	return 0;
+    }
+  }
+
+  /**
+   * @see PathComponent#getAnalysisBits()
+   */
+  public int getAnalysisBits()
+  {
+  	if((null != m_iter) && (m_iter instanceof PathComponent))
+    	return ((PathComponent)m_iter).getAnalysisBits();
+    else
+    	return 0;
+  }
+
+  /**
+   * @see org.apache.xpath.Expression#fixupVariables(Vector, int)
+   */
+  public void fixupVariables(Vector vars, int globalsSize)
+  {
+  	super.fixupVariables(vars, globalsSize);
+  }  
+  
+  /**
+   * Add the node into a vector of nodes where it should occur in
+   * document order.
+   * @param node The node to be added.
+   * @return insertIndex.
+   * @throws RuntimeException thrown if this NodeSetDTM is not of 
+   * a mutable type.
+   */
+   protected int addNodeInDocOrder(int node)
+   {
+      assertion(hasCache(), "addNodeInDocOrder must be done on a mutable sequence!");
+
+      int insertIndex = -1;
+      
+      NodeVector vec = getVector();
+
+      // This needs to do a binary search, but a binary search 
+      // is somewhat tough because the sequence test involves 
+      // two nodes.
+      int size = vec.size(), i;
+
+      for (i = size - 1; i >= 0; i--)
+      {
+        int child = vec.elementAt(i);
+
+        if (child == node)
+        {
+          i = -2; // Duplicate, suppress insert
+
+          break;
+        }
+
+        DTM dtm = m_dtmMgr.getDTM(node);
+        if (!dtm.isNodeAfter(node, child))
+        {
+          break;
+        }
+      }
+
+      if (i != -2)
+      {
+        insertIndex = i + 1;
+
+        vec.insertElementAt(node, insertIndex);
+      }
+
+      // checkDups();
+      return insertIndex;
+    } // end addNodeInDocOrder(Vector v, Object obj)
+   
+   /**
+    * It used to be that many locations in the code simply
+    * did an assignment to this.m_obj directly, rather than
+    * calling the setObject(Object) method. The problem is
+    * that our super-class would be updated on what the 
+    * cache associated with this NodeSequence, but
+    * we wouldn't know ourselves.
+    * <p>
+    * All setting of m_obj is done through setObject() now,
+    * and this method over-rides the super-class method.
+    * So now we are in the loop have an opportunity
+    * to update some caching information.
+    *
+    */
+   protected void setObject(Object obj) {
+       if (obj instanceof NodeVector) {
+           // Keep our superclass informed of the current NodeVector
+           // ... if we don't the smoketest fails (don't know why).
+           super.setObject(obj);
+           
+           // A copy of the code of what SetVector() would do.
+           NodeVector v = (NodeVector)obj;
+           if (m_cache != null) {
+               m_cache.setVector(v);
+           } else if (v!=null) {
+               m_cache = new IteratorCache();
+               m_cache.setVector(v);
+           }
+       } else if (obj instanceof IteratorCache) {
+           IteratorCache cache = (IteratorCache) obj;
+           m_cache = cache;
+           m_cache.increaseUseCount();
+           
+           // Keep our superclass informed of the current NodeVector
+           super.setObject(cache.getVector());
+       } else {
+           super.setObject(obj);
+       }
+       
+   }
+
+   /**
+    * Each NodeSequence object has an iterator which is "walked".
+    * As an iterator is walked one obtains nodes from it.
+    * As those nodes are obtained they may be cached, making
+    * the next walking of a copy or clone of the iterator faster.
+    * This field (m_cache) is a reference to such a cache, 
+    * which is populated as the iterator is walked.
+    * <p>
+    * Note that multiple NodeSequence objects may hold a 
+    * reference to the same cache, and also 
+    * (and this is important) the same iterator.
+    * The iterator and its cache may be shared among 
+    * many NodeSequence objects.
+    * <p>
+    * If one of the NodeSequence objects walks ahead
+    * of the others it fills in the cache.
+    * As the others NodeSequence objects catch up they
+    * get their values from
+    * the cache rather than the iterator itself, so
+    * the iterator is only ever walked once and everyone
+    * benefits from the cache.
+    * <p>
+    * At some point the cache may be
+    * complete due to walking to the end of one of
+    * the copies of the iterator, and the cache is
+    * then marked as "complete".
+    * and the cache will have no more nodes added to it.
+    * <p>
+    * Its use-count is the number of NodeSequence objects that use it.
+    */
+   private final static class IteratorCache {
+       /**
+        * A list of nodes already obtained from the iterator.
+        * As the iterator is walked the nodes obtained from
+        * it are appended to this list.
+        * <p>
+        * Both an iterator and its corresponding cache can
+        * be shared by multiple NodeSequence objects.
+        * <p>
+        * For example, consider three NodeSequence objects
+        * ns1, ns2 and ns3 doing such sharing, and the
+        * nodes to be obtaind from the iterator being 
+        * the sequence { 33, 11, 44, 22, 55 }.
+        * <p>
+        * If ns3.nextNode() is called 3 times the the
+        * underlying iterator will have walked through
+        * 33, 11, 55 and these three nodes will have been put
+        * in the cache.
+        * <p>
+        * If ns2.nextNode() is called 2 times it will return
+        * 33 and 11 from the cache, leaving the iterator alone.
+        * <p>
+        * If ns1.nextNode() is called 6 times it will return
+        * 33 and 11 from the cache, then get 44, 22, 55 from
+        * the iterator, and appending 44, 22, 55 to the cache.
+        * On the sixth call it is found that the iterator is
+        * exhausted and the cache is marked complete.
+        * <p>
+        * Should ns2 or ns3 have nextNode() called they will
+        * know that the cache is complete, and they will
+        * obtain all subsequent nodes from the cache.
+        * <p>
+        * Note that the underlying iterator, though shared
+        * is only ever walked once. 
+        */
+        private NodeVector m_vec2;
+
+        /**
+         * true if the associated iterator is exhausted and
+         * all nodes obtained from it are in the cache.
+         */
+        private boolean m_isComplete2;
+
+        private int m_useCount2;
+
+        IteratorCache() {
+            m_vec2 = null;
+            m_isComplete2 = false;
+            m_useCount2 = 1;
+            return;
+        }
+
+        /**
+         * Returns count of how many NodeSequence objects share this
+         * IteratorCache object.
+         */
+        private int useCount() {
+            return m_useCount2;
+        }
+
+        /**
+         * This method is called when yet another
+         * NodeSequence object uses, or shares
+         * this same cache.
+         *
+         */
+        private void increaseUseCount() {
+            if (m_vec2 != null)
+                m_useCount2++;
+
+        }
+
+        /**
+         * Sets the NodeVector that holds the
+         * growing list of nodes as they are appended
+         * to the cached list.
+         */
+        private void setVector(NodeVector nv) {
+            m_vec2 = nv;
+            m_useCount2 = 1;
+        }
+
+        /**
+         * Get the cached list of nodes obtained from
+         * the iterator so far.
+         */
+        private NodeVector getVector() {
+            return m_vec2;
+        }
+
+        /**
+         * Call this method with 'true' if the
+         * iterator is exhausted and the cached list
+         * is complete, or no longer growing.
+         */
+        private void setCacheComplete(boolean b) {
+            m_isComplete2 = b;
+
+        }
+
+        /**
+         * Returns true if no cache is complete
+         * and immutable.
+         */
+        private boolean isComplete() {
+            return m_isComplete2;
+        }
+    }
+   
+    /**
+     * Get the cached list of nodes appended with
+     * values obtained from the iterator as
+     * a NodeSequence is walked when its
+     * nextNode() method is called.
+     */
+    protected IteratorCache getIteratorCache() {
+        return m_cache;
+    }
+}
+
diff --git a/src/main/java/org/apache/xpath/axes/OneStepIterator.java b/src/main/java/org/apache/xpath/axes/OneStepIterator.java
new file mode 100644
index 0000000..43ae094
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/OneStepIterator.java
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: OneStepIterator.java 469314 2006-10-30 23:31:59Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisIterator;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.OpMap;
+
+/**
+ * This class implements a general iterator for
+ * those LocationSteps with only one step, and perhaps a predicate.
+ * @see org.apache.xpath.axes#LocPathIterator
+ * @xsl.usage advanced
+ */
+public class OneStepIterator extends ChildTestIterator
+{
+    static final long serialVersionUID = 4623710779664998283L;
+  /** The traversal axis from where the nodes will be filtered. */
+  protected int m_axis = -1;
+
+  /** The DTM inner traversal class, that corresponds to the super axis. */
+  protected DTMAxisIterator m_iterator;
+
+  /**
+   * Create a OneStepIterator object.
+   *
+   * @param compiler A reference to the Compiler that contains the op map.
+   * @param opPos The position within the op map, which contains the
+   * location path expression for this itterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  OneStepIterator(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis);
+    int firstStepPos = OpMap.getFirstChildPos(opPos);
+    
+    m_axis = WalkerFactory.getAxisFromStep(compiler, firstStepPos);
+    
+  }
+  
+  
+  /**
+   * Create a OneStepIterator object.
+   *
+   * @param iterator The DTM iterator which this iterator will use.
+   * @param axis One of Axis.Child, etc., or -1 if the axis is unknown.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public OneStepIterator(DTMAxisIterator iterator, int axis)
+          throws javax.xml.transform.TransformerException
+  {
+    super(null);
+    
+    m_iterator = iterator;
+    m_axis = axis;
+    int whatToShow = DTMFilter.SHOW_ALL;
+    initNodeTest(whatToShow);
+  }
+  
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+    super.setRoot(context, environment);
+    if(m_axis > -1)
+      m_iterator = m_cdtm.getAxisIterator(m_axis);
+    m_iterator.setStartNode(m_context);
+  }
+
+  /**
+   *  Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   */
+  public void detach()
+  {    
+    if(m_allowDetach)
+    {
+      if(m_axis > -1)
+        m_iterator = null;
+      
+      // Always call the superclass detach last!
+      super.detach();
+    }
+  }
+  
+  /**
+   * Get the next node via getFirstAttribute && getNextAttribute.
+   */
+  protected int getNextNode()
+  {
+    return m_lastFetched = m_iterator.next();
+  }
+  
+  /**
+   * Get a cloned iterator.
+   *
+   * @return A new iterator that can be used without mutating this one.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+    // Do not access the location path itterator during this operation!
+    
+    OneStepIterator clone = (OneStepIterator) super.clone();
+
+    if(m_iterator != null)
+    {
+      clone.m_iterator = m_iterator.cloneIterator();
+    }
+    return clone;
+  }
+  
+  /**
+   *  Get a cloned Iterator that is reset to the beginning
+   *  of the query.
+   * 
+   *  @return A cloned NodeIterator set of the start of the query.
+   * 
+   *  @throws CloneNotSupportedException
+   */
+  public DTMIterator cloneWithReset() throws CloneNotSupportedException
+  {
+
+    OneStepIterator clone = (OneStepIterator) super.cloneWithReset();
+    clone.m_iterator = m_iterator;
+
+    return clone;
+  }
+
+
+
+  /**
+   * Tells if this is a reverse axes.  Overrides AxesWalker#isReverseAxes.
+   *
+   * @return true for this class.
+   */
+  public boolean isReverseAxes()
+  {
+    return m_iterator.isReverse();
+  }
+
+  /**
+   * Get the current sub-context position.  In order to do the
+   * reverse axes count, for the moment this re-searches the axes
+   * up to the predicate.  An optimization on this is to cache
+   * the nodes searched, but, for the moment, this case is probably
+   * rare enough that the added complexity isn't worth it.
+   *
+   * @param predicateIndex The predicate index of the proximity position.
+   *
+   * @return The pridicate index, or -1.
+   */
+  protected int getProximityPosition(int predicateIndex)
+  {
+    if(!isReverseAxes())
+      return super.getProximityPosition(predicateIndex);
+      
+    // A negative predicate index seems to occur with
+    // (preceding-sibling::*|following-sibling::*)/ancestor::*[position()]/*[position()]
+    // -sb
+    if(predicateIndex < 0)
+      return -1;
+      
+    if (m_proximityPositions[predicateIndex] <= 0)
+    {
+      XPathContext xctxt = getXPathContext();
+      try
+      {
+        OneStepIterator clone = (OneStepIterator) this.clone();
+        
+        int root = getRoot();
+        xctxt.pushCurrentNode(root);
+        clone.setRoot(root, xctxt);
+
+        // clone.setPredicateCount(predicateIndex);
+        clone.m_predCount = predicateIndex;
+
+        // Count 'em all
+        int count = 1;
+        int next;
+
+        while (DTM.NULL != (next = clone.nextNode()))
+        {
+          count++;
+        }
+
+        m_proximityPositions[predicateIndex] += count;
+      }
+      catch (CloneNotSupportedException cnse)
+      {
+
+        // can't happen
+      }
+      finally
+      {
+        xctxt.popCurrentNode();
+      }
+    }
+
+    return m_proximityPositions[predicateIndex];
+  }
+
+  /**
+   *  The number of nodes in the list. The range of valid child node indices
+   * is 0 to <code>length-1</code> inclusive.
+   *
+   * @return The number of nodes in the list, always greater or equal to zero.
+   */
+  public int getLength()
+  {
+    if(!isReverseAxes())
+      return super.getLength();
+      
+    // Tell if this is being called from within a predicate.
+    boolean isPredicateTest = (this == m_execContext.getSubContextList());
+
+    // And get how many total predicates are part of this step.
+    int predCount = getPredicateCount();
+   
+    // If we have already calculated the length, and the current predicate 
+    // is the first predicate, then return the length.  We don't cache 
+    // the anything but the length of the list to the first predicate.
+    if (-1 != m_length && isPredicateTest && m_predicateIndex < 1)
+       return m_length;      
+
+    int count = 0;
+    
+    XPathContext xctxt = getXPathContext();
+    try
+    {
+      OneStepIterator clone = (OneStepIterator) this.cloneWithReset();
+      
+      int root = getRoot();
+      xctxt.pushCurrentNode(root);
+      clone.setRoot(root, xctxt);
+ 
+      clone.m_predCount = m_predicateIndex;
+
+      int next;
+
+      while (DTM.NULL != (next = clone.nextNode()))
+      {
+        count++;
+      }
+    }
+    catch (CloneNotSupportedException cnse)
+    {
+       // can't happen
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+    }
+    if (isPredicateTest && m_predicateIndex < 1)
+      m_length = count;    
+      
+    return count;
+  }
+
+  /**
+   * Count backwards one proximity position.
+   *
+   * @param i The predicate index.
+   */
+  protected void countProximityPosition(int i)
+  {
+    if(!isReverseAxes())
+      super.countProximityPosition(i);
+    else if (i < m_proximityPositions.length)
+      m_proximityPositions[i]--;
+  }
+  
+  /**
+   * Reset the iterator.
+   */
+  public void reset()
+  {
+
+    super.reset();
+    if(null != m_iterator)
+      m_iterator.reset();
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return m_axis;
+  }
+  
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!super.deepEquals(expr))
+  		return false;
+  		
+  	if(m_axis != ((OneStepIterator)expr).m_axis)
+  		return false;
+  		
+  	return true;
+  }
+
+  
+}
diff --git a/src/main/java/org/apache/xpath/axes/OneStepIteratorForward.java b/src/main/java/org/apache/xpath/axes/OneStepIteratorForward.java
new file mode 100644
index 0000000..854da45
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/OneStepIteratorForward.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: OneStepIteratorForward.java 469314 2006-10-30 23:31:59Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xpath.Expression;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.OpMap;
+
+/**
+ * This class implements a general iterator for
+ * those LocationSteps with only one step, and perhaps a predicate, 
+ * that only go forward (i.e. it can not be used with ancestors, 
+ * preceding, etc.)
+ * @see org.apache.xpath.axes#ChildTestIterator
+ * @xsl.usage advanced
+ */
+public class OneStepIteratorForward extends ChildTestIterator
+{
+    static final long serialVersionUID = -1576936606178190566L;
+  /** The traversal axis from where the nodes will be filtered. */
+  protected int m_axis = -1;
+
+  /**
+   * Create a OneStepIterator object.
+   *
+   * @param compiler A reference to the Compiler that contains the op map.
+   * @param opPos The position within the op map, which contains the
+   * location path expression for this itterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  OneStepIteratorForward(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis);
+    int firstStepPos = OpMap.getFirstChildPos(opPos);
+    
+    m_axis = WalkerFactory.getAxisFromStep(compiler, firstStepPos);
+    
+  }
+    
+  /**
+   * Create a OneStepIterator object that will just traverse the self axes.
+   * 
+   * @param axis One of the org.apache.xml.dtm.Axis integers.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public OneStepIteratorForward(int axis)
+  {
+    super(null);
+    
+    m_axis = axis;
+    int whatToShow = DTMFilter.SHOW_ALL;
+    initNodeTest(whatToShow);
+  }
+
+  
+
+  
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+    super.setRoot(context, environment);
+    m_traverser = m_cdtm.getAxisTraverser(m_axis);
+    
+  }
+  
+//  /**
+//   * Return the first node out of the nodeset, if this expression is 
+//   * a nodeset expression.  This is the default implementation for 
+//   * nodesets.
+//   * <p>WARNING: Do not mutate this class from this function!</p>
+//   * @param xctxt The XPath runtime context.
+//   * @return the first node out of the nodeset, or DTM.NULL.
+//   */
+//  public int asNode(XPathContext xctxt)
+//    throws javax.xml.transform.TransformerException
+//  {
+//    if(getPredicateCount() > 0)
+//      return super.asNode(xctxt);
+//      
+//    int current = xctxt.getCurrentNode();
+//    
+//    DTM dtm = xctxt.getDTM(current);
+//    DTMAxisTraverser traverser = dtm.getAxisTraverser(m_axis);
+//    
+//    String localName = getLocalName();
+//    String namespace = getNamespace();
+//    int what = m_whatToShow;
+//    
+//    // System.out.println("what: ");
+//    // NodeTest.debugWhatToShow(what);
+//    if(DTMFilter.SHOW_ALL == what
+//       || ((DTMFilter.SHOW_ELEMENT & what) == 0)
+//       || localName == NodeTest.WILD
+//       || namespace == NodeTest.WILD)
+//    {
+//      return traverser.first(current);
+//    }
+//    else
+//    {
+//      int type = getNodeTypeTest(what);
+//      int extendedType = dtm.getExpandedTypeID(namespace, localName, type);
+//      return traverser.first(current, extendedType);
+//    }
+//  }
+  
+  /**
+   * Get the next node via getFirstAttribute && getNextAttribute.
+   */
+  protected int getNextNode()
+  {
+    m_lastFetched = (DTM.NULL == m_lastFetched)
+                     ? m_traverser.first(m_context)
+                     : m_traverser.next(m_context, m_lastFetched);
+    return m_lastFetched;
+  }
+  
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    return m_axis;
+  }
+
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!super.deepEquals(expr))
+  		return false;
+  		
+  	if(m_axis != ((OneStepIteratorForward)expr).m_axis)
+  		return false;
+  		
+  	return true;
+  }
+
+  
+}
diff --git a/src/main/java/org/apache/xpath/axes/PathComponent.java b/src/main/java/org/apache/xpath/axes/PathComponent.java
new file mode 100644
index 0000000..c7a2b85
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/PathComponent.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: PathComponent.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+/**
+ * Classes who implement this information provide information needed for 
+ * static analysis of a path component.
+ */
+public interface PathComponent
+{
+  /** 
+   * Get the analysis bits for this path component, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits();
+
+}
+
diff --git a/src/main/java/org/apache/xpath/axes/PredicatedNodeTest.java b/src/main/java/org/apache/xpath/axes/PredicatedNodeTest.java
new file mode 100644
index 0000000..f72ea3a
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/PredicatedNodeTest.java
@@ -0,0 +1,647 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: PredicatedNodeTest.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.patterns.NodeTest;
+
+public abstract class PredicatedNodeTest extends NodeTest implements SubContextList
+{
+    static final long serialVersionUID = -6193530757296377351L;
+
+  /**
+   * Construct an AxesWalker using a LocPathIterator.
+   *
+   * @param locPathIterator non-null reference to the parent iterator.
+   */
+  PredicatedNodeTest(LocPathIterator locPathIterator)
+  {
+    m_lpi = locPathIterator;
+  }
+  
+  /**
+   * Construct an AxesWalker.  The location path iterator will have to be set
+   * before use.
+   */
+  PredicatedNodeTest()
+  {
+  }
+  
+  /**
+   * Read the object from a serialization stream.
+   *
+   * @param stream Input stream to read from
+   *
+   * @throws java.io.IOException
+   * @throws javax.xml.transform.TransformerException
+   */
+  private void readObject(java.io.ObjectInputStream stream)
+          throws java.io.IOException, javax.xml.transform.TransformerException
+  {
+    try
+    {
+      stream.defaultReadObject();
+      m_predicateIndex = -1;
+      resetProximityPositions();
+    }
+    catch (ClassNotFoundException cnfe)
+    {
+      throw new javax.xml.transform.TransformerException(cnfe);
+    }
+  }
+  
+  /**
+   * Get a cloned PrdicatedNodeTest.
+   *
+   * @return A new PredicatedNodeTest that can be used without mutating this one.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+    // Do not access the location path itterator during this operation!
+    
+    PredicatedNodeTest clone = (PredicatedNodeTest) super.clone();
+
+    if ((null != this.m_proximityPositions)
+            && (this.m_proximityPositions == clone.m_proximityPositions))
+    {
+      clone.m_proximityPositions = new int[this.m_proximityPositions.length];
+
+      System.arraycopy(this.m_proximityPositions, 0,
+                       clone.m_proximityPositions, 0,
+                       this.m_proximityPositions.length);
+    }
+    
+    if(clone.m_lpi == this)
+      clone.m_lpi = (LocPathIterator)clone;
+
+    return clone;
+  }
+  
+  // Only for clones for findLastPos.  See bug4638.
+  protected int m_predCount = -1;
+
+  /**
+   * Get the number of predicates that this walker has.
+   *
+   * @return the number of predicates that this walker has.
+   */
+  public int getPredicateCount()
+  {
+    if(-1 == m_predCount)
+      return (null == m_predicates) ? 0 : m_predicates.length;
+    else
+      return m_predCount;
+  }
+
+  /**
+   * Set the number of predicates that this walker has.  This does more 
+   * that one would think, as it creates a new predicate array of the 
+   * size of the count argument, and copies count predicates into the new 
+   * one from the old, and then reassigns the predicates value.  All this 
+   * to keep from having to have a predicate count value.
+   *
+   * @param count The number of predicates, which must be equal or less 
+   *               than the existing count.
+   */
+  public void setPredicateCount(int count)
+  {
+    if(count > 0)
+    {
+      Expression[] newPredicates = new Expression[count];
+      for (int i = 0; i < count; i++) 
+      {
+        newPredicates[i] = m_predicates[i];
+      }
+      m_predicates = newPredicates;
+    }
+    else
+      m_predicates = null;
+    
+  }
+
+  /**
+   * Init predicate info.
+   *
+   * @param compiler The Compiler object that has information about this 
+   *                 walker in the op map.
+   * @param opPos The op code position of this location step.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void initPredicateInfo(Compiler compiler, int opPos)
+          throws javax.xml.transform.TransformerException
+  {
+
+    int pos = compiler.getFirstPredicateOpPos(opPos);
+
+    if(pos > 0)
+    {
+      m_predicates = compiler.getCompiledPredicates(pos);
+      if(null != m_predicates)
+      {
+      	for(int i = 0; i < m_predicates.length; i++)
+      	{
+      		m_predicates[i].exprSetParent(this);
+      	}
+      }
+    }
+  }
+
+  /**
+   * Get a predicate expression at the given index.
+   *
+   *
+   * @param index Index of the predicate.
+   *
+   * @return A predicate expression.
+   */
+  public Expression getPredicate(int index)
+  {
+    return m_predicates[index];
+  }
+  
+  /**
+   * Get the current sub-context position.
+   *
+   * @return The node position of this walker in the sub-context node list.
+   */
+  public int getProximityPosition()
+  {
+
+    // System.out.println("getProximityPosition - m_predicateIndex: "+m_predicateIndex);
+    return getProximityPosition(m_predicateIndex);
+  }
+
+  /**
+   * Get the current sub-context position.
+   *
+   * @param xctxt The XPath runtime context.
+   *
+   * @return The node position of this walker in the sub-context node list.
+   */
+  public int getProximityPosition(XPathContext xctxt)
+  {
+    return getProximityPosition();
+  }
+  
+  /**
+   * Get the index of the last node that can be itterated to.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return the index of the last node that can be itterated to.
+   */
+  public abstract int getLastPos(XPathContext xctxt);
+
+  /**
+   * Get the current sub-context position.
+   *
+   * @param predicateIndex The index of the predicate where the proximity 
+   *                       should be taken from.
+   *
+   * @return The node position of this walker in the sub-context node list.
+   */
+  protected int getProximityPosition(int predicateIndex)
+  {
+    return (predicateIndex >= 0) ? m_proximityPositions[predicateIndex] : 0;
+  }
+
+  /**
+   * Reset the proximity positions counts.
+   */
+  public void resetProximityPositions()
+  {
+    int nPredicates = getPredicateCount();
+    if (nPredicates > 0)
+    {
+      if (null == m_proximityPositions)
+        m_proximityPositions = new int[nPredicates];
+
+      for (int i = 0; i < nPredicates; i++)
+      {
+        try
+        {
+          initProximityPosition(i);
+        }
+        catch(Exception e)
+        {
+          // TODO: Fix this...
+          throw new org.apache.xml.utils.WrappedRuntimeException(e);
+        }
+      }
+    }
+  }
+
+  /**
+   * Init the proximity position to zero for a forward axes.
+   *
+   * @param i The index into the m_proximityPositions array.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void initProximityPosition(int i) throws javax.xml.transform.TransformerException
+  {
+    m_proximityPositions[i] = 0;
+  }
+
+  /**
+   * Count forward one proximity position.
+   *
+   * @param i The index into the m_proximityPositions array, where the increment 
+   *          will occur.
+   */
+  protected void countProximityPosition(int i)
+  {
+  	// Note that in the case of a UnionChildIterator, this may be a 
+  	// static object and so m_proximityPositions may indeed be null!
+  	int[] pp = m_proximityPositions;
+    if ((null != pp) && (i < pp.length))
+      pp[i]++;
+  }
+
+  /**
+   * Tells if this is a reverse axes.
+   *
+   * @return false, unless a derived class overrides.
+   */
+  public boolean isReverseAxes()
+  {
+    return false;
+  }
+
+  /**
+   * Get which predicate is executing.
+   *
+   * @return The current predicate index, or -1 if no predicate is executing.
+   */
+  public int getPredicateIndex()
+  {
+    return m_predicateIndex;
+  }
+
+  /**
+   * Process the predicates.
+   *
+   * @param context The current context node.
+   * @param xctxt The XPath runtime context.
+   *
+   * @return the result of executing the predicate expressions.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  boolean executePredicates(int context, XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    
+    int nPredicates = getPredicateCount();
+    // System.out.println("nPredicates: "+nPredicates);
+    if (nPredicates == 0)
+      return true;
+
+    PrefixResolver savedResolver = xctxt.getNamespaceContext();
+
+    try
+    {
+      m_predicateIndex = 0;
+      xctxt.pushSubContextList(this);
+      xctxt.pushNamespaceContext(m_lpi.getPrefixResolver());
+      xctxt.pushCurrentNode(context);
+
+      for (int i = 0; i < nPredicates; i++)
+      {
+        // System.out.println("Executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
+        XObject pred = m_predicates[i].execute(xctxt);
+        // System.out.println("\nBack from executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
+        // System.out.println("pred.getType(): "+pred.getType());
+        if (XObject.CLASS_NUMBER == pred.getType())
+        {
+          if (DEBUG_PREDICATECOUNTING)
+          {
+            System.out.flush();
+            System.out.println("\n===== start predicate count ========");
+            System.out.println("m_predicateIndex: " + m_predicateIndex);
+            // System.out.println("getProximityPosition(m_predicateIndex): "
+            //                   + getProximityPosition(m_predicateIndex));
+            System.out.println("pred.num(): " + pred.num());
+          }
+
+          int proxPos = this.getProximityPosition(m_predicateIndex);
+          int predIndex = (int) pred.num();
+          if (proxPos != predIndex)
+          {
+            if (DEBUG_PREDICATECOUNTING)
+            {
+              System.out.println("\nnode context: "+nodeToString(context));
+              System.out.println("index predicate is false: "+proxPos);
+              System.out.println("\n===== end predicate count ========");
+            }
+            return false;
+          }
+          else if (DEBUG_PREDICATECOUNTING)
+          {
+            System.out.println("\nnode context: "+nodeToString(context));
+            System.out.println("index predicate is true: "+proxPos);
+            System.out.println("\n===== end predicate count ========");
+          }
+          
+          // If there is a proximity index that will not change during the 
+          // course of itteration, then we know there can be no more true 
+          // occurances of this predicate, so flag that we're done after 
+          // this.
+          //
+          // bugzilla 14365
+          // We can't set m_foundLast = true unless we're sure that -all-
+          // remaining parameters are stable, or else last() fails. Fixed so
+          // only sets m_foundLast if on the last predicate
+          if(m_predicates[i].isStableNumber() && i == nPredicates - 1)
+          {
+            m_foundLast = true;
+          }
+        }
+        else if (!pred.bool())
+          return false;
+
+        countProximityPosition(++m_predicateIndex);
+      }
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+      xctxt.popNamespaceContext();
+      xctxt.popSubContextList();
+      m_predicateIndex = -1;
+    }
+
+    return true;
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+
+    int nPredicates = getPredicateCount();
+
+    for (int i = 0; i < nPredicates; i++)
+    {
+      m_predicates[i].fixupVariables(vars, globalsSize);
+    }
+  }
+
+  
+  /**
+   * Diagnostics.
+   *
+   * @param n Node to give diagnostic information about, or null.
+   *
+   * @return Informative string about the argument.
+   */
+  protected String nodeToString(int n)
+  {
+    if(DTM.NULL != n)
+    {
+      DTM dtm = m_lpi.getXPathContext().getDTM(n);
+      return dtm.getNodeName(n) + "{" + (n+1) + "}";
+    }
+    else
+    {
+      return "null";
+    }
+  }
+  
+  //=============== NodeFilter Implementation ===============
+
+  /**
+   *  Test whether a specified node is visible in the logical view of a
+   * TreeWalker or NodeIterator. This function will be called by the
+   * implementation of TreeWalker and NodeIterator; it is not intended to
+   * be called directly from user code.
+   * @param n  The node to check to see if it passes the filter or not.
+   * @return  a constant to determine whether the node is accepted,
+   *   rejected, or skipped, as defined  above .
+   */
+  public short acceptNode(int n)
+  {
+
+    XPathContext xctxt = m_lpi.getXPathContext();
+
+    try
+    {
+      xctxt.pushCurrentNode(n);
+
+      XObject score = execute(xctxt, n);
+
+      // System.out.println("\n::acceptNode - score: "+score.num()+"::");
+      if (score != NodeTest.SCORE_NONE)
+      {
+        if (getPredicateCount() > 0)
+        {
+          countProximityPosition(0);
+
+          if (!executePredicates(n, xctxt))
+            return DTMIterator.FILTER_SKIP;
+        }
+
+        return DTMIterator.FILTER_ACCEPT;
+      }
+    }
+    catch (javax.xml.transform.TransformerException se)
+    {
+
+      // TODO: Fix this.
+      throw new RuntimeException(se.getMessage());
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+    }
+
+    return DTMIterator.FILTER_SKIP;
+  }
+
+  
+  /**
+   * Get the owning location path iterator.
+   *
+   * @return the owning location path iterator, which should not be null.
+   */
+  public LocPathIterator getLocPathIterator()
+  {
+    return m_lpi;
+  }
+
+  /**
+   * Set the location path iterator owner for this walker.  Besides 
+   * initialization, this function is called during cloning operations.
+   *
+   * @param li non-null reference to the owning location path iterator.
+   */
+  public void setLocPathIterator(LocPathIterator li)
+  {
+    m_lpi = li;
+    if(this != li)
+      li.exprSetParent(this);
+  }
+  
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside 
+   * the current subtree.
+   * 
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+   public boolean canTraverseOutsideSubtree()
+   {
+    int n = getPredicateCount();
+    for (int i = 0; i < n; i++) 
+    {
+      if(getPredicate(i).canTraverseOutsideSubtree())
+        return true;
+    }
+    return false;
+   }
+   
+	/**
+	 * This will traverse the heararchy, calling the visitor for 
+	 * each member.  If the called visitor method returns 
+	 * false, the subtree should not be called.
+	 * 
+	 * @param visitor The visitor whose appropriate method will be called.
+	 */
+	public void callPredicateVisitors(XPathVisitor visitor)
+	{
+	  if (null != m_predicates)
+	    {
+	    int n = m_predicates.length;
+	    for (int i = 0; i < n; i++)
+	      {
+	      ExpressionOwner predOwner = new PredOwner(i);
+	      if (visitor.visitPredicate(predOwner, m_predicates[i]))
+	        {
+	        m_predicates[i].callVisitors(predOwner, visitor);
+	      }
+	
+	    }
+	  }
+	} 
+	
+    /**
+     * @see Expression#deepEquals(Expression)
+     */
+    public boolean deepEquals(Expression expr)
+    {
+      if (!super.deepEquals(expr))
+            return false;
+
+      PredicatedNodeTest pnt = (PredicatedNodeTest) expr;
+      if (null != m_predicates)
+      {
+
+        int n = m_predicates.length;
+        if ((null == pnt.m_predicates) || (pnt.m_predicates.length != n))
+              return false;
+        for (int i = 0; i < n; i++)
+        {
+          if (!m_predicates[i].deepEquals(pnt.m_predicates[i]))
+          	return false; 
+        }
+      }
+      else if (null != pnt.m_predicates)
+              return false; 
+              
+      return true; 
+    }
+    
+  /** This is true if nextNode returns null. */
+  transient protected boolean m_foundLast = false;
+    
+  /** The owning location path iterator.
+   *  @serial */
+  protected LocPathIterator m_lpi;
+  
+  /**
+   * Which predicate we are executing.
+   */
+  transient int m_predicateIndex = -1;
+  
+  /** The list of predicate expressions. Is static and does not need 
+   *  to be deep cloned.
+   *  @serial 
+   */
+  private Expression[] m_predicates;
+
+  /**
+   * An array of counts that correspond to the number
+   * of predicates the step contains.
+   */
+  transient protected int[] m_proximityPositions;
+
+  /** If true, diagnostic messages about predicate execution will be posted.  */
+  static final boolean DEBUG_PREDICATECOUNTING = false;
+  
+  class PredOwner implements ExpressionOwner
+  {
+  	int m_index;
+  	
+  	PredOwner(int index)
+  	{
+  		m_index = index;
+  	}
+  	
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_predicates[m_index];
+    }
+
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(PredicatedNodeTest.this);
+    	m_predicates[m_index] = exp;
+    }
+  }
+    
+}
diff --git a/src/main/java/org/apache/xpath/axes/RTFIterator.java b/src/main/java/org/apache/xpath/axes/RTFIterator.java
new file mode 100644
index 0000000..cb018b4
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/RTFIterator.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: RTFIterator.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+
+/**
+ * This class implements an RTF Iterator. Currently exists for sole
+ * purpose of enabling EXSLT object-type function to return "RTF".
+ * 
+  * @xsl.usage advanced
+  */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xpath.NodeSetDTM;
+
+public class RTFIterator extends NodeSetDTM {
+    static final long serialVersionUID = 7658117366258528996L;
+
+	/**
+	 * Constructor for RTFIterator
+	 */	
+	public RTFIterator(int root, DTMManager manager) {
+		super(root, manager);
+	}
+}
+
diff --git a/src/main/java/org/apache/xpath/axes/ReverseAxesWalker.java b/src/main/java/org/apache/xpath/axes/ReverseAxesWalker.java
new file mode 100644
index 0000000..4efd804
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/ReverseAxesWalker.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ReverseAxesWalker.java 513117 2007-03-01 03:28:52Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisIterator;
+import org.apache.xpath.XPathContext;
+
+/**
+ * Walker for a reverse axes.
+ * @see <a href="http://www.w3.org/TR/xpath#predicates">XPath 2.4 Predicates</a>
+ */
+public class ReverseAxesWalker extends AxesWalker
+{
+    static final long serialVersionUID = 2847007647832768941L;
+
+  /**
+   * Construct an AxesWalker using a LocPathIterator.
+   *
+   * @param locPathIterator The location path iterator that 'owns' this walker.
+   */
+  ReverseAxesWalker(LocPathIterator locPathIterator, int axis)
+  {
+    super(locPathIterator, axis);
+  }
+  
+  /**
+   * Set the root node of the TreeWalker.
+   * (Not part of the DOM2 TreeWalker interface).
+   *
+   * @param root The context node of this step.
+   */
+  public void setRoot(int root)
+  {
+    super.setRoot(root);
+    m_iterator = getDTM(root).getAxisIterator(m_axis);
+    m_iterator.setStartNode(root);
+  }
+
+  /**
+   * Detaches the walker from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state.
+   */
+  public void detach()
+  {
+    m_iterator = null;
+    super.detach();
+  }
+  
+  /**
+   * Get the next node in document order on the axes.
+   *
+   * @return the next node in document order on the axes, or null.
+   */
+  protected int getNextNode()
+  {
+    if (m_foundLast)
+      return DTM.NULL;
+
+    int next = m_iterator.next();
+    
+    if (m_isFresh)
+      m_isFresh = false;
+
+    if (DTM.NULL == next)
+      this.m_foundLast = true;
+
+    return next;
+  }
+
+
+  /**
+   * Tells if this is a reverse axes.  Overrides AxesWalker#isReverseAxes.
+   *
+   * @return true for this class.
+   */
+  public boolean isReverseAxes()
+  {
+    return true;
+  }
+
+//  /**
+//   *  Set the root node of the TreeWalker.
+//   *
+//   * @param root The context node of this step.
+//   */
+//  public void setRoot(int root)
+//  {
+//    super.setRoot(root);
+//  }
+
+  /**
+   * Get the current sub-context position.  In order to do the
+   * reverse axes count, for the moment this re-searches the axes
+   * up to the predicate.  An optimization on this is to cache
+   * the nodes searched, but, for the moment, this case is probably
+   * rare enough that the added complexity isn't worth it.
+   *
+   * @param predicateIndex The predicate index of the proximity position.
+   *
+   * @return The pridicate index, or -1.
+   */
+  protected int getProximityPosition(int predicateIndex)
+  {
+    // A negative predicate index seems to occur with
+    // (preceding-sibling::*|following-sibling::*)/ancestor::*[position()]/*[position()]
+    // -sb
+    if(predicateIndex < 0)
+      return -1;
+      
+    int count = m_proximityPositions[predicateIndex];
+      
+    if (count <= 0)
+    {
+      AxesWalker savedWalker = wi().getLastUsedWalker();
+
+      try
+      {
+        ReverseAxesWalker clone = (ReverseAxesWalker) this.clone();
+
+        clone.setRoot(this.getRoot());
+
+        clone.setPredicateCount(predicateIndex);
+
+        clone.setPrevWalker(null);
+        clone.setNextWalker(null);
+        wi().setLastUsedWalker(clone);
+
+        // Count 'em all
+        count++;
+        int next;
+
+        while (DTM.NULL != (next = clone.nextNode()))
+        {
+          count++;
+        }
+
+        m_proximityPositions[predicateIndex] = count;
+      }
+      catch (CloneNotSupportedException cnse)
+      {
+
+        // can't happen
+      }
+      finally
+      {
+        wi().setLastUsedWalker(savedWalker);
+      }
+    }
+    
+    return count;
+  }
+
+  /**
+   * Count backwards one proximity position.
+   *
+   * @param i The predicate index.
+   */
+  protected void countProximityPosition(int i)
+  {
+    if (i < m_proximityPositions.length)
+      m_proximityPositions[i]--;
+  }
+
+  /**
+   * Get the number of nodes in this node list.  The function is probably ill
+   * named?
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   *
+   * @return the number of nodes in this node list.
+   */
+  public int getLastPos(XPathContext xctxt)
+  {
+
+    int count = 0;
+    AxesWalker savedWalker = wi().getLastUsedWalker();
+
+    try
+    {
+      ReverseAxesWalker clone = (ReverseAxesWalker) this.clone();
+
+      clone.setRoot(this.getRoot());
+
+      clone.setPredicateCount(m_predicateIndex);
+
+      clone.setPrevWalker(null);
+      clone.setNextWalker(null);
+      wi().setLastUsedWalker(clone);
+
+      // Count 'em all
+      // count = 1;
+      int next;
+
+      while (DTM.NULL != (next = clone.nextNode()))
+      {
+        count++;
+      }
+    }
+    catch (CloneNotSupportedException cnse)
+    {
+
+      // can't happen
+    }
+    finally
+    {
+      wi().setLastUsedWalker(savedWalker);
+    }
+
+    return count;
+  }
+  
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * Warning: This can only be called after setRoot has been called!
+   * 
+   * @return false.
+   */
+  public boolean isDocOrdered()
+  {
+    return false;  // I think.
+  }
+  
+  /** The DTM inner traversal class, that corresponds to the super axis. */
+  protected DTMAxisIterator m_iterator;
+}
diff --git a/src/main/java/org/apache/xpath/axes/SelfIteratorNoPredicate.java b/src/main/java/org/apache/xpath/axes/SelfIteratorNoPredicate.java
new file mode 100644
index 0000000..b63f8d6
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/SelfIteratorNoPredicate.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SelfIteratorNoPredicate.java 469263 2006-10-30 20:45:40Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.compiler.Compiler;
+
+/**
+ * This class implements an optimized iterator for
+ * "." patterns, that is, the self axes without any predicates.  
+ * @see org.apache.xpath.axes.LocPathIterator
+ * @xsl.usage advanced
+ */
+public class SelfIteratorNoPredicate extends LocPathIterator
+{
+    static final long serialVersionUID = -4226887905279814201L;
+
+  /**
+   * Create a SelfIteratorNoPredicate object.
+   *
+   * @param compiler A reference to the Compiler that contains the op map.
+   * @param opPos The position within the op map, which contains the
+   * location path expression for this itterator.
+   * @param analysis Analysis bits.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  SelfIteratorNoPredicate(Compiler compiler, int opPos, int analysis)
+          throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis, false);
+  }
+  
+  /**
+   * Create a SelfIteratorNoPredicate object.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public SelfIteratorNoPredicate()
+          throws javax.xml.transform.TransformerException
+  {
+    super(null);
+  }
+
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   *
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   */
+  public int nextNode()
+  {
+    if (m_foundLast)
+      return DTM.NULL;
+      
+    int next;
+
+    m_lastFetched = next = (DTM.NULL == m_lastFetched)
+                           ? m_context
+                           : DTM.NULL;
+
+    // m_lastFetched = next;
+    if (DTM.NULL != next)
+    {
+      m_pos++;
+
+      return next;
+    }
+    else
+    {
+      m_foundLast = true;
+
+      return DTM.NULL;
+    }
+  }
+  
+  /**
+   * Return the first node out of the nodeset, if this expression is 
+   * a nodeset expression.  This is the default implementation for 
+   * nodesets.  Derived classes should try and override this and return a 
+   * value without having to do a clone operation.
+   * @param xctxt The XPath runtime context.
+   * @return the first node out of the nodeset, or DTM.NULL.
+   */
+  public int asNode(XPathContext xctxt)
+    throws javax.xml.transform.TransformerException
+  {
+    return xctxt.getCurrentNode();
+  }
+  
+  /**
+   * Get the index of the last node that can be itterated to.
+   * This probably will need to be overridded by derived classes.
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return the index of the last node that can be itterated to.
+   */
+  public int getLastPos(XPathContext xctxt)
+  {
+    return 1;
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/SubContextList.java b/src/main/java/org/apache/xpath/axes/SubContextList.java
new file mode 100644
index 0000000..e422586
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/SubContextList.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SubContextList.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xpath.XPathContext;
+ 
+/**
+ * A class that implements this interface is a sub context node list, meaning it
+ * is a node list for a location path step for a predicate.
+ * @xsl.usage advanced
+ */
+public interface SubContextList
+{
+
+  /**
+   * Get the number of nodes in the node list, which, in the XSLT 1 based 
+   * counting system, is the last index position.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   *
+   * @return the number of nodes in the node list.
+   */
+  public int getLastPos(XPathContext xctxt);
+
+  /**
+   * Get the current sub-context position.
+   *
+   * @param xctxt The XPath runtime context.
+   *
+   * @return The position of the current node in the list.
+   */
+  public int getProximityPosition(XPathContext xctxt);
+}
diff --git a/src/main/java/org/apache/xpath/axes/UnionChildIterator.java b/src/main/java/org/apache/xpath/axes/UnionChildIterator.java
new file mode 100644
index 0000000..19cd4ec
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/UnionChildIterator.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: UnionChildIterator.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.patterns.NodeTest;
+
+/**
+ * This class defines a simplified type of union iterator that only 
+ * tests along the child axes.  If the conditions are right, it is 
+ * much faster than using a UnionPathIterator.
+ */
+public class UnionChildIterator extends ChildTestIterator
+{
+    static final long serialVersionUID = 3500298482193003495L;
+  /**
+   * Even though these may hold full LocPathIterators, this array does 
+   * not have to be cloned, since only the node test and predicate 
+   * portion are used, and these only need static information.  However, 
+   * also note that index predicates can not be used!
+   */
+  private PredicatedNodeTest[] m_nodeTests = null;
+
+  /**
+   * Constructor for UnionChildIterator
+   */
+  public UnionChildIterator()
+  {
+    super(null);
+  }
+
+  /**
+   * Add a node test to the union list.
+   *
+   * @param test reference to a NodeTest, which will be added 
+   * directly to the list of node tests (in other words, it will 
+   * not be cloned).  The parent of this test will be set to 
+   * this object.
+   */
+  public void addNodeTest(PredicatedNodeTest test)
+  {
+
+    // Increase array size by only 1 at a time.  Fix this
+    // if it looks to be a problem.
+    if (null == m_nodeTests)
+    {
+      m_nodeTests = new PredicatedNodeTest[1];
+      m_nodeTests[0] = test;
+    }
+    else
+    {
+      PredicatedNodeTest[] tests = m_nodeTests;
+      int len = m_nodeTests.length;
+
+      m_nodeTests = new PredicatedNodeTest[len + 1];
+
+      System.arraycopy(tests, 0, m_nodeTests, 0, len);
+
+      m_nodeTests[len] = test;
+    }
+    test.exprSetParent(this);
+  }
+
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+    if (m_nodeTests != null) {
+      for (int i = 0; i < m_nodeTests.length; i++) {
+        m_nodeTests[i].fixupVariables(vars, globalsSize);
+      }
+    }
+  }
+
+  /**
+   * Test whether a specified node is visible in the logical view of a
+   * TreeWalker or NodeIterator. This function will be called by the
+   * implementation of TreeWalker and NodeIterator; it is not intended to
+   * be called directly from user code.
+   * @param n  The node to check to see if it passes the filter or not.
+   * @return  a constant to determine whether the node is accepted,
+   *   rejected, or skipped, as defined  above .
+   */
+  public short acceptNode(int n)
+  {
+    XPathContext xctxt = getXPathContext();
+    try
+    {
+      xctxt.pushCurrentNode(n);
+      for (int i = 0; i < m_nodeTests.length; i++)
+      {
+        PredicatedNodeTest pnt = m_nodeTests[i];
+        XObject score = pnt.execute(xctxt, n);
+        if (score != NodeTest.SCORE_NONE)
+        {
+          // Note that we are assuming there are no positional predicates!
+          if (pnt.getPredicateCount() > 0)
+          {
+            if (pnt.executePredicates(n, xctxt))
+              return DTMIterator.FILTER_ACCEPT;
+          }
+          else
+            return DTMIterator.FILTER_ACCEPT;
+
+        }
+      }
+    }
+    catch (javax.xml.transform.TransformerException se)
+    {
+
+      // TODO: Fix this.
+      throw new RuntimeException(se.getMessage());
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+    }
+    return DTMIterator.FILTER_SKIP;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/UnionPathIterator.java b/src/main/java/org/apache/xpath/axes/UnionPathIterator.java
new file mode 100644
index 0000000..6d714bb
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/UnionPathIterator.java
@@ -0,0 +1,584 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: UnionPathIterator.java 469314 2006-10-30 23:31:59Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.OpCodes;
+import org.apache.xpath.compiler.OpMap;
+
+/**
+ * This class extends NodeSetDTM, which implements DTMIterator,
+ * and fetches nodes one at a time in document order based on a XPath
+ * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
+ * As each node is iterated via nextNode(), the node is also stored
+ * in the NodeVector, so that previousNode() can easily be done.
+ * @xsl.usage advanced
+ */
+public class UnionPathIterator extends LocPathIterator
+        implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
+{
+    static final long serialVersionUID = -3910351546843826781L;
+
+  /**
+   * Constructor to create an instance which you can add location paths to.
+   */
+  public UnionPathIterator()
+  {
+
+    super();
+
+    // m_mutable = false;
+    // m_cacheNodes = false;
+    m_iterators = null;
+    m_exprs = null;
+  }
+
+  /**
+   * Initialize the context values for this expression 
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this 
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+    super.setRoot(context, environment);
+
+    try
+    {
+      if (null != m_exprs)
+      {
+        int n = m_exprs.length;
+        DTMIterator newIters[] = new DTMIterator[n];
+  
+        for (int i = 0; i < n; i++)
+        {
+          DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
+          newIters[i] = iter;
+          iter.nextNode();
+        }
+        m_iterators = newIters;
+      }
+    }
+    catch(Exception e)
+    {
+      throw new org.apache.xml.utils.WrappedRuntimeException(e);
+    }
+  }
+  
+  /**
+   * Add an iterator to the union list.
+   *
+   * @param expr non-null reference to a location path iterator.
+   */
+  public void addIterator(DTMIterator expr)
+  {
+
+    // Increase array size by only 1 at a time.  Fix this
+    // if it looks to be a problem.
+    if (null == m_iterators)
+    {
+      m_iterators = new DTMIterator[1];
+      m_iterators[0] = expr;
+    }
+    else
+    {
+      DTMIterator[] exprs = m_iterators;
+      int len = m_iterators.length;
+
+      m_iterators = new DTMIterator[len + 1];
+
+      System.arraycopy(exprs, 0, m_iterators, 0, len);
+
+      m_iterators[len] = expr;
+    }
+    expr.nextNode();
+    if(expr instanceof Expression)
+    	((Expression)expr).exprSetParent(this);
+  }
+  
+  /**
+   *  Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   */
+  public void detach()
+  {
+          if(m_allowDetach && null != m_iterators){         
+                  int n = m_iterators.length;
+                  for(int i = 0; i < n; i++)
+                  {
+                          m_iterators[i].detach();
+                  }
+                  m_iterators = null;                                
+          }
+  }
+
+
+  /**
+   * Create a UnionPathIterator object, including creation 
+   * of location path iterators from the opcode list, and call back 
+   * into the Compiler to create predicate expressions.
+   *
+   * @param compiler The Compiler which is creating 
+   * this expression.
+   * @param opPos The position of this iterator in the 
+   * opcode list from the compiler.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public UnionPathIterator(Compiler compiler, int opPos)
+          throws javax.xml.transform.TransformerException
+  {
+
+    super();
+
+    opPos = OpMap.getFirstChildPos(opPos);
+
+    loadLocationPaths(compiler, opPos, 0);
+  }
+  
+  /**
+   * This will return an iterator capable of handling the union of paths given.
+   * 
+   * @param compiler The Compiler which is creating 
+   * this expression.
+   * @param opPos The position of this iterator in the 
+   * opcode list from the compiler.
+   * 
+   * @return Object that is derived from LocPathIterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public static LocPathIterator createUnionIterator(Compiler compiler, int opPos)
+          throws javax.xml.transform.TransformerException
+  {
+  	// For the moment, I'm going to first create a full UnionPathIterator, and 
+  	// then see if I can reduce it to a UnionChildIterator.  It would obviously 
+  	// be more effecient to just test for the conditions for a UnionChildIterator, 
+  	// and then create that directly.
+  	UnionPathIterator upi = new UnionPathIterator(compiler, opPos);
+  	int nPaths = upi.m_exprs.length;
+  	boolean isAllChildIterators = true;
+  	for(int i = 0; i < nPaths; i++)
+  	{
+  		LocPathIterator lpi = upi.m_exprs[i];
+  		
+  		if(lpi.getAxis() != Axis.CHILD)
+  		{
+  			isAllChildIterators = false;
+  			break;
+  		}
+  		else
+  		{
+  			// check for positional predicates or position function, which won't work.
+  			if(HasPositionalPredChecker.check(lpi))
+  			{
+  				isAllChildIterators = false;
+  				break;
+  			}
+  		}
+  	}
+  	if(isAllChildIterators)
+  	{
+  		UnionChildIterator uci = new UnionChildIterator();
+  		
+	  	for(int i = 0; i < nPaths; i++)
+	  	{
+	  		PredicatedNodeTest lpi = upi.m_exprs[i];
+	  		// I could strip the lpi down to a pure PredicatedNodeTest, but 
+	  		// I don't think it's worth it.  Note that the test can be used 
+	  		// as a static object... so it doesn't have to be cloned.
+	  		uci.addNodeTest(lpi);
+	  	}
+	  	return uci;
+  		
+  	}
+  	else
+  		return upi;
+  }
+  
+  /** 
+   * Get the analysis bits for this walker, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits()
+  {
+    int bits = 0;
+    
+    if (m_exprs != null)
+    {
+      int n = m_exprs.length;
+
+      for (int i = 0; i < n; i++)
+      {
+      	int bit = m_exprs[i].getAnalysisBits();
+        bits |= bit;
+      }
+    }
+
+    return bits;
+  }
+  
+  /**
+   * Read the object from a serialization stream.
+   *
+   * @param stream Input stream to read from
+   *
+   * @throws java.io.IOException
+   * @throws javax.xml.transform.TransformerException
+   */
+  private void readObject(java.io.ObjectInputStream stream)
+          throws java.io.IOException, javax.xml.transform.TransformerException
+  {
+    try
+    {
+      stream.defaultReadObject();
+      m_clones =  new IteratorPool(this);
+    }
+    catch (ClassNotFoundException cnfe)
+    {
+      throw new javax.xml.transform.TransformerException(cnfe);
+    }
+  }
+
+  /**
+   * Get a cloned LocPathIterator that holds the same 
+   * position as this iterator.
+   *
+   * @return A clone of this iterator that holds the same node position.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+
+    UnionPathIterator clone = (UnionPathIterator) super.clone();
+    if (m_iterators != null)
+    {
+      int n = m_iterators.length;
+      
+      clone.m_iterators = new DTMIterator[n];
+
+      for (int i = 0; i < n; i++)
+      {
+        clone.m_iterators[i] = (DTMIterator)m_iterators[i].clone();
+      }
+    }
+
+    return clone;
+  }
+  
+  
+  /**
+   * Create a new location path iterator.
+   *
+   * @param compiler The Compiler which is creating 
+   * this expression.
+   * @param opPos The position of this iterator in the 
+   *
+   * @return New location path iterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected LocPathIterator createDTMIterator(
+          Compiler compiler, int opPos) throws javax.xml.transform.TransformerException
+  {
+    LocPathIterator lpi = (LocPathIterator)WalkerFactory.newDTMIterator(compiler, opPos, 
+                                      (compiler.getLocationPathDepth() <= 0));
+    return lpi;
+  }
+
+  /**
+   * Initialize the location path iterators.  Recursive.
+   *
+   * @param compiler The Compiler which is creating 
+   * this expression.
+   * @param opPos The position of this iterator in the 
+   * opcode list from the compiler.
+   * @param count The insert position of the iterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void loadLocationPaths(Compiler compiler, int opPos, int count)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // TODO: Handle unwrapped FilterExpr
+    int steptype = compiler.getOp(opPos);
+
+    if (steptype == OpCodes.OP_LOCATIONPATH)
+    {
+      loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
+
+      m_exprs[count] = createDTMIterator(compiler, opPos);
+      m_exprs[count].exprSetParent(this);
+    }
+    else
+    {
+
+      // Have to check for unwrapped functions, which the LocPathIterator
+      // doesn't handle. 
+      switch (steptype)
+      {
+      case OpCodes.OP_VARIABLE :
+      case OpCodes.OP_EXTFUNCTION :
+      case OpCodes.OP_FUNCTION :
+      case OpCodes.OP_GROUP :
+        loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
+
+        WalkingIterator iter =
+          new WalkingIterator(compiler.getNamespaceContext());
+        iter.exprSetParent(this);
+          
+        if(compiler.getLocationPathDepth() <= 0)
+          iter.setIsTopLevel(true);
+
+        iter.m_firstWalker = new org.apache.xpath.axes.FilterExprWalker(iter);
+
+        iter.m_firstWalker.init(compiler, opPos, steptype);
+
+        m_exprs[count] = iter;
+        break;
+      default :
+        m_exprs = new LocPathIterator[count];
+      }
+    }
+  }
+
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a DTMIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   */
+  public int nextNode()
+  {
+  	if(m_foundLast)
+  		return DTM.NULL;
+
+    // Loop through the iterators getting the current fetched 
+    // node, and get the earliest occuring in document order
+    int earliestNode = DTM.NULL;
+
+    if (null != m_iterators)
+    {
+      int n = m_iterators.length;
+      int iteratorUsed = -1;
+
+      for (int i = 0; i < n; i++)
+      {
+        int node = m_iterators[i].getCurrentNode();
+
+        if (DTM.NULL == node)
+          continue;
+        else if (DTM.NULL == earliestNode)
+        {
+          iteratorUsed = i;
+          earliestNode = node;
+        }
+        else
+        {
+          if (node == earliestNode)
+          {
+
+            // Found a duplicate, so skip past it.
+            m_iterators[i].nextNode();
+          }
+          else
+          {
+            DTM dtm = getDTM(node);
+
+            if (dtm.isNodeAfter(node, earliestNode))
+            {
+              iteratorUsed = i;
+              earliestNode = node;
+            }
+          }
+        }
+      }
+
+      if (DTM.NULL != earliestNode)
+      {
+        m_iterators[iteratorUsed].nextNode();
+
+        incrementCurrentPos();
+      }
+      else
+        m_foundLast = true;
+    }
+
+    m_lastFetched = earliestNode;
+
+    return earliestNode;
+  }
+            
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    for (int i = 0; i < m_exprs.length; i++) 
+    {
+      m_exprs[i].fixupVariables(vars, globalsSize);
+    }
+    
+  }
+  
+  /**
+   * The location path iterators, one for each
+   * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
+   * path</a> contained in the union expression.
+   * @serial
+   */
+  protected LocPathIterator[] m_exprs;
+
+    
+  /**
+   * The location path iterators, one for each
+   * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
+   * path</a> contained in the union expression.
+   * @serial
+   */
+  protected DTMIterator[] m_iterators;
+      
+  /**
+   * Returns the axis being iterated, if it is known.
+   * 
+   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
+   * types.
+   */
+  public int getAxis()
+  {
+    // Could be smarter.
+    return -1;
+  }
+  
+  class iterOwner implements ExpressionOwner
+  {
+  	int m_index;
+  	
+  	iterOwner(int index)
+  	{
+  		m_index = index;
+  	}
+  	
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_exprs[m_index];
+    }
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	
+    	if(!(exp instanceof LocPathIterator))
+    	{
+    		// Yuck.  Need FilterExprIter.  Or make it so m_exprs can be just 
+    		// plain expressions?
+    		WalkingIterator wi = new WalkingIterator(getPrefixResolver());
+    		FilterExprWalker few = new FilterExprWalker(wi);
+    		wi.setFirstWalker(few);
+    		few.setInnerExpression(exp);
+    		wi.exprSetParent(UnionPathIterator.this);
+    		few.exprSetParent(wi);
+    		exp.exprSetParent(few);
+    		exp = wi;
+    	}
+    	else
+    		exp.exprSetParent(UnionPathIterator.this);
+    	m_exprs[m_index] = (LocPathIterator)exp;
+    }
+
+  }
+
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	 	if(visitor.visitUnionPath(owner, this))
+  	 	{
+  	 		if(null != m_exprs)
+  	 		{
+  	 			int n = m_exprs.length;
+  	 			for(int i = 0; i < n; i++)
+  	 			{
+  	 				m_exprs[i].callVisitors(new iterOwner(i), visitor);
+  	 			}
+  	 		}
+  	 	}
+  }
+  
+    /**
+     * @see Expression#deepEquals(Expression)
+     */
+    public boolean deepEquals(Expression expr)
+    {
+      if (!super.deepEquals(expr))
+            return false;
+
+      UnionPathIterator upi = (UnionPathIterator) expr;
+
+      if (null != m_exprs)
+      {
+        int n = m_exprs.length;
+        
+        if((null == upi.m_exprs) || (upi.m_exprs.length != n))
+        	return false;
+        
+        for (int i = 0; i < n; i++)
+        {
+          if(!m_exprs[i].deepEquals(upi.m_exprs[i]))
+          	return false;
+        }
+      }
+      else if (null != upi.m_exprs)
+      {
+          return false;
+      }
+
+      return true;
+    }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/WalkerFactory.java b/src/main/java/org/apache/xpath/axes/WalkerFactory.java
new file mode 100644
index 0000000..363fab1
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/WalkerFactory.java
@@ -0,0 +1,1825 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WalkerFactory.java 469314 2006-10-30 23:31:59Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.FunctionTable;
+import org.apache.xpath.compiler.OpCodes;
+import org.apache.xpath.compiler.OpMap;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.patterns.ContextMatchStepPattern;
+import org.apache.xpath.patterns.FunctionPattern;
+import org.apache.xpath.patterns.NodeTest;
+import org.apache.xpath.patterns.StepPattern;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * This class is both a factory for XPath location path expressions,
+ * which are built from the opcode map output, and an analysis engine
+ * for the location path expressions in order to provide optimization hints.
+ */
+public class WalkerFactory
+{
+
+  /**
+   * This method is for building an array of possible levels
+   * where the target element(s) could be found for a match.
+   * @param lpi The owning location path iterator.
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param stepOpCodePos The opcode position for the step.
+   *
+   * @return non-null AxesWalker derivative.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage advanced
+   */
+  static AxesWalker loadOneWalker(
+          WalkingIterator lpi, Compiler compiler, int stepOpCodePos)
+            throws javax.xml.transform.TransformerException
+  {
+
+    AxesWalker firstWalker = null;
+    int stepType = compiler.getOp(stepOpCodePos);
+
+    if (stepType != OpCodes.ENDOP)
+    {
+
+      // m_axesWalkers = new AxesWalker[1];
+      // As we unwind from the recursion, create the iterators.
+      firstWalker = createDefaultWalker(compiler, stepType, lpi, 0);
+
+      firstWalker.init(compiler, stepOpCodePos, stepType);
+    }
+
+    return firstWalker;
+  }
+
+  /**
+   * This method is for building an array of possible levels
+   * where the target element(s) could be found for a match.
+   * @param lpi The owning location path iterator object.
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param stepOpCodePos The opcode position for the step.
+   * @param stepIndex The top-level step index withing the iterator.
+   *
+   * @return non-null AxesWalker derivative.
+   *
+   * @throws javax.xml.transform.TransformerException
+   * @xsl.usage advanced
+   */
+  static AxesWalker loadWalkers(
+          WalkingIterator lpi, Compiler compiler, int stepOpCodePos, int stepIndex)
+            throws javax.xml.transform.TransformerException
+  {
+
+    int stepType;
+    AxesWalker firstWalker = null;
+    AxesWalker walker, prevWalker = null;
+
+    int analysis = analyze(compiler, stepOpCodePos, stepIndex);
+
+    while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos)))
+    {
+      walker = createDefaultWalker(compiler, stepOpCodePos, lpi, analysis);
+
+      walker.init(compiler, stepOpCodePos, stepType);
+      walker.exprSetParent(lpi);
+
+      // walker.setAnalysis(analysis);
+      if (null == firstWalker)
+      {
+        firstWalker = walker;
+      }
+      else
+      {
+        prevWalker.setNextWalker(walker);
+        walker.setPrevWalker(prevWalker);
+      }
+
+      prevWalker = walker;
+      stepOpCodePos = compiler.getNextStepPos(stepOpCodePos);
+
+      if (stepOpCodePos < 0)
+        break;
+    }
+
+    return firstWalker;
+  }
+  
+  public static boolean isSet(int analysis, int bits)
+  {
+    return (0 != (analysis & bits));
+  }
+  
+  public static void diagnoseIterator(String name, int analysis, Compiler compiler)
+  {
+    System.out.println(compiler.toString()+", "+name+", "
+                             + Integer.toBinaryString(analysis) + ", "
+                             + getAnalysisString(analysis));
+  }
+
+  /**
+   * Create a new LocPathIterator iterator.  The exact type of iterator
+   * returned is based on an analysis of the XPath operations.
+   *
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param opPos The position of the operation code for this itterator.
+   *
+   * @return non-null reference to a LocPathIterator or derivative.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public static DTMIterator newDTMIterator(
+          Compiler compiler, int opPos,
+          boolean isTopLevel)
+            throws javax.xml.transform.TransformerException
+  {
+
+    int firstStepPos = OpMap.getFirstChildPos(opPos);
+    int analysis = analyze(compiler, firstStepPos, 0);
+    boolean isOneStep = isOneStep(analysis);
+    DTMIterator iter;
+
+    // Is the iteration a one-step attribute pattern (i.e. select="@foo")?
+    if (isOneStep && walksSelfOnly(analysis) && 
+        isWild(analysis) && !hasPredicate(analysis))
+    {
+      if (DEBUG_ITERATOR_CREATION)
+        diagnoseIterator("SelfIteratorNoPredicate", analysis, compiler);
+
+      // Then use a simple iteration of the attributes, with node test 
+      // and predicate testing.
+      iter = new SelfIteratorNoPredicate(compiler, opPos, analysis);
+    }
+    // Is the iteration exactly one child step?
+    else if (walksChildrenOnly(analysis) && isOneStep)
+    {
+
+      // Does the pattern specify *any* child with no predicate? (i.e. select="child::node()".
+      if (isWild(analysis) && !hasPredicate(analysis))
+      {
+        if (DEBUG_ITERATOR_CREATION)
+          diagnoseIterator("ChildIterator", analysis, compiler);
+
+        // Use simple child iteration without any test.
+        iter = new ChildIterator(compiler, opPos, analysis);
+      }
+      else
+      {
+        if (DEBUG_ITERATOR_CREATION)
+          diagnoseIterator("ChildTestIterator", analysis, compiler);
+
+        // Else use simple node test iteration with predicate test.
+        iter = new ChildTestIterator(compiler, opPos, analysis);
+      }
+    }
+    // Is the iteration a one-step attribute pattern (i.e. select="@foo")?
+    else if (isOneStep && walksAttributes(analysis))
+    {
+      if (DEBUG_ITERATOR_CREATION)
+        diagnoseIterator("AttributeIterator", analysis, compiler);
+
+      // Then use a simple iteration of the attributes, with node test 
+      // and predicate testing.
+      iter = new AttributeIterator(compiler, opPos, analysis);
+    }
+    else if(isOneStep && !walksFilteredList(analysis))
+    {
+      if( !walksNamespaces(analysis) 
+      && (walksInDocOrder(analysis) || isSet(analysis, BIT_PARENT)))
+      {
+        if (false || DEBUG_ITERATOR_CREATION)
+          diagnoseIterator("OneStepIteratorForward", analysis, compiler);
+  
+        // Then use a simple iteration of the attributes, with node test 
+        // and predicate testing.
+        iter = new OneStepIteratorForward(compiler, opPos, analysis);
+      }
+      else
+      {
+        if (false || DEBUG_ITERATOR_CREATION)
+          diagnoseIterator("OneStepIterator", analysis, compiler);
+  
+        // Then use a simple iteration of the attributes, with node test 
+        // and predicate testing.
+        iter = new OneStepIterator(compiler, opPos, analysis);
+      }
+    }
+
+    // Analysis of "//center":
+    // bits: 1001000000001010000000000000011
+    // count: 3
+    // root
+    // child:node()
+    // BIT_DESCENDANT_OR_SELF
+    // It's highly possible that we should have a seperate bit set for 
+    // "//foo" patterns.
+    // For at least the time being, we can't optimize patterns like 
+    // "//table[3]", because this has to be analyzed as 
+    // "/descendant-or-self::node()/table[3]" in order for the indexes 
+    // to work right.
+    else if (isOptimizableForDescendantIterator(compiler, firstStepPos, 0)
+              // && getStepCount(analysis) <= 3 
+              // && walksDescendants(analysis) 
+              // && walksSubtreeOnlyFromRootOrContext(analysis)
+             )
+    {
+      if (DEBUG_ITERATOR_CREATION)
+        diagnoseIterator("DescendantIterator", analysis, compiler);
+
+      iter = new DescendantIterator(compiler, opPos, analysis);
+    }
+    else
+    { 
+      if(isNaturalDocOrder(compiler, firstStepPos, 0, analysis))
+      {
+        if (false || DEBUG_ITERATOR_CREATION)
+        {
+          diagnoseIterator("WalkingIterator", analysis, compiler);
+        }
+  
+        iter = new WalkingIterator(compiler, opPos, analysis, true);
+      }
+      else
+      {
+//        if (DEBUG_ITERATOR_CREATION)
+//          diagnoseIterator("MatchPatternIterator", analysis, compiler);
+//
+//        return new MatchPatternIterator(compiler, opPos, analysis);
+        if (DEBUG_ITERATOR_CREATION)
+          diagnoseIterator("WalkingIteratorSorted", analysis, compiler);
+
+        iter = new WalkingIteratorSorted(compiler, opPos, analysis, true);
+      }
+    }
+    if(iter instanceof LocPathIterator)
+      ((LocPathIterator)iter).setIsTopLevel(isTopLevel);
+      
+    return iter;
+  }
+  
+  /**
+   * Special purpose function to see if we can optimize the pattern for 
+   * a DescendantIterator.
+   *
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param stepOpCodePos The opcode position for the step.
+   *
+   * @return 32 bits as an integer that give information about the location
+   * path as a whole.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public static int getAxisFromStep(
+          Compiler compiler, int stepOpCodePos)
+            throws javax.xml.transform.TransformerException
+  {
+
+    int stepType = compiler.getOp(stepOpCodePos);
+
+    switch (stepType)
+    {
+    case OpCodes.FROM_FOLLOWING :
+      return Axis.FOLLOWING;
+    case OpCodes.FROM_FOLLOWING_SIBLINGS :
+      return Axis.FOLLOWINGSIBLING;
+    case OpCodes.FROM_PRECEDING :
+      return Axis.PRECEDING;
+    case OpCodes.FROM_PRECEDING_SIBLINGS :
+      return Axis.PRECEDINGSIBLING;
+    case OpCodes.FROM_PARENT :
+      return Axis.PARENT;
+    case OpCodes.FROM_NAMESPACE :
+      return Axis.NAMESPACE;
+    case OpCodes.FROM_ANCESTORS :
+      return Axis.ANCESTOR;
+    case OpCodes.FROM_ANCESTORS_OR_SELF :
+      return Axis.ANCESTORORSELF;
+    case OpCodes.FROM_ATTRIBUTES :
+      return Axis.ATTRIBUTE;
+    case OpCodes.FROM_ROOT :
+      return Axis.ROOT;
+    case OpCodes.FROM_CHILDREN :
+      return Axis.CHILD;
+    case OpCodes.FROM_DESCENDANTS_OR_SELF :
+      return Axis.DESCENDANTORSELF;
+    case OpCodes.FROM_DESCENDANTS :
+      return Axis.DESCENDANT;
+    case OpCodes.FROM_SELF :
+      return Axis.SELF;
+    case OpCodes.OP_EXTFUNCTION :
+    case OpCodes.OP_FUNCTION :
+    case OpCodes.OP_GROUP :
+    case OpCodes.OP_VARIABLE :
+      return Axis.FILTEREDLIST;
+    }
+
+    throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: "
+                               //+ stepType);
+   }
+    
+    /**
+     * Get a corresponding BIT_XXX from an axis.
+     * @param axis One of Axis.ANCESTOR, etc.
+     * @return One of BIT_ANCESTOR, etc.
+     */
+    static public int getAnalysisBitFromAxes(int axis)
+    {
+      switch (axis) // Generate new traverser
+        {
+        case Axis.ANCESTOR :
+          return BIT_ANCESTOR;
+        case Axis.ANCESTORORSELF :
+          return BIT_ANCESTOR_OR_SELF;
+        case Axis.ATTRIBUTE :
+          return BIT_ATTRIBUTE;
+        case Axis.CHILD :
+          return BIT_CHILD;
+        case Axis.DESCENDANT :
+          return BIT_DESCENDANT;
+        case Axis.DESCENDANTORSELF :
+          return BIT_DESCENDANT_OR_SELF;
+        case Axis.FOLLOWING :
+          return BIT_FOLLOWING;
+        case Axis.FOLLOWINGSIBLING :
+          return BIT_FOLLOWING_SIBLING;
+        case Axis.NAMESPACE :
+        case Axis.NAMESPACEDECLS :
+          return BIT_NAMESPACE;
+        case Axis.PARENT :
+          return BIT_PARENT;
+        case Axis.PRECEDING :
+          return BIT_PRECEDING;
+        case Axis.PRECEDINGSIBLING :
+          return BIT_PRECEDING_SIBLING;
+        case Axis.SELF :
+          return BIT_SELF;
+        case Axis.ALLFROMNODE :
+          return BIT_DESCENDANT_OR_SELF;
+          // case Axis.PRECEDINGANDANCESTOR :
+        case Axis.DESCENDANTSFROMROOT :
+        case Axis.ALL :
+        case Axis.DESCENDANTSORSELFFROMROOT :
+          return BIT_ANY_DESCENDANT_FROM_ROOT;
+        case Axis.ROOT :
+          return BIT_ROOT;
+        case Axis.FILTEREDLIST :
+          return BIT_FILTER;
+        default :
+          return BIT_FILTER;
+      }
+    }
+  
+  static boolean functionProximateOrContainsProximate(Compiler compiler, 
+                                                      int opPos)
+  {
+    int endFunc = opPos + compiler.getOp(opPos + 1) - 1;
+    opPos = OpMap.getFirstChildPos(opPos);
+    int funcID = compiler.getOp(opPos);
+    //  System.out.println("funcID: "+funcID);
+    //  System.out.println("opPos: "+opPos);
+    //  System.out.println("endFunc: "+endFunc);
+    switch(funcID)
+    {
+      case FunctionTable.FUNC_LAST:
+      case FunctionTable.FUNC_POSITION:
+        return true;
+      default:
+        opPos++;
+        int i = 0;
+        for (int p = opPos; p < endFunc; p = compiler.getNextOpPos(p), i++)
+        {
+          int innerExprOpPos = p+2;
+          int argOp = compiler.getOp(innerExprOpPos);
+          boolean prox = isProximateInnerExpr(compiler, innerExprOpPos);
+          if(prox)
+            return true;
+        }
+
+    }
+    return false;
+  }
+  
+  static boolean isProximateInnerExpr(Compiler compiler, int opPos)
+  {
+    int op = compiler.getOp(opPos);
+    int innerExprOpPos = opPos+2;
+    switch(op)
+    {
+      case OpCodes.OP_ARGUMENT:
+        if(isProximateInnerExpr(compiler, innerExprOpPos))
+          return true;
+        break;
+      case OpCodes.OP_VARIABLE:
+      case OpCodes.OP_NUMBERLIT:
+      case OpCodes.OP_LITERAL:
+      case OpCodes.OP_LOCATIONPATH:
+        break; // OK
+      case OpCodes.OP_FUNCTION:
+        boolean isProx = functionProximateOrContainsProximate(compiler, opPos);
+        if(isProx)
+          return true;
+        break;
+      case OpCodes.OP_GT:
+      case OpCodes.OP_GTE:
+      case OpCodes.OP_LT:
+      case OpCodes.OP_LTE:
+      case OpCodes.OP_EQUALS:
+        int leftPos = OpMap.getFirstChildPos(op);
+        int rightPos = compiler.getNextOpPos(leftPos);
+        isProx = isProximateInnerExpr(compiler, leftPos);
+        if(isProx)
+          return true;
+        isProx = isProximateInnerExpr(compiler, rightPos);
+        if(isProx)
+          return true;
+        break;
+      default:
+        return true; // be conservative...
+    }
+    return false;
+  }
+    
+  /**
+   * Tell if the predicates need to have proximity knowledge.
+   */
+  public static boolean mightBeProximate(Compiler compiler, int opPos, int stepType)
+          throws javax.xml.transform.TransformerException
+  {
+
+    boolean mightBeProximate = false;
+    int argLen;
+
+    switch (stepType)
+    {
+    case OpCodes.OP_VARIABLE :
+    case OpCodes.OP_EXTFUNCTION :
+    case OpCodes.OP_FUNCTION :
+    case OpCodes.OP_GROUP :
+      argLen = compiler.getArgLength(opPos);
+      break;
+    default :
+      argLen = compiler.getArgLengthOfStep(opPos);
+    }
+
+    int predPos = compiler.getFirstPredicateOpPos(opPos);
+    int count = 0;
+
+    while (OpCodes.OP_PREDICATE == compiler.getOp(predPos))
+    {
+      count++;
+      
+      int innerExprOpPos = predPos+2;
+      int predOp = compiler.getOp(innerExprOpPos);
+
+      switch(predOp)
+      {
+        case OpCodes.OP_VARIABLE:
+        	return true; // Would need more smarts to tell if this could be a number or not!
+        case OpCodes.OP_LOCATIONPATH:
+          // OK.
+          break;
+        case OpCodes.OP_NUMBER:
+        case OpCodes.OP_NUMBERLIT:
+          return true; // that's all she wrote!
+        case OpCodes.OP_FUNCTION:
+          boolean isProx 
+            = functionProximateOrContainsProximate(compiler, innerExprOpPos);
+          if(isProx)
+            return true;
+          break;
+        case OpCodes.OP_GT:
+        case OpCodes.OP_GTE:
+        case OpCodes.OP_LT:
+        case OpCodes.OP_LTE:
+        case OpCodes.OP_EQUALS:
+          int leftPos = OpMap.getFirstChildPos(innerExprOpPos);
+          int rightPos = compiler.getNextOpPos(leftPos);
+          isProx = isProximateInnerExpr(compiler, leftPos);
+          if(isProx)
+            return true;
+          isProx = isProximateInnerExpr(compiler, rightPos);
+          if(isProx)
+            return true;
+          break;
+        default:
+          return true; // be conservative...
+      }
+
+      predPos = compiler.getNextOpPos(predPos);
+    }
+
+    return mightBeProximate;
+  }
+  
+  /**
+   * Special purpose function to see if we can optimize the pattern for 
+   * a DescendantIterator.
+   *
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param stepOpCodePos The opcode position for the step.
+   * @param stepIndex The top-level step index withing the iterator.
+   *
+   * @return 32 bits as an integer that give information about the location
+   * path as a whole.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  private static boolean isOptimizableForDescendantIterator(
+          Compiler compiler, int stepOpCodePos, int stepIndex)
+            throws javax.xml.transform.TransformerException
+  {
+
+    int stepType;
+    int stepCount = 0;
+    boolean foundDorDS = false;
+    boolean foundSelf = false;
+    boolean foundDS = false;
+    
+    int nodeTestType = OpCodes.NODETYPE_NODE;
+    
+    while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos)))
+    {
+      // The DescendantIterator can only do one node test.  If there's more 
+      // than one, use another iterator.
+      if(nodeTestType != OpCodes.NODETYPE_NODE && nodeTestType != OpCodes.NODETYPE_ROOT)
+        return false;
+        
+      stepCount++;
+      if(stepCount > 3)
+        return false;
+        
+      boolean mightBeProximate = mightBeProximate(compiler, stepOpCodePos, stepType);
+      if(mightBeProximate)
+        return false;
+
+      switch (stepType)
+      {
+      case OpCodes.FROM_FOLLOWING :
+      case OpCodes.FROM_FOLLOWING_SIBLINGS :
+      case OpCodes.FROM_PRECEDING :
+      case OpCodes.FROM_PRECEDING_SIBLINGS :
+      case OpCodes.FROM_PARENT :
+      case OpCodes.OP_VARIABLE :
+      case OpCodes.OP_EXTFUNCTION :
+      case OpCodes.OP_FUNCTION :
+      case OpCodes.OP_GROUP :
+      case OpCodes.FROM_NAMESPACE :
+      case OpCodes.FROM_ANCESTORS :
+      case OpCodes.FROM_ANCESTORS_OR_SELF :
+      case OpCodes.FROM_ATTRIBUTES :
+      case OpCodes.MATCH_ATTRIBUTE :
+      case OpCodes.MATCH_ANY_ANCESTOR :
+      case OpCodes.MATCH_IMMEDIATE_ANCESTOR :
+        return false;
+      case OpCodes.FROM_ROOT :
+        if(1 != stepCount)
+          return false;
+        break;
+      case OpCodes.FROM_CHILDREN :
+        if(!foundDS && !(foundDorDS && foundSelf))
+          return false;
+        break;
+      case OpCodes.FROM_DESCENDANTS_OR_SELF :
+        foundDS = true;
+      case OpCodes.FROM_DESCENDANTS :
+        if(3 == stepCount)
+          return false;
+        foundDorDS = true;
+        break;
+      case OpCodes.FROM_SELF :
+        if(1 != stepCount)
+          return false;
+        foundSelf = true;
+        break;
+      default :
+        throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: "
+                                  // + stepType);
+      }
+      
+      nodeTestType = compiler.getStepTestType(stepOpCodePos);
+
+      int nextStepOpCodePos = compiler.getNextStepPos(stepOpCodePos);
+
+      if (nextStepOpCodePos < 0)
+        break;
+        
+      if(OpCodes.ENDOP != compiler.getOp(nextStepOpCodePos))
+      {
+        if(compiler.countPredicates(stepOpCodePos) > 0)
+        {
+          return false;
+        }
+      }
+      
+      stepOpCodePos = nextStepOpCodePos;
+    }
+
+    return true;
+  }
+
+  /**
+   * Analyze the location path and return 32 bits that give information about
+   * the location path as a whole.  See the BIT_XXX constants for meaning about
+   * each of the bits.
+   *
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param stepOpCodePos The opcode position for the step.
+   * @param stepIndex The top-level step index withing the iterator.
+   *
+   * @return 32 bits as an integer that give information about the location
+   * path as a whole.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  private static int analyze(
+          Compiler compiler, int stepOpCodePos, int stepIndex)
+            throws javax.xml.transform.TransformerException
+  {
+
+    int stepType;
+    int stepCount = 0;
+    int analysisResult = 0x00000000;  // 32 bits of analysis
+
+    while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos)))
+    {
+      stepCount++;
+
+      // String namespace = compiler.getStepNS(stepOpCodePos);
+      // boolean isNSWild = (null != namespace) 
+      //                   ? namespace.equals(NodeTest.WILD) : false;
+      // String localname = compiler.getStepLocalName(stepOpCodePos);
+      // boolean isWild = (null != localname) ? localname.equals(NodeTest.WILD) : false;
+      boolean predAnalysis = analyzePredicate(compiler, stepOpCodePos,
+                                              stepType);
+
+      if (predAnalysis)
+        analysisResult |= BIT_PREDICATE;
+
+      switch (stepType)
+      {
+      case OpCodes.OP_VARIABLE :
+      case OpCodes.OP_EXTFUNCTION :
+      case OpCodes.OP_FUNCTION :
+      case OpCodes.OP_GROUP :
+        analysisResult |= BIT_FILTER;
+        break;
+      case OpCodes.FROM_ROOT :
+        analysisResult |= BIT_ROOT;
+        break;
+      case OpCodes.FROM_ANCESTORS :
+        analysisResult |= BIT_ANCESTOR;
+        break;
+      case OpCodes.FROM_ANCESTORS_OR_SELF :
+        analysisResult |= BIT_ANCESTOR_OR_SELF;
+        break;
+      case OpCodes.FROM_ATTRIBUTES :
+        analysisResult |= BIT_ATTRIBUTE;
+        break;
+      case OpCodes.FROM_NAMESPACE :
+        analysisResult |= BIT_NAMESPACE;
+        break;
+      case OpCodes.FROM_CHILDREN :
+        analysisResult |= BIT_CHILD;
+        break;
+      case OpCodes.FROM_DESCENDANTS :
+        analysisResult |= BIT_DESCENDANT;
+        break;
+      case OpCodes.FROM_DESCENDANTS_OR_SELF :
+
+        // Use a special bit to to make sure we get the right analysis of "//foo".
+        if (2 == stepCount && BIT_ROOT == analysisResult)
+        {
+          analysisResult |= BIT_ANY_DESCENDANT_FROM_ROOT;
+        }
+
+        analysisResult |= BIT_DESCENDANT_OR_SELF;
+        break;
+      case OpCodes.FROM_FOLLOWING :
+        analysisResult |= BIT_FOLLOWING;
+        break;
+      case OpCodes.FROM_FOLLOWING_SIBLINGS :
+        analysisResult |= BIT_FOLLOWING_SIBLING;
+        break;
+      case OpCodes.FROM_PRECEDING :
+        analysisResult |= BIT_PRECEDING;
+        break;
+      case OpCodes.FROM_PRECEDING_SIBLINGS :
+        analysisResult |= BIT_PRECEDING_SIBLING;
+        break;
+      case OpCodes.FROM_PARENT :
+        analysisResult |= BIT_PARENT;
+        break;
+      case OpCodes.FROM_SELF :
+        analysisResult |= BIT_SELF;
+        break;
+      case OpCodes.MATCH_ATTRIBUTE :
+        analysisResult |= (BIT_MATCH_PATTERN | BIT_ATTRIBUTE);
+        break;
+      case OpCodes.MATCH_ANY_ANCESTOR :
+        analysisResult |= (BIT_MATCH_PATTERN | BIT_ANCESTOR);
+        break;
+      case OpCodes.MATCH_IMMEDIATE_ANCESTOR :
+        analysisResult |= (BIT_MATCH_PATTERN | BIT_PARENT);
+        break;
+      default :
+        throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: "
+                                   //+ stepType);
+      }
+
+      if (OpCodes.NODETYPE_NODE == compiler.getOp(stepOpCodePos + 3))  // child::node()
+      {
+        analysisResult |= BIT_NODETEST_ANY;
+      }
+
+      stepOpCodePos = compiler.getNextStepPos(stepOpCodePos);
+
+      if (stepOpCodePos < 0)
+        break;
+    }
+
+    analysisResult |= (stepCount & BITS_COUNT);
+
+    return analysisResult;
+  }
+  
+  /**
+   * Tell if the given axis goes downword.  Bogus name, if you can think of 
+   * a better one, please do tell.  This really has to do with inverting 
+   * attribute axis.
+   * @param axis One of Axis.XXX.
+   * @return true if the axis is not a child axis and does not go up from 
+   * the axis root.
+   */
+  public static boolean isDownwardAxisOfMany(int axis)
+  {
+    return ((Axis.DESCENDANTORSELF == axis) ||
+          (Axis.DESCENDANT == axis) 
+          || (Axis.FOLLOWING == axis) 
+//          || (Axis.FOLLOWINGSIBLING == axis) 
+          || (Axis.PRECEDING == axis) 
+//          || (Axis.PRECEDINGSIBLING == axis)
+          );
+  }
+
+  /**
+   * Read a <a href="http://www.w3.org/TR/xpath#location-paths">LocationPath</a>
+   * as a generalized match pattern.  What this means is that the LocationPath
+   * is read backwards, as a test on a given node, to see if it matches the
+   * criteria of the selection, and ends up at the context node.  Essentially,
+   * this is a backwards query from a given node, to find the context node.
+   * <p>So, the selection "foo/daz[2]" is, in non-abreviated expanded syntax,
+   * "self::node()/following-sibling::foo/child::daz[position()=2]".
+   * Taking this as a match pattern for a probable node, it works out to
+   * "self::daz/parent::foo[child::daz[position()=2 and isPrevStepNode()]
+   * precedingSibling::node()[isContextNodeOfLocationPath()]", adding magic
+   * isPrevStepNode and isContextNodeOfLocationPath operations.  Predicates in
+   * the location path have to be executed by the following step,
+   * because they have to know the context of their execution.
+   *
+   * @param mpi The MatchPatternIterator to which the steps will be attached.
+   * @param compiler The compiler that holds the syntax tree/op map to
+   * construct from.
+   * @param stepOpCodePos The current op code position within the opmap.
+   * @param stepIndex The top-level step index withing the iterator.
+   *
+   * @return A StepPattern object, which may contain relative StepPatterns.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  static StepPattern loadSteps(
+          MatchPatternIterator mpi, Compiler compiler, int stepOpCodePos, 
+                                                       int stepIndex)
+            throws javax.xml.transform.TransformerException
+  {
+    if (DEBUG_PATTERN_CREATION)
+    {
+      System.out.println("================");
+      System.out.println("loadSteps for: "+compiler.getPatternString());
+    }
+    int stepType;
+    StepPattern step = null;
+    StepPattern firstStep = null, prevStep = null;
+    int analysis = analyze(compiler, stepOpCodePos, stepIndex);
+
+    while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos)))
+    {
+      step = createDefaultStepPattern(compiler, stepOpCodePos, mpi, analysis,
+                                      firstStep, prevStep);
+
+      if (null == firstStep)
+      {
+        firstStep = step;
+      }
+      else
+      {
+
+        //prevStep.setNextWalker(step);
+        step.setRelativePathPattern(prevStep);
+      }
+
+      prevStep = step;
+      stepOpCodePos = compiler.getNextStepPos(stepOpCodePos);
+
+      if (stepOpCodePos < 0)
+        break;
+    }
+    
+    int axis = Axis.SELF;
+    int paxis = Axis.SELF;
+    StepPattern tail = step;
+    for (StepPattern pat = step; null != pat; 
+         pat = pat.getRelativePathPattern()) 
+    {
+      int nextAxis = pat.getAxis();
+      //int nextPaxis = pat.getPredicateAxis();
+      pat.setAxis(axis);
+      
+      // The predicate axis can't be moved!!!  Test Axes103
+      // pat.setPredicateAxis(paxis);
+      
+      // If we have an attribute or namespace axis that went up, then 
+      // it won't find the attribute in the inverse, since the select-to-match
+      // axes are not invertable (an element is a parent of an attribute, but 
+      // and attribute is not a child of an element).
+      // If we don't do the magic below, then "@*/ancestor-or-self::*" gets
+      // inverted for match to "self::*/descendant-or-self::@*/parent::node()",
+      // which obviously won't work.
+      // So we will rewrite this as:
+      // "self::*/descendant-or-self::*/attribute::*/parent::node()"
+      // Child has to be rewritten a little differently:
+      // select: "@*/parent::*"
+      // inverted match: "self::*/child::@*/parent::node()"
+      // rewrite: "self::*/attribute::*/parent::node()"
+      // Axes that go down in the select, do not have to have special treatment 
+      // in the rewrite. The following inverted match will still not select 
+      // anything.
+      // select: "@*/child::*"
+      // inverted match: "self::*/parent::@*/parent::node()"
+      // Lovely business, this.
+      // -sb
+      int whatToShow = pat.getWhatToShow();
+      if(whatToShow == DTMFilter.SHOW_ATTRIBUTE || 
+         whatToShow == DTMFilter.SHOW_NAMESPACE)
+      {
+        int newAxis = (whatToShow == DTMFilter.SHOW_ATTRIBUTE) ? 
+                       Axis.ATTRIBUTE : Axis.NAMESPACE;
+        if(isDownwardAxisOfMany(axis))
+        {
+          StepPattern attrPat = new StepPattern(whatToShow, 
+                                    pat.getNamespace(),
+                                    pat.getLocalName(),
+                                //newAxis, pat.getPredicateAxis);
+                                                newAxis, 0); // don't care about the predicate axis
+          XNumber score = pat.getStaticScore();
+          pat.setNamespace(null);
+          pat.setLocalName(NodeTest.WILD);
+          attrPat.setPredicates(pat.getPredicates());
+          pat.setPredicates(null);
+          pat.setWhatToShow(DTMFilter.SHOW_ELEMENT);
+          StepPattern rel = pat.getRelativePathPattern();
+          pat.setRelativePathPattern(attrPat);
+          attrPat.setRelativePathPattern(rel);
+          attrPat.setStaticScore(score);
+          
+          // This is needed to inverse a following pattern, because of the 
+          // wacky Xalan rules for following from an attribute.  See axes108.
+          // By these rules, following from an attribute is not strictly 
+          // inverseable.
+          if(Axis.PRECEDING == pat.getAxis())
+            pat.setAxis(Axis.PRECEDINGANDANCESTOR);
+            
+          else if(Axis.DESCENDANT == pat.getAxis())
+            pat.setAxis(Axis.DESCENDANTORSELF);
+          
+          pat = attrPat;
+        }
+        else if(Axis.CHILD == pat.getAxis())
+        {
+          // In this case just change the axis.
+          // pat.setWhatToShow(whatToShow);
+          pat.setAxis(Axis.ATTRIBUTE);
+        }
+      }
+      axis = nextAxis;
+      //paxis = nextPaxis;
+      tail = pat;
+    }
+    
+    if(axis < Axis.ALL)
+    {
+      StepPattern selfPattern = new ContextMatchStepPattern(axis, paxis);
+      // We need to keep the new nodetest from affecting the score...
+      XNumber score = tail.getStaticScore();
+      tail.setRelativePathPattern(selfPattern);
+      tail.setStaticScore(score);
+      selfPattern.setStaticScore(score);
+    }        
+
+    if (DEBUG_PATTERN_CREATION)
+    {
+      System.out.println("Done loading steps: "+step.toString());
+            
+      System.out.println("");
+    }
+    return step;  // start from last pattern?? //firstStep;
+  }
+
+  /**
+   * Create a StepPattern that is contained within a LocationPath.
+   *
+   *
+   * @param compiler The compiler that holds the syntax tree/op map to
+   * construct from.
+   * @param stepOpCodePos The current op code position within the opmap.
+   * @param mpi The MatchPatternIterator to which the steps will be attached.
+   * @param analysis 32 bits of analysis, from which the type of AxesWalker
+   *                 may be influenced.
+   * @param tail The step that is the first step analyzed, but the last 
+   *                  step in the relative match linked list, i.e. the tail.
+   *                  May be null.
+   * @param head The step that is the current head of the relative 
+   *                 match step linked list.
+   *                 May be null.
+   *
+   * @return the head of the list.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  private static StepPattern createDefaultStepPattern(
+          Compiler compiler, int opPos, MatchPatternIterator mpi, 
+          int analysis, StepPattern tail, StepPattern head)
+            throws javax.xml.transform.TransformerException
+  {
+
+    int stepType = compiler.getOp(opPos);
+    boolean simpleInit = false;
+    boolean prevIsOneStepDown = true;
+    
+    int whatToShow = compiler.getWhatToShow(opPos);
+    StepPattern ai = null;
+    int axis, predicateAxis;
+    
+    switch (stepType)
+    {
+    case OpCodes.OP_VARIABLE :
+    case OpCodes.OP_EXTFUNCTION :
+    case OpCodes.OP_FUNCTION :
+    case OpCodes.OP_GROUP :
+      prevIsOneStepDown = false;
+
+      Expression expr;
+
+      switch (stepType)
+      {
+      case OpCodes.OP_VARIABLE :
+      case OpCodes.OP_EXTFUNCTION :
+      case OpCodes.OP_FUNCTION :
+      case OpCodes.OP_GROUP :
+        expr = compiler.compile(opPos);
+        break;
+      default :
+        expr = compiler.compile(opPos + 2);
+      }
+
+      axis = Axis.FILTEREDLIST;
+      predicateAxis = Axis.FILTEREDLIST;
+      ai = new FunctionPattern(expr, axis, predicateAxis);
+      simpleInit = true;
+      break;
+    case OpCodes.FROM_ROOT :
+      whatToShow = DTMFilter.SHOW_DOCUMENT
+                   | DTMFilter.SHOW_DOCUMENT_FRAGMENT;
+
+      axis = Axis.ROOT;
+      predicateAxis = Axis.ROOT;
+      ai = new StepPattern(DTMFilter.SHOW_DOCUMENT | 
+                                DTMFilter.SHOW_DOCUMENT_FRAGMENT,
+                                axis, predicateAxis);
+      break;
+    case OpCodes.FROM_ATTRIBUTES :
+      whatToShow = DTMFilter.SHOW_ATTRIBUTE;
+      axis = Axis.PARENT;
+      predicateAxis = Axis.ATTRIBUTE;
+      // ai = new StepPattern(whatToShow, Axis.SELF, Axis.SELF);
+      break;
+    case OpCodes.FROM_NAMESPACE :
+      whatToShow = DTMFilter.SHOW_NAMESPACE;
+      axis = Axis.PARENT;
+      predicateAxis = Axis.NAMESPACE;
+      // ai = new StepPattern(whatToShow, axis, predicateAxis);
+      break;
+    case OpCodes.FROM_ANCESTORS :
+      axis = Axis.DESCENDANT;
+      predicateAxis = Axis.ANCESTOR;
+      break;
+    case OpCodes.FROM_CHILDREN :
+      axis = Axis.PARENT;
+      predicateAxis = Axis.CHILD;
+      break;
+    case OpCodes.FROM_ANCESTORS_OR_SELF :
+      axis = Axis.DESCENDANTORSELF;
+      predicateAxis = Axis.ANCESTORORSELF;
+      break;
+    case OpCodes.FROM_SELF :
+      axis = Axis.SELF;
+      predicateAxis = Axis.SELF;
+      break;
+    case OpCodes.FROM_PARENT :
+      axis = Axis.CHILD;
+      predicateAxis = Axis.PARENT;
+      break;
+    case OpCodes.FROM_PRECEDING_SIBLINGS :
+      axis = Axis.FOLLOWINGSIBLING;
+      predicateAxis = Axis.PRECEDINGSIBLING;
+      break;
+    case OpCodes.FROM_PRECEDING :
+      axis = Axis.FOLLOWING;
+      predicateAxis = Axis.PRECEDING;
+      break;
+    case OpCodes.FROM_FOLLOWING_SIBLINGS :
+      axis = Axis.PRECEDINGSIBLING;
+      predicateAxis = Axis.FOLLOWINGSIBLING;
+      break;
+    case OpCodes.FROM_FOLLOWING :
+      axis = Axis.PRECEDING;
+      predicateAxis = Axis.FOLLOWING;
+      break;
+    case OpCodes.FROM_DESCENDANTS_OR_SELF :
+      axis = Axis.ANCESTORORSELF;
+      predicateAxis = Axis.DESCENDANTORSELF;
+      break;
+    case OpCodes.FROM_DESCENDANTS :
+      axis = Axis.ANCESTOR;
+      predicateAxis = Axis.DESCENDANT;
+      break;
+    default :
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: "
+                                 //+ stepType);
+    }
+    if(null == ai)
+    {
+      whatToShow = compiler.getWhatToShow(opPos); // %REVIEW%
+      ai = new StepPattern(whatToShow, compiler.getStepNS(opPos),
+                                compiler.getStepLocalName(opPos),
+                                axis, predicateAxis);
+    }
+   
+    if (false || DEBUG_PATTERN_CREATION)
+    {
+      System.out.print("new step: "+ ai);
+      System.out.print(", axis: " + Axis.getNames(ai.getAxis()));
+      System.out.print(", predAxis: " + Axis.getNames(ai.getAxis()));
+      System.out.print(", what: ");
+      System.out.print("    ");
+      ai.debugWhatToShow(ai.getWhatToShow());
+    }
+
+    int argLen = compiler.getFirstPredicateOpPos(opPos);
+
+    ai.setPredicates(compiler.getCompiledPredicates(argLen));
+
+    return ai;
+  }
+
+  /**
+   * Analyze a step and give information about it's predicates.  Right now this
+   * just returns true or false if the step has a predicate.
+   *
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param opPos The opcode position for the step.
+   * @param stepType The type of step, one of OP_GROUP, etc.
+   *
+   * @return true if step has a predicate.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  static boolean analyzePredicate(Compiler compiler, int opPos, int stepType)
+          throws javax.xml.transform.TransformerException
+  {
+
+    int argLen;
+
+    switch (stepType)
+    {
+    case OpCodes.OP_VARIABLE :
+    case OpCodes.OP_EXTFUNCTION :
+    case OpCodes.OP_FUNCTION :
+    case OpCodes.OP_GROUP :
+      argLen = compiler.getArgLength(opPos);
+      break;
+    default :
+      argLen = compiler.getArgLengthOfStep(opPos);
+    }
+
+    int pos = compiler.getFirstPredicateOpPos(opPos);
+    int nPredicates = compiler.countPredicates(pos);
+
+    return (nPredicates > 0) ? true : false;
+  }
+
+  /**
+   * Create the proper Walker from the axes type.
+   *
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param opPos The opcode position for the step.
+   * @param lpi The owning location path iterator.
+   * @param analysis 32 bits of analysis, from which the type of AxesWalker
+   *                 may be influenced.
+   *
+   * @return non-null reference to AxesWalker derivative.
+   * @throws RuntimeException if the input is bad.
+   */
+  private static AxesWalker createDefaultWalker(Compiler compiler, int opPos,
+          WalkingIterator lpi, int analysis)
+  {
+
+    AxesWalker ai = null;
+    int stepType = compiler.getOp(opPos);
+
+    /*
+    System.out.println("0: "+compiler.getOp(opPos));
+    System.out.println("1: "+compiler.getOp(opPos+1));
+    System.out.println("2: "+compiler.getOp(opPos+2));
+    System.out.println("3: "+compiler.getOp(opPos+3));
+    System.out.println("4: "+compiler.getOp(opPos+4));
+    System.out.println("5: "+compiler.getOp(opPos+5));
+    */
+    boolean simpleInit = false;
+    int totalNumberWalkers = (analysis & BITS_COUNT);
+    boolean prevIsOneStepDown = true;
+
+    switch (stepType)
+    {
+    case OpCodes.OP_VARIABLE :
+    case OpCodes.OP_EXTFUNCTION :
+    case OpCodes.OP_FUNCTION :
+    case OpCodes.OP_GROUP :
+      prevIsOneStepDown = false;
+
+      if (DEBUG_WALKER_CREATION)
+        System.out.println("new walker:  FilterExprWalker: " + analysis
+                           + ", " + compiler.toString());
+
+      ai = new FilterExprWalker(lpi);
+      simpleInit = true;
+      break;
+    case OpCodes.FROM_ROOT :
+      ai = new AxesWalker(lpi, Axis.ROOT);
+      break;
+    case OpCodes.FROM_ANCESTORS :
+      prevIsOneStepDown = false;
+      ai = new ReverseAxesWalker(lpi, Axis.ANCESTOR);
+      break;
+    case OpCodes.FROM_ANCESTORS_OR_SELF :
+      prevIsOneStepDown = false;
+      ai = new ReverseAxesWalker(lpi, Axis.ANCESTORORSELF);
+      break;
+    case OpCodes.FROM_ATTRIBUTES :
+      ai = new AxesWalker(lpi, Axis.ATTRIBUTE);
+      break;
+    case OpCodes.FROM_NAMESPACE :
+      ai = new AxesWalker(lpi, Axis.NAMESPACE);
+      break;
+    case OpCodes.FROM_CHILDREN :
+      ai = new AxesWalker(lpi, Axis.CHILD);
+      break;
+    case OpCodes.FROM_DESCENDANTS :
+      prevIsOneStepDown = false;
+      ai = new AxesWalker(lpi, Axis.DESCENDANT);
+      break;
+    case OpCodes.FROM_DESCENDANTS_OR_SELF :
+      prevIsOneStepDown = false;
+      ai = new AxesWalker(lpi, Axis.DESCENDANTORSELF);
+      break;
+    case OpCodes.FROM_FOLLOWING :
+      prevIsOneStepDown = false;
+      ai = new AxesWalker(lpi, Axis.FOLLOWING);
+      break;
+    case OpCodes.FROM_FOLLOWING_SIBLINGS :
+      prevIsOneStepDown = false;
+      ai = new AxesWalker(lpi, Axis.FOLLOWINGSIBLING);
+      break;
+    case OpCodes.FROM_PRECEDING :
+      prevIsOneStepDown = false;
+      ai = new ReverseAxesWalker(lpi, Axis.PRECEDING);
+      break;
+    case OpCodes.FROM_PRECEDING_SIBLINGS :
+      prevIsOneStepDown = false;
+      ai = new ReverseAxesWalker(lpi, Axis.PRECEDINGSIBLING);
+      break;
+    case OpCodes.FROM_PARENT :
+      prevIsOneStepDown = false;
+      ai = new ReverseAxesWalker(lpi, Axis.PARENT);
+      break;
+    case OpCodes.FROM_SELF :
+      ai = new AxesWalker(lpi, Axis.SELF);
+      break;
+    default :
+      throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: "
+                                 //+ stepType);
+    }
+
+    if (simpleInit)
+    {
+      ai.initNodeTest(DTMFilter.SHOW_ALL);
+    }
+    else
+    {
+      int whatToShow = compiler.getWhatToShow(opPos);
+
+      /*
+      System.out.print("construct: ");
+      NodeTest.debugWhatToShow(whatToShow);
+      System.out.println("or stuff: "+(whatToShow & (DTMFilter.SHOW_ATTRIBUTE
+                             | DTMFilter.SHOW_ELEMENT
+                             | DTMFilter.SHOW_PROCESSING_INSTRUCTION)));
+      */
+      if ((0 == (whatToShow
+                 & (DTMFilter.SHOW_ATTRIBUTE | DTMFilter.SHOW_NAMESPACE | DTMFilter.SHOW_ELEMENT
+                    | DTMFilter.SHOW_PROCESSING_INSTRUCTION))) || (whatToShow == DTMFilter.SHOW_ALL))
+        ai.initNodeTest(whatToShow);
+      else
+      {
+        ai.initNodeTest(whatToShow, compiler.getStepNS(opPos),
+                        compiler.getStepLocalName(opPos));
+      }
+    }
+
+    return ai;
+  }
+  
+  public static String getAnalysisString(int analysis)
+  {
+    StringBuffer buf = new StringBuffer();
+    buf.append("count: "+getStepCount(analysis)+" ");
+    if((analysis & BIT_NODETEST_ANY) != 0)
+    {
+      buf.append("NTANY|");
+    }
+    if((analysis & BIT_PREDICATE) != 0)
+    {
+      buf.append("PRED|");
+    }
+    if((analysis & BIT_ANCESTOR) != 0)
+    {
+      buf.append("ANC|");
+    }
+    if((analysis & BIT_ANCESTOR_OR_SELF) != 0)
+    {
+      buf.append("ANCOS|");
+    }
+    if((analysis & BIT_ATTRIBUTE) != 0)
+    {
+      buf.append("ATTR|");
+    }
+    if((analysis & BIT_CHILD) != 0)
+    {
+      buf.append("CH|");
+    }
+    if((analysis & BIT_DESCENDANT) != 0)
+    {
+      buf.append("DESC|");
+    }
+    if((analysis & BIT_DESCENDANT_OR_SELF) != 0)
+    {
+      buf.append("DESCOS|");
+    }
+    if((analysis & BIT_FOLLOWING) != 0)
+    {
+      buf.append("FOL|");
+    }
+    if((analysis & BIT_FOLLOWING_SIBLING) != 0)
+    {
+      buf.append("FOLS|");
+    }
+    if((analysis & BIT_NAMESPACE) != 0)
+    {
+      buf.append("NS|");
+    }
+    if((analysis & BIT_PARENT) != 0)
+    {
+      buf.append("P|");
+    }
+    if((analysis & BIT_PRECEDING) != 0)
+    {
+      buf.append("PREC|");
+    }
+    if((analysis & BIT_PRECEDING_SIBLING) != 0)
+    {
+      buf.append("PRECS|");
+    }
+    if((analysis & BIT_SELF) != 0)
+    {
+      buf.append(".|");
+    }
+    if((analysis & BIT_FILTER) != 0)
+    {
+      buf.append("FLT|");
+    }
+    if((analysis & BIT_ROOT) != 0)
+    {
+      buf.append("R|");
+    }
+    return buf.toString();
+  }
+
+  /** Set to true for diagnostics about walker creation */
+  static final boolean DEBUG_PATTERN_CREATION = false;
+
+  /** Set to true for diagnostics about walker creation */
+  static final boolean DEBUG_WALKER_CREATION = false;
+
+  /** Set to true for diagnostics about iterator creation */
+  static final boolean DEBUG_ITERATOR_CREATION = false;
+  
+  public static boolean hasPredicate(int analysis)
+  {
+    return (0 != (analysis & BIT_PREDICATE));
+  }
+
+  public static boolean isWild(int analysis)
+  {
+    return (0 != (analysis & BIT_NODETEST_ANY));
+  }
+
+  public static boolean walksAncestors(int analysis)
+  {
+    return isSet(analysis, BIT_ANCESTOR | BIT_ANCESTOR_OR_SELF);
+  }
+  
+  public static boolean walksAttributes(int analysis)
+  {
+    return (0 != (analysis & BIT_ATTRIBUTE));
+  }
+
+  public static boolean walksNamespaces(int analysis)
+  {
+    return (0 != (analysis & BIT_NAMESPACE));
+  }  
+
+  public static boolean walksChildren(int analysis)
+  {
+    return (0 != (analysis & BIT_CHILD));
+  }
+
+  public static boolean walksDescendants(int analysis)
+  {
+    return isSet(analysis, BIT_DESCENDANT | BIT_DESCENDANT_OR_SELF);
+  }
+
+  public static boolean walksSubtree(int analysis)
+  {
+    return isSet(analysis, BIT_DESCENDANT | BIT_DESCENDANT_OR_SELF | BIT_CHILD);
+  }
+  
+  public static boolean walksSubtreeOnlyMaybeAbsolute(int analysis)
+  {
+    return walksSubtree(analysis)
+           && !walksExtraNodes(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           ;
+  }
+  
+  public static boolean walksSubtreeOnly(int analysis)
+  {
+    return walksSubtreeOnlyMaybeAbsolute(analysis) 
+           && !isAbsolute(analysis) 
+           ;
+  }
+
+  public static boolean walksFilteredList(int analysis)
+  {
+    return isSet(analysis, BIT_FILTER);
+  }
+  
+  public static boolean walksSubtreeOnlyFromRootOrContext(int analysis)
+  {
+    return walksSubtree(analysis)
+           && !walksExtraNodes(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && !isSet(analysis, BIT_FILTER) 
+           ;
+  }
+
+  public static boolean walksInDocOrder(int analysis)
+  {
+    return (walksSubtreeOnlyMaybeAbsolute(analysis)
+           || walksExtraNodesOnly(analysis)
+           || walksFollowingOnlyMaybeAbsolute(analysis)) 
+           && !isSet(analysis, BIT_FILTER) 
+           ;
+  }
+  
+  public static boolean walksFollowingOnlyMaybeAbsolute(int analysis)
+  {
+    return isSet(analysis, BIT_SELF | BIT_FOLLOWING_SIBLING | BIT_FOLLOWING)
+           && !walksSubtree(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           ;
+  }
+  
+  public static boolean walksUp(int analysis)
+  {
+    return isSet(analysis, BIT_PARENT | BIT_ANCESTOR | BIT_ANCESTOR_OR_SELF);
+  }
+  
+  public static boolean walksSideways(int analysis)
+  {
+    return isSet(analysis, BIT_FOLLOWING | BIT_FOLLOWING_SIBLING | 
+                           BIT_PRECEDING | BIT_PRECEDING_SIBLING);
+  }
+  
+  public static boolean walksExtraNodes(int analysis)
+  {
+    return isSet(analysis, BIT_NAMESPACE | BIT_ATTRIBUTE);
+  }
+
+  public static boolean walksExtraNodesOnly(int analysis)
+  {
+    return walksExtraNodes(analysis)
+           && !isSet(analysis, BIT_SELF) 
+           && !walksSubtree(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && !isAbsolute(analysis) 
+           ;
+  }
+
+  public static boolean isAbsolute(int analysis)
+  {
+    return isSet(analysis, BIT_ROOT | BIT_FILTER);
+  }
+  
+  public static boolean walksChildrenOnly(int analysis)
+  {
+    return walksChildren(analysis)
+           && !isSet(analysis, BIT_SELF)
+           && !walksExtraNodes(analysis)
+           && !walksDescendants(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && (!isAbsolute(analysis) || isSet(analysis, BIT_ROOT))
+           ;
+  }
+  
+  public static boolean walksChildrenAndExtraAndSelfOnly(int analysis)
+  {
+    return walksChildren(analysis)
+           && !walksDescendants(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && (!isAbsolute(analysis) || isSet(analysis, BIT_ROOT))
+           ;
+  }
+  
+  public static boolean walksDescendantsAndExtraAndSelfOnly(int analysis)
+  {
+    return !walksChildren(analysis)
+           && walksDescendants(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && (!isAbsolute(analysis) || isSet(analysis, BIT_ROOT))
+           ;
+  }
+  
+  public static boolean walksSelfOnly(int analysis)
+  {
+    return isSet(analysis, BIT_SELF) 
+           && !walksSubtree(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && !isAbsolute(analysis) 
+           ;
+  }
+
+  
+  public static boolean walksUpOnly(int analysis)
+  {
+    return !walksSubtree(analysis) 
+           && walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && !isAbsolute(analysis) 
+           ;
+  }
+  
+  public static boolean walksDownOnly(int analysis)
+  {
+    return walksSubtree(analysis) 
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && !isAbsolute(analysis) 
+           ;
+  }
+
+  public static boolean walksDownExtraOnly(int analysis)
+  {
+    return walksSubtree(analysis) &&  walksExtraNodes(analysis)
+           && !walksUp(analysis) 
+           && !walksSideways(analysis) 
+           && !isAbsolute(analysis) 
+           ;
+  }
+  
+  public static boolean canSkipSubtrees(int analysis)
+  {
+    return isSet(analysis, BIT_CHILD) | walksSideways(analysis);
+  }
+  
+  public static boolean canCrissCross(int analysis)
+  {
+    // This could be done faster.  Coded for clarity.
+    if(walksSelfOnly(analysis))
+      return false;
+    else if(walksDownOnly(analysis) && !canSkipSubtrees(analysis))
+      return false;
+    else if(walksChildrenAndExtraAndSelfOnly(analysis))
+      return false;
+    else if(walksDescendantsAndExtraAndSelfOnly(analysis))
+      return false;
+    else if(walksUpOnly(analysis))
+      return false;
+    else if(walksExtraNodesOnly(analysis))
+      return false;
+    else if(walksSubtree(analysis) 
+           && (walksSideways(analysis) 
+            || walksUp(analysis) 
+            || canSkipSubtrees(analysis)))
+      return true;
+    else
+      return false;
+  }
+  
+  /**
+   * Tell if the pattern can be 'walked' with the iteration steps in natural 
+   * document order, without duplicates.
+   *
+   * @param analysis The general analysis of the pattern.
+   *
+   * @return true if the walk can be done in natural order.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  static public boolean isNaturalDocOrder(int analysis)
+  {
+    if(canCrissCross(analysis) || isSet(analysis, BIT_NAMESPACE) ||
+       walksFilteredList(analysis))
+      return false;
+      
+    if(walksInDocOrder(analysis))
+      return true;
+      
+    return false;
+  }
+  
+  /**
+   * Tell if the pattern can be 'walked' with the iteration steps in natural 
+   * document order, without duplicates.
+   *
+   * @param compiler non-null reference to compiler object that has processed
+   *                 the XPath operations into an opcode map.
+   * @param stepOpCodePos The opcode position for the step.
+   * @param stepIndex The top-level step index withing the iterator.
+   * @param analysis The general analysis of the pattern.
+   *
+   * @return true if the walk can be done in natural order.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  private static boolean isNaturalDocOrder(
+          Compiler compiler, int stepOpCodePos, int stepIndex, int analysis)
+            throws javax.xml.transform.TransformerException
+  {
+    if(canCrissCross(analysis))
+      return false;
+      
+    // Namespaces can present some problems, so just punt if we're looking for 
+    // these.
+    if(isSet(analysis, BIT_NAMESPACE))
+      return false;
+      
+    // The following, preceding, following-sibling, and preceding sibling can 
+    // be found in doc order if we get to this point, but if they occur 
+    // together, they produce 
+    // duplicates, so it's better for us to eliminate this case so we don't 
+    // have to check for duplicates during runtime if we're using a 
+    // WalkingIterator.
+    if(isSet(analysis, BIT_FOLLOWING | BIT_FOLLOWING_SIBLING) && 
+       isSet(analysis, BIT_PRECEDING | BIT_PRECEDING_SIBLING))
+      return  false;
+      
+    // OK, now we have to check for select="@*/axis::*" patterns, which 
+    // can also cause duplicates to happen.  But select="axis*/@::*" patterns 
+    // are OK, as are select="@foo/axis::*" patterns.
+    // Unfortunately, we can't do this just via the analysis bits.
+    
+    int stepType;
+    int stepCount = 0;
+    boolean foundWildAttribute = false;
+    
+    // Steps that can traverse anything other than down a 
+    // subtree or that can produce duplicates when used in 
+    // combonation are counted with this variable.
+    int potentialDuplicateMakingStepCount = 0;
+    
+    while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos)))
+    {        
+      stepCount++;
+        
+      switch (stepType)
+      {
+      case OpCodes.FROM_ATTRIBUTES :
+      case OpCodes.MATCH_ATTRIBUTE :
+        if(foundWildAttribute) // Maybe not needed, but be safe.
+          return false;
+        
+        // This doesn't seem to work as a test for wild card.  Hmph.
+        // int nodeTestType = compiler.getStepTestType(stepOpCodePos);  
+        
+        String localName = compiler.getStepLocalName(stepOpCodePos);
+        // System.err.println("localName: "+localName);
+        if(localName.equals("*"))
+        {
+          foundWildAttribute = true;
+        }
+        break;
+      case OpCodes.FROM_FOLLOWING :
+      case OpCodes.FROM_FOLLOWING_SIBLINGS :
+      case OpCodes.FROM_PRECEDING :
+      case OpCodes.FROM_PRECEDING_SIBLINGS :
+      case OpCodes.FROM_PARENT :
+      case OpCodes.OP_VARIABLE :
+      case OpCodes.OP_EXTFUNCTION :
+      case OpCodes.OP_FUNCTION :
+      case OpCodes.OP_GROUP :
+      case OpCodes.FROM_NAMESPACE :
+      case OpCodes.FROM_ANCESTORS :
+      case OpCodes.FROM_ANCESTORS_OR_SELF :      
+      case OpCodes.MATCH_ANY_ANCESTOR :
+      case OpCodes.MATCH_IMMEDIATE_ANCESTOR :
+      case OpCodes.FROM_DESCENDANTS_OR_SELF :
+      case OpCodes.FROM_DESCENDANTS :
+        if(potentialDuplicateMakingStepCount > 0)
+            return false;
+        potentialDuplicateMakingStepCount++;
+      case OpCodes.FROM_ROOT :
+      case OpCodes.FROM_CHILDREN :
+      case OpCodes.FROM_SELF :
+        if(foundWildAttribute)
+          return false;
+        break;
+      default :
+        throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: "
+                                  // + stepType);
+      }
+
+      int nextStepOpCodePos = compiler.getNextStepPos(stepOpCodePos);
+
+      if (nextStepOpCodePos < 0)
+        break;
+              
+      stepOpCodePos = nextStepOpCodePos;
+    }
+
+    return true;
+  }
+  
+  public static boolean isOneStep(int analysis)
+  {
+    return (analysis & BITS_COUNT) == 0x00000001;
+  }
+
+  public static int getStepCount(int analysis)
+  {
+    return (analysis & BITS_COUNT);
+  }
+
+  /**
+   * First 8 bits are the number of top-level location steps.  Hopefully
+   *  there will never be more that 255 location steps!!!
+   */
+  public static final int BITS_COUNT = 0x000000FF;
+
+  /** 4 bits are reserved for future use. */
+  public static final int BITS_RESERVED = 0x00000F00;
+
+  /** Bit is on if the expression contains a top-level predicate. */
+  public static final int BIT_PREDICATE = (0x00001000);
+
+  /** Bit is on if any of the walkers contain an ancestor step. */
+  public static final int BIT_ANCESTOR = (0x00001000 << 1);
+
+  /** Bit is on if any of the walkers contain an ancestor-or-self step. */
+  public static final int BIT_ANCESTOR_OR_SELF = (0x00001000 << 2);
+
+  /** Bit is on if any of the walkers contain an attribute step. */
+  public static final int BIT_ATTRIBUTE = (0x00001000 << 3);
+
+  /** Bit is on if any of the walkers contain a child step. */
+  public static final int BIT_CHILD = (0x00001000 << 4);
+
+  /** Bit is on if any of the walkers contain a descendant step. */
+  public static final int BIT_DESCENDANT = (0x00001000 << 5);
+
+  /** Bit is on if any of the walkers contain a descendant-or-self step. */
+  public static final int BIT_DESCENDANT_OR_SELF = (0x00001000 << 6);
+
+  /** Bit is on if any of the walkers contain a following step. */
+  public static final int BIT_FOLLOWING = (0x00001000 << 7);
+
+  /** Bit is on if any of the walkers contain a following-sibiling step. */
+  public static final int BIT_FOLLOWING_SIBLING = (0x00001000 << 8);
+
+  /** Bit is on if any of the walkers contain a namespace step. */
+  public static final int BIT_NAMESPACE = (0x00001000 << 9);
+
+  /** Bit is on if any of the walkers contain a parent step. */
+  public static final int BIT_PARENT = (0x00001000 << 10);
+
+  /** Bit is on if any of the walkers contain a preceding step. */
+  public static final int BIT_PRECEDING = (0x00001000 << 11);
+
+  /** Bit is on if any of the walkers contain a preceding-sibling step. */
+  public static final int BIT_PRECEDING_SIBLING = (0x00001000 << 12);
+
+  /** Bit is on if any of the walkers contain a self step. */
+  public static final int BIT_SELF = (0x00001000 << 13);
+
+  /**
+   * Bit is on if any of the walkers contain a filter (i.e. id(), extension
+   *  function, etc.) step.
+   */
+  public static final int BIT_FILTER = (0x00001000 << 14);
+
+  /** Bit is on if any of the walkers contain a root step. */
+  public static final int BIT_ROOT = (0x00001000 << 15);
+
+  /**
+   * If any of these bits are on, the expression may likely traverse outside
+   *  the given subtree.
+   */
+  public static final int BITMASK_TRAVERSES_OUTSIDE_SUBTREE = (BIT_NAMESPACE  // ??
+                                                                | BIT_PRECEDING_SIBLING
+                                                                | BIT_PRECEDING
+                                                                | BIT_FOLLOWING_SIBLING
+                                                                | BIT_FOLLOWING
+                                                                | BIT_PARENT  // except parent of attrs.
+                                                                | BIT_ANCESTOR_OR_SELF
+                                                                | BIT_ANCESTOR
+                                                                | BIT_FILTER
+                                                                | BIT_ROOT);
+
+  /**
+   * Bit is on if any of the walkers can go backwards in document
+   *  order from the context node.
+   */
+  public static final int BIT_BACKWARDS_SELF = (0x00001000 << 16);
+
+  /** Found "//foo" pattern */
+  public static final int BIT_ANY_DESCENDANT_FROM_ROOT = (0x00001000 << 17);
+
+  /**
+   * Bit is on if any of the walkers contain an node() test.  This is
+   *  really only useful if the count is 1.
+   */
+  public static final int BIT_NODETEST_ANY = (0x00001000 << 18);
+
+  // can't go higher than 18!
+
+  /** Bit is on if the expression is a match pattern. */
+  public static final int BIT_MATCH_PATTERN = (0x00001000 << 19);
+}
diff --git a/src/main/java/org/apache/xpath/axes/WalkingIterator.java b/src/main/java/org/apache/xpath/axes/WalkingIterator.java
new file mode 100644
index 0000000..e82a125
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/WalkingIterator.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WalkingIterator.java 469314 2006-10-30 23:31:59Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.compiler.OpMap;
+
+/**
+ * Location path iterator that uses Walkers.
+ */
+
+public class WalkingIterator extends LocPathIterator implements ExpressionOwner
+{
+    static final long serialVersionUID = 9110225941815665906L;
+  /**
+   * Create a WalkingIterator iterator, including creation
+   * of step walkers from the opcode list, and call back
+   * into the Compiler to create predicate expressions.
+   *
+   * @param compiler The Compiler which is creating
+   * this expression.
+   * @param opPos The position of this iterator in the
+   * opcode list from the compiler.
+   * @param shouldLoadWalkers True if walkers should be
+   * loaded, or false if this is a derived iterator and
+   * it doesn't wish to load child walkers.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  WalkingIterator(
+          Compiler compiler, int opPos, int analysis, boolean shouldLoadWalkers)
+            throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis, shouldLoadWalkers);
+    
+    int firstStepPos = OpMap.getFirstChildPos(opPos);
+
+    if (shouldLoadWalkers)
+    {
+      m_firstWalker = WalkerFactory.loadWalkers(this, compiler, firstStepPos, 0);
+      m_lastUsedWalker = m_firstWalker;
+    }
+  }
+  
+  /**
+   * Create a WalkingIterator object.
+   *
+   * @param nscontext The namespace context for this iterator,
+   * should be OK if null.
+   */
+  public WalkingIterator(PrefixResolver nscontext)
+  {
+
+    super(nscontext);
+  }
+  
+  
+  /** 
+   * Get the analysis bits for this walker, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits()
+  {
+    int bits = 0;
+    if (null != m_firstWalker)
+    {    	
+      AxesWalker walker = m_firstWalker;
+
+      while (null != walker)
+      {
+        int bit = walker.getAnalysisBits();
+        bits |= bit;
+        walker = walker.getNextWalker();
+      }       
+    }
+    return bits;
+  }
+  
+  /**
+   * Get a cloned WalkingIterator that holds the same
+   * position as this iterator.
+   *
+   * @return A clone of this iterator that holds the same node position.
+   *
+   * @throws CloneNotSupportedException
+   */
+  public Object clone() throws CloneNotSupportedException
+  {
+
+    WalkingIterator clone = (WalkingIterator) super.clone();
+
+    //    clone.m_varStackPos = this.m_varStackPos;
+    //    clone.m_varStackContext = this.m_varStackContext;
+    if (null != m_firstWalker)
+    {
+      clone.m_firstWalker = m_firstWalker.cloneDeep(clone, null);
+    }
+
+    return clone;
+  }
+  
+  /**
+   * Reset the iterator.
+   */
+  public void reset()
+  {
+
+    super.reset();
+
+    if (null != m_firstWalker)
+    {
+      m_lastUsedWalker = m_firstWalker;
+
+      m_firstWalker.setRoot(m_context);
+    }
+
+  }
+  
+  /**
+   * Initialize the context values for this expression
+   * after it is cloned.
+   *
+   * @param context The XPath runtime context for this
+   * transformation.
+   */
+  public void setRoot(int context, Object environment)
+  {
+
+    super.setRoot(context, environment);
+    
+    if(null != m_firstWalker)
+    {
+      m_firstWalker.setRoot(context);
+      m_lastUsedWalker = m_firstWalker;
+    }
+  }
+  
+  /**
+   *  Returns the next node in the set and advances the position of the
+   * iterator in the set. After a NodeIterator is created, the first call
+   * to nextNode() returns the first node in the set.
+   * @return  The next <code>Node</code> in the set being iterated over, or
+   *   <code>null</code> if there are no more members in that set.
+   */
+  public int nextNode()
+  {
+  	if(m_foundLast)
+  		return DTM.NULL;
+
+    // If the variable stack position is not -1, we'll have to 
+    // set our position in the variable stack, so our variable access 
+    // will be correct.  Iterators that are at the top level of the 
+    // expression need to reset the variable stack, while iterators 
+    // in predicates do not need to, and should not, since their execution
+    // may be much later than top-level iterators.  
+    // m_varStackPos is set in setRoot, which is called 
+    // from the execute method.
+    if (-1 == m_stackFrame)
+    {
+      return returnNextNode(m_firstWalker.nextNode());
+    }
+    else
+    {
+      VariableStack vars = m_execContext.getVarStack();
+
+      // These three statements need to be combined into one operation.
+      int savedStart = vars.getStackFrame();
+
+      vars.setStackFrame(m_stackFrame);
+
+      int n = returnNextNode(m_firstWalker.nextNode());
+
+      // These two statements need to be combined into one operation.
+      vars.setStackFrame(savedStart);
+
+      return n;
+    }
+  }
+
+  
+  /**
+   * Get the head of the walker list.
+   *
+   * @return The head of the walker list, or null
+   * if this iterator does not implement walkers.
+   * @xsl.usage advanced
+   */
+  public final AxesWalker getFirstWalker()
+  {
+    return m_firstWalker;
+  }
+  
+  /**
+   * Set the head of the walker list.
+   * 
+   * @param walker Should be a valid AxesWalker.
+   * @xsl.usage advanced
+   */
+  public final void setFirstWalker(AxesWalker walker)
+  {
+    m_firstWalker = walker;
+  }
+
+
+  /**
+   * Set the last used walker.
+   *
+   * @param walker The last used walker, or null.
+   * @xsl.usage advanced
+   */
+  public final void setLastUsedWalker(AxesWalker walker)
+  {
+    m_lastUsedWalker = walker;
+  }
+
+  /**
+   * Get the last used walker.
+   *
+   * @return The last used walker, or null.
+   * @xsl.usage advanced
+   */
+  public final AxesWalker getLastUsedWalker()
+  {
+    return m_lastUsedWalker;
+  }
+  
+  /**
+   *  Detaches the iterator from the set which it iterated over, releasing
+   * any computational resources and placing the iterator in the INVALID
+   * state. After<code>detach</code> has been invoked, calls to
+   * <code>nextNode</code> or<code>previousNode</code> will raise the
+   * exception INVALID_STATE_ERR.
+   */
+  public void detach()
+  {   
+    if(m_allowDetach)
+    {
+	  	AxesWalker walker = m_firstWalker; 
+	    while (null != walker)
+	    {
+	      walker.detach();
+	      walker = walker.getNextWalker();
+	    }
+	
+	    m_lastUsedWalker = null;
+	    
+	    // Always call the superclass detach last!
+	    super.detach();
+    }
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    m_predicateIndex = -1;
+
+    AxesWalker walker = m_firstWalker;
+
+    while (null != walker)
+    {
+      walker.fixupVariables(vars, globalsSize);
+      walker = walker.getNextWalker();
+    }
+  }
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	 	if(visitor.visitLocationPath(owner, this))
+  	 	{
+  	 		if(null != m_firstWalker)
+  	 		{
+  	 			m_firstWalker.callVisitors(this, visitor);
+  	 		}
+  	 	}
+  }
+
+  
+  /** The last used step walker in the walker list.
+   *  @serial */
+  protected AxesWalker m_lastUsedWalker;
+
+  /** The head of the step walker list.
+   *  @serial */
+  protected AxesWalker m_firstWalker;
+
+  /**
+   * @see ExpressionOwner#getExpression()
+   */
+  public Expression getExpression()
+  {
+    return m_firstWalker;
+  }
+
+  /**
+   * @see ExpressionOwner#setExpression(Expression)
+   */
+  public void setExpression(Expression exp)
+  {
+  	exp.exprSetParent(this);
+  	m_firstWalker = (AxesWalker)exp;
+  }
+  
+    /**
+     * @see Expression#deepEquals(Expression)
+     */
+    public boolean deepEquals(Expression expr)
+    {
+      if (!super.deepEquals(expr))
+                return false;
+
+      AxesWalker walker1 = m_firstWalker;
+      AxesWalker walker2 = ((WalkingIterator)expr).m_firstWalker;
+      while ((null != walker1) && (null != walker2))
+      {
+        if(!walker1.deepEquals(walker2))
+        	return false;
+        walker1 = walker1.getNextWalker();
+        walker2 = walker2.getNextWalker();
+      }
+      
+      if((null != walker1) || (null != walker2))
+      	return false;
+
+      return true;
+    }
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/WalkingIteratorSorted.java b/src/main/java/org/apache/xpath/axes/WalkingIteratorSorted.java
new file mode 100644
index 0000000..0878cfe
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/WalkingIteratorSorted.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WalkingIteratorSorted.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.axes;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.compiler.Compiler;
+
+/**
+ * This class iterates over set of nodes that needs to be sorted.
+ * @xsl.usage internal
+ */
+public class WalkingIteratorSorted extends WalkingIterator
+{
+    static final long serialVersionUID = -4512512007542368213L;
+
+//  /** True if the nodes will be found in document order */
+//  protected boolean m_inNaturalOrder = false;
+  
+  /** True if the nodes will be found in document order, and this can 
+   * be determined statically. */
+  protected boolean m_inNaturalOrderStatic = false;
+
+  /**
+   * Create a WalkingIteratorSorted object.
+   *
+   * @param nscontext The namespace context for this iterator,
+   * should be OK if null.
+   */
+  public WalkingIteratorSorted(PrefixResolver nscontext)
+  {
+    super(nscontext);
+  }
+
+  /**
+   * Create a WalkingIterator iterator, including creation
+   * of step walkers from the opcode list, and call back
+   * into the Compiler to create predicate expressions.
+   *
+   * @param compiler The Compiler which is creating
+   * this expression.
+   * @param opPos The position of this iterator in the
+   * opcode list from the compiler.
+   * @param shouldLoadWalkers True if walkers should be
+   * loaded, or false if this is a derived iterator and
+   * it doesn't wish to load child walkers.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  WalkingIteratorSorted(
+          Compiler compiler, int opPos, int analysis, boolean shouldLoadWalkers)
+            throws javax.xml.transform.TransformerException
+  {
+    super(compiler, opPos, analysis, shouldLoadWalkers);
+  }
+  
+  /**
+   * Returns true if all the nodes in the iteration well be returned in document 
+   * order.
+   * 
+   * @return true as a default.
+   */
+  public boolean isDocOrdered()
+  {
+    return m_inNaturalOrderStatic;
+  }
+
+    
+  /**
+   * Tell if the nodeset can be walked in doc order, via static analysis. 
+   *
+   *
+   * @return true if the nodeset can be walked in doc order, without sorting.
+   */
+  boolean canBeWalkedInNaturalDocOrderStatic()
+  {
+
+    if (null != m_firstWalker)
+    {
+      AxesWalker walker = m_firstWalker;
+      int prevAxis = -1;
+      boolean prevIsSimpleDownAxis = true;
+
+      for(int i = 0; null != walker; i++)
+      {
+        int axis = walker.getAxis();
+        
+        if(walker.isDocOrdered())
+        {
+          boolean isSimpleDownAxis = ((axis == Axis.CHILD)
+                                   || (axis == Axis.SELF)
+                                   || (axis == Axis.ROOT));
+          // Catching the filtered list here is only OK because
+          // FilterExprWalker#isDocOrdered() did the right thing.
+          if(isSimpleDownAxis || (axis == -1))
+            walker = walker.getNextWalker();
+          else
+          {
+            boolean isLastWalker = (null == walker.getNextWalker());
+            if(isLastWalker)
+            {
+              if(walker.isDocOrdered() && (axis == Axis.DESCENDANT || 
+                 axis == Axis.DESCENDANTORSELF || axis == Axis.DESCENDANTSFROMROOT
+                 || axis == Axis.DESCENDANTSORSELFFROMROOT) || (axis == Axis.ATTRIBUTE))
+                return true;
+            }
+            return false;
+          }
+        }
+        else
+          return false;
+      }
+      return true;
+    }
+    return false;
+  }
+
+
+//  /**
+//   * NEEDSDOC Method canBeWalkedInNaturalDocOrder 
+//   *
+//   *
+//   * NEEDSDOC (canBeWalkedInNaturalDocOrder) @return
+//   */
+//  boolean canBeWalkedInNaturalDocOrder()
+//  {
+//
+//    if (null != m_firstWalker)
+//    {
+//      AxesWalker walker = m_firstWalker;
+//      int prevAxis = -1;
+//      boolean prevIsSimpleDownAxis = true;
+//
+//      for(int i = 0; null != walker; i++)
+//      {
+//        int axis = walker.getAxis();
+//        
+//        if(walker.isDocOrdered())
+//        {
+//          boolean isSimpleDownAxis = ((axis == Axis.CHILD)
+//                                   || (axis == Axis.SELF)
+//                                   || (axis == Axis.ROOT));
+//          // Catching the filtered list here is only OK because
+//          // FilterExprWalker#isDocOrdered() did the right thing.
+//          if(isSimpleDownAxis || (axis == -1))
+//            walker = walker.getNextWalker();
+//          else
+//          {
+//            boolean isLastWalker = (null == walker.getNextWalker());
+//            if(isLastWalker)
+//            {
+//              if(walker.isDocOrdered() && (axis == Axis.DESCENDANT || 
+//                 axis == Axis.DESCENDANTORSELF || axis == Axis.DESCENDANTSFROMROOT
+//                 || axis == Axis.DESCENDANTSORSELFFROMROOT) || (axis == Axis.ATTRIBUTE))
+//                return true;
+//            }
+//            return false;
+//          }
+//        }
+//        else
+//          return false;
+//      }
+//      return true;
+//    }
+//    return false;
+//  }
+  
+  /**
+   * This function is used to perform some extra analysis of the iterator.
+   * 
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+
+    int analysis = getAnalysisBits();
+    if(WalkerFactory.isNaturalDocOrder(analysis))
+    {
+    	m_inNaturalOrderStatic = true;
+    }
+    else
+    {
+    	m_inNaturalOrderStatic = false;
+    	// System.out.println("Setting natural doc order to false: "+
+    	//    WalkerFactory.getAnalysisString(analysis));
+    }
+    
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/axes/package.html b/src/main/java/org/apache/xpath/axes/package.html
new file mode 100644
index 0000000..4fc1234
--- /dev/null
+++ b/src/main/java/org/apache/xpath/axes/package.html
@@ -0,0 +1,27 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468655 2006-10-28 07:12:06Z minchau $ -->
+<html>
+  <title>XPath LocationPath support.</title>
+  <body>
+    <p>Implementation of XPath LocationPath support -- primary classes are 
+    LocPathIterator and UnionPathIterator.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xpath/compiler/Compiler.java b/src/main/java/org/apache/xpath/compiler/Compiler.java
new file mode 100644
index 0000000..70b1ea1
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/Compiler.java
@@ -0,0 +1,1268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Compiler.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.compiler;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.SAXSourceLocator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.axes.UnionPathIterator;
+import org.apache.xpath.axes.WalkerFactory;
+import org.apache.xpath.functions.FuncExtFunction;
+import org.apache.xpath.functions.FuncExtFunctionAvailable;
+import org.apache.xpath.functions.Function;
+import org.apache.xpath.functions.WrongNumberArgsException;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XString;
+import org.apache.xpath.operations.And;
+import org.apache.xpath.operations.Div;
+import org.apache.xpath.operations.Equals;
+import org.apache.xpath.operations.Gt;
+import org.apache.xpath.operations.Gte;
+import org.apache.xpath.operations.Lt;
+import org.apache.xpath.operations.Lte;
+import org.apache.xpath.operations.Minus;
+import org.apache.xpath.operations.Mod;
+import org.apache.xpath.operations.Mult;
+import org.apache.xpath.operations.Neg;
+import org.apache.xpath.operations.NotEquals;
+import org.apache.xpath.operations.Operation;
+import org.apache.xpath.operations.Or;
+import org.apache.xpath.operations.Plus;
+import org.apache.xpath.operations.UnaryOperation;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.patterns.FunctionPattern;
+import org.apache.xpath.patterns.NodeTest;
+import org.apache.xpath.patterns.StepPattern;
+import org.apache.xpath.patterns.UnionPattern;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * An instance of this class compiles an XPath string expression into 
+ * a Expression object.  This class compiles the string into a sequence 
+ * of operation codes (op map) and then builds from that into an Expression 
+ * tree.
+ * @xsl.usage advanced
+ */
+public class Compiler extends OpMap
+{
+
+  /**
+   * Construct a Compiler object with a specific ErrorListener and 
+   * SourceLocator where the expression is located.
+   *
+   * @param errorHandler Error listener where messages will be sent, or null 
+   *                     if messages should be sent to System err.
+   * @param locator The location object where the expression lives, which 
+   *                may be null, but which, if not null, must be valid over 
+   *                the long haul, in other words, it will not be cloned.
+   * @param fTable  The FunctionTable object where the xpath build-in 
+   *                functions are stored.
+   */
+  public Compiler(ErrorListener errorHandler, SourceLocator locator, 
+            FunctionTable fTable)
+  {
+    m_errorHandler = errorHandler;
+    m_locator = locator;
+    m_functionTable = fTable;
+  }
+
+  /**
+   * Construct a Compiler instance that has a null error listener and a 
+   * null source locator.
+   */
+  public Compiler()
+  {
+    m_errorHandler = null;
+    m_locator = null;
+  }
+
+  /**
+   * Execute the XPath object from a given opcode position.
+   * @param opPos The current position in the xpath.m_opMap array.
+   * @return The result of the XPath.
+   *
+   * @throws TransformerException if there is a syntax or other error.
+   * @xsl.usage advanced
+   */
+  public Expression compile(int opPos) throws TransformerException
+  {
+
+    int op = getOp(opPos);
+
+    Expression expr = null;
+    // System.out.println(getPatternString()+"op: "+op);
+    switch (op)
+    {
+    case OpCodes.OP_XPATH :
+      expr = compile(opPos + 2); break;
+    case OpCodes.OP_OR :
+      expr = or(opPos); break;
+    case OpCodes.OP_AND :
+      expr = and(opPos); break;
+    case OpCodes.OP_NOTEQUALS :
+      expr = notequals(opPos); break;
+    case OpCodes.OP_EQUALS :
+      expr = equals(opPos); break;
+    case OpCodes.OP_LTE :
+      expr = lte(opPos); break;
+    case OpCodes.OP_LT :
+      expr = lt(opPos); break;
+    case OpCodes.OP_GTE :
+      expr = gte(opPos); break;
+    case OpCodes.OP_GT :
+      expr = gt(opPos); break;
+    case OpCodes.OP_PLUS :
+      expr = plus(opPos); break;
+    case OpCodes.OP_MINUS :
+      expr = minus(opPos); break;
+    case OpCodes.OP_MULT :
+      expr = mult(opPos); break;
+    case OpCodes.OP_DIV :
+      expr = div(opPos); break;
+    case OpCodes.OP_MOD :
+      expr = mod(opPos); break;
+//    case OpCodes.OP_QUO :
+//      expr = quo(opPos); break;
+    case OpCodes.OP_NEG :
+      expr = neg(opPos); break;
+    case OpCodes.OP_STRING :
+      expr = string(opPos); break;
+    case OpCodes.OP_BOOL :
+      expr = bool(opPos); break;
+    case OpCodes.OP_NUMBER :
+      expr = number(opPos); break;
+    case OpCodes.OP_UNION :
+      expr = union(opPos); break;
+    case OpCodes.OP_LITERAL :
+      expr = literal(opPos); break;
+    case OpCodes.OP_VARIABLE :
+      expr = variable(opPos); break;
+    case OpCodes.OP_GROUP :
+      expr = group(opPos); break;
+    case OpCodes.OP_NUMBERLIT :
+      expr = numberlit(opPos); break;
+    case OpCodes.OP_ARGUMENT :
+      expr = arg(opPos); break;
+    case OpCodes.OP_EXTFUNCTION :
+      expr = compileExtension(opPos); break;
+    case OpCodes.OP_FUNCTION :
+      expr = compileFunction(opPos); break;
+    case OpCodes.OP_LOCATIONPATH :
+      expr = locationPath(opPos); break;
+    case OpCodes.OP_PREDICATE :
+      expr = null; break;  // should never hit this here.
+    case OpCodes.OP_MATCHPATTERN :
+      expr = matchPattern(opPos + 2); break;
+    case OpCodes.OP_LOCATIONPATHPATTERN :
+      expr = locationPathPattern(opPos); break;
+    case OpCodes.OP_QUO:
+      error(XPATHErrorResources.ER_UNKNOWN_OPCODE,
+            new Object[]{ "quo" });  //"ERROR! Unknown op code: "+m_opMap[opPos]);
+      break;
+    default :
+      error(XPATHErrorResources.ER_UNKNOWN_OPCODE,
+            new Object[]{ Integer.toString(getOp(opPos)) });  //"ERROR! Unknown op code: "+m_opMap[opPos]);
+    }
+//    if(null != expr)
+//      expr.setSourceLocator(m_locator);
+
+    return expr;
+  }
+
+  /**
+   * Bottle-neck compilation of an operation with left and right operands.
+   *
+   * @param operation non-null reference to parent operation.
+   * @param opPos The op map position of the parent operation.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Operation} instance.
+   *
+   * @throws TransformerException if there is a syntax or other error.
+   */
+  private Expression compileOperation(Operation operation, int opPos)
+          throws TransformerException
+  {
+
+    int leftPos = getFirstChildPos(opPos);
+    int rightPos = getNextOpPos(leftPos);
+
+    operation.setLeftRight(compile(leftPos), compile(rightPos));
+
+    return operation;
+  }
+
+  /**
+   * Bottle-neck compilation of a unary operation.
+   *
+   * @param unary The parent unary operation.
+   * @param opPos The position in the op map of the parent operation.
+   *
+   * @return The unary argument.
+   *
+   * @throws TransformerException if syntax or other error occurs.
+   */
+  private Expression compileUnary(UnaryOperation unary, int opPos)
+          throws TransformerException
+  {
+
+    int rightPos = getFirstChildPos(opPos);
+
+    unary.setRight(compile(rightPos));
+
+    return unary;
+  }
+
+  /**
+   * Compile an 'or' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Or} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression or(int opPos) throws TransformerException
+  {
+    return compileOperation(new Or(), opPos);
+  }
+
+  /**
+   * Compile an 'and' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.And} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression and(int opPos) throws TransformerException
+  {
+    return compileOperation(new And(), opPos);
+  }
+
+  /**
+   * Compile a '!=' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.NotEquals} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression notequals(int opPos) throws TransformerException
+  {
+    return compileOperation(new NotEquals(), opPos);
+  }
+
+  /**
+   * Compile a '=' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Equals} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression equals(int opPos) throws TransformerException
+  {
+    return compileOperation(new Equals(), opPos);
+  }
+
+  /**
+   * Compile a '<=' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Lte} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression lte(int opPos) throws TransformerException
+  {
+    return compileOperation(new Lte(), opPos);
+  }
+
+  /**
+   * Compile a '<' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Lt} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression lt(int opPos) throws TransformerException
+  {
+    return compileOperation(new Lt(), opPos);
+  }
+
+  /**
+   * Compile a '>=' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Gte} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression gte(int opPos) throws TransformerException
+  {
+    return compileOperation(new Gte(), opPos);
+  }
+
+  /**
+   * Compile a '>' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Gt} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression gt(int opPos) throws TransformerException
+  {
+    return compileOperation(new Gt(), opPos);
+  }
+
+  /**
+   * Compile a '+' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Plus} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression plus(int opPos) throws TransformerException
+  {
+    return compileOperation(new Plus(), opPos);
+  }
+
+  /**
+   * Compile a '-' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Minus} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression minus(int opPos) throws TransformerException
+  {
+    return compileOperation(new Minus(), opPos);
+  }
+
+  /**
+   * Compile a '*' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Mult} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression mult(int opPos) throws TransformerException
+  {
+    return compileOperation(new Mult(), opPos);
+  }
+
+  /**
+   * Compile a 'div' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Div} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression div(int opPos) throws TransformerException
+  {
+    return compileOperation(new Div(), opPos);
+  }
+
+  /**
+   * Compile a 'mod' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Mod} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression mod(int opPos) throws TransformerException
+  {
+    return compileOperation(new Mod(), opPos);
+  }
+
+  /*
+   * Compile a 'quo' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Quo} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+//  protected Expression quo(int opPos) throws TransformerException
+//  {
+//    return compileOperation(new Quo(), opPos);
+//  }
+
+  /**
+   * Compile a unary '-' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Neg} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression neg(int opPos) throws TransformerException
+  {
+    return compileUnary(new Neg(), opPos);
+  }
+
+  /**
+   * Compile a 'string(...)' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.String} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression string(int opPos) throws TransformerException
+  {
+    return compileUnary(new org.apache.xpath.operations.String(), opPos);
+  }
+
+  /**
+   * Compile a 'boolean(...)' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Bool} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression bool(int opPos) throws TransformerException
+  {
+    return compileUnary(new org.apache.xpath.operations.Bool(), opPos);
+  }
+
+  /**
+   * Compile a 'number(...)' operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Number} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression number(int opPos) throws TransformerException
+  {
+    return compileUnary(new org.apache.xpath.operations.Number(), opPos);
+  }
+
+  /**
+   * Compile a literal string value.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.objects.XString} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression literal(int opPos)
+  {
+
+    opPos = getFirstChildPos(opPos);
+
+    return (XString) getTokenQueue().elementAt(getOp(opPos));
+  }
+
+  /**
+   * Compile a literal number value.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.objects.XNumber} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression numberlit(int opPos)
+  {
+
+    opPos = getFirstChildPos(opPos);
+
+    return (XNumber) getTokenQueue().elementAt(getOp(opPos));
+  }
+
+  /**
+   * Compile a variable reference.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.operations.Variable} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression variable(int opPos) throws TransformerException
+  {
+
+    Variable var = new Variable();
+
+    opPos = getFirstChildPos(opPos);
+
+    int nsPos = getOp(opPos);
+    java.lang.String namespace 
+      = (OpCodes.EMPTY == nsPos) ? null 
+                                   : (java.lang.String) getTokenQueue().elementAt(nsPos);
+    java.lang.String localname 
+      = (java.lang.String) getTokenQueue().elementAt(getOp(opPos+1));
+    QName qname = new QName(namespace, localname);
+
+    var.setQName(qname);
+
+    return var;
+  }
+
+  /**
+   * Compile an expression group.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to the contained expression.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression group(int opPos) throws TransformerException
+  {
+
+    // no-op
+    return compile(opPos + 2);
+  }
+
+  /**
+   * Compile a function argument.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to the argument expression.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression arg(int opPos) throws TransformerException
+  {
+
+    // no-op
+    return compile(opPos + 2);
+  }
+
+  /**
+   * Compile a location path union. The UnionPathIterator itself may create
+   * {@link org.apache.xpath.axes.LocPathIterator} children.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.axes.LocPathIterator} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression union(int opPos) throws TransformerException
+  {
+    locPathDepth++;
+    try
+    {
+      return UnionPathIterator.createUnionIterator(this, opPos);
+    }
+    finally
+    {
+      locPathDepth--;
+    }
+  }
+  
+  private int locPathDepth = -1;
+  
+  /**
+   * Get the level of the location path or union being constructed.  
+   * @return 0 if it is a top-level path.
+   */
+  public int getLocationPathDepth()
+  {
+    return locPathDepth;
+  }
+
+  /**
+   * Get the function table  
+   */
+  FunctionTable getFunctionTable()
+  {
+    return m_functionTable;
+  }
+
+  /**
+   * Compile a location path.  The LocPathIterator itself may create
+   * {@link org.apache.xpath.axes.AxesWalker} children.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.axes.LocPathIterator} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  public Expression locationPath(int opPos) throws TransformerException
+  {
+    locPathDepth++;
+    try
+    {
+      DTMIterator iter = WalkerFactory.newDTMIterator(this, opPos, (locPathDepth == 0));
+      return (Expression)iter; // cast OK, I guess.
+    }
+    finally
+    {
+      locPathDepth--;
+    }
+  }
+
+  /**
+   * Compile a location step predicate expression.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return the contained predicate expression.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  public Expression predicate(int opPos) throws TransformerException
+  {
+    return compile(opPos + 2);
+  }
+
+  /**
+   * Compile an entire match pattern expression.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.patterns.UnionPattern} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression matchPattern(int opPos) throws TransformerException
+  {
+    locPathDepth++;
+    try
+    {
+      // First, count...
+      int nextOpPos = opPos;
+      int i;
+
+      for (i = 0; getOp(nextOpPos) == OpCodes.OP_LOCATIONPATHPATTERN; i++)
+      {
+        nextOpPos = getNextOpPos(nextOpPos);
+      }
+
+      if (i == 1)
+        return compile(opPos);
+
+      UnionPattern up = new UnionPattern();
+      StepPattern[] patterns = new StepPattern[i];
+
+      for (i = 0; getOp(opPos) == OpCodes.OP_LOCATIONPATHPATTERN; i++)
+      {
+        nextOpPos = getNextOpPos(opPos);
+        patterns[i] = (StepPattern) compile(opPos);
+        opPos = nextOpPos;
+      }
+
+      up.setPatterns(patterns);
+
+      return up;
+    }
+    finally
+    {
+      locPathDepth--;
+    }
+  }
+
+  /**
+   * Compile a location match pattern unit expression.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.patterns.StepPattern} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  public Expression locationPathPattern(int opPos)
+          throws TransformerException
+  {
+
+    opPos = getFirstChildPos(opPos);
+
+    return stepPattern(opPos, 0, null);
+  }
+
+  /**
+   * Get a {@link org.w3c.dom.traversal.NodeFilter} bit set that tells what 
+   * to show for a given node test.
+   *
+   * @param opPos the op map position for the location step.
+   *
+   * @return {@link org.w3c.dom.traversal.NodeFilter} bit set that tells what 
+   *         to show for a given node test.
+   */
+  public int getWhatToShow(int opPos)
+  {
+
+    int axesType = getOp(opPos);
+    int testType = getOp(opPos + 3);
+
+    // System.out.println("testType: "+testType);
+    switch (testType)
+    {
+    case OpCodes.NODETYPE_COMMENT :
+      return DTMFilter.SHOW_COMMENT;
+    case OpCodes.NODETYPE_TEXT :
+//      return DTMFilter.SHOW_TEXT | DTMFilter.SHOW_COMMENT;
+      return DTMFilter.SHOW_TEXT | DTMFilter.SHOW_CDATA_SECTION ;
+    case OpCodes.NODETYPE_PI :
+      return DTMFilter.SHOW_PROCESSING_INSTRUCTION;
+    case OpCodes.NODETYPE_NODE :
+//      return DTMFilter.SHOW_ALL;
+      switch (axesType)
+      {
+      case OpCodes.FROM_NAMESPACE:
+        return DTMFilter.SHOW_NAMESPACE;
+      case OpCodes.FROM_ATTRIBUTES :
+      case OpCodes.MATCH_ATTRIBUTE :
+        return DTMFilter.SHOW_ATTRIBUTE;
+      case OpCodes.FROM_SELF:
+      case OpCodes.FROM_ANCESTORS_OR_SELF:
+      case OpCodes.FROM_DESCENDANTS_OR_SELF:
+        return DTMFilter.SHOW_ALL;
+      default:
+        if (getOp(0) == OpCodes.OP_MATCHPATTERN)
+          return ~DTMFilter.SHOW_ATTRIBUTE
+                  & ~DTMFilter.SHOW_DOCUMENT
+                  & ~DTMFilter.SHOW_DOCUMENT_FRAGMENT;
+        else
+          return ~DTMFilter.SHOW_ATTRIBUTE;
+      }
+    case OpCodes.NODETYPE_ROOT :
+      return DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT;
+    case OpCodes.NODETYPE_FUNCTEST :
+      return NodeTest.SHOW_BYFUNCTION;
+    case OpCodes.NODENAME :
+      switch (axesType)
+      {
+      case OpCodes.FROM_NAMESPACE :
+        return DTMFilter.SHOW_NAMESPACE;
+      case OpCodes.FROM_ATTRIBUTES :
+      case OpCodes.MATCH_ATTRIBUTE :
+        return DTMFilter.SHOW_ATTRIBUTE;
+
+      // break;
+      case OpCodes.MATCH_ANY_ANCESTOR :
+      case OpCodes.MATCH_IMMEDIATE_ANCESTOR :
+        return DTMFilter.SHOW_ELEMENT;
+
+      // break;
+      default :
+        return DTMFilter.SHOW_ELEMENT;
+      }
+    default :
+      // System.err.println("We should never reach here.");
+      return DTMFilter.SHOW_ALL;
+    }
+  }
+  
+private static final boolean DEBUG = false;
+
+  /**
+   * Compile a step pattern unit expression, used for both location paths 
+   * and match patterns.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   * @param stepCount The number of steps to expect.
+   * @param ancestorPattern The owning StepPattern, which may be null.
+   *
+   * @return reference to {@link org.apache.xpath.patterns.StepPattern} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected StepPattern stepPattern(
+          int opPos, int stepCount, StepPattern ancestorPattern)
+            throws TransformerException
+  {
+
+    int startOpPos = opPos;
+    int stepType = getOp(opPos);
+
+    if (OpCodes.ENDOP == stepType)
+    {
+      return null;
+    }
+    
+    boolean addMagicSelf = true;
+
+    int endStep = getNextOpPos(opPos);
+
+    // int nextStepType = getOpMap()[endStep];
+    StepPattern pattern;
+    
+    // boolean isSimple = ((OpCodes.ENDOP == nextStepType) && (stepCount == 0));
+    int argLen;
+
+    switch (stepType)
+    {
+    case OpCodes.OP_FUNCTION :
+      if(DEBUG)
+        System.out.println("MATCH_FUNCTION: "+m_currentPattern); 
+      addMagicSelf = false;
+      argLen = getOp(opPos + OpMap.MAPINDEX_LENGTH);
+      pattern = new FunctionPattern(compileFunction(opPos), Axis.PARENT, Axis.CHILD);
+      break;
+    case OpCodes.FROM_ROOT :
+      if(DEBUG)
+        System.out.println("FROM_ROOT, "+m_currentPattern);
+      addMagicSelf = false;
+      argLen = getArgLengthOfStep(opPos);
+      opPos = getFirstChildPosOfStep(opPos);
+      pattern = new StepPattern(DTMFilter.SHOW_DOCUMENT | 
+                                DTMFilter.SHOW_DOCUMENT_FRAGMENT,
+                                Axis.PARENT, Axis.CHILD);
+      break;
+    case OpCodes.MATCH_ATTRIBUTE :
+     if(DEBUG)
+        System.out.println("MATCH_ATTRIBUTE: "+getStepLocalName(startOpPos)+", "+m_currentPattern);
+      argLen = getArgLengthOfStep(opPos);
+      opPos = getFirstChildPosOfStep(opPos);
+      pattern = new StepPattern(DTMFilter.SHOW_ATTRIBUTE,
+                                getStepNS(startOpPos),
+                                getStepLocalName(startOpPos),
+                                Axis.PARENT, Axis.ATTRIBUTE);
+      break;
+    case OpCodes.MATCH_ANY_ANCESTOR :
+      if(DEBUG)
+        System.out.println("MATCH_ANY_ANCESTOR: "+getStepLocalName(startOpPos)+", "+m_currentPattern);
+      argLen = getArgLengthOfStep(opPos);
+      opPos = getFirstChildPosOfStep(opPos);
+      int what = getWhatToShow(startOpPos);
+      // bit-o-hackery, but this code is due for the morgue anyway...
+      if(0x00000500 == what)
+        addMagicSelf = false;
+      pattern = new StepPattern(getWhatToShow(startOpPos),
+                                        getStepNS(startOpPos),
+                                        getStepLocalName(startOpPos),
+                                        Axis.ANCESTOR, Axis.CHILD);
+      break;
+    case OpCodes.MATCH_IMMEDIATE_ANCESTOR :
+      if(DEBUG)
+        System.out.println("MATCH_IMMEDIATE_ANCESTOR: "+getStepLocalName(startOpPos)+", "+m_currentPattern);
+      argLen = getArgLengthOfStep(opPos);
+      opPos = getFirstChildPosOfStep(opPos);
+      pattern = new StepPattern(getWhatToShow(startOpPos),
+                                getStepNS(startOpPos),
+                                getStepLocalName(startOpPos),
+                                Axis.PARENT, Axis.CHILD);
+      break;
+    default :
+      error(XPATHErrorResources.ER_UNKNOWN_MATCH_OPERATION, null);  //"unknown match operation!");
+
+      return null;
+    }
+
+    pattern.setPredicates(getCompiledPredicates(opPos + argLen));
+    if(null == ancestorPattern)
+    {
+      // This is the magic and invisible "." at the head of every 
+      // match pattern, and corresponds to the current node in the context 
+      // list, from where predicates are counted.
+      // So, in order to calculate "foo[3]", it has to count from the 
+      // current node in the context list, so, from that current node, 
+      // the full pattern is really "self::node()/child::foo[3]".  If you 
+      // translate this to a select pattern from the node being tested, 
+      // which is really how we're treating match patterns, it works out to 
+      // self::foo/parent::node[child::foo[3]]", or close enough.
+	/*      if(addMagicSelf && pattern.getPredicateCount() > 0)
+      {
+        StepPattern selfPattern = new StepPattern(DTMFilter.SHOW_ALL, 
+                                                  Axis.PARENT, Axis.CHILD);
+        // We need to keep the new nodetest from affecting the score...
+        XNumber score = pattern.getStaticScore();
+        pattern.setRelativePathPattern(selfPattern);
+        pattern.setStaticScore(score);
+        selfPattern.setStaticScore(score);
+	}*/
+    }
+    else
+    {
+      // System.out.println("Setting "+ancestorPattern+" as relative to "+pattern);
+      pattern.setRelativePathPattern(ancestorPattern);
+    }
+
+    StepPattern relativePathPattern = stepPattern(endStep, stepCount + 1,
+                                        pattern);
+
+    return (null != relativePathPattern) ? relativePathPattern : pattern;
+  }
+
+  /**
+   * Compile a zero or more predicates for a given match pattern.
+   * 
+   * @param opPos The position of the first predicate the m_opMap array.
+   *
+   * @return reference to array of {@link org.apache.xpath.Expression} instances.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  public Expression[] getCompiledPredicates(int opPos)
+          throws TransformerException
+  {
+
+    int count = countPredicates(opPos);
+
+    if (count > 0)
+    {
+      Expression[] predicates = new Expression[count];
+
+      compilePredicates(opPos, predicates);
+
+      return predicates;
+    }
+
+    return null;
+  }
+
+  /**
+   * Count the number of predicates in the step.
+   *
+   * @param opPos The position of the first predicate the m_opMap array.
+   *
+   * @return The number of predicates for this step.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  public int countPredicates(int opPos) throws TransformerException
+  {
+
+    int count = 0;
+
+    while (OpCodes.OP_PREDICATE == getOp(opPos))
+    {
+      count++;
+
+      opPos = getNextOpPos(opPos);
+    }
+
+    return count;
+  }
+
+  /**
+   * Compiles predicates in the step.
+   *
+   * @param opPos The position of the first predicate the m_opMap array.
+   * @param predicates An empty pre-determined array of 
+   *            {@link org.apache.xpath.Expression}s, that will be filled in.
+   *
+   * @throws TransformerException
+   */
+  private void compilePredicates(int opPos, Expression[] predicates)
+          throws TransformerException
+  {
+
+    for (int i = 0; OpCodes.OP_PREDICATE == getOp(opPos); i++)
+    {
+      predicates[i] = predicate(opPos);
+      opPos = getNextOpPos(opPos);
+    }
+  }
+
+  /**
+   * Compile a built-in XPath function.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.functions.Function} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  Expression compileFunction(int opPos) throws TransformerException
+  {
+
+    int endFunc = opPos + getOp(opPos + 1) - 1;
+
+    opPos = getFirstChildPos(opPos);
+
+    int funcID = getOp(opPos);
+
+    opPos++;
+
+    if (-1 != funcID)
+    {
+      Function func = m_functionTable.getFunction(funcID);
+      
+      /**
+       * It is a trick for function-available. Since the function table is an
+       * instance field, insert this table at compilation time for later usage
+       */
+      
+      if (func instanceof FuncExtFunctionAvailable)
+          ((FuncExtFunctionAvailable) func).setFunctionTable(m_functionTable);
+
+      func.postCompileStep(this);
+      
+      try
+      {
+        int i = 0;
+
+        for (int p = opPos; p < endFunc; p = getNextOpPos(p), i++)
+        {
+
+          // System.out.println("argPos: "+ p);
+          // System.out.println("argCode: "+ m_opMap[p]);
+          func.setArg(compile(p), i);
+        }
+
+        func.checkNumberArgs(i);
+      }
+      catch (WrongNumberArgsException wnae)
+      {
+        java.lang.String name = m_functionTable.getFunctionName(funcID);
+
+        m_errorHandler.fatalError( new TransformerException(
+                  XSLMessages.createXPATHMessage(XPATHErrorResources.ER_ONLY_ALLOWS, 
+                      new Object[]{name, wnae.getMessage()}), m_locator)); 
+              //"name + " only allows " + wnae.getMessage() + " arguments", m_locator));
+      }
+
+      return func;
+    }
+    else
+    {
+      error(XPATHErrorResources.ER_FUNCTION_TOKEN_NOT_FOUND, null);  //"function token not found.");
+
+      return null;
+    }
+  }
+
+  // The current id for extension functions.
+  private static long s_nextMethodId = 0;
+
+  /**
+   * Get the next available method id
+   */
+  synchronized private long getNextMethodId()
+  {
+    if (s_nextMethodId == Long.MAX_VALUE)
+      s_nextMethodId = 0;
+    
+    return s_nextMethodId++;
+  }
+  
+  /**
+   * Compile an extension function.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   *
+   * @return reference to {@link org.apache.xpath.functions.FuncExtFunction} instance.
+   *
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  private Expression compileExtension(int opPos)
+          throws TransformerException
+  {
+
+    int endExtFunc = opPos + getOp(opPos + 1) - 1;
+
+    opPos = getFirstChildPos(opPos);
+
+    java.lang.String ns = (java.lang.String) getTokenQueue().elementAt(getOp(opPos));
+
+    opPos++;
+
+    java.lang.String funcName =
+      (java.lang.String) getTokenQueue().elementAt(getOp(opPos));
+
+    opPos++;
+
+    // We create a method key to uniquely identify this function so that we
+    // can cache the object needed to invoke it.  This way, we only pay the
+    // reflection overhead on the first call.
+
+    Function extension = new FuncExtFunction(ns, funcName, String.valueOf(getNextMethodId()));
+
+    try
+    {
+      int i = 0;
+
+      while (opPos < endExtFunc)
+      {
+        int nextOpPos = getNextOpPos(opPos);
+
+        extension.setArg(this.compile(opPos), i);
+
+        opPos = nextOpPos;
+
+        i++;
+      }
+    }
+    catch (WrongNumberArgsException wnae)
+    {
+      ;  // should never happen
+    }
+
+    return extension;
+  }
+
+  /**
+   * Warn the user of an problem.
+   *
+   * @param msg An error msgkey that corresponds to one of the constants found 
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which 
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to 
+   *                              throw an exception.
+   */
+  public void warn(String msg, Object[] args) throws TransformerException
+  {
+
+    java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args);
+
+    if (null != m_errorHandler)
+    {
+      m_errorHandler.warning(new TransformerException(fmsg, m_locator));
+    }
+    else
+    {
+      System.out.println(fmsg
+                          +"; file "+m_locator.getSystemId()
+                          +"; line "+m_locator.getLineNumber()
+                          +"; column "+m_locator.getColumnNumber());
+    }
+  }
+
+  /**
+   * Tell the user of an assertion error, and probably throw an
+   * exception.
+   *
+   * @param b  If false, a runtime exception will be thrown.
+   * @param msg The assertion message, which should be informative.
+   * 
+   * @throws RuntimeException if the b argument is false.
+   */
+  public void assertion(boolean b, java.lang.String msg)
+  {
+
+    if (!b)
+    {
+      java.lang.String fMsg = XSLMessages.createXPATHMessage(
+        XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
+        new Object[]{ msg });
+
+      throw new RuntimeException(fMsg);
+    }
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg An error msgkey that corresponds to one of the constants found 
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which 
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to 
+   *                              throw an exception.
+   */
+  public void error(String msg, Object[] args) throws TransformerException
+  {
+
+    java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args);
+    
+
+    if (null != m_errorHandler)
+    {
+      m_errorHandler.fatalError(new TransformerException(fmsg, m_locator));
+    }
+    else
+    {
+
+      // System.out.println(te.getMessage()
+      //                    +"; file "+te.getSystemId()
+      //                    +"; line "+te.getLineNumber()
+      //                    +"; column "+te.getColumnNumber());
+      throw new TransformerException(fmsg, (SAXSourceLocator)m_locator);
+    }
+  }
+
+  /**
+   * The current prefixResolver for the execution context.
+   */
+  private PrefixResolver m_currentPrefixResolver = null;
+
+  /**
+   * Get the current namespace context for the xpath.
+   *
+   * @return The current prefix resolver, *may* be null, though hopefully not.
+   */
+  public PrefixResolver getNamespaceContext()
+  {
+    return m_currentPrefixResolver;
+  }
+
+  /**
+   * Set the current namespace context for the xpath.
+   *
+   * @param pr The resolver for prefixes in the XPath expression.
+   */
+  public void setNamespaceContext(PrefixResolver pr)
+  {
+    m_currentPrefixResolver = pr;
+  }
+
+  /** The error listener where errors will be sent.  If this is null, errors 
+   *  and warnings will be sent to System.err.  May be null.    */
+  ErrorListener m_errorHandler;
+
+  /** The source locator for the expression being compiled.  May be null. */
+  SourceLocator m_locator;
+  
+  /**
+   * The FunctionTable for all xpath build-in functions
+   */
+  private FunctionTable m_functionTable;
+}
diff --git a/src/main/java/org/apache/xpath/compiler/FunctionTable.java b/src/main/java/org/apache/xpath/compiler/FunctionTable.java
new file mode 100644
index 0000000..1ffb564
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/FunctionTable.java
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FunctionTable.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.compiler;
+
+import org.apache.xpath.Expression;
+import org.apache.xpath.functions.Function;
+import java.util.HashMap;
+import javax.xml.transform.TransformerException;
+
+/**
+ * The function table for XPath.
+ */
+public class FunctionTable
+{
+
+  /** The 'current()' id. */
+  public static final int FUNC_CURRENT = 0;
+
+  /** The 'last()' id. */
+  public static final int FUNC_LAST = 1;
+
+  /** The 'position()' id. */
+  public static final int FUNC_POSITION = 2;
+
+  /** The 'count()' id. */
+  public static final int FUNC_COUNT = 3;
+
+  /** The 'id()' id. */
+  public static final int FUNC_ID = 4;
+
+  /** The 'key()' id (XSLT). */
+  public static final int FUNC_KEY = 5;
+
+  /** The 'local-name()' id. */
+  public static final int FUNC_LOCAL_PART = 7;
+
+  /** The 'namespace-uri()' id. */
+  public static final int FUNC_NAMESPACE = 8;
+
+  /** The 'name()' id. */
+  public static final int FUNC_QNAME = 9;
+
+  /** The 'generate-id()' id. */
+  public static final int FUNC_GENERATE_ID = 10;
+
+  /** The 'not()' id. */
+  public static final int FUNC_NOT = 11;
+
+  /** The 'true()' id. */
+  public static final int FUNC_TRUE = 12;
+
+  /** The 'false()' id. */
+  public static final int FUNC_FALSE = 13;
+
+  /** The 'boolean()' id. */
+  public static final int FUNC_BOOLEAN = 14;
+
+  /** The 'number()' id. */
+  public static final int FUNC_NUMBER = 15;
+
+  /** The 'floor()' id. */
+  public static final int FUNC_FLOOR = 16;
+
+  /** The 'ceiling()' id. */
+  public static final int FUNC_CEILING = 17;
+
+  /** The 'round()' id. */
+  public static final int FUNC_ROUND = 18;
+
+  /** The 'sum()' id. */
+  public static final int FUNC_SUM = 19;
+
+  /** The 'string()' id. */
+  public static final int FUNC_STRING = 20;
+
+  /** The 'starts-with()' id. */
+  public static final int FUNC_STARTS_WITH = 21;
+
+  /** The 'contains()' id. */
+  public static final int FUNC_CONTAINS = 22;
+
+  /** The 'substring-before()' id. */
+  public static final int FUNC_SUBSTRING_BEFORE = 23;
+
+  /** The 'substring-after()' id. */
+  public static final int FUNC_SUBSTRING_AFTER = 24;
+
+  /** The 'normalize-space()' id. */
+  public static final int FUNC_NORMALIZE_SPACE = 25;
+
+  /** The 'translate()' id. */
+  public static final int FUNC_TRANSLATE = 26;
+
+  /** The 'concat()' id. */
+  public static final int FUNC_CONCAT = 27;
+
+  /** The 'substring()' id. */
+  public static final int FUNC_SUBSTRING = 29;
+
+  /** The 'string-length()' id. */
+  public static final int FUNC_STRING_LENGTH = 30;
+
+  /** The 'system-property()' id. */
+  public static final int FUNC_SYSTEM_PROPERTY = 31;
+
+  /** The 'lang()' id. */
+  public static final int FUNC_LANG = 32;
+
+  /** The 'function-available()' id (XSLT). */
+  public static final int FUNC_EXT_FUNCTION_AVAILABLE = 33;
+
+  /** The 'element-available()' id (XSLT). */
+  public static final int FUNC_EXT_ELEM_AVAILABLE = 34;
+
+  /** The 'unparsed-entity-uri()' id (XSLT). */
+  public static final int FUNC_UNPARSED_ENTITY_URI = 36;
+
+  // Proprietary
+
+  /** The 'document-location()' id (Proprietary). */
+  public static final int FUNC_DOCLOCATION = 35;
+
+  /**
+   * The function table.
+   */
+  private static Class m_functions[];
+
+  /** Table of function name to function ID associations. */
+  private static HashMap m_functionID = new HashMap();
+    
+  /**
+   * The function table contains customized functions
+   */
+  private Class m_functions_customer[] = new Class[NUM_ALLOWABLE_ADDINS];
+
+  /**
+   * Table of function name to function ID associations for customized functions
+   */
+  private HashMap m_functionID_customer = new HashMap();
+  
+  /**
+   * Number of built in functions.  Be sure to update this as
+   * built-in functions are added.
+   */
+  private static final int NUM_BUILT_IN_FUNCS = 37;
+
+  /**
+   * Number of built-in functions that may be added.
+   */
+  private static final int NUM_ALLOWABLE_ADDINS = 30;
+
+  /**
+   * The index to the next free function index.
+   */
+  private int m_funcNextFreeIndex = NUM_BUILT_IN_FUNCS;
+  
+  static
+  {
+    m_functions = new Class[NUM_BUILT_IN_FUNCS];
+    m_functions[FUNC_CURRENT] = org.apache.xpath.functions.FuncCurrent.class;
+    m_functions[FUNC_LAST] = org.apache.xpath.functions.FuncLast.class;
+    m_functions[FUNC_POSITION] = org.apache.xpath.functions.FuncPosition.class;
+    m_functions[FUNC_COUNT] = org.apache.xpath.functions.FuncCount.class;
+    m_functions[FUNC_ID] = org.apache.xpath.functions.FuncId.class;
+    m_functions[FUNC_KEY] =
+      org.apache.xalan.templates.FuncKey.class;
+    m_functions[FUNC_LOCAL_PART] = 
+      org.apache.xpath.functions.FuncLocalPart.class;
+    m_functions[FUNC_NAMESPACE] = 
+      org.apache.xpath.functions.FuncNamespace.class;
+    m_functions[FUNC_QNAME] = org.apache.xpath.functions.FuncQname.class;
+    m_functions[FUNC_GENERATE_ID] = 
+      org.apache.xpath.functions.FuncGenerateId.class;
+    m_functions[FUNC_NOT] = org.apache.xpath.functions.FuncNot.class;
+    m_functions[FUNC_TRUE] = org.apache.xpath.functions.FuncTrue.class;
+    m_functions[FUNC_FALSE] = org.apache.xpath.functions.FuncFalse.class;
+    m_functions[FUNC_BOOLEAN] = org.apache.xpath.functions.FuncBoolean.class;
+    m_functions[FUNC_LANG] = org.apache.xpath.functions.FuncLang.class;
+    m_functions[FUNC_NUMBER] = org.apache.xpath.functions.FuncNumber.class;
+    m_functions[FUNC_FLOOR] = org.apache.xpath.functions.FuncFloor.class;
+    m_functions[FUNC_CEILING] = org.apache.xpath.functions.FuncCeiling.class;
+    m_functions[FUNC_ROUND] = org.apache.xpath.functions.FuncRound.class;
+    m_functions[FUNC_SUM] = org.apache.xpath.functions.FuncSum.class;
+    m_functions[FUNC_STRING] = org.apache.xpath.functions.FuncString.class;
+    m_functions[FUNC_STARTS_WITH] = 
+      org.apache.xpath.functions.FuncStartsWith.class;
+    m_functions[FUNC_CONTAINS] = org.apache.xpath.functions.FuncContains.class;
+    m_functions[FUNC_SUBSTRING_BEFORE] = 
+      org.apache.xpath.functions.FuncSubstringBefore.class;
+    m_functions[FUNC_SUBSTRING_AFTER] = 
+      org.apache.xpath.functions.FuncSubstringAfter.class;
+    m_functions[FUNC_NORMALIZE_SPACE] = 
+      org.apache.xpath.functions.FuncNormalizeSpace.class;
+    m_functions[FUNC_TRANSLATE] = 
+      org.apache.xpath.functions.FuncTranslate.class;
+    m_functions[FUNC_CONCAT] = org.apache.xpath.functions.FuncConcat.class;
+    m_functions[FUNC_SYSTEM_PROPERTY] = 
+      org.apache.xpath.functions.FuncSystemProperty.class;
+    m_functions[FUNC_EXT_FUNCTION_AVAILABLE] =
+      org.apache.xpath.functions.FuncExtFunctionAvailable.class;
+    m_functions[FUNC_EXT_ELEM_AVAILABLE] =
+      org.apache.xpath.functions.FuncExtElementAvailable.class;
+    m_functions[FUNC_SUBSTRING] = 
+      org.apache.xpath.functions.FuncSubstring.class;
+    m_functions[FUNC_STRING_LENGTH] = 
+      org.apache.xpath.functions.FuncStringLength.class;
+    m_functions[FUNC_DOCLOCATION] = 
+      org.apache.xpath.functions.FuncDoclocation.class;
+    m_functions[FUNC_UNPARSED_ENTITY_URI] =
+      org.apache.xpath.functions.FuncUnparsedEntityURI.class;
+  }
+
+  static{
+          m_functionID.put(Keywords.FUNC_CURRENT_STRING,
+                          new Integer(FunctionTable.FUNC_CURRENT));
+          m_functionID.put(Keywords.FUNC_LAST_STRING,
+                          new Integer(FunctionTable.FUNC_LAST));
+          m_functionID.put(Keywords.FUNC_POSITION_STRING,
+                          new Integer(FunctionTable.FUNC_POSITION));
+          m_functionID.put(Keywords.FUNC_COUNT_STRING,
+                          new Integer(FunctionTable.FUNC_COUNT));
+          m_functionID.put(Keywords.FUNC_ID_STRING,
+                          new Integer(FunctionTable.FUNC_ID));
+          m_functionID.put(Keywords.FUNC_KEY_STRING,
+                          new Integer(FunctionTable.FUNC_KEY));
+          m_functionID.put(Keywords.FUNC_LOCAL_PART_STRING,
+                          new Integer(FunctionTable.FUNC_LOCAL_PART));
+          m_functionID.put(Keywords.FUNC_NAMESPACE_STRING,
+                          new Integer(FunctionTable.FUNC_NAMESPACE));
+          m_functionID.put(Keywords.FUNC_NAME_STRING,
+                          new Integer(FunctionTable.FUNC_QNAME));
+          m_functionID.put(Keywords.FUNC_GENERATE_ID_STRING,
+                          new Integer(FunctionTable.FUNC_GENERATE_ID));
+          m_functionID.put(Keywords.FUNC_NOT_STRING,
+                          new Integer(FunctionTable.FUNC_NOT));
+          m_functionID.put(Keywords.FUNC_TRUE_STRING,
+                          new Integer(FunctionTable.FUNC_TRUE));
+          m_functionID.put(Keywords.FUNC_FALSE_STRING,
+                          new Integer(FunctionTable.FUNC_FALSE));
+          m_functionID.put(Keywords.FUNC_BOOLEAN_STRING,
+                          new Integer(FunctionTable.FUNC_BOOLEAN));
+          m_functionID.put(Keywords.FUNC_LANG_STRING,
+                          new Integer(FunctionTable.FUNC_LANG));
+          m_functionID.put(Keywords.FUNC_NUMBER_STRING,
+                          new Integer(FunctionTable.FUNC_NUMBER));
+          m_functionID.put(Keywords.FUNC_FLOOR_STRING,
+                          new Integer(FunctionTable.FUNC_FLOOR));
+          m_functionID.put(Keywords.FUNC_CEILING_STRING,
+                          new Integer(FunctionTable.FUNC_CEILING));
+          m_functionID.put(Keywords.FUNC_ROUND_STRING,
+                          new Integer(FunctionTable.FUNC_ROUND));
+          m_functionID.put(Keywords.FUNC_SUM_STRING,
+                          new Integer(FunctionTable.FUNC_SUM));
+          m_functionID.put(Keywords.FUNC_STRING_STRING,
+                          new Integer(FunctionTable.FUNC_STRING));
+          m_functionID.put(Keywords.FUNC_STARTS_WITH_STRING,
+                          new Integer(FunctionTable.FUNC_STARTS_WITH));
+          m_functionID.put(Keywords.FUNC_CONTAINS_STRING,
+                          new Integer(FunctionTable.FUNC_CONTAINS));
+          m_functionID.put(Keywords.FUNC_SUBSTRING_BEFORE_STRING,
+                          new Integer(FunctionTable.FUNC_SUBSTRING_BEFORE));
+          m_functionID.put(Keywords.FUNC_SUBSTRING_AFTER_STRING,
+                          new Integer(FunctionTable.FUNC_SUBSTRING_AFTER));
+          m_functionID.put(Keywords.FUNC_NORMALIZE_SPACE_STRING,
+                          new Integer(FunctionTable.FUNC_NORMALIZE_SPACE));
+          m_functionID.put(Keywords.FUNC_TRANSLATE_STRING,
+                          new Integer(FunctionTable.FUNC_TRANSLATE));
+          m_functionID.put(Keywords.FUNC_CONCAT_STRING,
+                          new Integer(FunctionTable.FUNC_CONCAT));
+          m_functionID.put(Keywords.FUNC_SYSTEM_PROPERTY_STRING,
+                          new Integer(FunctionTable.FUNC_SYSTEM_PROPERTY));
+          m_functionID.put(Keywords.FUNC_EXT_FUNCTION_AVAILABLE_STRING,
+                        new Integer(FunctionTable.FUNC_EXT_FUNCTION_AVAILABLE));
+          m_functionID.put(Keywords.FUNC_EXT_ELEM_AVAILABLE_STRING,
+                          new Integer(FunctionTable.FUNC_EXT_ELEM_AVAILABLE));
+          m_functionID.put(Keywords.FUNC_SUBSTRING_STRING,
+                          new Integer(FunctionTable.FUNC_SUBSTRING));
+          m_functionID.put(Keywords.FUNC_STRING_LENGTH_STRING,
+                          new Integer(FunctionTable.FUNC_STRING_LENGTH));
+          m_functionID.put(Keywords.FUNC_UNPARSED_ENTITY_URI_STRING,
+                          new Integer(FunctionTable.FUNC_UNPARSED_ENTITY_URI));
+          m_functionID.put(Keywords.FUNC_DOCLOCATION_STRING,
+                          new Integer(FunctionTable.FUNC_DOCLOCATION));          
+  }
+  
+  public FunctionTable(){
+  }
+  
+  /**
+   * Return the name of the a function in the static table. Needed to avoid
+   * making the table publicly available.
+   */
+  String getFunctionName(int funcID) {
+      if (funcID < NUM_BUILT_IN_FUNCS) return m_functions[funcID].getName();
+      else return m_functions_customer[funcID - NUM_BUILT_IN_FUNCS].getName();
+  }
+
+  /**
+   * Obtain a new Function object from a function ID.
+   *
+   * @param which  The function ID, which may correspond to one of the FUNC_XXX 
+   *    values found in {@link org.apache.xpath.compiler.FunctionTable}, but may 
+   *    be a value installed by an external module. 
+   *
+   * @return a a new Function instance.
+   *
+   * @throws javax.xml.transform.TransformerException if ClassNotFoundException, 
+   *    IllegalAccessException, or InstantiationException is thrown.
+   */
+  Function getFunction(int which)
+          throws javax.xml.transform.TransformerException
+  {
+          try{
+              if (which < NUM_BUILT_IN_FUNCS) 
+                  return (Function) m_functions[which].newInstance();
+              else 
+                  return (Function) m_functions_customer[
+                      which-NUM_BUILT_IN_FUNCS].newInstance();                  
+          }catch (IllegalAccessException ex){
+                  throw new TransformerException(ex.getMessage());
+          }catch (InstantiationException ex){
+                  throw new TransformerException(ex.getMessage());
+          }
+  }
+  
+  /**
+   * Obtain a function ID from a given function name
+   * @param key the function name in a java.lang.String format.
+   * @return a function ID, which may correspond to one of the FUNC_XXX values
+   * found in {@link org.apache.xpath.compiler.FunctionTable}, but may be a 
+   * value installed by an external module.
+   */
+  Object getFunctionID(String key){
+          Object id = m_functionID_customer.get(key);
+          if (null == id) id = m_functionID.get(key);
+          return id;
+  }
+  
+  /**
+   * Install a built-in function.
+   * @param name The unqualified name of the function, must not be null
+   * @param func A Implementation of an XPath Function object.
+   * @return the position of the function in the internal index.
+   */
+  public int installFunction(String name, Class func)
+  {
+
+    int funcIndex;
+    Object funcIndexObj = getFunctionID(name);
+
+    if (null != funcIndexObj)
+    {
+      funcIndex = ((Integer) funcIndexObj).intValue();
+      
+      if (funcIndex < NUM_BUILT_IN_FUNCS){
+              funcIndex = m_funcNextFreeIndex++;
+              m_functionID_customer.put(name, new Integer(funcIndex)); 
+      }
+      m_functions_customer[funcIndex - NUM_BUILT_IN_FUNCS] = func;          
+    }
+    else
+    {
+            funcIndex = m_funcNextFreeIndex++;
+                          
+            m_functions_customer[funcIndex-NUM_BUILT_IN_FUNCS] = func;
+                    
+            m_functionID_customer.put(name, 
+                new Integer(funcIndex));   
+    }
+    return funcIndex;
+  }
+
+  /**
+   * Tell if a built-in, non-namespaced function is available.
+   *
+   * @param methName The local name of the function.
+   *
+   * @return True if the function can be executed.
+   */
+  public boolean functionAvailable(String methName)
+  {
+      Object tblEntry = m_functionID.get(methName);
+      if (null != tblEntry) return true;
+      else{
+              tblEntry = m_functionID_customer.get(methName);
+              return (null != tblEntry)? true : false;
+      }
+  }
+}
diff --git a/src/main/java/org/apache/xpath/compiler/Keywords.java b/src/main/java/org/apache/xpath/compiler/Keywords.java
new file mode 100644
index 0000000..b624a87
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/Keywords.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Keywords.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.compiler;
+
+import java.util.Hashtable;
+
+/**
+ * Table of strings to operation code lookups.
+ * @xsl.usage internal
+ */
+public class Keywords
+{
+
+  /** Table of keywords to opcode associations. */
+  private static Hashtable m_keywords = new Hashtable();
+
+  /** Table of axes names to opcode associations. */
+  private static Hashtable m_axisnames = new Hashtable();
+
+  /** Table of function name to function ID associations. */
+  private static Hashtable m_nodetests = new Hashtable();
+
+  /** Table of node type strings to opcode associations. */
+  private static Hashtable m_nodetypes = new Hashtable();
+
+  /** ancestor axes string. */
+  private static final String FROM_ANCESTORS_STRING = "ancestor";
+
+  /** ancestor-or-self axes string. */
+  private static final String FROM_ANCESTORS_OR_SELF_STRING =
+    "ancestor-or-self";
+
+  /** attribute axes string. */
+  private static final String FROM_ATTRIBUTES_STRING = "attribute";
+
+  /** child axes string. */
+  private static final String FROM_CHILDREN_STRING = "child";
+
+  /** descendant-or-self axes string. */
+  private static final String FROM_DESCENDANTS_STRING = "descendant";
+
+  /** ancestor axes string. */
+  private static final String FROM_DESCENDANTS_OR_SELF_STRING =
+    "descendant-or-self";
+
+  /** following axes string. */
+  private static final String FROM_FOLLOWING_STRING = "following";
+
+  /** following-sibling axes string. */
+  private static final String FROM_FOLLOWING_SIBLINGS_STRING =
+    "following-sibling";
+
+  /** parent axes string. */
+  private static final String FROM_PARENT_STRING = "parent";
+
+  /** preceding axes string. */
+  private static final String FROM_PRECEDING_STRING = "preceding";
+
+  /** preceding-sibling axes string. */
+  private static final String FROM_PRECEDING_SIBLINGS_STRING =
+    "preceding-sibling";
+
+  /** self axes string. */
+  private static final String FROM_SELF_STRING = "self";
+
+  /** namespace axes string. */
+  private static final String FROM_NAMESPACE_STRING = "namespace";
+
+  /** self axes abreviated string. */
+  private static final String FROM_SELF_ABBREVIATED_STRING = ".";
+
+  /** comment node test string. */
+  private static final String NODETYPE_COMMENT_STRING = "comment";
+
+  /** text node test string. */
+  private static final String NODETYPE_TEXT_STRING = "text";
+
+  /** processing-instruction node test string. */
+  private static final String NODETYPE_PI_STRING = "processing-instruction";
+
+  /** Any node test string. */
+  private static final String NODETYPE_NODE_STRING = "node";
+
+  /** Wildcard element string. */
+  private static final String NODETYPE_ANYELEMENT_STRING = "*";
+
+  /** current function string. */
+  public static final String FUNC_CURRENT_STRING = "current";
+
+  /** last function string. */
+  public static final String FUNC_LAST_STRING = "last";
+
+  /** position function string. */
+  public static final String FUNC_POSITION_STRING = "position";
+
+  /** count function string. */
+  public static final String FUNC_COUNT_STRING = "count";
+
+  /** id function string. */
+  static final String FUNC_ID_STRING = "id";
+
+  /** key function string (XSLT). */
+  public static final String FUNC_KEY_STRING = "key";
+
+  /** local-name function string. */
+  public static final String FUNC_LOCAL_PART_STRING = "local-name";
+
+  /** namespace-uri function string. */
+  public static final String FUNC_NAMESPACE_STRING = "namespace-uri";
+
+  /** name function string. */
+  public static final String FUNC_NAME_STRING = "name";
+
+  /** generate-id function string (XSLT). */
+  public static final String FUNC_GENERATE_ID_STRING = "generate-id";
+
+  /** not function string. */
+  public static final String FUNC_NOT_STRING = "not";
+
+  /** true function string. */
+  public static final String FUNC_TRUE_STRING = "true";
+
+  /** false function string. */
+  public static final String FUNC_FALSE_STRING = "false";
+
+  /** boolean function string. */
+  public static final String FUNC_BOOLEAN_STRING = "boolean";
+
+  /** lang function string. */
+  public static final String FUNC_LANG_STRING = "lang";
+
+  /** number function string. */
+  public static final String FUNC_NUMBER_STRING = "number";
+
+  /** floor function string. */
+  public static final String FUNC_FLOOR_STRING = "floor";
+
+  /** ceiling function string. */
+  public static final String FUNC_CEILING_STRING = "ceiling";
+
+  /** round function string. */
+  public static final String FUNC_ROUND_STRING = "round";
+
+  /** sum function string. */
+  public static final String FUNC_SUM_STRING = "sum";
+
+  /** string function string. */
+  public static final String FUNC_STRING_STRING = "string";
+
+  /** starts-with function string. */
+  public static final String FUNC_STARTS_WITH_STRING = "starts-with";
+
+  /** contains function string. */
+  public static final String FUNC_CONTAINS_STRING = "contains";
+
+  /** substring-before function string. */
+  public static final String FUNC_SUBSTRING_BEFORE_STRING =
+    "substring-before";
+
+  /** substring-after function string. */
+  public static final String FUNC_SUBSTRING_AFTER_STRING = "substring-after";
+
+  /** normalize-space function string. */
+  public static final String FUNC_NORMALIZE_SPACE_STRING = "normalize-space";
+
+  /** translate function string. */
+  public static final String FUNC_TRANSLATE_STRING = "translate";
+
+  /** concat function string. */
+  public static final String FUNC_CONCAT_STRING = "concat";
+
+  /** system-property function string. */
+  public static final String FUNC_SYSTEM_PROPERTY_STRING = "system-property";
+
+  /** function-available function string (XSLT). */
+  public static final String FUNC_EXT_FUNCTION_AVAILABLE_STRING =
+    "function-available";
+
+  /** element-available function string (XSLT). */
+  public static final String FUNC_EXT_ELEM_AVAILABLE_STRING =
+    "element-available";
+
+  /** substring function string. */
+  public static final String FUNC_SUBSTRING_STRING = "substring";
+
+  /** string-length function string. */
+  public static final String FUNC_STRING_LENGTH_STRING = "string-length";
+
+  /** unparsed-entity-uri function string (XSLT). */
+  public static final String FUNC_UNPARSED_ENTITY_URI_STRING =
+    "unparsed-entity-uri";
+
+  // Proprietary, built in functions
+
+  /** current function string (Proprietary). */
+  public static final String FUNC_DOCLOCATION_STRING = "document-location";
+
+  static
+  {
+    m_axisnames.put(FROM_ANCESTORS_STRING,
+                    new Integer(OpCodes.FROM_ANCESTORS));
+    m_axisnames.put(FROM_ANCESTORS_OR_SELF_STRING,
+                    new Integer(OpCodes.FROM_ANCESTORS_OR_SELF));
+    m_axisnames.put(FROM_ATTRIBUTES_STRING,
+                    new Integer(OpCodes.FROM_ATTRIBUTES));
+    m_axisnames.put(FROM_CHILDREN_STRING,
+                    new Integer(OpCodes.FROM_CHILDREN));
+    m_axisnames.put(FROM_DESCENDANTS_STRING,
+                    new Integer(OpCodes.FROM_DESCENDANTS));
+    m_axisnames.put(FROM_DESCENDANTS_OR_SELF_STRING,
+                    new Integer(OpCodes.FROM_DESCENDANTS_OR_SELF));
+    m_axisnames.put(FROM_FOLLOWING_STRING,
+                    new Integer(OpCodes.FROM_FOLLOWING));
+    m_axisnames.put(FROM_FOLLOWING_SIBLINGS_STRING,
+                    new Integer(OpCodes.FROM_FOLLOWING_SIBLINGS));
+    m_axisnames.put(FROM_PARENT_STRING,
+                    new Integer(OpCodes.FROM_PARENT));
+    m_axisnames.put(FROM_PRECEDING_STRING,
+                    new Integer(OpCodes.FROM_PRECEDING));
+    m_axisnames.put(FROM_PRECEDING_SIBLINGS_STRING,
+                    new Integer(OpCodes.FROM_PRECEDING_SIBLINGS));
+    m_axisnames.put(FROM_SELF_STRING,
+                    new Integer(OpCodes.FROM_SELF));
+    m_axisnames.put(FROM_NAMESPACE_STRING,
+                    new Integer(OpCodes.FROM_NAMESPACE));
+    m_nodetypes.put(NODETYPE_COMMENT_STRING,
+                    new Integer(OpCodes.NODETYPE_COMMENT));
+    m_nodetypes.put(NODETYPE_TEXT_STRING,
+                    new Integer(OpCodes.NODETYPE_TEXT));
+    m_nodetypes.put(NODETYPE_PI_STRING,
+                    new Integer(OpCodes.NODETYPE_PI));
+    m_nodetypes.put(NODETYPE_NODE_STRING,
+                    new Integer(OpCodes.NODETYPE_NODE));
+    m_nodetypes.put(NODETYPE_ANYELEMENT_STRING,
+                    new Integer(OpCodes.NODETYPE_ANYELEMENT));
+    m_keywords.put(FROM_SELF_ABBREVIATED_STRING,
+                   new Integer(OpCodes.FROM_SELF));
+    m_keywords.put(FUNC_ID_STRING,
+                   new Integer(FunctionTable.FUNC_ID));
+    m_keywords.put(FUNC_KEY_STRING,
+                   new Integer(FunctionTable.FUNC_KEY));
+
+    m_nodetests.put(NODETYPE_COMMENT_STRING,
+                    new Integer(OpCodes.NODETYPE_COMMENT));
+    m_nodetests.put(NODETYPE_TEXT_STRING,
+                    new Integer(OpCodes.NODETYPE_TEXT));
+    m_nodetests.put(NODETYPE_PI_STRING,
+                    new Integer(OpCodes.NODETYPE_PI));
+    m_nodetests.put(NODETYPE_NODE_STRING,
+                    new Integer(OpCodes.NODETYPE_NODE));
+  }
+  
+  static Object getAxisName(String key){
+          return m_axisnames.get(key);
+  }
+  
+  static Object lookupNodeTest(String key){
+          return m_nodetests.get(key);
+  }
+  
+  static Object getKeyWord(String key){
+          return m_keywords.get(key);
+  }
+  
+  static Object getNodeType(String key){
+          return m_nodetypes.get(key);
+  }      
+}
diff --git a/src/main/java/org/apache/xpath/compiler/Lexer.java b/src/main/java/org/apache/xpath/compiler/Lexer.java
new file mode 100644
index 0000000..008ac43
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/Lexer.java
@@ -0,0 +1,669 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Lexer.java 524810 2007-04-02 15:51:55Z zongaro $
+ */
+package org.apache.xpath.compiler;
+
+import java.util.Vector;
+
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * This class is in charge of lexical processing of the XPath
+ * expression into tokens.
+ */
+class Lexer
+{
+
+  /**
+   * The target XPath.
+   */
+  private Compiler m_compiler;
+
+  /**
+   * The prefix resolver to map prefixes to namespaces in the XPath.
+   */
+  PrefixResolver m_namespaceContext;
+
+  /**
+   * The XPath processor object.
+   */
+  XPathParser m_processor;
+
+  /**
+   * This value is added to each element name in the TARGETEXTRA
+   * that is a 'target' (right-most top-level element name).
+   */
+  static final int TARGETEXTRA = 10000;
+
+  /**
+   * Ignore this, it is going away.
+   * This holds a map to the m_tokenQueue that tells where the top-level elements are.
+   * It is used for pattern matching so the m_tokenQueue can be walked backwards.
+   * Each element that is a 'target', (right-most top level element name) has
+   * TARGETEXTRA added to it.
+   *
+   */
+  private int m_patternMap[] = new int[100];
+
+  /**
+   * Ignore this, it is going away.
+   * The number of elements that m_patternMap maps;
+   */
+  private int m_patternMapSize;
+
+  /**
+   * Create a Lexer object.
+   *
+   * @param compiler The owning compiler for this lexer.
+   * @param resolver The prefix resolver for mapping qualified name prefixes 
+   *                 to namespace URIs.
+   * @param xpathProcessor The parser that is processing strings to opcodes.
+   */
+  Lexer(Compiler compiler, PrefixResolver resolver,
+        XPathParser xpathProcessor)
+  {
+
+    m_compiler = compiler;
+    m_namespaceContext = resolver;
+    m_processor = xpathProcessor;
+  }
+
+  /**
+   * Walk through the expression and build a token queue, and a map of the top-level
+   * elements.
+   * @param pat XSLT Expression.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  void tokenize(String pat) throws javax.xml.transform.TransformerException
+  {
+    tokenize(pat, null);
+  }
+
+  /**
+   * Walk through the expression and build a token queue, and a map of the top-level
+   * elements.
+   * @param pat XSLT Expression.
+   * @param targetStrings Vector to hold Strings, may be null.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  void tokenize(String pat, Vector targetStrings)
+          throws javax.xml.transform.TransformerException
+  {
+
+    m_compiler.m_currentPattern = pat;
+    m_patternMapSize = 0; 
+
+    // This needs to grow too.  Use a conservative estimate that the OpMapVector
+    // needs about five time the length of the input path expression - to a
+    // maximum of MAXTOKENQUEUESIZE*5.  If the OpMapVector needs to grow, grow
+    // it freely (second argument to constructor).
+    int initTokQueueSize = ((pat.length() < OpMap.MAXTOKENQUEUESIZE)
+                                 ? pat.length() :  OpMap.MAXTOKENQUEUESIZE) * 5;
+    m_compiler.m_opMap = new OpMapVector(initTokQueueSize,
+                                         OpMap.BLOCKTOKENQUEUESIZE * 5,
+                                         OpMap.MAPINDEX_LENGTH);
+
+    int nChars = pat.length();
+    int startSubstring = -1; 
+    int posOfNSSep = -1;
+    boolean isStartOfPat = true;
+    boolean isAttrName = false;
+    boolean isNum = false;
+
+    // Nesting of '[' so we can know if the given element should be
+    // counted inside the m_patternMap.
+    int nesting = 0;
+
+    // char[] chars = pat.toCharArray();
+    for (int i = 0; i < nChars; i++)
+    {
+      char c = pat.charAt(i);
+
+      switch (c)
+      {
+      case '\"' :
+      {
+        if (startSubstring != -1)
+        {
+          isNum = false;
+          isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
+          isAttrName = false;
+
+          if (-1 != posOfNSSep)
+          {
+            posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, i);
+          }
+          else
+          {
+            addToTokenQueue(pat.substring(startSubstring, i));
+          }
+        }
+
+        startSubstring = i;
+
+        for (i++; (i < nChars) && ((c = pat.charAt(i)) != '\"'); i++);
+
+        if (c == '\"' && i < nChars)
+        {
+          addToTokenQueue(pat.substring(startSubstring, i + 1));
+
+          startSubstring = -1;
+        }
+        else
+        {
+          m_processor.error(XPATHErrorResources.ER_EXPECTED_DOUBLE_QUOTE,
+                            null);  //"misquoted literal... expected double quote!");
+        }
+      }
+      break;
+      case '\'' :
+        if (startSubstring != -1)
+        {
+          isNum = false;
+          isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
+          isAttrName = false;
+
+          if (-1 != posOfNSSep)
+          {
+            posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, i);
+          }
+          else
+          {
+            addToTokenQueue(pat.substring(startSubstring, i));
+          }
+        }
+
+        startSubstring = i;
+
+        for (i++; (i < nChars) && ((c = pat.charAt(i)) != '\''); i++);
+
+        if (c == '\'' && i < nChars)
+        {
+          addToTokenQueue(pat.substring(startSubstring, i + 1));
+
+          startSubstring = -1;
+        }
+        else
+        {
+          m_processor.error(XPATHErrorResources.ER_EXPECTED_SINGLE_QUOTE,
+                            null);  //"misquoted literal... expected single quote!");
+        }
+        break;
+      case 0x0A :
+      case 0x0D :
+      case ' ' :
+      case '\t' :
+        if (startSubstring != -1)
+        {
+          isNum = false;
+          isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
+          isAttrName = false;
+
+          if (-1 != posOfNSSep)
+          {
+            posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, i);
+          }
+          else
+          {
+            addToTokenQueue(pat.substring(startSubstring, i));
+          }
+
+          startSubstring = -1;
+        }
+        break;
+      case '@' :
+        isAttrName = true;
+
+      // fall-through on purpose
+      case '-' :
+        if ('-' == c)
+        {
+          if (!(isNum || (startSubstring == -1)))
+          {
+            break;
+          }
+
+          isNum = false;
+        }
+
+      // fall-through on purpose
+      case '(' :
+      case '[' :
+      case ')' :
+      case ']' :
+      case '|' :
+      case '/' :
+      case '*' :
+      case '+' :
+      case '=' :
+      case ',' :
+      case '\\' :  // Unused at the moment
+      case '^' :  // Unused at the moment
+      case '!' :  // Unused at the moment
+      case '$' :
+      case '<' :
+      case '>' :
+        if (startSubstring != -1)
+        {
+          isNum = false;
+          isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
+          isAttrName = false;
+
+          if (-1 != posOfNSSep)
+          {
+            posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, i);
+          }
+          else
+          {
+            addToTokenQueue(pat.substring(startSubstring, i));
+          }
+
+          startSubstring = -1;
+        }
+        else if (('/' == c) && isStartOfPat)
+        {
+          isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
+        }
+        else if ('*' == c)
+        {
+          isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
+          isAttrName = false;
+        }
+
+        if (0 == nesting)
+        {
+          if ('|' == c)
+          {
+            if (null != targetStrings)
+            {
+              recordTokenString(targetStrings);
+            }
+
+            isStartOfPat = true;
+          }
+        }
+
+        if ((')' == c) || (']' == c))
+        {
+          nesting--;
+        }
+        else if (('(' == c) || ('[' == c))
+        {
+          nesting++;
+        }
+
+        addToTokenQueue(pat.substring(i, i + 1));
+        break;
+      case ':' :
+        if (i>0)
+        {
+          if (posOfNSSep == (i - 1))
+          {
+            if (startSubstring != -1)
+            {
+              if (startSubstring < (i - 1))
+                addToTokenQueue(pat.substring(startSubstring, i - 1));
+            }
+
+            isNum = false;
+            isAttrName = false;
+            startSubstring = -1;
+            posOfNSSep = -1;
+
+            addToTokenQueue(pat.substring(i - 1, i + 1));
+
+            break;
+          }
+          else
+          {
+            posOfNSSep = i;
+          }
+        }
+
+      // fall through on purpose
+      default :
+        if (-1 == startSubstring)
+        {
+          startSubstring = i;
+          isNum = Character.isDigit(c);
+        }
+        else if (isNum)
+        {
+          isNum = Character.isDigit(c);
+        }
+      }
+    }
+
+    if (startSubstring != -1)
+    {
+      isNum = false;
+      isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
+
+      if ((-1 != posOfNSSep) || 
+         ((m_namespaceContext != null) && (m_namespaceContext.handlesNullPrefixes())))
+      {
+        posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, nChars);
+      }
+      else
+      {
+        addToTokenQueue(pat.substring(startSubstring, nChars));
+      }
+    }
+
+    if (0 == m_compiler.getTokenQueueSize())
+    {
+      m_processor.error(XPATHErrorResources.ER_EMPTY_EXPRESSION, null);  //"Empty expression!");
+    }
+    else if (null != targetStrings)
+    {
+      recordTokenString(targetStrings);
+    }
+
+    m_processor.m_queueMark = 0;
+  }
+
+  /**
+   * Record the current position on the token queue as long as
+   * this is a top-level element.  Must be called before the
+   * next token is added to the m_tokenQueue.
+   *
+   * @param nesting The nesting count for the pattern element.
+   * @param isStart true if this is the start of a pattern.
+   * @param isAttrName true if we have determined that this is an attribute name.
+   *
+   * @return true if this is the start of a pattern.
+   */
+  private boolean mapPatternElemPos(int nesting, boolean isStart,
+                                    boolean isAttrName)
+  {
+
+    if (0 == nesting)
+    {
+      if(m_patternMapSize >= m_patternMap.length)
+      {
+        int patternMap[] = m_patternMap;
+        int len = m_patternMap.length;
+        m_patternMap = new int[m_patternMapSize + 100];
+        System.arraycopy(patternMap, 0, m_patternMap, 0, len);
+      } 
+      if (!isStart)
+      {
+        m_patternMap[m_patternMapSize - 1] -= TARGETEXTRA;
+      }
+      m_patternMap[m_patternMapSize] =
+        (m_compiler.getTokenQueueSize() - (isAttrName ? 1 : 0)) + TARGETEXTRA;
+
+      m_patternMapSize++;
+
+      isStart = false;
+    }
+
+    return isStart;
+  }
+
+  /**
+   * Given a map pos, return the corresponding token queue pos.
+   *
+   * @param i The index in the m_patternMap.
+   *
+   * @return the token queue position.
+   */
+  private int getTokenQueuePosFromMap(int i)
+  {
+
+    int pos = m_patternMap[i];
+
+    return (pos >= TARGETEXTRA) ? (pos - TARGETEXTRA) : pos;
+  }
+
+  /**
+   * Reset token queue mark and m_token to a
+   * given position.
+   * @param mark The new position.
+   */
+  private final void resetTokenMark(int mark)
+  {
+
+    int qsz = m_compiler.getTokenQueueSize();
+
+    m_processor.m_queueMark = (mark > 0)
+                              ? ((mark <= qsz) ? mark - 1 : mark) : 0;
+
+    if (m_processor.m_queueMark < qsz)
+    {
+      m_processor.m_token =
+        (String) m_compiler.getTokenQueue().elementAt(m_processor.m_queueMark++);
+      m_processor.m_tokenChar = m_processor.m_token.charAt(0);
+    }
+    else
+    {
+      m_processor.m_token = null;
+      m_processor.m_tokenChar = 0;
+    }
+  }
+
+  /**
+   * Given a string, return the corresponding keyword token.
+   *
+   * @param key The keyword.
+   *
+   * @return An opcode value.
+   */
+  final int getKeywordToken(String key)
+  {
+
+    int tok;
+
+    try
+    {
+      Integer itok = (Integer) Keywords.getKeyWord(key);
+
+      tok = (null != itok) ? itok.intValue() : 0;
+    }
+    catch (NullPointerException npe)
+    {
+      tok = 0;
+    }
+    catch (ClassCastException cce)
+    {
+      tok = 0;
+    }
+
+    return tok;
+  }
+
+  /**
+   * Record the current token in the passed vector.
+   *
+   * @param targetStrings Vector of string.
+   */
+  private void recordTokenString(Vector targetStrings)
+  {
+
+    int tokPos = getTokenQueuePosFromMap(m_patternMapSize - 1);
+
+    resetTokenMark(tokPos + 1);
+
+    if (m_processor.lookahead('(', 1))
+    {
+      int tok = getKeywordToken(m_processor.m_token);
+
+      switch (tok)
+      {
+      case OpCodes.NODETYPE_COMMENT :
+        targetStrings.addElement(PsuedoNames.PSEUDONAME_COMMENT);
+        break;
+      case OpCodes.NODETYPE_TEXT :
+        targetStrings.addElement(PsuedoNames.PSEUDONAME_TEXT);
+        break;
+      case OpCodes.NODETYPE_NODE :
+        targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
+        break;
+      case OpCodes.NODETYPE_ROOT :
+        targetStrings.addElement(PsuedoNames.PSEUDONAME_ROOT);
+        break;
+      case OpCodes.NODETYPE_ANYELEMENT :
+        targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
+        break;
+      case OpCodes.NODETYPE_PI :
+        targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
+        break;
+      default :
+        targetStrings.addElement(PsuedoNames.PSEUDONAME_ANY);
+      }
+    }
+    else
+    {
+      if (m_processor.tokenIs('@'))
+      {
+        tokPos++;
+
+        resetTokenMark(tokPos + 1);
+      }
+
+      if (m_processor.lookahead(':', 1))
+      {
+        tokPos += 2;
+      }
+
+      targetStrings.addElement(m_compiler.getTokenQueue().elementAt(tokPos));
+    }
+  }
+
+  /**
+   * Add a token to the token queue.
+   *
+   *
+   * @param s The token.
+   */
+  private final void addToTokenQueue(String s)
+  {
+    m_compiler.getTokenQueue().addElement(s);
+  }
+
+  /**
+   * When a seperator token is found, see if there's a element name or
+   * the like to map.
+   *
+   * @param pat The XPath name string.
+   * @param startSubstring The start of the name string.
+   * @param posOfNSSep The position of the namespace seperator (':').
+   * @param posOfScan The end of the name index.
+   *
+   * @throws javax.xml.transform.TransformerException
+   *
+   * @return -1 always.
+   */
+  private int mapNSTokens(String pat, int startSubstring, int posOfNSSep,
+                          int posOfScan)
+           throws javax.xml.transform.TransformerException
+ {
+
+    String prefix = "";
+    
+    if ((startSubstring >= 0) && (posOfNSSep >= 0))
+    {
+       prefix = pat.substring(startSubstring, posOfNSSep);
+    }
+    String uName;
+
+    if ((null != m_namespaceContext) &&!prefix.equals("*")
+            &&!prefix.equals("xmlns"))
+    {
+      try
+      {
+        if (prefix.length() > 0)
+          uName = ((PrefixResolver) m_namespaceContext).getNamespaceForPrefix(
+            prefix);
+        else
+        {
+
+          // Assume last was wildcard. This is not legal according
+          // to the draft. Set the below to true to make namespace
+          // wildcards work.
+          if (false)
+          {
+            addToTokenQueue(":");
+
+            String s = pat.substring(posOfNSSep + 1, posOfScan);
+
+            if (s.length() > 0)
+              addToTokenQueue(s);
+
+            return -1;
+          }
+          else
+          {
+            uName =
+              ((PrefixResolver) m_namespaceContext).getNamespaceForPrefix(
+                prefix);
+          }
+        }
+      }
+      catch (ClassCastException cce)
+      {
+        uName = m_namespaceContext.getNamespaceForPrefix(prefix);
+      }
+    }
+    else
+    {
+      uName = prefix;
+    }
+
+    if ((null != uName) && (uName.length() > 0))
+    {
+      addToTokenQueue(uName);
+      addToTokenQueue(":");
+
+      String s = pat.substring(posOfNSSep + 1, posOfScan);
+
+      if (s.length() > 0)
+        addToTokenQueue(s);
+    }
+    else
+    {
+        // To older XPath code it doesn't matter if
+        // error() is called or errorForDOM3().
+		m_processor.errorForDOM3(XPATHErrorResources.ER_PREFIX_MUST_RESOLVE,
+						 new String[] {prefix});  //"Prefix must resolve to a namespace: {0}";
+
+/** old code commented out 17-Sep-2004
+// error("Could not locate namespace for prefix: "+prefix);
+//		  m_processor.error(XPATHErrorResources.ER_PREFIX_MUST_RESOLVE,
+//					 new String[] {prefix});  //"Prefix must resolve to a namespace: {0}";
+*/
+
+      /***  Old code commented out 10-Jan-2001
+      addToTokenQueue(prefix);
+      addToTokenQueue(":");
+
+      String s = pat.substring(posOfNSSep + 1, posOfScan);
+
+      if (s.length() > 0)
+        addToTokenQueue(s);
+      ***/
+    }
+
+    return -1;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/compiler/OpCodes.java b/src/main/java/org/apache/xpath/compiler/OpCodes.java
new file mode 100644
index 0000000..04bb958
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/OpCodes.java
@@ -0,0 +1,632 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: OpCodes.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.compiler;
+
+/**
+ * Operations codes for XPath.
+ *
+ * Code for the descriptions of the operations codes:
+ * [UPPER CASE] indicates a literal value,
+ * [lower case] is a description of a value,
+ *      ([length] always indicates the length of the operation,
+ *       including the operations code and the length integer.)
+ * {UPPER CASE} indicates the given production,
+ * {description} is the description of a new production,
+ *      (For instance, {boolean expression} means some expression
+ *       that should be resolved to a boolean.)
+ *  * means that it occurs zero or more times,
+ *  + means that it occurs one or more times,
+ *  ? means that it is optional.
+ *
+ * returns: indicates what the production should return.
+ */
+public class OpCodes
+{
+
+  /**
+   * [ENDOP]
+   * Some operators may like to have a terminator.
+   * @xsl.usage advanced
+   */
+  public static final int ENDOP = -1;
+
+  /**
+   * [EMPTY]
+   * Empty slot to indicate NULL.
+   */
+  public static final int EMPTY = -2;
+
+  /**
+   * [ELEMWILDCARD]
+   * Means ELEMWILDCARD ("*"), used instead
+   * of string index in some places.
+   * @xsl.usage advanced
+   */
+  public static final int ELEMWILDCARD = -3;
+
+  /**
+   * [OP_XPATH]
+   * [length]
+   *  {expression}
+   *
+   * returns:
+   *  XNodeSet
+   *  XNumber
+   *  XString
+   *  XBoolean
+   *  XRTree
+   *  XObject
+   * @xsl.usage advanced
+   */
+  public static final int OP_XPATH = 1;
+
+  /**
+   * [OP_OR]
+   * [length]
+   *  {boolean expression}
+   *  {boolean expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_OR = 2;
+
+  /**
+   * [OP_AND]
+   * [length]
+   *  {boolean expression}
+   *  {boolean expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_AND = 3;
+
+  /**
+   * [OP_NOTEQUALS]
+   * [length]
+   *  {expression}
+   *  {expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_NOTEQUALS = 4;
+
+  /**
+   * [OP_EQUALS]
+   * [length]
+   *  {expression}
+   *  {expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_EQUALS = 5;
+
+  /**
+   * [OP_LTE] (less-than-or-equals)
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_LTE = 6;
+
+  /**
+   * [OP_LT] (less-than)
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_LT = 7;
+
+  /**
+   * [OP_GTE] (greater-than-or-equals)
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_GTE = 8;
+
+  /**
+   * [OP_GT] (greater-than)
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_GT = 9;
+
+  /**
+   * [OP_PLUS]
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XNumber
+   * @xsl.usage advanced
+   */
+  public static final int OP_PLUS = 10;
+
+  /**
+   * [OP_MINUS]
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XNumber
+   * @xsl.usage advanced
+   */
+  public static final int OP_MINUS = 11;
+
+  /**
+   * [OP_MULT]
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XNumber
+   * @xsl.usage advanced
+   */
+  public static final int OP_MULT = 12;
+
+  /**
+   * [OP_DIV]
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XNumber
+   * @xsl.usage advanced
+   */
+  public static final int OP_DIV = 13;
+
+  /**
+   * [OP_MOD]
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XNumber
+   * @xsl.usage advanced
+   */
+  public static final int OP_MOD = 14;
+
+  /**
+   * [OP_QUO]
+   * [length]
+   *  {number expression}
+   *  {number expression}
+   *
+   * returns:
+   *  XNumber
+   * @xsl.usage advanced
+   */
+  public static final int OP_QUO = 15;
+
+  /**
+   * [OP_NEG]
+   * [length]
+   *  {number expression}
+   *
+   * returns:
+   *  XNumber
+   * @xsl.usage advanced
+   */
+  public static final int OP_NEG = 16;
+
+  /**
+   * [OP_STRING] (cast operation)
+   * [length]
+   *  {expression}
+   *
+   * returns:
+   *  XString
+   * @xsl.usage advanced
+   */
+  public static final int OP_STRING = 17;
+
+  /**
+   * [OP_BOOL] (cast operation)
+   * [length]
+   *  {expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_BOOL = 18;
+
+  /**
+   * [OP_NUMBER] (cast operation)
+   * [length]
+   *  {expression}
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int OP_NUMBER = 19;
+
+  /**
+   * [OP_UNION]
+   * [length]
+   *  {PathExpr}+
+   *
+   * returns:
+   *  XNodeSet
+   * @xsl.usage advanced
+   */
+  public static final int OP_UNION = 20;
+
+  /**
+   * [OP_LITERAL]
+   * [3]
+   * [index to token]
+   *
+   * returns:
+   *  XString
+   * @xsl.usage advanced
+   */
+  public static final int OP_LITERAL = 21;
+
+  /** The low opcode for nodesets, needed by getFirstPredicateOpPos and 
+   *  getNextStepPos.          */
+  static final int FIRST_NODESET_OP = 22;
+
+  /**
+   * [OP_VARIABLE]
+   * [4]
+   * [index to namespace token, or EMPTY]
+   * [index to function name token]
+   *
+   * returns:
+   *  XString
+   * @xsl.usage advanced
+   */
+  public static final int OP_VARIABLE = 22;
+
+  /**
+   * [OP_GROUP]
+   * [length]
+   *  {expression}
+   *
+   * returns:
+   *  XNodeSet
+   *  XNumber
+   *  XString
+   *  XBoolean
+   *  XRTree
+   *  XObject
+   * @xsl.usage advanced
+   */
+  public static final int OP_GROUP = 23;
+
+  /**
+   * [OP_EXTFUNCTION] (Extension function.)
+   * [length]
+   * [index to namespace token]
+   * [index to function name token]
+   *  {OP_ARGUMENT}
+   *
+   * returns:
+   *  XNodeSet
+   *  XNumber
+   *  XString
+   *  XBoolean
+   *  XRTree
+   *  XObject
+   * @xsl.usage advanced
+   */
+  public static final int OP_EXTFUNCTION = 24;
+
+  /**
+   * [OP_FUNCTION]
+   * [length]
+   * [FUNC_name]
+   *  {OP_ARGUMENT}
+   * [ENDOP]
+   *
+   * returns:
+   *  XNodeSet
+   *  XNumber
+   *  XString
+   *  XBoolean
+   *  XRTree
+   *  XObject
+   * @xsl.usage advanced
+   */
+  public static final int OP_FUNCTION = 25;
+
+  /** The last opcode for stuff that can be a nodeset.         */
+  static final int LAST_NODESET_OP = 25;
+
+  /**
+   * [OP_ARGUMENT] (Function argument.)
+   * [length]
+   *  {expression}
+   *
+   * returns:
+   *  XNodeSet
+   *  XNumber
+   *  XString
+   *  XBoolean
+   *  XRTree
+   *  XObject
+   * @xsl.usage advanced
+   */
+  public static final int OP_ARGUMENT = 26;
+
+  /**
+   * [OP_NUMBERLIT] (Number literal.)
+   * [3]
+   * [index to token]
+   *
+   * returns:
+   *  XString
+   * @xsl.usage advanced
+   */
+  public static final int OP_NUMBERLIT = 27;
+
+  /**
+   * [OP_LOCATIONPATH]
+   * [length]
+   *   {FROM_stepType}
+   * | {function}
+   * {predicate}
+   * [ENDOP]
+   *
+   * (Note that element and attribute namespaces and
+   * names can be wildcarded '*'.)
+   *
+   * returns:
+   *  XNodeSet
+   * @xsl.usage advanced
+   */
+  public static final int OP_LOCATIONPATH = 28;
+
+  // public static final int LOCATIONPATHEX_MASK = 0x0000FFFF;
+  // public static final int LOCATIONPATHEX_ISSIMPLE = 0x00010000;
+  // public static final int OP_LOCATIONPATH_EX = (28 | 0x00010000);
+
+  /**
+   * [OP_PREDICATE]
+   * [length]
+   *  {expression}
+   * [ENDOP] (For safety)
+   *
+   * returns:
+   *  XBoolean or XNumber
+   * @xsl.usage advanced
+   */
+  public static final int OP_PREDICATE = 29;
+
+  /**
+   * [OP_MATCHPATTERN]
+   * [length]
+   *  {PathExpr}+
+   *
+   * returns:
+   *  XNodeSet
+   * @xsl.usage advanced
+   */
+  public static final int OP_MATCHPATTERN = 30;
+
+  /**
+   * [OP_LOCATIONPATHPATTERN]
+   * [length]
+   *   {FROM_stepType}
+   * | {function}{predicate}
+   * [ENDOP]
+   * returns:
+   *  XNodeSet
+   * @xsl.usage advanced
+   */
+  public static final int OP_LOCATIONPATHPATTERN = 31;
+
+  /**
+   * [NODETYPE_COMMENT]
+   * No size or arguments.
+   * Note: must not overlap function OP number!
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int NODETYPE_COMMENT = 1030;
+
+  /**
+   * [NODETYPE_TEXT]
+   * No size or arguments.
+   * Note: must not overlap function OP number!
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int NODETYPE_TEXT = 1031;
+
+  /**
+   * [NODETYPE_PI]
+   * [index to token]
+   * Note: must not overlap function OP number!
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int NODETYPE_PI = 1032;
+
+  /**
+   * [NODETYPE_NODE]
+   * No size or arguments.
+   * Note: must not overlap function OP number!
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int NODETYPE_NODE = 1033;
+
+  /**
+   * [NODENAME]
+   * [index to ns token or EMPTY]
+   * [index to name token]
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int NODENAME = 34;
+
+  /**
+   * [NODETYPE_ROOT]
+   * No size or arguments.
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int NODETYPE_ROOT = 35;
+
+  /**
+   * [NODETYPE_ANY]
+   * No size or arguments.
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int NODETYPE_ANYELEMENT = 36;
+
+  /**
+   * [NODETYPE_ANY]
+   * No size or arguments.
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int NODETYPE_FUNCTEST = 1034;
+
+  /**
+   * [FROM_stepType]
+   * [length, including predicates]
+   * [length of just the step, without the predicates]
+   * {node test}
+   * {predicates}?
+   *
+   * returns:
+   *  XBoolean
+   * @xsl.usage advanced
+   */
+  public static final int AXES_START_TYPES = 37;
+
+  /** ancestor axes opcode.         */
+  public static final int FROM_ANCESTORS = 37;
+
+  /** ancestor-or-self axes opcode.         */
+  public static final int FROM_ANCESTORS_OR_SELF = 38;
+
+  /** attribute axes opcode.         */
+  public static final int FROM_ATTRIBUTES = 39;
+
+  /** children axes opcode.         */
+  public static final int FROM_CHILDREN = 40;
+
+  /** descendants axes opcode.         */
+  public static final int FROM_DESCENDANTS = 41;
+
+  /** descendants-of-self axes opcode.         */
+  public static final int FROM_DESCENDANTS_OR_SELF = 42;
+
+  /** following axes opcode.         */
+  public static final int FROM_FOLLOWING = 43;
+
+  /** following-siblings axes opcode.         */
+  public static final int FROM_FOLLOWING_SIBLINGS = 44;
+
+  /** parent axes opcode.         */
+  public static final int FROM_PARENT = 45;
+
+  /** preceding axes opcode.         */
+  public static final int FROM_PRECEDING = 46;
+
+  /** preceding-sibling axes opcode.         */
+  public static final int FROM_PRECEDING_SIBLINGS = 47;
+
+  /** self axes opcode.         */
+  public static final int FROM_SELF = 48;
+
+  /** namespace axes opcode.         */
+  public static final int FROM_NAMESPACE = 49;
+
+  /** '/' axes opcode.         */
+  public static final int FROM_ROOT = 50;
+
+  /**
+   * For match patterns.
+   * @xsl.usage advanced
+   */
+  public static final int MATCH_ATTRIBUTE = 51;
+
+  /**
+   * For match patterns.
+   * @xsl.usage advanced
+   */
+  public static final int MATCH_ANY_ANCESTOR = 52;
+
+  /**
+   * For match patterns.
+   * @xsl.usage advanced
+   */
+  public static final int MATCH_IMMEDIATE_ANCESTOR = 53;
+
+  /** The end of the axes types.    */
+  public static final int AXES_END_TYPES = 53;
+
+  /** The next free ID.  Please keep this up to date.  */
+  private static final int NEXT_FREE_ID = 99;
+}
diff --git a/src/main/java/org/apache/xpath/compiler/OpMap.java b/src/main/java/org/apache/xpath/compiler/OpMap.java
new file mode 100644
index 0000000..349721f
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/OpMap.java
@@ -0,0 +1,455 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: OpMap.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.compiler;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.utils.ObjectVector;
+import org.apache.xpath.patterns.NodeTest;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * This class represents the data structure basics of the XPath
+ * object.
+ */
+public class OpMap
+{
+
+  /**
+   * The current pattern string, for diagnostics purposes
+   */
+  protected String m_currentPattern;
+
+  /**
+   * Return the expression as a string for diagnostics.
+   *
+   * @return The expression string.
+   */
+  public String toString()
+  {
+    return m_currentPattern;
+  }
+
+  /**
+   * Return the expression as a string for diagnostics.
+   *
+   * @return The expression string.
+   */
+  public String getPatternString()
+  {
+    return m_currentPattern;
+  }
+
+  /**
+   * The starting size of the token queue.
+   */
+  static final int MAXTOKENQUEUESIZE = 500;
+
+  /*
+   * Amount to grow token queue when it becomes full
+   */
+  static final int BLOCKTOKENQUEUESIZE = 500;
+  
+  /**
+   *  TokenStack is the queue of used tokens. The current token is the token at the
+   * end of the m_tokenQueue. The idea is that the queue can be marked and a sequence
+   * of tokens can be reused.
+   */
+  ObjectVector m_tokenQueue = new ObjectVector(MAXTOKENQUEUESIZE, BLOCKTOKENQUEUESIZE);
+
+  /**
+   * Get the XPath as a list of tokens.
+   *
+   * @return ObjectVector of tokens.
+   */
+  public ObjectVector getTokenQueue()
+  {
+    return m_tokenQueue;
+  }
+
+  /**
+   * Get the XPath as a list of tokens.
+   *
+   * @param pos index into token queue.
+   *
+   * @return The token, normally a string.
+   */
+  public Object getToken(int pos)
+  {
+    return m_tokenQueue.elementAt(pos);
+  }
+
+  /**
+   * The current size of the token queue.
+   */
+//  public int m_tokenQueueSize = 0;
+
+  /**
+    * Get size of the token queue.
+   *
+   * @return The size of the token queue.
+   */
+  public int getTokenQueueSize()
+  {
+    return m_tokenQueue.size();
+    
+  }
+
+  /**
+   * An operations map is used instead of a proper parse tree.  It contains
+   * operations codes and indexes into the m_tokenQueue.
+   * I use an array instead of a full parse tree in order to cut down
+   * on the number of objects created.
+   */
+  OpMapVector m_opMap = null;
+
+  /**
+    * Get the opcode list that describes the XPath operations.  It contains
+   * operations codes and indexes into the m_tokenQueue.
+   * I use an array instead of a full parse tree in order to cut down
+   * on the number of objects created.
+   *
+   * @return An IntVector that is the opcode list that describes the XPath operations.
+   */
+  public OpMapVector getOpMap()
+  {
+    return m_opMap;
+  }
+
+  // Position indexes
+
+  /**
+   * The length is always the opcode position + 1.
+   * Length is always expressed as the opcode+length bytes,
+   * so it is always 2 or greater.
+   */
+  public static final int MAPINDEX_LENGTH = 1;
+
+  /**
+   * Replace the large arrays
+   * with a small array.
+   */
+  void shrink()
+  {
+
+    int n = m_opMap.elementAt(MAPINDEX_LENGTH);
+    m_opMap.setToSize(n + 4);
+
+    m_opMap.setElementAt(0,n);
+    m_opMap.setElementAt(0,n+1);
+    m_opMap.setElementAt(0,n+2);
+
+
+    n = m_tokenQueue.size();
+    m_tokenQueue.setToSize(n + 4);
+
+    m_tokenQueue.setElementAt(null,n);
+    m_tokenQueue.setElementAt(null,n + 1);
+    m_tokenQueue.setElementAt(null,n + 2);
+  }
+
+  /**
+  * Given an operation position, return the current op.
+   *
+   * @param opPos index into op map.
+   * @return the op that corresponds to the opPos argument.
+   */
+  public int getOp(int opPos)
+  {
+    return m_opMap.elementAt(opPos);
+  }
+
+  /**
+  * Set the op at index to the given int.
+   *
+   * @param opPos index into op map.
+   * @param value Value to set
+   */
+  public void setOp(int opPos, int value)
+  {
+     m_opMap.setElementAt(value,opPos);
+  }
+  
+  /**
+   * Given an operation position, return the end position, i.e. the
+   * beginning of the next operation.
+   *
+   * @param opPos An op position of an operation for which there is a size 
+   *              entry following.
+   * @return position of next operation in m_opMap.
+   */
+  public int getNextOpPos(int opPos)
+  {
+    return opPos + m_opMap.elementAt(opPos + 1);
+  }
+
+  /**
+   * Given a location step position, return the end position, i.e. the
+   * beginning of the next step.
+   *
+   * @param opPos the position of a location step.
+   * @return the position of the next location step.
+   */
+  public int getNextStepPos(int opPos)
+  {
+
+    int stepType = getOp(opPos);
+
+    if ((stepType >= OpCodes.AXES_START_TYPES)
+            && (stepType <= OpCodes.AXES_END_TYPES))
+    {
+      return getNextOpPos(opPos);
+    }
+    else if ((stepType >= OpCodes.FIRST_NODESET_OP)
+             && (stepType <= OpCodes.LAST_NODESET_OP))
+    {
+      int newOpPos = getNextOpPos(opPos);
+
+      while (OpCodes.OP_PREDICATE == getOp(newOpPos))
+      {
+        newOpPos = getNextOpPos(newOpPos);
+      }
+
+      stepType = getOp(newOpPos);
+
+      if (!((stepType >= OpCodes.AXES_START_TYPES)
+            && (stepType <= OpCodes.AXES_END_TYPES)))
+      {
+        return OpCodes.ENDOP;
+      }
+
+      return newOpPos;
+    }
+    else
+    {
+      throw new RuntimeException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_UNKNOWN_STEP, new Object[]{String.valueOf(stepType)})); 
+      //"Programmer's assertion in getNextStepPos: unknown stepType: " + stepType);
+    }
+  }
+
+  /**
+   * Given an operation position, return the end position, i.e. the
+   * beginning of the next operation.
+   *
+   * @param opMap The operations map.
+   * @param opPos index to operation, for which there is a size entry following.
+   * @return position of next operation in m_opMap.
+   */
+  public static int getNextOpPos(int[] opMap, int opPos)
+  {
+    return opPos + opMap[opPos + 1];
+  }
+
+  /**
+   * Given an FROM_stepType position, return the position of the
+   * first predicate, if there is one, or else this will point
+   * to the end of the FROM_stepType.
+   * Example:
+   *  int posOfPredicate = xpath.getNextOpPos(stepPos);
+   *  boolean hasPredicates =
+   *            OpCodes.OP_PREDICATE == xpath.getOp(posOfPredicate);
+   *
+   * @param opPos position of FROM_stepType op. 
+   * @return position of predicate in FROM_stepType structure.
+   */
+  public int getFirstPredicateOpPos(int opPos)
+     throws javax.xml.transform.TransformerException
+  {
+
+    int stepType = m_opMap.elementAt(opPos);
+
+    if ((stepType >= OpCodes.AXES_START_TYPES)
+            && (stepType <= OpCodes.AXES_END_TYPES))
+    {
+      return opPos + m_opMap.elementAt(opPos + 2);
+    }
+    else if ((stepType >= OpCodes.FIRST_NODESET_OP)
+             && (stepType <= OpCodes.LAST_NODESET_OP))
+    {
+      return opPos + m_opMap.elementAt(opPos + 1);
+    }
+    else if(-2 == stepType)
+    {
+      return -2;
+    }
+    else
+    {
+      error(org.apache.xpath.res.XPATHErrorResources.ER_UNKNOWN_OPCODE,
+            new Object[]{ String.valueOf(stepType) });  //"ERROR! Unknown op code: "+m_opMap[opPos]);
+      return -1;
+    }
+  }
+  
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg An error msgkey that corresponds to one of the constants found 
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which 
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to 
+   *                              throw an exception.
+   */
+  public void error(String msg, Object[] args) throws javax.xml.transform.TransformerException
+  {
+
+    java.lang.String fmsg = org.apache.xalan.res.XSLMessages.createXPATHMessage(msg, args);
+    
+
+    throw new javax.xml.transform.TransformerException(fmsg);
+  }
+
+
+  /**
+   * Go to the first child of a given operation.
+   *
+   * @param opPos position of operation.
+   *
+   * @return The position of the first child of the operation.
+   */
+  public static int getFirstChildPos(int opPos)
+  {
+    return opPos + 2;
+  }
+
+  /**
+   * Get the length of an operation.
+   *
+   * @param opPos The position of the operation in the op map.
+   *
+   * @return The size of the operation.
+   */
+  public int getArgLength(int opPos)
+  {
+    return m_opMap.elementAt(opPos + MAPINDEX_LENGTH);
+  }
+
+  /**
+   * Given a location step, get the length of that step.
+   *
+   * @param opPos Position of location step in op map.
+   *
+   * @return The length of the step.
+   */
+  public int getArgLengthOfStep(int opPos)
+  {
+    return m_opMap.elementAt(opPos + MAPINDEX_LENGTH + 1) - 3;
+  }
+
+  /**
+   * Get the first child position of a given location step.
+   *
+   * @param opPos Position of location step in the location map.
+   *
+   * @return The first child position of the step.
+   */
+  public static int getFirstChildPosOfStep(int opPos)
+  {
+    return opPos + 3;
+  }
+
+  /**
+   * Get the test type of the step, i.e. NODETYPE_XXX value.
+   * 
+   * @param opPosOfStep The position of the FROM_XXX step.
+   *
+   * @return NODETYPE_XXX value.
+   */
+  public int getStepTestType(int opPosOfStep)
+  {
+    return m_opMap.elementAt(opPosOfStep + 3);  // skip past op, len, len without predicates
+  }
+
+  /**
+   * Get the namespace of the step.
+   * 
+   * @param opPosOfStep The position of the FROM_XXX step.
+   *
+   * @return The step's namespace, NodeTest.WILD, or null for null namespace.
+   */
+  public String getStepNS(int opPosOfStep)
+  {
+
+    int argLenOfStep = getArgLengthOfStep(opPosOfStep);
+
+    // System.out.println("getStepNS.argLenOfStep: "+argLenOfStep);
+    if (argLenOfStep == 3)
+    {
+      int index = m_opMap.elementAt(opPosOfStep + 4);
+
+      if (index >= 0)
+        return (String) m_tokenQueue.elementAt(index);
+      else if (OpCodes.ELEMWILDCARD == index)
+        return NodeTest.WILD;
+      else
+        return null;
+    }
+    else
+      return null;
+  }
+
+  /**
+   * Get the local name of the step.
+   * @param opPosOfStep The position of the FROM_XXX step.
+   *
+   * @return OpCodes.EMPTY, OpCodes.ELEMWILDCARD, or the local name.
+   */
+  public String getStepLocalName(int opPosOfStep)
+  {
+
+    int argLenOfStep = getArgLengthOfStep(opPosOfStep);
+
+    // System.out.println("getStepLocalName.argLenOfStep: "+argLenOfStep);
+    int index;
+
+    switch (argLenOfStep)
+    {
+    case 0 :
+      index = OpCodes.EMPTY;
+      break;
+    case 1 :
+      index = OpCodes.ELEMWILDCARD;
+      break;
+    case 2 :
+      index = m_opMap.elementAt(opPosOfStep + 4);
+      break;
+    case 3 :
+      index = m_opMap.elementAt(opPosOfStep + 5);
+      break;
+    default :
+      index = OpCodes.EMPTY;
+      break;  // Should assert error
+    }
+
+    // int index = (argLenOfStep == 3) ? m_opMap[opPosOfStep+5] 
+    //                                  : ((argLenOfStep == 1) ? -3 : -2);
+    if (index >= 0)
+      return (String) m_tokenQueue.elementAt(index).toString();
+    else if (OpCodes.ELEMWILDCARD == index)
+      return NodeTest.WILD;
+    else
+      return null;
+  }
+  
+}
diff --git a/src/main/java/org/apache/xpath/compiler/OpMapVector.java b/src/main/java/org/apache/xpath/compiler/OpMapVector.java
new file mode 100644
index 0000000..a2c53d2
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/OpMapVector.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xpath.compiler;
+
+/**
+ * 
+ * Like IntVector, but used only for the OpMap array.  Length of array
+ * is kept in the m_lengthPos position of the array.  Only the required methods 
+ * are in included here.
+ * @xsl.usage internal
+ */
+public class OpMapVector {
+    
+ /** Size of blocks to allocate          */
+  protected int m_blocksize;
+
+  /** Array of ints          */
+  protected int m_map[]; // IntStack is trying to see this directly
+
+  /** Position where size of array is kept          */
+  protected int m_lengthPos = 0;
+
+  /** Size of array          */
+  protected int m_mapSize;
+  
+    /**
+   * Construct a OpMapVector, using the given block size.
+   *
+   * @param blocksize Size of block to allocate
+   */
+  public OpMapVector(int blocksize, int increaseSize, int lengthPos)
+  {
+
+    m_blocksize = increaseSize;
+    m_mapSize = blocksize;
+    m_lengthPos = lengthPos;
+    m_map = new int[blocksize];
+  }
+  
+  /**
+   * Get the nth element.
+   *
+   * @param i index of object to get
+   *
+   * @return object at given index
+   */
+  public final int elementAt(int i)
+  {
+    return m_map[i];
+  }  
+   
+    /**
+   * Sets the component at the specified index of this vector to be the
+   * specified object. The previous component at that position is discarded.
+   *
+   * The index must be a value greater than or equal to 0 and less
+   * than the current size of the vector.
+   *
+   * @param value object to set
+   * @param index Index of where to set the object
+   */
+  public final void setElementAt(int value, int index)
+  {
+    if (index >= m_mapSize)
+    {
+      int oldSize = m_mapSize;
+      
+      m_mapSize += m_blocksize;
+
+      int newMap[] = new int[m_mapSize];
+
+      System.arraycopy(m_map, 0, newMap, 0, oldSize);
+
+      m_map = newMap;
+    }
+
+    m_map[index] = value;
+  }
+  
+  
+  /*
+   * Reset the array to the supplied size.  No checking is done.
+   * 
+   * @param size The size to trim to.
+   */
+  public final void setToSize(int size) {
+    
+    int newMap[] = new int[size];
+
+    System.arraycopy(m_map, 0, newMap, 0, m_map[m_lengthPos]);
+
+    m_mapSize = size;
+    m_map = newMap;
+    
+  }  
+
+}
diff --git a/src/main/java/org/apache/xpath/compiler/PsuedoNames.java b/src/main/java/org/apache/xpath/compiler/PsuedoNames.java
new file mode 100644
index 0000000..d585c81
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/PsuedoNames.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: PsuedoNames.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.compiler;
+
+/**
+ * This is used to represent names of nodes that may not be named, like a 
+ * comment node.
+ */
+public class PsuedoNames
+{
+
+  /**
+   * Psuedo name for a wild card pattern ('*').
+   */
+  public static final String PSEUDONAME_ANY = "*";
+
+  /**
+   * Psuedo name for the root node.
+   */
+  public static final String PSEUDONAME_ROOT = "/";
+
+  /**
+   * Psuedo name for a text node.
+   */
+  public static final String PSEUDONAME_TEXT = "#text";
+
+  /**
+   * Psuedo name for a comment node.
+   */
+  public static final String PSEUDONAME_COMMENT = "#comment";
+
+  /**
+   * Psuedo name for a processing instruction node.
+   */
+  public static final String PSEUDONAME_PI = "#pi";
+
+  /**
+   * Psuedo name for an unknown type value.
+   */
+  public static final String PSEUDONAME_OTHER = "*";
+}
diff --git a/src/main/java/org/apache/xpath/compiler/XPathDumper.java b/src/main/java/org/apache/xpath/compiler/XPathDumper.java
new file mode 100644
index 0000000..c53e0a5
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/XPathDumper.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathDumper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.compiler;
+
+
+/**
+ * Class for XPath diagnostic functions.
+ */
+public class XPathDumper
+{
+
+  // deleted for the time being.
+}
diff --git a/src/main/java/org/apache/xpath/compiler/XPathParser.java b/src/main/java/org/apache/xpath/compiler/XPathParser.java
new file mode 100644
index 0000000..58067e1
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/XPathParser.java
@@ -0,0 +1,2401 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPathParser.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.compiler;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.XPathProcessorException;
+import org.apache.xpath.domapi.XPathStylesheetDOM3Exception;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XString;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Tokenizes and parses XPath expressions. This should really be named
+ * XPathParserImpl, and may be renamed in the future.
+ * @xsl.usage general
+ */
+public class XPathParser
+{
+	// %REVIEW% Is there a better way of doing this?
+	// Upside is minimum object churn. Downside is that we don't have a useful
+	// backtrace in the exception itself -- but we don't expect to need one.
+	static public final String CONTINUE_AFTER_FATAL_ERROR="CONTINUE_AFTER_FATAL_ERROR";
+
+  /**
+   * The XPath to be processed.
+   */
+  private OpMap m_ops;
+
+  /**
+   * The next token in the pattern.
+   */
+  transient String m_token;
+
+  /**
+   * The first char in m_token, the theory being that this
+   * is an optimization because we won't have to do charAt(0) as
+   * often.
+   */
+  transient char m_tokenChar = 0;
+
+  /**
+   * The position in the token queue is tracked by m_queueMark.
+   */
+  int m_queueMark = 0;
+
+  /**
+   * Results from checking FilterExpr syntax
+   */
+  protected final static int FILTER_MATCH_FAILED     = 0;
+  protected final static int FILTER_MATCH_PRIMARY    = 1;
+  protected final static int FILTER_MATCH_PREDICATES = 2;
+
+  /**
+   * The parser constructor.
+   */
+  public XPathParser(ErrorListener errorListener, javax.xml.transform.SourceLocator sourceLocator)
+  {
+    m_errorListener = errorListener;
+    m_sourceLocator = sourceLocator;
+  }
+
+  /**
+   * The prefix resolver to map prefixes to namespaces in the OpMap.
+   */
+  PrefixResolver m_namespaceContext;
+
+  /**
+   * Given an string, init an XPath object for selections,
+   * in order that a parse doesn't
+   * have to be done each time the expression is evaluated.
+   * 
+   * @param compiler The compiler object.
+   * @param expression A string conforming to the XPath grammar.
+   * @param namespaceContext An object that is able to resolve prefixes in
+   * the XPath to namespaces.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void initXPath(
+          Compiler compiler, String expression, PrefixResolver namespaceContext)
+            throws javax.xml.transform.TransformerException
+  {
+
+    m_ops = compiler;
+    m_namespaceContext = namespaceContext;
+    m_functionTable = compiler.getFunctionTable();
+
+    Lexer lexer = new Lexer(compiler, namespaceContext, this);
+
+    lexer.tokenize(expression);
+
+    m_ops.setOp(0,OpCodes.OP_XPATH);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH,2);
+    
+    
+	// Patch for Christine's gripe. She wants her errorHandler to return from
+	// a fatal error and continue trying to parse, rather than throwing an exception.
+	// Without the patch, that put us into an endless loop.
+	//
+	// %REVIEW% Is there a better way of doing this?
+	// %REVIEW% Are there any other cases which need the safety net?
+	// 	(and if so do we care right now, or should we rewrite the XPath
+	//	grammar engine and can fix it at that time?)
+	try {
+
+      nextToken();
+      Expr();
+
+      if (null != m_token)
+      {
+        String extraTokens = "";
+
+        while (null != m_token)
+        {
+          extraTokens += "'" + m_token + "'";
+
+          nextToken();
+
+          if (null != m_token)
+            extraTokens += ", ";
+        }
+
+        error(XPATHErrorResources.ER_EXTRA_ILLEGAL_TOKENS,
+              new Object[]{ extraTokens });  //"Extra illegal tokens: "+extraTokens);
+      }
+
+    } 
+    catch (org.apache.xpath.XPathProcessorException e)
+    {
+	  if(CONTINUE_AFTER_FATAL_ERROR.equals(e.getMessage()))
+	  {
+		// What I _want_ to do is null out this XPath.
+		// I doubt this has the desired effect, but I'm not sure what else to do.
+		// %REVIEW%!!!
+		initXPath(compiler, "/..",  namespaceContext);
+	  }
+	  else
+		throw e;
+    }
+
+    compiler.shrink();
+  }
+
+  /**
+   * Given an string, init an XPath object for pattern matches,
+   * in order that a parse doesn't
+   * have to be done each time the expression is evaluated.
+   * @param compiler The XPath object to be initialized.
+   * @param expression A String representing the XPath.
+   * @param namespaceContext An object that is able to resolve prefixes in
+   * the XPath to namespaces.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public void initMatchPattern(
+          Compiler compiler, String expression, PrefixResolver namespaceContext)
+            throws javax.xml.transform.TransformerException
+  {
+
+    m_ops = compiler;
+    m_namespaceContext = namespaceContext;
+    m_functionTable = compiler.getFunctionTable();
+
+    Lexer lexer = new Lexer(compiler, namespaceContext, this);
+
+    lexer.tokenize(expression);
+
+    m_ops.setOp(0, OpCodes.OP_MATCHPATTERN);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, 2);
+
+    nextToken();
+    Pattern();
+
+    if (null != m_token)
+    {
+      String extraTokens = "";
+
+      while (null != m_token)
+      {
+        extraTokens += "'" + m_token + "'";
+
+        nextToken();
+
+        if (null != m_token)
+          extraTokens += ", ";
+      }
+
+      error(XPATHErrorResources.ER_EXTRA_ILLEGAL_TOKENS,
+            new Object[]{ extraTokens });  //"Extra illegal tokens: "+extraTokens);
+    }
+
+    // Terminate for safety.
+    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH)+1);
+
+    m_ops.shrink();
+  }
+
+  /** The error listener where syntax errors are to be sent.
+   */
+  private ErrorListener m_errorListener;
+  
+  /** The source location of the XPath. */
+  javax.xml.transform.SourceLocator m_sourceLocator;
+  
+  /** The table contains build-in functions and customized functions */
+  private FunctionTable m_functionTable;
+
+  /**
+   * Allow an application to register an error event handler, where syntax 
+   * errors will be sent.  If the error listener is not set, syntax errors 
+   * will be sent to System.err.
+   * 
+   * @param handler Reference to error listener where syntax errors will be 
+   *                sent.
+   */
+  public void setErrorHandler(ErrorListener handler)
+  {
+    m_errorListener = handler;
+  }
+
+  /**
+   * Return the current error listener.
+   *
+   * @return The error listener, which should not normally be null, but may be.
+   */
+  public ErrorListener getErrorListener()
+  {
+    return m_errorListener;
+  }
+
+  /**
+   * Check whether m_token matches the target string. 
+   *
+   * @param s A string reference or null.
+   *
+   * @return If m_token is null, returns false (or true if s is also null), or 
+   * return true if the current token matches the string, else false.
+   */
+  final boolean tokenIs(String s)
+  {
+    return (m_token != null) ? (m_token.equals(s)) : (s == null);
+  }
+
+  /**
+   * Check whether m_tokenChar==c. 
+   *
+   * @param c A character to be tested.
+   *
+   * @return If m_token is null, returns false, or return true if c matches 
+   *         the current token.
+   */
+  final boolean tokenIs(char c)
+  {
+    return (m_token != null) ? (m_tokenChar == c) : false;
+  }
+
+  /**
+   * Look ahead of the current token in order to
+   * make a branching decision.
+   *
+   * @param c the character to be tested for.
+   * @param n number of tokens to look ahead.  Must be
+   * greater than 1.
+   *
+   * @return true if the next token matches the character argument.
+   */
+  final boolean lookahead(char c, int n)
+  {
+
+    int pos = (m_queueMark + n);
+    boolean b;
+
+    if ((pos <= m_ops.getTokenQueueSize()) && (pos > 0)
+            && (m_ops.getTokenQueueSize() != 0))
+    {
+      String tok = ((String) m_ops.m_tokenQueue.elementAt(pos - 1));
+
+      b = (tok.length() == 1) ? (tok.charAt(0) == c) : false;
+    }
+    else
+    {
+      b = false;
+    }
+
+    return b;
+  }
+
+  /**
+   * Look behind the first character of the current token in order to
+   * make a branching decision.
+   * 
+   * @param c the character to compare it to.
+   * @param n number of tokens to look behind.  Must be
+   * greater than 1.  Note that the look behind terminates
+   * at either the beginning of the string or on a '|'
+   * character.  Because of this, this method should only
+   * be used for pattern matching.
+   *
+   * @return true if the token behind the current token matches the character 
+   *         argument.
+   */
+  private final boolean lookbehind(char c, int n)
+  {
+
+    boolean isToken;
+    int lookBehindPos = m_queueMark - (n + 1);
+
+    if (lookBehindPos >= 0)
+    {
+      String lookbehind = (String) m_ops.m_tokenQueue.elementAt(lookBehindPos);
+
+      if (lookbehind.length() == 1)
+      {
+        char c0 = (lookbehind == null) ? '|' : lookbehind.charAt(0);
+
+        isToken = (c0 == '|') ? false : (c0 == c);
+      }
+      else
+      {
+        isToken = false;
+      }
+    }
+    else
+    {
+      isToken = false;
+    }
+
+    return isToken;
+  }
+
+  /**
+   * look behind the current token in order to
+   * see if there is a useable token.
+   * 
+   * @param n number of tokens to look behind.  Must be
+   * greater than 1.  Note that the look behind terminates
+   * at either the beginning of the string or on a '|'
+   * character.  Because of this, this method should only
+   * be used for pattern matching.
+   * 
+   * @return true if look behind has a token, false otherwise.
+   */
+  private final boolean lookbehindHasToken(int n)
+  {
+
+    boolean hasToken;
+
+    if ((m_queueMark - n) > 0)
+    {
+      String lookbehind = (String) m_ops.m_tokenQueue.elementAt(m_queueMark - (n - 1));
+      char c0 = (lookbehind == null) ? '|' : lookbehind.charAt(0);
+
+      hasToken = (c0 == '|') ? false : true;
+    }
+    else
+    {
+      hasToken = false;
+    }
+
+    return hasToken;
+  }
+
+  /**
+   * Look ahead of the current token in order to
+   * make a branching decision.
+   * 
+   * @param s the string to compare it to.
+   * @param n number of tokens to lookahead.  Must be
+   * greater than 1.
+   *
+   * @return true if the token behind the current token matches the string 
+   *         argument.
+   */
+  private final boolean lookahead(String s, int n)
+  {
+
+    boolean isToken;
+
+    if ((m_queueMark + n) <= m_ops.getTokenQueueSize())
+    {
+      String lookahead = (String) m_ops.m_tokenQueue.elementAt(m_queueMark + (n - 1));
+
+      isToken = (lookahead != null) ? lookahead.equals(s) : (s == null);
+    }
+    else
+    {
+      isToken = (null == s);
+    }
+
+    return isToken;
+  }
+
+  /**
+   * Retrieve the next token from the command and
+   * store it in m_token string.
+   */
+  private final void nextToken()
+  {
+
+    if (m_queueMark < m_ops.getTokenQueueSize())
+    {
+      m_token = (String) m_ops.m_tokenQueue.elementAt(m_queueMark++);
+      m_tokenChar = m_token.charAt(0);
+    }
+    else
+    {
+      m_token = null;
+      m_tokenChar = 0;
+    }
+  }
+
+  /**
+   * Retrieve a token relative to the current token.
+   * 
+   * @param i Position relative to current token.
+   *
+   * @return The string at the given index, or null if the index is out 
+   *         of range.
+   */
+  private final String getTokenRelative(int i)
+  {
+
+    String tok;
+    int relative = m_queueMark + i;
+
+    if ((relative > 0) && (relative < m_ops.getTokenQueueSize()))
+    {
+      tok = (String) m_ops.m_tokenQueue.elementAt(relative);
+    }
+    else
+    {
+      tok = null;
+    }
+
+    return tok;
+  }
+
+  /**
+   * Retrieve the previous token from the command and
+   * store it in m_token string.
+   */
+  private final void prevToken()
+  {
+
+    if (m_queueMark > 0)
+    {
+      m_queueMark--;
+
+      m_token = (String) m_ops.m_tokenQueue.elementAt(m_queueMark);
+      m_tokenChar = m_token.charAt(0);
+    }
+    else
+    {
+      m_token = null;
+      m_tokenChar = 0;
+    }
+  }
+
+  /**
+   * Consume an expected token, throwing an exception if it
+   * isn't there.
+   *
+   * @param expected The string to be expected.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  private final void consumeExpected(String expected)
+          throws javax.xml.transform.TransformerException
+  {
+
+    if (tokenIs(expected))
+    {
+      nextToken();
+    }
+    else
+    {
+      error(XPATHErrorResources.ER_EXPECTED_BUT_FOUND, new Object[]{ expected,
+                                                                     m_token });  //"Expected "+expected+", but found: "+m_token);
+
+	  // Patch for Christina's gripe. She wants her errorHandler to return from
+	  // this error and continue trying to parse, rather than throwing an exception.
+	  // Without the patch, that put us into an endless loop.
+		throw new XPathProcessorException(CONTINUE_AFTER_FATAL_ERROR);
+	}
+  }
+
+  /**
+   * Consume an expected token, throwing an exception if it
+   * isn't there.
+   *
+   * @param expected the character to be expected.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  private final void consumeExpected(char expected)
+          throws javax.xml.transform.TransformerException
+  {
+
+    if (tokenIs(expected))
+    {
+      nextToken();
+    }
+    else
+    {
+      error(XPATHErrorResources.ER_EXPECTED_BUT_FOUND,
+            new Object[]{ String.valueOf(expected),
+                          m_token });  //"Expected "+expected+", but found: "+m_token);
+
+	  // Patch for Christina's gripe. She wants her errorHandler to return from
+	  // this error and continue trying to parse, rather than throwing an exception.
+	  // Without the patch, that put us into an endless loop.
+		throw new XPathProcessorException(CONTINUE_AFTER_FATAL_ERROR);
+    }
+  }
+
+  /**
+   * Warn the user of a problem.
+   *
+   * @param msg An error msgkey that corresponds to one of the constants found 
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which 
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to 
+   *                              throw an exception.
+   */
+  void warn(String msg, Object[] args) throws TransformerException
+  {
+
+    String fmsg = XSLMessages.createXPATHWarning(msg, args);
+    ErrorListener ehandler = this.getErrorListener();
+
+    if (null != ehandler)
+    {
+      // TO DO: Need to get stylesheet Locator from here.
+      ehandler.warning(new TransformerException(fmsg, m_sourceLocator));
+    }
+    else
+    {
+      // Should never happen.
+      System.err.println(fmsg);
+    }
+  }
+
+  /**
+   * Notify the user of an assertion error, and probably throw an
+   * exception.
+   *
+   * @param b  If false, a runtime exception will be thrown.
+   * @param msg The assertion message, which should be informative.
+   * 
+   * @throws RuntimeException if the b argument is false.
+   */
+  private void assertion(boolean b, String msg)
+  {
+
+    if (!b)
+    {
+      String fMsg = XSLMessages.createXPATHMessage(
+        XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
+        new Object[]{ msg });
+
+      throw new RuntimeException(fMsg);
+    }
+  }
+
+  /**
+   * Notify the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg An error msgkey that corresponds to one of the constants found 
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which 
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to 
+   *                              throw an exception.
+   */
+  void error(String msg, Object[] args) throws TransformerException
+  {
+
+    String fmsg = XSLMessages.createXPATHMessage(msg, args);
+    ErrorListener ehandler = this.getErrorListener();
+
+    TransformerException te = new TransformerException(fmsg, m_sourceLocator);
+    if (null != ehandler)
+    {
+      // TO DO: Need to get stylesheet Locator from here.
+      ehandler.fatalError(te);
+    }
+    else
+    {
+      // System.err.println(fmsg);
+      throw te;
+    }
+  }
+
+  /**
+   * This method is added to support DOM 3 XPath API.
+   * <p>
+   * This method is exactly like error(String, Object[]); except that
+   * the underlying TransformerException is 
+   * XpathStylesheetDOM3Exception (which extends TransformerException).
+   * <p>
+   * So older XPath code in Xalan is not affected by this. To older XPath code
+   * the behavior of whether error() or errorForDOM3() is called because it is
+   * always catching TransformerException objects and is oblivious to
+   * the new subclass of XPathStylesheetDOM3Exception. Older XPath code 
+   * runs as before.
+   * <p>
+   * However, newer DOM3 XPath code upon catching a TransformerException can
+   * can check if the exception is an instance of XPathStylesheetDOM3Exception
+   * and take appropriate action.
+   * 
+   * @param msg An error msgkey that corresponds to one of the constants found 
+   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
+   *            a key for a format string.
+   * @param args An array of arguments represented in the format string, which 
+   *             may be null.
+   *
+   * @throws TransformerException if the current ErrorListoner determines to 
+   *                              throw an exception.
+   */
+  void errorForDOM3(String msg, Object[] args) throws TransformerException
+  {
+
+	String fmsg = XSLMessages.createXPATHMessage(msg, args);
+	ErrorListener ehandler = this.getErrorListener();
+
+	TransformerException te = new XPathStylesheetDOM3Exception(fmsg, m_sourceLocator);
+	if (null != ehandler)
+	{
+	  // TO DO: Need to get stylesheet Locator from here.
+	  ehandler.fatalError(te);
+	}
+	else
+	{
+	  // System.err.println(fmsg);
+	  throw te;
+	}
+  }
+  /**
+   * Dump the remaining token queue.
+   * Thanks to Craig for this.
+   *
+   * @return A dump of the remaining token queue, which may be appended to 
+   *         an error message.
+   */
+  protected String dumpRemainingTokenQueue()
+  {
+
+    int q = m_queueMark;
+    String returnMsg;
+
+    if (q < m_ops.getTokenQueueSize())
+    {
+      String msg = "\n Remaining tokens: (";
+
+      while (q < m_ops.getTokenQueueSize())
+      {
+        String t = (String) m_ops.m_tokenQueue.elementAt(q++);
+
+        msg += (" '" + t + "'");
+      }
+
+      returnMsg = msg + ")";
+    }
+    else
+    {
+      returnMsg = "";
+    }
+
+    return returnMsg;
+  }
+
+  /**
+   * Given a string, return the corresponding function token.
+   *
+   * @param key A local name of a function.
+   *
+   * @return   The function ID, which may correspond to one of the FUNC_XXX 
+   *    values found in {@link org.apache.xpath.compiler.FunctionTable}, but may 
+   *    be a value installed by an external module.
+   */
+  final int getFunctionToken(String key)
+  {
+
+    int tok;
+    
+    Object id;
+
+    try
+    {
+      // These are nodetests, xpathparser treats them as functions when parsing
+      // a FilterExpr. 
+      id = Keywords.lookupNodeTest(key);
+      if (null == id) id = m_functionTable.getFunctionID(key);
+      tok = ((Integer) id).intValue();
+    }
+    catch (NullPointerException npe)
+    {
+      tok = -1;
+    }
+    catch (ClassCastException cce)
+    {
+      tok = -1;
+    }
+
+    return tok;
+  }
+
+  /**
+   * Insert room for operation.  This will NOT set
+   * the length value of the operation, but will update
+   * the length value for the total expression.
+   *
+   * @param pos The position where the op is to be inserted.
+   * @param length The length of the operation space in the op map.
+   * @param op The op code to the inserted.
+   */
+  void insertOp(int pos, int length, int op)
+  {
+
+    int totalLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    for (int i = totalLen - 1; i >= pos; i--)
+    {
+      m_ops.setOp(i + length, m_ops.getOp(i));
+    }
+
+    m_ops.setOp(pos,op);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH,totalLen + length);
+  }
+
+  /**
+   * Insert room for operation.  This WILL set
+   * the length value of the operation, and will update
+   * the length value for the total expression.
+   *
+   * @param length The length of the operation.
+   * @param op The op code to the inserted.
+   */
+  void appendOp(int length, int op)
+  {
+
+    int totalLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    m_ops.setOp(totalLen, op);
+    m_ops.setOp(totalLen + OpMap.MAPINDEX_LENGTH, length);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, totalLen + length);
+  }
+
+  // ============= EXPRESSIONS FUNCTIONS =================
+
+  /**
+   *
+   *
+   * Expr  ::=  OrExpr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void Expr() throws javax.xml.transform.TransformerException
+  {
+    OrExpr();
+  }
+
+  /**
+   *
+   *
+   * OrExpr  ::=  AndExpr
+   * | OrExpr 'or' AndExpr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void OrExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    AndExpr();
+
+    if ((null != m_token) && tokenIs("or"))
+    {
+      nextToken();
+      insertOp(opPos, 2, OpCodes.OP_OR);
+      OrExpr();
+
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+        m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+    }
+  }
+
+  /**
+   *
+   *
+   * AndExpr  ::=  EqualityExpr
+   * | AndExpr 'and' EqualityExpr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void AndExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    EqualityExpr(-1);
+
+    if ((null != m_token) && tokenIs("and"))
+    {
+      nextToken();
+      insertOp(opPos, 2, OpCodes.OP_AND);
+      AndExpr();
+
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+        m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+    }
+  }
+
+  /**
+   *
+   * @returns an Object which is either a String, a Number, a Boolean, or a vector
+   * of nodes.
+   *
+   * EqualityExpr  ::=  RelationalExpr
+   * | EqualityExpr '=' RelationalExpr
+   *
+   *
+   * @param addPos Position where expression is to be added, or -1 for append.
+   *
+   * @return the position at the end of the equality expression.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected int EqualityExpr(int addPos) throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    if (-1 == addPos)
+      addPos = opPos;
+
+    RelationalExpr(-1);
+
+    if (null != m_token)
+    {
+      if (tokenIs('!') && lookahead('=', 1))
+      {
+        nextToken();
+        nextToken();
+        insertOp(addPos, 2, OpCodes.OP_NOTEQUALS);
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = EqualityExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+      else if (tokenIs('='))
+      {
+        nextToken();
+        insertOp(addPos, 2, OpCodes.OP_EQUALS);
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = EqualityExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+    }
+
+    return addPos;
+  }
+
+  /**
+   * .
+   * @returns an Object which is either a String, a Number, a Boolean, or a vector
+   * of nodes.
+   *
+   * RelationalExpr  ::=  AdditiveExpr
+   * | RelationalExpr '<' AdditiveExpr
+   * | RelationalExpr '>' AdditiveExpr
+   * | RelationalExpr '<=' AdditiveExpr
+   * | RelationalExpr '>=' AdditiveExpr
+   *
+   *
+   * @param addPos Position where expression is to be added, or -1 for append.
+   *
+   * @return the position at the end of the relational expression.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected int RelationalExpr(int addPos) throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    if (-1 == addPos)
+      addPos = opPos;
+
+    AdditiveExpr(-1);
+
+    if (null != m_token)
+    {
+      if (tokenIs('<'))
+      {
+        nextToken();
+
+        if (tokenIs('='))
+        {
+          nextToken();
+          insertOp(addPos, 2, OpCodes.OP_LTE);
+        }
+        else
+        {
+          insertOp(addPos, 2, OpCodes.OP_LT);
+        }
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = RelationalExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, 
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+      else if (tokenIs('>'))
+      {
+        nextToken();
+
+        if (tokenIs('='))
+        {
+          nextToken();
+          insertOp(addPos, 2, OpCodes.OP_GTE);
+        }
+        else
+        {
+          insertOp(addPos, 2, OpCodes.OP_GT);
+        }
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = RelationalExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+    }
+
+    return addPos;
+  }
+
+  /**
+   * This has to handle construction of the operations so that they are evaluated
+   * in pre-fix order.  So, for 9+7-6, instead of |+|9|-|7|6|, this needs to be
+   * evaluated as |-|+|9|7|6|.
+   *
+   * AdditiveExpr  ::=  MultiplicativeExpr
+   * | AdditiveExpr '+' MultiplicativeExpr
+   * | AdditiveExpr '-' MultiplicativeExpr
+   *
+   *
+   * @param addPos Position where expression is to be added, or -1 for append.
+   *
+   * @return the position at the end of the equality expression.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected int AdditiveExpr(int addPos) throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    if (-1 == addPos)
+      addPos = opPos;
+
+    MultiplicativeExpr(-1);
+
+    if (null != m_token)
+    {
+      if (tokenIs('+'))
+      {
+        nextToken();
+        insertOp(addPos, 2, OpCodes.OP_PLUS);
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = AdditiveExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+      else if (tokenIs('-'))
+      {
+        nextToken();
+        insertOp(addPos, 2, OpCodes.OP_MINUS);
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = AdditiveExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, 
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+    }
+
+    return addPos;
+  }
+
+  /**
+   * This has to handle construction of the operations so that they are evaluated
+   * in pre-fix order.  So, for 9+7-6, instead of |+|9|-|7|6|, this needs to be
+   * evaluated as |-|+|9|7|6|.
+   *
+   * MultiplicativeExpr  ::=  UnaryExpr
+   * | MultiplicativeExpr MultiplyOperator UnaryExpr
+   * | MultiplicativeExpr 'div' UnaryExpr
+   * | MultiplicativeExpr 'mod' UnaryExpr
+   * | MultiplicativeExpr 'quo' UnaryExpr
+   *
+   * @param addPos Position where expression is to be added, or -1 for append.
+   *
+   * @return the position at the end of the equality expression.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected int MultiplicativeExpr(int addPos) throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    if (-1 == addPos)
+      addPos = opPos;
+
+    UnaryExpr();
+
+    if (null != m_token)
+    {
+      if (tokenIs('*'))
+      {
+        nextToken();
+        insertOp(addPos, 2, OpCodes.OP_MULT);
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = MultiplicativeExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+      else if (tokenIs("div"))
+      {
+        nextToken();
+        insertOp(addPos, 2, OpCodes.OP_DIV);
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = MultiplicativeExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+      else if (tokenIs("mod"))
+      {
+        nextToken();
+        insertOp(addPos, 2, OpCodes.OP_MOD);
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = MultiplicativeExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+      else if (tokenIs("quo"))
+      {
+        nextToken();
+        insertOp(addPos, 2, OpCodes.OP_QUO);
+
+        int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
+
+        addPos = MultiplicativeExpr(addPos);
+        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
+        addPos += 2;
+      }
+    }
+
+    return addPos;
+  }
+
+  /**
+   *
+   * UnaryExpr  ::=  UnionExpr
+   * | '-' UnaryExpr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void UnaryExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+    boolean isNeg = false;
+
+    if (m_tokenChar == '-')
+    {
+      nextToken();
+      appendOp(2, OpCodes.OP_NEG);
+
+      isNeg = true;
+    }
+
+    UnionExpr();
+
+    if (isNeg)
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+        m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+  }
+
+  /**
+   *
+   * StringExpr  ::=  Expr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void StringExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    appendOp(2, OpCodes.OP_STRING);
+    Expr();
+
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+  }
+
+  /**
+   *
+   *
+   * StringExpr  ::=  Expr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void BooleanExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    appendOp(2, OpCodes.OP_BOOL);
+    Expr();
+
+    int opLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos;
+
+    if (opLen == 2)
+    {
+      error(XPATHErrorResources.ER_BOOLEAN_ARG_NO_LONGER_OPTIONAL, null);  //"boolean(...) argument is no longer optional with 19990709 XPath draft.");
+    }
+
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, opLen);
+  }
+
+  /**
+   *
+   *
+   * NumberExpr  ::=  Expr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void NumberExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    appendOp(2, OpCodes.OP_NUMBER);
+    Expr();
+
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+  }
+
+  /**
+   * The context of the right hand side expressions is the context of the
+   * left hand side expression. The results of the right hand side expressions
+   * are node sets. The result of the left hand side UnionExpr is the union
+   * of the results of the right hand side expressions.
+   *
+   *
+   * UnionExpr    ::=    PathExpr
+   * | UnionExpr '|' PathExpr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void UnionExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+    boolean continueOrLoop = true;
+    boolean foundUnion = false;
+
+    do
+    {
+      PathExpr();
+
+      if (tokenIs('|'))
+      {
+        if (false == foundUnion)
+        {
+          foundUnion = true;
+
+          insertOp(opPos, 2, OpCodes.OP_UNION);
+        }
+
+        nextToken();
+      }
+      else
+      {
+        break;
+      }
+
+      // this.m_testForDocOrder = true;
+    }
+    while (continueOrLoop);
+
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+  }
+
+  /**
+   * PathExpr  ::=  LocationPath
+   * | FilterExpr
+   * | FilterExpr '/' RelativeLocationPath
+   * | FilterExpr '//' RelativeLocationPath
+   *
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void PathExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    int filterExprMatch = FilterExpr();
+
+    if (filterExprMatch != FILTER_MATCH_FAILED)
+    {
+      // If FilterExpr had Predicates, a OP_LOCATIONPATH opcode would already
+      // have been inserted.
+      boolean locationPathStarted = (filterExprMatch==FILTER_MATCH_PREDICATES);
+
+      if (tokenIs('/'))
+      {
+        nextToken();
+
+        if (!locationPathStarted)
+        {
+          // int locationPathOpPos = opPos;
+          insertOp(opPos, 2, OpCodes.OP_LOCATIONPATH);
+
+          locationPathStarted = true;
+        }
+
+        if (!RelativeLocationPath())
+        {
+          // "Relative location path expected following '/' or '//'"
+          error(XPATHErrorResources.ER_EXPECTED_REL_LOC_PATH, null);
+        }
+
+      }
+
+      // Terminate for safety.
+      if (locationPathStarted)
+      {
+        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
+        m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+        m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+      }
+    }
+    else
+    {
+      LocationPath();
+    }
+  }
+
+  /**
+   *
+   *
+   * FilterExpr  ::=  PrimaryExpr
+   * | FilterExpr Predicate
+   *
+   * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
+   * the error condition is severe enough to halt processing.
+   *
+   * @return  FILTER_MATCH_PREDICATES, if this method successfully matched a
+   *          FilterExpr with one or more Predicates;
+   *          FILTER_MATCH_PRIMARY, if this method successfully matched a
+   *          FilterExpr that was just a PrimaryExpr; or
+   *          FILTER_MATCH_FAILED, if this method did not match a FilterExpr
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected int FilterExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    int filterMatch;
+
+    if (PrimaryExpr())
+    {
+      if (tokenIs('['))
+      {
+
+        // int locationPathOpPos = opPos;
+        insertOp(opPos, 2, OpCodes.OP_LOCATIONPATH);
+
+        while (tokenIs('['))
+        {
+          Predicate();
+        }
+
+        filterMatch = FILTER_MATCH_PREDICATES;
+      }
+      else
+      {
+        filterMatch = FILTER_MATCH_PRIMARY;
+      }
+    }
+    else
+    {
+      filterMatch = FILTER_MATCH_FAILED;
+    }
+
+    return filterMatch;
+
+    /*
+     * if(tokenIs('['))
+     * {
+     *   Predicate();
+     *   m_ops.m_opMap[opPos + OpMap.MAPINDEX_LENGTH] = m_ops.m_opMap[OpMap.MAPINDEX_LENGTH] - opPos;
+     * }
+     */
+  }
+
+  /**
+   *
+   * PrimaryExpr  ::=  VariableReference
+   * | '(' Expr ')'
+   * | Literal
+   * | Number
+   * | FunctionCall
+   *
+   * @return true if this method successfully matched a PrimaryExpr
+   *
+   * @throws javax.xml.transform.TransformerException
+   *
+   */
+  protected boolean PrimaryExpr() throws javax.xml.transform.TransformerException
+  {
+
+    boolean matchFound;
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    if ((m_tokenChar == '\'') || (m_tokenChar == '"'))
+    {
+      appendOp(2, OpCodes.OP_LITERAL);
+      Literal();
+
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, 
+        m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+      matchFound = true;
+    }
+    else if (m_tokenChar == '$')
+    {
+      nextToken();  // consume '$'
+      appendOp(2, OpCodes.OP_VARIABLE);
+      QName();
+      
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+        m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+      matchFound = true;
+    }
+    else if (m_tokenChar == '(')
+    {
+      nextToken();
+      appendOp(2, OpCodes.OP_GROUP);
+      Expr();
+      consumeExpected(')');
+
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+        m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+      matchFound = true;
+    }
+    else if ((null != m_token) && ((('.' == m_tokenChar) && (m_token.length() > 1) && Character.isDigit(
+            m_token.charAt(1))) || Character.isDigit(m_tokenChar)))
+    {
+      appendOp(2, OpCodes.OP_NUMBERLIT);
+      Number();
+
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+        m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+      matchFound = true;
+    }
+    else if (lookahead('(', 1) || (lookahead(':', 1) && lookahead('(', 3)))
+    {
+      matchFound = FunctionCall();
+    }
+    else
+    {
+      matchFound = false;
+    }
+
+    return matchFound;
+  }
+
+  /**
+   *
+   * Argument    ::=    Expr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void Argument() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    appendOp(2, OpCodes.OP_ARGUMENT);
+    Expr();
+
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+  }
+
+  /**
+   *
+   * FunctionCall    ::=    FunctionName '(' ( Argument ( ',' Argument)*)? ')'
+   *
+   * @return true if, and only if, a FunctionCall was matched
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected boolean FunctionCall() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    if (lookahead(':', 1))
+    {
+      appendOp(4, OpCodes.OP_EXTFUNCTION);
+
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, m_queueMark - 1);
+
+      nextToken();
+      consumeExpected(':');
+
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 2, m_queueMark - 1);
+
+      nextToken();
+    }
+    else
+    {
+      int funcTok = getFunctionToken(m_token);
+
+      if (-1 == funcTok)
+      {
+        error(XPATHErrorResources.ER_COULDNOT_FIND_FUNCTION,
+              new Object[]{ m_token });  //"Could not find function: "+m_token+"()");
+      }
+
+      switch (funcTok)
+      {
+      case OpCodes.NODETYPE_PI :
+      case OpCodes.NODETYPE_COMMENT :
+      case OpCodes.NODETYPE_TEXT :
+      case OpCodes.NODETYPE_NODE :
+        // Node type tests look like function calls, but they're not
+        return false;
+      default :
+        appendOp(3, OpCodes.OP_FUNCTION);
+
+        m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, funcTok);
+      }
+
+      nextToken();
+    }
+
+    consumeExpected('(');
+
+    while (!tokenIs(')') && m_token != null)
+    {
+      if (tokenIs(','))
+      {
+        error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG, null);  //"Found ',' but no preceding argument!");
+      }
+
+      Argument();
+
+      if (!tokenIs(')'))
+      {
+        consumeExpected(',');
+
+        if (tokenIs(')'))
+        {
+          error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_FOLLOWING_ARG,
+                null);  //"Found ',' but no following argument!");
+        }
+      }
+    }
+
+    consumeExpected(')');
+
+    // Terminate for safety.
+    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH,m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, 
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+    return true;
+  }
+
+  // ============= GRAMMAR FUNCTIONS =================
+
+  /**
+   *
+   * LocationPath ::= RelativeLocationPath
+   * | AbsoluteLocationPath
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void LocationPath() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    // int locationPathOpPos = opPos;
+    appendOp(2, OpCodes.OP_LOCATIONPATH);
+
+    boolean seenSlash = tokenIs('/');
+
+    if (seenSlash)
+    {
+      appendOp(4, OpCodes.FROM_ROOT);
+
+      // Tell how long the step is without the predicate
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_ROOT);
+
+      nextToken();
+    } else if (m_token == null) {
+      error(XPATHErrorResources.ER_EXPECTED_LOC_PATH_AT_END_EXPR, null);
+    }
+
+    if (m_token != null)
+    {
+      if (!RelativeLocationPath() && !seenSlash)
+      {
+        // Neither a '/' nor a RelativeLocationPath - i.e., matched nothing
+        // "Location path expected, but found "+m_token+" was encountered."
+        error(XPATHErrorResources.ER_EXPECTED_LOC_PATH, 
+              new Object [] {m_token});
+      }
+    }
+
+    // Terminate for safety.
+    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH,m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+  }
+
+  /**
+   *
+   * RelativeLocationPath ::= Step
+   * | RelativeLocationPath '/' Step
+   * | AbbreviatedRelativeLocationPath
+   *
+   * @returns true if, and only if, a RelativeLocationPath was matched
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected boolean RelativeLocationPath()
+               throws javax.xml.transform.TransformerException
+  {
+    if (!Step())
+    {
+      return false;
+    }
+
+    while (tokenIs('/'))
+    {
+      nextToken();
+
+      if (!Step())
+      {
+        // RelativeLocationPath can't end with a trailing '/'
+        // "Location step expected following '/' or '//'"
+        error(XPATHErrorResources.ER_EXPECTED_LOC_STEP, null);
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   *
+   * Step    ::=    Basis Predicate
+   * | AbbreviatedStep
+   *
+   * @returns false if step was empty (or only a '/'); true, otherwise
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected boolean Step() throws javax.xml.transform.TransformerException
+  {
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    boolean doubleSlash = tokenIs('/');
+
+    // At most a single '/' before each Step is consumed by caller; if the
+    // first thing is a '/', that means we had '//' and the Step must not
+    // be empty.
+    if (doubleSlash)
+    {
+      nextToken();
+
+      appendOp(2, OpCodes.FROM_DESCENDANTS_OR_SELF);
+
+      // Have to fix up for patterns such as '//@foo' or '//attribute::foo',
+      // which translate to 'descendant-or-self::node()/attribute::foo'.
+      // notice I leave the '/' on the queue, so the next will be processed
+      // by a regular step pattern.
+
+      // Make room for telling how long the step is without the predicate
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH,m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.NODETYPE_NODE);
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH,m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+      // Tell how long the step is without the predicate
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
+          m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+      // Tell how long the step is with the predicate
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+          m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+      opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+    }
+
+    if (tokenIs("."))
+    {
+      nextToken();
+
+      if (tokenIs('['))
+      {
+        error(XPATHErrorResources.ER_PREDICATE_ILLEGAL_SYNTAX, null);  //"'..[predicate]' or '.[predicate]' is illegal syntax.  Use 'self::node()[predicate]' instead.");
+      }
+
+      appendOp(4, OpCodes.FROM_SELF);
+
+      // Tell how long the step is without the predicate
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2,4);
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_NODE);
+    }
+    else if (tokenIs(".."))
+    {
+      nextToken();
+      appendOp(4, OpCodes.FROM_PARENT);
+
+      // Tell how long the step is without the predicate
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2,4);
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_NODE);
+    }
+
+    // There is probably a better way to test for this 
+    // transition... but it gets real hairy if you try 
+    // to do it in basis().
+    else if (tokenIs('*') || tokenIs('@') || tokenIs('_')
+             || (m_token!= null && Character.isLetter(m_token.charAt(0))))
+    {
+      Basis();
+
+      while (tokenIs('['))
+      {
+        Predicate();
+      }
+
+      // Tell how long the entire step is.
+      m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+        m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos); 
+    }
+    else
+    {
+      // No Step matched - that's an error if previous thing was a '//'
+      if (doubleSlash)
+      {
+        // "Location step expected following '/' or '//'"
+        error(XPATHErrorResources.ER_EXPECTED_LOC_STEP, null);
+      }
+
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   *
+   * Basis    ::=    AxisName '::' NodeTest
+   * | AbbreviatedBasis
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void Basis() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+    int axesType;
+
+    // The next blocks guarantee that a FROM_XXX will be added.
+    if (lookahead("::", 1))
+    {
+      axesType = AxisName();
+
+      nextToken();
+      nextToken();
+    }
+    else if (tokenIs('@'))
+    {
+      axesType = OpCodes.FROM_ATTRIBUTES;
+
+      appendOp(2, axesType);
+      nextToken();
+    }
+    else
+    {
+      axesType = OpCodes.FROM_CHILDREN;
+
+      appendOp(2, axesType);
+    }
+
+    // Make room for telling how long the step is without the predicate
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+    NodeTest(axesType);
+
+    // Tell how long the step is without the predicate
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+   }
+
+  /**
+   *
+   * Basis    ::=    AxisName '::' NodeTest
+   * | AbbreviatedBasis
+   *
+   * @return FROM_XXX axes type, found in {@link org.apache.xpath.compiler.Keywords}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected int AxisName() throws javax.xml.transform.TransformerException
+  {
+
+    Object val = Keywords.getAxisName(m_token);
+
+    if (null == val)
+    {
+      error(XPATHErrorResources.ER_ILLEGAL_AXIS_NAME,
+            new Object[]{ m_token });  //"illegal axis name: "+m_token);
+    }
+
+    int axesType = ((Integer) val).intValue();
+
+    appendOp(2, axesType);
+
+    return axesType;
+  }
+
+  /**
+   *
+   * NodeTest    ::=    WildcardName
+   * | NodeType '(' ')'
+   * | 'processing-instruction' '(' Literal ')'
+   *
+   * @param axesType FROM_XXX axes type, found in {@link org.apache.xpath.compiler.Keywords}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void NodeTest(int axesType) throws javax.xml.transform.TransformerException
+  {
+
+    if (lookahead('(', 1))
+    {
+      Object nodeTestOp = Keywords.getNodeType(m_token);
+
+      if (null == nodeTestOp)
+      {
+        error(XPATHErrorResources.ER_UNKNOWN_NODETYPE,
+              new Object[]{ m_token });  //"Unknown nodetype: "+m_token);
+      }
+      else
+      {
+        nextToken();
+
+        int nt = ((Integer) nodeTestOp).intValue();
+
+        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), nt);
+        m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+        consumeExpected('(');
+
+        if (OpCodes.NODETYPE_PI == nt)
+        {
+          if (!tokenIs(')'))
+          {
+            Literal();
+          }
+        }
+
+        consumeExpected(')');
+      }
+    }
+    else
+    {
+
+      // Assume name of attribute or element.
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.NODENAME);
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+      if (lookahead(':', 1))
+      {
+        if (tokenIs('*'))
+        {
+          m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ELEMWILDCARD);
+        }
+        else
+        {
+          m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
+
+          // Minimalist check for an NCName - just check first character
+          // to distinguish from other possible tokens
+          if (!Character.isLetter(m_tokenChar) && !tokenIs('_'))
+          {
+            // "Node test that matches either NCName:* or QName was expected."
+            error(XPATHErrorResources.ER_EXPECTED_NODE_TEST, null);
+          }
+        }
+
+        nextToken();
+        consumeExpected(':');
+      }
+      else
+      {
+        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.EMPTY);
+      }
+
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+      if (tokenIs('*'))
+      {
+        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ELEMWILDCARD);
+      }
+      else
+      {
+        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
+
+        // Minimalist check for an NCName - just check first character
+        // to distinguish from other possible tokens
+        if (!Character.isLetter(m_tokenChar) && !tokenIs('_'))
+        {
+          // "Node test that matches either NCName:* or QName was expected."
+          error(XPATHErrorResources.ER_EXPECTED_NODE_TEST, null);
+        }
+      }
+
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+      nextToken();
+    }
+  }
+
+  /**
+   *
+   * Predicate ::= '[' PredicateExpr ']'
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void Predicate() throws javax.xml.transform.TransformerException
+  {
+
+    if (tokenIs('['))
+    {
+      nextToken();
+      PredicateExpr();
+      consumeExpected(']');
+    }
+  }
+
+  /**
+   *
+   * PredicateExpr ::= Expr
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void PredicateExpr() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    appendOp(2, OpCodes.OP_PREDICATE);
+    Expr();
+
+    // Terminate for safety.
+    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+  }
+
+  /**
+   * QName ::=  (Prefix ':')? LocalPart
+   * Prefix ::=  NCName
+   * LocalPart ::=  NCName
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void QName() throws javax.xml.transform.TransformerException
+  {
+    // Namespace
+    if(lookahead(':', 1))
+    {
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+      nextToken();
+      consumeExpected(':');
+    }
+    else
+    {
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.EMPTY);
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+    }
+    
+    // Local name
+    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+    nextToken();
+  }
+
+  /**
+   * NCName ::=  (Letter | '_') (NCNameChar)
+   * NCNameChar ::=  Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
+   */
+  protected void NCName()
+  {
+
+    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+    nextToken();
+  }
+
+  /**
+   * The value of the Literal is the sequence of characters inside
+   * the " or ' characters>.
+   *
+   * Literal  ::=  '"' [^"]* '"'
+   * | "'" [^']* "'"
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void Literal() throws javax.xml.transform.TransformerException
+  {
+
+    int last = m_token.length() - 1;
+    char c0 = m_tokenChar;
+    char cX = m_token.charAt(last);
+
+    if (((c0 == '\"') && (cX == '\"')) || ((c0 == '\'') && (cX == '\'')))
+    {
+
+      // Mutate the token to remove the quotes and have the XString object
+      // already made.
+      int tokenQueuePos = m_queueMark - 1;
+
+      m_ops.m_tokenQueue.setElementAt(null,tokenQueuePos);
+
+      Object obj = new XString(m_token.substring(1, last));
+
+      m_ops.m_tokenQueue.setElementAt(obj,tokenQueuePos);
+
+      // lit = m_token.substring(1, last);
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), tokenQueuePos);
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+      nextToken();
+    }
+    else
+    {
+      error(XPATHErrorResources.ER_PATTERN_LITERAL_NEEDS_BE_QUOTED,
+            new Object[]{ m_token });  //"Pattern literal ("+m_token+") needs to be quoted!");
+    }
+  }
+
+  /**
+   *
+   * Number ::= [0-9]+('.'[0-9]+)? | '.'[0-9]+
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void Number() throws javax.xml.transform.TransformerException
+  {
+
+    if (null != m_token)
+    {
+
+      // Mutate the token to remove the quotes and have the XNumber object
+      // already made.
+      double num;
+
+      try
+      {
+      	// XPath 1.0 does not support number in exp notation
+      	if ((m_token.indexOf('e') > -1)||(m_token.indexOf('E') > -1))
+      		throw new NumberFormatException();
+        num = Double.valueOf(m_token).doubleValue();
+      }
+      catch (NumberFormatException nfe)
+      {
+        num = 0.0;  // to shut up compiler.
+
+        error(XPATHErrorResources.ER_COULDNOT_BE_FORMATTED_TO_NUMBER,
+              new Object[]{ m_token });  //m_token+" could not be formatted to a number!");
+      }
+
+      m_ops.m_tokenQueue.setElementAt(new XNumber(num),m_queueMark - 1);
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
+      m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+      nextToken();
+    }
+  }
+
+  // ============= PATTERN FUNCTIONS =================
+
+  /**
+   *
+   * Pattern  ::=  LocationPathPattern
+   * | Pattern '|' LocationPathPattern
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void Pattern() throws javax.xml.transform.TransformerException
+  {
+
+    while (true)
+    {
+      LocationPathPattern();
+
+      if (tokenIs('|'))
+      {
+        nextToken();
+      }
+      else
+      {
+        break;
+      }
+    }
+  }
+
+  /**
+   *
+   *
+   * LocationPathPattern  ::=  '/' RelativePathPattern?
+   * | IdKeyPattern (('/' | '//') RelativePathPattern)?
+   * | '//'? RelativePathPattern
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void LocationPathPattern() throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+
+    final int RELATIVE_PATH_NOT_PERMITTED = 0;
+    final int RELATIVE_PATH_PERMITTED     = 1;
+    final int RELATIVE_PATH_REQUIRED      = 2;
+
+    int relativePathStatus = RELATIVE_PATH_NOT_PERMITTED;
+
+    appendOp(2, OpCodes.OP_LOCATIONPATHPATTERN);
+
+    if (lookahead('(', 1)
+            && (tokenIs(Keywords.FUNC_ID_STRING)
+                || tokenIs(Keywords.FUNC_KEY_STRING)))
+    {
+      IdKeyPattern();
+
+      if (tokenIs('/'))
+      {
+        nextToken();
+
+        if (tokenIs('/'))
+        {
+          appendOp(4, OpCodes.MATCH_ANY_ANCESTOR);
+
+          nextToken();
+        }
+        else
+        {
+          appendOp(4, OpCodes.MATCH_IMMEDIATE_ANCESTOR);
+        }
+
+        // Tell how long the step is without the predicate
+        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
+        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_FUNCTEST);
+
+        relativePathStatus = RELATIVE_PATH_REQUIRED;
+      }
+    }
+    else if (tokenIs('/'))
+    {
+      if (lookahead('/', 1))
+      {
+        appendOp(4, OpCodes.MATCH_ANY_ANCESTOR);
+        
+        // Added this to fix bug reported by Myriam for match="//x/a"
+        // patterns.  If you don't do this, the 'x' step will think it's part
+        // of a '//' pattern, and so will cause 'a' to be matched when it has
+        // any ancestor that is 'x'.
+        nextToken();
+
+        relativePathStatus = RELATIVE_PATH_REQUIRED;
+      }
+      else
+      {
+        appendOp(4, OpCodes.FROM_ROOT);
+
+        relativePathStatus = RELATIVE_PATH_PERMITTED;
+      }
+
+
+      // Tell how long the step is without the predicate
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
+      m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_ROOT);
+
+      nextToken();
+    }
+    else
+    {
+      relativePathStatus = RELATIVE_PATH_REQUIRED;
+    }
+
+    if (relativePathStatus != RELATIVE_PATH_NOT_PERMITTED)
+    {
+      if (!tokenIs('|') && (null != m_token))
+      {
+        RelativePathPattern();
+      }
+      else if (relativePathStatus == RELATIVE_PATH_REQUIRED)
+      {
+        // "A relative path pattern was expected."
+        error(XPATHErrorResources.ER_EXPECTED_REL_PATH_PATTERN, null);
+      }
+    }
+
+    // Terminate for safety.
+    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+  }
+
+  /**
+   *
+   * IdKeyPattern  ::=  'id' '(' Literal ')'
+   * | 'key' '(' Literal ',' Literal ')'
+   * (Also handle doc())
+   *
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void IdKeyPattern() throws javax.xml.transform.TransformerException
+  {
+    FunctionCall();
+  }
+
+  /**
+   *
+   * RelativePathPattern  ::=  StepPattern
+   * | RelativePathPattern '/' StepPattern
+   * | RelativePathPattern '//' StepPattern
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void RelativePathPattern()
+              throws javax.xml.transform.TransformerException
+  {
+
+    // Caller will have consumed any '/' or '//' preceding the
+    // RelativePathPattern, so let StepPattern know it can't begin with a '/'
+    boolean trailingSlashConsumed = StepPattern(false);
+
+    while (tokenIs('/'))
+    {
+      nextToken();
+
+      // StepPattern() may consume first slash of pair in "a//b" while
+      // processing StepPattern "a".  On next iteration, let StepPattern know
+      // that happened, so it doesn't match ill-formed patterns like "a///b".
+      trailingSlashConsumed = StepPattern(!trailingSlashConsumed);
+    }
+  }
+
+  /**
+   *
+   * StepPattern  ::=  AbbreviatedNodeTestStep
+   *
+   * @param isLeadingSlashPermitted a boolean indicating whether a slash can
+   *        appear at the start of this step
+   *
+   * @return boolean indicating whether a slash following the step was consumed
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected boolean StepPattern(boolean isLeadingSlashPermitted)
+            throws javax.xml.transform.TransformerException
+  {
+    return AbbreviatedNodeTestStep(isLeadingSlashPermitted);
+  }
+
+  /**
+   *
+   * AbbreviatedNodeTestStep    ::=    '@'? NodeTest Predicate
+   *
+   * @param isLeadingSlashPermitted a boolean indicating whether a slash can
+   *        appear at the start of this step
+   *
+   * @return boolean indicating whether a slash following the step was consumed
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected boolean AbbreviatedNodeTestStep(boolean isLeadingSlashPermitted)
+            throws javax.xml.transform.TransformerException
+  {
+
+    int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+    int axesType;
+
+    // The next blocks guarantee that a MATCH_XXX will be added.
+    int matchTypePos = -1;
+
+    if (tokenIs('@'))
+    {
+      axesType = OpCodes.MATCH_ATTRIBUTE;
+
+      appendOp(2, axesType);
+      nextToken();
+    }
+    else if (this.lookahead("::", 1))
+    {
+      if (tokenIs("attribute"))
+      {
+        axesType = OpCodes.MATCH_ATTRIBUTE;
+
+        appendOp(2, axesType);
+      }
+      else if (tokenIs("child"))
+      {
+        matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+        axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR;
+
+        appendOp(2, axesType);
+      }
+      else
+      {
+        axesType = -1;
+
+        this.error(XPATHErrorResources.ER_AXES_NOT_ALLOWED,
+                   new Object[]{ this.m_token });
+      }
+
+      nextToken();
+      nextToken();
+    }
+    else if (tokenIs('/'))
+    {
+      if (!isLeadingSlashPermitted)
+      {
+        // "A step was expected in the pattern, but '/' was encountered."
+        error(XPATHErrorResources.ER_EXPECTED_STEP_PATTERN, null);
+      }
+      axesType = OpCodes.MATCH_ANY_ANCESTOR;
+
+      appendOp(2, axesType);
+      nextToken();
+    }
+    else
+    {
+      matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+      axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR;
+
+      appendOp(2, axesType);
+    }
+
+    // Make room for telling how long the step is without the predicate
+    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+
+    NodeTest(axesType);
+
+    // Tell how long the step is without the predicate
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+    while (tokenIs('['))
+    {
+      Predicate();
+    }
+
+    boolean trailingSlashConsumed;
+
+    // For "a//b", where "a" is current step, we need to mark operation of
+    // current step as "MATCH_ANY_ANCESTOR".  Then we'll consume the first
+    // slash and subsequent step will be treated as a MATCH_IMMEDIATE_ANCESTOR
+    // (unless it too is followed by '//'.)
+    //
+    // %REVIEW%  Following is what happens today, but I'm not sure that's
+    // %REVIEW%  correct behaviour.  Perhaps no valid case could be constructed
+    // %REVIEW%  where it would matter?
+    //
+    // If current step is on the attribute axis (e.g., "@x//b"), we won't
+    // change the current step, and let following step be marked as
+    // MATCH_ANY_ANCESTOR on next call instead.
+    if ((matchTypePos > -1) && tokenIs('/') && lookahead('/', 1))
+    {
+      m_ops.setOp(matchTypePos, OpCodes.MATCH_ANY_ANCESTOR);
+
+      nextToken();
+
+      trailingSlashConsumed = true;
+    }
+    else
+    {
+      trailingSlashConsumed = false;
+    }
+
+    // Tell how long the entire step is.
+    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+
+    return trailingSlashConsumed;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/compiler/package.html b/src/main/java/org/apache/xpath/compiler/package.html
new file mode 100644
index 0000000..6f45248
--- /dev/null
+++ b/src/main/java/org/apache/xpath/compiler/package.html
@@ -0,0 +1,26 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<html>
+  <title>XPath parsing and compilation support Package.</title>
+  <body>
+    <p>Implements an XPath parser which produces an OpMap, and a so-called Compiler
+    which produces an expression tree for fast evaluation.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xpath/domapi/XPathStylesheetDOM3Exception.java b/src/main/java/org/apache/xpath/domapi/XPathStylesheetDOM3Exception.java
new file mode 100644
index 0000000..b63eb85
--- /dev/null
+++ b/src/main/java/org/apache/xpath/domapi/XPathStylesheetDOM3Exception.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2002 World Wide Web Consortium,
+ * (Massachusetts Institute of Technology, Institut National de
+ * Recherche en Informatique et en Automatique, Keio University). All
+ * Rights Reserved. This program is distributed under the W3C's Software
+ * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details.
+ */
+
+package org.apache.xpath.domapi;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+/**
+ *
+ * A new exception to add support for DOM Level 3 XPath API.
+ * This class is needed to throw a org.w3c.dom.DOMException with proper error code in
+ * createExpression method of XPathEvaluatorImpl (a DOM Level 3 class).
+ * 
+ * This class extends TransformerException because the error message includes information
+ * about where the XPath problem is in the stylesheet as well as the XPath expression itself.
+ * 
+ * @xsl.usage internal
+ */
+final public class XPathStylesheetDOM3Exception extends TransformerException {
+	public XPathStylesheetDOM3Exception(String msg, SourceLocator arg1)
+	{
+		super(msg, arg1);
+	}
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncBoolean.java b/src/main/java/org/apache/xpath/functions/FuncBoolean.java
new file mode 100644
index 0000000..36eb203
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncBoolean.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncBoolean.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Boolean() function.
+ * @xsl.usage advanced
+ */
+public class FuncBoolean extends FunctionOneArg
+{
+    static final long serialVersionUID = 4328660760070034592L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return m_arg0.execute(xctxt).bool() ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+  
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncCeiling.java b/src/main/java/org/apache/xpath/functions/FuncCeiling.java
new file mode 100644
index 0000000..f496d25
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncCeiling.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncCeiling.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Ceiling() function.
+ * @xsl.usage advanced
+ */
+public class FuncCeiling extends FunctionOneArg
+{
+    static final long serialVersionUID = -1275988936390464739L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(Math.ceil(m_arg0.execute(xctxt).num()));
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncConcat.java b/src/main/java/org/apache/xpath/functions/FuncConcat.java
new file mode 100644
index 0000000..570f585
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncConcat.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncConcat.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the Concat() function.
+ * @xsl.usage advanced
+ */
+public class FuncConcat extends FunctionMultiArgs
+{
+    static final long serialVersionUID = 1737228885202314413L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    StringBuffer sb = new StringBuffer();
+
+    // Compiler says we must have at least two arguments.
+    sb.append(m_arg0.execute(xctxt).str());
+    sb.append(m_arg1.execute(xctxt).str());
+
+    if (null != m_arg2)
+      sb.append(m_arg2.execute(xctxt).str());
+
+    if (null != m_args)
+    {
+      for (int i = 0; i < m_args.length; i++)
+      {
+        sb.append(m_args[i].execute(xctxt).str());
+      }
+    }
+
+    return new XString(sb.toString());
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   *
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if (argNum < 2)
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage("gtone", null));
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncContains.java b/src/main/java/org/apache/xpath/functions/FuncContains.java
new file mode 100644
index 0000000..0c6849f
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncContains.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncContains.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Contains() function.
+ * @xsl.usage advanced
+ */
+public class FuncContains extends Function2Args
+{
+    static final long serialVersionUID = 5084753781887919723L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    String s1 = m_arg0.execute(xctxt).str();
+    String s2 = m_arg1.execute(xctxt).str();
+
+    // Add this check for JDK consistency for empty strings.
+    if (s1.length() == 0 && s2.length() == 0)
+      return XBoolean.S_TRUE;
+
+    int index = s1.indexOf(s2);
+
+    return (index > -1) ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncCount.java b/src/main/java/org/apache/xpath/functions/FuncCount.java
new file mode 100644
index 0000000..71e7ba8
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncCount.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncCount.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Count() function.
+ * @xsl.usage advanced
+ */
+public class FuncCount extends FunctionOneArg
+{
+    static final long serialVersionUID = -7116225100474153751L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+//    DTMIterator nl = m_arg0.asIterator(xctxt, xctxt.getCurrentNode());
+
+//    // We should probably make a function on the iterator for this, 
+//    // as a given implementation could optimize.
+//    int i = 0;
+//
+//    while (DTM.NULL != nl.nextNode())
+//    {
+//      i++;
+//    }
+//    nl.detach();
+	DTMIterator nl = m_arg0.asIterator(xctxt, xctxt.getCurrentNode());
+	int i = nl.getLength();	
+	nl.detach();
+
+    return new XNumber((double) i);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncCurrent.java b/src/main/java/org/apache/xpath/functions/FuncCurrent.java
new file mode 100644
index 0000000..764cc0d
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncCurrent.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncCurrent.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.PredicatedNodeTest;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.axes.SubContextList;
+import org.apache.xpath.patterns.StepPattern;
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.res.XSLTErrorResources;
+
+
+/**
+ * Execute the current() function.
+ * @xsl.usage advanced
+ */
+public class FuncCurrent extends Function
+{
+    static final long serialVersionUID = 5715316804877715008L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+   
+    SubContextList subContextList = xctxt.getCurrentNodeList();
+    int currentNode = DTM.NULL;
+
+    if (null != subContextList) {
+        if (subContextList instanceof PredicatedNodeTest) {
+            LocPathIterator iter = ((PredicatedNodeTest)subContextList)
+                                                          .getLocPathIterator();
+            currentNode = iter.getCurrentContextNode();
+         } else if(subContextList instanceof StepPattern) {
+           throw new RuntimeException(XSLMessages.createMessage(
+              XSLTErrorResources.ER_PROCESSOR_ERROR,null));
+         }
+    } else {
+        // not predicate => ContextNode == CurrentNode
+        currentNode = xctxt.getContextNode();
+    }
+    return new XNodeSet(currentNode, xctxt.getDTMManager());
+  }
+  
+  /**
+   * No arguments to process, so this does nothing.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    // no-op
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncDoclocation.java b/src/main/java/org/apache/xpath/functions/FuncDoclocation.java
new file mode 100644
index 0000000..50d0b15
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncDoclocation.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncDoclocation.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the proprietary document-location() function, which returns
+ * a node set of documents.
+ * @xsl.usage advanced
+ */
+public class FuncDoclocation extends FunctionDef1Arg
+{
+    static final long serialVersionUID = 7469213946343568769L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    int whereNode = getArg0AsNode(xctxt);
+    String fileLocation = null;
+
+    if (DTM.NULL != whereNode)
+    {
+      DTM dtm = xctxt.getDTM(whereNode);
+      
+      // %REVIEW%
+      if (DTM.DOCUMENT_FRAGMENT_NODE ==  dtm.getNodeType(whereNode))
+      {
+        whereNode = dtm.getFirstChild(whereNode);
+      }
+
+      if (DTM.NULL != whereNode)
+      {        
+        fileLocation = dtm.getDocumentBaseURI();
+//        int owner = dtm.getDocument();
+//        fileLocation = xctxt.getSourceTreeManager().findURIFromDoc(owner);
+      }
+    }
+
+    return new XString((null != fileLocation) ? fileLocation : "");
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncExtElementAvailable.java b/src/main/java/org/apache/xpath/functions/FuncExtElementAvailable.java
new file mode 100644
index 0000000..5feea0d
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncExtElementAvailable.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncExtElementAvailable.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.templates.Constants;
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.ExtensionsProvider;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the ExtElementAvailable() function.
+ * @xsl.usage advanced
+ */
+public class FuncExtElementAvailable extends FunctionOneArg
+{
+    static final long serialVersionUID = -472533699257968546L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    String prefix;
+    String namespace;
+    String methName;
+
+    String fullName = m_arg0.execute(xctxt).str();
+    int indexOfNSSep = fullName.indexOf(':');
+
+    if (indexOfNSSep < 0)
+    {
+      prefix = "";
+      namespace = Constants.S_XSLNAMESPACEURL;
+      methName = fullName;
+    }
+    else
+    {
+      prefix = fullName.substring(0, indexOfNSSep);
+      namespace = xctxt.getNamespaceContext().getNamespaceForPrefix(prefix);
+      if (null == namespace)
+        return XBoolean.S_FALSE;
+      methName= fullName.substring(indexOfNSSep + 1);
+    }
+
+    if (namespace.equals(Constants.S_XSLNAMESPACEURL)
+    ||  namespace.equals(Constants.S_BUILTIN_EXTENSIONS_URL))
+    {
+      try
+      {
+        TransformerImpl transformer = (TransformerImpl) xctxt.getOwnerObject();
+        return transformer.getStylesheet().getAvailableElements().containsKey(
+                                                            new QName(namespace, methName))
+               ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+      }
+      catch (Exception e)
+      {
+        return XBoolean.S_FALSE;
+      }
+    }
+    else
+    {
+      //dml
+      ExtensionsProvider extProvider = (ExtensionsProvider)xctxt.getOwnerObject();
+      return extProvider.elementAvailable(namespace, methName)
+             ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncExtFunction.java b/src/main/java/org/apache/xpath/functions/FuncExtFunction.java
new file mode 100644
index 0000000..9293ccc
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncExtFunction.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncExtFunction.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import java.util.Vector;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionNode;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.ExtensionsProvider;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.XNull;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.apache.xpath.res.XPATHMessages;
+
+/**
+ * An object of this class represents an extension call expression.  When
+ * the expression executes, it calls ExtensionsTable#extFunction, and then
+ * converts the result to the appropriate XObject.
+ * @xsl.usage advanced
+ */
+public class FuncExtFunction extends Function
+{
+    static final long serialVersionUID = 5196115554693708718L;
+
+  /**
+   * The namespace for the extension function, which should not normally
+   *  be null or empty.
+   *  @serial    
+   */
+  String m_namespace;
+
+  /**
+   * The local name of the extension.
+   *  @serial   
+   */
+  String m_extensionName;
+
+  /**
+   * Unique method key, which is passed to ExtensionsTable#extFunction in
+   *  order to allow caching of the method.
+   *  @serial 
+   */
+  Object m_methodKey;
+
+  /**
+   * Array of static expressions which represent the parameters to the
+   *  function.
+   *  @serial   
+   */
+  Vector m_argVec = new Vector();
+
+  /**
+   * This function is used to fixup variables from QNames to stack frame
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list
+   * should be searched backwards for the first qualified name that
+   * corresponds to the variable reference qname.  The position of the
+   * QName in the vector from the start of the vector will be its position
+   * in the stack frame (but variables above the globalsTop value will need
+   * to be offset to the current stack frame).
+   * NEEDSDOC @param globalsSize
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+
+    if (null != m_argVec)
+    {
+      int nArgs = m_argVec.size();
+
+      for (int i = 0; i < nArgs; i++)
+      {
+        Expression arg = (Expression) m_argVec.elementAt(i);
+
+        arg.fixupVariables(vars, globalsSize);
+      }
+    }
+  }
+  
+  /**
+   * Return the namespace of the extension function.
+   *
+   * @return The namespace of the extension function.
+   */
+  public String getNamespace()
+  {
+    return m_namespace;
+  }
+  
+  /**
+   * Return the name of the extension function.
+   *
+   * @return The name of the extension function.
+   */
+  public String getFunctionName()
+  {
+    return m_extensionName;
+  }
+  
+  /**
+   * Return the method key of the extension function.
+   *
+   * @return The method key of the extension function.
+   */
+  public Object getMethodKey()
+  {
+    return m_methodKey;
+  }
+
+  /** 
+   * Return the nth argument passed to the extension function.
+   * 
+   * @param n The argument number index.
+   * @return The Expression object at the given index.
+   */    
+  public Expression getArg(int n) {
+    if (n >= 0 && n < m_argVec.size())
+      return (Expression) m_argVec.elementAt(n);
+    else
+      return null;
+  }
+
+  /**
+   * Return the number of arguments that were passed
+   * into this extension function.
+   *
+   * @return The number of arguments.
+   */    
+  public int getArgCount() {
+    return m_argVec.size();
+  }
+
+  /**
+   * Create a new FuncExtFunction based on the qualified name of the extension,
+   * and a unique method key.
+   *
+   * @param namespace The namespace for the extension function, which should
+   *                  not normally be null or empty.
+   * @param extensionName The local name of the extension.
+   * @param methodKey Unique method key, which is passed to
+   *                  ExtensionsTable#extFunction in order to allow caching
+   *                  of the method.
+   */
+  public FuncExtFunction(java.lang.String namespace,
+                         java.lang.String extensionName, Object methodKey)
+  {
+    //try{throw new Exception("FuncExtFunction() " + namespace + " " + extensionName);} catch (Exception e){e.printStackTrace();}
+    m_namespace = namespace;
+    m_extensionName = extensionName;
+    m_methodKey = methodKey;
+  }
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    if (xctxt.isSecureProcessing())
+      throw new javax.xml.transform.TransformerException(
+        XPATHMessages.createXPATHMessage(
+          XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
+          new Object[] {toString()}));
+      
+    XObject result;
+    Vector argVec = new Vector();
+    int nArgs = m_argVec.size();
+
+    for (int i = 0; i < nArgs; i++)
+    {
+      Expression arg = (Expression) m_argVec.elementAt(i);
+      
+      XObject xobj = arg.execute(xctxt);
+      /*
+       * Should cache the arguments for func:function
+       */
+      xobj.allowDetachToRelease(false); 
+      argVec.addElement(xobj);
+    }
+    //dml
+    ExtensionsProvider extProvider = (ExtensionsProvider)xctxt.getOwnerObject();
+    Object val = extProvider.extFunction(this, argVec);
+
+    if (null != val)
+    {
+      result = XObject.create(val, xctxt);
+    }
+    else
+    {
+      result = new XNull();
+    }
+
+    return result;
+  }
+
+  /**
+   * Set an argument expression for a function.  This method is called by the
+   * XPath compiler.
+   *
+   * @param arg non-null expression that represents the argument.
+   * @param argNum The argument number index.
+   *
+   * @throws WrongNumberArgsException If the argNum parameter is beyond what
+   * is specified for this function.
+   */
+  public void setArg(Expression arg, int argNum)
+          throws WrongNumberArgsException
+  {
+    m_argVec.addElement(arg);
+    arg.exprSetParent(this);
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   *
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException{}
+
+
+  class ArgExtOwner implements ExpressionOwner
+  {
+  
+    Expression m_exp;
+  	
+  	ArgExtOwner(Expression exp)
+  	{
+  		m_exp = exp;
+  	}
+  	
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_exp;
+    }
+
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(FuncExtFunction.this);
+    	m_exp = exp;
+    }
+  }
+  
+  
+  /**
+   * Call the visitors for the function arguments.
+   */
+  public void callArgVisitors(XPathVisitor visitor)
+  {
+      for (int i = 0; i < m_argVec.size(); i++)
+      {
+         Expression exp = (Expression)m_argVec.elementAt(i);
+         exp.callVisitors(new ArgExtOwner(exp), visitor);
+      }
+    
+  }
+
+  /**
+   * Set the parent node.
+   * For an extension function, we also need to set the parent
+   * node for all argument expressions.
+   * 
+   * @param n The parent node
+   */
+  public void exprSetParent(ExpressionNode n) 
+  {
+	
+    super.exprSetParent(n);
+      
+    int nArgs = m_argVec.size();
+
+    for (int i = 0; i < nArgs; i++)
+    {
+      Expression arg = (Expression) m_argVec.elementAt(i);
+
+      arg.exprSetParent(n);
+    }		
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.  This class supports an arbitrary
+   * number of arguments, so this method must never be called.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+    String fMsg = XSLMessages.createXPATHMessage(
+        XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
+        new Object[]{ "Programmer's assertion:  the method FunctionMultiArgs.reportWrongNumberArgs() should never be called." });
+
+    throw new RuntimeException(fMsg);
+  }
+  
+  /**
+   * Return the name of the extesion function in string format
+   */
+  public String toString()
+  {
+    if (m_namespace != null && m_namespace.length() > 0)
+      return "{" + m_namespace + "}" + m_extensionName;
+    else
+      return m_extensionName;
+  }  
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncExtFunctionAvailable.java b/src/main/java/org/apache/xpath/functions/FuncExtFunctionAvailable.java
new file mode 100644
index 0000000..8e4e5d7
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncExtFunctionAvailable.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncExtFunctionAvailable.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.templates.Constants;
+import org.apache.xpath.ExtensionsProvider;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.compiler.FunctionTable;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the ExtFunctionAvailable() function.
+ * @xsl.usage advanced
+ */
+public class FuncExtFunctionAvailable extends FunctionOneArg
+{
+    static final long serialVersionUID = 5118814314918592241L;
+    
+    transient private FunctionTable m_functionTable = null;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    String prefix;
+    String namespace;
+    String methName;
+
+    String fullName = m_arg0.execute(xctxt).str();
+    int indexOfNSSep = fullName.indexOf(':');
+
+    if (indexOfNSSep < 0)
+    {
+      prefix = "";
+      namespace = Constants.S_XSLNAMESPACEURL;
+      methName = fullName;
+    }
+    else
+    {
+      prefix = fullName.substring(0, indexOfNSSep);
+      namespace = xctxt.getNamespaceContext().getNamespaceForPrefix(prefix);
+      if (null == namespace)
+        return XBoolean.S_FALSE;
+        methName = fullName.substring(indexOfNSSep + 1);
+    }
+
+    if (namespace.equals(Constants.S_XSLNAMESPACEURL))
+    {
+      try
+      {
+        if (null == m_functionTable) m_functionTable = new FunctionTable();
+        return m_functionTable.functionAvailable(methName) ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+      }
+      catch (Exception e)
+      {
+        return XBoolean.S_FALSE;
+      }
+    }
+    else
+    {
+      //dml
+      ExtensionsProvider extProvider = (ExtensionsProvider)xctxt.getOwnerObject();
+      return extProvider.functionAvailable(namespace, methName)
+             ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+    }
+  }
+  
+  /**
+   * The function table is an instance field. In order to access this instance 
+   * field during evaluation, this method is called at compilation time to
+   * insert function table information for later usage. It should only be used
+   * during compiling of XPath expressions.
+   * @param aTable an instance of the function table
+   */
+  public void setFunctionTable(FunctionTable aTable){
+          m_functionTable = aTable;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncFalse.java b/src/main/java/org/apache/xpath/functions/FuncFalse.java
new file mode 100644
index 0000000..5bb671b
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncFalse.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncFalse.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the False() function.
+ * @xsl.usage advanced
+ */
+public class FuncFalse extends Function
+{
+    static final long serialVersionUID = 6150918062759769887L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return XBoolean.S_FALSE;
+  }
+  
+  /**
+   * No arguments to process, so this does nothing.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    // no-op
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncFloor.java b/src/main/java/org/apache/xpath/functions/FuncFloor.java
new file mode 100644
index 0000000..3dda98d
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncFloor.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncFloor.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Floor() function.
+ * @xsl.usage advanced
+ */
+public class FuncFloor extends FunctionOneArg
+{
+    static final long serialVersionUID = 2326752233236309265L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(java.lang.Math.floor(m_arg0.execute(xctxt).num()));
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncGenerateId.java b/src/main/java/org/apache/xpath/functions/FuncGenerateId.java
new file mode 100644
index 0000000..a79dcca
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncGenerateId.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncGenerateId.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the GenerateId() function.
+ * @xsl.usage advanced
+ */
+public class FuncGenerateId extends FunctionDef1Arg
+{
+    static final long serialVersionUID = 973544842091724273L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    int which = getArg0AsNode(xctxt);
+
+    if (DTM.NULL != which)
+    {
+      // Note that this is a different value than in previous releases
+      // of Xalan. It's sensitive to the exact encoding of the node
+      // handle anyway, so fighting to maintain backward compatability
+      // really didn't make sense; it may change again as we continue
+      // to experiment with balancing document and node numbers within
+      // that value.
+      return new XString("N" + Integer.toHexString(which).toUpperCase());
+    }
+    else
+      return XString.EMPTYSTRING;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncId.java b/src/main/java/org/apache/xpath/functions/FuncId.java
new file mode 100644
index 0000000..b06d66f
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncId.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncId.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import java.util.StringTokenizer;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.StringVector;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Execute the Id() function.
+ * @xsl.usage advanced
+ */
+public class FuncId extends FunctionOneArg
+{
+    static final long serialVersionUID = 8930573966143567310L;
+
+  /**
+   * Fill in a list with nodes that match a space delimited list if ID 
+   * ID references.
+   *
+   * @param xctxt The runtime XPath context.
+   * @param docContext The document where the nodes are being looked for.
+   * @param refval A space delimited list of ID references.
+   * @param usedrefs List of references for which nodes were found.
+   * @param nodeSet Node set where the nodes will be added to.
+   * @param mayBeMore true if there is another set of nodes to be looked for.
+   *
+   * @return The usedrefs value.
+   */
+  private StringVector getNodesByID(XPathContext xctxt, int docContext,
+                                    String refval, StringVector usedrefs,
+                                    NodeSetDTM nodeSet, boolean mayBeMore)
+  {
+
+    if (null != refval)
+    {
+      String ref = null;
+//      DOMHelper dh = xctxt.getDOMHelper();
+      StringTokenizer tokenizer = new StringTokenizer(refval);
+      boolean hasMore = tokenizer.hasMoreTokens();
+      DTM dtm = xctxt.getDTM(docContext);
+
+      while (hasMore)
+      {
+        ref = tokenizer.nextToken();
+        hasMore = tokenizer.hasMoreTokens();
+
+        if ((null != usedrefs) && usedrefs.contains(ref))
+        {
+          ref = null;
+
+          continue;
+        }
+
+        int node = dtm.getElementById(ref);
+
+        if (DTM.NULL != node)
+          nodeSet.addNodeInDocOrder(node, xctxt);
+
+        if ((null != ref) && (hasMore || mayBeMore))
+        {
+          if (null == usedrefs)
+            usedrefs = new StringVector();
+
+          usedrefs.addElement(ref);
+        }
+      }
+    }
+
+    return usedrefs;
+  }
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    int context = xctxt.getCurrentNode();
+    DTM dtm = xctxt.getDTM(context);
+    int docContext = dtm.getDocument();
+
+    if (DTM.NULL == docContext)
+      error(xctxt, XPATHErrorResources.ER_CONTEXT_HAS_NO_OWNERDOC, null);
+
+    XObject arg = m_arg0.execute(xctxt);
+    int argType = arg.getType();
+    XNodeSet nodes = new XNodeSet(xctxt.getDTMManager());
+    NodeSetDTM nodeSet = nodes.mutableNodeset();
+
+    if (XObject.CLASS_NODESET == argType)
+    {
+      DTMIterator ni = arg.iter();
+      StringVector usedrefs = null;
+      int pos = ni.nextNode();
+
+      while (DTM.NULL != pos)
+      {
+        DTM ndtm = ni.getDTM(pos);
+        String refval = ndtm.getStringValue(pos).toString();
+
+        pos = ni.nextNode();
+        usedrefs = getNodesByID(xctxt, docContext, refval, usedrefs, nodeSet,
+                                DTM.NULL != pos);
+      }
+      // ni.detach();
+    }
+    else if (XObject.CLASS_NULL == argType)
+    {
+      return nodes;
+    }
+    else
+    {
+      String refval = arg.str();
+
+      getNodesByID(xctxt, docContext, refval, null, nodeSet, false);
+    }
+
+    return nodes;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncLang.java b/src/main/java/org/apache/xpath/functions/FuncLang.java
new file mode 100644
index 0000000..61b8d25
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncLang.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncLang.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Lang() function.
+ * @xsl.usage advanced
+ */
+public class FuncLang extends FunctionOneArg
+{
+    static final long serialVersionUID = -7868705139354872185L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    String lang = m_arg0.execute(xctxt).str();
+    int parent = xctxt.getCurrentNode();
+    boolean isLang = false;
+    DTM dtm = xctxt.getDTM(parent);
+
+    while (DTM.NULL != parent)
+    {
+      if (DTM.ELEMENT_NODE == dtm.getNodeType(parent))
+      {
+        int langAttr = dtm.getAttributeNode(parent, "http://www.w3.org/XML/1998/namespace", "lang");
+
+        if (DTM.NULL != langAttr)
+        {
+          String langVal = dtm.getNodeValue(langAttr);
+          // %OPT%
+          if (langVal.toLowerCase().startsWith(lang.toLowerCase()))
+          {
+            int valLen = lang.length();
+
+            if ((langVal.length() == valLen)
+                    || (langVal.charAt(valLen) == '-'))
+            {
+              isLang = true;
+            }
+          }
+
+          break;
+        }
+      }
+
+      parent = dtm.getParent(parent);
+    }
+
+    return isLang ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncLast.java b/src/main/java/org/apache/xpath/functions/FuncLast.java
new file mode 100644
index 0000000..93f1afd
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncLast.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncLast.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.SubContextList;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+
+/**
+ * Execute the Last() function.
+ * @xsl.usage advanced
+ */
+public class FuncLast extends Function
+{
+    static final long serialVersionUID = 9205812403085432943L;
+  
+  private boolean m_isTopLevel;
+  
+  /**
+   * Figure out if we're executing a toplevel expression.
+   * If so, we can't be inside of a predicate. 
+   */
+  public void postCompileStep(Compiler compiler)
+  {
+    m_isTopLevel = compiler.getLocationPathDepth() == -1;
+  }
+
+  /**
+   * Get the position in the current context node list.
+   *
+   * @param xctxt non-null reference to XPath runtime context.
+   *
+   * @return The number of nodes in the list.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public int getCountOfContextNodeList(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // assert(null != m_contextNodeList, "m_contextNodeList must be non-null");
+    // If we're in a predicate, then this will return non-null.
+    SubContextList iter = m_isTopLevel ? null : xctxt.getSubContextList();
+
+    // System.out.println("iter: "+iter);
+    if (null != iter)
+      return iter.getLastPos(xctxt);
+
+    DTMIterator cnl = xctxt.getContextNodeList();
+    int count;
+    if(null != cnl)
+    {
+      count = cnl.getLength();
+      // System.out.println("count: "+count); 
+    }
+    else
+      count = 0;   
+    return count;
+  }
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    XNumber xnum = new XNumber((double) getCountOfContextNodeList(xctxt));
+    // System.out.println("last: "+xnum.num());
+    return xnum;
+  }
+  
+  /**
+   * No arguments to process, so this does nothing.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    // no-op
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncLocalPart.java b/src/main/java/org/apache/xpath/functions/FuncLocalPart.java
new file mode 100644
index 0000000..f73a1d8
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncLocalPart.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncLocalPart.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the LocalPart() function.
+ * @xsl.usage advanced
+ */
+public class FuncLocalPart extends FunctionDef1Arg
+{
+    static final long serialVersionUID = 7591798770325814746L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    int context = getArg0AsNode(xctxt);
+    if(DTM.NULL == context)
+      return XString.EMPTYSTRING;
+    DTM dtm = xctxt.getDTM(context);
+    String s = (context != DTM.NULL) ? dtm.getLocalName(context) : "";
+    if(s.startsWith("#") || s.equals("xmlns"))
+      return XString.EMPTYSTRING;
+
+    return new XString(s);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncNamespace.java b/src/main/java/org/apache/xpath/functions/FuncNamespace.java
new file mode 100644
index 0000000..6c040e6
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncNamespace.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncNamespace.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the Namespace() function.
+ * @xsl.usage advanced
+ */
+public class FuncNamespace extends FunctionDef1Arg
+{
+    static final long serialVersionUID = -4695674566722321237L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    int context = getArg0AsNode(xctxt);
+    
+    String s;
+    if(context != DTM.NULL)
+    {
+      DTM dtm = xctxt.getDTM(context);
+      int t = dtm.getNodeType(context);
+      if(t == DTM.ELEMENT_NODE)
+      {
+        s = dtm.getNamespaceURI(context);
+      }
+      else if(t == DTM.ATTRIBUTE_NODE)
+      {
+
+        // This function always returns an empty string for namespace nodes.
+        // We check for those here.  Fix inspired by Davanum Srinivas.
+
+        s = dtm.getNodeName(context);
+        if(s.startsWith("xmlns:") || s.equals("xmlns"))
+          return XString.EMPTYSTRING;
+
+        s = dtm.getNamespaceURI(context);
+      }
+      else
+        return XString.EMPTYSTRING;
+    }
+    else 
+      return XString.EMPTYSTRING;
+    
+    return ((null == s) ? XString.EMPTYSTRING : new XString(s));
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncNormalizeSpace.java b/src/main/java/org/apache/xpath/functions/FuncNormalizeSpace.java
new file mode 100644
index 0000000..5c00d20
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncNormalizeSpace.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncNormalizeSpace.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+import org.xml.sax.ContentHandler;
+
+/**
+ * Execute the normalize-space() function.
+ * @xsl.usage advanced
+ */
+public class FuncNormalizeSpace extends FunctionDef1Arg
+{
+    static final long serialVersionUID = -3377956872032190880L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    XMLString s1 = getArg0AsString(xctxt);
+
+    return (XString)s1.fixWhiteSpace(true, true, false);
+  }
+  
+  /**
+   * Execute an expression in the XPath runtime context, and return the 
+   * result of the expression.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception 
+   *         occurs.
+   */
+  public void executeCharsToContentHandler(XPathContext xctxt, 
+                                              ContentHandler handler)
+    throws javax.xml.transform.TransformerException,
+           org.xml.sax.SAXException
+  {
+    if(Arg0IsNodesetExpr())
+    {
+      int node = getArg0AsNode(xctxt);
+      if(DTM.NULL != node)
+      {
+        DTM dtm = xctxt.getDTM(node);
+        dtm.dispatchCharactersEvents(node, handler, true);
+      }
+    }
+    else
+    {
+      XObject obj = execute(xctxt);
+      obj.dispatchCharactersEvents(handler);
+    }
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncNot.java b/src/main/java/org/apache/xpath/functions/FuncNot.java
new file mode 100644
index 0000000..b69a450
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncNot.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncNot.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Not() function.
+ * @xsl.usage advanced
+ */
+public class FuncNot extends FunctionOneArg
+{
+    static final long serialVersionUID = 7299699961076329790L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return m_arg0.execute(xctxt).bool() ? XBoolean.S_FALSE : XBoolean.S_TRUE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncNumber.java b/src/main/java/org/apache/xpath/functions/FuncNumber.java
new file mode 100644
index 0000000..401447c
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncNumber.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncNumber.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Number() function.
+ * @xsl.usage advanced
+ */
+public class FuncNumber extends FunctionDef1Arg
+{
+    static final long serialVersionUID = 7266745342264153076L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(getArg0AsNumber(xctxt));
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncPosition.java b/src/main/java/org/apache/xpath/functions/FuncPosition.java
new file mode 100644
index 0000000..c6b9a05
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncPosition.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncPosition.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.SubContextList;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Position() function.
+ * @xsl.usage advanced
+ */
+public class FuncPosition extends Function
+{
+    static final long serialVersionUID = -9092846348197271582L;
+  private boolean m_isTopLevel;
+  
+  /**
+   * Figure out if we're executing a toplevel expression.
+   * If so, we can't be inside of a predicate. 
+   */
+  public void postCompileStep(Compiler compiler)
+  {
+    m_isTopLevel = compiler.getLocationPathDepth() == -1;
+  }
+
+  /**
+   * Get the position in the current context node list.
+   *
+   * @param xctxt Runtime XPath context.
+   *
+   * @return The current position of the itteration in the context node list, 
+   *         or -1 if there is no active context node list.
+   */
+  public int getPositionInContextNodeList(XPathContext xctxt)
+  {
+
+    // System.out.println("FuncPosition- entry");
+    // If we're in a predicate, then this will return non-null.
+    SubContextList iter = m_isTopLevel ? null : xctxt.getSubContextList();
+
+    if (null != iter)
+    {
+      int prox = iter.getProximityPosition(xctxt);
+ 
+      // System.out.println("FuncPosition- prox: "+prox);
+      return prox;
+    }
+
+    DTMIterator cnl = xctxt.getContextNodeList();
+
+    if (null != cnl)
+    {
+      int n = cnl.getCurrentNode();
+      if(n == DTM.NULL)
+      {
+        if(cnl.getCurrentPos() == 0)
+          return 0;
+          
+        // Then I think we're in a sort.  See sort21.xsl. So the iterator has 
+        // already been spent, and is not on the node we're processing. 
+        // It's highly possible that this is an issue for other context-list 
+        // functions.  Shouldn't be a problem for last(), and it shouldn't be 
+        // a problem for current().
+        try 
+        { 
+          cnl = cnl.cloneWithReset(); 
+        }
+        catch(CloneNotSupportedException cnse)
+        {
+          throw new org.apache.xml.utils.WrappedRuntimeException(cnse);
+        }
+        int currentNode = xctxt.getContextNode();
+        // System.out.println("currentNode: "+currentNode);
+        while(DTM.NULL != (n = cnl.nextNode()))
+        {
+          if(n == currentNode)
+            break;
+        }
+      }
+      // System.out.println("n: "+n);
+      // System.out.println("FuncPosition- cnl.getCurrentPos(): "+cnl.getCurrentPos());
+      return cnl.getCurrentPos();
+    }
+
+    // System.out.println("FuncPosition - out of guesses: -1");
+    return -1;
+  }
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    double pos = (double) getPositionInContextNodeList(xctxt);
+    
+    return new XNumber(pos);
+  }
+  
+  /**
+   * No arguments to process, so this does nothing.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    // no-op
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncQname.java b/src/main/java/org/apache/xpath/functions/FuncQname.java
new file mode 100644
index 0000000..db51cab
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncQname.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncQname.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the Qname() function.
+ * @xsl.usage advanced
+ */
+public class FuncQname extends FunctionDef1Arg
+{
+    static final long serialVersionUID = -1532307875532617380L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    int context = getArg0AsNode(xctxt);
+    XObject val;
+
+    if (DTM.NULL != context)
+    {
+      DTM dtm = xctxt.getDTM(context);
+      String qname = dtm.getNodeNameX(context);
+      val = (null == qname) ? XString.EMPTYSTRING : new XString(qname);
+    }
+    else
+    {
+      val = XString.EMPTYSTRING;
+    }
+
+    return val;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncRound.java b/src/main/java/org/apache/xpath/functions/FuncRound.java
new file mode 100644
index 0000000..78943c5
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncRound.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncRound.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the round() function.
+ * @xsl.usage advanced
+ */
+public class FuncRound extends FunctionOneArg
+{
+    static final long serialVersionUID = -7970583902573826611L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+          final XObject obj = m_arg0.execute(xctxt);
+          final double val= obj.num();
+          if (val >= -0.5 && val < 0) return new XNumber(-0.0);
+          if (val == 0.0) return new XNumber(val);
+          return new XNumber(java.lang.Math.floor(val
+                                            + 0.5));
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncStartsWith.java b/src/main/java/org/apache/xpath/functions/FuncStartsWith.java
new file mode 100644
index 0000000..5a090a2
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncStartsWith.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncStartsWith.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the StartsWith() function.
+ * @xsl.usage advanced
+ */
+public class FuncStartsWith extends Function2Args
+{
+    static final long serialVersionUID = 2194585774699567928L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return m_arg0.execute(xctxt).xstr().startsWith(m_arg1.execute(xctxt).xstr())
+           ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncString.java b/src/main/java/org/apache/xpath/functions/FuncString.java
new file mode 100644
index 0000000..4107b22
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncString.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncString.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the String() function.
+ * @xsl.usage advanced
+ */
+public class FuncString extends FunctionDef1Arg
+{
+    static final long serialVersionUID = -2206677149497712883L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return (XString)getArg0AsString(xctxt);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncStringLength.java b/src/main/java/org/apache/xpath/functions/FuncStringLength.java
new file mode 100644
index 0000000..bc8bce6
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncStringLength.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncStringLength.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the StringLength() function.
+ * @xsl.usage advanced
+ */
+public class FuncStringLength extends FunctionDef1Arg
+{
+    static final long serialVersionUID = -159616417996519839L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(getArg0AsString(xctxt).length());
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncSubstring.java b/src/main/java/org/apache/xpath/functions/FuncSubstring.java
new file mode 100644
index 0000000..b2254e2
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncSubstring.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncSubstring.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Execute the Substring() function.
+ * @xsl.usage advanced
+ */
+public class FuncSubstring extends Function3Args
+{
+    static final long serialVersionUID = -5996676095024715502L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    XMLString s1 = m_arg0.execute(xctxt).xstr();
+    double start = m_arg1.execute(xctxt).num();
+    int lenOfS1 = s1.length();
+    XMLString substr;
+
+    if (lenOfS1 <= 0)
+      return XString.EMPTYSTRING;
+    else
+    {
+      int startIndex;
+
+      if (Double.isNaN(start))
+      {
+
+        // Double.MIN_VALUE doesn't work with math below 
+        // so just use a big number and hope I never get caught.
+        start = -1000000;
+        startIndex = 0;
+      }
+      else
+      {
+        start = Math.round(start);
+        startIndex = (start > 0) ? (int) start - 1 : 0;
+      }
+
+      if (null != m_arg2)
+      {
+        double len = m_arg2.num(xctxt);
+        int end = (int) (Math.round(len) + start) - 1;
+
+        // Normalize end index.
+        if (end < 0)
+          end = 0;
+        else if (end > lenOfS1)
+          end = lenOfS1;
+
+        if (startIndex > lenOfS1)
+          startIndex = lenOfS1;
+
+        substr = s1.substring(startIndex, end);
+      }
+      else
+      {
+        if (startIndex > lenOfS1)
+          startIndex = lenOfS1;
+        substr = s1.substring(startIndex);
+      }
+    }
+
+    return (XString)substr; // cast semi-safe
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct. 
+   *
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if (argNum < 2)
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_TWO_OR_THREE, null)); //"2 or 3");
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncSubstringAfter.java b/src/main/java/org/apache/xpath/functions/FuncSubstringAfter.java
new file mode 100644
index 0000000..9685977
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncSubstringAfter.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncSubstringAfter.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the SubstringAfter() function.
+ * @xsl.usage advanced
+ */
+public class FuncSubstringAfter extends Function2Args
+{
+    static final long serialVersionUID = -8119731889862512194L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    XMLString s1 = m_arg0.execute(xctxt).xstr();
+    XMLString s2 = m_arg1.execute(xctxt).xstr();
+    int index = s1.indexOf(s2);
+
+    return (-1 == index)
+           ? XString.EMPTYSTRING
+           : (XString)s1.substring(index + s2.length());
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncSubstringBefore.java b/src/main/java/org/apache/xpath/functions/FuncSubstringBefore.java
new file mode 100644
index 0000000..4dcd2f1
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncSubstringBefore.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncSubstringBefore.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the SubstringBefore() function.
+ * @xsl.usage advanced
+ */
+public class FuncSubstringBefore extends Function2Args
+{
+    static final long serialVersionUID = 4110547161672431775L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    String s1 = m_arg0.execute(xctxt).str();
+    String s2 = m_arg1.execute(xctxt).str();
+    int index = s1.indexOf(s2);
+
+    return (-1 == index)
+           ? XString.EMPTYSTRING : new XString(s1.substring(0, index));
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncSum.java b/src/main/java/org/apache/xpath/functions/FuncSum.java
new file mode 100644
index 0000000..1abbe65
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncSum.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncSum.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the Sum() function.
+ * @xsl.usage advanced
+ */
+public class FuncSum extends FunctionOneArg
+{
+    static final long serialVersionUID = -2719049259574677519L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    DTMIterator nodes = m_arg0.asIterator(xctxt, xctxt.getCurrentNode());
+    double sum = 0.0;
+    int pos;
+
+    while (DTM.NULL != (pos = nodes.nextNode()))
+    {
+      DTM dtm = nodes.getDTM(pos);
+      XMLString s = dtm.getStringValue(pos);
+
+      if (null != s)
+        sum += s.toDouble();
+    }
+    nodes.detach();
+
+    return new XNumber(sum);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncSystemProperty.java b/src/main/java/org/apache/xpath/functions/FuncSystemProperty.java
new file mode 100644
index 0000000..154e686
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncSystemProperty.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncSystemProperty.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Execute the SystemProperty() function.
+ * @xsl.usage advanced
+ */
+public class FuncSystemProperty extends FunctionOneArg
+{
+    static final long serialVersionUID = 3694874980992204867L;
+  /**
+   * The path/filename of the property file: XSLTInfo.properties
+   * Maintenance note: see also
+   * org.apache.xalan.processor.TransformerFactoryImpl.XSLT_PROPERTIES
+   */
+  static final String XSLT_PROPERTIES = 
+            "org/apache/xalan/res/XSLTInfo.properties";
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    String fullName = m_arg0.execute(xctxt).str();
+    int indexOfNSSep = fullName.indexOf(':');
+    String result;
+    String propName = "";
+
+    // List of properties where the name of the
+    // property argument is to be looked for.
+    Properties xsltInfo = new Properties();
+
+    loadPropertyFile(XSLT_PROPERTIES, xsltInfo);
+
+    if (indexOfNSSep > 0)
+    {
+      String prefix = (indexOfNSSep >= 0)
+                      ? fullName.substring(0, indexOfNSSep) : "";
+      String namespace;
+
+      namespace = xctxt.getNamespaceContext().getNamespaceForPrefix(prefix);
+      propName = (indexOfNSSep < 0)
+                 ? fullName : fullName.substring(indexOfNSSep + 1);
+
+      if (namespace.startsWith("http://www.w3.org/XSL/Transform")
+              || namespace.equals("http://www.w3.org/1999/XSL/Transform"))
+      {
+        result = xsltInfo.getProperty(propName);
+
+        if (null == result)
+        {
+          warn(xctxt, XPATHErrorResources.WG_PROPERTY_NOT_SUPPORTED,
+               new Object[]{ fullName });  //"XSL Property not supported: "+fullName);
+
+          return XString.EMPTYSTRING;
+        }
+      }
+      else
+      {
+        warn(xctxt, XPATHErrorResources.WG_DONT_DO_ANYTHING_WITH_NS,
+             new Object[]{ namespace,
+                           fullName });  //"Don't currently do anything with namespace "+namespace+" in property: "+fullName);
+
+        try
+        {
+          result = System.getProperty(propName);
+
+          if (null == result)
+          {
+
+            // result = System.getenv(propName);
+            return XString.EMPTYSTRING;
+          }
+        }
+        catch (SecurityException se)
+        {
+          warn(xctxt, XPATHErrorResources.WG_SECURITY_EXCEPTION,
+               new Object[]{ fullName });  //"SecurityException when trying to access XSL system property: "+fullName);
+
+          return XString.EMPTYSTRING;
+        }
+      }
+    }
+    else
+    {
+      try
+      {
+        result = System.getProperty(fullName);
+
+        if (null == result)
+        {
+
+          // result = System.getenv(fullName);
+          return XString.EMPTYSTRING;
+        }
+      }
+      catch (SecurityException se)
+      {
+        warn(xctxt, XPATHErrorResources.WG_SECURITY_EXCEPTION,
+             new Object[]{ fullName });  //"SecurityException when trying to access XSL system property: "+fullName);
+
+        return XString.EMPTYSTRING;
+      }
+    }
+
+    if (propName.equals("version") && result.length() > 0)
+    {
+      try
+      {
+        // Needs to return the version number of the spec we conform to.
+        return new XString("1.0");
+      }
+      catch (Exception ex)
+      {
+        return new XString(result);
+      }
+    }
+    else
+      return new XString(result);
+  }
+
+  /**
+   * Retrieve a propery bundle from a specified file
+   * 
+   * @param file The string name of the property file.  The name 
+   * should already be fully qualified as path/filename
+   * @param target The target property bag the file will be placed into.
+   */
+  public void loadPropertyFile(String file, Properties target)
+  {
+    try
+    {
+      // Use SecuritySupport class to provide priveleged access to property file
+      SecuritySupport ss = SecuritySupport.getInstance();
+
+      InputStream is = ss.getResourceAsStream(ObjectFactory.findClassLoader(),
+                                              file);
+
+      // get a buffered version
+      BufferedInputStream bis = new BufferedInputStream(is);
+
+      target.load(bis);  // and load up the property bag from this
+      bis.close();  // close out after reading
+    }
+    catch (Exception ex)
+    {
+      // ex.printStackTrace();
+      throw new org.apache.xml.utils.WrappedRuntimeException(ex);
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncTranslate.java b/src/main/java/org/apache/xpath/functions/FuncTranslate.java
new file mode 100644
index 0000000..4eb1fc8
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncTranslate.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncTranslate.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * Execute the Translate() function.
+ * @xsl.usage advanced
+ */
+public class FuncTranslate extends Function3Args
+{
+    static final long serialVersionUID = -1672834340026116482L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    String theFirstString = m_arg0.execute(xctxt).str();
+    String theSecondString = m_arg1.execute(xctxt).str();
+    String theThirdString = m_arg2.execute(xctxt).str();
+    int theFirstStringLength = theFirstString.length();
+    int theThirdStringLength = theThirdString.length();
+
+    // A vector to contain the new characters.  We'll use it to construct
+    // the result string.
+    StringBuffer sbuffer = new StringBuffer();
+
+    for (int i = 0; i < theFirstStringLength; i++)
+    {
+      char theCurrentChar = theFirstString.charAt(i);
+      int theIndex = theSecondString.indexOf(theCurrentChar);
+
+      if (theIndex < 0)
+      {
+
+        // Didn't find the character in the second string, so it
+        // is not translated.
+        sbuffer.append(theCurrentChar);
+      }
+      else if (theIndex < theThirdStringLength)
+      {
+
+        // OK, there's a corresponding character in the
+        // third string, so do the translation...
+        sbuffer.append(theThirdString.charAt(theIndex));
+      }
+      else
+      {
+
+        // There's no corresponding character in the
+        // third string, since it's shorter than the
+        // second string.  In this case, the character
+        // is removed from the output string, so don't
+        // do anything.
+      }
+    }
+
+    return new XString(sbuffer.toString());
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncTrue.java b/src/main/java/org/apache/xpath/functions/FuncTrue.java
new file mode 100644
index 0000000..b8c7479
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncTrue.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncTrue.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Execute the True() function.
+ * @xsl.usage advanced
+ */
+public class FuncTrue extends Function
+{
+    static final long serialVersionUID = 5663314547346339447L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+    return XBoolean.S_TRUE;
+  }
+  
+  /**
+   * No arguments to process, so this does nothing.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    // no-op
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/functions/FuncUnparsedEntityURI.java b/src/main/java/org/apache/xpath/functions/FuncUnparsedEntityURI.java
new file mode 100644
index 0000000..4517e0d
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FuncUnparsedEntityURI.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FuncUnparsedEntityURI.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * @xsl.usage advanced
+ */
+public class FuncUnparsedEntityURI extends FunctionOneArg
+{
+    static final long serialVersionUID = 845309759097448178L;
+
+  /**
+   * Execute the function.  The function must return
+   * a valid object.
+   * @param xctxt The current execution context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    String name = m_arg0.execute(xctxt).str();
+    int context = xctxt.getCurrentNode();
+    DTM dtm = xctxt.getDTM(context);
+    int doc = dtm.getDocument();
+    
+    String uri = dtm.getUnparsedEntityURI(name);
+
+    return new XString(uri);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/Function.java b/src/main/java/org/apache/xpath/functions/Function.java
new file mode 100644
index 0000000..6e923a4
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/Function.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Function.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.compiler.Compiler;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * This is a superclass of all XPath functions.  This allows two
+ * ways for the class to be called. One method is that the
+ * super class processes the arguments and hands the results to
+ * the derived class, the other method is that the derived
+ * class may process it's own arguments, which is faster since
+ * the arguments don't have to be added to an array, but causes
+ * a larger code footprint.
+ * @xsl.usage advanced
+ */
+public abstract class Function extends Expression
+{
+    static final long serialVersionUID = 6927661240854599768L;
+
+  /**
+   * Set an argument expression for a function.  This method is called by the 
+   * XPath compiler.
+   *
+   * @param arg non-null expression that represents the argument.
+   * @param argNum The argument number index.
+   *
+   * @throws WrongNumberArgsException If the argNum parameter is beyond what 
+   * is specified for this function.
+   */
+  public void setArg(Expression arg, int argNum)
+          throws WrongNumberArgsException
+  {
+			// throw new WrongNumberArgsException(XSLMessages.createXPATHMessage("zero", null));
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   * This method is meant to be overloaded by derived classes, to check for 
+   * the number of arguments for a specific function type.  This method is 
+   * called by the compiler for static number of arguments checking.
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if (argNum != 0)
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.  This method is meant to be overloaded
+   * by derived classes so that the message will be as specific as possible.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage("zero", null));
+  }
+
+  /**
+   * Execute an XPath function object.  The function must return
+   * a valid object.
+   * @param xctxt The execution current context.
+   * @return A valid XObject.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    // Programmer's assert.  (And, no, I don't want the method to be abstract).
+    System.out.println("Error! Function.execute should not be called!");
+
+    return null;
+  }
+  
+  /**
+   * Call the visitors for the function arguments.
+   */
+  public void callArgVisitors(XPathVisitor visitor)
+  {
+  }
+
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	if(visitor.visitFunction(owner, this))
+  	{
+  		callArgVisitors(visitor);
+  	}
+  }
+  
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!isSameClass(expr))
+  		return false;
+  		
+  	return true;
+  }
+
+  /**
+   * This function is currently only being used by Position()
+   * and Last(). See respective functions for more detail.
+   */
+  public void postCompileStep(Compiler compiler)
+  {
+    // no default action
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/Function2Args.java b/src/main/java/org/apache/xpath/functions/Function2Args.java
new file mode 100644
index 0000000..7c36c4a
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/Function2Args.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Function2Args.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+
+/**
+ * Base class for functions that accept two arguments.
+ * @xsl.usage advanced
+ */
+public class Function2Args extends FunctionOneArg
+{
+    static final long serialVersionUID = 5574294996842710641L;
+
+  /** The second argument passed to the function (at index 1).
+   *  @serial  */
+  Expression m_arg1;
+
+  /**
+   * Return the second argument passed to the function (at index 1).
+   *
+   * @return An expression that represents the second argument passed to the 
+   *         function.
+   */
+  public Expression getArg1()
+  {
+    return m_arg1;
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+    if(null != m_arg1)
+      m_arg1.fixupVariables(vars, globalsSize);
+  }
+
+
+  /**
+   * Set an argument expression for a function.  This method is called by the 
+   * XPath compiler.
+   *
+   * @param arg non-null expression that represents the argument.
+   * @param argNum The argument number index.
+   *
+   * @throws WrongNumberArgsException If the argNum parameter is greater than 1.
+   */
+  public void setArg(Expression arg, int argNum)
+          throws WrongNumberArgsException
+  {
+
+    // System.out.println("argNum: "+argNum);
+    if (argNum == 0)
+      super.setArg(arg, argNum);
+    else if (1 == argNum)
+    {
+      m_arg1 = arg;
+      arg.exprSetParent(this);
+    }
+    else
+		  reportWrongNumberArgs();
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct. 
+   *
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if (argNum != 2)
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage("two", null));
+  }
+  
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside 
+   * the current subtree.
+   * 
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+   public boolean canTraverseOutsideSubtree()
+   {
+    return super.canTraverseOutsideSubtree() 
+    ? true : m_arg1.canTraverseOutsideSubtree();
+   }
+   
+  class Arg1Owner implements ExpressionOwner
+  {
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_arg1;
+    }
+
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(Function2Args.this);
+    	m_arg1 = exp;
+    }
+  }
+
+   
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callArgVisitors(XPathVisitor visitor)
+  {
+  	super.callArgVisitors(visitor);
+  	if(null != m_arg1)
+  		m_arg1.callVisitors(new Arg1Owner(), visitor);
+  }
+
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!super.deepEquals(expr))
+  		return false;
+  		
+  	if(null != m_arg1)
+  	{
+  		if(null == ((Function2Args)expr).m_arg1)
+  			return false;
+  			
+  		if(!m_arg1.deepEquals(((Function2Args)expr).m_arg1))
+  			return false;
+  	}
+  	else if(null != ((Function2Args)expr).m_arg1)
+  		return false;
+  		
+  	return true;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/functions/Function3Args.java b/src/main/java/org/apache/xpath/functions/Function3Args.java
new file mode 100644
index 0000000..bb49216
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/Function3Args.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Function3Args.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+
+/**
+ * Base class for functions that accept three arguments.
+ * @xsl.usage advanced
+ */
+public class Function3Args extends Function2Args
+{
+    static final long serialVersionUID = 7915240747161506646L;
+
+  /** The third argument passed to the function (at index 2).
+   *  @serial  */
+  Expression m_arg2;
+
+  /**
+   * Return the third argument passed to the function (at index 2).
+   *
+   * @return An expression that represents the third argument passed to the 
+   *         function.
+   */
+  public Expression getArg2()
+  {
+    return m_arg2;
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+    if(null != m_arg2)
+      m_arg2.fixupVariables(vars, globalsSize);
+  }
+
+  /**
+   * Set an argument expression for a function.  This method is called by the 
+   * XPath compiler.
+   *
+   * @param arg non-null expression that represents the argument.
+   * @param argNum The argument number index.
+   *
+   * @throws WrongNumberArgsException If the argNum parameter is greater than 2.
+   */
+  public void setArg(Expression arg, int argNum)
+          throws WrongNumberArgsException
+  {
+
+    if (argNum < 2)
+      super.setArg(arg, argNum);
+    else if (2 == argNum)
+    {
+      m_arg2 = arg;
+      arg.exprSetParent(this);
+    }
+    else
+		  reportWrongNumberArgs();
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct. 
+   *
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if (argNum != 3)
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage("three", null));
+  }
+  
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside 
+   * the current subtree.
+   * 
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+   public boolean canTraverseOutsideSubtree()
+   {
+    return super.canTraverseOutsideSubtree() 
+    ? true : m_arg2.canTraverseOutsideSubtree();
+   }
+   
+  class Arg2Owner implements ExpressionOwner
+  {
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_arg2;
+    }
+
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(Function3Args.this);
+    	m_arg2 = exp;
+    }
+  }
+
+   
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callArgVisitors(XPathVisitor visitor)
+  {
+  	super.callArgVisitors(visitor);
+  	if(null != m_arg2)
+  		m_arg2.callVisitors(new Arg2Owner(), visitor);
+  }
+
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!super.deepEquals(expr))
+  		return false;
+  		
+  	if(null != m_arg2)
+  	{
+  		if(null == ((Function3Args)expr).m_arg2)
+  			return false;
+
+  		if(!m_arg2.deepEquals(((Function3Args)expr).m_arg2))
+  			return false;
+  	}
+  	else if (null != ((Function3Args)expr).m_arg2)
+  		return false;
+  		
+  	return true;
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/functions/FunctionDef1Arg.java b/src/main/java/org/apache/xpath/functions/FunctionDef1Arg.java
new file mode 100644
index 0000000..40e36cf
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FunctionDef1Arg.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FunctionDef1Arg.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XString;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Base class for functions that accept one argument that can be defaulted if
+ * not specified.
+ * @xsl.usage advanced
+ */
+public class FunctionDef1Arg extends FunctionOneArg
+{
+    static final long serialVersionUID = 2325189412814149264L;
+
+  /**
+   * Execute the first argument expression that is expected to return a
+   * nodeset.  If the argument is null, then return the current context node.
+   *
+   * @param xctxt Runtime XPath context.
+   *
+   * @return The first node of the executed nodeset, or the current context
+   *         node if the first argument is null.
+   *
+   * @throws javax.xml.transform.TransformerException if an error occurs while
+   *                                   executing the argument expression.
+   */
+  protected int getArg0AsNode(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    return (null == m_arg0)
+           ? xctxt.getCurrentNode() : m_arg0.asNode(xctxt);
+  }
+  
+  /**
+   * Tell if the expression is a nodeset expression.
+   * @return true if the expression can be represented as a nodeset.
+   */
+  public boolean Arg0IsNodesetExpr()
+  {
+    return (null == m_arg0) ? true : m_arg0.isNodesetExpr();
+  }
+
+  /**
+   * Execute the first argument expression that is expected to return a
+   * string.  If the argument is null, then get the string value from the
+   * current context node.
+   *
+   * @param xctxt Runtime XPath context.
+   *
+   * @return The string value of the first argument, or the string value of the
+   *         current context node if the first argument is null.
+   *
+   * @throws javax.xml.transform.TransformerException if an error occurs while
+   *                                   executing the argument expression.
+   */
+  protected XMLString getArg0AsString(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    if(null == m_arg0)
+    {
+      int currentNode = xctxt.getCurrentNode();
+      if(DTM.NULL == currentNode)
+        return XString.EMPTYSTRING;
+      else
+      {
+        DTM dtm = xctxt.getDTM(currentNode);
+        return dtm.getStringValue(currentNode);
+      }
+      
+    }
+    else
+      return m_arg0.execute(xctxt).xstr();   
+  }
+
+  /**
+   * Execute the first argument expression that is expected to return a
+   * number.  If the argument is null, then get the number value from the
+   * current context node.
+   *
+   * @param xctxt Runtime XPath context.
+   *
+   * @return The number value of the first argument, or the number value of the
+   *         current context node if the first argument is null.
+   *
+   * @throws javax.xml.transform.TransformerException if an error occurs while
+   *                                   executing the argument expression.
+   */
+  protected double getArg0AsNumber(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    if(null == m_arg0)
+    {
+      int currentNode = xctxt.getCurrentNode();
+      if(DTM.NULL == currentNode)
+        return 0;
+      else
+      {
+        DTM dtm = xctxt.getDTM(currentNode);
+        XMLString str = dtm.getStringValue(currentNode);
+        return str.toDouble();
+      }
+      
+    }
+    else
+      return m_arg0.execute(xctxt).num();
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException if the number of arguments is not 0 or 1.
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if (argNum > 1)
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_ZERO_OR_ONE, null)); //"0 or 1");
+  }
+
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside
+   * the current subtree.
+   *
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+  public boolean canTraverseOutsideSubtree()
+  {
+    return (null == m_arg0) ? false : super.canTraverseOutsideSubtree();
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FunctionMultiArgs.java b/src/main/java/org/apache/xpath/functions/FunctionMultiArgs.java
new file mode 100644
index 0000000..559e66f
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FunctionMultiArgs.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FunctionMultiArgs.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Base class for functions that accept an undetermined number of multiple
+ * arguments.
+ * @xsl.usage advanced
+ */
+public class FunctionMultiArgs extends Function3Args
+{
+    static final long serialVersionUID = 7117257746138417181L;
+
+  /** Argument expressions that are at index 3 or greater.
+   *  @serial */
+  Expression[] m_args;
+  
+  /**
+   * Return an expression array containing arguments at index 3 or greater.
+   *
+   * @return An array that contains the arguments at index 3 or greater.
+   */
+  public Expression[] getArgs()
+  {
+    return m_args;
+  }
+
+  /**
+   * Set an argument expression for a function.  This method is called by the
+   * XPath compiler.
+   *
+   * @param arg non-null expression that represents the argument.
+   * @param argNum The argument number index.
+   *
+   * @throws WrongNumberArgsException If a derived class determines that the
+   * number of arguments is incorrect.
+   */
+  public void setArg(Expression arg, int argNum)
+          throws WrongNumberArgsException
+  {
+
+    if (argNum < 3)
+      super.setArg(arg, argNum);
+    else
+    {
+      if (null == m_args)
+      {
+        m_args = new Expression[1];
+        m_args[0] = arg;
+      }
+      else
+      {
+
+        // Slow but space conservative.
+        Expression[] args = new Expression[m_args.length + 1];
+
+        System.arraycopy(m_args, 0, args, 0, m_args.length);
+
+        args[m_args.length] = arg;
+        m_args = args;
+      }
+      arg.exprSetParent(this);
+    }
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+    if(null != m_args)
+    {
+      for (int i = 0; i < m_args.length; i++) 
+      {
+        m_args[i].fixupVariables(vars, globalsSize);
+      }
+    }
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct.
+   *
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException{}
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.  This class supports an arbitrary
+   * number of arguments, so this method must never be called.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+    String fMsg = XSLMessages.createXPATHMessage(
+        XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
+        new Object[]{ "Programmer's assertion:  the method FunctionMultiArgs.reportWrongNumberArgs() should never be called." });
+
+    throw new RuntimeException(fMsg);
+  }
+
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside
+   * the current subtree.
+   *
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+  public boolean canTraverseOutsideSubtree()
+  {
+
+    if (super.canTraverseOutsideSubtree())
+      return true;
+    else
+    {
+      int n = m_args.length;
+
+      for (int i = 0; i < n; i++)
+      {
+        if (m_args[i].canTraverseOutsideSubtree())
+          return true;
+      }
+
+      return false;
+    }
+  }
+  
+  class ArgMultiOwner implements ExpressionOwner
+  {
+  	int m_argIndex;
+  	
+  	ArgMultiOwner(int index)
+  	{
+  		m_argIndex = index;
+  	}
+  	
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_args[m_argIndex];
+    }
+
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(FunctionMultiArgs.this);
+    	m_args[m_argIndex] = exp;
+    }
+  }
+
+   
+    /**
+     * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+     */
+    public void callArgVisitors(XPathVisitor visitor)
+    {
+      super.callArgVisitors(visitor);
+      if (null != m_args)
+      {
+        int n = m_args.length;
+        for (int i = 0; i < n; i++)
+        {
+          m_args[i].callVisitors(new ArgMultiOwner(i), visitor);
+        }
+      }
+    }
+    
+    /**
+     * @see Expression#deepEquals(Expression)
+     */
+    public boolean deepEquals(Expression expr)
+    {
+      if (!super.deepEquals(expr))
+            return false;
+
+      FunctionMultiArgs fma = (FunctionMultiArgs) expr;
+      if (null != m_args)
+      {
+        int n = m_args.length;
+        if ((null == fma) || (fma.m_args.length != n))
+              return false;
+
+        for (int i = 0; i < n; i++)
+        {
+          if (!m_args[i].deepEquals(fma.m_args[i]))
+                return false;
+        }
+
+      }
+      else if (null != fma.m_args)
+      {
+          return false;
+      }
+
+      return true;
+    }
+}
diff --git a/src/main/java/org/apache/xpath/functions/FunctionOneArg.java b/src/main/java/org/apache/xpath/functions/FunctionOneArg.java
new file mode 100644
index 0000000..40ea4ff
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/FunctionOneArg.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FunctionOneArg.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathVisitor;
+
+/**
+ * Base class for functions that accept one argument.
+ * @xsl.usage advanced
+ */
+public class FunctionOneArg extends Function implements ExpressionOwner
+{
+    static final long serialVersionUID = -5180174180765609758L;
+
+  /** The first argument passed to the function (at index 0).
+   *  @serial  */
+  Expression m_arg0;
+
+  /**
+   * Return the first argument passed to the function (at index 0).
+   *
+   * @return An expression that represents the first argument passed to the 
+   *         function.
+   */
+  public Expression getArg0()
+  {
+    return m_arg0;
+  }
+  
+  /**
+   * Set an argument expression for a function.  This method is called by the 
+   * XPath compiler.
+   *
+   * @param arg non-null expression that represents the argument.
+   * @param argNum The argument number index.
+   *
+   * @throws WrongNumberArgsException If the argNum parameter is greater than 0.
+   */
+  public void setArg(Expression arg, int argNum)
+          throws WrongNumberArgsException
+  {
+
+    if (0 == argNum)
+    {
+      m_arg0 = arg;
+      arg.exprSetParent(this);
+    }
+    else
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Check that the number of arguments passed to this function is correct. 
+   *
+   *
+   * @param argNum The number of arguments that is being passed to the function.
+   *
+   * @throws WrongNumberArgsException
+   */
+  public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+  {
+    if (argNum != 1)
+      reportWrongNumberArgs();
+  }
+
+  /**
+   * Constructs and throws a WrongNumberArgException with the appropriate
+   * message for this function object.
+   *
+   * @throws WrongNumberArgsException
+   */
+  protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+      throw new WrongNumberArgsException(XSLMessages.createXPATHMessage("one", null));
+  }
+  
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside 
+   * the current subtree.
+   * 
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+   public boolean canTraverseOutsideSubtree()
+   {
+    return m_arg0.canTraverseOutsideSubtree();
+   }
+   
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    if(null != m_arg0)
+      m_arg0.fixupVariables(vars, globalsSize);
+  }
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callArgVisitors(XPathVisitor visitor)
+  {
+  	if(null != m_arg0)
+  		m_arg0.callVisitors(this, visitor);
+  }
+
+
+  /**
+   * @see ExpressionOwner#getExpression()
+   */
+  public Expression getExpression()
+  {
+    return m_arg0;
+  }
+
+  /**
+   * @see ExpressionOwner#setExpression(Expression)
+   */
+  public void setExpression(Expression exp)
+  {
+  	exp.exprSetParent(this);
+  	m_arg0 = exp;
+  }
+  
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!super.deepEquals(expr))
+  		return false;
+  		
+  	if(null != m_arg0)
+  	{
+  		if(null == ((FunctionOneArg)expr).m_arg0)
+  			return false;
+  			
+  		if(!m_arg0.deepEquals(((FunctionOneArg)expr).m_arg0))
+  			return false;
+  	}
+  	else if(null != ((FunctionOneArg)expr).m_arg0)
+  		return false;
+
+  	return true;
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/functions/ObjectFactory.java b/src/main/java/org/apache/xpath/functions/ObjectFactory.java
new file mode 100755
index 0000000..96ac601
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/ObjectFactory.java
@@ -0,0 +1,661 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ObjectFactory.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+
+package org.apache.xpath.functions;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+
+import java.util.Properties;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ * This class is duplicated for each JAXP subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the JAXP
+ * API.
+ * <p>
+ * This code is designed to implement the JAXP 1.1 spec pluggability
+ * feature and is designed to run on JDK version 1.1 and
+ * later, and to compile on JDK 1.2 and onward.  
+ * The code also runs both as part of an unbundled jar file and
+ * when bundled as part of the JDK.
+ * <p>
+ * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
+ * class and modified to be used as a general utility for creating objects 
+ * dynamically.
+ *
+ * @version $Id: ObjectFactory.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+class ObjectFactory {
+
+    //
+    // Constants
+    //
+
+    // name of default properties file to look for in JDK's jre/lib directory
+    private static final String DEFAULT_PROPERTIES_FILENAME =
+                                                     "xalan.properties";
+
+    private static final String SERVICES_PATH = "META-INF/services/";
+
+    /** Set to true for debugging */
+    private static final boolean DEBUG = false;
+
+    /** cache the contents of the xalan.properties file.
+     *  Until an attempt has been made to read this file, this will
+     * be null; if the file does not exist or we encounter some other error
+     * during the read, this will be empty.
+     */
+    private static Properties fXalanProperties = null;
+
+    /***
+     * Cache the time stamp of the xalan.properties file so
+     * that we know if it's been modified and can invalidate
+     * the cache when necessary.
+     */
+    private static long fLastModified = -1;
+
+    //
+    // Public static methods
+    //
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, String fallbackClassName)
+        throws ConfigurationError {
+        return createObject(factoryId, null, fallbackClassName);
+    } // createObject(String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return instance of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Object createObject(String factoryId, 
+                                      String propertiesFilename,
+                                      String fallbackClassName)
+        throws ConfigurationError
+    {
+        Class factoryClass = lookUpFactoryClass(factoryId,
+                                                propertiesFilename,
+                                                fallbackClassName);
+
+        if (factoryClass == null) {
+            throw new ConfigurationError(
+                "Provider for " + factoryId + " cannot be found", null);
+        }
+
+        try{
+            Object instance = factoryClass.newInstance();
+            debugPrintln("created new instance of factory " + factoryId);
+            return instance;
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider for factory " + factoryId
+                    + " could not be instantiated: " + x, x);
+        }
+    } // createObject(String,String,String):Object
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object of factory, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId) 
+        throws ConfigurationError
+    {
+        return lookUpFactoryClass(factoryId, null, null);
+    } // lookUpFactoryClass(String):Class
+
+    /**
+     * Finds the implementation Class object in the specified order.  The
+     * specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return Class object that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static Class lookUpFactoryClass(String factoryId,
+                                           String propertiesFilename,
+                                           String fallbackClassName)
+        throws ConfigurationError
+    {
+        String factoryClassName = lookUpFactoryClassName(factoryId,
+                                                         propertiesFilename,
+                                                         fallbackClassName);
+        ClassLoader cl = findClassLoader();
+
+        if (factoryClassName == null) {
+            factoryClassName = fallbackClassName;
+        }
+
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(factoryClassName,
+                                                    cl,
+                                                    true);
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return providerClass;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + factoryClassName + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider "+factoryClassName+" could not be instantiated: "+x,
+                x);
+        }
+    } // lookUpFactoryClass(String,String,String):Class
+
+    /**
+     * Finds the name of the required implementation class in the specified
+     * order.  The specified order is the following:
+     * <ol>
+     *  <li>query the system property using <code>System.getProperty</code>
+     *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
+     *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
+     *  <li>use fallback classname
+     * </ol>
+     *
+     * @return name of class that provides factory service, never null
+     *
+     * @param factoryId             Name of the factory to find, same as
+     *                              a property name
+     * @param propertiesFilename The filename in the $java.home/lib directory
+     *                           of the properties file.  If none specified,
+     *                           ${java.home}/lib/xalan.properties will be used.
+     * @param fallbackClassName     Implementation class name, if nothing else
+     *                              is found.  Use null to mean no fallback.
+     *
+     * @exception ObjectFactory.ConfigurationError
+     */
+    static String lookUpFactoryClassName(String factoryId,
+                                                String propertiesFilename,
+                                                String fallbackClassName)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Use the system property first
+        try {
+            String systemProp = ss.getSystemProperty(factoryId);
+            if (systemProp != null) {
+                debugPrintln("found system property, value=" + systemProp);
+                return systemProp;
+            }
+        } catch (SecurityException se) {
+            // Ignore and continue w/ next location
+        }
+
+        // Try to read from propertiesFilename, or
+        // $java.home/lib/xalan.properties
+        String factoryClassName = null;
+        // no properties file name specified; use
+        // $JAVA_HOME/lib/xalan.properties:
+        if (propertiesFilename == null) {
+            File propertiesFile = null;
+            boolean propertiesFileExists = false;
+            try {
+                String javah = ss.getSystemProperty("java.home");
+                propertiesFilename = javah + File.separator +
+                    "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
+                propertiesFile = new File(propertiesFilename);
+                propertiesFileExists = ss.getFileExists(propertiesFile);
+            } catch (SecurityException e) {
+                // try again...
+                fLastModified = -1;
+                fXalanProperties = null;
+            }
+
+            synchronized (ObjectFactory.class) {
+                boolean loadProperties = false;
+                FileInputStream fis = null;
+                try {
+                    // file existed last time
+                    if(fLastModified >= 0) {
+                        if(propertiesFileExists &&
+                                (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
+                            loadProperties = true;
+                        } else {
+                            // file has stopped existing...
+                            if(!propertiesFileExists) {
+                                fLastModified = -1;
+                                fXalanProperties = null;
+                            } // else, file wasn't modified!
+                        }
+                    } else {
+                        // file has started to exist:
+                        if(propertiesFileExists) {
+                            loadProperties = true;
+                            fLastModified = ss.getLastModified(propertiesFile);
+                        } // else, nothing's changed
+                    }
+                    if(loadProperties) {
+                        // must never have attempted to read xalan.properties
+                        // before (or it's outdeated)
+                        fXalanProperties = new Properties();
+                        fis = ss.getFileInputStream(propertiesFile);
+                        fXalanProperties.load(fis);
+                    }
+	        } catch (Exception x) {
+	            fXalanProperties = null;
+	            fLastModified = -1;
+                    // assert(x instanceof FileNotFoundException
+	            //        || x instanceof SecurityException)
+	            // In both cases, ignore and continue w/ next location
+	        }
+                finally {
+                    // try to close the input stream if one was opened.
+                    if (fis != null) {
+                        try {
+                            fis.close();
+                        }
+                        // Ignore the exception.
+                        catch (IOException exc) {}
+                    }
+                }	            
+            }
+            if(fXalanProperties != null) {
+                factoryClassName = fXalanProperties.getProperty(factoryId);
+            }
+        } else {
+            FileInputStream fis = null;
+            try {
+                fis = ss.getFileInputStream(new File(propertiesFilename));
+                Properties props = new Properties();
+                props.load(fis);
+                factoryClassName = props.getProperty(factoryId);
+            } catch (Exception x) {
+                // assert(x instanceof FileNotFoundException
+                //        || x instanceof SecurityException)
+                // In both cases, ignore and continue w/ next location
+            }
+            finally {
+                // try to close the input stream if one was opened.
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    }
+                    // Ignore the exception.
+                    catch (IOException exc) {}
+                }
+            }               
+        }
+        if (factoryClassName != null) {
+            debugPrintln("found in " + propertiesFilename + ", value="
+                          + factoryClassName);
+            return factoryClassName;
+        }
+
+        // Try Jar Service Provider Mechanism
+        return findJarServiceProviderName(factoryId);
+    } // lookUpFactoryClass(String,String):String
+
+    //
+    // Private static methods
+    //
+
+    /** Prints a message to standard error if debugging is enabled. */
+    private static void debugPrintln(String msg) {
+        if (DEBUG) {
+            System.err.println("JAXP: " + msg);
+        }
+    } // debugPrintln(String)
+
+    /**
+     * Figure out which ClassLoader to use.  For JDK 1.2 and later use
+     * the context ClassLoader.
+     */
+    static ClassLoader findClassLoader()
+        throws ConfigurationError
+    { 
+        SecuritySupport ss = SecuritySupport.getInstance();
+
+        // Figure out which ClassLoader to use for loading the provider
+        // class.  If there is a Context ClassLoader then use it.
+        ClassLoader context = ss.getContextClassLoader();
+        ClassLoader system = ss.getSystemClassLoader();
+
+        ClassLoader chain = system;
+        while (true) {
+            if (context == chain) {
+                // Assert: we are on JDK 1.1 or we have no Context ClassLoader
+                // or any Context ClassLoader in chain of system classloader
+                // (including extension ClassLoader) so extend to widest
+                // ClassLoader (always look in system ClassLoader if Xalan
+                // is in boot/extension/system classpath and in current
+                // ClassLoader otherwise); normal classloaders delegate
+                // back to system ClassLoader first so this widening doesn't
+                // change the fact that context ClassLoader will be consulted
+                ClassLoader current = ObjectFactory.class.getClassLoader();
+
+                chain = system;
+                while (true) {
+                    if (current == chain) {
+                        // Assert: Current ClassLoader in chain of
+                        // boot/extension/system ClassLoaders
+                        return system;
+                    }
+                    if (chain == null) {
+                        break;
+                    }
+                    chain = ss.getParentClassLoader(chain);
+                }
+
+                // Assert: Current ClassLoader not in chain of
+                // boot/extension/system ClassLoaders
+                return current;
+            }
+
+            if (chain == null) {
+                // boot ClassLoader reached
+                break;
+            }
+
+            // Check for any extension ClassLoaders in chain up to
+            // boot ClassLoader
+            chain = ss.getParentClassLoader(chain);
+        };
+
+        // Assert: Context ClassLoader not in chain of
+        // boot/extension/system ClassLoaders
+        return context;
+    } // findClassLoader():ClassLoader
+
+    /**
+     * Create an instance of a class using the specified ClassLoader
+     */ 
+    static Object newInstance(String className, ClassLoader cl,
+                                      boolean doFallback)
+        throws ConfigurationError
+    {
+        // assert(className != null);
+        try{
+            Class providerClass = findProviderClass(className, cl, doFallback);
+            Object instance = providerClass.newInstance();
+            debugPrintln("created new instance of " + providerClass +
+                   " using ClassLoader: " + cl);
+            return instance;
+        } catch (ClassNotFoundException x) {
+            throw new ConfigurationError(
+                "Provider " + className + " not found", x);
+        } catch (Exception x) {
+            throw new ConfigurationError(
+                "Provider " + className + " could not be instantiated: " + x,
+                x);
+        }
+    }
+
+    /**
+     * Find a Class using the specified ClassLoader
+     */ 
+    static Class findProviderClass(String className, ClassLoader cl,
+                                           boolean doFallback)
+        throws ClassNotFoundException, ConfigurationError
+    {   
+        //throw security exception if the calling thread is not allowed to access the
+        //class. Restrict the access to the package classes as specified in java.security policy.
+        SecurityManager security = System.getSecurityManager();
+        try{
+                if (security != null){
+                    final int lastDot = className.lastIndexOf(".");
+                    String packageName = className;
+                    if (lastDot != -1) packageName = className.substring(0, lastDot);
+                    security.checkPackageAccess(packageName);
+                 }   
+        }catch(SecurityException e){
+            throw e;
+        }
+        
+        Class providerClass;
+        if (cl == null) {
+            // XXX Use the bootstrap ClassLoader.  There is no way to
+            // load a class using the bootstrap ClassLoader that works
+            // in both JDK 1.1 and Java 2.  However, this should still
+            // work b/c the following should be true:
+            //
+            // (cl == null) iff current ClassLoader == null
+            //
+            // Thus Class.forName(String) will use the current
+            // ClassLoader which will be the bootstrap ClassLoader.
+            providerClass = Class.forName(className);
+        } else {
+            try {
+                providerClass = cl.loadClass(className);
+            } catch (ClassNotFoundException x) {
+                if (doFallback) {
+                    // Fall back to current classloader
+                    ClassLoader current = ObjectFactory.class.getClassLoader();
+                    if (current == null) {
+                        providerClass = Class.forName(className);
+                    } else if (cl != current) {
+                        cl = current;
+                        providerClass = cl.loadClass(className);
+                    } else {
+                        throw x;
+                    }
+                } else {
+                    throw x;
+                }
+            }
+        }
+
+        return providerClass;
+    }
+
+    /**
+     * Find the name of service provider using Jar Service Provider Mechanism
+     *
+     * @return instance of provider class if found or null
+     */
+    private static String findJarServiceProviderName(String factoryId)
+    {
+        SecuritySupport ss = SecuritySupport.getInstance();
+        String serviceId = SERVICES_PATH + factoryId;
+        InputStream is = null;
+
+        // First try the Context ClassLoader
+        ClassLoader cl = findClassLoader();
+
+        is = ss.getResourceAsStream(cl, serviceId);
+
+        // If no provider found then try the current ClassLoader
+        if (is == null) {
+            ClassLoader current = ObjectFactory.class.getClassLoader();
+            if (cl != current) {
+                cl = current;
+                is = ss.getResourceAsStream(cl, serviceId);
+            }
+        }
+
+        if (is == null) {
+            // No provider found
+            return null;
+        }
+
+        debugPrintln("found jar resource=" + serviceId +
+               " using ClassLoader: " + cl);
+
+        // Read the service provider name in UTF-8 as specified in
+        // the jar spec.  Unfortunately this fails in Microsoft
+        // VJ++, which does not implement the UTF-8
+        // encoding. Theoretically, we should simply let it fail in
+        // that case, since the JVM is obviously broken if it
+        // doesn't support such a basic standard.  But since there
+        // are still some users attempting to use VJ++ for
+        // development, we have dropped in a fallback which makes a
+        // second attempt using the platform's default encoding. In
+        // VJ++ this is apparently ASCII, which is a subset of
+        // UTF-8... and since the strings we'll be reading here are
+        // also primarily limited to the 7-bit ASCII range (at
+        // least, in English versions), this should work well
+        // enough to keep us on the air until we're ready to
+        // officially decommit from VJ++. [Edited comment from
+        // jkesselm]
+        BufferedReader rd;
+        try {
+            rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+        } catch (java.io.UnsupportedEncodingException e) {
+            rd = new BufferedReader(new InputStreamReader(is));
+        }
+        
+        String factoryClassName = null;
+        try {
+            // XXX Does not handle all possible input as specified by the
+            // Jar Service Provider specification
+            factoryClassName = rd.readLine();
+        } catch (IOException x) {
+            // No provider found
+            return null;
+        }
+        finally {
+            try {
+                // try to close the reader.
+                rd.close();
+            }
+            // Ignore the exception.
+            catch (IOException exc) {}
+        }          
+
+        if (factoryClassName != null &&
+            ! "".equals(factoryClassName)) {
+            debugPrintln("found in resource, value="
+                   + factoryClassName);
+
+            // Note: here we do not want to fall back to the current
+            // ClassLoader because we want to avoid the case where the
+            // resource file was found using one ClassLoader and the
+            // provider class was instantiated using a different one.
+            return factoryClassName;
+        }
+
+        // No provider found
+        return null;
+    }
+
+    //
+    // Classes
+    //
+
+    /**
+     * A configuration error.
+     */
+    static class ConfigurationError 
+        extends Error {
+                static final long serialVersionUID = -5782303800588797207L;
+        //
+        // Data
+        //
+
+        /** Exception. */
+        private Exception exception;
+
+        //
+        // Constructors
+        //
+
+        /**
+         * Construct a new instance with the specified detail string and
+         * exception.
+         */
+        ConfigurationError(String msg, Exception x) {
+            super(msg);
+            this.exception = x;
+        } // <init>(String,Exception)
+
+        //
+        // Public methods
+        //
+
+        /** Returns the exception associated to this error. */
+        Exception getException() {
+            return exception;
+        } // getException():Exception
+
+    } // class ConfigurationError
+
+} // class ObjectFactory
diff --git a/src/main/java/org/apache/xpath/functions/SecuritySupport.java b/src/main/java/org/apache/xpath/functions/SecuritySupport.java
new file mode 100644
index 0000000..556a159
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/SecuritySupport.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SecuritySupport.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+
+package org.apache.xpath.functions;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Base class with security related methods that work on JDK 1.1.
+ */
+class SecuritySupport {
+
+    /*
+     * Make this of type Object so that the verifier won't try to
+     * prove its type, thus possibly trying to load the SecuritySupport12
+     * class.
+     */
+    private static final Object securitySupport;
+
+    static {
+	SecuritySupport ss = null;
+	try {
+	    Class c = Class.forName("java.security.AccessController");
+	    // if that worked, we're on 1.2.
+	    /*
+	    // don't reference the class explicitly so it doesn't
+	    // get dragged in accidentally.
+	    c = Class.forName("javax.mail.SecuritySupport12");
+	    Constructor cons = c.getConstructor(new Class[] { });
+	    ss = (SecuritySupport)cons.newInstance(new Object[] { });
+	    */
+	    /*
+	     * Unfortunately, we can't load the class using reflection
+	     * because the class is package private.  And the class has
+	     * to be package private so the APIs aren't exposed to other
+	     * code that could use them to circumvent security.  Thus,
+	     * we accept the risk that the direct reference might fail
+	     * on some JDK 1.1 JVMs, even though we would never execute
+	     * this code in such a case.  Sigh...
+	     */
+	    ss = new SecuritySupport12();
+	} catch (Exception ex) {
+	    // ignore it
+	} finally {
+	    if (ss == null)
+		ss = new SecuritySupport();
+	    securitySupport = ss;
+	}
+    }
+
+    /**
+     * Return an appropriate instance of this class, depending on whether
+     * we're on a JDK 1.1 or J2SE 1.2 (or later) system.
+     */
+    static SecuritySupport getInstance() {
+	return (SecuritySupport)securitySupport;
+    }
+
+    ClassLoader getContextClassLoader() {
+	return null;
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return null;
+    }
+
+    ClassLoader getParentClassLoader(ClassLoader cl) {
+        return null;
+    }
+
+    String getSystemProperty(String propName) {
+        return System.getProperty(propName);
+    }
+
+    FileInputStream getFileInputStream(File file)
+        throws FileNotFoundException
+    {
+        return new FileInputStream(file);
+    }
+
+    InputStream getResourceAsStream(ClassLoader cl, String name) {
+        InputStream ris;
+        if (cl == null) {
+            ris = ClassLoader.getSystemResourceAsStream(name);
+        } else {
+            ris = cl.getResourceAsStream(name);
+        }
+        return ris;
+    }
+    
+    boolean getFileExists(File f) {
+        return f.exists();
+    }
+    
+    long getLastModified(File f) {
+        return f.lastModified();
+    }    
+}
diff --git a/src/main/java/org/apache/xpath/functions/SecuritySupport12.java b/src/main/java/org/apache/xpath/functions/SecuritySupport12.java
new file mode 100644
index 0000000..e7edf5a
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/SecuritySupport12.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: SecuritySupport12.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+
+package org.apache.xpath.functions;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import java.util.Properties;
+
+/**
+ * This class is duplicated for each Xalan-Java subpackage so keep it in sync.
+ * It is package private and therefore is not exposed as part of the Xalan-Java
+ * API.
+ *
+ * Security related methods that only work on J2SE 1.2 and newer.
+ */
+class SecuritySupport12 extends SecuritySupport {
+
+    ClassLoader getContextClassLoader() {
+        return (ClassLoader)
+                AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                ClassLoader cl = null;
+                try {
+                    cl = Thread.currentThread().getContextClassLoader();
+                } catch (SecurityException ex) { }
+                return cl;
+            }
+        });
+    }
+
+    ClassLoader getSystemClassLoader() {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader cl = null;
+                    try {
+                        cl = ClassLoader.getSystemClassLoader();
+                    } catch (SecurityException ex) {}
+                    return cl;
+                }
+            });
+    }
+
+    ClassLoader getParentClassLoader(final ClassLoader cl) {
+        return (ClassLoader)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader parent = null;
+                    try {
+                        parent = cl.getParent();
+                    } catch (SecurityException ex) {}
+
+                    // eliminate loops in case of the boot
+                    // ClassLoader returning itself as a parent
+                    return (parent == cl) ? null : parent;
+                }
+            });
+    }
+
+    String getSystemProperty(final String propName) {
+        return (String)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return System.getProperty(propName);
+                }
+            });
+    }
+
+    FileInputStream getFileInputStream(final File file)
+        throws FileNotFoundException
+    {
+        try {
+            return (FileInputStream)
+                AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    public Object run() throws FileNotFoundException {
+                        return new FileInputStream(file);
+                    }
+                });
+        } catch (PrivilegedActionException e) {
+            throw (FileNotFoundException)e.getException();
+        }
+    }
+
+    InputStream getResourceAsStream(final ClassLoader cl,
+                                           final String name)
+    {
+        return (InputStream)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    InputStream ris;
+                    if (cl == null) {
+                        ris = ClassLoader.getSystemResourceAsStream(name);
+                    } else {
+                        ris = cl.getResourceAsStream(name);
+                    }
+                    return ris;
+                }
+            });
+    }
+    
+    boolean getFileExists(final File f) {
+    return ((Boolean)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Boolean(f.exists());
+                }
+            })).booleanValue();
+    }
+    
+    long getLastModified(final File f) {
+    return ((Long)
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new Long(f.lastModified());
+                }
+            })).longValue();
+    }
+        
+}
diff --git a/src/main/java/org/apache/xpath/functions/WrongNumberArgsException.java b/src/main/java/org/apache/xpath/functions/WrongNumberArgsException.java
new file mode 100644
index 0000000..6f11938
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/WrongNumberArgsException.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: WrongNumberArgsException.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.functions;
+
+/**
+ * An exception that is thrown if the wrong number of arguments to an exception 
+ * are specified by the stylesheet.
+ * @xsl.usage advanced
+ */
+public class WrongNumberArgsException extends Exception
+{
+    static final long serialVersionUID = -4551577097576242432L;
+
+  /**
+   * Constructor WrongNumberArgsException
+   *
+   * @param argsExpected Error message that tells the number of arguments that 
+   * were expected.
+   */
+  public WrongNumberArgsException(String argsExpected)
+  {
+
+    super(argsExpected);
+  }
+}
diff --git a/src/main/java/org/apache/xpath/functions/package.html b/src/main/java/org/apache/xpath/functions/package.html
new file mode 100644
index 0000000..362770a
--- /dev/null
+++ b/src/main/java/org/apache/xpath/functions/package.html
@@ -0,0 +1,29 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468655 2006-10-28 07:12:06Z minchau $ -->
+<html>
+  <title>XPath functions Package.</title>
+  <body>
+    <p>Implements XPath functions -- each function is derived from Function, 
+    FunctionOneArg, Function2Args, etc, with number-of-arguments checking being 
+    applied mainly at compile time -- this package only implements XPath functions, 
+    XSLT functions are found in the "templates" package.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xpath/jaxp/JAXPExtensionsProvider.java b/src/main/java/org/apache/xpath/jaxp/JAXPExtensionsProvider.java
new file mode 100644
index 0000000..fced9c7
--- /dev/null
+++ b/src/main/java/org/apache/xpath/jaxp/JAXPExtensionsProvider.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// $Id: JAXPExtensionsProvider.java 468655 2006-10-28 07:12:06Z minchau $
+
+package org.apache.xpath.jaxp;
+
+import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+
+import org.apache.xpath.ExtensionsProvider;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.apache.xalan.res.XSLMessages;
+
+import org.apache.xpath.functions.FuncExtFunction;
+import java.util.Vector;
+import java.util.ArrayList;
+import javax.xml.namespace.QName;
+
+/**
+ * 
+ * @author Ramesh Mandava ( ramesh.mandava@sun.com )
+ */
+public class JAXPExtensionsProvider implements ExtensionsProvider {
+    	
+    private final XPathFunctionResolver resolver;
+    private boolean extensionInvocationDisabled = false;
+	
+    public JAXPExtensionsProvider(XPathFunctionResolver resolver) {
+        this.resolver = resolver;
+        this.extensionInvocationDisabled = false;
+    }
+
+    public JAXPExtensionsProvider(XPathFunctionResolver resolver, 
+        boolean featureSecureProcessing ) {
+        this.resolver = resolver;
+        this.extensionInvocationDisabled = featureSecureProcessing;
+    }
+
+    /**
+     * Is the extension function available?
+     */
+
+    public boolean functionAvailable(String ns, String funcName)
+          throws javax.xml.transform.TransformerException {
+      try {
+        if ( funcName == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                new Object[] {"Function Name"} );
+            throw new NullPointerException ( fmsg ); 
+        }
+        //Find the XPathFunction corresponding to namespace and funcName
+        javax.xml.namespace.QName myQName = new QName( ns, funcName );
+        javax.xml.xpath.XPathFunction xpathFunction = 
+            resolver.resolveFunction ( myQName, 0 );
+        if (  xpathFunction == null ) {
+            return false;
+        }
+        return true;
+      } catch ( Exception e ) {
+        return false;
+      }
+       
+
+    }
+        
+
+    /**
+     * Is the extension element available?
+     */
+    public boolean elementAvailable(String ns, String elemName)
+          throws javax.xml.transform.TransformerException {
+        return false;
+    }
+
+    /**
+     * Execute the extension function.
+     */
+    public Object extFunction(String ns, String funcName, Vector argVec,
+        Object methodKey) throws javax.xml.transform.TransformerException {
+        try {
+
+            if ( funcName == null ) {
+                String fmsg = XSLMessages.createXPATHMessage(
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"Function Name"} );
+                throw new NullPointerException ( fmsg ); 
+            }
+            //Find the XPathFunction corresponding to namespace and funcName
+            javax.xml.namespace.QName myQName = new QName( ns, funcName );
+
+            // JAXP 1.3 spec says When XMLConstants.FEATURE_SECURE_PROCESSING 
+            // feature is set then invocation of extension functions need to
+            // throw XPathFunctionException
+            if ( extensionInvocationDisabled ) {
+                String fmsg = XSLMessages.createXPATHMessage(
+                    XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
+                    new Object[] { myQName.toString() } );
+                throw new XPathFunctionException ( fmsg );
+            }
+
+            // Assuming user is passing all the needed parameters ( including
+            // default values )
+            int arity = argVec.size();
+
+            javax.xml.xpath.XPathFunction xpathFunction = 
+                resolver.resolveFunction ( myQName, arity );
+
+            // not using methodKey
+            ArrayList argList = new ArrayList( arity);
+            for ( int i=0; i<arity; i++ ) {
+                Object argument = argVec.elementAt( i );
+                // XNodeSet object() returns NodeVector and not NodeList
+                // Explicitly getting NodeList by using nodelist()
+                if ( argument instanceof XNodeSet ) {
+                    argList.add ( i, ((XNodeSet)argument).nodelist() );
+                } else if ( argument instanceof XObject ) {
+                    Object passedArgument = ((XObject)argument).object();
+                    argList.add ( i, passedArgument );
+                } else {
+                    argList.add ( i, argument );
+                }
+            }
+
+            return ( xpathFunction.evaluate ( argList ));
+        } catch ( XPathFunctionException xfe ) {
+            // If we get XPathFunctionException then we want to terminate
+            // further execution by throwing WrappedRuntimeException 
+            throw new org.apache.xml.utils.WrappedRuntimeException ( xfe );
+        } catch ( Exception e ) {
+            throw new javax.xml.transform.TransformerException ( e );
+        }
+    
+    }
+
+    /**
+     * Execute the extension function.
+     */
+    public Object extFunction(FuncExtFunction extFunction,
+                              Vector argVec)
+        throws javax.xml.transform.TransformerException {
+        try {
+            String namespace = extFunction.getNamespace();
+            String functionName = extFunction.getFunctionName();
+            int arity = extFunction.getArgCount();
+            javax.xml.namespace.QName myQName = 
+                new javax.xml.namespace.QName( namespace, functionName );
+
+            // JAXP 1.3 spec says  When XMLConstants.FEATURE_SECURE_PROCESSING
+            // feature is set then invocation of extension functions need to
+            // throw XPathFunctionException
+            if ( extensionInvocationDisabled ) {
+                String fmsg = XSLMessages.createXPATHMessage(
+                    XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,                    new Object[] { myQName.toString() } );
+                throw new XPathFunctionException ( fmsg );
+            }
+
+            XPathFunction xpathFunction = 
+                resolver.resolveFunction( myQName, arity );
+
+            ArrayList argList = new ArrayList( arity);
+            for ( int i=0; i<arity; i++ ) {
+                Object argument = argVec.elementAt( i );
+                // XNodeSet object() returns NodeVector and not NodeList
+                // Explicitly getting NodeList by using nodelist()
+                if ( argument instanceof XNodeSet ) {
+                    argList.add ( i, ((XNodeSet)argument).nodelist() );
+                } else if ( argument instanceof XObject ) {
+                    Object passedArgument = ((XObject)argument).object();
+                    argList.add ( i, passedArgument );
+                } else {
+                    argList.add ( i, argument );
+                }
+            }
+       
+            return ( xpathFunction.evaluate ( argList ));
+
+        } catch ( XPathFunctionException xfe ) {
+            // If we get XPathFunctionException then we want to terminate 
+            // further execution by throwing WrappedRuntimeException
+            throw new org.apache.xml.utils.WrappedRuntimeException ( xfe );
+        } catch ( Exception e ) {
+            throw new javax.xml.transform.TransformerException ( e );
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/xpath/jaxp/JAXPPrefixResolver.java b/src/main/java/org/apache/xpath/jaxp/JAXPPrefixResolver.java
new file mode 100644
index 0000000..9208dc1
--- /dev/null
+++ b/src/main/java/org/apache/xpath/jaxp/JAXPPrefixResolver.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// $Id: JAXPPrefixResolver.java 468655 2006-10-28 07:12:06Z minchau $
+
+package org.apache.xpath.jaxp;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NamedNodeMap;
+import org.apache.xml.utils.PrefixResolver;
+
+import javax.xml.namespace.NamespaceContext;
+
+/**
+ * <meta name="usage" content="general"/>
+ * This class implements a Default PrefixResolver which
+ * can be used to perform prefix-to-namespace lookup
+ * for the XPath object.
+ * This class delegates the resolution to the passed NamespaceContext
+ */
+public class JAXPPrefixResolver implements PrefixResolver
+{
+
+    private NamespaceContext namespaceContext;
+    
+
+    public JAXPPrefixResolver ( NamespaceContext nsContext ) {
+        this.namespaceContext = nsContext;
+    } 
+
+
+    public String getNamespaceForPrefix( String prefix ) {
+        return namespaceContext.getNamespaceURI( prefix );
+    }
+
+    /**
+     * Return the base identifier.
+     *
+     * @return null
+     */
+    public String getBaseIdentifier() {
+        return null;
+    }
+
+    /**
+     * @see PrefixResolver#handlesNullPrefixes() 
+     */
+    public boolean handlesNullPrefixes() {
+        return false;
+    }
+
+
+    /**
+     * The URI for the XML namespace.
+     * (Duplicate of that found in org.apache.xpath.XPathContext). 
+     */
+     
+    public static final String S_XMLNAMESPACEURI =
+        "http://www.w3.org/XML/1998/namespace";
+
+
+    /**
+     * Given a prefix and a Context Node, get the corresponding namespace.
+     * Warning: This will not work correctly if namespaceContext
+     * is an attribute node.
+     * @param prefix Prefix to resolve.
+     * @param namespaceContext Node from which to start searching for a
+     * xmlns attribute that binds a prefix to a namespace.
+     * @return Namespace that prefix resolves to, or null if prefix
+     * is not bound.
+     */
+    public String getNamespaceForPrefix(String prefix,
+                                      org.w3c.dom.Node namespaceContext) {
+        Node parent = namespaceContext;
+        String namespace = null;
+
+        if (prefix.equals("xml")) {
+            namespace = S_XMLNAMESPACEURI;
+        } else {
+            int type;
+
+            while ((null != parent) && (null == namespace)
+                && (((type = parent.getNodeType()) == Node.ELEMENT_NODE)
+                    || (type == Node.ENTITY_REFERENCE_NODE))) {
+
+                if (type == Node.ELEMENT_NODE) {
+                    NamedNodeMap nnm = parent.getAttributes();
+
+                    for (int i = 0; i < nnm.getLength(); i++) {
+                        Node attr = nnm.item(i);
+                        String aname = attr.getNodeName();
+                        boolean isPrefix = aname.startsWith("xmlns:");
+
+                        if (isPrefix || aname.equals("xmlns")) {
+                            int index = aname.indexOf(':');
+                            String p =isPrefix ?aname.substring(index + 1) :"";
+
+                            if (p.equals(prefix)) {
+                                namespace = attr.getNodeValue();
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                parent = parent.getParentNode();
+            }
+        }
+        return namespace;
+    }
+
+}
+
diff --git a/src/main/java/org/apache/xpath/jaxp/JAXPVariableStack.java b/src/main/java/org/apache/xpath/jaxp/JAXPVariableStack.java
new file mode 100644
index 0000000..ec076ea
--- /dev/null
+++ b/src/main/java/org/apache/xpath/jaxp/JAXPVariableStack.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// $Id: JAXPVariableStack.java 524815 2007-04-02 15:52:15Z zongaro $
+
+package org.apache.xpath.jaxp;
+
+import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPathVariableResolver;
+
+import org.apache.xml.utils.QName;
+import org.apache.xpath.VariableStack;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XObject;
+
+import org.apache.xpath.res.XPATHErrorResources;
+import org.apache.xalan.res.XSLMessages;
+
+
+/**
+ * Overrides {@link VariableStack} and delegates the call to
+ * {@link javax.xml.xpath.XPathVariableResolver}.
+ * 
+ * @author Ramesh Mandava ( ramesh.mandava@sun.com )
+ */
+public class JAXPVariableStack extends VariableStack {
+    	
+    private final XPathVariableResolver resolver;
+	
+    public JAXPVariableStack(XPathVariableResolver resolver) {
+        super(2);
+        this.resolver = resolver;
+    }
+	
+    public XObject getVariableOrParam(XPathContext xctxt, QName qname)
+        throws TransformerException,IllegalArgumentException {
+        if ( qname == null ) {
+            //JAXP 1.3 spec says that if variable name is null then 
+            // we need to through IllegalArgumentException
+            String fmsg = XSLMessages.createXPATHMessage( 
+                XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                new Object[] {"Variable qname"} );
+            throw new IllegalArgumentException( fmsg );
+        }
+	javax.xml.namespace.QName name =
+	    new javax.xml.namespace.QName(
+                qname.getNamespace(),
+                qname.getLocalPart());
+        Object varValue = resolver.resolveVariable( name );		
+        if ( varValue == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                XPATHErrorResources.ER_RESOLVE_VARIABLE_RETURNS_NULL,
+                new Object[] { name.toString()} );
+            throw new TransformerException( fmsg );
+        } 
+        return XObject.create( varValue, xctxt );
+    }
+
+}
diff --git a/src/main/java/org/apache/xpath/jaxp/XPathExpressionImpl.java b/src/main/java/org/apache/xpath/jaxp/XPathExpressionImpl.java
new file mode 100644
index 0000000..ba6c1b4
--- /dev/null
+++ b/src/main/java/org/apache/xpath/jaxp/XPathExpressionImpl.java
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// $Id: XPathExpressionImpl.java 524813 2007-04-02 15:52:07Z zongaro $
+
+package org.apache.xpath.jaxp;
+
+import org.apache.xpath.*;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xpath.objects.XObject;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.apache.xalan.res.XSLMessages;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathVariableResolver;
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.Document;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.traversal.NodeIterator;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+
+import org.xml.sax.InputSource;
+
+/**
+ * The XPathExpression interface encapsulates a (compiled) XPath expression.
+ *
+ * @version $Revision: 524813 $
+ * @author  Ramesh Mandava
+ */
+public class XPathExpressionImpl  implements javax.xml.xpath.XPathExpression{
+
+    private XPathFunctionResolver functionResolver;
+    private XPathVariableResolver variableResolver;
+    private JAXPPrefixResolver prefixResolver;
+    private org.apache.xpath.XPath xpath;
+
+    // By default Extension Functions are allowed in XPath Expressions. If
+    // Secure Processing Feature is set on XPathFactory then the invocation of
+    // extensions function need to throw XPathFunctionException
+    private boolean featureSecureProcessing = false;
+
+    /** Protected constructor to prevent direct instantiation; use compile()
+     * from the context.
+     */
+    protected XPathExpressionImpl() { };
+
+    protected XPathExpressionImpl(org.apache.xpath.XPath xpath, 
+            JAXPPrefixResolver prefixResolver, 
+            XPathFunctionResolver functionResolver,
+            XPathVariableResolver variableResolver ) { 
+        this.xpath = xpath;
+        this.prefixResolver = prefixResolver;
+        this.functionResolver = functionResolver;
+        this.variableResolver = variableResolver;
+        this.featureSecureProcessing = false;
+    };
+
+    protected XPathExpressionImpl(org.apache.xpath.XPath xpath,
+            JAXPPrefixResolver prefixResolver,
+            XPathFunctionResolver functionResolver,
+            XPathVariableResolver variableResolver,
+            boolean featureSecureProcessing ) { 
+        this.xpath = xpath;
+        this.prefixResolver = prefixResolver;
+        this.functionResolver = functionResolver;
+        this.variableResolver = variableResolver;
+        this.featureSecureProcessing = featureSecureProcessing;
+    };
+
+    public void setXPath (org.apache.xpath.XPath xpath ) {
+        this.xpath = xpath;
+    }  
+
+    public Object eval(Object item, QName returnType)
+            throws javax.xml.transform.TransformerException {
+        XObject resultObject = eval ( item );
+        return getResultAsType( resultObject, returnType );
+    }
+    
+    private XObject eval ( Object contextItem )
+            throws javax.xml.transform.TransformerException {
+        org.apache.xpath.XPathContext xpathSupport = null;
+
+        // Create an XPathContext that doesn't support pushing and popping of
+        // variable resolution scopes.  Sufficient for simple XPath 1.0
+        // expressions.
+        if ( functionResolver != null ) {
+            JAXPExtensionsProvider jep = new JAXPExtensionsProvider(
+                    functionResolver, featureSecureProcessing );
+            xpathSupport = new org.apache.xpath.XPathContext(jep, false);
+        } else {
+            xpathSupport = new org.apache.xpath.XPathContext(false);
+        }
+
+        xpathSupport.setVarStack(new JAXPVariableStack(variableResolver));
+        XObject xobj = null;
+          
+        Node contextNode = (Node)contextItem;
+        // We always need to have a ContextNode with Xalan XPath implementation
+        // To allow simple expression evaluation like 1+1 we are setting 
+        // dummy Document as Context Node
+        if ( contextNode == null ) {
+              contextNode = getDummyDocument();
+        } 
+
+        xobj = xpath.execute(xpathSupport, contextNode, prefixResolver );
+        return xobj;
+    }
+
+
+    /**
+     * <p>Evaluate the compiled XPath expression in the specified context and
+     *  return the result as the specified type.</p>
+     *
+     * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
+     * for context item evaluation,
+     * variable, function and QName resolution and return type conversion.</p>
+     *
+     * <p>If <code>returnType</code> is not one of the types defined 
+     * in {@link XPathConstants},
+     * then an <code>IllegalArgumentException</code> is thrown.</p>
+     *
+     * <p>If a <code>null</code> value is provided for
+     * <code>item</code>, an empty document will be used for the
+     * context.
+     * If <code>returnType</code> is <code>null</code>, then a 
+     * <code>NullPointerException</code> is thrown.</p>
+     *
+     * @param item The starting context (node or node list, for example).
+     * @param returnType The desired return type.
+     *
+     * @return The <code>Object</code> that is the result of evaluating the
+     * expression and converting the result to
+     *   <code>returnType</code>.
+     *
+     * @throws XPathExpressionException If the expression cannot be evaluated.
+     * @throws IllegalArgumentException If <code>returnType</code> is not one
+     * of the types defined in {@link XPathConstants}.
+     * @throws NullPointerException If  <code>returnType</code> is
+     * <code>null</code>.
+     */
+    public Object evaluate(Object item, QName returnType)
+        throws XPathExpressionException {
+        //Validating parameters to enforce constraints defined by JAXP spec
+        if ( returnType == null ) {
+           //Throwing NullPointerException as defined in spec
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"returnType"} );
+            throw new NullPointerException( fmsg );
+        }
+        // Checking if requested returnType is supported. returnType need to be
+        // defined in XPathConstants 
+        if ( !isSupported ( returnType ) ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
+                    new Object[] { returnType.toString() } );
+            throw new IllegalArgumentException ( fmsg );
+        }
+        try { 
+            return eval( item, returnType);
+        } catch ( java.lang.NullPointerException npe ) {
+            // If VariableResolver returns null Or if we get 
+            // NullPointerException at this stage for some other reason
+            // then we have to reurn XPathException
+            throw new XPathExpressionException ( npe );
+        } catch ( javax.xml.transform.TransformerException te ) {
+            Throwable nestedException = te.getException();
+            if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) {
+                throw (javax.xml.xpath.XPathFunctionException)nestedException;
+            } else {
+                // For any other exceptions we need to throw
+                // XPathExpressionException ( as per spec )
+                throw new XPathExpressionException( te);
+            }
+        }
+
+    }
+    
+    /**
+     * <p>Evaluate the compiled XPath expression in the specified context and
+     * return the result as a <code>String</code>.</p>
+     *
+     * <p>This method calls {@link #evaluate(Object item, QName returnType)}
+     * with a <code>returnType</code> of
+     * {@link XPathConstants#STRING}.</p>
+     *
+     * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
+     *  for context item evaluation,
+     * variable, function and QName resolution and return type conversion.</p>
+     *
+     * <p>If a <code>null</code> value is provided for
+     * <code>item</code>, an empty document will be used for the
+     * context.
+     *
+     * @param item The starting context (node or node list, for example).
+     *
+     * @return The <code>String</code> that is the result of evaluating the
+     * expression and converting the result to a
+     *   <code>String</code>.
+     *
+     * @throws XPathExpressionException If the expression cannot be evaluated.
+     */
+    public String evaluate(Object item) 
+        throws XPathExpressionException {
+        return (String)this.evaluate( item, XPathConstants.STRING );
+    }
+
+
+
+    static DocumentBuilderFactory dbf = null;
+    static DocumentBuilder db = null;
+    static Document d = null;
+
+    /**
+     * <p>Evaluate the compiled XPath expression in the context of the 
+     * specified <code>InputSource</code> and return the result as the
+     *  specified type.</p>
+     *
+     * <p>This method builds a data model for the {@link InputSource} and calls
+     * {@link #evaluate(Object item, QName returnType)} on the resulting 
+     * document object.</p>
+     *
+     * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
+     *  for context item evaluation,
+     * variable, function and QName resolution and return type conversion.</p>
+     *
+     * <p>If <code>returnType</code> is not one of the types defined in 
+     * {@link XPathConstants},
+     * then an <code>IllegalArgumentException</code> is thrown.</p>
+     *
+     *<p>If <code>source</code> or <code>returnType</code> is <code>null</code>,
+     * then a <code>NullPointerException</code> is thrown.</p>
+     *
+     * @param source The <code>InputSource</code> of the document to evaluate
+     * over.
+     * @param returnType The desired return type.
+     *
+     * @return The <code>Object</code> that is the result of evaluating the
+     * expression and converting the result to
+     *   <code>returnType</code>.
+     *
+     * @throws XPathExpressionException If the expression cannot be evaluated.
+     * @throws IllegalArgumentException If <code>returnType</code> is not one
+     * of the types defined in {@link XPathConstants}.
+     * @throws NullPointerException If  <code>source</code> or 
+     * <code>returnType</code> is <code>null</code>.
+     */
+    public Object evaluate(InputSource source, QName returnType)
+        throws XPathExpressionException {
+        if ( ( source == null ) || ( returnType == null ) ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL,
+                    null );
+            throw new NullPointerException ( fmsg );
+        }
+        // Checking if requested returnType is supported. returnType need to be
+        // defined in XPathConstants 
+        if ( !isSupported ( returnType ) ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
+                    new Object[] { returnType.toString() } );
+            throw new IllegalArgumentException ( fmsg );
+        }
+        try {
+            if ( dbf == null ) {
+                dbf = DocumentBuilderFactory.newInstance();
+                dbf.setNamespaceAware( true );
+                dbf.setValidating( false );
+            }
+            db = dbf.newDocumentBuilder();
+            Document document = db.parse( source );
+            return eval(  document, returnType );
+        } catch ( Exception e ) {
+            throw new XPathExpressionException ( e );
+        }
+    }
+
+    /**
+     * <p>Evaluate the compiled XPath expression in the context of the specified <code>InputSource</code> and return the result as a
+     * <code>String</code>.</p>
+     *
+     * <p>This method calls {@link #evaluate(InputSource source, QName returnType)} with a <code>returnType</code> of
+     * {@link XPathConstants#STRING}.</p>
+     *
+     * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
+     * for context item evaluation,
+     * variable, function and QName resolution and return type conversion.</p>
+     *
+     * <p>If <code>source</code> is <code>null</code>, then a <code>NullPointerException</code> is thrown.</p>
+     *
+     * @param source The <code>InputSource</code> of the document to evaluate over.
+     *
+     * @return The <code>String</code> that is the result of evaluating the expression and converting the result to a
+     *   <code>String</code>.
+     *
+     * @throws XPathExpressionException If the expression cannot be evaluated.
+     * @throws NullPointerException If  <code>source</code> is <code>null</code>.
+     */
+    public String evaluate(InputSource source)
+        throws XPathExpressionException {
+        return (String)this.evaluate( source, XPathConstants.STRING );
+    }
+
+    private boolean isSupported( QName returnType ) {
+        // XPathConstants.STRING
+        if ( ( returnType.equals( XPathConstants.STRING ) ) ||
+             ( returnType.equals( XPathConstants.NUMBER ) ) ||
+             ( returnType.equals( XPathConstants.BOOLEAN ) ) ||
+             ( returnType.equals( XPathConstants.NODE ) ) ||
+             ( returnType.equals( XPathConstants.NODESET ) )  ) {
+    
+            return true;
+        }
+        return false;
+     }
+
+     private Object getResultAsType( XObject resultObject, QName returnType )
+        throws javax.xml.transform.TransformerException {
+        // XPathConstants.STRING
+        if ( returnType.equals( XPathConstants.STRING ) ) {
+            return resultObject.str();
+        }
+        // XPathConstants.NUMBER
+        if ( returnType.equals( XPathConstants.NUMBER ) ) {
+            return new Double ( resultObject.num());
+        }
+        // XPathConstants.BOOLEAN
+        if ( returnType.equals( XPathConstants.BOOLEAN ) ) {
+            return new Boolean( resultObject.bool());
+        }
+        // XPathConstants.NODESET ---ORdered, UNOrdered???
+        if ( returnType.equals( XPathConstants.NODESET ) ) {
+            return resultObject.nodelist();
+        }
+        // XPathConstants.NODE
+        if ( returnType.equals( XPathConstants.NODE ) ) {
+            NodeIterator ni = resultObject.nodeset();
+            //Return the first node, or null
+            return ni.nextNode();
+        }
+        // If isSupported check is already done then the execution path 
+        // shouldn't come here. Being defensive
+        String fmsg = XSLMessages.createXPATHMessage( 
+                XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
+                new Object[] { returnType.toString()});
+        throw new IllegalArgumentException ( fmsg );
+    }
+
+
+    private static Document getDummyDocument( ) {
+        try {
+            if ( dbf == null ) {
+                dbf = DocumentBuilderFactory.newInstance();
+                dbf.setNamespaceAware( true );
+                dbf.setValidating( false );
+            }
+            db = dbf.newDocumentBuilder();
+
+            DOMImplementation dim = db.getDOMImplementation();
+            d = dim.createDocument("http://java.sun.com/jaxp/xpath",
+                "dummyroot", null);
+            return d;
+        } catch ( Exception e ) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+
+
+}
diff --git a/src/main/java/org/apache/xpath/jaxp/XPathFactoryImpl.java b/src/main/java/org/apache/xpath/jaxp/XPathFactoryImpl.java
new file mode 100644
index 0000000..669673e
--- /dev/null
+++ b/src/main/java/org/apache/xpath/jaxp/XPathFactoryImpl.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// $Id: XPathFactoryImpl.java 468655 2006-10-28 07:12:06Z minchau $
+
+package org.apache.xpath.jaxp;
+
+import org.apache.xpath.res.XPATHErrorResources;
+import org.apache.xalan.res.XSLMessages;
+
+import javax.xml.XMLConstants;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathFactoryConfigurationException;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathVariableResolver;
+
+/**
+ * The XPathFactory builds XPaths.
+ *
+ * @version $Revision: 468655 $
+ * @author  Ramesh Mandava
+ */
+public  class XPathFactoryImpl extends XPathFactory {
+	
+	/**
+	 * <p>Name of class as a constant to use for debugging.</p>
+	 */
+	private static final String CLASS_NAME = "XPathFactoryImpl";
+	
+	/**
+	 *<p>XPathFunctionResolver for this XPathFactory and created XPaths.</p>
+	 */
+	private XPathFunctionResolver xPathFunctionResolver = null;
+	
+	/**
+	 * <p>XPathVariableResolver for this XPathFactory and created XPaths</p>
+	 */
+	private XPathVariableResolver xPathVariableResolver = null;
+
+	/**
+	 * <p>State of secure processing feature.</p>
+	 */
+	private boolean featureSecureProcessing = false;
+		
+	/**
+	 * <p>Is specified object model supported by this 
+         * <code>XPathFactory</code>?</p>
+	 * 
+	 * @param objectModel Specifies the object model which the returned
+         * <code>XPathFactory</code> will understand.
+	 *  
+	 * @return <code>true</code> if <code>XPathFactory</code> supports 
+         * <code>objectModel</code>, else <code>false</code>.
+	 * 
+	 * @throws NullPointerException If <code>objectModel</code> is <code>null</code>.
+	 * @throws IllegalArgumentException If <code>objectModel.length() == 0</code>.
+	 */
+	public boolean isObjectModelSupported(String objectModel) {
+		
+            if (objectModel == null) {
+                String fmsg = XSLMessages.createXPATHMessage(
+                        XPATHErrorResources.ER_OBJECT_MODEL_NULL,
+                        new Object[] { this.getClass().getName() } );
+
+                throw new NullPointerException( fmsg );
+            }
+		
+            if (objectModel.length() == 0) {
+                String fmsg = XSLMessages.createXPATHMessage(
+                        XPATHErrorResources.ER_OBJECT_MODEL_EMPTY,
+                        new Object[] { this.getClass().getName() } );
+                throw new IllegalArgumentException( fmsg );
+            }
+		
+	    // know how to support default object model, W3C DOM
+            if (objectModel.equals(XPathFactory.DEFAULT_OBJECT_MODEL_URI)) {
+                return true;
+            }
+		
+            // don't know how to support anything else
+            return false;
+	}
+
+        /**
+         * <p>Returns a new <code>XPath</code> object using the underlying
+         * object model determined when the factory was instantiated.</p>
+	 * 
+	 * @return New <code>XPath</code>
+	 */
+	public javax.xml.xpath.XPath newXPath() {
+	    return new org.apache.xpath.jaxp.XPathImpl(
+                    xPathVariableResolver, xPathFunctionResolver,
+                    featureSecureProcessing );
+	}
+	    
+	/**
+	 * <p>Set a feature for this <code>XPathFactory</code> and 
+         * <code>XPath</code>s created by this factory.</p>
+	 * 
+	 * <p>
+	 * Feature names are fully qualified {@link java.net.URI}s.
+	 * Implementations may define their own features.
+	 * An {@link XPathFactoryConfigurationException} is thrown if this
+         * <code>XPathFactory</code> or the <code>XPath</code>s
+	 *  it creates cannot support the feature.
+	 * It is possible for an <code>XPathFactory</code> to expose a feature
+         * value but be unable to change its state.
+	 * </p>
+	 * 
+	 * <p>See {@link javax.xml.xpath.XPathFactory} for full documentation
+         * of specific features.</p>
+	 * 
+	 * @param name Feature name.
+	 * @param value Is feature state <code>true</code> or <code>false</code>.
+	 *  
+	 * @throws XPathFactoryConfigurationException if this 
+         * <code>XPathFactory</code> or the <code>XPath</code>s
+	 *   it creates cannot support this feature.
+         * @throws NullPointerException if <code>name</code> is 
+         * <code>null</code>.
+	 */
+	public void setFeature(String name, boolean value)
+		throws XPathFactoryConfigurationException {
+			
+            // feature name cannot be null
+            if (name == null) {
+                String fmsg = XSLMessages.createXPATHMessage(
+                        XPATHErrorResources.ER_FEATURE_NAME_NULL,
+                        new Object[] { CLASS_NAME, new Boolean( value) } );
+                throw new NullPointerException( fmsg );
+             }
+		
+            // secure processing?
+            if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
+
+                featureSecureProcessing = value;
+						
+                // all done processing feature
+                return;
+            }
+		
+            // unknown feature
+            String fmsg = XSLMessages.createXPATHMessage(
+                    XPATHErrorResources.ER_FEATURE_UNKNOWN,
+                    new Object[] { name, CLASS_NAME, new Boolean(value) } );
+            throw new XPathFactoryConfigurationException( fmsg );
+	}
+
+	/**
+	 * <p>Get the state of the named feature.</p>
+	 * 
+	 * <p>
+	 * Feature names are fully qualified {@link java.net.URI}s.
+	 * Implementations may define their own features.
+	 * An {@link XPathFactoryConfigurationException} is thrown if this
+         * <code>XPathFactory</code> or the <code>XPath</code>s
+	 * it creates cannot support the feature.
+	 * It is possible for an <code>XPathFactory</code> to expose a feature 
+         * value but be unable to change its state.
+	 * </p>
+	 * 
+	 * @param name Feature name.
+	 * 
+	 * @return State of the named feature.
+	 * 
+	 * @throws XPathFactoryConfigurationException if this 
+         * <code>XPathFactory</code> or the <code>XPath</code>s
+	 *   it creates cannot support this feature.
+         * @throws NullPointerException if <code>name</code> is 
+         * <code>null</code>.
+	 */
+	public boolean getFeature(String name)
+		throws XPathFactoryConfigurationException {
+
+            // feature name cannot be null
+            if (name == null) {
+                String fmsg = XSLMessages.createXPATHMessage(
+                        XPATHErrorResources.ER_GETTING_NULL_FEATURE,
+                        new Object[] { CLASS_NAME } );
+                throw new NullPointerException( fmsg );
+            }
+		
+            // secure processing?
+            if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
+                return featureSecureProcessing;
+            }
+		
+            // unknown feature
+            String fmsg = XSLMessages.createXPATHMessage(
+                    XPATHErrorResources.ER_GETTING_UNKNOWN_FEATURE,
+                    new Object[] { name, CLASS_NAME } );
+
+            throw new XPathFactoryConfigurationException( fmsg );
+        }
+		
+	/**
+         * <p>Establish a default function resolver.</p>
+         * 
+	 * <p>Any <code>XPath</code> objects constructed from this factory will use
+	 * the specified resolver by default.</p>
+	 *
+	 * <p>A <code>NullPointerException</code> is thrown if 
+         * <code>resolver</code> is <code>null</code>.</p>
+         * 
+	 * @param resolver XPath function resolver.
+	 * 
+	 * @throws NullPointerException If <code>resolver</code> is 
+         * <code>null</code>.
+	 */
+        public void setXPathFunctionResolver(XPathFunctionResolver resolver) {
+			
+            // resolver cannot be null
+            if (resolver == null) {
+                String fmsg = XSLMessages.createXPATHMessage(
+                        XPATHErrorResources.ER_NULL_XPATH_FUNCTION_RESOLVER,
+                        new Object[] {  CLASS_NAME } );
+                throw new NullPointerException( fmsg );
+            }
+			
+            xPathFunctionResolver = resolver;
+        }
+		
+	/**
+	 * <p>Establish a default variable resolver.</p>
+	 *
+	 * <p>Any <code>XPath</code> objects constructed from this factory will use
+	 * the specified resolver by default.</p>
+	 * 
+	 * <p>A <code>NullPointerException</code> is thrown if <code>resolver</code> is <code>null</code>.</p>
+	 * 
+	 * @param resolver Variable resolver.
+	 * 
+	 *  @throws NullPointerException If <code>resolver</code> is 
+         * <code>null</code>.
+	 */
+	public void setXPathVariableResolver(XPathVariableResolver resolver) {
+
+		// resolver cannot be null
+		if (resolver == null) {
+                    String fmsg = XSLMessages.createXPATHMessage(
+                            XPATHErrorResources.ER_NULL_XPATH_VARIABLE_RESOLVER,
+                            new Object[] {  CLASS_NAME } );
+		    throw new NullPointerException( fmsg );
+		}
+			
+		xPathVariableResolver = resolver;
+	}
+}
+
+
+
diff --git a/src/main/java/org/apache/xpath/jaxp/XPathImpl.java b/src/main/java/org/apache/xpath/jaxp/XPathImpl.java
new file mode 100644
index 0000000..9889e48
--- /dev/null
+++ b/src/main/java/org/apache/xpath/jaxp/XPathImpl.java
@@ -0,0 +1,545 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// $Id: XPathImpl.java 524814 2007-04-02 15:52:11Z zongaro $
+
+package org.apache.xpath.jaxp;
+
+import javax.xml.namespace.QName;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathVariableResolver;
+import javax.xml.xpath.XPathExpression;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.*;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+import org.apache.xalan.res.XSLMessages;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.traversal.NodeIterator;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.*;
+
+import java.io.IOException;
+
+/**
+ * The XPathImpl class provides implementation for the methods defined  in
+ * javax.xml.xpath.XPath interface. This provide simple access to the results
+ * of an XPath expression.
+ *
+ *
+ * @version $Revision: 524814 $
+ * @author  Ramesh Mandava
+ */
+public class XPathImpl implements javax.xml.xpath.XPath {
+
+    // Private variables
+    private XPathVariableResolver variableResolver;
+    private XPathFunctionResolver functionResolver;
+    private XPathVariableResolver origVariableResolver;
+    private XPathFunctionResolver origFunctionResolver;
+    private NamespaceContext namespaceContext=null;
+    private JAXPPrefixResolver prefixResolver;
+    // By default Extension Functions are allowed in XPath Expressions. If 
+    // Secure Processing Feature is set on XPathFactory then the invocation of
+    // extensions function need to throw XPathFunctionException
+    private boolean featureSecureProcessing = false; 
+
+    XPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr ) {
+        this.origVariableResolver = this.variableResolver = vr;
+        this.origFunctionResolver = this.functionResolver = fr;
+    }
+
+    XPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr, 
+            boolean featureSecureProcessing ) {
+        this.origVariableResolver = this.variableResolver = vr;
+        this.origFunctionResolver = this.functionResolver = fr;
+        this.featureSecureProcessing = featureSecureProcessing;
+    }
+
+    /**
+     * <p>Establishes a variable resolver.</p>
+     *
+     * @param resolver Variable Resolver
+     */
+    public void setXPathVariableResolver(XPathVariableResolver resolver) {
+        if ( resolver == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"XPathVariableResolver"} );
+            throw new NullPointerException( fmsg );
+        }
+        this.variableResolver = resolver;
+    }
+
+    /**
+     * <p>Returns the current variable resolver.</p>
+     *
+     * @return Current variable resolver
+     */
+    public XPathVariableResolver getXPathVariableResolver() {
+        return variableResolver;
+    }
+
+    /**
+     * <p>Establishes a function resolver.</p>
+     *
+     * @param resolver XPath function resolver
+     */
+    public void setXPathFunctionResolver(XPathFunctionResolver resolver) {
+        if ( resolver == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"XPathFunctionResolver"} );
+            throw new NullPointerException( fmsg );
+        }
+        this.functionResolver = resolver;
+    }
+
+    /**
+     * <p>Returns the current function resolver.</p>
+     *
+     * @return Current function resolver
+     */
+    public XPathFunctionResolver getXPathFunctionResolver() {
+        return functionResolver;
+    }
+
+    /**
+     * <p>Establishes a namespace context.</p>
+     *
+     * @param nsContext Namespace context to use
+     */
+    public void setNamespaceContext(NamespaceContext nsContext) {
+        if ( nsContext == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"NamespaceContext"} );
+            throw new NullPointerException( fmsg ); 
+        }
+        this.namespaceContext = nsContext;
+        this.prefixResolver = new JAXPPrefixResolver ( nsContext );
+    }
+
+    /**
+     * <p>Returns the current namespace context.</p>
+     *
+     * @return Current Namespace context
+     */
+    public NamespaceContext getNamespaceContext() {
+        return namespaceContext;
+    }
+
+    private static Document d = null;
+    
+    private static DocumentBuilder getParser() {
+        try {
+            // we'd really like to cache those DocumentBuilders, but we can't because:
+            // 1. thread safety. parsers are not thread-safe, so at least
+            //    we need one instance per a thread.
+            // 2. parsers are non-reentrant, so now we are looking at having a
+            // pool of parsers.
+            // 3. then the class loading issue. The look-up procedure of
+            //    DocumentBuilderFactory.newInstance() depends on context class loader
+            //    and system properties, which may change during the execution of JVM.
+            //
+            // so we really have to create a fresh DocumentBuilder every time we need one
+            // - KK
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            dbf.setNamespaceAware( true );
+            dbf.setValidating( false );
+            return dbf.newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            // this should never happen with a well-behaving JAXP implementation. 
+            throw new Error(e.toString());
+        }
+    }
+
+    private static Document getDummyDocument( ) {
+        // we don't need synchronization here; even if two threads
+        // enter this code at the same time, we just waste a little time
+        if(d==null) {
+            DOMImplementation dim = getParser().getDOMImplementation();
+            d = dim.createDocument("http://java.sun.com/jaxp/xpath",
+                "dummyroot", null);
+        }
+        return d;
+    }
+
+    
+    private XObject eval(String expression, Object contextItem)
+        throws javax.xml.transform.TransformerException {
+        org.apache.xpath.XPath xpath = new org.apache.xpath.XPath( expression,
+            null, prefixResolver, org.apache.xpath.XPath.SELECT ); 
+        org.apache.xpath.XPathContext xpathSupport = null;
+
+        // Create an XPathContext that doesn't support pushing and popping of
+        // variable resolution scopes.  Sufficient for simple XPath 1.0
+        // expressions.
+        if ( functionResolver != null ) {
+            JAXPExtensionsProvider jep = new JAXPExtensionsProvider(
+                    functionResolver, featureSecureProcessing );
+            xpathSupport = new org.apache.xpath.XPathContext(jep, false);
+        } else { 
+            xpathSupport = new org.apache.xpath.XPathContext(false);
+        }
+
+        XObject xobj = null;
+        
+        xpathSupport.setVarStack(new JAXPVariableStack(variableResolver));
+        
+        // If item is null, then we will create a a Dummy contextNode
+        if ( contextItem instanceof Node ) {
+            xobj = xpath.execute (xpathSupport, (Node)contextItem,
+                    prefixResolver );
+        } else {
+            xobj = xpath.execute ( xpathSupport, DTM.NULL, prefixResolver );
+        }
+ 
+        return xobj;
+    }
+        
+    /**
+     * <p>Evaluate an <code>XPath</code> expression in the specified context and return the result as the specified type.</p>
+     *
+     * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
+     * for context item evaluation,
+     * variable, function and <code>QName</code> resolution and return type conversion.</p>
+     *
+     * <p>If <code>returnType</code> is not one of the types defined in {@link XPathConstants} (
+     * {@link XPathConstants#NUMBER NUMBER},
+     * {@link XPathConstants#STRING STRING},
+     * {@link XPathConstants#BOOLEAN BOOLEAN},
+     * {@link XPathConstants#NODE NODE} or
+     * {@link XPathConstants#NODESET NODESET})
+     * then an <code>IllegalArgumentException</code> is thrown.</p>
+     *
+     * <p>If a <code>null</code> value is provided for
+     * <code>item</code>, an empty document will be used for the
+     * context.
+     * If <code>expression</code> or <code>returnType</code> is <code>null</code>, then a
+     * <code>NullPointerException</code> is thrown.</p>
+     *
+     * @param expression The XPath expression.
+     * @param item The starting context (node or node list, for example).
+     * @param returnType The desired return type.
+     *
+     * @return Result of evaluating an XPath expression as an <code>Object</code> of <code>returnType</code>.
+     *
+     * @throws XPathExpressionException If <code>expression</code> cannot be evaluated.
+     * @throws IllegalArgumentException If <code>returnType</code> is not one of the types defined in {@link XPathConstants}.
+     * @throws NullPointerException If <code>expression</code> or <code>returnType</code> is <code>null</code>.
+     */
+    public Object evaluate(String expression, Object item, QName returnType)
+            throws XPathExpressionException {
+        if ( expression == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"XPath expression"} );
+            throw new NullPointerException ( fmsg );
+        }
+        if ( returnType == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"returnType"} );
+            throw new NullPointerException ( fmsg );
+        }
+        // Checking if requested returnType is supported. returnType need to
+        // be defined in XPathConstants
+        if ( !isSupported ( returnType ) ) {
+            String fmsg = XSLMessages.createXPATHMessage(
+                    XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
+                    new Object[] { returnType.toString() } );
+            throw new IllegalArgumentException ( fmsg );
+        }
+
+        try {
+ 
+            XObject resultObject = eval( expression, item );
+            return getResultAsType( resultObject, returnType );
+        } catch ( java.lang.NullPointerException npe ) {
+            // If VariableResolver returns null Or if we get 
+            // NullPointerException at this stage for some other reason
+            // then we have to reurn XPathException 
+            throw new XPathExpressionException ( npe );
+        } catch ( javax.xml.transform.TransformerException te ) {
+            Throwable nestedException = te.getException();
+            if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) {
+                throw (javax.xml.xpath.XPathFunctionException)nestedException;
+            } else {
+                // For any other exceptions we need to throw 
+                // XPathExpressionException ( as per spec )
+                throw new XPathExpressionException ( te );
+            }
+        } 
+        
+    }
+
+    private boolean isSupported( QName returnType ) {
+        if ( ( returnType.equals( XPathConstants.STRING ) ) ||
+             ( returnType.equals( XPathConstants.NUMBER ) ) ||
+             ( returnType.equals( XPathConstants.BOOLEAN ) ) ||
+             ( returnType.equals( XPathConstants.NODE ) ) ||
+             ( returnType.equals( XPathConstants.NODESET ) )  ) {
+  
+            return true;
+        }
+        return false;
+     }
+
+    private Object getResultAsType( XObject resultObject, QName returnType )
+        throws javax.xml.transform.TransformerException {
+        // XPathConstants.STRING
+        if ( returnType.equals( XPathConstants.STRING ) ) { 
+            return resultObject.str();
+        }
+        // XPathConstants.NUMBER
+        if ( returnType.equals( XPathConstants.NUMBER ) ) { 
+            return new Double ( resultObject.num());
+        }
+        // XPathConstants.BOOLEAN
+        if ( returnType.equals( XPathConstants.BOOLEAN ) ) { 
+            return new Boolean( resultObject.bool());
+        }
+        // XPathConstants.NODESET ---ORdered, UNOrdered???
+        if ( returnType.equals( XPathConstants.NODESET ) ) { 
+            return resultObject.nodelist();
+        }
+        // XPathConstants.NODE
+        if ( returnType.equals( XPathConstants.NODE ) ) { 
+            NodeIterator ni = resultObject.nodeset(); 
+            //Return the first node, or null
+            return ni.nextNode();
+        }
+        String fmsg = XSLMessages.createXPATHMessage(
+                XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
+                new Object[] { returnType.toString()});
+        throw new IllegalArgumentException( fmsg );
+    }
+         
+            
+        
+    /**
+     * <p>Evaluate an XPath expression in the specified context and return the result as a <code>String</code>.</p>
+     *
+     * <p>This method calls {@link #evaluate(String expression, Object item, QName returnType)} with a <code>returnType</code> of
+     * {@link XPathConstants#STRING}.</p>
+     *
+     * <p>See "Evaluation of XPath Expressions" of JAXP 1.3 spec 
+     * for context item evaluation,
+     * variable, function and QName resolution and return type conversion.</p>
+     *
+     * <p>If a <code>null</code> value is provided for
+     * <code>item</code>, an empty document will be used for the
+     * context.
+     * If <code>expression</code> is <code>null</code>, then a <code>NullPointerException</code> is thrown.</p>
+     *
+     * @param expression The XPath expression.
+     * @param item The starting context (node or node list, for example).
+     *
+     * @return The <code>String</code> that is the result of evaluating the expression and
+     *   converting the result to a <code>String</code>.
+     *
+     * @throws XPathExpressionException If <code>expression</code> cannot be evaluated.
+     * @throws NullPointerException If <code>expression</code> is <code>null</code>.
+     */
+    public String evaluate(String expression, Object item)
+        throws XPathExpressionException {
+        return (String)this.evaluate( expression, item, XPathConstants.STRING );
+    }
+
+    /**
+     * <p>Compile an XPath expression for later evaluation.</p>
+     *
+     * <p>If <code>expression</code> contains any {@link XPathFunction}s,
+     * they must be available via the {@link XPathFunctionResolver}.
+     * An {@link XPathExpressionException} will be thrown if the <code>XPathFunction</code>
+     * cannot be resovled with the <code>XPathFunctionResolver</code>.</p>
+     * 
+     * <p>If <code>expression</code> is <code>null</code>, a <code>NullPointerException</code> is thrown.</p>
+     *
+     * @param expression The XPath expression.
+     *
+     * @return Compiled XPath expression.
+
+     * @throws XPathExpressionException If <code>expression</code> cannot be compiled.
+     * @throws NullPointerException If <code>expression</code> is <code>null</code>.
+     */
+    public XPathExpression compile(String expression)
+        throws XPathExpressionException {
+        if ( expression == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"XPath expression"} );
+            throw new NullPointerException ( fmsg );
+        }
+        try {
+            org.apache.xpath.XPath xpath = new XPath (expression, null,
+                    prefixResolver, org.apache.xpath.XPath.SELECT );
+            // Can have errorListener
+            XPathExpressionImpl ximpl = new XPathExpressionImpl (xpath,
+                    prefixResolver, functionResolver, variableResolver,
+                    featureSecureProcessing );
+            return ximpl;
+        } catch ( javax.xml.transform.TransformerException te ) {
+            throw new XPathExpressionException ( te ) ;
+        }
+    }
+
+
+    /**
+     * <p>Evaluate an XPath expression in the context of the specified <code>InputSource</code>
+     * and return the result as the specified type.</p>
+     *
+     * <p>This method builds a data model for the {@link InputSource} and calls
+     * {@link #evaluate(String expression, Object item, QName returnType)} on the resulting document object.</p>
+     *
+     * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec 
+     * for context item evaluation,
+     * variable, function and QName resolution and return type conversion.</p>
+     *
+     * <p>If <code>returnType</code> is not one of the types defined in {@link XPathConstants},
+     * then an <code>IllegalArgumentException</code> is thrown.</p>
+     *
+     * <p>If <code>expression</code>, <code>source</code> or <code>returnType</code> is <code>null</code>,
+     * then a <code>NullPointerException</code> is thrown.</p>
+     *
+     * @param expression The XPath expression.
+     * @param source The input source of the document to evaluate over.
+     * @param returnType The desired return type.
+     *
+     * @return The <code>Object</code> that encapsulates the result of evaluating the expression.
+     *
+     * @throws XPathExpressionException If expression cannot be evaluated.
+     * @throws IllegalArgumentException If <code>returnType</code> is not one of the types defined in {@link XPathConstants}.
+     * @throws NullPointerException If <code>expression</code>, <code>source</code> or <code>returnType</code>
+     *   is <code>null</code>.
+     */
+    public Object evaluate(String expression, InputSource source, 
+            QName returnType) throws XPathExpressionException {
+        // Checking validity of different parameters
+        if( source== null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"source"} );
+            throw new NullPointerException ( fmsg );
+        }
+        if ( expression == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"XPath expression"} );
+            throw new NullPointerException ( fmsg );
+        }
+        if ( returnType == null ) {
+            String fmsg = XSLMessages.createXPATHMessage( 
+                    XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
+                    new Object[] {"returnType"} );
+            throw new NullPointerException ( fmsg );
+        }
+
+        //Checking if requested returnType is supported. 
+        //returnType need to be defined in XPathConstants
+        if ( !isSupported ( returnType ) ) {
+            String fmsg = XSLMessages.createXPATHMessage(
+                    XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
+                    new Object[] { returnType.toString() } );
+            throw new IllegalArgumentException ( fmsg );
+        }
+        
+        try {
+
+            Document document = getParser().parse( source );
+
+            XObject resultObject = eval( expression, document );
+            return getResultAsType( resultObject, returnType );
+        } catch ( SAXException e ) {
+            throw new XPathExpressionException ( e );
+        } catch( IOException e ) {
+            throw new XPathExpressionException ( e );            
+        } catch ( javax.xml.transform.TransformerException te ) {
+            Throwable nestedException = te.getException();
+            if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) {
+                throw (javax.xml.xpath.XPathFunctionException)nestedException;
+            } else {
+                throw new XPathExpressionException ( te );
+            }
+        }
+
+    } 
+ 
+
+
+
+    /**
+     * <p>Evaluate an XPath expression in the context of the specified <code>InputSource</code>
+     * and return the result as a <code>String</code>.</p>
+     *
+     * <p>This method calls {@link #evaluate(String expression, InputSource source, QName returnType)} with a
+     * <code>returnType</code> of {@link XPathConstants#STRING}.</p>
+     *
+     * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
+     * for context item evaluation,
+     * variable, function and QName resolution and return type conversion.</p>
+     *
+     * <p>If <code>expression</code> or <code>source</code> is <code>null</code>,
+     * then a <code>NullPointerException</code> is thrown.</p>
+     *
+     * @param expression The XPath expression.
+     * @param source The <code>InputSource</code> of the document to evaluate over.
+     *
+     * @return The <code>String</code> that is the result of evaluating the expression and
+     *   converting the result to a <code>String</code>.
+     *
+     * @throws XPathExpressionException If expression cannot be evaluated.
+     * @throws NullPointerException If <code>expression</code> or <code>source</code> is <code>null</code>.
+     */
+    public String evaluate(String expression, InputSource source)
+        throws XPathExpressionException {
+        return (String)this.evaluate( expression, source, XPathConstants.STRING );
+    }
+
+    /**
+     * <p>Reset this <code>XPath</code> to its original configuration.</p>
+     *
+     * <p><code>XPath</code> is reset to the same state as when it was created with
+     * {@link XPathFactory#newXPath()}.
+     * <code>reset()</code> is designed to allow the reuse of existing <code>XPath</code>s
+     * thus saving resources associated with the creation of new <code>XPath</code>s.</p>
+     *
+     * <p>The reset <code>XPath</code> is not guaranteed to have the same
+     * {@link XPathFunctionResolver}, {@link XPathVariableResolver}
+     * or {@link NamespaceContext} <code>Object</code>s, e.g. {@link Object#equals(Object obj)}.
+     * It is guaranteed to have a functionally equal <code>XPathFunctionResolver</code>,
+     * <code>XPathVariableResolver</code>
+     * and <code>NamespaceContext</code>.</p>
+     */
+    public void reset() {
+        this.variableResolver = this.origVariableResolver;
+        this.functionResolver = this.origFunctionResolver;
+        this.namespaceContext = null;
+    }
+ 
+}
diff --git a/src/main/java/org/apache/xpath/objects/DTMXRTreeFrag.java b/src/main/java/org/apache/xpath/objects/DTMXRTreeFrag.java
new file mode 100755
index 0000000..2c75eb4
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/DTMXRTreeFrag.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xpath.objects;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+/*
+ * 
+ * @author igorh
+ *
+ * Simple wrapper to DTM and XPathContext objects.
+ * Used in XRTreeFrag for caching references to the objects.
+ */
+ public final class DTMXRTreeFrag {
+  private DTM m_dtm;
+  private int m_dtmIdentity = DTM.NULL;
+  private XPathContext m_xctxt;
+ 
+  public DTMXRTreeFrag(int dtmIdentity, XPathContext xctxt){
+      m_xctxt = xctxt;
+      m_dtmIdentity = dtmIdentity;
+      m_dtm = xctxt.getDTM(dtmIdentity); 
+    }
+
+  public final void destruct(){
+    m_dtm = null;         
+    m_xctxt = null; 
+ }
+
+final  DTM getDTM(){return m_dtm;}
+public final  int getDTMIdentity(){return m_dtmIdentity;}
+final  XPathContext getXPathContext(){return m_xctxt;}
+
+public final int hashCode() { return m_dtmIdentity; }
+public final boolean equals(Object obj) {
+   if (obj instanceof DTMXRTreeFrag) {
+       return (m_dtmIdentity == ((DTMXRTreeFrag)obj).getDTMIdentity());
+   }
+   return false;
+ }
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XBoolean.java b/src/main/java/org/apache/xpath/objects/XBoolean.java
new file mode 100644
index 0000000..9bf3049
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XBoolean.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XBoolean.java 469368 2006-10-31 04:41:36Z minchau $
+ */
+package org.apache.xpath.objects;
+
+/**
+ * This class represents an XPath boolean object, and is capable of
+ * converting the boolean to other types, such as a string.
+ * @xsl.usage advanced
+ */
+public class XBoolean extends XObject
+{
+    static final long serialVersionUID = -2964933058866100881L;
+
+  /**
+   * A true boolean object so we don't have to keep creating them.
+   * @xsl.usage internal
+   */
+  public static final XBoolean S_TRUE = new XBooleanStatic(true);
+
+  /**
+   * A true boolean object so we don't have to keep creating them.
+   * @xsl.usage internal
+   */
+  public static final XBoolean S_FALSE = new XBooleanStatic(false);
+
+  /** Value of the object.
+   *  @serial         */
+  private final boolean m_val;
+
+  /**
+   * Construct a XBoolean object.
+   *
+   * @param b Value of the boolean object
+   */
+  public XBoolean(boolean b)
+  {
+
+    super();
+
+    m_val = b;
+  }
+  
+  /**
+   * Construct a XBoolean object.
+   *
+   * @param b Value of the boolean object
+   */
+  public XBoolean(Boolean b)
+  {
+
+    super();
+
+    m_val = b.booleanValue();
+    setObject(b);
+  }
+
+
+  /**
+   * Tell that this is a CLASS_BOOLEAN.
+   *
+   * @return type of CLASS_BOOLEAN
+   */
+  public int getType()
+  {
+    return CLASS_BOOLEAN;
+  }
+
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return type string "#BOOLEAN"
+   */
+  public String getTypeString()
+  {
+    return "#BOOLEAN";
+  }
+
+  /**
+   * Cast result object to a number.
+   *
+   * @return numeric value of the object value
+   */
+  public double num()
+  {
+    return m_val ? 1.0 : 0.0;
+  }
+
+  /**
+   * Cast result object to a boolean.
+   *
+   * @return The object value as a boolean
+   */
+  public boolean bool()
+  {
+    return m_val;
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The object's value as a string
+   */
+  public String str()
+  {
+    return m_val ? "true" : "false";
+  }
+
+  /**
+   * Return a java object that's closest to the representation
+   * that should be handed to an extension.
+   *
+   * @return The object's value as a java object
+   */
+  public Object object()
+  {
+    if(null == m_obj)
+      setObject(new Boolean(m_val));
+    return m_obj;
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 Object to compare to this  
+   *
+   * @return True if the two objects are equal
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(XObject obj2)
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.
+    if (obj2.getType() == XObject.CLASS_NODESET)
+      return obj2.equals(this);
+
+    try
+    {
+      return m_val == obj2.bool();
+    }
+    catch(javax.xml.transform.TransformerException te)
+    {
+      throw new org.apache.xml.utils.WrappedRuntimeException(te);
+    }
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XBooleanStatic.java b/src/main/java/org/apache/xpath/objects/XBooleanStatic.java
new file mode 100644
index 0000000..0e9d7b2
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XBooleanStatic.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XBooleanStatic.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.objects;
+
+/**
+ * This class doesn't have any XPathContext, so override
+ * whatever to ensure it works OK.
+ * @xsl.usage internal
+ */
+public class XBooleanStatic extends XBoolean
+{
+    static final long serialVersionUID = -8064147275772687409L;
+
+  /** The value of the object.
+   *  @serial          */
+  private final boolean m_val;
+
+  /**
+   * Construct a XBooleanStatic object.
+   *
+   * @param b The value of the object
+   */
+  public XBooleanStatic(boolean b)
+  {
+
+    super(b);
+
+    m_val = b;
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 Object to compare to this 
+   *
+   * @return True if the two objects are equal
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(XObject obj2)
+  {
+    try
+    {
+      return m_val == obj2.bool();
+    }
+    catch(javax.xml.transform.TransformerException te)
+    {
+      throw new org.apache.xml.utils.WrappedRuntimeException(te);
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xpath/objects/XMLStringFactoryImpl.java b/src/main/java/org/apache/xpath/objects/XMLStringFactoryImpl.java
new file mode 100644
index 0000000..db42db0
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XMLStringFactoryImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XMLStringFactoryImpl.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringFactory;
+
+/**
+ * Class XMLStringFactoryImpl creates XString versions of XMLStrings.
+ * @xsl.usage internal
+ */
+public class XMLStringFactoryImpl extends XMLStringFactory
+{
+  /** The XMLStringFactory to pass to DTM construction.   */
+  private static XMLStringFactory m_xstringfactory =
+    new XMLStringFactoryImpl();
+
+  /**
+   * Get the XMLStringFactory to pass to DTM construction.
+   *
+   *
+   * @return A never-null static reference to a String factory.
+   */
+  public static XMLStringFactory getFactory()
+  {
+    return m_xstringfactory;
+  }
+
+  /**
+   * Create a new XMLString from a Java string.
+   *
+   *
+   * @param string Java String reference, which must be non-null.
+   *
+   * @return An XMLString object that wraps the String reference.
+   */
+  public XMLString newstr(String string)
+  {
+    return new XString(string);
+  }
+
+  /**
+   * Create a XMLString from a FastStringBuffer.
+   *
+   *
+   * @param fsb FastStringBuffer reference, which must be non-null.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   *
+   * @return An XMLString object that wraps the FastStringBuffer reference.
+   */
+  public XMLString newstr(FastStringBuffer fsb, int start, int length)
+  {
+    return new XStringForFSB(fsb, start, length);
+  }
+  
+  /**
+   * Create a XMLString from a FastStringBuffer.
+   *
+   *
+   * @param string FastStringBuffer reference, which must be non-null.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   *
+   * @return An XMLString object that wraps the FastStringBuffer reference.
+   */
+  public XMLString newstr(char[] string, int start, int length)
+  {
+    return new XStringForChars(string, start, length);
+  }
+  
+  /**
+   * Get a cheap representation of an empty string.
+   * 
+   * @return An non-null reference to an XMLString that represents "".
+   */
+  public XMLString emptystr()
+  {
+    return XString.EMPTYSTRING;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XNodeSet.java b/src/main/java/org/apache/xpath/objects/XNodeSet.java
new file mode 100644
index 0000000..2b38506
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XNodeSet.java
@@ -0,0 +1,969 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XNodeSet.java 469368 2006-10-31 04:41:36Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.axes.NodeSequence;
+
+import org.w3c.dom.NodeList;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * This class represents an XPath nodeset object, and is capable of
+ * converting the nodeset to other types, such as a string.
+ * @xsl.usage general
+ */
+public class XNodeSet extends NodeSequence
+{  
+    static final long serialVersionUID = 1916026368035639667L;
+  /**
+   * Default constructor for derived objects.
+   */
+  protected XNodeSet()
+  {
+  }
+
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param val Value of the XNodeSet object
+   */
+  public XNodeSet(DTMIterator val)
+  {
+  	super();
+  	if(val instanceof XNodeSet)
+  	{
+        final XNodeSet nodeSet = (XNodeSet) val;
+	    setIter(nodeSet.m_iter);
+	    m_dtmMgr = nodeSet.m_dtmMgr;
+	    m_last = nodeSet.m_last;
+        // First make sure the DTMIterator val has a cache,
+        // so if it doesn't have one, make one.
+	    if(!nodeSet.hasCache())
+	        nodeSet.setShouldCacheNodes(true);
+        
+        // Get the cache from val and use it ourselves (we share it).
+	    setObject(nodeSet.getIteratorCache());
+  	}
+  	else
+    	setIter(val);
+  }
+  
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param val Value of the XNodeSet object
+   */
+  public XNodeSet(XNodeSet val)
+  {
+  	super();
+    setIter(val.m_iter);
+    m_dtmMgr = val.m_dtmMgr;
+    m_last = val.m_last;
+    if(!val.hasCache())
+    	val.setShouldCacheNodes(true);
+    setObject(val.m_obj);
+  }
+
+
+  /**
+   * Construct an empty XNodeSet object.  This is used to create a mutable 
+   * nodeset to which random nodes may be added.
+   */
+  public XNodeSet(DTMManager dtmMgr) 
+  {
+     this(DTM.NULL,dtmMgr);
+  }
+
+  /**
+   * Construct a XNodeSet object for one node.
+   *
+   * @param n Node to add to the new XNodeSet object
+   */
+  public XNodeSet(int n, DTMManager dtmMgr)
+  {
+
+    super(new NodeSetDTM(dtmMgr));
+    m_dtmMgr = dtmMgr;
+
+    if (DTM.NULL != n)
+    {
+      ((NodeSetDTM) m_obj).addNode(n);
+      m_last = 1;
+    }
+    else
+    	m_last = 0;
+  }
+
+  /**
+   * Tell that this is a CLASS_NODESET.
+   *
+   * @return type CLASS_NODESET
+   */
+  public int getType()
+  {
+    return CLASS_NODESET;
+  }
+
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return type string "#NODESET"
+   */
+  public String getTypeString()
+  {
+    return "#NODESET";
+  }
+
+  /**
+   * Get numeric value of the string conversion from a single node.
+   *
+   * @param n Node to convert
+   *
+   * @return numeric value of the string conversion from a single node.
+   */
+  public double getNumberFromNode(int n)
+  {
+    XMLString xstr = m_dtmMgr.getDTM(n).getStringValue(n);
+    return xstr.toDouble();
+  }
+
+  /**
+   * Cast result object to a number.
+   *
+   * @return numeric value of the string conversion from the 
+   * next node in the NodeSetDTM, or NAN if no node was found
+   */
+  public double num()
+  {
+
+    int node = item(0);
+    return (node != DTM.NULL) ? getNumberFromNode(node) : Double.NaN;
+  }
+  
+  /**
+   * Cast result object to a number, but allow side effects, such as the 
+   * incrementing of an iterator.
+   *
+   * @return numeric value of the string conversion from the 
+   * next node in the NodeSetDTM, or NAN if no node was found
+   */
+  public double numWithSideEffects()
+  {
+    int node = nextNode();
+
+    return (node != DTM.NULL) ? getNumberFromNode(node) : Double.NaN;
+  }
+
+
+  /**
+   * Cast result object to a boolean.
+   *
+   * @return True if there is a next node in the nodeset
+   */
+  public boolean bool()
+  {
+    return (item(0) != DTM.NULL);
+  }
+  
+  /**
+   * Cast result object to a boolean, but allow side effects, such as the 
+   * incrementing of an iterator.
+   *
+   * @return True if there is a next node in the nodeset
+   */
+  public boolean boolWithSideEffects()
+  {
+    return (nextNode() != DTM.NULL);
+  }
+
+  
+  /**
+   * Get the string conversion from a single node.
+   *
+   * @param n Node to convert
+   *
+   * @return the string conversion from a single node.
+   */
+  public XMLString getStringFromNode(int n)
+  {
+    // %OPT%
+    // I guess we'll have to get a static instance of the DTM manager...
+    if(DTM.NULL != n)
+    {
+      return m_dtmMgr.getDTM(n).getStringValue(n);
+    }
+    else
+    {
+      return org.apache.xpath.objects.XString.EMPTYSTRING;
+    }
+  }
+  
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value. Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
+          throws org.xml.sax.SAXException
+  {
+    int node = item(0);
+	
+    if(node != DTM.NULL)
+    {
+      m_dtmMgr.getDTM(node).dispatchCharactersEvents(node, ch, false);
+    }
+    
+  }
+  
+  /**
+   * Cast result object to an XMLString.
+   *
+   * @return The document fragment node data or the empty string. 
+   */
+  public XMLString xstr()
+  {
+    int node = item(0);
+    return (node != DTM.NULL) ? getStringFromNode(node) : XString.EMPTYSTRING;
+  }
+  
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)
+  {
+    XString xstring = (XString)xstr();
+    xstring.appendToFsb(fsb);
+  }
+  
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return the string conversion from the next node in the nodeset
+   * or "" if there is no next node
+   */
+  public String str()
+  {
+    int node = item(0);
+    return (node != DTM.NULL) ? getStringFromNode(node).toString() : "";   
+  }
+  
+  /**
+   * Return a java object that's closest to the representation
+   * that should be handed to an extension.
+   *
+   * @return The object that this class wraps
+   */
+  public Object object()
+  {
+    if(null == m_obj)
+    	return this;
+    else
+    	return m_obj;
+  }
+
+  // %REVIEW%
+  // hmmm...
+//  /**
+//   * Cast result object to a result tree fragment.
+//   *
+//   * @param support The XPath context to use for the conversion 
+//   *
+//   * @return the nodeset as a result tree fragment.
+//   */
+//  public DocumentFragment rtree(XPathContext support)
+//  {
+//    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+//    DocumentBuilder db = dbf.newDocumentBuilder();
+//    Document myDoc = db.newDocument();
+//    
+//    DocumentFragment docFrag = myDoc.createDocumentFragment();
+//
+//    DTMIterator nl = iter();
+//    int node;
+//
+//    while (DTM.NULL != (node = nl.nextNode()))
+//    {
+//      frag.appendChild(node, true, true);
+//    }
+//
+//    return frag.getDocument();
+//  }
+
+  /**
+   * Cast result object to a nodelist.
+   *
+   * @return a NodeIterator.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public NodeIterator nodeset() throws javax.xml.transform.TransformerException
+  {
+    return new org.apache.xml.dtm.ref.DTMNodeIterator(iter());
+  }
+  
+  /**
+   * Cast result object to a nodelist.
+   *
+   * @return a NodeList.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public NodeList nodelist() throws javax.xml.transform.TransformerException
+  {
+    org.apache.xml.dtm.ref.DTMNodeList nodelist = new org.apache.xml.dtm.ref.DTMNodeList(this);
+    // Creating a DTMNodeList has the side-effect that it will create a clone
+    // XNodeSet with cache and run m_iter to the end. You cannot get any node
+    // from m_iter after this call. As a fix, we call SetVector() on the clone's 
+    // cache. See Bugzilla 14406.
+    XNodeSet clone = (XNodeSet)nodelist.getDTMIterator();
+    SetVector(clone.getVector());
+    return nodelist;
+  }
+
+  
+//  /**
+//   * Return a java object that's closest to the representation
+//   * that should be handed to an extension.
+//   *
+//   * @return The object that this class wraps
+//   */
+//  public Object object()
+//  {
+//    return new org.apache.xml.dtm.ref.DTMNodeList(iter());
+//  }
+
+  /**
+   * Return the iterator without cloning, etc.
+   */
+  public DTMIterator iterRaw()
+  {
+    return this;
+  }
+  
+  public void release(DTMIterator iter)
+  {
+  }
+  
+  /**
+   * Cast result object to a nodelist.
+   *
+   * @return The nodeset as a nodelist
+   */
+  public DTMIterator iter()
+  {
+    try
+    {
+    	if(hasCache())
+      		return cloneWithReset();
+      	else
+      		return this; // don't bother to clone... won't do any good!
+    }
+    catch (CloneNotSupportedException cnse)
+    {
+      throw new RuntimeException(cnse.getMessage());
+    }
+  }
+  
+  /**
+   * Get a fresh copy of the object.  For use with variables.
+   *
+   * @return A fresh nodelist.
+   */
+  public XObject getFresh()
+  {
+    try
+    {
+    	if(hasCache())
+      		return (XObject)cloneWithReset();
+      	else
+      		return this; // don't bother to clone... won't do any good!
+    }
+    catch (CloneNotSupportedException cnse)
+    {
+      throw new RuntimeException(cnse.getMessage());
+    }
+  }
+
+  /**
+   * Cast result object to a mutableNodeset.
+   *
+   * @return The nodeset as a mutableNodeset
+   */
+  public NodeSetDTM mutableNodeset()
+  {
+    NodeSetDTM mnl;
+
+    if(m_obj instanceof NodeSetDTM)
+    {
+      mnl = (NodeSetDTM) m_obj;
+    }
+    else
+    {
+      mnl = new NodeSetDTM(iter());
+      setObject(mnl);
+      setCurrentPos(0);
+    }
+
+    return mnl;
+  }
+
+  /** Less than comparator         */
+  static final LessThanComparator S_LT = new LessThanComparator();
+
+  /** Less than or equal comparator          */
+  static final LessThanOrEqualComparator S_LTE = new LessThanOrEqualComparator();
+
+  /** Greater than comparator         */
+  static final GreaterThanComparator S_GT = new GreaterThanComparator();
+
+  /** Greater than or equal comparator          */
+  static final GreaterThanOrEqualComparator S_GTE =
+    new GreaterThanOrEqualComparator();
+
+  /** Equal comparator         */
+  static final EqualComparator S_EQ = new EqualComparator();
+
+  /** Not equal comparator         */
+  static final NotEqualComparator S_NEQ = new NotEqualComparator();
+
+  /**
+   * Tell if one object is less than the other.
+   *
+   * @param obj2 Object to compare this nodeset to
+   * @param comparator Comparator to use
+   *
+   * @return See the comments below for each object type comparison 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean compare(XObject obj2, Comparator comparator)
+          throws javax.xml.transform.TransformerException
+  {
+
+    boolean result = false;
+    int type = obj2.getType();
+
+    if (XObject.CLASS_NODESET == type)
+    {
+      // %OPT% This should be XMLString based instead of string based...
+
+      // From http://www.w3.org/TR/xpath: 
+      // If both objects to be compared are node-sets, then the comparison 
+      // will be true if and only if there is a node in the first node-set 
+      // and a node in the second node-set such that the result of performing 
+      // the comparison on the string-values of the two nodes is true.
+      // Note this little gem from the draft:
+      // NOTE: If $x is bound to a node-set, then $x="foo" 
+      // does not mean the same as not($x!="foo"): the former 
+      // is true if and only if some node in $x has the string-value 
+      // foo; the latter is true if and only if all nodes in $x have 
+      // the string-value foo.
+      DTMIterator list1 = iterRaw();
+      DTMIterator list2 = ((XNodeSet) obj2).iterRaw();
+      int node1;
+      java.util.Vector node2Strings = null;
+
+      while (DTM.NULL != (node1 = list1.nextNode()))
+      {
+        XMLString s1 = getStringFromNode(node1);
+
+        if (null == node2Strings)
+        {
+          int node2;
+
+          while (DTM.NULL != (node2 = list2.nextNode()))
+          {
+            XMLString s2 = getStringFromNode(node2);
+
+            if (comparator.compareStrings(s1, s2))
+            {
+              result = true;
+
+              break;
+            }
+
+            if (null == node2Strings)
+              node2Strings = new java.util.Vector();
+
+            node2Strings.addElement(s2);
+          }
+        }
+        else
+        {
+          int n = node2Strings.size();
+
+          for (int i = 0; i < n; i++)
+          {
+            if (comparator.compareStrings(s1, (XMLString)node2Strings.elementAt(i)))
+            {
+              result = true;
+
+              break;
+            }
+          }
+        }
+      }
+      list1.reset();
+      list2.reset();
+    }
+    else if (XObject.CLASS_BOOLEAN == type)
+    {
+
+      // From http://www.w3.org/TR/xpath: 
+      // If one object to be compared is a node-set and the other is a boolean, 
+      // then the comparison will be true if and only if the result of 
+      // performing the comparison on the boolean and on the result of 
+      // converting the node-set to a boolean using the boolean function 
+      // is true.
+      double num1 = bool() ? 1.0 : 0.0;
+      double num2 = obj2.num();
+
+      result = comparator.compareNumbers(num1, num2);
+    }
+    else if (XObject.CLASS_NUMBER == type)
+    {
+
+      // From http://www.w3.org/TR/xpath: 
+      // If one object to be compared is a node-set and the other is a number, 
+      // then the comparison will be true if and only if there is a 
+      // node in the node-set such that the result of performing the 
+      // comparison on the number to be compared and on the result of 
+      // converting the string-value of that node to a number using 
+      // the number function is true. 
+      DTMIterator list1 = iterRaw();
+      double num2 = obj2.num();
+      int node;
+
+      while (DTM.NULL != (node = list1.nextNode()))
+      {
+        double num1 = getNumberFromNode(node);
+
+        if (comparator.compareNumbers(num1, num2))
+        {
+          result = true;
+
+          break;
+        }
+      }
+      list1.reset();
+    }
+    else if (XObject.CLASS_RTREEFRAG == type)
+    {
+      XMLString s2 = obj2.xstr();
+      DTMIterator list1 = iterRaw();
+      int node;
+
+      while (DTM.NULL != (node = list1.nextNode()))
+      {
+        XMLString s1 = getStringFromNode(node);
+
+        if (comparator.compareStrings(s1, s2))
+        {
+          result = true;
+
+          break;
+        }
+      }
+      list1.reset();
+    }
+    else if (XObject.CLASS_STRING == type)
+    {
+
+      // From http://www.w3.org/TR/xpath: 
+      // If one object to be compared is a node-set and the other is a 
+      // string, then the comparison will be true if and only if there 
+      // is a node in the node-set such that the result of performing 
+      // the comparison on the string-value of the node and the other 
+      // string is true. 
+      XMLString s2 = obj2.xstr();
+      DTMIterator list1 = iterRaw();
+      int node;
+
+      while (DTM.NULL != (node = list1.nextNode()))
+      {
+        XMLString s1 = getStringFromNode(node);
+        if (comparator.compareStrings(s1, s2))
+        {
+          result = true;
+
+          break;
+        }
+      }
+      list1.reset();
+    }
+    else
+    {
+      result = comparator.compareNumbers(this.num(), obj2.num());
+    }
+
+    return result;
+  }
+
+  /**
+   * Tell if one object is less than the other.
+   *
+   * @param obj2 object to compare this nodeset to
+   *
+   * @return see this.compare(...) 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean lessThan(XObject obj2) throws javax.xml.transform.TransformerException
+  {
+    return compare(obj2, S_LT);
+  }
+
+  /**
+   * Tell if one object is less than or equal to the other.
+   *
+   * @param obj2 object to compare this nodeset to
+   *
+   * @return see this.compare(...) 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean lessThanOrEqual(XObject obj2) throws javax.xml.transform.TransformerException
+  {
+    return compare(obj2, S_LTE);
+  }
+
+  /**
+   * Tell if one object is less than the other.
+   *
+   * @param obj2 object to compare this nodeset to
+   *
+   * @return see this.compare(...) 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean greaterThan(XObject obj2) throws javax.xml.transform.TransformerException
+  {
+    return compare(obj2, S_GT);
+  }
+
+  /**
+   * Tell if one object is less than the other.
+   *
+   * @param obj2 object to compare this nodeset to
+   *
+   * @return see this.compare(...) 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean greaterThanOrEqual(XObject obj2)
+          throws javax.xml.transform.TransformerException
+  {
+    return compare(obj2, S_GTE);
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 object to compare this nodeset to
+   *
+   * @return see this.compare(...) 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(XObject obj2)
+  {
+    try
+    {
+      return compare(obj2, S_EQ);
+    }
+    catch(javax.xml.transform.TransformerException te)
+    {
+      throw new org.apache.xml.utils.WrappedRuntimeException(te);
+    }
+  }
+
+  /**
+   * Tell if two objects are functionally not equal.
+   *
+   * @param obj2 object to compare this nodeset to
+   *
+   * @return see this.compare(...) 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean notEquals(XObject obj2) throws javax.xml.transform.TransformerException
+  {
+    return compare(obj2, S_NEQ);
+  }
+}
+
+/**
+ * compares nodes for various boolean operations.
+ */
+abstract class Comparator
+{
+
+  /**
+   * Compare two strings
+   *
+   *
+   * @param s1 First string to compare
+   * @param s2 Second String to compare 
+   *
+   * @return Whether the strings are equal or not
+   */
+  abstract boolean compareStrings(XMLString s1, XMLString s2);
+
+  /**
+   * Compare two numbers
+   *
+   *
+   * @param n1 First number to compare
+   * @param n2 Second number to compare
+   *
+   * @return Whether the numbers are equal or not
+   */
+  abstract boolean compareNumbers(double n1, double n2);
+}
+
+/**
+ * Compare strings or numbers for less than.
+ */
+class LessThanComparator extends Comparator
+{
+
+  /**
+   * Compare two strings for less than.
+   *
+   *
+   * @param s1 First string to compare
+   * @param s2 Second String to compare 
+   *
+   * @return True if s1 is less than s2
+   */
+  boolean compareStrings(XMLString s1, XMLString s2)
+  {
+    return (s1.toDouble() < s2.toDouble());
+    // return s1.compareTo(s2) < 0;
+  }
+
+  /**
+   * Compare two numbers for less than.
+   *
+   *
+   * @param n1 First number to compare
+   * @param n2 Second number to compare
+   *
+   * @return true if n1 is less than n2
+   */
+  boolean compareNumbers(double n1, double n2)
+  {
+    return n1 < n2;
+  }
+}
+
+/**
+ * Compare strings or numbers for less than or equal.
+ */
+class LessThanOrEqualComparator extends Comparator
+{
+
+  /**
+   * Compare two strings for less than or equal.
+   *
+   *
+   * @param s1 First string to compare
+   * @param s2 Second String to compare
+   *
+   * @return true if s1 is less than or equal to s2
+   */
+  boolean compareStrings(XMLString s1, XMLString s2)
+  {
+    return (s1.toDouble() <= s2.toDouble());
+    // return s1.compareTo(s2) <= 0;
+  }
+
+  /**
+   * Compare two numbers for less than or equal.
+   *
+   *
+   * @param n1 First number to compare
+   * @param n2 Second number to compare
+   *
+   * @return true if n1 is less than or equal to n2
+   */
+  boolean compareNumbers(double n1, double n2)
+  {
+    return n1 <= n2;
+  }
+}
+
+/**
+ * Compare strings or numbers for greater than.
+ */
+class GreaterThanComparator extends Comparator
+{
+
+  /**
+   * Compare two strings for greater than.
+   *
+   *
+   * @param s1 First string to compare
+   * @param s2 Second String to compare
+   *
+   * @return true if s1 is greater than s2
+   */
+  boolean compareStrings(XMLString s1, XMLString s2)
+  {
+    return (s1.toDouble() > s2.toDouble());
+    // return s1.compareTo(s2) > 0;
+  }
+
+  /**
+   * Compare two numbers for greater than.
+   *
+   *
+   * @param n1 First number to compare
+   * @param n2 Second number to compare
+   *
+   * @return true if n1 is greater than n2
+   */
+  boolean compareNumbers(double n1, double n2)
+  {
+    return n1 > n2;
+  }
+}
+
+/**
+ * Compare strings or numbers for greater than or equal.
+ */
+class GreaterThanOrEqualComparator extends Comparator
+{
+
+  /**
+   * Compare two strings for greater than or equal.
+   *
+   *
+   * @param s1 First string to compare
+   * @param s2 Second String to compare
+   *
+   * @return true if s1 is greater than or equal to s2
+   */
+  boolean compareStrings(XMLString s1, XMLString s2)
+  {
+    return (s1.toDouble() >= s2.toDouble());
+    // return s1.compareTo(s2) >= 0;
+  }
+
+  /**
+   * Compare two numbers for greater than or equal.
+   *
+   *
+   * @param n1 First number to compare
+   * @param n2 Second number to compare
+   *
+   * @return true if n1 is greater than or equal to n2
+   */
+  boolean compareNumbers(double n1, double n2)
+  {
+    return n1 >= n2;
+  }
+}
+
+/**
+ * Compare strings or numbers for equality.
+ */
+class EqualComparator extends Comparator
+{
+
+  /**
+   * Compare two strings for equality.
+   *
+   *
+   * @param s1 First string to compare
+   * @param s2 Second String to compare
+   *
+   * @return true if s1 is equal to s2
+   */
+  boolean compareStrings(XMLString s1, XMLString s2)
+  {
+    return s1.equals(s2);
+  }
+
+  /**
+   * Compare two numbers for equality.
+   *
+   *
+   * @param n1 First number to compare
+   * @param n2 Second number to compare
+   *
+   * @return true if n1 is equal to n2
+   */
+  boolean compareNumbers(double n1, double n2)
+  {
+    return n1 == n2;
+  }
+}
+
+/**
+ * Compare strings or numbers for non-equality.
+ */
+class NotEqualComparator extends Comparator
+{
+
+  /**
+   * Compare two strings for non-equality.
+   *
+   *
+   * @param s1 First string to compare
+   * @param s2 Second String to compare
+   *
+   * @return true if s1 is not equal to s2
+   */
+  boolean compareStrings(XMLString s1, XMLString s2)
+  {
+    return !s1.equals(s2);
+  }
+
+  /**
+   * Compare two numbers for non-equality.
+   *
+   *
+   * @param n1 First number to compare
+   * @param n2 Second number to compare
+   *
+   * @return true if n1 is not equal to n2
+   */
+  boolean compareNumbers(double n1, double n2)
+  {
+    return n1 != n2;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/objects/XNodeSetForDOM.java b/src/main/java/org/apache/xpath/objects/XNodeSetForDOM.java
new file mode 100644
index 0000000..7075d3e
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XNodeSetForDOM.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XNodeSetForDOM.java 469368 2006-10-31 04:41:36Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.XPathContext;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * This class overrides the XNodeSet#object() method to provide the original 
+ * Node object, NodeList object, or NodeIterator.
+ */
+public class XNodeSetForDOM extends XNodeSet
+{
+    static final long serialVersionUID = -8396190713754624640L;
+  Object m_origObj;
+
+  public XNodeSetForDOM(Node node, DTMManager dtmMgr)
+  {
+    m_dtmMgr = dtmMgr;
+    m_origObj = node;
+    int dtmHandle = dtmMgr.getDTMHandleFromNode(node);
+    setObject(new NodeSetDTM(dtmMgr));
+    ((NodeSetDTM) m_obj).addNode(dtmHandle);
+  }
+  
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param val Value of the XNodeSet object
+   */
+  public XNodeSetForDOM(XNodeSet val)
+  {
+  	super(val);
+  	if(val instanceof XNodeSetForDOM)
+    	m_origObj = ((XNodeSetForDOM)val).m_origObj;
+  }
+  
+  public XNodeSetForDOM(NodeList nodeList, XPathContext xctxt)
+  {
+    m_dtmMgr = xctxt.getDTMManager();
+    m_origObj = nodeList;
+
+    // JKESS 20020514: Longer-term solution is to force
+    // folks to request length through an accessor, so we can defer this
+    // retrieval... but that requires an API change.
+    // m_obj=new org.apache.xpath.NodeSetDTM(nodeList, xctxt);
+    org.apache.xpath.NodeSetDTM nsdtm=new org.apache.xpath.NodeSetDTM(nodeList, xctxt);
+    m_last=nsdtm.getLength();
+    setObject(nsdtm);   
+  }
+
+  public XNodeSetForDOM(NodeIterator nodeIter, XPathContext xctxt)
+  {
+    m_dtmMgr = xctxt.getDTMManager();
+    m_origObj = nodeIter;
+
+    // JKESS 20020514: Longer-term solution is to force
+    // folks to request length through an accessor, so we can defer this
+    // retrieval... but that requires an API change.
+    // m_obj = new org.apache.xpath.NodeSetDTM(nodeIter, xctxt);
+    org.apache.xpath.NodeSetDTM nsdtm=new org.apache.xpath.NodeSetDTM(nodeIter, xctxt);
+    m_last=nsdtm.getLength();
+    setObject(nsdtm);   
+  }
+  
+  /**
+   * Return the original DOM object that the user passed in.  For use primarily
+   * by the extension mechanism.
+   *
+   * @return The object that this class wraps
+   */
+  public Object object()
+  {
+    return m_origObj;
+  }
+  
+  /**
+   * Cast result object to a nodelist. Always issues an error.
+   *
+   * @return null
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public NodeIterator nodeset() throws javax.xml.transform.TransformerException
+  {
+    return (m_origObj instanceof NodeIterator) 
+                   ? (NodeIterator)m_origObj : super.nodeset();      
+  }
+  
+  /**
+   * Cast result object to a nodelist. Always issues an error.
+   *
+   * @return null
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public NodeList nodelist() throws javax.xml.transform.TransformerException
+  {
+    return (m_origObj instanceof NodeList) 
+                   ? (NodeList)m_origObj : super.nodelist();      
+  }
+
+
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XNull.java b/src/main/java/org/apache/xpath/objects/XNull.java
new file mode 100644
index 0000000..1a5fa82
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XNull.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XNull.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPathContext;
+
+/**
+ * This class represents an XPath null object, and is capable of
+ * converting the null to other types, such as a string.
+ * @xsl.usage general
+ */
+public class XNull extends XNodeSet
+{
+    static final long serialVersionUID = -6841683711458983005L;
+
+  /**
+   * Create an XObject.
+   */
+  public XNull()
+  {
+    super();
+  }
+
+  /**
+   * Tell what kind of class this is.
+   *
+   * @return type CLASS_NULL
+   */
+  public int getType()
+  {
+    return CLASS_NULL;
+  }
+
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return type string "#CLASS_NULL"
+   */
+  public String getTypeString()
+  {
+    return "#CLASS_NULL";
+  }
+
+  /**
+   * Cast result object to a number.
+   * 
+   * @return 0.0
+   */
+
+  public double num()
+  {
+    return 0.0;
+  }
+
+  /**
+   * Cast result object to a boolean.
+   *
+   * @return false
+   */
+  public boolean bool()
+  {
+    return false;
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return empty string ""
+   */
+  public String str()
+  {
+    return "";
+  }
+
+  /**
+   * Cast result object to a result tree fragment.
+   *
+   * @param support XPath context to use for the conversion
+   *
+   * @return The object as a result tree fragment.
+   */
+  public int rtf(XPathContext support)
+  {
+    // DTM frag = support.createDocumentFragment();
+    // %REVIEW%
+    return DTM.NULL;
+  }
+
+//  /**
+//   * Cast result object to a nodelist.
+//   *
+//   * @return null
+//   */
+//  public DTMIterator iter()
+//  {
+//    return null;
+//  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return True if the given object is of type CLASS_NULL
+   */
+  public boolean equals(XObject obj2)
+  {
+    return obj2.getType() == CLASS_NULL;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/objects/XNumber.java b/src/main/java/org/apache/xpath/objects/XNumber.java
new file mode 100644
index 0000000..b56fec4
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XNumber.java
@@ -0,0 +1,437 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XNumber.java 469368 2006-10-31 04:41:36Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+
+/**
+ * This class represents an XPath number, and is capable of
+ * converting the number to other types, such as a string.
+ * @xsl.usage general
+ */
+public class XNumber extends XObject
+{
+    static final long serialVersionUID = -2720400709619020193L;
+
+  /** Value of the XNumber object.
+   *  @serial         */
+  double m_val;
+
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param d Value of the object
+   */
+  public XNumber(double d)
+  {
+    super();
+
+    m_val = d;
+  }
+  
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param num Value of the object
+   */
+  public XNumber(Number num)
+  {
+
+    super();
+
+    m_val = num.doubleValue();
+    setObject(num);
+  }
+
+  /**
+   * Tell that this is a CLASS_NUMBER.
+   *
+   * @return node type CLASS_NUMBER 
+   */
+  public int getType()
+  {
+    return CLASS_NUMBER;
+  }
+
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return type string "#NUMBER" 
+   */
+  public String getTypeString()
+  {
+    return "#NUMBER";
+  }
+
+  /**
+   * Cast result object to a number.
+   *
+   * @return the value of the XNumber object
+   */
+  public double num()
+  {
+    return m_val;
+  }
+  
+  /**
+   * Evaluate expression to a number.
+   *
+   * @return 0.0
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt) 
+    throws javax.xml.transform.TransformerException
+  {
+
+    return m_val;
+  }
+
+  /**
+   * Cast result object to a boolean.
+   *
+   * @return false if the value is NaN or equal to 0.0
+   */
+  public boolean bool()
+  {
+    return (Double.isNaN(m_val) || (m_val == 0.0)) ? false : true;
+  }
+
+//  /**
+//   * Cast result object to a string.
+//   *
+//   * @return "NaN" if the number is NaN, Infinity or -Infinity if
+//   * the number is infinite or the string value of the number.
+//   */
+//  private static final int PRECISION = 16;
+//  public String str()
+//  {
+//
+//    if (Double.isNaN(m_val))
+//    {
+//      return "NaN";
+//    }
+//    else if (Double.isInfinite(m_val))
+//    {
+//      if (m_val > 0)
+//        return "Infinity";
+//      else
+//        return "-Infinity";
+//    }
+//
+//    long longVal = (long)m_val;
+//    if ((double)longVal == m_val)
+//      return Long.toString(longVal);
+//
+//
+//    String s = Double.toString(m_val);
+//    int len = s.length();
+//
+//    if (s.charAt(len - 2) == '.' && s.charAt(len - 1) == '0')
+//    {
+//      return s.substring(0, len - 2);
+//    }
+//
+//    int exp = 0;
+//    int e = s.indexOf('E');
+//    if (e != -1)
+//    {
+//      exp = Integer.parseInt(s.substring(e + 1));
+//      s = s.substring(0,e);
+//      len = e;
+//    }
+//
+//    // Calculate Significant Digits:
+//    // look from start of string for first digit
+//    // look from end for last digit
+//    // significant digits = end - start + (0 or 1 depending on decimal location)
+//
+//    int decimalPos = -1;
+//    int start = (s.charAt(0) == '-') ? 1 : 0;
+//    findStart: for( ; start < len; start++ )
+//    {
+//      switch (s.charAt(start))
+//      {
+//      case '0':
+//        break;
+//      case '.':
+//        decimalPos = start;
+//        break;
+//      default:
+//        break findStart;
+//      }
+//    }
+//    int end = s.length() - 1;
+//    findEnd: for( ; end > start; end-- )
+//    {
+//      switch (s.charAt(end))
+//      {
+//      case '0':
+//        break;
+//      case '.':
+//        decimalPos = end;
+//        break;
+//      default:
+//        break findEnd;
+//      }
+//    }
+//
+//    int sigDig = end - start;
+//
+//    // clarify decimal location if it has not yet been found
+//    if (decimalPos == -1)
+//      decimalPos = s.indexOf('.');
+//
+//    // if decimal is not between start and end, add one to sigDig
+//    if (decimalPos < start || decimalPos > end)
+//      ++sigDig;
+//
+//    // reduce significant digits to PRECISION if necessary
+//    if (sigDig > PRECISION)
+//    {
+//      // re-scale BigDecimal in order to get significant digits = PRECISION
+//      BigDecimal num = new BigDecimal(s);
+//      int newScale = num.scale() - (sigDig - PRECISION);
+//      if (newScale < 0)
+//        newScale = 0;
+//      s = num.setScale(newScale, BigDecimal.ROUND_HALF_UP).toString();
+//
+//      // remove trailing '0's; keep track of decimalPos
+//      int truncatePoint = s.length();
+//      while (s.charAt(--truncatePoint) == '0')
+//        ;
+//
+//      if (s.charAt(truncatePoint) == '.')
+//      {
+//        decimalPos = truncatePoint;
+//      }
+//      else
+//      {
+//        decimalPos = s.indexOf('.');
+//        truncatePoint += 1;
+//      }
+//
+//      s = s.substring(0, truncatePoint);
+//      len = s.length();
+//    }
+//
+//    // Account for exponent by adding zeros as needed 
+//    // and moving the decimal place
+//
+//    if (exp == 0)
+//       return s;
+//
+//    start = 0;
+//    String sign;
+//    if (s.charAt(0) == '-')
+//    {
+//      sign = "-";
+//      start++;
+//    }
+//    else
+//      sign = "";
+//
+//    String wholePart = s.substring(start, decimalPos);
+//    String decimalPart = s.substring(decimalPos + 1);
+//
+//    // get the number of digits right of the decimal
+//    int decimalLen = decimalPart.length();
+//
+//    if (exp >= decimalLen)
+//      return sign + wholePart + decimalPart + zeros(exp - decimalLen);
+//
+//    if (exp > 0)
+//      return sign + wholePart + decimalPart.substring(0, exp) + "."
+//             + decimalPart.substring(exp);
+//
+//    return sign + "0." + zeros(-1 - exp) + wholePart + decimalPart;
+//  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return "NaN" if the number is NaN, Infinity or -Infinity if
+   * the number is infinite or the string value of the number.
+   */
+  public String str()
+  {
+
+    if (Double.isNaN(m_val))
+    {
+      return "NaN";
+    }
+    else if (Double.isInfinite(m_val))
+    {
+      if (m_val > 0)
+        return "Infinity";
+      else
+        return "-Infinity";
+    }
+
+    double num = m_val;
+    String s = Double.toString(num);
+    int len = s.length();
+
+    if (s.charAt(len - 2) == '.' && s.charAt(len - 1) == '0')
+    {
+      s = s.substring(0, len - 2);
+
+      if (s.equals("-0"))
+        return "0";
+
+      return s;
+    }
+
+    int e = s.indexOf('E');
+
+    if (e < 0)
+    {
+      if (s.charAt(len - 1) == '0')
+        return s.substring(0, len - 1);
+      else
+        return s;
+    }
+
+    int exp = Integer.parseInt(s.substring(e + 1));
+    String sign;
+
+    if (s.charAt(0) == '-')
+    {
+      sign = "-";
+      s = s.substring(1);
+
+      --e;
+    }
+    else
+      sign = "";
+
+    int nDigits = e - 2;
+
+    if (exp >= nDigits)
+      return sign + s.substring(0, 1) + s.substring(2, e)
+             + zeros(exp - nDigits);
+
+    // Eliminate trailing 0's - bugzilla 14241
+    while (s.charAt(e-1) == '0')
+      e--;
+         
+    if (exp > 0)
+      return sign + s.substring(0, 1) + s.substring(2, 2 + exp) + "."
+             + s.substring(2 + exp, e);
+
+    return sign + "0." + zeros(-1 - exp) + s.substring(0, 1)
+           + s.substring(2, e);
+  }
+
+
+  /**
+   * Return a string of '0' of the given length
+   *
+   *
+   * @param n Length of the string to be returned
+   *
+   * @return a string of '0' with the given length
+   */
+  static private String zeros(int n)
+  {
+    if (n < 1)
+      return "";
+
+    char[] buf = new char[n];
+
+    for (int i = 0; i < n; i++)
+    {
+      buf[i] = '0';
+    }
+
+    return new String(buf);
+  }
+
+  /**
+   * Return a java object that's closest to the representation
+   * that should be handed to an extension.
+   *
+   * @return The value of this XNumber as a Double object
+   */
+  public Object object()
+  {
+    if(null == m_obj)
+      setObject(new Double(m_val));
+    return m_obj;
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return true if the two objects are equal 
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(XObject obj2)
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.
+    int t = obj2.getType();
+    try
+    {
+	    if (t == XObject.CLASS_NODESET)
+	      return obj2.equals(this);
+	    else if(t == XObject.CLASS_BOOLEAN)
+	      return obj2.bool() == bool();
+		else
+	       return m_val == obj2.num();
+    }
+    catch(javax.xml.transform.TransformerException te)
+    {
+      throw new org.apache.xml.utils.WrappedRuntimeException(te);
+    }
+  }
+  
+  /**
+   * Tell if this expression returns a stable number that will not change during 
+   * iterations within the expression.  This is used to determine if a proximity 
+   * position predicate can indicate that no more searching has to occur.
+   * 
+   *
+   * @return true if the expression represents a stable number.
+   */
+  public boolean isStableNumber()
+  {
+    return true;
+  }
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	visitor.visitNumberLiteral(owner, this);
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XObject.java b/src/main/java/org/apache/xpath/objects/XObject.java
new file mode 100644
index 0000000..85b2369
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XObject.java
@@ -0,0 +1,759 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XObject.java 469368 2006-10-31 04:41:36Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import java.io.Serializable;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.NodeSetDTM;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathException;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.res.XPATHErrorResources;
+
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * This class represents an XPath object, and is capable of
+ * converting the object to various types, such as a string.
+ * This class acts as the base class to other XPath type objects,
+ * such as XString, and provides polymorphic casting capabilities.
+ * @xsl.usage general
+ */
+public class XObject extends Expression implements Serializable, Cloneable
+{
+    static final long serialVersionUID = -821887098985662951L;
+
+  /**
+   * The java object which this object wraps.
+   *  @serial  
+   */
+  protected Object m_obj;  // This may be NULL!!!
+
+  /**
+   * Create an XObject.
+   */
+  public XObject(){}
+
+  /**
+   * Create an XObject.
+   *
+   * @param obj Can be any object, should be a specific type
+   * for derived classes, or null.
+   */
+  public XObject(Object obj)
+  {
+    setObject(obj);
+  }
+  
+  protected void setObject(Object obj) {
+      m_obj = obj;
+  }
+
+  /**
+   * For support of literal objects in xpaths.
+   *
+   * @param xctxt The XPath execution context.
+   *
+   * @return This object.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return this;
+  }
+
+  /**
+   * Specify if it's OK for detach to release the iterator for reuse.
+   * This function should be called with a value of false for objects that are 
+   * stored in variables.
+   * Calling this with a value of false on a XNodeSet will cause the nodeset 
+   * to be cached.
+   *
+   * @param allowRelease true if it is OK for detach to release this iterator
+   * for pooling.
+   */
+  public void allowDetachToRelease(boolean allowRelease){}
+
+  /**
+   * Detaches the <code>DTMIterator</code> from the set which it iterated
+   * over, releasing any computational resources and placing the iterator
+   * in the INVALID state. After <code>detach</code> has been invoked,
+   * calls to <code>nextNode</code> or <code>previousNode</code> will
+   * raise a runtime exception.
+   */
+  public void detach(){}
+
+  /**
+   * Forces the object to release it's resources.  This is more harsh than
+   * detach().
+   */
+  public void destruct()
+  {
+
+    if (null != m_obj)
+    {
+      allowDetachToRelease(true);
+      detach();
+
+      setObject(null);
+    }
+  }
+  
+  /**
+   * Reset for fresh reuse.
+   */
+  public void reset()
+  {
+  }
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value. Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
+          throws org.xml.sax.SAXException
+  {
+    xstr().dispatchCharactersEvents(ch);
+  }
+
+  /**
+   * Create the right XObject based on the type of the object passed.  This 
+   * function can not make an XObject that exposes DOM Nodes, NodeLists, and 
+   * NodeIterators to the XSLT stylesheet as node-sets.
+   *
+   * @param val The java object which this object will wrap.
+   *
+   * @return the right XObject based on the type of the object passed.
+   */
+  static public XObject create(Object val)
+  {
+    return XObjectFactory.create(val);
+  }
+  
+  /**
+   * Create the right XObject based on the type of the object passed.
+   * This function <emph>can</emph> make an XObject that exposes DOM Nodes, NodeLists, and 
+   * NodeIterators to the XSLT stylesheet as node-sets.
+   *
+   * @param val The java object which this object will wrap.
+   * @param xctxt The XPath context.
+   *
+   * @return the right XObject based on the type of the object passed.
+   */
+  static public XObject create(Object val, XPathContext xctxt)
+  {
+    return XObjectFactory.create(val, xctxt);
+  }
+
+  /** Constant for NULL object type */
+  public static final int CLASS_NULL = -1;
+
+  /** Constant for UNKNOWN object type */
+  public static final int CLASS_UNKNOWN = 0;
+
+  /** Constant for BOOLEAN  object type */
+  public static final int CLASS_BOOLEAN = 1;
+
+  /** Constant for NUMBER object type */
+  public static final int CLASS_NUMBER = 2;
+
+  /** Constant for STRING object type */
+  public static final int CLASS_STRING = 3;
+
+  /** Constant for NODESET object type */
+  public static final int CLASS_NODESET = 4;
+
+  /** Constant for RESULT TREE FRAGMENT object type */
+  public static final int CLASS_RTREEFRAG = 5;
+
+  /** Represents an unresolved variable type as an integer. */
+  public static final int CLASS_UNRESOLVEDVARIABLE = 600;
+
+  /**
+   * Tell what kind of class this is.
+   *
+   * @return CLASS_UNKNOWN
+   */
+  public int getType()
+  {
+    return CLASS_UNKNOWN;
+  }
+
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return type string "#UNKNOWN" + object class name
+   */
+  public String getTypeString()
+  {
+    return "#UNKNOWN (" + object().getClass().getName() + ")";
+  }
+
+  /**
+   * Cast result object to a number. Always issues an error.
+   *
+   * @return 0.0
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num() throws javax.xml.transform.TransformerException
+  {
+
+    error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER,
+          new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a number");
+
+    return 0.0;
+  }
+  
+  /**
+   * Cast result object to a number, but allow side effects, such as the 
+   * incrementing of an iterator.
+   *
+   * @return numeric value of the string conversion from the 
+   * next node in the NodeSetDTM, or NAN if no node was found
+   */
+  public double numWithSideEffects()  throws javax.xml.transform.TransformerException
+  {
+    return num();
+  }
+
+  /**
+   * Cast result object to a boolean. Always issues an error.
+   *
+   * @return false
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean bool() throws javax.xml.transform.TransformerException
+  {
+
+    error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER,
+          new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a number");
+
+    return false;
+  }
+  
+  /**
+   * Cast result object to a boolean, but allow side effects, such as the 
+   * incrementing of an iterator.
+   *
+   * @return True if there is a next node in the nodeset
+   */
+  public boolean boolWithSideEffects() throws javax.xml.transform.TransformerException
+  {
+    return bool();
+  }
+
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public XMLString xstr()
+  {
+    return XMLStringFactoryImpl.getFactory().newstr(str());
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The object as a string
+   */
+  public String str()
+  {
+    return (m_obj != null) ? m_obj.toString() : "";
+  }
+
+  /**
+   * Return the string representation of the object
+   *
+   *
+   * @return the string representation of the object
+   */
+  public String toString()
+  {
+    return str();
+  }
+
+  /**
+   * Cast result object to a result tree fragment.
+   *
+   * @param support XPath context to use for the conversion
+   *
+   * @return the objec as a result tree fragment.
+   */
+  public int rtf(XPathContext support)
+  {
+
+    int result = rtf();
+
+    if (DTM.NULL == result)
+    {
+      DTM frag = support.createDocumentFragment();
+
+      // %OPT%
+      frag.appendTextChild(str());
+
+      result = frag.getDocument();
+    }
+
+    return result;
+  }
+  
+  /**
+   * Cast result object to a result tree fragment.
+   *
+   * @param support XPath context to use for the conversion
+   *
+   * @return the objec as a result tree fragment.
+   */
+  public DocumentFragment rtree(XPathContext support)
+  {
+    DocumentFragment docFrag = null;
+    int result = rtf();
+
+    if (DTM.NULL == result)
+    {
+      DTM frag = support.createDocumentFragment();
+
+      // %OPT%
+      frag.appendTextChild(str());
+
+      docFrag = (DocumentFragment)frag.getNode(frag.getDocument());
+    }
+    else
+    {
+      DTM frag = support.getDTM(result);
+      docFrag = (DocumentFragment)frag.getNode(frag.getDocument());
+    }
+
+    return docFrag;
+  }
+  
+  
+  /**
+   * For functions to override.
+   *
+   * @return null
+   */
+  public DocumentFragment rtree()
+  {
+    return null;
+  }
+
+  /**
+   * For functions to override.
+   *
+   * @return null
+   */
+  public int rtf()
+  {
+    return DTM.NULL;
+  }
+
+  /**
+   * Return a java object that's closest to the representation
+   * that should be handed to an extension.
+   *
+   * @return The object that this class wraps
+   */
+  public Object object()
+  {
+    return m_obj;
+  }
+
+  /**
+   * Cast result object to a nodelist. Always issues an error.
+   *
+   * @return null
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public DTMIterator iter() throws javax.xml.transform.TransformerException
+  {
+
+    error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
+          new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a NodeList!");
+
+    return null;
+  }
+  
+  /**
+   * Get a fresh copy of the object.  For use with variables.
+   *
+   * @return This object, unless overridden by subclass.
+   */
+  public XObject getFresh()
+  {
+    return this;
+  }
+
+  
+  /**
+   * Cast result object to a nodelist. Always issues an error.
+   *
+   * @return null
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public NodeIterator nodeset() throws javax.xml.transform.TransformerException
+  {
+
+    error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
+          new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a NodeList!");
+
+    return null;
+  }
+  
+  /**
+   * Cast result object to a nodelist. Always issues an error.
+   *
+   * @return null
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public NodeList nodelist() throws javax.xml.transform.TransformerException
+  {
+
+    error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST,
+          new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a NodeList!");
+
+    return null;
+  }
+
+
+  /**
+   * Cast result object to a nodelist. Always issues an error.
+   *
+   * @return The object as a NodeSetDTM.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public NodeSetDTM mutableNodeset()
+          throws javax.xml.transform.TransformerException
+  {
+
+    error(XPATHErrorResources.ER_CANT_CONVERT_TO_MUTABLENODELIST,
+          new Object[]{ getTypeString() });  //"Can not convert "+getTypeString()+" to a NodeSetDTM!");
+
+    return (NodeSetDTM) m_obj;
+  }
+
+  /**
+   * Cast object to type t.
+   *
+   * @param t Type of object to cast this to
+   * @param support XPath context to use for the conversion
+   *
+   * @return This object as the given type t
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public Object castToType(int t, XPathContext support)
+          throws javax.xml.transform.TransformerException
+  {
+
+    Object result;
+
+    switch (t)
+    {
+    case CLASS_STRING :
+      result = str();
+      break;
+    case CLASS_NUMBER :
+      result = new Double(num());
+      break;
+    case CLASS_NODESET :
+      result = iter();
+      break;
+    case CLASS_BOOLEAN :
+      result = new Boolean(bool());
+      break;
+    case CLASS_UNKNOWN :
+      result = m_obj;
+      break;
+
+    // %TBD%  What to do here?
+    //    case CLASS_RTREEFRAG :
+    //      result = rtree(support);
+    //      break;
+    default :
+      error(XPATHErrorResources.ER_CANT_CONVERT_TO_TYPE,
+            new Object[]{ getTypeString(),
+                          Integer.toString(t) });  //"Can not convert "+getTypeString()+" to a type#"+t);
+
+      result = null;
+    }
+
+    return result;
+  }
+
+  /**
+   * Tell if one object is less than the other.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return True if this object is less than the given object
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean lessThan(XObject obj2)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.  Because the arguments 
+    // are backwards, we call the opposite comparison
+    // function.
+    if (obj2.getType() == XObject.CLASS_NODESET)
+      return obj2.greaterThan(this);
+
+    return this.num() < obj2.num();
+  }
+
+  /**
+   * Tell if one object is less than or equal to the other.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return True if this object is less than or equal to the given object
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean lessThanOrEqual(XObject obj2)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.  Because the arguments 
+    // are backwards, we call the opposite comparison
+    // function.
+    if (obj2.getType() == XObject.CLASS_NODESET)
+      return obj2.greaterThanOrEqual(this);
+
+    return this.num() <= obj2.num();
+  }
+
+  /**
+   * Tell if one object is greater than the other.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return True if this object is greater than the given object
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean greaterThan(XObject obj2)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.  Because the arguments 
+    // are backwards, we call the opposite comparison
+    // function.
+    if (obj2.getType() == XObject.CLASS_NODESET)
+      return obj2.lessThan(this);
+
+    return this.num() > obj2.num();
+  }
+
+  /**
+   * Tell if one object is greater than or equal to the other.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return True if this object is greater than or equal to the given object
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean greaterThanOrEqual(XObject obj2)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.  Because the arguments 
+    // are backwards, we call the opposite comparison
+    // function.
+    if (obj2.getType() == XObject.CLASS_NODESET)
+      return obj2.lessThanOrEqual(this);
+
+    return this.num() >= obj2.num();
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return True if this object is equal to the given object
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(XObject obj2)
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.
+    if (obj2.getType() == XObject.CLASS_NODESET)
+      return obj2.equals(this);
+
+    if (null != m_obj)
+    {
+      return m_obj.equals(obj2.m_obj);
+    }
+    else
+    {
+      return obj2.m_obj == null;
+    }
+  }
+
+  /**
+   * Tell if two objects are functionally not equal.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return True if this object is not equal to the given object
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean notEquals(XObject obj2)
+          throws javax.xml.transform.TransformerException
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.
+    if (obj2.getType() == XObject.CLASS_NODESET)
+      return obj2.notEquals(this);
+
+    return !equals(obj2);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg Error message to issue
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void error(String msg)
+          throws javax.xml.transform.TransformerException
+  {
+    error(msg, null);
+  }
+
+  /**
+   * Tell the user of an error, and probably throw an
+   * exception.
+   *
+   * @param msg Error message to issue
+   * @param args Arguments to use in the message
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected void error(String msg, Object[] args)
+          throws javax.xml.transform.TransformerException
+  {
+
+    String fmsg = XSLMessages.createXPATHMessage(msg, args);
+
+    // boolean shouldThrow = support.problem(m_support.XPATHPROCESSOR, 
+    //                                      m_support.ERROR,
+    //                                      null, 
+    //                                      null, fmsg, 0, 0);
+    // if(shouldThrow)
+    {
+      throw new XPathException(fmsg, this);
+    }
+  }
+  
+  
+  /**
+   * XObjects should not normally need to fix up variables.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    // no-op
+  }
+
+
+  /**
+   * Cast result object to a string.
+   *
+   *
+   * NEEDSDOC @param fsb
+   * @return The string this wraps or the empty string if null
+   */
+  public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)
+  {
+    fsb.append(str());
+  }
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	assertion(false, "callVisitors should not be called for this object!!!");
+  }
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!isSameClass(expr))
+  		return false;
+  		
+  	// If equals at the expression level calls deepEquals, I think we're 
+  	// still safe from infinite recursion since this object overrides 
+  	// equals.  I hope.
+  	if(!this.equals((XObject)expr))
+  		return false;
+  		
+  	return true;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XObjectFactory.java b/src/main/java/org/apache/xpath/objects/XObjectFactory.java
new file mode 100644
index 0000000..2a5176e
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XObjectFactory.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XObjectFactory.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisIterator;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.OneStepIterator;
+
+
+public class XObjectFactory
+{
+  
+  /**
+   * Create the right XObject based on the type of the object passed.  This 
+   * function can not make an XObject that exposes DOM Nodes, NodeLists, and 
+   * NodeIterators to the XSLT stylesheet as node-sets.
+   *
+   * @param val The java object which this object will wrap.
+   *
+   * @return the right XObject based on the type of the object passed.
+   */
+  static public XObject create(Object val)
+  {
+
+    XObject result;
+
+    if (val instanceof XObject)
+    {
+      result = (XObject) val;
+    }
+    else if (val instanceof String)
+    {
+      result = new XString((String) val);
+    }
+    else if (val instanceof Boolean)
+    {
+      result = new XBoolean((Boolean)val);
+    }
+    else if (val instanceof Double)
+    {
+      result = new XNumber(((Double) val));
+    }
+    else
+    {
+      result = new XObject(val);
+    }
+
+    return result;
+  }
+  
+  /**
+   * Create the right XObject based on the type of the object passed.
+   * This function <emph>can</emph> make an XObject that exposes DOM Nodes, NodeLists, and 
+   * NodeIterators to the XSLT stylesheet as node-sets.
+   *
+   * @param val The java object which this object will wrap.
+   * @param xctxt The XPath context.
+   *
+   * @return the right XObject based on the type of the object passed.
+   */
+  static public XObject create(Object val, XPathContext xctxt)
+  {
+
+    XObject result;
+
+    if (val instanceof XObject)
+    {
+      result = (XObject) val;
+    }
+    else if (val instanceof String)
+    {
+      result = new XString((String) val);
+    }
+    else if (val instanceof Boolean)
+    {
+      result = new XBoolean((Boolean)val);
+    }
+    else if (val instanceof Number)
+    {
+      result = new XNumber(((Number) val));
+    }
+    else if (val instanceof DTM)
+    {
+      DTM dtm = (DTM)val;
+      try
+      {
+        int dtmRoot = dtm.getDocument();
+        DTMAxisIterator iter = dtm.getAxisIterator(Axis.SELF);
+        iter.setStartNode(dtmRoot);
+        DTMIterator iterator = new OneStepIterator(iter, Axis.SELF);
+        iterator.setRoot(dtmRoot, xctxt);
+        result = new XNodeSet(iterator);
+      }
+      catch(Exception ex)
+      {
+        throw new org.apache.xml.utils.WrappedRuntimeException(ex);
+      }
+    }
+    else if (val instanceof DTMAxisIterator)
+    {
+      DTMAxisIterator iter = (DTMAxisIterator)val;
+      try
+      {
+        DTMIterator iterator = new OneStepIterator(iter, Axis.SELF);
+        iterator.setRoot(iter.getStartNode(), xctxt);
+        result = new XNodeSet(iterator);
+      }
+      catch(Exception ex)
+      {
+        throw new org.apache.xml.utils.WrappedRuntimeException(ex);
+      }
+    }
+    else if (val instanceof DTMIterator)
+    {
+      result = new XNodeSet((DTMIterator) val);
+    }
+    // This next three instanceofs are a little worrysome, since a NodeList 
+    // might also implement a Node!
+    else if (val instanceof org.w3c.dom.Node)
+    {
+      result = new XNodeSetForDOM((org.w3c.dom.Node)val, xctxt);
+    }
+    // This must come after org.w3c.dom.Node, since many Node implementations 
+    // also implement NodeList.
+    else if (val instanceof org.w3c.dom.NodeList)
+    {
+      result = new XNodeSetForDOM((org.w3c.dom.NodeList)val, xctxt);
+    }
+    else if (val instanceof org.w3c.dom.traversal.NodeIterator)
+    {
+      result = new XNodeSetForDOM((org.w3c.dom.traversal.NodeIterator)val, xctxt);
+    }
+    else
+    {
+      result = new XObject(val);
+    }
+
+    return result;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/objects/XRTreeFrag.java b/src/main/java/org/apache/xpath/objects/XRTreeFrag.java
new file mode 100644
index 0000000..048f8ab
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XRTreeFrag.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XRTreeFrag.java 469368 2006-10-31 04:41:36Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionNode;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.RTFIterator;
+
+import org.w3c.dom.NodeList;
+
+/**
+ * This class represents an XPath result tree fragment object, and is capable of
+ * converting the RTF to other types, such as a string.
+ * @xsl.usage general
+ */
+public class XRTreeFrag extends XObject implements Cloneable
+{
+    static final long serialVersionUID = -3201553822254911567L;
+  private DTMXRTreeFrag m_DTMXRTreeFrag;
+  private int m_dtmRoot = DTM.NULL;
+  protected boolean m_allowRelease = false;
+
+
+  /**
+   * Create an XRTreeFrag Object.
+   *
+   */
+  public XRTreeFrag(int root, XPathContext xctxt, ExpressionNode parent)
+  {
+    super(null);
+    exprSetParent(parent);
+    initDTM(root, xctxt);    
+  }
+  
+  /**
+   * Create an XRTreeFrag Object.
+   *
+   */
+  public XRTreeFrag(int root, XPathContext xctxt)
+  {
+    super(null); 
+   initDTM(root, xctxt); 
+  }
+  
+  private final void initDTM(int root, XPathContext xctxt){
+    m_dtmRoot = root;
+    final DTM dtm = xctxt.getDTM(root);
+    if(dtm != null){
+      m_DTMXRTreeFrag = xctxt.getDTMXRTreeFrag(xctxt.getDTMIdentity(dtm));
+    }
+  }
+  
+  /**
+   * Return a java object that's closest to the representation
+   * that should be handed to an extension.
+   *
+   * @return The object that this class wraps
+   */
+  public Object object()
+  {
+    if (m_DTMXRTreeFrag.getXPathContext() != null)
+      return new org.apache.xml.dtm.ref.DTMNodeIterator((DTMIterator)(new org.apache.xpath.NodeSetDTM(m_dtmRoot, m_DTMXRTreeFrag.getXPathContext().getDTMManager())));
+    else
+      return super.object();
+  }
+  
+  /**
+   * Create an XRTreeFrag Object.
+   *
+   */
+  public XRTreeFrag(Expression expr)
+  {
+    super(expr);
+  }
+    
+  /**
+   * Specify if it's OK for detach to release the iterator for reuse.
+   * 
+   * @param allowRelease true if it is OK for detach to release this iterator 
+   * for pooling.
+   */
+  public void allowDetachToRelease(boolean allowRelease)
+  {
+    m_allowRelease = allowRelease;
+  }
+
+  /**
+   * Detaches the <code>DTMIterator</code> from the set which it iterated
+   * over, releasing any computational resources and placing the iterator
+   * in the INVALID state. After <code>detach</code> has been invoked,
+   * calls to <code>nextNode</code> or <code>previousNode</code> will
+   * raise a runtime exception.
+   * 
+   * In general, detach should only be called once on the object.
+   */
+  public void detach(){
+    if(m_allowRelease){
+    	m_DTMXRTreeFrag.destruct();
+      setObject(null);
+    }
+  }
+  
+  /**
+   * Tell what kind of class this is.
+   *
+   * @return type CLASS_RTREEFRAG 
+   */
+  public int getType()
+  {
+    return CLASS_RTREEFRAG;
+  }
+
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return type string "#RTREEFRAG"
+   */
+  public String getTypeString()
+  {
+    return "#RTREEFRAG";
+  }
+
+  /**
+   * Cast result object to a number.
+   *
+   * @return The result tree fragment as a number or NaN
+   */
+  public double num()
+    throws javax.xml.transform.TransformerException
+  {
+
+    XMLString s = xstr();
+
+    return s.toDouble();
+  }
+
+  /**
+   * Cast result object to a boolean.  This always returns true for a RTreeFrag
+   * because it is treated like a node-set with a single root node.
+   *
+   * @return true
+   */
+  public boolean bool()
+  {
+    return true;
+  }
+  
+  private XMLString m_xmlStr = null;
+  
+  /**
+   * Cast result object to an XMLString.
+   *
+   * @return The document fragment node data or the empty string. 
+   */
+  public XMLString xstr()
+  {
+    if(null == m_xmlStr)
+      m_xmlStr = m_DTMXRTreeFrag.getDTM().getStringValue(m_dtmRoot);
+    
+    return m_xmlStr;
+  }
+  
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)
+  {
+    XString xstring = (XString)xstr();
+    xstring.appendToFsb(fsb);
+  }
+
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The document fragment node data or the empty string. 
+   */
+  public String str()
+  {
+    String str = m_DTMXRTreeFrag.getDTM().getStringValue(m_dtmRoot).toString();
+
+    return (null == str) ? "" : str;
+  }
+
+  /**
+   * Cast result object to a result tree fragment.
+   *
+   * @return The document fragment this wraps
+   */
+  public int rtf()
+  {
+    return m_dtmRoot;
+  }
+
+  /**
+   * Cast result object to a DTMIterator.
+   * dml - modified to return an RTFIterator for
+   * benefit of EXSLT object-type function in 
+   * {@code org.apache.xalan.lib.ExsltCommon}.
+   * @return The document fragment as a DTMIterator
+   */
+  public DTMIterator asNodeIterator()
+  {
+    return new RTFIterator(m_dtmRoot, m_DTMXRTreeFrag.getXPathContext().getDTMManager());
+  }
+
+  /**
+   * Cast result object to a nodelist. (special function).
+   *
+   * @return The document fragment as a nodelist
+   */
+  public NodeList convertToNodeset()
+  {
+
+    if (m_obj instanceof NodeList)
+      return (NodeList) m_obj;
+    else
+      return new org.apache.xml.dtm.ref.DTMNodeList(asNodeIterator());
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return True if the two objects are equal
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(XObject obj2)
+  {
+
+    try
+    {
+      if (XObject.CLASS_NODESET == obj2.getType())
+      {
+  
+        // In order to handle the 'all' semantics of 
+        // nodeset comparisons, we always call the 
+        // nodeset function.
+        return obj2.equals(this);
+      }
+      else if (XObject.CLASS_BOOLEAN == obj2.getType())
+      {
+        return bool() == obj2.bool();
+      }
+      else if (XObject.CLASS_NUMBER == obj2.getType())
+      {
+        return num() == obj2.num();
+      }
+      else if (XObject.CLASS_NODESET == obj2.getType())
+      {
+        return xstr().equals(obj2.xstr());
+      }
+      else if (XObject.CLASS_STRING == obj2.getType())
+      {
+        return xstr().equals(obj2.xstr());
+      }
+      else if (XObject.CLASS_RTREEFRAG == obj2.getType())
+      {
+  
+        // Probably not so good.  Think about this.
+        return xstr().equals(obj2.xstr());
+      }
+      else
+      {
+        return super.equals(obj2);
+      }
+    }
+    catch(javax.xml.transform.TransformerException te)
+    {
+      throw new org.apache.xml.utils.WrappedRuntimeException(te);
+    }
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XRTreeFragSelectWrapper.java b/src/main/java/org/apache/xpath/objects/XRTreeFragSelectWrapper.java
new file mode 100644
index 0000000..5449e71
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XRTreeFragSelectWrapper.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XRTreeFragSelectWrapper.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.utils.XMLString;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * This class makes an select statement act like an result tree fragment.
+ */
+public class XRTreeFragSelectWrapper extends XRTreeFrag implements Cloneable
+{
+    static final long serialVersionUID = -6526177905590461251L;
+  public XRTreeFragSelectWrapper(Expression expr)
+  {
+    super(expr);
+  }
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    ((Expression)m_obj).fixupVariables(vars, globalsSize);
+  }
+  
+  /**
+   * For support of literal objects in xpaths.
+   *
+   * @param xctxt The XPath execution context.
+   *
+   * @return the result of executing the select expression
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+	 XObject m_selected;
+     m_selected = ((Expression)m_obj).execute(xctxt);
+     m_selected.allowDetachToRelease(m_allowRelease);
+     if (m_selected.getType() == CLASS_STRING)
+       return m_selected;
+     else
+       return new XString(m_selected.str());
+  }
+    
+  /**
+   * Detaches the <code>DTMIterator</code> from the set which it iterated
+   * over, releasing any computational resources and placing the iterator
+   * in the INVALID state. After <code>detach</code> has been invoked,
+   * calls to <code>nextNode</code> or <code>previousNode</code> will
+   * raise a runtime exception.
+   * 
+   * In general, detach should only be called once on the object.
+   */
+  public void detach()
+  {
+	throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_DETACH_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER, null)); //"detach() not supported by XRTreeFragSelectWrapper!");
+  }
+  
+  /**
+   * Cast result object to a number.
+   *
+   * @return The result tree fragment as a number or NaN
+   */
+  public double num()
+    throws javax.xml.transform.TransformerException
+  {
+
+	throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NUM_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER, null)); //"num() not supported by XRTreeFragSelectWrapper!");
+  }
+
+  
+  /**
+   * Cast result object to an XMLString.
+   *
+   * @return The document fragment node data or the empty string. 
+   */
+  public XMLString xstr()
+  {
+	throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_XSTR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER, null)); //"xstr() not supported by XRTreeFragSelectWrapper!");
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The document fragment node data or the empty string. 
+   */
+  public String str()
+  {
+	throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_STR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER, null)); //"str() not supported by XRTreeFragSelectWrapper!");
+  }
+  
+  /**
+   * Tell what kind of class this is.
+   *
+   * @return the string type
+   */
+  public int getType()
+  {
+    return CLASS_STRING;
+  }
+
+  /**
+   * Cast result object to a result tree fragment.
+   *
+   * @return The document fragment this wraps
+   */
+  public int rtf()
+  {
+    throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_RTF_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER, null)); //"rtf() not supported by XRTreeFragSelectWrapper!");
+  }
+
+  /**
+   * Cast result object to a DTMIterator.
+   *
+   * @return The document fragment as a DTMIterator
+   */
+  public DTMIterator asNodeIterator()
+  {
+    throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_RTF_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER, null)); //"asNodeIterator() not supported by XRTreeFragSelectWrapper!");
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XString.java b/src/main/java/org/apache/xpath/objects/XString.java
new file mode 100644
index 0000000..963b832
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XString.java
@@ -0,0 +1,1122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XString.java 570108 2007-08-27 13:30:57Z zongaro $
+ */
+package org.apache.xpath.objects;
+
+import java.util.Locale;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.XMLCharacterRecognizer;
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringFactory;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+
+/**
+ * This class represents an XPath string object, and is capable of
+ * converting the string to other types, such as a number.
+ * @xsl.usage general
+ */
+public class XString extends XObject implements XMLString
+{
+    static final long serialVersionUID = 2020470518395094525L;
+
+  /** Empty string XString object */
+  public static final XString EMPTYSTRING = new XString("");
+
+  /**
+   * Construct a XString object.  This constructor exists for derived classes.
+   *
+   * @param val String object this will wrap.
+   */
+  protected XString(Object val)
+  {
+    super(val);
+  }
+
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param val String object this will wrap.
+   */
+  public XString(String val)
+  {
+    super(val);
+  }
+
+  /**
+   * Tell that this is a CLASS_STRING.
+   *
+   * @return type CLASS_STRING
+   */
+  public int getType()
+  {
+    return CLASS_STRING;
+  }
+
+  /**
+   * Given a request type, return the equivalent string.
+   * For diagnostic purposes.
+   *
+   * @return type string "#STRING"
+   */
+  public String getTypeString()
+  {
+    return "#STRING";
+  }
+
+  /**
+   * Tell if this object contains a java String object.
+   *
+   * @return true if this XMLString can return a string without creating one.
+   */
+  public boolean hasString()
+  {
+    return true;
+  }
+
+  /**
+   * Cast result object to a number.
+   *
+   * @return 0.0 if this string is null, numeric value of this string
+   * or NaN
+   */
+  public double num()
+  {
+    return toDouble();
+  }
+
+  /**
+   * Convert a string to a double -- Allowed input is in fixed
+   * notation ddd.fff.
+   *
+   * @return A double value representation of the string, or return Double.NaN
+   * if the string can not be converted.
+   */
+  public double toDouble()
+  {
+    /* XMLCharacterRecognizer.isWhiteSpace(char c) methods treats the following 
+     * characters as white space characters.
+     * ht - horizontal tab, nl - newline , cr - carriage return and sp - space
+     * trim() methods by default also takes care of these white space characters
+     * So trim() method is used to remove leading and trailing white spaces.
+     */
+	XMLString s = trim();
+	double result = Double.NaN;
+	for (int i = 0; i < s.length(); i++)
+	{
+		char c = s.charAt(i);
+    if (c != '-' && c != '.' && ( c < 0X30 || c > 0x39)) {
+            // The character is not a '-' or a '.' or a digit
+            // then return NaN because something is wrong.
+			return result;
+        }
+	}
+	try
+	{
+		result = Double.parseDouble(s.toString());
+	} catch (NumberFormatException e){}
+
+	return result;
+}
+
+  /**
+   * Cast result object to a boolean.
+   *
+   * @return True if the length of this string object is greater
+   * than 0.
+   */
+  public boolean bool()
+  {
+    return str().length() > 0;
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public XMLString xstr()
+  {
+    return this;
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public String str()
+  {
+    return (null != m_obj) ? ((String) m_obj) : "";
+  }
+
+  /**
+   * Cast result object to a result tree fragment.
+   *
+   * @param support Xpath context to use for the conversion
+   *
+   * @return A document fragment with this string as a child node
+   */
+  public int rtf(XPathContext support)
+  {
+
+    DTM frag = support.createDocumentFragment();
+
+    frag.appendTextChild(str());
+
+    return frag.getDocument();
+  }
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value. Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
+          throws org.xml.sax.SAXException
+  {
+
+    String str = str();
+
+    ch.characters(str.toCharArray(), 0, str.length());
+  }
+
+  /**
+   * Directly call the
+   * comment method on the passed LexicalHandler for the
+   * string-value.
+   *
+   * @param lh A non-null reference to a LexicalHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchAsComment(org.xml.sax.ext.LexicalHandler lh)
+          throws org.xml.sax.SAXException
+  {
+
+    String str = str();
+
+    lh.comment(str.toCharArray(), 0, str.length());
+  }
+
+  /**
+   * Returns the length of this string.
+   *
+   * @return  the length of the sequence of characters represented by this
+   *          object.
+   */
+  public int length()
+  {
+    return str().length();
+  }
+
+  /**
+   * Returns the character at the specified index. An index ranges
+   * from <code>0</code> to <code>length() - 1</code>. The first character
+   * of the sequence is at index <code>0</code>, the next at index
+   * <code>1</code>, and so on, as for array indexing.
+   *
+   * @param      index   the index of the character.
+   * @return     the character at the specified index of this string.
+   *             The first character is at index <code>0</code>.
+   * @exception  IndexOutOfBoundsException  if the <code>index</code>
+   *             argument is negative or not less than the length of this
+   *             string.
+   */
+  public char charAt(int index)
+  {
+    return str().charAt(index);
+  }
+
+  /**
+   * Copies characters from this string into the destination character
+   * array.
+   *
+   * @param      srcBegin   index of the first character in the string
+   *                        to copy.
+   * @param      srcEnd     index after the last character in the string
+   *                        to copy.
+   * @param      dst        the destination array.
+   * @param      dstBegin   the start offset in the destination array.
+   * @exception IndexOutOfBoundsException If any of the following
+   *            is true:
+   *            <ul><li><code>srcBegin</code> is negative.
+   *            <li><code>srcBegin</code> is greater than <code>srcEnd</code>
+   *            <li><code>srcEnd</code> is greater than the length of this
+   *                string
+   *            <li><code>dstBegin</code> is negative
+   *            <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
+   *                <code>dst.length</code></ul>
+   * @exception NullPointerException if <code>dst</code> is <code>null</code>
+   */
+  public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
+  {
+    str().getChars(srcBegin, srcEnd, dst, dstBegin);
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return true if the two objects are equal
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(XObject obj2)
+  {
+
+    // In order to handle the 'all' semantics of 
+    // nodeset comparisons, we always call the 
+    // nodeset function.
+    int t = obj2.getType();
+    try
+    {
+	    if (XObject.CLASS_NODESET == t)
+	      return obj2.equals(this);
+	    // If at least one object to be compared is a boolean, then each object 
+	    // to be compared is converted to a boolean as if by applying the 
+	    // boolean function. 
+	    else if(XObject.CLASS_BOOLEAN == t)
+	    	return obj2.bool() == bool();
+	    // Otherwise, if at least one object to be compared is a number, then each object 
+	    // to be compared is converted to a number as if by applying the number function. 
+	    else if(XObject.CLASS_NUMBER == t)
+	    	return obj2.num() == num();
+    }
+    catch(javax.xml.transform.TransformerException te)
+    {
+    	throw new org.apache.xml.utils.WrappedRuntimeException(te);
+    }
+
+    // Otherwise, both objects to be compared are converted to strings as 
+    // if by applying the string function. 
+    return xstr().equals(obj2.xstr());
+  }
+
+  /**
+   * Compares this string to the specified <code>String</code>.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   obj2   the object to compare this <code>String</code> against.
+   * @return  <code>true</code> if the <code>String</code>s are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public boolean equals(String obj2) {
+    return str().equals(obj2);
+  }
+
+  /**
+   * Compares this string to the specified object.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param obj2   the object to compare this <code>String</code>
+   *                     against.
+   * @return  <code>true</code> if the <code>String </code>are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public boolean equals(XMLString obj2)
+  {
+    if (obj2 != null) {
+      if (!obj2.hasString()) {
+        return obj2.equals(str());
+      } else {
+        return str().equals(obj2.toString());
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Compares this string to the specified object.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   obj2       the object to compare this <code>String</code>
+   *                     against.
+   * @return  <code>true</code> if the <code>String </code>are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public boolean equals(Object obj2)
+  {
+
+    if (null == obj2)
+      return false;
+
+      // In order to handle the 'all' semantics of 
+      // nodeset comparisons, we always call the 
+      // nodeset function.
+    else if (obj2 instanceof XNodeSet)
+      return obj2.equals(this);
+    else if(obj2 instanceof XNumber)
+    	return obj2.equals(this);
+    else
+      return str().equals(obj2.toString());
+  }
+
+  /**
+   * Compares this <code>String</code> to another <code>String</code>,
+   * ignoring case considerations.  Two strings are considered equal
+   * ignoring case if they are of the same length, and corresponding
+   * characters in the two strings are equal ignoring case.
+   *
+   * @param   anotherString   the <code>String</code> to compare this
+   *                          <code>String</code> against.
+   * @return  <code>true</code> if the argument is not <code>null</code>
+   *          and the <code>String</code>s are equal,
+   *          ignoring case; <code>false</code> otherwise.
+   * @see     #equals(Object)
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see java.lang.Character#toUpperCase(char)
+   */
+  public boolean equalsIgnoreCase(String anotherString)
+  {
+    return str().equalsIgnoreCase(anotherString);
+  }
+
+  /**
+   * Compares two strings lexicographically.
+   *
+   * @param   xstr   the <code>String</code> to be compared.
+   *
+   * @return  the value <code>0</code> if the argument string is equal to
+   *          this string; a value less than <code>0</code> if this string
+   *          is lexicographically less than the string argument; and a
+   *          value greater than <code>0</code> if this string is
+   *          lexicographically greater than the string argument.
+   * @exception java.lang.NullPointerException if <code>anotherString</code>
+   *          is <code>null</code>.
+   */
+  public int compareTo(XMLString xstr)
+  {
+
+    int len1 = this.length();
+    int len2 = xstr.length();
+    int n = Math.min(len1, len2);
+    int i = 0;
+    int j = 0;
+
+    while (n-- != 0)
+    {
+      char c1 = this.charAt(i);
+      char c2 = xstr.charAt(j);
+
+      if (c1 != c2)
+      {
+        return c1 - c2;
+      }
+
+      i++;
+      j++;
+    }
+
+    return len1 - len2;
+  }
+
+  /**
+   * Compares two strings lexicographically, ignoring case considerations.
+   * This method returns an integer whose sign is that of
+   * <code>this.toUpperCase().toLowerCase().compareTo(
+   * str.toUpperCase().toLowerCase())</code>.
+   * <p>
+   * Note that this method does <em>not</em> take locale into account,
+   * and will result in an unsatisfactory ordering for certain locales.
+   * The java.text package provides <em>collators</em> to allow
+   * locale-sensitive ordering.
+   *
+   * @param   str   the <code>String</code> to be compared.
+   * @return  a negative integer, zero, or a positive integer as the
+   *          the specified String is greater than, equal to, or less
+   *          than this String, ignoring case considerations.
+   * @see     java.text.Collator#compare(String, String)
+   * @since   1.2
+   */
+  public int compareToIgnoreCase(XMLString str)
+  {
+    // %REVIEW%  Like it says, @since 1.2. Doesn't exist in earlier
+    // versions of Java, hence we can't yet shell out to it. We can implement
+    // it as character-by-character compare, but doing so efficiently
+    // is likely to be (ahem) interesting.
+    //  
+    // However, since nobody is actually _using_ this method yet:
+    //    return str().compareToIgnoreCase(str.toString());
+    
+    throw new org.apache.xml.utils.WrappedRuntimeException(
+      new java.lang.NoSuchMethodException(
+        "Java 1.2 method, not yet implemented"));
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix beginning
+   * a specified index.
+   *
+   * @param   prefix    the prefix.
+   * @param   toffset   where to begin looking in the string.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the substring of this object starting
+   *          at index <code>toffset</code>; <code>false</code> otherwise.
+   *          The result is <code>false</code> if <code>toffset</code> is
+   *          negative or greater than the length of this
+   *          <code>String</code> object; otherwise the result is the same
+   *          as the result of the expression
+   *          <pre>
+   *          this.subString(toffset).startsWith(prefix)
+   *          </pre>
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public boolean startsWith(String prefix, int toffset)
+  {
+    return str().startsWith(prefix, toffset);
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix.
+   *
+   * @param   prefix   the prefix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the character sequence represented by
+   *          this string; <code>false</code> otherwise.
+   *          Note also that <code>true</code> will be returned if the
+   *          argument is an empty string or is equal to this
+   *          <code>String</code> object as determined by the
+   *          {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public boolean startsWith(String prefix)
+  {
+    return startsWith(prefix, 0);
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix beginning
+   * a specified index.
+   *
+   * @param   prefix    the prefix.
+   * @param   toffset   where to begin looking in the string.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the substring of this object starting
+   *          at index <code>toffset</code>; <code>false</code> otherwise.
+   *          The result is <code>false</code> if <code>toffset</code> is
+   *          negative or greater than the length of this
+   *          <code>String</code> object; otherwise the result is the same
+   *          as the result of the expression
+   *          <pre>
+   *          this.subString(toffset).startsWith(prefix)
+   *          </pre>
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public boolean startsWith(XMLString prefix, int toffset)
+  {
+
+    int to = toffset;
+    int tlim = this.length();
+    int po = 0;
+    int pc = prefix.length();
+
+    // Note: toffset might be near -1>>>1.
+    if ((toffset < 0) || (toffset > tlim - pc))
+    {
+      return false;
+    }
+
+    while (--pc >= 0)
+    {
+      if (this.charAt(to) != prefix.charAt(po))
+      {
+        return false;
+      }
+
+      to++;
+      po++;
+    }
+
+    return true;
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix.
+   *
+   * @param   prefix   the prefix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the character sequence represented by
+   *          this string; <code>false</code> otherwise.
+   *          Note also that <code>true</code> will be returned if the
+   *          argument is an empty string or is equal to this
+   *          <code>String</code> object as determined by the
+   *          {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public boolean startsWith(XMLString prefix)
+  {
+    return startsWith(prefix, 0);
+  }
+
+  /**
+   * Tests if this string ends with the specified suffix.
+   *
+   * @param   suffix   the suffix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a suffix of the character sequence represented by
+   *          this object; <code>false</code> otherwise. Note that the
+   *          result will be <code>true</code> if the argument is the
+   *          empty string or is equal to this <code>String</code> object
+   *          as determined by the {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>suffix</code> is
+   *          <code>null</code>.
+   */
+  public boolean endsWith(String suffix)
+  {
+    return str().endsWith(suffix);
+  }
+
+  /**
+   * Returns a hashcode for this string. The hashcode for a
+   * <code>String</code> object is computed as
+   * <blockquote><pre>
+   * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
+   * </pre></blockquote>
+   * using <code>int</code> arithmetic, where <code>s[i]</code> is the
+   * <i>i</i>th character of the string, <code>n</code> is the length of
+   * the string, and <code>^</code> indicates exponentiation.
+   * (The hash value of the empty string is zero.)
+   *
+   * @return  a hash code value for this object.
+   */
+  public int hashCode()
+  {
+    return str().hashCode();
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified character. If a character with value <code>ch</code> occurs
+   * in the character sequence represented by this <code>String</code>
+   * object, then the index of the first such occurrence is returned --
+   * that is, the smallest value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.charAt(<i>k</i>) == ch
+   * </pre></blockquote>
+   * is <code>true</code>. If no such character occurs in this string,
+   * then <code>-1</code> is returned.
+   *
+   * @param   ch   a character.
+   * @return  the index of the first occurrence of the character in the
+   *          character sequence represented by this object, or
+   *          <code>-1</code> if the character does not occur.
+   */
+  public int indexOf(int ch)
+  {
+    return str().indexOf(ch);
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified character, starting the search at the specified index.
+   * <p>
+   * If a character with value <code>ch</code> occurs in the character
+   * sequence represented by this <code>String</code> object at an index
+   * no smaller than <code>fromIndex</code>, then the index of the first
+   * such occurrence is returned--that is, the smallest value <i>k</i>
+   * such that:
+   * <blockquote><pre>
+   * (this.charAt(<i>k</i>) == ch) && (<i>k</i> >= fromIndex)
+   * </pre></blockquote>
+   * is true. If no such character occurs in this string at or after
+   * position <code>fromIndex</code>, then <code>-1</code> is returned.
+   * <p>
+   * There is no restriction on the value of <code>fromIndex</code>. If it
+   * is negative, it has the same effect as if it were zero: this entire
+   * string may be searched. If it is greater than the length of this
+   * string, it has the same effect as if it were equal to the length of
+   * this string: <code>-1</code> is returned.
+   *
+   * @param   ch          a character.
+   * @param   fromIndex   the index to start the search from.
+   * @return  the index of the first occurrence of the character in the
+   *          character sequence represented by this object that is greater
+   *          than or equal to <code>fromIndex</code>, or <code>-1</code>
+   *          if the character does not occur.
+   */
+  public int indexOf(int ch, int fromIndex)
+  {
+    return str().indexOf(ch, fromIndex);
+  }
+
+  /**
+   * Returns the index within this string of the last occurrence of the
+   * specified character. That is, the index returned is the largest
+   * value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.charAt(<i>k</i>) == ch
+   * </pre></blockquote>
+   * is true.
+   * The String is searched backwards starting at the last character.
+   *
+   * @param   ch   a character.
+   * @return  the index of the last occurrence of the character in the
+   *          character sequence represented by this object, or
+   *          <code>-1</code> if the character does not occur.
+   */
+  public int lastIndexOf(int ch)
+  {
+    return str().lastIndexOf(ch);
+  }
+
+  /**
+   * Returns the index within this string of the last occurrence of the
+   * specified character, searching backward starting at the specified
+   * index. That is, the index returned is the largest value <i>k</i>
+   * such that:
+   * <blockquote><pre>
+   * this.charAt(k) == ch) && (k <= fromIndex)
+   * </pre></blockquote>
+   * is true.
+   *
+   * @param   ch          a character.
+   * @param   fromIndex   the index to start the search from. There is no
+   *          restriction on the value of <code>fromIndex</code>. If it is
+   *          greater than or equal to the length of this string, it has
+   *          the same effect as if it were equal to one less than the
+   *          length of this string: this entire string may be searched.
+   *          If it is negative, it has the same effect as if it were -1:
+   *          -1 is returned.
+   * @return  the index of the last occurrence of the character in the
+   *          character sequence represented by this object that is less
+   *          than or equal to <code>fromIndex</code>, or <code>-1</code>
+   *          if the character does not occur before that point.
+   */
+  public int lastIndexOf(int ch, int fromIndex)
+  {
+    return str().lastIndexOf(ch, fromIndex);
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring. The integer returned is the smallest value
+   * <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   *
+   * @param   str   any string.
+   * @return  if the string argument occurs as a substring within this
+   *          object, then the index of the first character of the first
+   *          such substring is returned; if it does not occur as a
+   *          substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public int indexOf(String str)
+  {
+    return str().indexOf(str);
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring. The integer returned is the smallest value
+   * <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   *
+   * @param   str   any string.
+   * @return  if the string argument occurs as a substring within this
+   *          object, then the index of the first character of the first
+   *          such substring is returned; if it does not occur as a
+   *          substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public int indexOf(XMLString str)
+  {
+    return str().indexOf(str.toString());
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified substring, starting at the specified index. The integer
+   * returned is the smallest value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.startsWith(str, <i>k</i>) && (<i>k</i> >= fromIndex)
+   * </pre></blockquote>
+   * is <code>true</code>.
+   * <p>
+   * There is no restriction on the value of <code>fromIndex</code>. If
+   * it is negative, it has the same effect as if it were zero: this entire
+   * string may be searched. If it is greater than the length of this
+   * string, it has the same effect as if it were equal to the length of
+   * this string: <code>-1</code> is returned.
+   *
+   * @param   str         the substring to search for.
+   * @param   fromIndex   the index to start the search from.
+   * @return  If the string argument occurs as a substring within this
+   *          object at a starting index no smaller than
+   *          <code>fromIndex</code>, then the index of the first character
+   *          of the first such substring is returned. If it does not occur
+   *          as a substring starting at <code>fromIndex</code> or beyond,
+   *          <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>
+   */
+  public int indexOf(String str, int fromIndex)
+  {
+    return str().indexOf(str, fromIndex);
+  }
+
+  /**
+   * Returns the index within this string of the rightmost occurrence
+   * of the specified substring.  The rightmost empty string "" is
+   * considered to occur at the index value <code>this.length()</code>.
+   * The returned index is the largest value <i>k</i> such that
+   * <blockquote><pre>
+   * this.startsWith(str, k)
+   * </pre></blockquote>
+   * is true.
+   *
+   * @param   str   the substring to search for.
+   * @return  if the string argument occurs one or more times as a substring
+   *          within this object, then the index of the first character of
+   *          the last such substring is returned. If it does not occur as
+   *          a substring, <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException  if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public int lastIndexOf(String str)
+  {
+    return str().lastIndexOf(str);
+  }
+
+  /**
+   * Returns the index within this string of the last occurrence of
+   * the specified substring.
+   *
+   * @param   str         the substring to search for.
+   * @param   fromIndex   the index to start the search from. There is no
+   *          restriction on the value of fromIndex. If it is greater than
+   *          the length of this string, it has the same effect as if it
+   *          were equal to the length of this string: this entire string
+   *          may be searched. If it is negative, it has the same effect
+   *          as if it were -1: -1 is returned.
+   * @return  If the string argument occurs one or more times as a substring
+   *          within this object at a starting index no greater than
+   *          <code>fromIndex</code>, then the index of the first character of
+   *          the last such substring is returned. If it does not occur as a
+   *          substring starting at <code>fromIndex</code> or earlier,
+   *          <code>-1</code> is returned.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public int lastIndexOf(String str, int fromIndex)
+  {
+    return str().lastIndexOf(str, fromIndex);
+  }
+
+  /**
+   * Returns a new string that is a substring of this string. The
+   * substring begins with the character at the specified index and
+   * extends to the end of this string. <p>
+   * Examples:
+   * <blockquote><pre>
+   * "unhappy".substring(2) returns "happy"
+   * "Harbison".substring(3) returns "bison"
+   * "emptiness".substring(9) returns "" (an empty string)
+   * </pre></blockquote>
+   *
+   * @param      beginIndex   the beginning index, inclusive.
+   * @return     the specified substring.
+   * @exception  IndexOutOfBoundsException  if
+   *             <code>beginIndex</code> is negative or larger than the
+   *             length of this <code>String</code> object.
+   */
+  public XMLString substring(int beginIndex)
+  {
+    return new XString(str().substring(beginIndex));
+  }
+
+  /**
+   * Returns a new string that is a substring of this string. The
+   * substring begins at the specified <code>beginIndex</code> and
+   * extends to the character at index <code>endIndex - 1</code>.
+   * Thus the length of the substring is <code>endIndex-beginIndex</code>.
+   *
+   * @param      beginIndex   the beginning index, inclusive.
+   * @param      endIndex     the ending index, exclusive.
+   * @return     the specified substring.
+   * @exception  IndexOutOfBoundsException  if the
+   *             <code>beginIndex</code> is negative, or
+   *             <code>endIndex</code> is larger than the length of
+   *             this <code>String</code> object, or
+   *             <code>beginIndex</code> is larger than
+   *             <code>endIndex</code>.
+   */
+  public XMLString substring(int beginIndex, int endIndex)
+  {
+    return new XString(str().substring(beginIndex, endIndex));
+  }
+
+  /**
+   * Concatenates the specified string to the end of this string.
+   *
+   * @param   str   the <code>String</code> that is concatenated to the end
+   *                of this <code>String</code>.
+   * @return  a string that represents the concatenation of this object's
+   *          characters followed by the string argument's characters.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public XMLString concat(String str)
+  {
+
+    // %REVIEW% Make an FSB here?
+    return new XString(str().concat(str));
+  }
+
+  /**
+   * Converts all of the characters in this <code>String</code> to lower
+   * case using the rules of the given <code>Locale</code>.
+   *
+   * @param locale use the case transformation rules for this locale
+   * @return the String, converted to lowercase.
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see     java.lang.String#toUpperCase(Locale)
+   */
+  public XMLString toLowerCase(Locale locale)
+  {
+    return new XString(str().toLowerCase(locale));
+  }
+
+  /**
+   * Converts all of the characters in this <code>String</code> to lower
+   * case using the rules of the default locale, which is returned
+   * by <code>Locale.getDefault</code>.
+   * <p>
+   *
+   * @return  the string, converted to lowercase.
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see     java.lang.String#toLowerCase(Locale)
+   */
+  public XMLString toLowerCase()
+  {
+    return new XString(str().toLowerCase());
+  }
+
+  /**
+   * Converts all of the characters in this <code>String</code> to upper
+   * case using the rules of the given locale.
+   * @param locale use the case transformation rules for this locale
+   * @return the String, converted to uppercase.
+   * @see     java.lang.Character#toUpperCase(char)
+   * @see     java.lang.String#toLowerCase(Locale)
+   */
+  public XMLString toUpperCase(Locale locale)
+  {
+    return new XString(str().toUpperCase(locale));
+  }
+
+  /**
+   * Converts all of the characters in this <code>String</code> to upper
+   * case using the rules of the default locale, which is returned
+   * by <code>Locale.getDefault</code>.
+   *
+   * <p>
+   * If no character in this string has a different uppercase version,
+   * based on calling the <code>toUpperCase</code> method defined by
+   * <code>Character</code>, then the original string is returned.
+   * <p>
+   * Otherwise, this method creates a new <code>String</code> object
+   * representing a character sequence identical in length to the
+   * character sequence represented by this <code>String</code> object and
+   * with every character equal to the result of applying the method
+   * <code>Character.toUpperCase</code> to the corresponding character of
+   * this <code>String</code> object. <p>
+   * Examples:
+   * <blockquote><pre>
+   * "Fahrvergn&uuml;gen".toUpperCase() returns "FAHRVERGN&Uuml;GEN"
+   * "Visit Ljubinje!".toUpperCase() returns "VISIT LJUBINJE!"
+   * </pre></blockquote>
+   *
+   * @return  the string, converted to uppercase.
+   * @see     java.lang.Character#toUpperCase(char)
+   * @see     java.lang.String#toUpperCase(Locale)
+   */
+  public XMLString toUpperCase()
+  {
+    return new XString(str().toUpperCase());
+  }
+
+  /**
+   * Removes white space from both ends of this string.
+   *
+   * @return  this string, with white space removed from the front and end.
+   */
+  public XMLString trim()
+  {
+    return new XString(str().trim());
+  }
+
+  /**
+   * Returns whether the specified <var>ch</var> conforms to the XML 1.0 definition
+   * of whitespace.  Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S">
+   * the definition of <CODE>S</CODE></A> for details.
+   * @param   ch      Character to check as XML whitespace.
+   * @return          =true if <var>ch</var> is XML whitespace; otherwise =false.
+   */
+  private static boolean isSpace(char ch)
+  {
+    return XMLCharacterRecognizer.isWhiteSpace(ch);  // Take the easy way out for now.
+  }
+
+  /**
+   * Conditionally trim all leading and trailing whitespace in the specified String.
+   * All strings of white space are
+   * replaced by a single space character (#x20), except spaces after punctuation which
+   * receive double spaces if doublePunctuationSpaces is true.
+   * This function may be useful to a formatter, but to get first class
+   * results, the formatter should probably do it's own white space handling
+   * based on the semantics of the formatting object.
+   *
+   * @param   trimHead    Trim leading whitespace?
+   * @param   trimTail    Trim trailing whitespace?
+   * @param   doublePunctuationSpaces    Use double spaces for punctuation?
+   * @return              The trimmed string.
+   */
+  public XMLString fixWhiteSpace(boolean trimHead, boolean trimTail,
+                                 boolean doublePunctuationSpaces)
+  {
+
+    // %OPT% !!!!!!!
+    int len = this.length();
+    char[] buf = new char[len];
+
+    this.getChars(0, len, buf, 0);
+
+    boolean edit = false;
+    int s;
+
+    for (s = 0; s < len; s++)
+    {
+      if (isSpace(buf[s]))
+      {
+        break;
+      }
+    }
+
+    /* replace S to ' '. and ' '+ -> single ' '. */
+    int d = s;
+    boolean pres = false;
+
+    for (; s < len; s++)
+    {
+      char c = buf[s];
+
+      if (isSpace(c))
+      {
+        if (!pres)
+        {
+          if (' ' != c)
+          {
+            edit = true;
+          }
+
+          buf[d++] = ' ';
+
+          if (doublePunctuationSpaces && (s != 0))
+          {
+            char prevChar = buf[s - 1];
+
+            if (!((prevChar == '.') || (prevChar == '!')
+                  || (prevChar == '?')))
+            {
+              pres = true;
+            }
+          }
+          else
+          {
+            pres = true;
+          }
+        }
+        else
+        {
+          edit = true;
+          pres = true;
+        }
+      }
+      else
+      {
+        buf[d++] = c;
+        pres = false;
+      }
+    }
+
+    if (trimTail && 1 <= d && ' ' == buf[d - 1])
+    {
+      edit = true;
+
+      d--;
+    }
+
+    int start = 0;
+
+    if (trimHead && 0 < d && ' ' == buf[0])
+    {
+      edit = true;
+
+      start++;
+    }
+
+    XMLStringFactory xsf = XMLStringFactoryImpl.getFactory();
+
+    return edit ? xsf.newstr(new String(buf, start, d - start)) : this;
+  }
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	visitor.visitStringLiteral(owner, this);
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/objects/XStringForChars.java b/src/main/java/org/apache/xpath/objects/XStringForChars.java
new file mode 100644
index 0000000..fe18d23
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XStringForChars.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XStringForChars.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xpath.res.XPATHErrorResources;
+
+
+/**
+ * This class will wrap a FastStringBuffer and allow for
+ */
+public class XStringForChars extends XString
+{
+    static final long serialVersionUID = -2235248887220850467L;
+  /** The start position in the fsb. */
+  int m_start;
+  
+  /** The length of the string. */
+  int m_length;
+  
+  protected String m_strCache = null;
+  
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param val FastStringBuffer object this will wrap, must be non-null.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   */
+  public XStringForChars(char[] val, int start, int length)
+  {
+    super(val);
+    m_start = start;
+    m_length = length;
+    if(null == val)
+      throw new IllegalArgumentException(
+                          XSLMessages.createXPATHMessage(XPATHErrorResources.ER_FASTSTRINGBUFFER_CANNOT_BE_NULL, null)); //"The FastStringBuffer argument can not be null!!");
+  }
+
+
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param val String object this will wrap.
+   */
+  private XStringForChars(String val)
+  {
+    super(val);
+    throw new IllegalArgumentException(
+                      XSLMessages.createXPATHMessage(XPATHErrorResources.ER_XSTRINGFORCHARS_CANNOT_TAKE_STRING, null)); //"XStringForChars can not take a string for an argument!");
+  }
+  
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public FastStringBuffer fsb()
+  {
+    throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_FSB_NOT_SUPPORTED_XSTRINGFORCHARS, null)); //"fsb() not supported for XStringForChars!");
+  }
+  
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)
+  {
+    fsb.append((char[])m_obj, m_start, m_length);
+  }
+
+  
+  /**
+   * Tell if this object contains a java String object.
+   * 
+   * @return true if this XMLString can return a string without creating one.
+   */
+  public boolean hasString()
+  {
+    return (null != m_strCache);
+  }
+
+  
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public String str()
+  {
+    if(null == m_strCache)
+      m_strCache = new String((char[])m_obj, m_start, m_length);
+    
+    return m_strCache;
+  }
+  
+
+  /**
+   * Since this object is incomplete without the length and the offset, we 
+   * have to convert to a string when this function is called.
+   *
+   * @return The java String representation of this object.
+   */
+  public Object object()
+  {
+    return str();
+  }
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value. Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
+      throws org.xml.sax.SAXException
+  {
+    ch.characters((char[])m_obj, m_start, m_length);
+  }
+      
+  /**
+   * Directly call the
+   * comment method on the passed LexicalHandler for the
+   * string-value.
+   *
+   * @param lh A non-null reference to a LexicalHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchAsComment(org.xml.sax.ext.LexicalHandler lh)
+      throws org.xml.sax.SAXException
+  {
+    lh.comment((char[])m_obj, m_start, m_length);
+  }
+  
+  /**
+   * Returns the length of this string.
+   *
+   * @return  the length of the sequence of characters represented by this
+   *          object.
+   */
+  public int length()
+  {
+    return m_length;
+  }
+
+  /**
+   * Returns the character at the specified index. An index ranges
+   * from <code>0</code> to <code>length() - 1</code>. The first character
+   * of the sequence is at index <code>0</code>, the next at index
+   * <code>1</code>, and so on, as for array indexing.
+   *
+   * @param      index   the index of the character.
+   * @return     the character at the specified index of this string.
+   *             The first character is at index <code>0</code>.
+   * @exception  IndexOutOfBoundsException  if the <code>index</code>
+   *             argument is negative or not less than the length of this
+   *             string.
+   */
+  public char charAt(int index)
+  {
+    return ((char[])m_obj)[index+m_start];
+  }
+
+  /**
+   * Copies characters from this string into the destination character
+   * array.
+   *
+   * @param      srcBegin   index of the first character in the string
+   *                        to copy.
+   * @param      srcEnd     index after the last character in the string
+   *                        to copy.
+   * @param      dst        the destination array.
+   * @param      dstBegin   the start offset in the destination array.
+   * @exception IndexOutOfBoundsException If any of the following
+   *            is true:
+   *            <ul><li><code>srcBegin</code> is negative.
+   *            <li><code>srcBegin</code> is greater than <code>srcEnd</code>
+   *            <li><code>srcEnd</code> is greater than the length of this
+   *                string
+   *            <li><code>dstBegin</code> is negative
+   *            <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
+   *                <code>dst.length</code></ul>
+   * @exception NullPointerException if <code>dst</code> is <code>null</code>
+   */
+  public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
+  {
+    System.arraycopy((char[])m_obj, m_start+srcBegin, dst, dstBegin, srcEnd);
+  }
+  
+}
diff --git a/src/main/java/org/apache/xpath/objects/XStringForFSB.java b/src/main/java/org/apache/xpath/objects/XStringForFSB.java
new file mode 100644
index 0000000..083bfbc
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/XStringForFSB.java
@@ -0,0 +1,986 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XStringForFSB.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.objects;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.XMLCharacterRecognizer;
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringFactory;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * This class will wrap a FastStringBuffer and allow for
+ */
+public class XStringForFSB extends XString
+{
+    static final long serialVersionUID = -1533039186550674548L;
+
+  /** The start position in the fsb. */
+  int m_start;
+
+  /** The length of the string. */
+  int m_length;
+
+  /** If the str() function is called, the string will be cached here. */
+  protected String m_strCache = null;
+
+  /** cached hash code */
+  protected int m_hash = 0;
+
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param val FastStringBuffer object this will wrap, must be non-null.
+   * @param start The start position in the array.
+   * @param length The number of characters to read from the array.
+   */
+  public XStringForFSB(FastStringBuffer val, int start, int length)
+  {
+
+    super(val);
+
+    m_start = start;
+    m_length = length;
+
+    if (null == val)
+      throw new IllegalArgumentException(
+        XSLMessages.createXPATHMessage(XPATHErrorResources.ER_FASTSTRINGBUFFER_CANNOT_BE_NULL, null));
+  }
+
+  /**
+   * Construct a XNodeSet object.
+   *
+   * @param val String object this will wrap.
+   */
+  private XStringForFSB(String val)
+  {
+
+    super(val);
+
+    throw new IllegalArgumentException(
+      XSLMessages.createXPATHMessage(XPATHErrorResources.ER_FSB_CANNOT_TAKE_STRING, null)); // "XStringForFSB can not take a string for an argument!");
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public FastStringBuffer fsb()
+  {
+    return ((FastStringBuffer) m_obj);
+  }
+  
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)
+  {
+    // %OPT% !!! FSB has to be updated to take partial fsb's for append.
+    fsb.append(str());
+  }
+
+  /**
+   * Tell if this object contains a java String object.
+   *
+   * @return true if this XMLString can return a string without creating one.
+   */
+  public boolean hasString()
+  {
+    return (null != m_strCache);
+  }
+
+//  /** NEEDSDOC Field strCount */
+//  public static int strCount = 0;
+//
+//  /** NEEDSDOC Field xtable */
+//  static java.util.Hashtable xtable = new java.util.Hashtable();
+
+  /**
+   * Since this object is incomplete without the length and the offset, we 
+   * have to convert to a string when this function is called.
+   *
+   * @return The java String representation of this object.
+   */
+  public Object object()
+  {
+    return str();
+  }
+
+  /**
+   * Cast result object to a string.
+   *
+   * @return The string this wraps or the empty string if null
+   */
+  public String str()
+  {
+
+    if (null == m_strCache)
+    {
+      m_strCache = fsb().getString(m_start, m_length);
+
+//      strCount++;
+//
+//      RuntimeException e = new RuntimeException("Bad!  Bad!");
+//      java.io.CharArrayWriter writer = new java.io.CharArrayWriter();
+//      java.io.PrintWriter pw = new java.io.PrintWriter(writer);
+//
+//      e.printStackTrace(pw);
+//
+//      String str = writer.toString();
+//
+//      str = str.substring(0, 600);
+//
+//      if (null == xtable.get(str))
+//      {
+//        xtable.put(str, str);
+//        System.out.println(str);
+//      }
+//      System.out.println("strCount: " + strCount);
+
+//      throw e;
+//      e.printStackTrace();
+      // System.exit(-1);
+    }
+
+    return m_strCache;
+  }
+
+  /**
+   * Directly call the
+   * characters method on the passed ContentHandler for the
+   * string-value. Multiple calls to the
+   * ContentHandler's characters methods may well occur for a single call to
+   * this method.
+   *
+   * @param ch A non-null reference to a ContentHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
+          throws org.xml.sax.SAXException
+  {
+    fsb().sendSAXcharacters(ch, m_start, m_length);
+  }
+
+  /**
+   * Directly call the
+   * comment method on the passed LexicalHandler for the
+   * string-value.
+   *
+   * @param lh A non-null reference to a LexicalHandler.
+   *
+   * @throws org.xml.sax.SAXException
+   */
+  public void dispatchAsComment(org.xml.sax.ext.LexicalHandler lh)
+          throws org.xml.sax.SAXException
+  {
+    fsb().sendSAXComment(lh, m_start, m_length);
+  }
+
+  /**
+   * Returns the length of this string.
+   *
+   * @return  the length of the sequence of characters represented by this
+   *          object.
+   */
+  public int length()
+  {
+    return m_length;
+  }
+
+  /**
+   * Returns the character at the specified index. An index ranges
+   * from <code>0</code> to <code>length() - 1</code>. The first character
+   * of the sequence is at index <code>0</code>, the next at index
+   * <code>1</code>, and so on, as for array indexing.
+   *
+   * @param      index   the index of the character.
+   * @return     the character at the specified index of this string.
+   *             The first character is at index <code>0</code>.
+   * @exception  IndexOutOfBoundsException  if the <code>index</code>
+   *             argument is negative or not less than the length of this
+   *             string.
+   */
+  public char charAt(int index)
+  {
+    return fsb().charAt(m_start + index);
+  }
+
+  /**
+   * Copies characters from this string into the destination character
+   * array.
+   *
+   * @param      srcBegin   index of the first character in the string
+   *                        to copy.
+   * @param      srcEnd     index after the last character in the string
+   *                        to copy.
+   * @param      dst        the destination array.
+   * @param      dstBegin   the start offset in the destination array.
+   * @exception IndexOutOfBoundsException If any of the following
+   *            is true:
+   *            <ul><li><code>srcBegin</code> is negative.
+   *            <li><code>srcBegin</code> is greater than <code>srcEnd</code>
+   *            <li><code>srcEnd</code> is greater than the length of this
+   *                string
+   *            <li><code>dstBegin</code> is negative
+   *            <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
+   *                <code>dst.length</code></ul>
+   * @exception NullPointerException if <code>dst</code> is <code>null</code>
+   */
+  public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
+  {
+
+    // %OPT% Need to call this on FSB when it is implemented.
+    // %UNTESTED% (I don't think anyone calls this yet?)
+    int n = srcEnd - srcBegin;
+
+    if (n > m_length)
+      n = m_length;
+
+    if (n > (dst.length - dstBegin))
+      n = (dst.length - dstBegin);
+
+    int end = srcBegin + m_start + n;
+    int d = dstBegin;
+    FastStringBuffer fsb = fsb();
+
+    for (int i = srcBegin + m_start; i < end; i++)
+    {
+      dst[d++] = fsb.charAt(i);
+    }
+  }
+
+  /**
+   * Compares this string to the specified object.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   obj2       the object to compare this <code>String</code>
+   *                     against.
+   *
+   * @return  <code>true</code> if the <code>String </code>are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public boolean equals(XMLString obj2)
+  {
+
+    if (this == obj2)
+    {
+      return true;
+    }
+
+    int n = m_length;
+
+    if (n == obj2.length())
+    {
+      FastStringBuffer fsb = fsb();
+      int i = m_start;
+      int j = 0;
+
+      while (n-- != 0)
+      {
+        if (fsb.charAt(i) != obj2.charAt(j))
+        {
+          return false;
+        }
+
+        i++;
+        j++;
+      }
+
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param obj2 Object to compare this to
+   *
+   * @return true if the two objects are equal
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(XObject obj2)
+  {
+
+    if (this == obj2)
+    {
+      return true;
+    }
+    if(obj2.getType() == XObject.CLASS_NUMBER)
+    	return obj2.equals(this);
+
+    String str = obj2.str();
+    int n = m_length;
+
+    if (n == str.length())
+    {
+      FastStringBuffer fsb = fsb();
+      int i = m_start;
+      int j = 0;
+
+      while (n-- != 0)
+      {
+        if (fsb.charAt(i) != str.charAt(j))
+        {
+          return false;
+        }
+
+        i++;
+        j++;
+      }
+
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Tell if two objects are functionally equal.
+   *
+   * @param anotherString Object to compare this to
+   *
+   * @return true if the two objects are equal
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean equals(String anotherString)
+  {
+
+    int n = m_length;
+
+    if (n == anotherString.length())
+    {
+      FastStringBuffer fsb = fsb();
+      int i = m_start;
+      int j = 0;
+
+      while (n-- != 0)
+      {
+        if (fsb.charAt(i) != anotherString.charAt(j))
+        {
+          return false;
+        }
+
+        i++;
+        j++;
+      }
+
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Compares this string to the specified object.
+   * The result is <code>true</code> if and only if the argument is not
+   * <code>null</code> and is a <code>String</code> object that represents
+   * the same sequence of characters as this object.
+   *
+   * @param   obj2       the object to compare this <code>String</code>
+   *                     against.
+   *
+   * @return  <code>true</code> if the <code>String </code>are equal;
+   *          <code>false</code> otherwise.
+   * @see     java.lang.String#compareTo(java.lang.String)
+   * @see     java.lang.String#equalsIgnoreCase(java.lang.String)
+   */
+  public boolean equals(Object obj2)
+  {
+
+    if (null == obj2)
+      return false;
+      
+    if(obj2 instanceof XNumber)
+    	return obj2.equals(this);
+
+      // In order to handle the 'all' semantics of 
+      // nodeset comparisons, we always call the 
+      // nodeset function.
+    else if (obj2 instanceof XNodeSet)
+      return obj2.equals(this);
+    else if (obj2 instanceof XStringForFSB)
+      return equals((XMLString) obj2);
+    else
+      return equals(obj2.toString());
+  }
+
+  /**
+   * Compares this <code>String</code> to another <code>String</code>,
+   * ignoring case considerations.  Two strings are considered equal
+   * ignoring case if they are of the same length, and corresponding
+   * characters in the two strings are equal ignoring case.
+   *
+   * @param   anotherString   the <code>String</code> to compare this
+   *                          <code>String</code> against.
+   * @return  <code>true</code> if the argument is not <code>null</code>
+   *          and the <code>String</code>s are equal,
+   *          ignoring case; <code>false</code> otherwise.
+   * @see     #equals(Object)
+   * @see     java.lang.Character#toLowerCase(char)
+   * @see java.lang.Character#toUpperCase(char)
+   */
+  public boolean equalsIgnoreCase(String anotherString)
+  {
+    return (m_length == anotherString.length())
+           ? str().equalsIgnoreCase(anotherString) : false;
+  }
+
+  /**
+   * Compares two strings lexicographically.
+   *
+   * @param   xstr   the <code>String</code> to be compared.
+   *
+   * @return  the value <code>0</code> if the argument string is equal to
+   *          this string; a value less than <code>0</code> if this string
+   *          is lexicographically less than the string argument; and a
+   *          value greater than <code>0</code> if this string is
+   *          lexicographically greater than the string argument.
+   * @exception java.lang.NullPointerException if <code>anotherString</code>
+   *          is <code>null</code>.
+   */
+  public int compareTo(XMLString xstr)
+  {
+
+    int len1 = m_length;
+    int len2 = xstr.length();
+    int n = Math.min(len1, len2);
+    FastStringBuffer fsb = fsb();
+    int i = m_start;
+    int j = 0;
+
+    while (n-- != 0)
+    {
+      char c1 = fsb.charAt(i);
+      char c2 = xstr.charAt(j);
+
+      if (c1 != c2)
+      {
+        return c1 - c2;
+      }
+
+      i++;
+      j++;
+    }
+
+    return len1 - len2;
+  }
+
+  /**
+   * Compares two strings lexicographically, ignoring case considerations.
+   * This method returns an integer whose sign is that of
+   * <code>this.toUpperCase().toLowerCase().compareTo(
+   * str.toUpperCase().toLowerCase())</code>.
+   * <p>
+   * Note that this method does <em>not</em> take locale into account,
+   * and will result in an unsatisfactory ordering for certain locales.
+   * The java.text package provides <em>collators</em> to allow
+   * locale-sensitive ordering.
+   *
+   * @param   xstr   the <code>String</code> to be compared.
+   *
+   * @return  a negative integer, zero, or a positive integer as the
+   *          the specified String is greater than, equal to, or less
+   *          than this String, ignoring case considerations.
+   * @see     java.text.Collator#compare(String, String)
+   * @since   1.2
+   */
+  public int compareToIgnoreCase(XMLString xstr)
+  {
+
+    int len1 = m_length;
+    int len2 = xstr.length();
+    int n = Math.min(len1, len2);
+    FastStringBuffer fsb = fsb();
+    int i = m_start;
+    int j = 0;
+
+    while (n-- != 0)
+    {
+      char c1 = Character.toLowerCase(fsb.charAt(i));
+      char c2 = Character.toLowerCase(xstr.charAt(j));
+
+      if (c1 != c2)
+      {
+        return c1 - c2;
+      }
+
+      i++;
+      j++;
+    }
+
+    return len1 - len2;
+  }
+
+  /**
+   * Returns a hashcode for this string. The hashcode for a
+   * <code>String</code> object is computed as
+   * <blockquote><pre>
+   * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
+   * </pre></blockquote>
+   * using <code>int</code> arithmetic, where <code>s[i]</code> is the
+   * <i>i</i>th character of the string, <code>n</code> is the length of
+   * the string, and <code>^</code> indicates exponentiation.
+   * (The hash value of the empty string is zero.)
+   *
+   * @return  a hash code value for this object.
+   */
+  public int hashCode()
+  {
+    // Commenting this out because in JDK1.1.8 and VJ++
+    // we don't match XMLStrings. Defaulting to the super
+    // causes us to create a string, but at this point
+    // this only seems to get called in key processing.
+    // Maybe we can live with it?
+    
+/*
+    int h = m_hash;
+
+    if (h == 0)
+    {
+      int off = m_start;
+      int len = m_length;
+      FastStringBuffer fsb = fsb();
+
+      for (int i = 0; i < len; i++)
+      {
+        h = 31 * h + fsb.charAt(off);
+
+        off++;
+      }
+
+      m_hash = h;
+    }
+    */
+
+    return super.hashCode(); // h;
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix beginning
+   * a specified index.
+   *
+   * @param   prefix    the prefix.
+   * @param   toffset   where to begin looking in the string.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the substring of this object starting
+   *          at index <code>toffset</code>; <code>false</code> otherwise.
+   *          The result is <code>false</code> if <code>toffset</code> is
+   *          negative or greater than the length of this
+   *          <code>String</code> object; otherwise the result is the same
+   *          as the result of the expression
+   *          <pre>
+   *          this.subString(toffset).startsWith(prefix)
+   *          </pre>
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   */
+  public boolean startsWith(XMLString prefix, int toffset)
+  {
+
+    FastStringBuffer fsb = fsb();
+    int to = m_start + toffset;
+    int tlim = m_start + m_length;
+    int po = 0;
+    int pc = prefix.length();
+
+    // Note: toffset might be near -1>>>1.
+    if ((toffset < 0) || (toffset > m_length - pc))
+    {
+      return false;
+    }
+
+    while (--pc >= 0)
+    {
+      if (fsb.charAt(to) != prefix.charAt(po))
+      {
+        return false;
+      }
+
+      to++;
+      po++;
+    }
+
+    return true;
+  }
+
+  /**
+   * Tests if this string starts with the specified prefix.
+   *
+   * @param   prefix   the prefix.
+   * @return  <code>true</code> if the character sequence represented by the
+   *          argument is a prefix of the character sequence represented by
+   *          this string; <code>false</code> otherwise.
+   *          Note also that <code>true</code> will be returned if the
+   *          argument is an empty string or is equal to this
+   *          <code>String</code> object as determined by the
+   *          {@link #equals(Object)} method.
+   * @exception java.lang.NullPointerException if <code>prefix</code> is
+   *          <code>null</code>.
+   * @since   JDK1. 0
+   */
+  public boolean startsWith(XMLString prefix)
+  {
+    return startsWith(prefix, 0);
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified character. If a character with value <code>ch</code> occurs
+   * in the character sequence represented by this <code>String</code>
+   * object, then the index of the first such occurrence is returned --
+   * that is, the smallest value <i>k</i> such that:
+   * <blockquote><pre>
+   * this.charAt(<i>k</i>) == ch
+   * </pre></blockquote>
+   * is <code>true</code>. If no such character occurs in this string,
+   * then <code>-1</code> is returned.
+   *
+   * @param   ch   a character.
+   * @return  the index of the first occurrence of the character in the
+   *          character sequence represented by this object, or
+   *          <code>-1</code> if the character does not occur.
+   */
+  public int indexOf(int ch)
+  {
+    return indexOf(ch, 0);
+  }
+
+  /**
+   * Returns the index within this string of the first occurrence of the
+   * specified character, starting the search at the specified index.
+   * <p>
+   * If a character with value <code>ch</code> occurs in the character
+   * sequence represented by this <code>String</code> object at an index
+   * no smaller than <code>fromIndex</code>, then the index of the first
+   * such occurrence is returned--that is, the smallest value <i>k</i>
+   * such that:
+   * <blockquote><pre>
+   * (this.charAt(<i>k</i>) == ch) && (<i>k</i> >= fromIndex)
+   * </pre></blockquote>
+   * is true. If no such character occurs in this string at or after
+   * position <code>fromIndex</code>, then <code>-1</code> is returned.
+   * <p>
+   * There is no restriction on the value of <code>fromIndex</code>. If it
+   * is negative, it has the same effect as if it were zero: this entire
+   * string may be searched. If it is greater than the length of this
+   * string, it has the same effect as if it were equal to the length of
+   * this string: <code>-1</code> is returned.
+   *
+   * @param   ch          a character.
+   * @param   fromIndex   the index to start the search from.
+   * @return  the index of the first occurrence of the character in the
+   *          character sequence represented by this object that is greater
+   *          than or equal to <code>fromIndex</code>, or <code>-1</code>
+   *          if the character does not occur.
+   */
+  public int indexOf(int ch, int fromIndex)
+  {
+
+    int max = m_start + m_length;
+    FastStringBuffer fsb = fsb();
+
+    if (fromIndex < 0)
+    {
+      fromIndex = 0;
+    }
+    else if (fromIndex >= m_length)
+    {
+
+      // Note: fromIndex might be near -1>>>1.
+      return -1;
+    }
+
+    for (int i = m_start + fromIndex; i < max; i++)
+    {
+      if (fsb.charAt(i) == ch)
+      {
+        return i - m_start;
+      }
+    }
+
+    return -1;
+  }
+
+  /**
+   * Returns a new string that is a substring of this string. The
+   * substring begins with the character at the specified index and
+   * extends to the end of this string. <p>
+   * Examples:
+   * <blockquote><pre>
+   * "unhappy".substring(2) returns "happy"
+   * "Harbison".substring(3) returns "bison"
+   * "emptiness".substring(9) returns "" (an empty string)
+   * </pre></blockquote>
+   *
+   * @param      beginIndex   the beginning index, inclusive.
+   * @return     the specified substring.
+   * @exception  IndexOutOfBoundsException  if
+   *             <code>beginIndex</code> is negative or larger than the
+   *             length of this <code>String</code> object.
+   */
+  public XMLString substring(int beginIndex)
+  {
+
+    int len = m_length - beginIndex;
+
+    if (len <= 0)
+      return XString.EMPTYSTRING;
+    else
+    {
+      int start = m_start + beginIndex;
+
+      return new XStringForFSB(fsb(), start, len);
+    }
+  }
+
+  /**
+   * Returns a new string that is a substring of this string. The
+   * substring begins at the specified <code>beginIndex</code> and
+   * extends to the character at index <code>endIndex - 1</code>.
+   * Thus the length of the substring is <code>endIndex-beginIndex</code>.
+   *
+   * @param      beginIndex   the beginning index, inclusive.
+   * @param      endIndex     the ending index, exclusive.
+   * @return     the specified substring.
+   * @exception  IndexOutOfBoundsException  if the
+   *             <code>beginIndex</code> is negative, or
+   *             <code>endIndex</code> is larger than the length of
+   *             this <code>String</code> object, or
+   *             <code>beginIndex</code> is larger than
+   *             <code>endIndex</code>.
+   */
+  public XMLString substring(int beginIndex, int endIndex)
+  {
+
+    int len = endIndex - beginIndex;
+
+    if (len > m_length)
+      len = m_length;
+
+    if (len <= 0)
+      return XString.EMPTYSTRING;
+    else
+    {
+      int start = m_start + beginIndex;
+
+      return new XStringForFSB(fsb(), start, len);
+    }
+  }
+
+  /**
+   * Concatenates the specified string to the end of this string.
+   *
+   * @param   str   the <code>String</code> that is concatenated to the end
+   *                of this <code>String</code>.
+   * @return  a string that represents the concatenation of this object's
+   *          characters followed by the string argument's characters.
+   * @exception java.lang.NullPointerException if <code>str</code> is
+   *          <code>null</code>.
+   */
+  public XMLString concat(String str)
+  {
+
+    // %OPT% Make an FSB here?
+    return new XString(str().concat(str));
+  }
+
+  /**
+   * Removes white space from both ends of this string.
+   *
+   * @return  this string, with white space removed from the front and end.
+   */
+  public XMLString trim()
+  {
+    return fixWhiteSpace(true, true, false);
+  }
+
+  /**
+   * Returns whether the specified <var>ch</var> conforms to the XML 1.0 definition
+   * of whitespace.  Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S">
+   * the definition of <CODE>S</CODE></A> for details.
+   * @param   ch      Character to check as XML whitespace.
+   * @return          =true if <var>ch</var> is XML whitespace; otherwise =false.
+   */
+  private static boolean isSpace(char ch)
+  {
+    return XMLCharacterRecognizer.isWhiteSpace(ch);  // Take the easy way out for now.
+  }
+
+  /**
+   * Conditionally trim all leading and trailing whitespace in the specified String.
+   * All strings of white space are
+   * replaced by a single space character (#x20), except spaces after punctuation which
+   * receive double spaces if doublePunctuationSpaces is true.
+   * This function may be useful to a formatter, but to get first class
+   * results, the formatter should probably do it's own white space handling
+   * based on the semantics of the formatting object.
+   *
+   * @param   trimHead    Trim leading whitespace?
+   * @param   trimTail    Trim trailing whitespace?
+   * @param   doublePunctuationSpaces    Use double spaces for punctuation?
+   * @return              The trimmed string.
+   */
+  public XMLString fixWhiteSpace(boolean trimHead, boolean trimTail,
+                                 boolean doublePunctuationSpaces)
+  {
+
+    int end = m_length + m_start;
+    char[] buf = new char[m_length];
+    FastStringBuffer fsb = fsb();
+    boolean edit = false;
+
+    /* replace S to ' '. and ' '+ -> single ' '. */
+    int d = 0;
+    boolean pres = false;
+
+    for (int s = m_start; s < end; s++)
+    {
+      char c = fsb.charAt(s);
+
+      if (isSpace(c))
+      {
+        if (!pres)
+        {
+          if (' ' != c)
+          {
+            edit = true;
+          }
+
+          buf[d++] = ' ';
+
+          if (doublePunctuationSpaces && (d != 0))
+          {
+            char prevChar = buf[d - 1];
+
+            if (!((prevChar == '.') || (prevChar == '!')
+                  || (prevChar == '?')))
+            {
+              pres = true;
+            }
+          }
+          else
+          {
+            pres = true;
+          }
+        }
+        else
+        {
+          edit = true;
+          pres = true;
+        }
+      }
+      else
+      {
+        buf[d++] = c;
+        pres = false;
+      }
+    }
+
+    if (trimTail && 1 <= d && ' ' == buf[d - 1])
+    {
+      edit = true;
+
+      d--;
+    }
+
+    int start = 0;
+
+    if (trimHead && 0 < d && ' ' == buf[0])
+    {
+      edit = true;
+
+      start++;
+    }
+
+    XMLStringFactory xsf = XMLStringFactoryImpl.getFactory();
+
+    return edit ? xsf.newstr(buf, start, d - start) : this;
+  }
+
+  /**
+   * Convert a string to a double -- Allowed input is in fixed
+   * notation ddd.fff.
+   *
+   * %OPT% CHECK PERFORMANCE against generating a Java String and
+   * converting it to double. The advantage of running in native
+   * machine code -- perhaps even microcode, on some systems -- may
+   * more than make up for the cost of allocating and discarding the
+   * additional object. We need to benchmark this. 
+   *
+   * %OPT% More importantly, we need to decide whether we _care_ about
+   * the performance of this operation. Does XString.toDouble constitute
+   * any measurable percentage of our typical runtime? I suspect not!
+   *
+   * @return A double value representation of the string, or return Double.NaN 
+   * if the string can not be converted.  */
+  public double toDouble()
+  {
+    if(m_length == 0)
+      return Double.NaN;
+    int i;
+    char c;
+    String valueString = fsb().getString(m_start,m_length);
+    
+    // The following are permitted in the Double.valueOf, but not by the XPath spec:
+    // - a plus sign
+    // - The use of e or E to indicate exponents
+    // - trailing f, F, d, or D
+    // See function comments; not sure if this is slower than actually doing the
+    // conversion ourselves (as was before).
+    
+    for (i=0;i<m_length;i++)
+      if (!XMLCharacterRecognizer.isWhiteSpace(valueString.charAt(i)))
+        break;
+    if (i == m_length) return Double.NaN;
+    if (valueString.charAt(i) == '-')
+      i++;
+    for (;i<m_length;i++) {
+      c = valueString.charAt(i);
+      if (c != '.' && (c < '0' || c > '9'))
+        break;
+    }   	    	
+    for (;i<m_length;i++)
+      if (!XMLCharacterRecognizer.isWhiteSpace(valueString.charAt(i)))
+        break;
+    if (i != m_length)
+      return Double.NaN;
+    	
+    try {
+      return new Double(valueString).doubleValue();
+    } catch (NumberFormatException nfe) {
+      // This should catch double periods, empty strings.
+      return Double.NaN;
+    }
+  }
+}
diff --git a/src/main/java/org/apache/xpath/objects/package.html b/src/main/java/org/apache/xpath/objects/package.html
new file mode 100644
index 0000000..d596ba3
--- /dev/null
+++ b/src/main/java/org/apache/xpath/objects/package.html
@@ -0,0 +1,27 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468655 2006-10-28 07:12:06Z minchau $ -->
+<html>
+  <title>XPath objects Package.</title>
+  <body>
+    <p>Implementation of XPath polymorphic type objects -- this package will grow 
+    as XPath objects are expanded to support XML Schema data types.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xpath/operations/And.java b/src/main/java/org/apache/xpath/operations/And.java
new file mode 100644
index 0000000..b7f3eab
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/And.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: And.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The 'and' operation expression executer.
+ */
+public class And extends Operation
+{
+    static final long serialVersionUID = 392330077126534022L;
+
+  /**
+   * AND two expressions and return the boolean result. Override
+   * superclass method for optimization purposes.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return {@link org.apache.xpath.objects.XBoolean#S_TRUE} or 
+   * {@link org.apache.xpath.objects.XBoolean#S_FALSE}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    XObject expr1 = m_left.execute(xctxt);
+
+    if (expr1.bool())
+    {
+      XObject expr2 = m_right.execute(xctxt);
+
+      return expr2.bool() ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+    }
+    else
+      return XBoolean.S_FALSE;
+  }
+  
+  /**
+   * Evaluate this operation directly to a boolean.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a boolean.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean bool(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return (m_left.bool(xctxt) && m_right.bool(xctxt));
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Bool.java b/src/main/java/org/apache/xpath/operations/Bool.java
new file mode 100644
index 0000000..2dd9c6a
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Bool.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Bool.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The 'boolean()' operation expression executer.
+ */
+public class Bool extends UnaryOperation
+{
+    static final long serialVersionUID = 44705375321914635L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject right) throws javax.xml.transform.TransformerException
+  {
+
+    if (XObject.CLASS_BOOLEAN == right.getType())
+      return right;
+    else
+      return right.bool() ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+  
+  /**
+   * Evaluate this operation directly to a boolean.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a boolean.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean bool(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return m_right.bool(xctxt);
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Div.java b/src/main/java/org/apache/xpath/operations/Div.java
new file mode 100644
index 0000000..f8b74e1
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Div.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Div.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The 'div' operation expression executer.
+ */
+public class Div extends Operation
+{
+    static final long serialVersionUID = 6220756595959798135L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(left.num() / right.num());
+  }
+  
+  /**
+   * Evaluate this operation directly to a double.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a double.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    return (m_left.num(xctxt) / m_right.num(xctxt));
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Equals.java b/src/main/java/org/apache/xpath/operations/Equals.java
new file mode 100644
index 0000000..8be4186
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Equals.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Equals.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The '=' operation expression executer.
+ */
+public class Equals extends Operation
+{
+    static final long serialVersionUID = -2658315633903426134L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return left.equals(right) ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+  
+  /**
+   * Execute a binary operation by calling execute on each of the operands,
+   * and then calling the operate method on the derived class.
+   *
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The XObject result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean bool(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    XObject left = m_left.execute(xctxt, true);
+    XObject right = m_right.execute(xctxt, true);
+
+    boolean result = left.equals(right) ? true : false;
+	left.detach();
+	right.detach();
+    return result;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Gt.java b/src/main/java/org/apache/xpath/operations/Gt.java
new file mode 100644
index 0000000..3190c83
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Gt.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Gt.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The '>' operation expression executer.
+ */
+public class Gt extends Operation
+{
+    static final long serialVersionUID = 8927078751014375950L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return left.greaterThan(right) ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/operations/Gte.java b/src/main/java/org/apache/xpath/operations/Gte.java
new file mode 100644
index 0000000..fd49c4a
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Gte.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Gte.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The '>=' operation expression executer.
+ */
+public class Gte extends Operation
+{
+    static final long serialVersionUID = 9142945909906680220L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return left.greaterThanOrEqual(right)
+           ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/operations/Lt.java b/src/main/java/org/apache/xpath/operations/Lt.java
new file mode 100644
index 0000000..e023fcb
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Lt.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Lt.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The '<' operation expression executer.
+ */
+public class Lt extends Operation
+{
+    static final long serialVersionUID = 3388420509289359422L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return left.lessThan(right) ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/operations/Lte.java b/src/main/java/org/apache/xpath/operations/Lte.java
new file mode 100644
index 0000000..cc29b52
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Lte.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Lte.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The '<=' operation expression executer.
+ */
+public class Lte extends Operation
+{
+    static final long serialVersionUID = 6945650810527140228L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return left.lessThanOrEqual(right) ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/operations/Minus.java b/src/main/java/org/apache/xpath/operations/Minus.java
new file mode 100644
index 0000000..4659419
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Minus.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Minus.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The binary '-' operation expression executer.
+ */
+public class Minus extends Operation
+{
+    static final long serialVersionUID = -5297672838170871043L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the 
+   *         result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(left.num() - right.num());
+  }
+  
+  /**
+   * Evaluate this operation directly to a double.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a double.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    return (m_left.num(xctxt) - m_right.num(xctxt));
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Mod.java b/src/main/java/org/apache/xpath/operations/Mod.java
new file mode 100644
index 0000000..61e3308
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Mod.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Mod.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The 'mod' operation expression executer.
+ */
+public class Mod extends Operation
+{
+    static final long serialVersionUID = 5009471154238918201L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(left.num() % right.num());
+  }
+  
+  /**
+   * Evaluate this operation directly to a double.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a double.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    return (m_left.num(xctxt) % m_right.num(xctxt));
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Mult.java b/src/main/java/org/apache/xpath/operations/Mult.java
new file mode 100644
index 0000000..2948497
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Mult.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Mult.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The '*' operation expression executer.
+ */
+public class Mult extends Operation
+{
+    static final long serialVersionUID = -4956770147013414675L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(left.num() * right.num());
+  }
+  
+  /**
+   * Evaluate this operation directly to a double.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a double.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return (m_left.num(xctxt) * m_right.num(xctxt));
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Neg.java b/src/main/java/org/apache/xpath/operations/Neg.java
new file mode 100644
index 0000000..e61c045
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Neg.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Neg.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The unary '-' operation expression executer.
+ */
+public class Neg extends UnaryOperation
+{
+    static final long serialVersionUID = -6280607702375702291L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject right) throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(-right.num());
+  }
+  
+  /**
+   * Evaluate this operation directly to a double.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a double.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    return -(m_right.num(xctxt));
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/NotEquals.java b/src/main/java/org/apache/xpath/operations/NotEquals.java
new file mode 100644
index 0000000..7ba51a2
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/NotEquals.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NotEquals.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The '!=' operation expression executer.
+ */
+public class NotEquals extends Operation
+{
+    static final long serialVersionUID = -7869072863070586900L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return (left.notEquals(right)) ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/operations/Number.java b/src/main/java/org/apache/xpath/operations/Number.java
new file mode 100644
index 0000000..dc5cad0
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Number.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Number.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The 'number()' operation expression executer.
+ */
+public class Number extends UnaryOperation
+{
+    static final long serialVersionUID = 7196954482871619765L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject right) throws javax.xml.transform.TransformerException
+  {
+
+    if (XObject.CLASS_NUMBER == right.getType())
+      return right;
+    else
+      return new XNumber(right.num());
+  }
+  
+  /**
+   * Evaluate this operation directly to a double.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a double.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    return m_right.num(xctxt);
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Operation.java b/src/main/java/org/apache/xpath/operations/Operation.java
new file mode 100644
index 0000000..f601ae6
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Operation.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Operation.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The baseclass for a binary operation.
+ */
+public class Operation extends Expression implements ExpressionOwner
+{
+    static final long serialVersionUID = -3037139537171050430L;
+
+  /** The left operand expression.
+   *  @serial */
+  protected Expression m_left;
+
+  /** The right operand expression.
+   *  @serial */
+  protected Expression m_right;
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    m_left.fixupVariables(vars, globalsSize);
+    m_right.fixupVariables(vars, globalsSize);
+  }
+
+
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside
+   * the current subtree.
+   *
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+  public boolean canTraverseOutsideSubtree()
+  {
+
+    if (null != m_left && m_left.canTraverseOutsideSubtree())
+      return true;
+
+    if (null != m_right && m_right.canTraverseOutsideSubtree())
+      return true;
+
+    return false;
+  }
+
+  /**
+   * Set the left and right operand expressions for this operation.
+   *
+   *
+   * @param l The left expression operand.
+   * @param r The right expression operand.
+   */
+  public void setLeftRight(Expression l, Expression r)
+  {
+    m_left = l;
+    m_right = r;
+    l.exprSetParent(this);
+    r.exprSetParent(this);
+  }
+
+  /**
+   * Execute a binary operation by calling execute on each of the operands,
+   * and then calling the operate method on the derived class.
+   *
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The XObject result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    XObject left = m_left.execute(xctxt, true);
+    XObject right = m_right.execute(xctxt, true);
+
+    XObject result = operate(left, right);
+    left.detach();
+    right.detach();
+    return result;
+  }
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return null;  // no-op
+  }
+
+  /** @return the left operand of binary operation, as an Expression.
+   */
+  public Expression getLeftOperand(){
+    return m_left;
+  }
+
+  /** @return the right operand of binary operation, as an Expression.
+   */
+  public Expression getRightOperand(){
+    return m_right;
+  }
+  
+  class LeftExprOwner implements ExpressionOwner
+  {
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_left;
+    }
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(Operation.this);
+    	m_left = exp;
+    }
+  }
+
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	if(visitor.visitBinaryOperation(owner, this))
+  	{
+  		m_left.callVisitors(new LeftExprOwner(), visitor);
+  		m_right.callVisitors(this, visitor);
+  	}
+  }
+
+  /**
+   * @see ExpressionOwner#getExpression()
+   */
+  public Expression getExpression()
+  {
+    return m_right;
+  }
+
+  /**
+   * @see ExpressionOwner#setExpression(Expression)
+   */
+  public void setExpression(Expression exp)
+  {
+  	exp.exprSetParent(this);
+  	m_right = exp;
+  }
+
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!isSameClass(expr))
+  		return false;
+  		
+  	if(!m_left.deepEquals(((Operation)expr).m_left))
+  		return false;
+  		
+  	if(!m_right.deepEquals(((Operation)expr).m_right))
+  		return false;
+  		
+  	return true;
+  }
+}
diff --git a/src/main/java/org/apache/xpath/operations/Or.java b/src/main/java/org/apache/xpath/operations/Or.java
new file mode 100644
index 0000000..769d5f9
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Or.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Or.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XBoolean;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The 'or' operation expression executer.
+ */
+public class Or extends Operation
+{
+    static final long serialVersionUID = -644107191353853079L;
+
+  /**
+   * OR two expressions and return the boolean result. Override
+   * superclass method for optimization purposes.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return {@link org.apache.xpath.objects.XBoolean#S_TRUE} or 
+   * {@link org.apache.xpath.objects.XBoolean#S_FALSE}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    XObject expr1 = m_left.execute(xctxt);
+
+    if (!expr1.bool())
+    {
+      XObject expr2 = m_right.execute(xctxt);
+
+      return expr2.bool() ? XBoolean.S_TRUE : XBoolean.S_FALSE;
+    }
+    else
+      return XBoolean.S_TRUE;
+  }
+  
+  /**
+   * Evaluate this operation directly to a boolean.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a boolean.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public boolean bool(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return (m_left.bool(xctxt) || m_right.bool(xctxt));
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Plus.java b/src/main/java/org/apache/xpath/operations/Plus.java
new file mode 100644
index 0000000..81980da
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Plus.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Plus.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The '+' operation expression executer.
+ */
+public class Plus extends Operation
+{
+    static final long serialVersionUID = -4492072861616504256L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return new XNumber(left.num() + right.num());
+  }
+  
+  /**
+   * Evaluate this operation directly to a double.
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The result of the operation as a double.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double num(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    return (m_right.num(xctxt) + m_left.num(xctxt));
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Quo.java b/src/main/java/org/apache/xpath/operations/Quo.java
new file mode 100644
index 0000000..fe07694
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Quo.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Quo.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The 'quo' operation expression executer. (no longer supported by XPath).
+ * @deprecated
+ */
+public class Quo extends Operation
+{
+    static final long serialVersionUID = 693765299196169905L;
+
+  // Actually, this is no longer supported by xpath...
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject left, XObject right)
+          throws javax.xml.transform.TransformerException
+  {
+    return new XNumber((int) (left.num() / right.num()));
+  }
+}
diff --git a/src/main/java/org/apache/xpath/operations/String.java b/src/main/java/org/apache/xpath/operations/String.java
new file mode 100644
index 0000000..c32678c
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/String.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: String.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XString;
+
+/**
+ * The 'string()' operation expression executer.
+ */
+public class String extends UnaryOperation
+{
+    static final long serialVersionUID = 2973374377453022888L;
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject operate(XObject right) throws javax.xml.transform.TransformerException
+  {
+    return (XString)right.xstr(); // semi-safe cast.
+  }
+}
diff --git a/src/main/java/org/apache/xpath/operations/UnaryOperation.java b/src/main/java/org/apache/xpath/operations/UnaryOperation.java
new file mode 100644
index 0000000..394ce88
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/UnaryOperation.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: UnaryOperation.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * The unary operation base class.
+ */
+public abstract class UnaryOperation extends Expression implements ExpressionOwner
+{
+    static final long serialVersionUID = 6536083808424286166L;
+
+  /** The operand for the operation.
+   *  @serial */
+  protected Expression m_right;
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    m_right.fixupVariables(vars, globalsSize);
+  }
+  
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside
+   * the current subtree.
+   *
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+  public boolean canTraverseOutsideSubtree()
+  {
+
+    if (null != m_right && m_right.canTraverseOutsideSubtree())
+      return true;
+
+    return false;
+  }
+
+  /**
+   * Set the expression operand for the operation.
+   *
+   *
+   * @param r The expression operand to which the unary operation will be 
+   *          applied.
+   */
+  public void setRight(Expression r)
+  {
+    m_right = r;
+    r.exprSetParent(this);
+  }
+
+  /**
+   * Execute the operand and apply the unary operation to the result.
+   *
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return An XObject that represents the result of applying the unary 
+   *         operation to the evaluated operand.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    return operate(m_right.execute(xctxt));
+  }
+
+  /**
+   * Apply the operation to two operands, and return the result.
+   *
+   *
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public abstract XObject operate(XObject right)
+    throws javax.xml.transform.TransformerException;
+
+  /** @return the operand of unary operation, as an Expression.
+   */
+  public Expression getOperand(){
+    return m_right;
+  }
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	if(visitor.visitUnaryOperation(owner, this))
+  	{
+  		m_right.callVisitors(this, visitor);
+  	}
+  }
+
+
+  /**
+   * @see ExpressionOwner#getExpression()
+   */
+  public Expression getExpression()
+  {
+    return m_right;
+  }
+
+  /**
+   * @see ExpressionOwner#setExpression(Expression)
+   */
+  public void setExpression(Expression exp)
+  {
+  	exp.exprSetParent(this);
+  	m_right = exp;
+  }
+  
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!isSameClass(expr))
+  		return false;
+  		
+  	if(!m_right.deepEquals(((UnaryOperation)expr).m_right))
+  		return false;
+  		
+  	return true;
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/Variable.java b/src/main/java/org/apache/xpath/operations/Variable.java
new file mode 100644
index 0000000..ab8358d
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/Variable.java
@@ -0,0 +1,387 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: Variable.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.axes.PathComponent;
+import org.apache.xpath.axes.WalkerFactory;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.res.XPATHErrorResources;
+
+
+/**
+ * The variable reference expression executer.
+ */
+public class Variable extends Expression implements PathComponent
+{
+    static final long serialVersionUID = -4334975375609297049L;
+  /** Tell if fixupVariables was called.
+   *  @serial   */
+  private boolean m_fixUpWasCalled = false;
+
+  /** The qualified name of the variable.
+   *  @serial   */
+  protected QName m_qname;
+  
+  /**
+   * The index of the variable, which is either an absolute index to a 
+   * global, or, if higher than the globals area, must be adjusted by adding 
+   * the offset to the current stack frame.
+   */
+  protected int m_index;
+  
+  /**
+   * Set the index for the variable into the stack.  For advanced use only. You 
+   * must know what you are doing to use this.
+   * 
+   * @param index a global or local index.
+   */
+  public void setIndex(int index)
+  {
+  	m_index = index;
+  }
+  
+  /**
+   * Set the index for the variable into the stack.  For advanced use only.
+   * 
+   * @return index a global or local index.
+   */
+  public int getIndex()
+  {
+  	return m_index;
+  }
+  
+  /**
+   * Set whether or not this is a global reference.  For advanced use only.
+   * 
+   * @param isGlobal true if this should be a global variable reference.
+   */
+  public void setIsGlobal(boolean isGlobal)
+  {
+  	m_isGlobal = isGlobal;
+  }
+  
+  /**
+   * Set the index for the variable into the stack.  For advanced use only.
+   * 
+   * @return true if this should be a global variable reference.
+   */
+  public boolean getGlobal()
+  {
+  	return m_isGlobal;
+  }
+
+  
+  
+
+  
+  protected boolean m_isGlobal = false;
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    m_fixUpWasCalled = true;
+    int sz = vars.size();
+
+    for (int i = vars.size()-1; i >= 0; i--) 
+    {
+      QName qn = (QName)vars.elementAt(i);
+      // System.out.println("qn: "+qn);
+      if(qn.equals(m_qname))
+      {
+        
+        if(i < globalsSize)
+        {
+          m_isGlobal = true;
+          m_index = i;
+        }
+        else
+        {
+          m_index = i-globalsSize;
+        }
+          
+        return;
+      }
+    }
+    
+    java.lang.String msg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_COULD_NOT_FIND_VAR, 
+                                             new Object[]{m_qname.toString()});
+                                             
+    TransformerException te = new TransformerException(msg, this);
+                                             
+    throw new org.apache.xml.utils.WrappedRuntimeException(te);
+    
+  }
+
+
+  /**
+   * Set the qualified name of the variable.
+   *
+   * @param qname Must be a non-null reference to a qualified name.
+   */
+  public void setQName(QName qname)
+  {
+    m_qname = qname;
+  }
+  
+  /**
+   * Get the qualified name of the variable.
+   *
+   * @return A non-null reference to a qualified name.
+   */
+  public QName getQName()
+  {
+    return m_qname;
+  }
+  
+  /**
+   * Execute an expression in the XPath runtime context, and return the
+   * result of the expression.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception
+   *         occurs.
+   */
+  public XObject execute(XPathContext xctxt)
+    throws javax.xml.transform.TransformerException
+  {
+  	return execute(xctxt, false);
+  }
+
+
+  /**
+   * Dereference the variable, and return the reference value.  Note that lazy 
+   * evaluation will occur.  If a variable within scope is not found, a warning 
+   * will be sent to the error listener, and an empty nodeset will be returned.
+   *
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The evaluated variable, or an empty nodeset if not found.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt, boolean destructiveOK) throws javax.xml.transform.TransformerException
+  {
+    org.apache.xml.utils.PrefixResolver xprefixResolver = xctxt.getNamespaceContext();
+
+    XObject result;
+    // Is the variable fetched always the same?
+    // XObject result = xctxt.getVariable(m_qname);
+    if(m_fixUpWasCalled)
+    {    
+      if(m_isGlobal)
+        result = xctxt.getVarStack().getGlobalVariable(xctxt, m_index, destructiveOK);
+      else
+        result = xctxt.getVarStack().getLocalVariable(xctxt, m_index, destructiveOK);
+    } 
+    else {  
+    	result = xctxt.getVarStack().getVariableOrParam(xctxt,m_qname);
+    }
+  
+      if (null == result)
+      {
+        // This should now never happen...
+        warn(xctxt, XPATHErrorResources.WG_ILLEGAL_VARIABLE_REFERENCE,
+             new Object[]{ m_qname.getLocalPart() });  //"VariableReference given for variable out "+
+  //      (new RuntimeException()).printStackTrace();
+  //      error(xctxt, XPATHErrorResources.ER_COULDNOT_GET_VAR_NAMED,
+  //            new Object[]{ m_qname.getLocalPart() });  //"Could not get variable named "+varName);
+        
+        result = new XNodeSet(xctxt.getDTMManager());
+      }
+  
+      return result;
+//    }
+//    else
+//    {
+//      // Hack city... big time.  This is needed to evaluate xpaths from extensions, 
+//      // pending some bright light going off in my head.  Some sort of callback?
+//      synchronized(this)
+//      {
+//      	org.apache.xalan.templates.ElemVariable vvar= getElemVariable();
+//      	if(null != vvar)
+//      	{
+//          m_index = vvar.getIndex();
+//          m_isGlobal = vvar.getIsTopLevel();
+//          m_fixUpWasCalled = true;
+//          return execute(xctxt);
+//      	}
+//      }
+//      throw new javax.xml.transform.TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VAR_NOT_RESOLVABLE, new Object[]{m_qname.toString()})); //"Variable not resolvable: "+m_qname);
+//    }
+  }
+  
+  /**
+   * Get the XSLT ElemVariable that this sub-expression references.  In order for 
+   * this to work, the SourceLocator must be the owning ElemTemplateElement.
+   * @return The dereference to the ElemVariable, or null if not found.
+   */
+  public org.apache.xalan.templates.ElemVariable getElemVariable()
+  {
+  	
+    // Get the current ElemTemplateElement, and then walk backwards in 
+    // document order, searching 
+    // for an xsl:param element or xsl:variable element that matches our 
+    // qname.  If we reach the top level, use the StylesheetRoot's composed
+    // list of top level variables and parameters.
+    
+    org.apache.xalan.templates.ElemVariable vvar = null;	
+    org.apache.xpath.ExpressionNode owner = getExpressionOwner();
+
+    if (null != owner && owner instanceof org.apache.xalan.templates.ElemTemplateElement)
+    {
+
+      org.apache.xalan.templates.ElemTemplateElement prev = 
+        (org.apache.xalan.templates.ElemTemplateElement) owner;
+
+      if (!(prev instanceof org.apache.xalan.templates.Stylesheet))
+      {            
+        while ( prev != null && !(prev.getParentNode() instanceof org.apache.xalan.templates.Stylesheet) )
+        {
+          org.apache.xalan.templates.ElemTemplateElement savedprev = prev;
+
+          while (null != (prev = prev.getPreviousSiblingElem()))
+          {
+            if(prev instanceof org.apache.xalan.templates.ElemVariable)
+            {
+              vvar = (org.apache.xalan.templates.ElemVariable) prev;
+            
+              if (vvar.getName().equals(m_qname))
+              {
+                return vvar;
+              }
+              vvar = null; 	 	
+            }
+          }
+          prev = savedprev.getParentElem();
+        }
+      }
+      if (prev != null)
+        vvar = prev.getStylesheetRoot().getVariableOrParamComposed(m_qname);
+    }
+    return vvar;
+
+  }
+  
+  /**
+   * Tell if this expression returns a stable number that will not change during 
+   * iterations within the expression.  This is used to determine if a proximity 
+   * position predicate can indicate that no more searching has to occur.
+   * 
+   *
+   * @return true if the expression represents a stable number.
+   */
+  public boolean isStableNumber()
+  {
+    return true;
+  }
+  
+  /** 
+   * Get the analysis bits for this walker, as defined in the WalkerFactory.
+   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
+   */
+  public int getAnalysisBits()
+  {
+  	org.apache.xalan.templates.ElemVariable vvar = getElemVariable();
+  	if(null != vvar)
+  	{
+  		XPath xpath = vvar.getSelect();
+  		if(null != xpath)
+  		{
+	  		Expression expr = xpath.getExpression();
+	  		if(null != expr && expr instanceof PathComponent)
+	  		{
+	  			return ((PathComponent)expr).getAnalysisBits();
+	  		}
+  		}
+  	}
+    return WalkerFactory.BIT_FILTER;
+  }
+
+
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	visitor.visitVariableRef(owner, this);
+  }
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!isSameClass(expr))
+  		return false;
+  		
+  	if(!m_qname.equals(((Variable)expr).m_qname))
+  		return false;
+  		
+  	// We have to make sure that the qname really references 
+  	// the same variable element.
+    if(getElemVariable() != ((Variable)expr).getElemVariable())
+    	return false;
+  		
+  	return true;
+  }
+  
+  static final java.lang.String PSUEDOVARNAMESPACE = "http://xml.apache.org/xalan/psuedovar";
+  
+  /**
+   * Tell if this is a psuedo variable reference, declared by Xalan instead 
+   * of by the user.
+   */
+  public boolean isPsuedoVarRef()
+  {
+  	java.lang.String ns = m_qname.getNamespaceURI();
+  	if((null != ns) && ns.equals(PSUEDOVARNAMESPACE))
+  	{
+  		if(m_qname.getLocalName().startsWith("#"))
+  			return true;
+  	}
+  	return false;
+  }
+  
+
+}
diff --git a/src/main/java/org/apache/xpath/operations/VariableSafeAbsRef.java b/src/main/java/org/apache/xpath/operations/VariableSafeAbsRef.java
new file mode 100644
index 0000000..63e4324
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/VariableSafeAbsRef.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: VariableSafeAbsRef.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.operations;
+
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+
+
+/**
+ * This is a "smart" variable reference that is used in situations where 
+ * an absolute path is optimized into a variable reference, but may 
+ * be used in some situations where the document context may have changed.
+ * For instance, in select="document(doc/@href)//name[//salary &gt; 7250]", the 
+ * root in the predicate will be different for each node in the set.  While 
+ * this is easy to detect statically in this case, in other cases static 
+ * detection would be very hard or impossible.  So, this class does a dynamic check 
+ * to make sure the document context of the referenced variable is the same as 
+ * the current document context, and, if it is not, execute the referenced variable's 
+ * expression with the current context instead.
+ */
+public class VariableSafeAbsRef extends Variable
+{
+    static final long serialVersionUID = -9174661990819967452L;
+	
+  /**
+   * Dereference the variable, and return the reference value.  Note that lazy 
+   * evaluation will occur.  If a variable within scope is not found, a warning 
+   * will be sent to the error listener, and an empty nodeset will be returned.
+   *
+   *
+   * @param xctxt The runtime execution context.
+   *
+   * @return The evaluated variable, or an empty nodeset if not found.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt, boolean destructiveOK) 
+  	throws javax.xml.transform.TransformerException
+  {
+  	XNodeSet xns = (XNodeSet)super.execute(xctxt, destructiveOK);
+  	DTMManager dtmMgr = xctxt.getDTMManager();
+  	int context = xctxt.getContextNode();
+  	if(dtmMgr.getDTM(xns.getRoot()).getDocument() != 
+  	   dtmMgr.getDTM(context).getDocument())
+  	{
+  		Expression expr = (Expression)xns.getContainedIter();
+  		xns = (XNodeSet)expr.asIterator(xctxt, context);
+  	}
+  	return xns;
+  }
+
+}
+
diff --git a/src/main/java/org/apache/xpath/operations/package.html b/src/main/java/org/apache/xpath/operations/package.html
new file mode 100644
index 0000000..e91d869
--- /dev/null
+++ b/src/main/java/org/apache/xpath/operations/package.html
@@ -0,0 +1,26 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468655 2006-10-28 07:12:06Z minchau $ -->
+<html>
+  <title>Xalan XPath operations.</title>
+  <body>
+    <p>Support for XPath operations, such as +, -, string(), etc.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xpath/package.html b/src/main/java/org/apache/xpath/package.html
new file mode 100644
index 0000000..2536a57
--- /dev/null
+++ b/src/main/java/org/apache/xpath/package.html
@@ -0,0 +1,27 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468655 2006-10-28 07:12:06Z minchau $ -->
+<html>
+  <title>XPath support Package.</title>
+  <body>
+    <p>Implementation of XPath; for the most part, only classes meant for public use are
+    found at this root level of the XPath packages.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xpath/patterns/ContextMatchStepPattern.java b/src/main/java/org/apache/xpath/patterns/ContextMatchStepPattern.java
new file mode 100644
index 0000000..54ecfae
--- /dev/null
+++ b/src/main/java/org/apache/xpath/patterns/ContextMatchStepPattern.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: ContextMatchStepPattern.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.patterns;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisTraverser;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.WalkerFactory;
+import org.apache.xpath.objects.XObject;
+/**
+ * Special context node pattern matcher.
+ */
+public class ContextMatchStepPattern extends StepPattern
+{
+    static final long serialVersionUID = -1888092779313211942L;
+
+  /**
+   * Construct a ContextMatchStepPattern.
+   *
+   */
+  public ContextMatchStepPattern(int axis, int paxis)
+  {
+    super(DTMFilter.SHOW_ALL, axis, paxis);
+  }
+
+  /**
+   * Execute this pattern step, including predicates.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    if (xctxt.getIteratorRoot() == xctxt.getCurrentNode())
+      return getStaticScore();
+    else
+      return this.SCORE_NONE;
+  }
+  
+  /**
+   * Execute the match pattern step relative to another step.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * NEEDSDOC @param prevStep
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject executeRelativePathPattern(
+          XPathContext xctxt, StepPattern prevStep)
+            throws javax.xml.transform.TransformerException
+  {
+
+    XObject score = NodeTest.SCORE_NONE;
+    int context = xctxt.getCurrentNode();
+    DTM dtm = xctxt.getDTM(context);
+
+    if (null != dtm)
+    {
+      int predContext = xctxt.getCurrentNode();
+      DTMAxisTraverser traverser;
+      
+      int axis = m_axis;
+      
+      boolean needToTraverseAttrs = WalkerFactory.isDownwardAxisOfMany(axis);
+      boolean iterRootIsAttr = (dtm.getNodeType(xctxt.getIteratorRoot()) 
+                                 == DTM.ATTRIBUTE_NODE);
+
+      if((Axis.PRECEDING == axis) && iterRootIsAttr)
+      {
+        axis = Axis.PRECEDINGANDANCESTOR;
+      }
+      
+      traverser = dtm.getAxisTraverser(axis);
+
+      for (int relative = traverser.first(context); DTM.NULL != relative;
+              relative = traverser.next(context, relative))
+      {
+        try
+        {
+          xctxt.pushCurrentNode(relative);
+
+          score = execute(xctxt);
+
+          if (score != NodeTest.SCORE_NONE)
+          {
+	      //score = executePredicates( xctxt, prevStep, SCORE_OTHER, 
+	      //       predContext, relative);
+	      if (executePredicates(xctxt, dtm, context))
+		  return score;
+	      
+	      score = NodeTest.SCORE_NONE;
+          }
+          
+          if(needToTraverseAttrs && iterRootIsAttr
+             && (DTM.ELEMENT_NODE == dtm.getNodeType(relative)))
+          {
+            int xaxis = Axis.ATTRIBUTE;
+            for (int i = 0; i < 2; i++) 
+            {            
+              DTMAxisTraverser atraverser = dtm.getAxisTraverser(xaxis);
+        
+              for (int arelative = atraverser.first(relative); 
+                      DTM.NULL != arelative;
+                      arelative = atraverser.next(relative, arelative))
+              {
+                try
+                {
+                  xctxt.pushCurrentNode(arelative);
+        
+                  score = execute(xctxt);
+        
+                  if (score != NodeTest.SCORE_NONE)
+                  {
+		      //score = executePredicates( xctxt, prevStep, SCORE_OTHER, 
+		      //       predContext, arelative);
+        
+                    if (score != NodeTest.SCORE_NONE)
+                      return score;
+                  }
+                }
+                finally
+                {
+                  xctxt.popCurrentNode();
+                }
+              }
+              xaxis = Axis.NAMESPACE;
+            }
+          }
+
+        }
+        finally
+        {
+          xctxt.popCurrentNode();
+        }
+      }
+
+    }
+
+    return score;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/patterns/FunctionPattern.java b/src/main/java/org/apache/xpath/patterns/FunctionPattern.java
new file mode 100644
index 0000000..3a45c05
--- /dev/null
+++ b/src/main/java/org/apache/xpath/patterns/FunctionPattern.java
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: FunctionPattern.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.patterns;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Match pattern step that contains a function.
+ * @xsl.usage advanced
+ */
+public class FunctionPattern extends StepPattern
+{
+    static final long serialVersionUID = -5426793413091209944L;
+
+  /**
+   * Construct a FunctionPattern from a
+   * {@link org.apache.xpath.functions.Function expression}.
+   *
+   * NEEDSDOC @param expr
+   */
+  public FunctionPattern(Expression expr, int axis, int predaxis)
+  {
+
+    super(0, null, null, axis, predaxis);
+
+    m_functionExpr = expr;
+  }
+
+  /**
+   * Static calc of match score.
+   */
+  public final void calcScore()
+  {
+
+    m_score = SCORE_OTHER;
+
+    if (null == m_targetString)
+      calcTargetString();
+  }
+
+  /**
+   * Should be a {@link org.apache.xpath.functions.Function expression}.
+   *  @serial   
+   */
+  Expression m_functionExpr;
+  
+  /**
+   * This function is used to fixup variables from QNames to stack frame 
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list 
+   * should be searched backwards for the first qualified name that 
+   * corresponds to the variable reference qname.  The position of the 
+   * QName in the vector from the start of the vector will be its position 
+   * in the stack frame (but variables above the globalsTop value will need 
+   * to be offset to the current stack frame).
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    super.fixupVariables(vars, globalsSize);
+    m_functionExpr.fixupVariables(vars, globalsSize);
+  }
+
+  
+  /**
+   * Test a node to see if it matches the given node test.
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt, int context)
+          throws javax.xml.transform.TransformerException
+  {
+
+    DTMIterator nl = m_functionExpr.asIterator(xctxt, context);
+    XNumber score = SCORE_NONE;
+
+    if (null != nl)
+    {
+      int n;
+
+      while (DTM.NULL != (n = nl.nextNode()))
+      {
+        score = (n == context) ? SCORE_OTHER : SCORE_NONE;
+
+        if (score == SCORE_OTHER)
+        {
+          context = n;
+
+          break;
+        }
+      }
+
+      // nl.detach();
+    }
+    nl.detach();
+
+    return score;
+  }
+  
+  /**
+   * Test a node to see if it matches the given node test.
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt, int context, 
+                         DTM dtm, int expType)
+          throws javax.xml.transform.TransformerException
+  {
+
+    DTMIterator nl = m_functionExpr.asIterator(xctxt, context);
+    XNumber score = SCORE_NONE;
+
+    if (null != nl)
+    {
+      int n;
+
+      while (DTM.NULL != (n = nl.nextNode()))
+      {
+        score = (n == context) ? SCORE_OTHER : SCORE_NONE;
+
+        if (score == SCORE_OTHER)
+        {
+          context = n;
+
+          break;
+        }
+      }
+
+      nl.detach();
+    }
+
+    return score;
+  }
+  
+  /**
+   * Test a node to see if it matches the given node test.
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+
+    int context = xctxt.getCurrentNode();
+    DTMIterator nl = m_functionExpr.asIterator(xctxt, context);
+    XNumber score = SCORE_NONE;
+
+    if (null != nl)
+    {
+      int n;
+
+      while (DTM.NULL != (n = nl.nextNode()))
+      {
+        score = (n == context) ? SCORE_OTHER : SCORE_NONE;
+
+        if (score == SCORE_OTHER)
+        {
+          context = n;
+
+          break;
+        }
+      }
+
+      nl.detach();
+    }
+
+    return score;
+  }
+  
+  class FunctionOwner implements ExpressionOwner
+  {
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_functionExpr;
+    }
+
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(FunctionPattern.this);
+    	m_functionExpr = exp;
+    }
+  }
+  
+  /**
+   * Call the visitor for the function.
+   */
+  protected void callSubtreeVisitors(XPathVisitor visitor)
+  {
+    m_functionExpr.callVisitors(new FunctionOwner(), visitor);
+    super.callSubtreeVisitors(visitor);
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/patterns/NodeTest.java b/src/main/java/org/apache/xpath/patterns/NodeTest.java
new file mode 100644
index 0000000..9829146
--- /dev/null
+++ b/src/main/java/org/apache/xpath/patterns/NodeTest.java
@@ -0,0 +1,692 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeTest.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.patterns;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.XNumber;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * This is the basic node test class for both match patterns and location path
+ * steps.
+ * @xsl.usage advanced
+ */
+public class NodeTest extends Expression
+{
+    static final long serialVersionUID = -5736721866747906182L;
+
+  /**
+   * The namespace or local name for node tests with a wildcard.
+   *  @see <a href="http://www.w3.org/TR/xpath#NT-NameTest">the XPath NameTest production.</a> 
+   */
+  public static final String WILD = "*";
+
+  /**
+   * The URL to pass to the Node#supports method, to see if the
+   * DOM has already been stripped of whitespace nodes. 
+   */
+  public static final String SUPPORTS_PRE_STRIPPING =
+    "http://xml.apache.org/xpath/features/whitespace-pre-stripping";
+
+  /**
+   * This attribute determines which node types are accepted.
+   * @serial
+   */
+  protected int m_whatToShow;
+
+  /**
+   * Special bitmap for match patterns starting with a function.
+   * Make sure this does not conflict with {@link org.w3c.dom.traversal.NodeFilter}.
+   */
+  public static final int SHOW_BYFUNCTION = 0x00010000;
+
+  /**
+   * This attribute determines which node types are accepted.
+   * These constants are defined in the {@link org.w3c.dom.traversal.NodeFilter}
+   * interface.
+   *
+   * @return bitset mainly defined in {@link org.w3c.dom.traversal.NodeFilter}.
+   */
+  public int getWhatToShow()
+  {
+    return m_whatToShow;
+  }
+  
+  /**
+   * This attribute determines which node types are accepted.
+   * These constants are defined in the {@link org.w3c.dom.traversal.NodeFilter}
+   * interface.
+   *
+   * @param what bitset mainly defined in {@link org.w3c.dom.traversal.NodeFilter}.
+   */
+  public void setWhatToShow(int what)
+  {
+    m_whatToShow = what;
+  }
+
+  /**
+   * The namespace to be tested for, which may be null.
+   *  @serial 
+   */
+  String m_namespace;
+
+  /**
+   * Return the namespace to be tested.
+   *
+   * @return The namespace to be tested for, or {@link #WILD}, or null.
+   */
+  public String getNamespace()
+  {
+    return m_namespace;
+  }
+
+  /**
+   * Set the namespace to be tested.
+   *
+   * @param ns The namespace to be tested for, or {@link #WILD}, or null.
+   */
+  public void setNamespace(String ns)
+  {
+    m_namespace = ns;
+  }
+
+  /**
+   * The local name to be tested for.
+   *  @serial 
+   */
+  protected String m_name;
+
+  /**
+   * Return the local name to be tested.
+   *
+   * @return the local name to be tested, or {@link #WILD}, or an empty string.
+   */
+  public String getLocalName()
+  {
+    return (null == m_name) ? "" : m_name;
+  }
+
+  /**
+   * Set the local name to be tested.
+   *
+   * @param name the local name to be tested, or {@link #WILD}, or an empty string.
+   */
+  public void setLocalName(String name)
+  {
+    m_name = name;
+  }
+
+  /**
+   * Statically calculated score for this test.  One of
+   *  {@link #SCORE_NODETEST},
+   *  {@link #SCORE_NONE},
+   *  {@link #SCORE_NSWILD},
+   *  {@link #SCORE_QNAME}, or
+   *  {@link #SCORE_OTHER}.
+   *  @serial
+   */
+  XNumber m_score;
+
+  /**
+   * The match score if the pattern consists of just a NodeTest.
+   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a> 
+   */
+  public static final XNumber SCORE_NODETEST =
+    new XNumber(XPath.MATCH_SCORE_NODETEST);
+
+  /**
+   * The match score if the pattern pattern has the form NCName:*.
+   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a> 
+   */
+  public static final XNumber SCORE_NSWILD =
+    new XNumber(XPath.MATCH_SCORE_NSWILD);
+
+  /**
+   * The match score if the pattern has the form
+   * of a QName optionally preceded by an @ character.
+   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a> 
+   */
+  public static final XNumber SCORE_QNAME =
+    new XNumber(XPath.MATCH_SCORE_QNAME);
+
+  /**
+   * The match score if the pattern consists of something
+   * other than just a NodeTest or just a qname.
+   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a> 
+   */
+  public static final XNumber SCORE_OTHER =
+    new XNumber(XPath.MATCH_SCORE_OTHER);
+
+  /**
+   * The match score if no match is made.
+   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a> 
+   */
+  public static final XNumber SCORE_NONE =
+    new XNumber(XPath.MATCH_SCORE_NONE);
+
+  /**
+   * Construct an NodeTest that tests for namespaces and node names.
+   *
+   *
+   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
+   * @param namespace The namespace to be tested.
+   * @param name The local name to be tested.
+   */
+  public NodeTest(int whatToShow, String namespace, String name)
+  {
+    initNodeTest(whatToShow, namespace, name);
+  }
+
+  /**
+   * Construct an NodeTest that doesn't test for node names.
+   *
+   *
+   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
+   */
+  public NodeTest(int whatToShow)
+  {
+    initNodeTest(whatToShow);
+  }
+  
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!isSameClass(expr))
+  		return false;
+  		
+  	NodeTest nt = (NodeTest)expr;
+
+  	if(null != nt.m_name)
+  	{
+  		if(null == m_name)
+  			return false;
+  		else if(!nt.m_name.equals(m_name))
+  			return false;
+  	}
+  	else if(null != m_name)
+  		return false;
+
+  	if(null != nt.m_namespace)
+  	{
+  		if(null == m_namespace)
+  			return false;
+  		else if(!nt.m_namespace.equals(m_namespace))
+  			return false;
+  	}
+  	else if(null != m_namespace)
+  		return false;
+  		  		
+  	if(m_whatToShow != nt.m_whatToShow)
+  		return false;
+  		
+  	if(m_isTotallyWild != nt.m_isTotallyWild)
+  		return false;
+
+	return true;
+  }
+
+  /**
+   * Null argument constructor.
+   */
+  public NodeTest(){}
+
+  /**
+   * Initialize this node test by setting the whatToShow property, and
+   * calculating the score that this test will return if a test succeeds.
+   *
+   *
+   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
+   */
+  public void initNodeTest(int whatToShow)
+  {
+
+    m_whatToShow = whatToShow;
+
+    calcScore();
+  }
+
+  /**
+   * Initialize this node test by setting the whatToShow property and the
+   * namespace and local name, and
+   * calculating the score that this test will return if a test succeeds.
+   *
+   *
+   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
+   * @param namespace The namespace to be tested.
+   * @param name The local name to be tested.
+   */
+  public void initNodeTest(int whatToShow, String namespace, String name)
+  {
+
+    m_whatToShow = whatToShow;
+    m_namespace = namespace;
+    m_name = name;
+
+    calcScore();
+  }
+
+  /**
+   * True if this test has a null namespace and a local name of {@link #WILD}.
+   *  @serial 
+   */
+  private boolean m_isTotallyWild;
+  
+  /**
+   * Get the static score for this node test.
+   * @return Should be one of the SCORE_XXX constants.
+   */
+  public XNumber getStaticScore()
+  {
+    return m_score;
+  }
+  
+  /**
+   * Set the static score for this node test.
+   * @param score Should be one of the SCORE_XXX constants.
+   */
+  public void setStaticScore(XNumber score)
+  {
+    m_score = score;
+  }
+
+  /**
+   * Static calc of match score.
+   */
+  protected void calcScore()
+  {
+
+    if ((m_namespace == null) && (m_name == null))
+      m_score = SCORE_NODETEST;
+    else if (((m_namespace == WILD) || (m_namespace == null))
+             && (m_name == WILD))
+      m_score = SCORE_NODETEST;
+    else if ((m_namespace != WILD) && (m_name == WILD))
+      m_score = SCORE_NSWILD;
+    else
+      m_score = SCORE_QNAME;
+
+    m_isTotallyWild = (m_namespace == null && m_name == WILD);
+  }
+
+  /**
+   * Get the score that this test will return if a test succeeds.
+   *
+   *
+   * @return the score that this test will return if a test succeeds.
+   */
+  public double getDefaultScore()
+  {
+    return m_score.num();
+  }
+  
+  /**
+   * Tell what node type to test, if not DTMFilter.SHOW_ALL.
+   *
+   * @param whatToShow Bit set defined mainly by 
+   *        {@link org.apache.xml.dtm.DTMFilter}.
+   * @return the node type for the whatToShow.  Since whatToShow can specify 
+   *         multiple types, it will return the first bit tested that is on, 
+   *         so the caller of this function should take care that this is 
+   *         the function they really want to call.  If none of the known bits
+   *         are set, this function will return zero.
+   */
+  public static int getNodeTypeTest(int whatToShow)
+  {
+    // %REVIEW% Is there a better way?
+    if (0 != (whatToShow & DTMFilter.SHOW_ELEMENT))
+      return DTM.ELEMENT_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_ATTRIBUTE))
+      return DTM.ATTRIBUTE_NODE;
+      
+    if (0 != (whatToShow & DTMFilter.SHOW_TEXT))
+      return DTM.TEXT_NODE;
+      
+    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT))
+      return DTM.DOCUMENT_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT_FRAGMENT))
+      return DTM.DOCUMENT_FRAGMENT_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_NAMESPACE))
+      return DTM.NAMESPACE_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_COMMENT))
+      return DTM.COMMENT_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_PROCESSING_INSTRUCTION))
+      return DTM.PROCESSING_INSTRUCTION_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT_TYPE))
+      return DTM.DOCUMENT_TYPE_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_ENTITY))
+      return DTM.ENTITY_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_ENTITY_REFERENCE))
+      return DTM.ENTITY_REFERENCE_NODE;
+
+    if (0 != (whatToShow & DTMFilter.SHOW_NOTATION))
+      return DTM.NOTATION_NODE;
+      
+    if (0 != (whatToShow & DTMFilter.SHOW_CDATA_SECTION))
+      return DTM.CDATA_SECTION_NODE;
+
+
+    return 0;
+  }
+
+
+  /**
+   * Do a diagnostics dump of a whatToShow bit set.
+   *
+   *
+   * @param whatToShow Bit set defined mainly by 
+   *        {@link org.apache.xml.dtm.DTMFilter}.
+   */
+  public static void debugWhatToShow(int whatToShow)
+  {
+
+    java.util.Vector v = new java.util.Vector();
+
+    if (0 != (whatToShow & DTMFilter.SHOW_ATTRIBUTE))
+      v.addElement("SHOW_ATTRIBUTE");
+      
+    if (0 != (whatToShow & DTMFilter.SHOW_NAMESPACE))
+      v.addElement("SHOW_NAMESPACE");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_CDATA_SECTION))
+      v.addElement("SHOW_CDATA_SECTION");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_COMMENT))
+      v.addElement("SHOW_COMMENT");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT))
+      v.addElement("SHOW_DOCUMENT");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT_FRAGMENT))
+      v.addElement("SHOW_DOCUMENT_FRAGMENT");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT_TYPE))
+      v.addElement("SHOW_DOCUMENT_TYPE");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_ELEMENT))
+      v.addElement("SHOW_ELEMENT");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_ENTITY))
+      v.addElement("SHOW_ENTITY");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_ENTITY_REFERENCE))
+      v.addElement("SHOW_ENTITY_REFERENCE");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_NOTATION))
+      v.addElement("SHOW_NOTATION");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_PROCESSING_INSTRUCTION))
+      v.addElement("SHOW_PROCESSING_INSTRUCTION");
+
+    if (0 != (whatToShow & DTMFilter.SHOW_TEXT))
+      v.addElement("SHOW_TEXT");
+
+    int n = v.size();
+
+    for (int i = 0; i < n; i++)
+    {
+      if (i > 0)
+        System.out.print(" | ");
+
+      System.out.print(v.elementAt(i));
+    }
+
+    if (0 == n)
+      System.out.print("empty whatToShow: " + whatToShow);
+
+    System.out.println();
+  }
+
+  /**
+   * Two names are equal if they and either both are null or
+   * the name t is wild and the name p is non-null, or the two
+   * strings are equal.
+   *
+   * @param p part string from the node.
+   * @param t target string, which may be {@link #WILD}.
+   *
+   * @return true if the strings match according to the rules of this method.
+   */
+  private static final boolean subPartMatch(String p, String t)
+  {
+
+    // boolean b = (p == t) || ((null != p) && ((t == WILD) || p.equals(t)));
+    // System.out.println("subPartMatch - p: "+p+", t: "+t+", result: "+b);
+    return (p == t) || ((null != p) && ((t == WILD) || p.equals(t)));
+  }
+
+  /**
+   * This is temporary to patch over Xerces issue with representing DOM
+   * namespaces as "".
+   *
+   * @param p part string from the node, which may represent the null namespace
+   *        as null or as "".
+   * @param t target string, which may be {@link #WILD}.
+   *
+   * @return true if the strings match according to the rules of this method.
+   */
+  private static final boolean subPartMatchNS(String p, String t)
+  {
+
+    return (p == t)
+           || ((null != p)
+               && ((p.length() > 0)
+                   ? ((t == WILD) || p.equals(t)) : null == t));
+  }
+
+  /**
+   * Tell what the test score is for the given node.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   * @param context The node being tested.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt, int context)
+          throws javax.xml.transform.TransformerException
+  {
+
+    DTM dtm = xctxt.getDTM(context);
+    short nodeType = dtm.getNodeType(context);
+
+    if (m_whatToShow == DTMFilter.SHOW_ALL)
+      return m_score;
+
+    int nodeBit = (m_whatToShow & (0x00000001 << (nodeType - 1)));
+
+    switch (nodeBit)
+    {
+    case DTMFilter.SHOW_DOCUMENT_FRAGMENT :
+    case DTMFilter.SHOW_DOCUMENT :
+      return SCORE_OTHER;
+    case DTMFilter.SHOW_COMMENT :
+      return m_score;
+    case DTMFilter.SHOW_CDATA_SECTION :
+    case DTMFilter.SHOW_TEXT :
+
+      // was: 
+      // return (!xctxt.getDOMHelper().shouldStripSourceNode(context))
+      //       ? m_score : SCORE_NONE;
+      return m_score;
+    case DTMFilter.SHOW_PROCESSING_INSTRUCTION :
+      return subPartMatch(dtm.getNodeName(context), m_name)
+             ? m_score : SCORE_NONE;
+
+    // From the draft: "Two expanded names are equal if they 
+    // have the same local part, and either both have no URI or 
+    // both have the same URI."
+    // "A node test * is true for any node of the principal node type. 
+    // For example, child::* will select all element children of the 
+    // context node, and attribute::* will select all attributes of 
+    // the context node."
+    // "A node test can have the form NCName:*. In this case, the prefix 
+    // is expanded in the same way as with a QName using the context 
+    // namespace declarations. The node test will be true for any node 
+    // of the principal type whose expanded name has the URI to which 
+    // the prefix expands, regardless of the local part of the name."
+    case DTMFilter.SHOW_NAMESPACE :
+    {
+      String ns = dtm.getLocalName(context);
+
+      return (subPartMatch(ns, m_name)) ? m_score : SCORE_NONE;
+    }
+    case DTMFilter.SHOW_ATTRIBUTE :
+    case DTMFilter.SHOW_ELEMENT :
+    {
+      return (m_isTotallyWild || (subPartMatchNS(dtm.getNamespaceURI(context), m_namespace) && subPartMatch(dtm.getLocalName(context), m_name)))
+             ? m_score : SCORE_NONE;
+    }
+    default :
+      return SCORE_NONE;
+    }  // end switch(testType)
+  }
+  
+  /**
+   * Tell what the test score is for the given node.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   * @param context The node being tested.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt, int context, 
+                         DTM dtm, int expType)
+          throws javax.xml.transform.TransformerException
+  {
+
+    if (m_whatToShow == DTMFilter.SHOW_ALL)
+      return m_score;
+
+    int nodeBit = (m_whatToShow & (0x00000001 
+                   << ((dtm.getNodeType(context)) - 1)));
+
+    switch (nodeBit)
+    {
+    case DTMFilter.SHOW_DOCUMENT_FRAGMENT :
+    case DTMFilter.SHOW_DOCUMENT :
+      return SCORE_OTHER;
+    case DTMFilter.SHOW_COMMENT :
+      return m_score;
+    case DTMFilter.SHOW_CDATA_SECTION :
+    case DTMFilter.SHOW_TEXT :
+
+      // was: 
+      // return (!xctxt.getDOMHelper().shouldStripSourceNode(context))
+      //       ? m_score : SCORE_NONE;
+      return m_score;
+    case DTMFilter.SHOW_PROCESSING_INSTRUCTION :
+      return subPartMatch(dtm.getNodeName(context), m_name)
+             ? m_score : SCORE_NONE;
+
+    // From the draft: "Two expanded names are equal if they 
+    // have the same local part, and either both have no URI or 
+    // both have the same URI."
+    // "A node test * is true for any node of the principal node type. 
+    // For example, child::* will select all element children of the 
+    // context node, and attribute::* will select all attributes of 
+    // the context node."
+    // "A node test can have the form NCName:*. In this case, the prefix 
+    // is expanded in the same way as with a QName using the context 
+    // namespace declarations. The node test will be true for any node 
+    // of the principal type whose expanded name has the URI to which 
+    // the prefix expands, regardless of the local part of the name."
+    case DTMFilter.SHOW_NAMESPACE :
+    {
+      String ns = dtm.getLocalName(context);
+
+      return (subPartMatch(ns, m_name)) ? m_score : SCORE_NONE;
+    }
+    case DTMFilter.SHOW_ATTRIBUTE :
+    case DTMFilter.SHOW_ELEMENT :
+    {
+      return (m_isTotallyWild || (subPartMatchNS(dtm.getNamespaceURI(context), m_namespace) && subPartMatch(dtm.getLocalName(context), m_name)))
+             ? m_score : SCORE_NONE;
+    }
+    default :
+      return SCORE_NONE;
+    }  // end switch(testType)
+  }
+
+  /**
+   * Test the current node to see if it matches the given node test.
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return execute(xctxt, xctxt.getCurrentNode());
+  }
+  
+  /**
+   * Node tests by themselves do not need to fix up variables.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    // no-op
+  }
+
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	assertion(false, "callVisitors should not be called for this object!!!");  	
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/patterns/NodeTestFilter.java b/src/main/java/org/apache/xpath/patterns/NodeTestFilter.java
new file mode 100644
index 0000000..82bbc8b
--- /dev/null
+++ b/src/main/java/org/apache/xpath/patterns/NodeTestFilter.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: NodeTestFilter.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.patterns;
+
+/**
+ * This interface should be implemented by Nodes and/or iterators,
+ * when they need to know what the node test is before they do
+ * getNextChild, etc.
+ */
+public interface NodeTestFilter
+{
+
+  /**
+   * Set the node test for this filter.
+   *
+   *
+   * @param nodeTest Reference to a NodeTest that may be used to predetermine 
+   *                 what nodes to return.
+   */
+  void setNodeTest(NodeTest nodeTest);
+}
diff --git a/src/main/java/org/apache/xpath/patterns/StepPattern.java b/src/main/java/org/apache/xpath/patterns/StepPattern.java
new file mode 100644
index 0000000..463b671
--- /dev/null
+++ b/src/main/java/org/apache/xpath/patterns/StepPattern.java
@@ -0,0 +1,1053 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: StepPattern.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.patterns;
+
+import org.apache.xml.dtm.Axis;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMAxisTraverser;
+import org.apache.xml.dtm.DTMFilter;
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.axes.SubContextList;
+import org.apache.xpath.compiler.PsuedoNames;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * This class represents a single pattern match step.
+ * @xsl.usage advanced
+ */
+public class StepPattern extends NodeTest implements SubContextList, ExpressionOwner
+{
+    static final long serialVersionUID = 9071668960168152644L;
+
+  /** The axis for this test. */
+  protected int m_axis;
+
+  /**
+   * Construct a StepPattern that tests for namespaces and node names.
+   *
+   *
+   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
+   * @param namespace The namespace to be tested.
+   * @param name The local name to be tested.
+   * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
+   * @param axisForPredicate No longer used.
+   */
+  public StepPattern(int whatToShow, String namespace, String name, int axis,
+                     int axisForPredicate)
+  {
+
+    super(whatToShow, namespace, name);
+
+    m_axis = axis;
+  }
+
+  /**
+   * Construct a StepPattern that doesn't test for node names.
+   *
+   *
+   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
+   * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
+   * @param axisForPredicate No longer used.
+   */
+  public StepPattern(int whatToShow, int axis, int axisForPredicate)
+  {
+
+    super(whatToShow);
+
+    m_axis = axis;
+  }
+
+  /**
+   * The target local name or psuedo name, for hash table lookup optimization.
+   *  @serial
+   */
+  String m_targetString;  // only calculate on head
+
+  /**
+   * Calculate the local name or psuedo name of the node that this pattern will test,
+   * for hash table lookup optimization.
+   *
+   * @see org.apache.xpath.compiler.PsuedoNames
+   */
+  public void calcTargetString()
+  {
+
+    int whatToShow = getWhatToShow();
+
+    switch (whatToShow)
+    {
+    case DTMFilter.SHOW_COMMENT :
+      m_targetString = PsuedoNames.PSEUDONAME_COMMENT;
+      break;
+    case DTMFilter.SHOW_TEXT :
+    case DTMFilter.SHOW_CDATA_SECTION :
+    case (DTMFilter.SHOW_TEXT | DTMFilter.SHOW_CDATA_SECTION) :
+      m_targetString = PsuedoNames.PSEUDONAME_TEXT;
+      break;
+    case DTMFilter.SHOW_ALL :
+      m_targetString = PsuedoNames.PSEUDONAME_ANY;
+      break;
+    case DTMFilter.SHOW_DOCUMENT :
+    case DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT :
+      m_targetString = PsuedoNames.PSEUDONAME_ROOT;
+      break;
+    case DTMFilter.SHOW_ELEMENT :
+      if (this.WILD == m_name)
+        m_targetString = PsuedoNames.PSEUDONAME_ANY;
+      else
+        m_targetString = m_name;
+      break;
+    default :
+      m_targetString = PsuedoNames.PSEUDONAME_ANY;
+      break;
+    }
+  }
+
+  /**
+   * Get the local name or psuedo name of the node that this pattern will test,
+   * for hash table lookup optimization.
+   *
+   *
+   * @return local name or psuedo name of the node.
+   * @see org.apache.xpath.compiler.PsuedoNames
+   */
+  public String getTargetString()
+  {
+    return m_targetString;
+  }
+
+  /**
+   * Reference to nodetest and predicate for
+   * parent or ancestor.
+   * @serial
+   */
+  StepPattern m_relativePathPattern;
+
+  /**
+   * This function is used to fixup variables from QNames to stack frame
+   * indexes at stylesheet build time.
+   * @param vars List of QNames that correspond to variables.  This list
+   * should be searched backwards for the first qualified name that
+   * corresponds to the variable reference qname.  The position of the
+   * QName in the vector from the start of the vector will be its position
+   * in the stack frame (but variables above the globalsTop value will need
+   * to be offset to the current stack frame).
+   * @param globalsSize The number of variables in the global variable area.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+
+    super.fixupVariables(vars, globalsSize);
+
+    if (null != m_predicates)
+    {
+      for (int i = 0; i < m_predicates.length; i++)
+      {
+        m_predicates[i].fixupVariables(vars, globalsSize);
+      }
+    }
+
+    if (null != m_relativePathPattern)
+    {
+      m_relativePathPattern.fixupVariables(vars, globalsSize);
+    }
+  }
+
+  /**
+   * Set the reference to nodetest and predicate for
+   * parent or ancestor.
+   *
+   *
+   * @param expr The relative pattern expression.
+   */
+  public void setRelativePathPattern(StepPattern expr)
+  {
+
+    m_relativePathPattern = expr;
+    expr.exprSetParent(this);
+
+    calcScore();
+  }
+
+  /**
+   * Get the reference to nodetest and predicate for
+   * parent or ancestor.
+   *
+   *
+   * @return The relative pattern expression.
+   */
+  public StepPattern getRelativePathPattern()
+  {
+    return m_relativePathPattern;
+  }
+
+  //  /**
+  //   * Set the list of predicate expressions for this pattern step.
+  //   * @param predicates List of expression objects.
+  //   */
+  //  public void setPredicates(Expression[] predicates)
+  //  {
+  //    m_predicates = predicates;
+  //  }
+
+  /**
+   * Set the list of predicate expressions for this pattern step.
+   * @return List of expression objects.
+   */
+  public Expression[] getPredicates()
+  {
+    return m_predicates;
+  }
+
+  /**
+   * The list of predicate expressions for this pattern step.
+   *  @serial
+   */
+  Expression[] m_predicates;
+
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside
+   * the current subtree.
+   *
+   * NOTE: Ancestors tests with predicates are problematic, and will require
+   * special treatment.
+   *
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+  public boolean canTraverseOutsideSubtree()
+  {
+
+    int n = getPredicateCount();
+
+    for (int i = 0; i < n; i++)
+    {
+      if (getPredicate(i).canTraverseOutsideSubtree())
+        return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Get a predicate expression.
+   *
+   *
+   * @param i The index of the predicate.
+   *
+   * @return A predicate expression.
+   */
+  public Expression getPredicate(int i)
+  {
+    return m_predicates[i];
+  }
+
+  /**
+   * Get the number of predicates for this match pattern step.
+   *
+   *
+   * @return the number of predicates for this match pattern step.
+   */
+  public final int getPredicateCount()
+  {
+    return (null == m_predicates) ? 0 : m_predicates.length;
+  }
+
+  /**
+   * Set the predicates for this match pattern step.
+   *
+   *
+   * @param predicates An array of expressions that define predicates
+   *                   for this step.
+   */
+  public void setPredicates(Expression[] predicates)
+  {
+
+    m_predicates = predicates;
+    if(null != predicates)
+    {
+    	for(int i = 0; i < predicates.length; i++)
+    	{
+    		predicates[i].exprSetParent(this);
+    	}
+    }
+
+    calcScore();
+  }
+
+  /**
+   * Static calc of match score.
+   */
+  public void calcScore()
+  {
+
+    if ((getPredicateCount() > 0) || (null != m_relativePathPattern))
+    {
+      m_score = SCORE_OTHER;
+    }
+    else
+      super.calcScore();
+
+    if (null == m_targetString)
+      calcTargetString();
+  }
+
+  /**
+   * Execute this pattern step, including predicates.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   * @param currentNode The current node context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt, int currentNode)
+          throws javax.xml.transform.TransformerException
+  {
+
+    DTM dtm = xctxt.getDTM(currentNode);
+
+    if (dtm != null)
+    {
+      int expType = dtm.getExpandedTypeID(currentNode);
+
+      return execute(xctxt, currentNode, dtm, expType);
+    }
+
+    return NodeTest.SCORE_NONE;
+  }
+
+  /**
+   * Execute this pattern step, including predicates.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt)
+          throws javax.xml.transform.TransformerException
+  {
+    return execute(xctxt, xctxt.getCurrentNode());
+  }
+
+  /**
+   * Execute an expression in the XPath runtime context, and return the
+   * result of the expression.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @param currentNode The currentNode.
+   * @param dtm The DTM of the current node.
+   * @param expType The expanded type ID of the current node.
+   *
+   * @return The result of the expression in the form of a <code>XObject</code>.
+   *
+   * @throws javax.xml.transform.TransformerException if a runtime exception
+   *         occurs.
+   */
+  public XObject execute(
+          XPathContext xctxt, int currentNode, DTM dtm, int expType)
+            throws javax.xml.transform.TransformerException
+  {
+
+    if (m_whatToShow == NodeTest.SHOW_BYFUNCTION)
+    {
+      if (null != m_relativePathPattern)
+      {
+        return m_relativePathPattern.execute(xctxt);
+      }
+      else
+        return NodeTest.SCORE_NONE;
+    }
+
+    XObject score;
+
+    score = super.execute(xctxt, currentNode, dtm, expType);
+
+    if (score == NodeTest.SCORE_NONE)
+      return NodeTest.SCORE_NONE;
+
+    if (getPredicateCount() != 0)
+    {
+      if (!executePredicates(xctxt, dtm, currentNode))
+        return NodeTest.SCORE_NONE;
+    }
+
+    if (null != m_relativePathPattern)
+      return m_relativePathPattern.executeRelativePathPattern(xctxt, dtm,
+              currentNode);
+
+    return score;
+  }
+
+  /**
+   * New Method to check whether the current node satisfies a position predicate
+   *
+   * @param xctxt The XPath runtime context.
+   * @param predPos Which predicate we're evaluating of foo[1][2][3].
+   * @param dtm The DTM of the current node.
+   * @param context The currentNode.
+   * @param pos The position being requested, i.e. the value returned by 
+   *            m_predicates[predPos].execute(xctxt).
+   *
+   * @return true of the position of the context matches pos, false otherwise.
+   */
+  private final boolean checkProximityPosition(XPathContext xctxt,
+          int predPos, DTM dtm, int context, int pos)
+  {
+
+    try
+    {
+      DTMAxisTraverser traverser =
+        dtm.getAxisTraverser(Axis.PRECEDINGSIBLING);
+
+      for (int child = traverser.first(context); DTM.NULL != child;
+              child = traverser.next(context, child))
+      {
+        try
+        {
+          xctxt.pushCurrentNode(child);
+
+          if (NodeTest.SCORE_NONE != super.execute(xctxt, child))
+          {
+            boolean pass = true;
+
+            try
+            {
+              xctxt.pushSubContextList(this);
+
+              for (int i = 0; i < predPos; i++)
+              {
+                xctxt.pushPredicatePos(i);
+                try
+                {
+                  XObject pred = m_predicates[i].execute(xctxt);
+                  
+                  try
+                  {
+                    if (XObject.CLASS_NUMBER == pred.getType())
+                    {
+                      throw new Error("Why: Should never have been called");
+                    }
+                    else if (!pred.boolWithSideEffects())
+                    {
+                      pass = false;
+    
+                      break;
+                    }
+                  }
+                  finally
+                  {
+                    pred.detach();
+                  }
+                }
+                finally
+                {
+                  xctxt.popPredicatePos();
+                }
+              }
+            }
+            finally
+            {
+              xctxt.popSubContextList();
+            }
+
+            if (pass)
+              pos--;
+
+            if (pos < 1)
+              return false;
+          }
+        }
+        finally
+        {
+          xctxt.popCurrentNode();
+        }
+      }
+    }
+    catch (javax.xml.transform.TransformerException se)
+    {
+
+      // TODO: should keep throw sax exception...
+      throw new java.lang.RuntimeException(se.getMessage());
+    }
+
+    return (pos == 1);
+  }
+
+  /**
+   * Get the proximity position index of the current node based on this
+   * node test.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   * @param predPos Which predicate we're evaluating of foo[1][2][3].
+   * @param findLast If true, don't terminate when the context node is found.
+   *
+   * @return the proximity position index of the current node based on the
+   *         node test.
+   */
+  private final int getProximityPosition(XPathContext xctxt, int predPos, 
+                    boolean findLast)
+  {
+
+    int pos = 0;
+    int context = xctxt.getCurrentNode();
+    DTM dtm = xctxt.getDTM(context);
+    int parent = dtm.getParent(context);
+
+    try
+    {
+      DTMAxisTraverser traverser = dtm.getAxisTraverser(Axis.CHILD);
+
+      for (int child = traverser.first(parent); DTM.NULL != child;
+              child = traverser.next(parent, child))
+      {
+        try
+        {
+          xctxt.pushCurrentNode(child);
+
+          if (NodeTest.SCORE_NONE != super.execute(xctxt, child))
+          {
+            boolean pass = true;
+
+            try
+            {
+              xctxt.pushSubContextList(this);
+
+              for (int i = 0; i < predPos; i++)
+              {
+                xctxt.pushPredicatePos(i);
+                try
+                {
+                  XObject pred = m_predicates[i].execute(xctxt);
+  
+                  try
+                  {
+                    if (XObject.CLASS_NUMBER == pred.getType())
+                    {
+                      if ((pos + 1) != (int) pred.numWithSideEffects())
+                      {
+                        pass = false;
+    
+                        break;
+                      }
+                    }
+                    else if (!pred.boolWithSideEffects())
+                    {
+                      pass = false;
+    
+                      break;
+                    }
+                  }
+                  finally
+                  {
+                    pred.detach();
+                  }
+                }
+                finally
+                {
+                  xctxt.popPredicatePos();
+                }
+              }
+            }
+            finally
+            {
+              xctxt.popSubContextList();
+            }
+
+            if (pass)
+              pos++;
+
+            if (!findLast && child == context)
+            {
+              return pos;
+            }
+          }
+        }
+        finally
+        {
+          xctxt.popCurrentNode();
+        }
+      }
+    }
+    catch (javax.xml.transform.TransformerException se)
+    {
+
+      // TODO: should keep throw sax exception...
+      throw new java.lang.RuntimeException(se.getMessage());
+    }
+
+    return pos;
+  }
+
+  /**
+   * Get the proximity position index of the current node based on this
+   * node test.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return the proximity position index of the current node based on the
+   *         node test.
+   */
+  public int getProximityPosition(XPathContext xctxt)
+  {
+    return getProximityPosition(xctxt, xctxt.getPredicatePos(), false);
+  }
+  
+  /**
+   * Get the count of the nodes that match the test, which is the proximity
+   * position of the last node that can pass this test in the sub context
+   * selection.  In XSLT 1-based indexing, this count is the index of the last
+   * node.
+   *
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return the count of the nodes that match the test.
+   */
+  public int getLastPos(XPathContext xctxt)
+  {
+    return getProximityPosition(xctxt, xctxt.getPredicatePos(), true);
+  }
+
+  /**
+   * Execute the match pattern step relative to another step.
+   *
+   *
+   * @param xctxt The XPath runtime context.
+   * @param dtm The DTM of the current node.
+   * @param currentNode The current node context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected final XObject executeRelativePathPattern(
+          XPathContext xctxt, DTM dtm, int currentNode)
+            throws javax.xml.transform.TransformerException
+  {
+
+    XObject score = NodeTest.SCORE_NONE;
+    int context = currentNode;
+    DTMAxisTraverser traverser;
+
+    traverser = dtm.getAxisTraverser(m_axis);
+
+    for (int relative = traverser.first(context); DTM.NULL != relative;
+            relative = traverser.next(context, relative))
+    {
+      try
+      {
+        xctxt.pushCurrentNode(relative);
+
+        score = execute(xctxt);
+
+        if (score != NodeTest.SCORE_NONE)
+          break;
+      }
+      finally
+      {
+        xctxt.popCurrentNode();
+      }
+    }
+
+    return score;
+  }
+
+  /**
+   * Execute the predicates on this step to determine if the current node 
+   * should be filtered or accepted.
+   *
+   * @param xctxt The XPath runtime context.
+   * @param dtm The DTM of the current node.
+   * @param currentNode The current node context.
+   *
+   * @return true if the node should be accepted, false otherwise.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  protected final boolean executePredicates(
+          XPathContext xctxt, DTM dtm, int currentNode)
+            throws javax.xml.transform.TransformerException
+  {
+
+    boolean result = true;
+    boolean positionAlreadySeen = false;
+    int n = getPredicateCount();
+
+    try
+    {
+      xctxt.pushSubContextList(this);
+
+      for (int i = 0; i < n; i++)
+      {
+        xctxt.pushPredicatePos(i);
+
+        try
+        {
+          XObject pred = m_predicates[i].execute(xctxt);
+
+          try
+          {
+            if (XObject.CLASS_NUMBER == pred.getType())
+            {
+              int pos = (int) pred.num();
+  
+              if (positionAlreadySeen)
+              {
+                result = (pos == 1);
+  
+                break;
+              }
+              else
+              {
+                positionAlreadySeen = true;
+  
+                if (!checkProximityPosition(xctxt, i, dtm, currentNode, pos))
+                {
+                  result = false;
+  
+                  break;
+                }
+              }
+            
+            }
+            else if (!pred.boolWithSideEffects())
+            {
+              result = false;
+  
+              break;
+            }
+          }
+          finally
+          {
+            pred.detach();
+          }
+        }
+        finally
+        {
+          xctxt.popPredicatePos();
+        }
+      }
+    }
+    finally
+    {
+      xctxt.popSubContextList();
+    }
+
+    return result;
+  }
+
+  /**
+   * Get the string represenentation of this step for diagnostic purposes.
+   *
+   *
+   * @return A string representation of this step, built by reverse-engineering 
+   * the contained info.
+   */
+  public String toString()
+  {
+
+    StringBuffer buf = new StringBuffer();
+
+    for (StepPattern pat = this; pat != null; pat = pat.m_relativePathPattern)
+    {
+      if (pat != this)
+        buf.append("/");
+
+      buf.append(Axis.getNames(pat.m_axis));
+      buf.append("::");
+
+      if (0x000005000 == pat.m_whatToShow)
+      {
+        buf.append("doc()");
+      }
+      else if (DTMFilter.SHOW_BYFUNCTION == pat.m_whatToShow)
+      {
+        buf.append("function()");
+      }
+      else if (DTMFilter.SHOW_ALL == pat.m_whatToShow)
+      {
+        buf.append("node()");
+      }
+      else if (DTMFilter.SHOW_TEXT == pat.m_whatToShow)
+      {
+        buf.append("text()");
+      }
+      else if (DTMFilter.SHOW_PROCESSING_INSTRUCTION == pat.m_whatToShow)
+      {
+        buf.append("processing-instruction(");
+
+        if (null != pat.m_name)
+        {
+          buf.append(pat.m_name);
+        }
+
+        buf.append(")");
+      }
+      else if (DTMFilter.SHOW_COMMENT == pat.m_whatToShow)
+      {
+        buf.append("comment()");
+      }
+      else if (null != pat.m_name)
+      {
+        if (DTMFilter.SHOW_ATTRIBUTE == pat.m_whatToShow)
+        {
+          buf.append("@");
+        }
+
+        if (null != pat.m_namespace)
+        {
+          buf.append("{");
+          buf.append(pat.m_namespace);
+          buf.append("}");
+        }
+
+        buf.append(pat.m_name);
+      }
+      else if (DTMFilter.SHOW_ATTRIBUTE == pat.m_whatToShow)
+      {
+        buf.append("@");
+      }
+      else if ((DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT)
+               == pat.m_whatToShow)
+      {
+        buf.append("doc-root()");
+      }
+      else
+      {
+        buf.append("?" + Integer.toHexString(pat.m_whatToShow));
+      }
+
+      if (null != pat.m_predicates)
+      {
+        for (int i = 0; i < pat.m_predicates.length; i++)
+        {
+          buf.append("[");
+          buf.append(pat.m_predicates[i]);
+          buf.append("]");
+        }
+      }
+    }
+
+    return buf.toString();
+  }
+
+  /** Set to true to send diagnostics about pattern matches to the consol. */
+  private static final boolean DEBUG_MATCHES = false;
+
+  /**
+   * Get the match score of the given node.
+   *
+   * @param xctxt The XPath runtime context.
+   * @param context The node to be tested.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public double getMatchScore(XPathContext xctxt, int context)
+          throws javax.xml.transform.TransformerException
+  {
+
+    xctxt.pushCurrentNode(context);
+    xctxt.pushCurrentExpressionNode(context);
+
+    try
+    {
+      XObject score = execute(xctxt);
+
+      return score.num();
+    }
+    finally
+    {
+      xctxt.popCurrentNode();
+      xctxt.popCurrentExpressionNode();
+    }
+
+    // return XPath.MATCH_SCORE_NONE;
+  }
+
+  /**
+   * Set the axis that this step should follow. 
+   *
+   *
+   * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
+   */
+  public void setAxis(int axis)
+  {
+    m_axis = axis;
+  }
+
+  /**
+   * Get the axis that this step follows. 
+   *
+   *
+   * @return The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
+   */
+  public int getAxis()
+  {
+    return m_axis;
+  }
+  
+  class PredOwner implements ExpressionOwner
+  {
+  	int m_index;
+  	
+  	PredOwner(int index)
+  	{
+  		m_index = index;
+  	}
+  	
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_predicates[m_index];
+    }
+
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(StepPattern.this);
+    	m_predicates[m_index] = exp;
+    }
+  }
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	 	if(visitor.visitMatchPattern(owner, this))
+  	 	{
+  	 		callSubtreeVisitors(visitor);
+  	 	}
+  }
+
+  /**
+   * Call the visitors on the subtree.  Factored out from callVisitors 
+   * so it may be called by derived classes.
+   */
+  protected void callSubtreeVisitors(XPathVisitor visitor)
+  {
+    if (null != m_predicates)
+    {
+      int n = m_predicates.length;
+      for (int i = 0; i < n; i++)
+      {
+        ExpressionOwner predOwner = new PredOwner(i);
+        if (visitor.visitPredicate(predOwner, m_predicates[i]))
+        {
+          m_predicates[i].callVisitors(predOwner, visitor);
+        }
+      }
+    }
+    if (null != m_relativePathPattern)
+    {
+      m_relativePathPattern.callVisitors(this, visitor);
+    }
+  }
+
+
+  /**
+   * @see ExpressionOwner#getExpression()
+   */
+  public Expression getExpression()
+  {
+    return m_relativePathPattern;
+  }
+
+  /**
+   * @see ExpressionOwner#setExpression(Expression)
+   */
+  public void setExpression(Expression exp)
+  {
+    exp.exprSetParent(this);
+  	m_relativePathPattern = (StepPattern)exp;
+  }
+  
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!super.deepEquals(expr))
+  		return false;
+  		
+  	StepPattern sp = (StepPattern)expr;
+  	
+    if (null != m_predicates)
+    {
+        int n = m_predicates.length;
+        if ((null == sp.m_predicates) || (sp.m_predicates.length != n))
+              return false;
+        for (int i = 0; i < n; i++)
+        {
+          if (!m_predicates[i].deepEquals(sp.m_predicates[i]))
+          	return false; 
+        }
+    }
+    else if (null != sp.m_predicates)
+    	return false;
+  		
+  	if(null != m_relativePathPattern)
+  	{
+  		if(!m_relativePathPattern.deepEquals(sp.m_relativePathPattern))
+  			return false;
+  	}
+  	else if(sp.m_relativePathPattern != null)
+  		return false;
+  		
+  	return true;
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/patterns/UnionPattern.java b/src/main/java/org/apache/xpath/patterns/UnionPattern.java
new file mode 100644
index 0000000..a06d095
--- /dev/null
+++ b/src/main/java/org/apache/xpath/patterns/UnionPattern.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: UnionPattern.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.patterns;
+
+import org.apache.xpath.Expression;
+import org.apache.xpath.ExpressionOwner;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * This class represents a union pattern, which can have multiple individual 
+ * StepPattern patterns.
+ * @xsl.usage advanced
+ */
+public class UnionPattern extends Expression
+{
+    static final long serialVersionUID = -6670449967116905820L;
+
+  /** Array of the contained step patterns to be tested.
+   *  @serial  */
+  private StepPattern[] m_patterns;
+  
+  /**
+   * No arguments to process, so this does nothing.
+   */
+  public void fixupVariables(java.util.Vector vars, int globalsSize)
+  {
+    for (int i = 0; i < m_patterns.length; i++) 
+    {
+      m_patterns[i].fixupVariables(vars, globalsSize);
+    }
+  }
+
+  
+  /**
+   * Tell if this expression or it's subexpressions can traverse outside 
+   * the current subtree.
+   * 
+   * @return true if traversal outside the context node's subtree can occur.
+   */
+   public boolean canTraverseOutsideSubtree()
+   {
+     if(null != m_patterns)
+     {
+      int n = m_patterns.length;
+      for (int i = 0; i < n; i++) 
+      {
+        if(m_patterns[i].canTraverseOutsideSubtree())
+          return true;
+      }
+     }
+     return false;
+   }
+
+  /**
+   * Set the contained step patterns to be tested. 
+   *
+   *
+   * @param patterns the contained step patterns to be tested. 
+   */
+  public void setPatterns(StepPattern[] patterns)
+  {
+    m_patterns = patterns;
+    if(null != patterns)
+    {
+    	for(int i = 0; i < patterns.length; i++)
+    	{
+    		patterns[i].exprSetParent(this);
+    	}
+    }
+    
+  }
+
+  /**
+   * Get the contained step patterns to be tested. 
+   *
+   *
+   * @return an array of the contained step patterns to be tested. 
+   */
+  public StepPattern[] getPatterns()
+  {
+    return m_patterns;
+  }
+
+  /**
+   * Test a node to see if it matches any of the patterns in the union.
+   *
+   * @param xctxt XPath runtime context.
+   *
+   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
+   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+  public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
+  {
+
+    XObject bestScore = null;
+    int n = m_patterns.length;
+
+    for (int i = 0; i < n; i++)
+    {
+      XObject score = m_patterns[i].execute(xctxt);
+
+      if (score != NodeTest.SCORE_NONE)
+      {
+        if (null == bestScore)
+          bestScore = score;
+        else if (score.num() > bestScore.num())
+          bestScore = score;
+      }
+    }
+
+    if (null == bestScore)
+    {
+      bestScore = NodeTest.SCORE_NONE;
+    }
+
+    return bestScore;
+  }
+  
+  class UnionPathPartOwner implements ExpressionOwner
+  {
+  	int m_index;
+  	
+  	UnionPathPartOwner(int index)
+  	{
+  		m_index = index;
+  	}
+  	
+    /**
+     * @see ExpressionOwner#getExpression()
+     */
+    public Expression getExpression()
+    {
+      return m_patterns[m_index];
+    }
+
+
+    /**
+     * @see ExpressionOwner#setExpression(Expression)
+     */
+    public void setExpression(Expression exp)
+    {
+    	exp.exprSetParent(UnionPattern.this);
+    	m_patterns[m_index] = (StepPattern)exp;
+    }
+  }
+  
+  /**
+   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
+   */
+  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
+  {
+  	visitor.visitUnionPattern(owner, this);
+  	if(null != m_patterns)
+  	{
+  		int n = m_patterns.length;
+  		for(int i = 0; i < n; i++)
+  		{
+  			m_patterns[i].callVisitors(new UnionPathPartOwner(i), visitor);
+  		}
+  	}
+  }
+  
+  /**
+   * @see Expression#deepEquals(Expression)
+   */
+  public boolean deepEquals(Expression expr)
+  {
+  	if(!isSameClass(expr))
+  		return false;
+  		
+  	UnionPattern up = (UnionPattern)expr;
+  		
+  	if(null != m_patterns)
+  	{
+  		int n = m_patterns.length;
+  		if((null == up.m_patterns) || (up.m_patterns.length != n))
+  			return false;
+  			
+  		for(int i = 0; i < n; i++)
+  		{
+  			if(!m_patterns[i].deepEquals(up.m_patterns[i]))
+  				return false;
+  		}
+  	}
+  	else if(up.m_patterns != null)
+  		return false;
+  		
+  	return true;
+  	
+  }
+
+
+}
diff --git a/src/main/java/org/apache/xpath/patterns/package.html b/src/main/java/org/apache/xpath/patterns/package.html
new file mode 100644
index 0000000..12db9e4
--- /dev/null
+++ b/src/main/java/org/apache/xpath/patterns/package.html
@@ -0,0 +1,26 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468655 2006-10-28 07:12:06Z minchau $ -->
+<html>
+  <title>XPath nodetest and XSLT pattern matching support.</title>
+  <body>
+    <p>Implementation of XPath nodeTest support, and XSLT pattern matching support.<p>
+ </body>
+</html>
+
+
diff --git a/src/main/java/org/apache/xpath/res/XPATHErrorResources.java b/src/main/java/org/apache/xpath/res/XPATHErrorResources.java
new file mode 100644
index 0000000..6c6120f
--- /dev/null
+++ b/src/main/java/org/apache/xpath/res/XPATHErrorResources.java
@@ -0,0 +1,999 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPATHErrorResources.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.res;
+
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Set up error messages.
+ * We build a two dimensional array of message keys and
+ * message strings. In order to add a new message here,
+ * you need to first add a Static string constant for the
+ * Key and update the contents array with Key, Value pair
+  * Also you need to  update the count of messages(MAX_CODE)or
+ * the count of warnings(MAX_WARNING) [ Information purpose only]
+ * @xsl.usage advanced
+ */
+public class XPATHErrorResources extends ListResourceBundle
+{
+
+/*
+ * General notes to translators:
+ *
+ * This file contains error and warning messages related to XPath Error
+ * Handling.
+ *
+ *  1) Xalan (or more properly, Xalan-interpretive) and XSLTC are names of
+ *     components.
+ *     XSLT is an acronym for "XML Stylesheet Language: Transformations".
+ *     XSLTC is an acronym for XSLT Compiler.
+ *
+ *  2) A stylesheet is a description of how to transform an input XML document
+ *     into a resultant XML document (or HTML document or text).  The
+ *     stylesheet itself is described in the form of an XML document.
+ *
+ *  3) A template is a component of a stylesheet that is used to match a
+ *     particular portion of an input document and specifies the form of the
+ *     corresponding portion of the output document.
+ *
+ *  4) An element is a mark-up tag in an XML document; an attribute is a
+ *     modifier on the tag.  For example, in <elem attr='val' attr2='val2'>
+ *     "elem" is an element name, "attr" and "attr2" are attribute names with
+ *     the values "val" and "val2", respectively.
+ *
+ *  5) A namespace declaration is a special attribute that is used to associate
+ *     a prefix with a URI (the namespace).  The meanings of element names and
+ *     attribute names that use that prefix are defined with respect to that
+ *     namespace.
+ *
+ *  6) "Translet" is an invented term that describes the class file that
+ *     results from compiling an XML stylesheet into a Java class.
+ *
+ *  7) XPath is a specification that describes a notation for identifying
+ *     nodes in a tree-structured representation of an XML document.  An
+ *     instance of that notation is referred to as an XPath expression.
+ *
+ *  8) The context node is the node in the document with respect to which an
+ *     XPath expression is being evaluated.
+ *
+ *  9) An iterator is an object that traverses nodes in the tree, one at a time.
+ *
+ *  10) NCName is an XML term used to describe a name that does not contain a
+ *     colon (a "no-colon name").
+ *
+ *  11) QName is an XML term meaning "qualified name".
+ */
+
+  /* 
+   * static variables
+   */
+  public static final String ERROR0000 = "ERROR0000";
+  public static final String ER_CURRENT_NOT_ALLOWED_IN_MATCH = 
+	 "ER_CURRENT_NOT_ALLOWED_IN_MATCH";
+  public static final String ER_CURRENT_TAKES_NO_ARGS = 
+	 "ER_CURRENT_TAKES_NO_ARGS";
+  public static final String ER_DOCUMENT_REPLACED = "ER_DOCUMENT_REPLACED";
+  public static final String ER_CONTEXT_HAS_NO_OWNERDOC = 
+	 "ER_CONTEXT_HAS_NO_OWNERDOC";
+  public static final String ER_LOCALNAME_HAS_TOO_MANY_ARGS = 
+	 "ER_LOCALNAME_HAS_TOO_MANY_ARGS";
+  public static final String ER_NAMESPACEURI_HAS_TOO_MANY_ARGS = 
+	 "ER_NAMESPACEURI_HAS_TOO_MANY_ARGS";
+  public static final String ER_NORMALIZESPACE_HAS_TOO_MANY_ARGS = 
+	 "ER_NORMALIZESPACE_HAS_TOO_MANY_ARGS";
+  public static final String ER_NUMBER_HAS_TOO_MANY_ARGS = 
+	 "ER_NUMBER_HAS_TOO_MANY_ARGS";
+  public static final String ER_NAME_HAS_TOO_MANY_ARGS = 
+	 "ER_NAME_HAS_TOO_MANY_ARGS";
+  public static final String ER_STRING_HAS_TOO_MANY_ARGS = 
+	 "ER_STRING_HAS_TOO_MANY_ARGS";
+  public static final String ER_STRINGLENGTH_HAS_TOO_MANY_ARGS = 
+	 "ER_STRINGLENGTH_HAS_TOO_MANY_ARGS";
+  public static final String ER_TRANSLATE_TAKES_3_ARGS = 
+	 "ER_TRANSLATE_TAKES_3_ARGS";
+  public static final String ER_UNPARSEDENTITYURI_TAKES_1_ARG = 
+	 "ER_UNPARSEDENTITYURI_TAKES_1_ARG";
+  public static final String ER_NAMESPACEAXIS_NOT_IMPLEMENTED = 
+	 "ER_NAMESPACEAXIS_NOT_IMPLEMENTED";
+  public static final String ER_UNKNOWN_AXIS = "ER_UNKNOWN_AXIS";
+  public static final String ER_UNKNOWN_MATCH_OPERATION = 
+	 "ER_UNKNOWN_MATCH_OPERATION";
+  public static final String ER_INCORRECT_ARG_LENGTH ="ER_INCORRECT_ARG_LENGTH";
+  public static final String ER_CANT_CONVERT_TO_NUMBER = 
+	 "ER_CANT_CONVERT_TO_NUMBER";
+  public static final String ER_CANT_CONVERT_XPATHRESULTTYPE_TO_NUMBER = 
+	   "ER_CANT_CONVERT_XPATHRESULTTYPE_TO_NUMBER";	 
+  public static final String ER_CANT_CONVERT_TO_NODELIST = 
+	 "ER_CANT_CONVERT_TO_NODELIST";
+  public static final String ER_CANT_CONVERT_TO_MUTABLENODELIST = 
+	 "ER_CANT_CONVERT_TO_MUTABLENODELIST";
+  public static final String ER_CANT_CONVERT_TO_TYPE ="ER_CANT_CONVERT_TO_TYPE";
+  public static final String ER_EXPECTED_MATCH_PATTERN = 
+	 "ER_EXPECTED_MATCH_PATTERN";
+  public static final String ER_COULDNOT_GET_VAR_NAMED = 
+	 "ER_COULDNOT_GET_VAR_NAMED";
+  public static final String ER_UNKNOWN_OPCODE = "ER_UNKNOWN_OPCODE";
+  public static final String ER_EXTRA_ILLEGAL_TOKENS ="ER_EXTRA_ILLEGAL_TOKENS";
+  public static final String ER_EXPECTED_DOUBLE_QUOTE = 
+	 "ER_EXPECTED_DOUBLE_QUOTE";
+  public static final String ER_EXPECTED_SINGLE_QUOTE = 
+	 "ER_EXPECTED_SINGLE_QUOTE";
+  public static final String ER_EMPTY_EXPRESSION = "ER_EMPTY_EXPRESSION";
+  public static final String ER_EXPECTED_BUT_FOUND = "ER_EXPECTED_BUT_FOUND";
+  public static final String ER_INCORRECT_PROGRAMMER_ASSERTION = 
+	 "ER_INCORRECT_PROGRAMMER_ASSERTION";
+  public static final String ER_BOOLEAN_ARG_NO_LONGER_OPTIONAL = 
+	 "ER_BOOLEAN_ARG_NO_LONGER_OPTIONAL";
+  public static final String ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG = 
+	 "ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG";
+  public static final String ER_FOUND_COMMA_BUT_NO_FOLLOWING_ARG = 
+	 "ER_FOUND_COMMA_BUT_NO_FOLLOWING_ARG";
+  public static final String ER_PREDICATE_ILLEGAL_SYNTAX = 
+	 "ER_PREDICATE_ILLEGAL_SYNTAX";
+  public static final String ER_ILLEGAL_AXIS_NAME = "ER_ILLEGAL_AXIS_NAME";
+  public static final String ER_UNKNOWN_NODETYPE = "ER_UNKNOWN_NODETYPE";
+  public static final String ER_PATTERN_LITERAL_NEEDS_BE_QUOTED = 
+	 "ER_PATTERN_LITERAL_NEEDS_BE_QUOTED";
+  public static final String ER_COULDNOT_BE_FORMATTED_TO_NUMBER = 
+	 "ER_COULDNOT_BE_FORMATTED_TO_NUMBER";
+  public static final String ER_COULDNOT_CREATE_XMLPROCESSORLIAISON = 
+	 "ER_COULDNOT_CREATE_XMLPROCESSORLIAISON";
+  public static final String ER_DIDNOT_FIND_XPATH_SELECT_EXP = 
+	 "ER_DIDNOT_FIND_XPATH_SELECT_EXP";
+  public static final String ER_COULDNOT_FIND_ENDOP_AFTER_OPLOCATIONPATH = 
+	 "ER_COULDNOT_FIND_ENDOP_AFTER_OPLOCATIONPATH";
+  public static final String ER_ERROR_OCCURED = "ER_ERROR_OCCURED";
+  public static final String ER_ILLEGAL_VARIABLE_REFERENCE = 
+	 "ER_ILLEGAL_VARIABLE_REFERENCE";
+  public static final String ER_AXES_NOT_ALLOWED = "ER_AXES_NOT_ALLOWED";
+  public static final String ER_KEY_HAS_TOO_MANY_ARGS = 
+	 "ER_KEY_HAS_TOO_MANY_ARGS";
+  public static final String ER_COUNT_TAKES_1_ARG = "ER_COUNT_TAKES_1_ARG";
+  public static final String ER_COULDNOT_FIND_FUNCTION = 
+	 "ER_COULDNOT_FIND_FUNCTION";
+  public static final String ER_UNSUPPORTED_ENCODING ="ER_UNSUPPORTED_ENCODING";
+  public static final String ER_PROBLEM_IN_DTM_NEXTSIBLING = 
+	 "ER_PROBLEM_IN_DTM_NEXTSIBLING";
+  public static final String ER_CANNOT_WRITE_TO_EMPTYNODELISTIMPL = 
+	 "ER_CANNOT_WRITE_TO_EMPTYNODELISTIMPL";
+  public static final String ER_SETDOMFACTORY_NOT_SUPPORTED = 
+	 "ER_SETDOMFACTORY_NOT_SUPPORTED";
+  public static final String ER_PREFIX_MUST_RESOLVE = "ER_PREFIX_MUST_RESOLVE";
+  public static final String ER_PARSE_NOT_SUPPORTED = "ER_PARSE_NOT_SUPPORTED";
+  public static final String ER_SAX_API_NOT_HANDLED = "ER_SAX_API_NOT_HANDLED";
+public static final String ER_IGNORABLE_WHITESPACE_NOT_HANDLED = 
+	 "ER_IGNORABLE_WHITESPACE_NOT_HANDLED";
+  public static final String ER_DTM_CANNOT_HANDLE_NODES = 
+	 "ER_DTM_CANNOT_HANDLE_NODES";
+  public static final String ER_XERCES_CANNOT_HANDLE_NODES = 
+	 "ER_XERCES_CANNOT_HANDLE_NODES";
+  public static final String ER_XERCES_PARSE_ERROR_DETAILS = 
+	 "ER_XERCES_PARSE_ERROR_DETAILS";
+  public static final String ER_XERCES_PARSE_ERROR = "ER_XERCES_PARSE_ERROR";
+  public static final String ER_INVALID_UTF16_SURROGATE = 
+	 "ER_INVALID_UTF16_SURROGATE";
+  public static final String ER_OIERROR = "ER_OIERROR";
+  public static final String ER_CANNOT_CREATE_URL = "ER_CANNOT_CREATE_URL";
+  public static final String ER_XPATH_READOBJECT = "ER_XPATH_READOBJECT";
+ public static final String ER_FUNCTION_TOKEN_NOT_FOUND = 
+	 "ER_FUNCTION_TOKEN_NOT_FOUND";
+  public static final String ER_CANNOT_DEAL_XPATH_TYPE = 
+	 "ER_CANNOT_DEAL_XPATH_TYPE";
+  public static final String ER_NODESET_NOT_MUTABLE = "ER_NODESET_NOT_MUTABLE";
+  public static final String ER_NODESETDTM_NOT_MUTABLE = 
+	 "ER_NODESETDTM_NOT_MUTABLE";
+   /**  Variable not resolvable:   */
+  public static final String ER_VAR_NOT_RESOLVABLE = "ER_VAR_NOT_RESOLVABLE";
+   /** Null error handler  */
+ public static final String ER_NULL_ERROR_HANDLER = "ER_NULL_ERROR_HANDLER";
+   /**  Programmer's assertion: unknown opcode  */
+  public static final String ER_PROG_ASSERT_UNKNOWN_OPCODE = 
+	 "ER_PROG_ASSERT_UNKNOWN_OPCODE";
+   /**  0 or 1   */
+  public static final String ER_ZERO_OR_ONE = "ER_ZERO_OR_ONE";
+   /**  rtf() not supported by XRTreeFragSelectWrapper   */
+  public static final String ER_RTF_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER = 
+	 "ER_RTF_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER";
+   /**  asNodeIterator() not supported by XRTreeFragSelectWrapper   */
+  public static final String ER_ASNODEITERATOR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER = "ER_ASNODEITERATOR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER";
+   /**  fsb() not supported for XStringForChars   */
+  public static final String ER_FSB_NOT_SUPPORTED_XSTRINGFORCHARS = 
+	 "ER_FSB_NOT_SUPPORTED_XSTRINGFORCHARS";
+   /**  Could not find variable with the name of   */
+ public static final String ER_COULD_NOT_FIND_VAR = "ER_COULD_NOT_FIND_VAR";
+   /**  XStringForChars can not take a string for an argument   */
+ public static final String ER_XSTRINGFORCHARS_CANNOT_TAKE_STRING = 
+	 "ER_XSTRINGFORCHARS_CANNOT_TAKE_STRING";
+   /**  The FastStringBuffer argument can not be null   */
+ public static final String ER_FASTSTRINGBUFFER_CANNOT_BE_NULL = 
+	 "ER_FASTSTRINGBUFFER_CANNOT_BE_NULL";
+   /**  2 or 3   */
+  public static final String ER_TWO_OR_THREE = "ER_TWO_OR_THREE";
+   /** Variable accessed before it is bound! */
+  public static final String ER_VARIABLE_ACCESSED_BEFORE_BIND = 
+	 "ER_VARIABLE_ACCESSED_BEFORE_BIND";
+   /** XStringForFSB can not take a string for an argument! */
+ public static final String ER_FSB_CANNOT_TAKE_STRING = 
+	 "ER_FSB_CANNOT_TAKE_STRING";
+   /** Error! Setting the root of a walker to null! */
+  public static final String ER_SETTING_WALKER_ROOT_TO_NULL = 
+	 "ER_SETTING_WALKER_ROOT_TO_NULL";
+   /** This NodeSetDTM can not iterate to a previous node! */
+  public static final String ER_NODESETDTM_CANNOT_ITERATE = 
+	 "ER_NODESETDTM_CANNOT_ITERATE";
+  /** This NodeSet can not iterate to a previous node! */
+ public static final String ER_NODESET_CANNOT_ITERATE = 
+	 "ER_NODESET_CANNOT_ITERATE";
+  /** This NodeSetDTM can not do indexing or counting functions! */
+  public static final String ER_NODESETDTM_CANNOT_INDEX = 
+	 "ER_NODESETDTM_CANNOT_INDEX";
+  /** This NodeSet can not do indexing or counting functions! */
+  public static final String ER_NODESET_CANNOT_INDEX = 
+	 "ER_NODESET_CANNOT_INDEX";
+  /** Can not call setShouldCacheNodes after nextNode has been called! */
+  public static final String ER_CANNOT_CALL_SETSHOULDCACHENODE = 
+	 "ER_CANNOT_CALL_SETSHOULDCACHENODE";
+  /** {0} only allows {1} arguments */
+ public static final String ER_ONLY_ALLOWS = "ER_ONLY_ALLOWS";
+  /** Programmer's assertion in getNextStepPos: unknown stepType: {0} */
+  public static final String ER_UNKNOWN_STEP = "ER_UNKNOWN_STEP";
+  /** Problem with RelativeLocationPath */
+  public static final String ER_EXPECTED_REL_LOC_PATH = 
+	 "ER_EXPECTED_REL_LOC_PATH";
+  /** Problem with LocationPath */
+  public static final String ER_EXPECTED_LOC_PATH = "ER_EXPECTED_LOC_PATH";
+  public static final String ER_EXPECTED_LOC_PATH_AT_END_EXPR =
+                                        "ER_EXPECTED_LOC_PATH_AT_END_EXPR";
+  /** Problem with Step */
+  public static final String ER_EXPECTED_LOC_STEP = "ER_EXPECTED_LOC_STEP";
+  /** Problem with NodeTest */
+  public static final String ER_EXPECTED_NODE_TEST = "ER_EXPECTED_NODE_TEST";
+  /** Expected step pattern */
+  public static final String ER_EXPECTED_STEP_PATTERN = 
+	"ER_EXPECTED_STEP_PATTERN";
+  /** Expected relative path pattern */
+  public static final String ER_EXPECTED_REL_PATH_PATTERN = 
+	 "ER_EXPECTED_REL_PATH_PATTERN";
+  /** ER_CANT_CONVERT_XPATHRESULTTYPE_TO_BOOLEAN          */
+  public static final String ER_CANT_CONVERT_TO_BOOLEAN = 
+	 "ER_CANT_CONVERT_TO_BOOLEAN";
+  /** Field ER_CANT_CONVERT_TO_SINGLENODE       */
+  public static final String ER_CANT_CONVERT_TO_SINGLENODE = 
+	 "ER_CANT_CONVERT_TO_SINGLENODE";
+  /** Field ER_CANT_GET_SNAPSHOT_LENGTH         */
+  public static final String ER_CANT_GET_SNAPSHOT_LENGTH = 
+	 "ER_CANT_GET_SNAPSHOT_LENGTH";
+  /** Field ER_NON_ITERATOR_TYPE                */
+  public static final String ER_NON_ITERATOR_TYPE = "ER_NON_ITERATOR_TYPE";
+  /** Field ER_DOC_MUTATED                      */
+  public static final String ER_DOC_MUTATED = "ER_DOC_MUTATED";
+  public static final String ER_INVALID_XPATH_TYPE = "ER_INVALID_XPATH_TYPE";
+  public static final String ER_EMPTY_XPATH_RESULT = "ER_EMPTY_XPATH_RESULT";
+  public static final String ER_INCOMPATIBLE_TYPES = "ER_INCOMPATIBLE_TYPES";
+  public static final String ER_NULL_RESOLVER = "ER_NULL_RESOLVER";
+  public static final String ER_CANT_CONVERT_TO_STRING = 
+	 "ER_CANT_CONVERT_TO_STRING";
+  public static final String ER_NON_SNAPSHOT_TYPE = "ER_NON_SNAPSHOT_TYPE";
+  public static final String ER_WRONG_DOCUMENT = "ER_WRONG_DOCUMENT";
+  /* Note to translators:  The XPath expression cannot be evaluated with respect
+   * to this type of node.
+   */
+  /** Field ER_WRONG_NODETYPE                    */
+  public static final String ER_WRONG_NODETYPE = "ER_WRONG_NODETYPE";
+  public static final String ER_XPATH_ERROR = "ER_XPATH_ERROR";
+
+  //BEGIN: Keys needed for exception messages of  JAXP 1.3 XPath API implementation
+  public static final String ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED = "ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED";
+  public static final String ER_RESOLVE_VARIABLE_RETURNS_NULL = "ER_RESOLVE_VARIABLE_RETURNS_NULL";
+  public static final String ER_UNSUPPORTED_RETURN_TYPE = "ER_UNSUPPORTED_RETURN_TYPE";
+  public static final String ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL = "ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL";
+  public static final String ER_ARG_CANNOT_BE_NULL = "ER_ARG_CANNOT_BE_NULL";
+
+  public static final String ER_OBJECT_MODEL_NULL = "ER_OBJECT_MODEL_NULL";
+  public static final String ER_OBJECT_MODEL_EMPTY = "ER_OBJECT_MODEL_EMPTY";
+  public static final String ER_FEATURE_NAME_NULL = "ER_FEATURE_NAME_NULL";
+  public static final String ER_FEATURE_UNKNOWN = "ER_FEATURE_UNKNOWN";
+  public static final String ER_GETTING_NULL_FEATURE = "ER_GETTING_NULL_FEATURE";
+  public static final String ER_GETTING_UNKNOWN_FEATURE = "ER_GETTING_UNKNOWN_FEATURE";
+  public static final String ER_NULL_XPATH_FUNCTION_RESOLVER = "ER_NULL_XPATH_FUNCTION_RESOLVER";
+  public static final String ER_NULL_XPATH_VARIABLE_RESOLVER = "ER_NULL_XPATH_VARIABLE_RESOLVER";
+  //END: Keys needed for exception messages of  JAXP 1.3 XPath API implementation 
+
+  public static final String WG_LOCALE_NAME_NOT_HANDLED = 
+	 "WG_LOCALE_NAME_NOT_HANDLED";
+  public static final String WG_PROPERTY_NOT_SUPPORTED = 
+	 "WG_PROPERTY_NOT_SUPPORTED";
+  public static final String WG_DONT_DO_ANYTHING_WITH_NS = 
+	 "WG_DONT_DO_ANYTHING_WITH_NS";
+  public static final String WG_SECURITY_EXCEPTION = "WG_SECURITY_EXCEPTION";
+  public static final String WG_QUO_NO_LONGER_DEFINED = 
+	 "WG_QUO_NO_LONGER_DEFINED";
+  public static final String WG_NEED_DERIVED_OBJECT_TO_IMPLEMENT_NODETEST = 
+	 "WG_NEED_DERIVED_OBJECT_TO_IMPLEMENT_NODETEST";
+  public static final String WG_FUNCTION_TOKEN_NOT_FOUND = 
+	 "WG_FUNCTION_TOKEN_NOT_FOUND";
+  public static final String WG_COULDNOT_FIND_FUNCTION = 
+	 "WG_COULDNOT_FIND_FUNCTION";
+  public static final String WG_CANNOT_MAKE_URL_FROM ="WG_CANNOT_MAKE_URL_FROM";
+  public static final String WG_EXPAND_ENTITIES_NOT_SUPPORTED = 
+	 "WG_EXPAND_ENTITIES_NOT_SUPPORTED";
+  public static final String WG_ILLEGAL_VARIABLE_REFERENCE = 
+	 "WG_ILLEGAL_VARIABLE_REFERENCE";
+  public static final String WG_UNSUPPORTED_ENCODING ="WG_UNSUPPORTED_ENCODING";
+
+  /**  detach() not supported by XRTreeFragSelectWrapper   */
+  public static final String ER_DETACH_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER =
+	 "ER_DETACH_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER";
+  /**  num() not supported by XRTreeFragSelectWrapper   */
+  public static final String ER_NUM_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER =
+	 "ER_NUM_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER";
+  /**  xstr() not supported by XRTreeFragSelectWrapper   */
+  public static final String ER_XSTR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER =
+	 "ER_XSTR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER";
+  /**  str() not supported by XRTreeFragSelectWrapper   */
+  public static final String ER_STR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER =
+	 "ER_STR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER";
+
+  // Error messages...
+
+
+  /**
+   * Get the association list.
+   *
+   * @return The association list.
+   */
+  public Object[][] getContents()
+  {
+    return new Object[][]{
+
+  { "ERROR0000" , "{0}" },
+
+  { ER_CURRENT_NOT_ALLOWED_IN_MATCH, "The current() function is not allowed in a match pattern!" },
+
+  { ER_CURRENT_TAKES_NO_ARGS, "The current() function does not accept arguments!" },
+
+  { ER_DOCUMENT_REPLACED,
+      "document() function implementation has been replaced by org.apache.xalan.xslt.FuncDocument!"},
+
+  { ER_CONTEXT_HAS_NO_OWNERDOC,
+      "context does not have an owner document!"},
+
+  { ER_LOCALNAME_HAS_TOO_MANY_ARGS,
+      "local-name() has too many arguments."},
+
+  { ER_NAMESPACEURI_HAS_TOO_MANY_ARGS,
+      "namespace-uri() has too many arguments."},
+
+  { ER_NORMALIZESPACE_HAS_TOO_MANY_ARGS,
+      "normalize-space() has too many arguments."},
+
+  { ER_NUMBER_HAS_TOO_MANY_ARGS,
+      "number() has too many arguments."},
+
+  { ER_NAME_HAS_TOO_MANY_ARGS,
+     "name() has too many arguments."},
+
+  { ER_STRING_HAS_TOO_MANY_ARGS,
+      "string() has too many arguments."},
+
+  { ER_STRINGLENGTH_HAS_TOO_MANY_ARGS,
+      "string-length() has too many arguments."},
+
+  { ER_TRANSLATE_TAKES_3_ARGS,
+      "The translate() function takes three arguments!"},
+
+  { ER_UNPARSEDENTITYURI_TAKES_1_ARG,
+      "The unparsed-entity-uri function should take one argument!"},
+
+  { ER_NAMESPACEAXIS_NOT_IMPLEMENTED,
+      "namespace axis not implemented yet!"},
+
+  { ER_UNKNOWN_AXIS,
+     "unknown axis: {0}"},
+
+  { ER_UNKNOWN_MATCH_OPERATION,
+     "unknown match operation!"},
+
+  { ER_INCORRECT_ARG_LENGTH,
+      "Arg length of processing-instruction() node test is incorrect!"},
+
+  { ER_CANT_CONVERT_TO_NUMBER,
+      "Can not convert {0} to a number"},
+
+  { ER_CANT_CONVERT_TO_NODELIST,
+      "Can not convert {0} to a NodeList!"},
+
+  { ER_CANT_CONVERT_TO_MUTABLENODELIST,
+      "Can not convert {0} to a NodeSetDTM!"},
+
+  { ER_CANT_CONVERT_TO_TYPE,
+      "Can not convert {0} to a type#{1}"},
+
+  { ER_EXPECTED_MATCH_PATTERN,
+      "Expected match pattern in getMatchScore!"},
+
+  { ER_COULDNOT_GET_VAR_NAMED,
+      "Could not get variable named {0}"},
+
+  { ER_UNKNOWN_OPCODE,
+     "ERROR! Unknown op code: {0}"},
+
+  { ER_EXTRA_ILLEGAL_TOKENS,
+     "Extra illegal tokens: {0}"},
+
+  { ER_EXPECTED_DOUBLE_QUOTE,
+      "misquoted literal... expected double quote!"},
+
+  { ER_EXPECTED_SINGLE_QUOTE,
+      "misquoted literal... expected single quote!"},
+
+  { ER_EMPTY_EXPRESSION,
+     "Empty expression!"},
+
+  { ER_EXPECTED_BUT_FOUND,
+     "Expected {0}, but found: {1}"},
+
+  { ER_INCORRECT_PROGRAMMER_ASSERTION,
+      "Programmer assertion is incorrect! - {0}"},
+
+  { ER_BOOLEAN_ARG_NO_LONGER_OPTIONAL,
+      "boolean(...) argument is no longer optional with 19990709 XPath draft."},
+
+  { ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG,
+      "Found ',' but no preceding argument!"},
+
+  { ER_FOUND_COMMA_BUT_NO_FOLLOWING_ARG,
+      "Found ',' but no following argument!"},
+
+  { ER_PREDICATE_ILLEGAL_SYNTAX,
+      "'..[predicate]' or '.[predicate]' is illegal syntax.  Use 'self::node()[predicate]' instead."},
+
+  { ER_ILLEGAL_AXIS_NAME,
+     "illegal axis name: {0}"},
+
+  { ER_UNKNOWN_NODETYPE,
+     "Unknown nodetype: {0}"},
+
+  { ER_PATTERN_LITERAL_NEEDS_BE_QUOTED,
+      "Pattern literal ({0}) needs to be quoted!"},
+
+  { ER_COULDNOT_BE_FORMATTED_TO_NUMBER,
+      "{0} could not be formatted to a number!"},
+
+  { ER_COULDNOT_CREATE_XMLPROCESSORLIAISON,
+      "Could not create XML TransformerFactory Liaison: {0}"},
+
+  { ER_DIDNOT_FIND_XPATH_SELECT_EXP,
+      "Error! Did not find xpath select expression (-select)."},
+
+  { ER_COULDNOT_FIND_ENDOP_AFTER_OPLOCATIONPATH,
+      "ERROR! Could not find ENDOP after OP_LOCATIONPATH"},
+
+  { ER_ERROR_OCCURED,
+     "Error occured!"},
+
+  { ER_ILLEGAL_VARIABLE_REFERENCE,
+      "VariableReference given for variable out of context or without definition!  Name = {0}"},
+
+  { ER_AXES_NOT_ALLOWED,
+      "Only child:: and attribute:: axes are allowed in match patterns!  Offending axes = {0}"},
+
+  { ER_KEY_HAS_TOO_MANY_ARGS,
+      "key() has an incorrect number of arguments."},
+
+  { ER_COUNT_TAKES_1_ARG,
+      "The count function should take one argument!"},
+
+  { ER_COULDNOT_FIND_FUNCTION,
+     "Could not find function: {0}"},
+
+  { ER_UNSUPPORTED_ENCODING,
+     "Unsupported encoding: {0}"},
+
+  { ER_PROBLEM_IN_DTM_NEXTSIBLING,
+      "Problem occured in DTM in getNextSibling... trying to recover"},
+
+  { ER_CANNOT_WRITE_TO_EMPTYNODELISTIMPL,
+      "Programmer error: EmptyNodeList can not be written to."},
+
+  { ER_SETDOMFACTORY_NOT_SUPPORTED,
+      "setDOMFactory is not supported by XPathContext!"},
+
+  { ER_PREFIX_MUST_RESOLVE,
+      "Prefix must resolve to a namespace: {0}"},
+
+  { ER_PARSE_NOT_SUPPORTED,
+      "parse (InputSource source) not supported in XPathContext! Can not open {0}"},
+
+  { ER_SAX_API_NOT_HANDLED,
+      "SAX API characters(char ch[]... not handled by the DTM!"},
+
+  { ER_IGNORABLE_WHITESPACE_NOT_HANDLED,
+      "ignorableWhitespace(char ch[]... not handled by the DTM!"},
+
+  { ER_DTM_CANNOT_HANDLE_NODES,
+      "DTMLiaison can not handle nodes of type {0}"},
+
+  { ER_XERCES_CANNOT_HANDLE_NODES,
+      "DOM2Helper can not handle nodes of type {0}"},
+
+  { ER_XERCES_PARSE_ERROR_DETAILS,
+      "DOM2Helper.parse error: SystemID - {0} line - {1}"},
+
+  { ER_XERCES_PARSE_ERROR,
+     "DOM2Helper.parse error"},
+
+  { ER_INVALID_UTF16_SURROGATE,
+      "Invalid UTF-16 surrogate detected: {0} ?"},
+
+  { ER_OIERROR,
+     "IO error"},
+
+  { ER_CANNOT_CREATE_URL,
+     "Cannot create url for: {0}"},
+
+  { ER_XPATH_READOBJECT,
+     "In XPath.readObject: {0}"},
+
+  { ER_FUNCTION_TOKEN_NOT_FOUND,
+      "function token not found."},
+
+  { ER_CANNOT_DEAL_XPATH_TYPE,
+       "Can not deal with XPath type: {0}"},
+
+  { ER_NODESET_NOT_MUTABLE,
+       "This NodeSet is not mutable"},
+
+  { ER_NODESETDTM_NOT_MUTABLE,
+       "This NodeSetDTM is not mutable"},
+
+  { ER_VAR_NOT_RESOLVABLE,
+        "Variable not resolvable: {0}"},
+
+  { ER_NULL_ERROR_HANDLER,
+        "Null error handler"},
+
+  { ER_PROG_ASSERT_UNKNOWN_OPCODE,
+       "Programmer''s assertion: unknown opcode: {0}"},
+
+  { ER_ZERO_OR_ONE,
+       "0 or 1"},
+
+  { ER_RTF_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER,
+       "rtf() not supported by XRTreeFragSelectWrapper"},
+
+  { ER_RTF_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER,
+       "asNodeIterator() not supported by XRTreeFragSelectWrapper"},
+       
+	/**  detach() not supported by XRTreeFragSelectWrapper   */
+   { ER_DETACH_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER,
+		"detach() not supported by XRTreeFragSelectWrapper"},
+		
+	/**  num() not supported by XRTreeFragSelectWrapper   */
+   { ER_NUM_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER,
+		"num() not supported by XRTreeFragSelectWrapper"},
+		
+	/**  xstr() not supported by XRTreeFragSelectWrapper   */
+   { ER_XSTR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER,
+		"xstr() not supported by XRTreeFragSelectWrapper"},
+		
+	/**  str() not supported by XRTreeFragSelectWrapper   */
+   { ER_STR_NOT_SUPPORTED_XRTREEFRAGSELECTWRAPPER,
+		"str() not supported by XRTreeFragSelectWrapper"},
+
+  { ER_FSB_NOT_SUPPORTED_XSTRINGFORCHARS,
+       "fsb() not supported for XStringForChars"},
+
+  { ER_COULD_NOT_FIND_VAR,
+      "Could not find variable with the name of {0}"},
+
+  { ER_XSTRINGFORCHARS_CANNOT_TAKE_STRING,
+      "XStringForChars can not take a string for an argument"},
+
+  { ER_FASTSTRINGBUFFER_CANNOT_BE_NULL,
+      "The FastStringBuffer argument can not be null"},
+
+  { ER_TWO_OR_THREE,
+       "2 or 3"},
+
+  { ER_VARIABLE_ACCESSED_BEFORE_BIND,
+       "Variable accessed before it is bound!"},
+
+  { ER_FSB_CANNOT_TAKE_STRING,
+       "XStringForFSB can not take a string for an argument!"},
+
+  { ER_SETTING_WALKER_ROOT_TO_NULL,
+       "\n !!!! Error! Setting the root of a walker to null!!!"},
+
+  { ER_NODESETDTM_CANNOT_ITERATE,
+       "This NodeSetDTM can not iterate to a previous node!"},
+
+  { ER_NODESET_CANNOT_ITERATE,
+       "This NodeSet can not iterate to a previous node!"},
+
+  { ER_NODESETDTM_CANNOT_INDEX,
+       "This NodeSetDTM can not do indexing or counting functions!"},
+
+  { ER_NODESET_CANNOT_INDEX,
+       "This NodeSet can not do indexing or counting functions!"},
+
+  { ER_CANNOT_CALL_SETSHOULDCACHENODE,
+       "Can not call setShouldCacheNodes after nextNode has been called!"},
+
+  { ER_ONLY_ALLOWS,
+       "{0} only allows {1} arguments"},
+
+  { ER_UNKNOWN_STEP,
+       "Programmer''s assertion in getNextStepPos: unknown stepType: {0}"},
+
+  //Note to translators:  A relative location path is a form of XPath expression.
+  // The message indicates that such an expression was expected following the
+  // characters '/' or '//', but was not found.
+  { ER_EXPECTED_REL_LOC_PATH,
+      "A relative location path was expected following the '/' or '//' token."},
+
+  // Note to translators:  A location path is a form of XPath expression.
+  // The message indicates that syntactically such an expression was expected,but
+  // the characters specified by the substitution text were encountered instead.
+  { ER_EXPECTED_LOC_PATH,
+       "A location path was expected, but the following token was encountered\u003a  {0}"},
+
+  // Note to translators:  A location path is a form of XPath expression.
+  // The message indicates that syntactically such a subexpression was expected,
+  // but no more characters were found in the expression.
+  { ER_EXPECTED_LOC_PATH_AT_END_EXPR,
+       "A location path was expected, but the end of the XPath expression was found instead."},
+
+  // Note to translators:  A location step is part of an XPath expression.
+  // The message indicates that syntactically such an expression was expected
+  // following the specified characters.
+  { ER_EXPECTED_LOC_STEP,
+       "A location step was expected following the '/' or '//' token."},
+
+  // Note to translators:  A node test is part of an XPath expression that is
+  // used to test for particular kinds of nodes.  In this case, a node test that
+  // consists of an NCName followed by a colon and an asterisk or that consists
+  // of a QName was expected, but was not found.
+  { ER_EXPECTED_NODE_TEST,
+       "A node test that matches either NCName:* or QName was expected."},
+
+  // Note to translators:  A step pattern is part of an XPath expression.
+  // The message indicates that syntactically such an expression was expected,
+  // but the specified character was found in the expression instead.
+  { ER_EXPECTED_STEP_PATTERN,
+       "A step pattern was expected, but '/' was encountered."},
+
+  // Note to translators: A relative path pattern is part of an XPath expression.
+  // The message indicates that syntactically such an expression was expected,
+  // but was not found.
+  { ER_EXPECTED_REL_PATH_PATTERN,
+       "A relative path pattern was expected."},
+
+  // Note to translators:  The substitution text is the name of a data type.  The
+  // message indicates that a value of a particular type could not be converted
+  // to a value of type boolean.
+  { ER_CANT_CONVERT_TO_BOOLEAN,
+       "The XPathResult of XPath expression ''{0}'' has an XPathResultType of {1} which cannot be converted to a boolean."},
+
+  // Note to translators: Do not translate ANY_UNORDERED_NODE_TYPE and 
+  // FIRST_ORDERED_NODE_TYPE.
+  { ER_CANT_CONVERT_TO_SINGLENODE,
+       "The XPathResult of XPath expression ''{0}'' has an XPathResultType of {1} which cannot be converted to a single node. The method getSingleNodeValue applies only to types ANY_UNORDERED_NODE_TYPE and FIRST_ORDERED_NODE_TYPE."},
+
+  // Note to translators: Do not translate UNORDERED_NODE_SNAPSHOT_TYPE and
+  // ORDERED_NODE_SNAPSHOT_TYPE.
+  { ER_CANT_GET_SNAPSHOT_LENGTH,
+       "The method getSnapshotLength cannot be called on the XPathResult of XPath expression ''{0}'' because its XPathResultType is {1}. This method applies only to types UNORDERED_NODE_SNAPSHOT_TYPE and ORDERED_NODE_SNAPSHOT_TYPE."},
+
+  { ER_NON_ITERATOR_TYPE,
+       "The method iterateNext cannot be called on the XPathResult of XPath expression ''{0}'' because its XPathResultType is {1}. This method applies only to types UNORDERED_NODE_ITERATOR_TYPE and ORDERED_NODE_ITERATOR_TYPE."},
+
+  // Note to translators: This message indicates that the document being operated
+  // upon changed, so the iterator object that was being used to traverse the
+  // document has now become invalid.
+  { ER_DOC_MUTATED,
+       "Document mutated since result was returned. Iterator is invalid."},
+
+  { ER_INVALID_XPATH_TYPE,
+       "Invalid XPath type argument: {0}"},
+
+  { ER_EMPTY_XPATH_RESULT,
+       "Empty XPath result object"},
+
+  { ER_INCOMPATIBLE_TYPES,
+       "The XPathResult of XPath expression ''{0}'' has an XPathResultType of {1} which cannot be coerced into the specified XPathResultType of {2}."},
+
+  { ER_NULL_RESOLVER,
+       "Unable to resolve prefix with null prefix resolver."},
+
+  // Note to translators:  The substitution text is the name of a data type.  The
+  // message indicates that a value of a particular type could not be converted
+  // to a value of type string.
+  { ER_CANT_CONVERT_TO_STRING,
+       "The XPathResult of XPath expression ''{0}'' has an XPathResultType of {1} which cannot be converted to a string."},
+
+  // Note to translators: Do not translate snapshotItem,
+  // UNORDERED_NODE_SNAPSHOT_TYPE and ORDERED_NODE_SNAPSHOT_TYPE.
+  { ER_NON_SNAPSHOT_TYPE,
+       "The method snapshotItem cannot be called on the XPathResult of XPath expression ''{0}'' because its XPathResultType is {1}. This method applies only to types UNORDERED_NODE_SNAPSHOT_TYPE and ORDERED_NODE_SNAPSHOT_TYPE."},
+
+  // Note to translators:  XPathEvaluator is a Java interface name.  An
+  // XPathEvaluator is created with respect to a particular XML document, and in
+  // this case the expression represented by this object was being evaluated with
+  // respect to a context node from a different document.
+  { ER_WRONG_DOCUMENT,
+       "Context node does not belong to the document that is bound to this XPathEvaluator."},
+
+  // Note to translators:  The XPath expression cannot be evaluated with respect
+  // to this type of node.
+  { ER_WRONG_NODETYPE,
+       "The context node type is not supported."},
+
+  { ER_XPATH_ERROR,
+       "Unknown error in XPath."},
+
+  { ER_CANT_CONVERT_XPATHRESULTTYPE_TO_NUMBER,
+	"The XPathResult of XPath expression ''{0}'' has an XPathResultType of {1} which cannot be converted to a number"},       
+
+  //BEGIN:  Definitions of error keys used  in exception messages of  JAXP 1.3 XPath API implementation
+
+  /** Field ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED                       */
+
+  { ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
+       "Extension function: ''{0}'' can not be invoked when the XMLConstants.FEATURE_SECURE_PROCESSING feature is set to true."},
+
+  /** Field ER_RESOLVE_VARIABLE_RETURNS_NULL                       */
+
+  { ER_RESOLVE_VARIABLE_RETURNS_NULL,
+       "resolveVariable for variable {0} returning null"},
+
+  /** Field ER_UNSUPPORTED_RETURN_TYPE                       */
+
+  { ER_UNSUPPORTED_RETURN_TYPE,
+       "UnSupported Return Type : {0}"},
+
+  /** Field ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL                       */
+
+  { ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL,
+       "Source and/or Return Type can not be null"},
+
+  /** Field ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL                       */
+
+  { ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL,
+       "Source and/or Return Type can not be null"},
+
+  /** Field ER_ARG_CANNOT_BE_NULL                       */
+
+  { ER_ARG_CANNOT_BE_NULL,
+       "{0} argument can not be null"},
+
+  /** Field ER_OBJECT_MODEL_NULL                       */
+
+  { ER_OBJECT_MODEL_NULL,
+       "{0}#isObjectModelSupported( String objectModel ) cannot be called with objectModel == null"},
+
+  /** Field ER_OBJECT_MODEL_EMPTY                       */
+
+  { ER_OBJECT_MODEL_EMPTY,
+       "{0}#isObjectModelSupported( String objectModel ) cannot be called with objectModel == \"\""},
+
+  /** Field ER_OBJECT_MODEL_EMPTY                       */
+
+  { ER_FEATURE_NAME_NULL,
+       "Trying to set a feature with a null name: {0}#setFeature( null, {1})"},
+
+  /** Field ER_FEATURE_UNKNOWN                       */
+
+  { ER_FEATURE_UNKNOWN,
+       "Trying to set the unknown feature \"{0}\":{1}#setFeature({0},{2})"},
+
+  /** Field ER_GETTING_NULL_FEATURE                       */
+
+  { ER_GETTING_NULL_FEATURE,
+       "Trying to get a feature with a null name: {0}#getFeature(null)"},
+
+  /** Field ER_GETTING_NULL_FEATURE                       */
+
+  { ER_GETTING_UNKNOWN_FEATURE,
+       "Trying to get the unknown feature \"{0}\":{1}#getFeature({0})"},
+
+  /** Field ER_NULL_XPATH_FUNCTION_RESOLVER                       */
+
+  { ER_NULL_XPATH_FUNCTION_RESOLVER,
+       "Attempting to set a null XPathFunctionResolver:{0}#setXPathFunctionResolver(null)"},
+
+  /** Field ER_NULL_XPATH_VARIABLE_RESOLVER                       */
+
+  { ER_NULL_XPATH_VARIABLE_RESOLVER,
+       "Attempting to set a null XPathVariableResolver:{0}#setXPathVariableResolver(null)"},
+
+  //END:  Definitions of error keys used  in exception messages of  JAXP 1.3 XPath API implementation
+
+  // Warnings...
+
+  { WG_LOCALE_NAME_NOT_HANDLED,
+      "locale name in the format-number function not yet handled!"},
+
+  { WG_PROPERTY_NOT_SUPPORTED,
+      "XSL Property not supported: {0}"},
+
+  { WG_DONT_DO_ANYTHING_WITH_NS,
+      "Do not currently do anything with namespace {0} in property: {1}"},
+
+  { WG_SECURITY_EXCEPTION,
+      "SecurityException when trying to access XSL system property: {0}"},
+
+  { WG_QUO_NO_LONGER_DEFINED,
+      "Old syntax: quo(...) is no longer defined in XPath."},
+
+  { WG_NEED_DERIVED_OBJECT_TO_IMPLEMENT_NODETEST,
+      "XPath needs a derived object to implement nodeTest!"},
+
+  { WG_FUNCTION_TOKEN_NOT_FOUND,
+      "function token not found."},
+
+  { WG_COULDNOT_FIND_FUNCTION,
+      "Could not find function: {0}"},
+
+  { WG_CANNOT_MAKE_URL_FROM,
+      "Can not make URL from: {0}"},
+
+  { WG_EXPAND_ENTITIES_NOT_SUPPORTED,
+      "-E option not supported for DTM parser"},
+
+  { WG_ILLEGAL_VARIABLE_REFERENCE,
+      "VariableReference given for variable out of context or without definition!  Name = {0}"},
+
+  { WG_UNSUPPORTED_ENCODING,
+     "Unsupported encoding: {0}"},
+
+
+
+  // Other miscellaneous text used inside the code...
+  { "ui_language", "en"},
+  { "help_language", "en"},
+  { "language", "en"},
+  { "BAD_CODE", "Parameter to createMessage was out of bounds"},
+  { "FORMAT_FAILED", "Exception thrown during messageFormat call"},
+  { "version", ">>>>>>> Xalan Version "},
+  { "version2", "<<<<<<<"},
+  { "yes", "yes"},
+  { "line", "Line #"},
+  { "column", "Column #"},
+  { "xsldone", "XSLProcessor: done"},
+  { "xpath_option", "xpath options: "},
+  { "optionIN", "   [-in inputXMLURL]"},
+  { "optionSelect", "   [-select xpath expression]"},
+  { "optionMatch", "   [-match match pattern (for match diagnostics)]"},
+  { "optionAnyExpr", "Or just an xpath expression will do a diagnostic dump"},
+  { "noParsermsg1", "XSL Process was not successful."},
+  { "noParsermsg2", "** Could not find parser **"},
+  { "noParsermsg3", "Please check your classpath."},
+  { "noParsermsg4", "If you don't have IBM's XML Parser for Java, you can download it from"},
+  { "noParsermsg5", "IBM's AlphaWorks: http://www.alphaworks.ibm.com/formula/xml"},
+  { "gtone", ">1" },
+  { "zero", "0" },
+  { "one", "1" },
+  { "two" , "2" },
+  { "three", "3" }
+
+  };
+  }
+
+
+  // ================= INFRASTRUCTURE ======================
+
+  /** Field BAD_CODE          */
+  public static final String BAD_CODE = "BAD_CODE";
+
+  /** Field FORMAT_FAILED          */
+  public static final String FORMAT_FAILED = "FORMAT_FAILED";
+
+  /** Field ERROR_RESOURCES          */
+  public static final String ERROR_RESOURCES =
+    "org.apache.xpath.res.XPATHErrorResources";
+
+  /** Field ERROR_STRING          */
+  public static final String ERROR_STRING = "#error";
+
+  /** Field ERROR_HEADER          */
+  public static final String ERROR_HEADER = "Error: ";
+
+  /** Field WARNING_HEADER          */
+  public static final String WARNING_HEADER = "Warning: ";
+
+  /** Field XSL_HEADER          */
+  public static final String XSL_HEADER = "XSL ";
+
+  /** Field XML_HEADER          */
+  public static final String XML_HEADER = "XML ";
+
+  /** Field QUERY_HEADER          */
+  public static final String QUERY_HEADER = "PATTERN ";
+
+
+  /**
+   * Return a named ResourceBundle for a particular locale.  This method mimics the behavior
+   * of ResourceBundle.getBundle().
+   *
+   * @param className Name of local-specific subclass.
+   * @return the ResourceBundle
+   * @throws MissingResourceException
+   */
+  public static final XPATHErrorResources loadResourceBundle(String className)
+          throws MissingResourceException
+  {
+
+    Locale locale = Locale.getDefault();
+    String suffix = getResourceSuffix(locale);
+
+    try
+    {
+
+      // first try with the given locale
+      return (XPATHErrorResources) ResourceBundle.getBundle(className
+              + suffix, locale);
+    }
+    catch (MissingResourceException e)
+    {
+      try  // try to fall back to en_US if we can't load
+      {
+
+        // Since we can't find the localized property file,
+        // fall back to en_US.
+        return (XPATHErrorResources) ResourceBundle.getBundle(className,
+                new Locale("en", "US"));
+      }
+      catch (MissingResourceException e2)
+      {
+
+        // Now we are really in trouble.
+        // very bad, definitely very bad...not going to get very far
+        throw new MissingResourceException(
+          "Could not load any resource bundles.", className, "");
+      }
+    }
+  }
+
+  /**
+   * Return the resource file suffic for the indicated locale
+   * For most locales, this will be based the language code.  However
+   * for Chinese, we do distinguish between Taiwan and PRC
+   *
+   * @param locale the locale
+   * @return an String suffix which canbe appended to a resource name
+   */
+  private static final String getResourceSuffix(Locale locale)
+  {
+
+    String suffix = "_" + locale.getLanguage();
+    String country = locale.getCountry();
+
+    if (country.equals("TW"))
+      suffix += "_" + country;
+
+    return suffix;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/res/XPATHMessages.java b/src/main/java/org/apache/xpath/res/XPATHMessages.java
new file mode 100644
index 0000000..7fb6135
--- /dev/null
+++ b/src/main/java/org/apache/xpath/res/XPATHMessages.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: XPATHMessages.java 468655 2006-10-28 07:12:06Z minchau $
+ */
+package org.apache.xpath.res;
+
+import java.util.ListResourceBundle;
+
+import org.apache.xml.res.XMLMessages;
+
+/**
+ * A utility class for issuing XPath error messages.
+ * @xsl.usage internal
+ */
+public class XPATHMessages extends XMLMessages
+{
+  /** The language specific resource object for XPath messages.  */
+  private static ListResourceBundle XPATHBundle = new XPATHErrorResources();
+
+  /** The class name of the XPath error message string table.     */
+  private static final String XPATH_ERROR_RESOURCES =
+    "org.apache.xpath.res.XPATHErrorResources";
+  
+  /**
+   * Creates a message from the specified key and replacement
+   * arguments, localized to the given locale.
+   *
+   * @param msgKey    The key for the message text.
+   * @param args      The arguments to be used as replacement text
+   *                  in the message created.
+   *
+   * @return The formatted message string.
+   */
+  public static final String createXPATHMessage(String msgKey, Object args[])  //throws Exception 
+  {
+      // BEGIN android-changed
+      //     don't localize exception messages
+      return createXPATHMsg(XPATHBundle, msgKey, args);
+      // END android-changed
+  }
+
+  /**
+   * Creates a message from the specified key and replacement
+   * arguments, localized to the given locale.
+   *
+   * @param msgKey The key for the message text.
+   * @param args      The arguments to be used as replacement text
+   *                  in the message created.
+   *
+   * @return The formatted warning string.
+   */
+  public static final String createXPATHWarning(String msgKey, Object args[])  //throws Exception
+  {
+      // BEGIN android-changed
+      //     don't localize exception messages
+      return createXPATHMsg(XPATHBundle, msgKey, args);
+      // END android-changed
+  }
+
+  /**
+   * Creates a message from the specified key and replacement
+   * arguments, localized to the given locale.
+   *
+   * @param fResourceBundle The resource bundle to use.
+   * @param msgKey  The message key to use.
+   * @param args      The arguments to be used as replacement text
+   *                  in the message created.
+   *
+   * @return The formatted message string.
+   */
+  public static final String createXPATHMsg(ListResourceBundle fResourceBundle,
+                                            String msgKey, Object args[])  //throws Exception
+  {
+
+    String fmsg = null;
+    boolean throwex = false;
+    String msg = null;
+
+    if (msgKey != null)
+      msg = fResourceBundle.getString(msgKey); 
+
+    if (msg == null)
+    {
+      msg = fResourceBundle.getString(XPATHErrorResources.BAD_CODE);
+      throwex = true;
+    }
+
+    if (args != null)
+    {
+      try
+      {
+
+        // Do this to keep format from crying.
+        // This is better than making a bunch of conditional
+        // code all over the place.
+        int n = args.length;
+
+        for (int i = 0; i < n; i++)
+        {
+          if (null == args[i])
+            args[i] = "";
+        }
+
+        fmsg = java.text.MessageFormat.format(msg, args);
+      }
+      catch (Exception e)
+      {
+        fmsg = fResourceBundle.getString(XPATHErrorResources.FORMAT_FAILED);
+        fmsg += " " + msg;
+      }
+    }
+    else
+      fmsg = msg;
+
+    if (throwex)
+    {
+      throw new RuntimeException(fmsg);
+    }
+
+    return fmsg;
+  }
+
+}
diff --git a/src/main/java/org/apache/xpath/res/package.html b/src/main/java/org/apache/xpath/res/package.html
new file mode 100644
index 0000000..ad04feb
--- /dev/null
+++ b/src/main/java/org/apache/xpath/res/package.html
@@ -0,0 +1,26 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- $Id: package.html 468655 2006-10-28 07:12:06Z minchau $ -->
+<html>
+  <title>XPath resources.</title>
+  <body>
+    <p>Contains strings for XPath support that require internationalization.<p>
+ </body>
+</html>
+
+