jkesselm 01/08/06 11:38:28
Modified: java/src/org/apache/xml/utils NamespaceSupport2.java
Log:
Playing a hunch, I moved the Context objects out of NamespaceSupport2.
They're now a second class in the same file rather than being an inner class.
That reduces the size of the Context2 object, and its initialization time, by
removing the need for it to carry a reference to the NamespaceSupport2
object that created it.
Performance seems significantly improved... even (surprisingly!) on the
getDeclaredPrefixes operation. Memory usage also seems better, as
expected.
NamespaceSupport2 is still acounting for about 5%
of the time spent in the ResultTreeHandler, according
to my latest trace. Apparently we are calling
getDeclaredPrefixes an obscenely large number of
times, and the Vector.elements call is adding up. We
may want to look at where this is being called from
and whether we can reduce the number of invocations.
Revision Changes Path
1.6 +338 -333
xml-xalan/java/src/org/apache/xml/utils/NamespaceSupport2.java
Index: NamespaceSupport2.java
===================================================================
RCS file:
/home/cvs/xml-xalan/java/src/org/apache/xml/utils/NamespaceSupport2.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- NamespaceSupport2.java 2001/08/06 14:56:32 1.5
+++ NamespaceSupport2.java 2001/08/06 18:38:28 1.6
@@ -61,7 +61,6 @@
import java.util.Hashtable;
import java.util.Vector;
-
/**
* Encapsulate Namespace tracking logic for use by SAX drivers.
*
@@ -85,7 +84,7 @@
// Internal state.
////////////////////////////////////////////////////////////////////
- private Context currentContext; // Current point on the double-linked
stack
+ private Context2 currentContext; // Current point on the double-linked
stack
////////////////////////////////////////////////////////////////////
@@ -103,12 +102,6 @@
"http://www.w3.org/XML/1998/namespace";
- /**
- * An empty enumeration.
- */
- private final static Enumeration EMPTY_ENUMERATION =
- new Vector().elements();
-
////////////////////////////////////////////////////////////////////
// Constructor.
////////////////////////////////////////////////////////////////////
@@ -139,7 +132,7 @@
// JJK: 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 Context(null);
+ currentContext = new Context2(null);
currentContext.declarePrefix("xml", XMLNS);
}
@@ -165,10 +158,10 @@
// We may want to retain for reuse, but that can be done via
// a child pointer.
- Context parentContext=currentContext;
+ Context2 parentContext=currentContext;
currentContext = parentContext.getChild();
if (currentContext == null){
- currentContext = new Context(parentContext);
+ currentContext = new Context2(parentContext);
}
else{
// JJK: This will wipe out any leftover data
@@ -193,7 +186,7 @@
*/
public void popContext ()
{
- Context parentContext=currentContext.getParent();
+ Context2 parentContext=currentContext.getParent();
if(parentContext==null)
throw new EmptyStackException();
else
@@ -391,60 +384,9 @@
//
// **** Currently a filter. That may not be most efficient
// when I'm done restructuring storage!
-
- PrefixEnumerator prefixEnumerator = new PrefixEnumerator();
- return prefixEnumerator.setup(uri,getPrefixes());
+ return new PrefixForUriEnumerator(this,uri,getPrefixes());
}
- /**
- * Implementation of Enumeration filter, wrapped
- * aroung the get-all-prefixes version of the operation.
- */
- class PrefixEnumerator implements Enumeration
- {
- private Enumeration allPrefixes;
- private String uri;
- private String lookahead=null;
-
- // Kluge: Since one can't do a constructor on an
- // anonymous class (as far as I know)...
- Enumeration setup(String uri, Enumeration allPrefixes)
- {
- this.uri=uri;
- this.allPrefixes=allPrefixes;
- return this;
- }
-
- public boolean hasMoreElements()
- {
- if(lookahead!=null)
- return true;
-
- while(allPrefixes.hasMoreElements())
- {
- String prefix=(String)allPrefixes.nextElement();
- if(uri.equals(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();
- }
- }
-
/**
* Return an enumeration of all prefixes declared in this context.
@@ -464,317 +406,380 @@
}
- ////////////////////////////////////////////////////////////////////
- // Internal classes.
- ////////////////////////////////////////////////////////////////////
+}
+
+////////////////////////////////////////////////////////////////////
+// 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;
+ }
+
+
/**
- * Internal class for a single Namespace context.
+ * Declare a Namespace prefix for this 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 Context {
-
-
-
- ////////////////////////////////////////////////////////////////
- // Protected state.
- ////////////////////////////////////////////////////////////////
-
- Hashtable prefixTable;
- Hashtable uriTable;
- Hashtable elementNameTable;
- Hashtable attributeNameTable;
- String defaultNS = null;
-
-
-
- ////////////////////////////////////////////////////////////////
- // Internal state.
- ////////////////////////////////////////////////////////////////
-
- private Vector declarations = null;
- private boolean tablesDirty = false;
- private Context parent = null;
- private Context child = null;
-
- /**
- * Create a new Namespace context.
- */
- Context (Context 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.
- */
- Context getChild()
- {
- return child;
- }
-
- /**
- * @returns The parent Namespace context object, or null if this
- * is the root.
- */
- Context 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 Context is reused by push-after-pop.
- *
- * @param context The parent Namespace context object.
- */
- void setParent (Context 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)
- {
+ * @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();
- }
+ 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;
- }
+ prefix = prefix.intern();
+ uri = uri.intern();
+ if ("".equals(prefix)) {
+ if ("".equals(uri)) {
+ defaultNS = null;
} else {
- prefixTable.put(prefix, uri);
- uriTable.put(uri, prefix); // may wipe out another prefix
+ defaultNS = uri;
}
- declarations.addElement(prefix);
+ } 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;
+ /**
+ * 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;
- }
+ 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;
- }
+ 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(':');
+ 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];
+ 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();
+ 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;
- }
+ 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 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);
- }
+ /**
+ * 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 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();
- }
+ /**
+ * 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 Context 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 Context 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;
- }
-
+ ////////////////////////////////////////////////////////////////
+ // 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
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]