SAX2: NSUtils.java

David Megginson david at megginson.com
Wed Dec 15 14:56:31 GMT 1999


I'm attaching a copy of the Java source code for the (short) NSUtils
class that I described in the last message.  I'd be very grateful if
the Java specialists on the list could look this over, paying special
attention to synchronization problems.

-------------- next part --------------
// NSUtils.java - utilities for dealing with Namespaces
// Copyright (c) 1999 by Megginson Technologies Ltd.
// Free redistribution permitted.

// $Id: NSUtils.java,v 1.1 1999/12/15 14:44:06 david Exp $

package org.xml.sax.helpers;

import java.util.Hashtable;


/**
 * Utilities for splitting and joining Namespace-qualified names.
 *
 * This class contains only static methods, and may not be instantiated.
 * The methods in this class allow applications to split and
 * rejoin Namespace-qualified names in the format {URI}localpart.  The
 * static methods use tables to cache the result of each split or
 * join, so that applications can avoid repeating expensive Java
 * String operations.
 *
 * @see org.xml.sax.DocumentHandler
 * @see org.xml.sax.NamespaceHandler
 */
public final class NSUtils
{


    ////////////////////////////////////////////////////////////////////
    // Private constructor to avoid instantiation.
    ////////////////////////////////////////////////////////////////////

    private NSUtils ()
    {
    }



    ////////////////////////////////////////////////////////////////////
    // Static tables.
    ////////////////////////////////////////////////////////////////////

				// Table counter.  Once this hits
				// COUNTER_MAX, flush all of the tables
				// and start over.
    private static int counter = 0;
    private static final int COUNTER_MAX = 1024;

				// Singleton instance of this class.
    private static NSUtils nsUtils;

				// A reusable qname for searching.
    private static QName qName;

				// Internal hash tables for caching
				// expensive string operations.
    private static Hashtable splitNameTable;
    private static Hashtable joinNameTable;


				// Initialize the tables when the
				// class is loaded.
    static {
	nsUtils = new NSUtils();
	qName = nsUtils.makeQName(null, null);
	resetTables();
    }




    ////////////////////////////////////////////////////////////////////
    // Public static utility methods.
    ////////////////////////////////////////////////////////////////////

    
    /**
     * Test whether a name is qualified with a Namespace URI.
     *
     * The name must appear in {URI}local format.
     *
     * @param name The name to test.
     * @return true if the name has a URI part, false otherwise.
     */
    public static boolean isQualified (String name)
    {
	return (name.charAt(0) == '{');
    }


    /**
     * Split a name into its URI part and its local part.
     *
     * <p>Both the URI and local parts will be internalized.  This
     * function is extremely efficient: it uses a hash
     * table to keep track of names it has already split, and
     * won't do the same work twice.  Since the same element and
     * attribute names tend to appear over and over in an XML
     * document, this method will not usually incur the
     * overhead of Java String processing.</p>
     *
     * @param name The qualified name in {URI}local format.
     * @return An array containing the URI part as the first
     *         member (or null if there is no URI part) and the
     *         local part as the second member.
     * @see java.lang.String#intern
     */
    public static String [] splitName (String name)
    {
	String parts[] = (String [])splitNameTable.get(name);

	if (parts == null) {
	    parts = new String[2];
	    if (name.charAt(0) == '{') {
		int endPos = name.indexOf('}');
		if (endPos == -1) {
		    throw new
			RuntimeException("Malformed Namespace name: " + name);
		}
		parts[0] = (name.substring(1, endPos)).intern();
		parts[1] = (name.substring(endPos+1)).intern();
	    } else {
		parts[0] = null;
		parts[1] = name.intern();
	    }
	    incrCounter();
	    synchronized (splitNameTable) {
		splitNameTable.put(name, parts);
	    }
	}
	return parts;
    }


    /**
     * Join a URI part and a local part into a single qualified name.
     *
     * <p>The name will be merged into a single string in
     * "{URI}local" format, and the merged string will be
     * internalized.</p>
     *
     * @param uriPart The URI part of the name.
     * @param localPart The local part of the name.
     * @return The joined name in {URI}local format.
     * @see java.lang.String#intern
     */
    public static String joinName (String uriPart, String localPart)
    {
	qName.uri = uriPart;
	qName.local = localPart;
	String name = (String)joinNameTable.get(qName);

	if (name == null) {
	    name = ("{" + uriPart + '}' + localPart).intern();
	    incrCounter();
	    synchronized (joinNameTable) {
		joinNameTable.put(nsUtils.makeQName(uriPart.intern(),
						    localPart.intern()),
				  name);
	    }
	}
	return name;
    }



    ////////////////////////////////////////////////////////////////////
    // Internal methods.
    ////////////////////////////////////////////////////////////////////

    private QName makeQName (String uri, String local)
    {
	return new QName(uri, local);
    }



    ////////////////////////////////////////////////////////////////////
    // Internal static methods.
    ////////////////////////////////////////////////////////////////////

    /**
     * Reset the internal cache.
     */
    private static void resetTables ()
    {
	splitNameTable = new Hashtable();
	joinNameTable = new Hashtable();
    }


    /**
     * Increment the counter, and reset tables at COUNTER_MAX.
     */
    private static void incrCounter ()
    {
	if (++counter == COUNTER_MAX) {
	    counter = 0;
	    resetTables();
	}
    }



    /**
     * Inner class for a split, qualified name.
     *
     * This class represents a two-part, Namespace-qualified
     * name for the sake of hashing.
     */
    class QName
    {
	QName (String uri, String local)
	{
	    this.uri = uri;
	    this.local = local;
	}

	public boolean equals (Object o)
	{
	    if (o instanceof QName) {
		String uri2 = ((QName)o).uri;
		String local2 = ((QName)o).local;
		if (uri == uri2 && local == local2) {
		    return true;
		} else if (uri == null && uri2 == null) {
		    return local.equals(local2);
		} else if (uri == null) {
		    return false;
		} else {
		    return uri.equals(uri2) && local.equals(local2);
		}
	    } else {
		return false;
	    }
	}

	public int hashCode ()
	{
	    int hash = 0;
	    if (uri != null) {
		hash += uri.hashCode();
	    }
	    if (local != null) {
		hash += local.hashCode();
	    }
	    return hash;
	}

	String uri;
	String local;
    }

}

// end of NamespaceUtils.java
-------------- next part --------------


Thanks, and all the best,


David

-- 
David Megginson                 david at megginson.com
           http://www.megginson.com/


More information about the Xml-dev mailing list