You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by do...@apache.org on 2001/02/26 01:39:43 UTC

cvs commit: jakarta-avalon/proposal/4.0/src/java/org/apache/aut/i18n ResourceGroup.java XMLResourceBundle.java XMLResourceBundleFactory.java XPathAPI.java

donaldp     01/02/25 16:39:43

  Added:       proposal/4.0/src/java/org/apache/aut/i18n ResourceGroup.java
                        XMLResourceBundle.java
                        XMLResourceBundleFactory.java XPathAPI.java
  Log:
  Added i18n util to aut
  
  Revision  Changes    Path
  1.1                  jakarta-avalon/proposal/4.0/src/java/org/apache/aut/i18n/ResourceGroup.java
  
  Index: ResourceGroup.java
  ===================================================================
  /* 
   * Copyright (C) The Apache Software Foundation. All rights reserved. 
   * 
   * This software is published under the terms of the Apache Software License 
   * version 1.1, a copy of which has been included with this distribution in 
   * the LICENSE file. 
   */ 
  package org.apache.aut.i18n;
  
  import java.text.MessageFormat;
  import java.util.HashMap;
  import java.util.Locale;
  import java.util.MissingResourceException;
  import java.util.Random;
  import java.util.ResourceBundle;
  
  /**
   * A class used to manage resource bundles.
   */
  public class ResourceGroup
  {
      protected final static Random  RANDOM        = new Random();
      protected final HashMap        m_bundles     = new HashMap();
      protected final Locale         m_locale;
  
      /**
       * Create a ResourceGroup to manage resource bundles for a particular locale.
       *
       * @param locale the locale
       */
      public ResourceGroup( final Locale locale ) 
      {
          m_locale = locale;
      }
  
      public Locale getLocale()
      {
          return m_locale;
      }
  
      public String format( final String base, final String key, final Object[] args ) 
      {
          final String pattern = getPattern( base, key );
          final MessageFormat messageFormat = new MessageFormat( pattern );
          messageFormat.setLocale( m_locale );
          return messageFormat.format( args );
      }
  
      public ResourceBundle getBundle( final String base ) 
          throws MissingResourceException
      {
          ResourceBundle result = (ResourceBundle) m_bundles.get( base );
          if( null != result ) return result;
          
          // bundle wasn't cached, so load it, cache it, and return it.
          final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
          result = ResourceBundle.getBundle( base, m_locale, classLoader );
  
          m_bundles.put( base, result );
  
          return result;                
      }
  
      public String getPattern( final String base, final String key ) 
          throws MissingResourceException
      {
          final ResourceBundle bundle = getBundle( base );
          final Object object = bundle.getObject( key );
          
          // is the resource a single string
          if( object instanceof String ) 
          {
              return (String)object;
          }
          else if( object instanceof String[] )
          {
              //if string array then randomly pick one
              final String[] strings = (String[])object;
              return strings[ RANDOM.nextInt( strings.length ) ];
          }
          else
          {
              throw new MissingResourceException( "Unable to find resource of appropriate type.",
                                                  "java.lang.String",
                                                  key );
          }
      }
  }
  
  
  
  1.1                  jakarta-avalon/proposal/4.0/src/java/org/apache/aut/i18n/XMLResourceBundle.java
  
  Index: XMLResourceBundle.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.aut.i18n;
  
  import java.io.IOException;
  import java.lang.ref.SoftReference;
  import java.util.Hashtable;
  import java.util.Locale;
  import java.util.MissingResourceException;
  import org.apache.xalan.xpath.XObject;
  import org.apache.xalan.xpath.XPath;
  import org.apache.xalan.xpath.XPathProcessorImpl;
  import org.apache.xalan.xpath.XPathSupport;
  import org.apache.xalan.xpath.xml.PrefixResolverDefault;
  import org.apache.xalan.xpath.xml.XMLParserLiaisonDefault;
  import org.apache.xerces.dom.DocumentImpl;
  import org.apache.xerces.dom.TextImpl;
  import org.apache.xerces.parsers.DOMParser;
  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.xml.sax.SAXException;
  
  /**
   * @author <a href="mailto:mengelhart@earthtrip.com">Mike Engelhart</a>
   * @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
   * @author <a href="mailto:oleg@one.lv">Oleg Podolsky</a>
   * @version $Id: XMLResourceBundle.java,v 1.1 2001/02/26 00:39:43 donaldp Exp $
   */
  public class XMLResourceBundle {
      // Cache for storing string values for existing XPaths
      private Hashtable cacheIS = new Hashtable();
      // Cache for storing non-existing XPaths
      private Hashtable cacheNO = new Hashtable();
  
      private Document resource;
      public String bundleName = ""; //used by getLocale()
      protected XMLResourceBundle parent = null;
  
  
      public XMLResourceBundle( Document doc, String name, XMLResourceBundle p ) {
          System.out.print( "Constructing XMLResourceBundle: " + name );
          if ( p != null )
              System.out.println( "  --> parent: " + p.bundleName );
          else
              System.out.println( "  --> parent: " + p );
  
          this.resource = doc;
          this.bundleName = name;
          this.parent = p;
      }
  
      public void addToCache( String key, String value ) {
          cacheIS.put( key, value );
      }
  
      public Document getResource() {
          return this.resource;
      }
  
      // gets string without throwing an exception, returns empty string instead
      public String getStringSimple( String xPathKey ) {
          String result = "";
          try {
              result = getString( xPathKey );
          } catch ( MissingResourceException e ) {
              // do nothing
          }
  
          return result;
      }
  
      public String getString( String xPathKey ) throws MissingResourceException {
          if ( cacheIS.containsKey( xPathKey ) )
              return ( String ) cacheIS.get( xPathKey );
          if ( cacheNO.containsKey( xPathKey ) )
              new MissingResourceException( "Unable to locate resource: " + xPathKey, "XMLResourceBundle", xPathKey );
  
          Node root = this.resource.getDocumentElement();
          try {
              Node node = XPathAPI.selectSingleNode( root, xPathKey );
              if ( node != null ) {
                  String temp = getTextNodeAsString( node );
                  addToCache( xPathKey, temp );
                  return temp;
              } else {
                  if ( this.parent != null )
                      return this.parent.getString( xPathKey );
                  else
                      throw new Exception();
              }
          } catch ( Exception e ) {
              // no nodes returned??
              cacheNO.put( xPathKey, "" );
              throw new MissingResourceException( "Unable to locate resource: " + xPathKey, "XMLResourceBundle", xPathKey );
          }
      }
  
      public String getString( Node role, String key ) throws MissingResourceException {
          try {
              Node node = XPathAPI.selectSingleNode( role, key );
              if ( node != null )
                  return getTextNodeAsString( node );
              else
                  throw new Exception();
          } catch ( Exception e ) {
              // no nodes returned??
              throw new MissingResourceException( "Unable to locate resource: " + key, "XMLResourceBundle", key );
          }
      }
  
      private String getTextNodeAsString( Node node ) throws MissingResourceException {
          node = node.getFirstChild();
          if ( node.getNodeType() == Node.TEXT_NODE )
              return ( ( TextImpl ) node ).getData();
          else
              throw new MissingResourceException( "Unable to locate XMLResourceBundle", "XMLResourceBundleFactory", "" );
      }
  
      public Node getRole( String xPath ) {
          Node root = resource.getDocumentElement();
          try {
              Node node = XPathAPI.selectSingleNode( root, xPath );
              if ( node != null )
                  return node;
              else
                  throw new Exception();
          } catch ( Exception e ) {
              // no nodes returned??
              throw new MissingResourceException( "Unable to locate resource: " + xPath, "XMLResourceBundle", xPath );
          }
      }
  
      public Node getRole( Node role, String xPath ) {
          try {
              Node node = XPathAPI.selectSingleNode( role, xPath );
              if ( node != null )
                  return node;
              else
                  throw new Exception();
          } catch ( Exception e ) {
              // no nodes returned??
              throw new MissingResourceException( "Unable to locate resource: " + xPath, "XMLResourceBundle", xPath );
          }
      }
  
      public XPath createXPath( String str, Node namespaceNode ) throws SAXException {
          XPathSupport xpathSupport = new XMLParserLiaisonDefault();
  
          if ( null == namespaceNode )
              throw new SAXException( "A namespace node is required to resolve prefixes!" );
  
          PrefixResolverDefault prefixResolver = new PrefixResolverDefault( ( namespaceNode.getNodeType() == Node.DOCUMENT_NODE ) ? ( ( Document ) namespaceNode ).getDocumentElement() : namespaceNode );
  
          // Create the XPath object.
          XPath xpath = new XPath();
  
          // Create a XPath parser.
          XPathProcessorImpl parser = new XPathProcessorImpl( xpathSupport );
          parser.initXPath( xpath, str, prefixResolver );
  
          return xpath;
      }
  
      public Locale getLocale() {
          String bundle = bundleName.substring( 0, bundleName.indexOf( ".xml" ) );
          int localeStart = bundle.indexOf( "_" );
          if ( localeStart == -1 )
              return new Locale( "", "", "" );
          bundle = bundle.substring( localeStart + 1 );
          localeStart = bundle.indexOf( "_" );
          if ( localeStart == -1 )
              return new Locale( bundle, "", "" );
  
          String lang = bundle.substring( 0, localeStart );
          bundle = bundle.substring( localeStart + 1 );
          localeStart = bundle.indexOf( "_" );
          if ( localeStart == -1 )
              return new Locale( lang, bundle, "" );
  
          String country = bundle.substring( 0, localeStart );
          bundle = bundle.substring( localeStart + 1 );
          return new Locale( lang, country, bundle );
      }
  }
  
  
  
  
  1.1                  jakarta-avalon/proposal/4.0/src/java/org/apache/aut/i18n/XMLResourceBundleFactory.java
  
  Index: XMLResourceBundleFactory.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included  with this distribution in
   * the LICENSE file.
   */
  package org.apache.aut.i18n;
  
  import java.io.IOException;
  import java.util.Hashtable;
  import java.util.Locale;
  import java.util.MissingResourceException;
  import java.util.Vector;
  import org.apache.xerces.dom.DocumentImpl;
  import org.apache.xerces.dom.TextImpl;
  import org.apache.xerces.parsers.DOMParser;
  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.xml.sax.SAXException;
  
  /**
   * @author <a href="mailto:mengelhart@earthtrip.com">Mike Engelhart</a>
   * @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
   * @author <a href="mailto:oleg@one.lv">Oleg Podolsky</a>
   * @version $Id: XMLResourceBundleFactory.java,v 1.1 2001/02/26 00:39:43 donaldp Exp $
    */
  public class XMLResourceBundleFactory {
      protected static Hashtable cache = new Hashtable();
      protected static String directory;
  
      protected XMLResourceBundleFactory() {}
  
      public static XMLResourceBundle getBundle( String name ) throws MissingResourceException {
          return getBundle( name, Locale.getDefault() );
      }
  
      public static XMLResourceBundle getBundle( String name, Locale loc ) throws MissingResourceException {
          return getBundle( name, loc, false );
      }
  
      public static XMLResourceBundle getBundle( String name, Locale loc, boolean cacheAtStartup ) throws MissingResourceException {
          XMLResourceBundle parent = null;
          String bundleName = getBundleName( name, loc );
          // first look in the cache - if there grab it
          XMLResourceBundle bundle = getCachedBundle( bundleName );
          if ( bundle != null )
              return bundle;
  
          // if bundle is not in cache try loading the bundle using the given name and locale bundleName
          Document doc = null;
          doc = loadResourceBundle( bundleName );
          if ( doc != null ) {
              if ( ! loc.getLanguage().equals( "" ) )
                  parent = getParentBundle( name, loc, cacheAtStartup );
              bundle = new XMLResourceBundle( doc, bundleName, parent );
              if ( cacheAtStartup )
                  storeTextElements( bundle, bundle.getResource(), "" );
              updateCache( bundleName, bundle );
              return bundle;
          }
          // if the locale's language is "" then we've already tried to load the default resource and it's not available
          while ( ! loc.getLanguage().equals( "" ) ) {
              // if the given bundle name is not found, then try loading using a shortened Locale
              loc = getParentLocale( loc );
              bundleName = getBundleName( name, loc );
              // first look in the cache - if there grab it and return
              bundle = getCachedBundle( bundleName );
              if ( bundle != null )
                  return bundle;
  
              // try loading the bundle using the given name and locale bundleName
              doc = loadResourceBundle( bundleName );
              if ( doc != null ) {
                  if ( ! loc.getLanguage().equals( "" ) )
                      parent = getParentBundle( name, loc, cacheAtStartup );
                  bundle = new XMLResourceBundle( doc, bundleName, parent );
                  if ( cacheAtStartup )
                      storeTextElements( bundle, bundle.getResource(), "" );
                  updateCache( bundleName, bundle );
                  return bundle;
              }
          }
          throw new MissingResourceException( "Unable to locate resource: " + bundleName, "XMLResourceBundleFactory", "" );
      }
  
      protected synchronized static XMLResourceBundle getParentBundle( String name, Locale loc ) {
          return getParentBundle( name, loc, false );
      }
  
      protected synchronized static XMLResourceBundle getParentBundle( String name, Locale loc, boolean cacheAtStartup ) {
          loc = getParentLocale( loc );
          String bundleName = getBundleName( name, loc );
          Document doc = loadResourceBundle( bundleName );
          XMLResourceBundle bundle = null;
          if ( doc != null ) {
              if ( ! loc.getLanguage().equals( "" ) )
                  bundle = getParentBundle( name, loc );
              bundle = new XMLResourceBundle( doc, bundleName, bundle );
              if ( cacheAtStartup )
                  storeTextElements( bundle, bundle.getResource(), "" );
              updateCache( bundleName, bundle );
          }
          return bundle;
      }
  
      // this method returns the next locale up the parent hierarchy
      // e.g.; the parent of new Locale("en","us","mac")
      // would be new Locale("en", "us", "");
      protected static Locale getParentLocale( Locale loc ) {
          if ( loc.getVariant().equals( "" ) ) {
              if ( loc.getCountry().equals( "" ) )
                  loc = new Locale( "", "", "" );
              else
                  loc = new Locale( loc.getLanguage(), "", "" );
          } else
              loc = new Locale( loc.getLanguage(), loc.getCountry(), "" );
  
          return loc;
      }
  
      protected synchronized static XMLResourceBundle getCachedBundle( String bundleName ) {
          /*
            SoftReference ref = (SoftReference)(cache.get(bundleName));
            if (ref != null)
            return (XMLResourceBundle) ref.get();
            else
            return null;
          */ 
          return ( XMLResourceBundle ) ( cache.get( bundleName ) );
      }
  
      protected synchronized static void updateCache( String bundleName, XMLResourceBundle bundle ) {
          cache.put( bundleName, bundle );
      }
  
      /*        protected static String getBundleName(String name, Locale loc)
                {
                StringBuffer sb = new StringBuffer(name);
                if (! loc.getLanguage().equals(""))
                {
                sb.append("_");
                sb.append(loc.getLanguage());
                }
                if (! loc.getCountry().equals(""))
                {
                sb.append("_");
                sb.append(loc.getCountry());
                }
                if (! loc.getVariant().equals(""))
                {
                sb.append("_");
                sb.append(loc.getVariant());
                }
                // should all the files have an extension of .xml?  Seems reasonable
                sb.append(".xml");
       
                return sb.toString();
                }
      */
  
      protected static String getBundleName( String name, Locale loc ) {
          String lang = loc.getLanguage();
          StringBuffer sb = new StringBuffer( getDirectory() );
  
          if ( lang.length() > 0 ) sb.append( "/" ).append( lang );
          sb.append( "/" ).append( name ).append( ".xml" );
  
          return sb.toString();
      }
  
      public static XMLResourceBundle getBundle( String fileName, String localeName ) throws MissingResourceException {
          return getBundle( fileName, new Locale( localeName, localeName ) );
      }
  
      public static XMLResourceBundle getBundleFromFilename( String bundleName ) throws MissingResourceException {
          return getBundleFromFilename( bundleName, true );
      }
  
      public static XMLResourceBundle getBundleFromFilename( String bundleName, boolean cacheAtStartup ) throws MissingResourceException {
          Document doc = null;
          doc = loadResourceBundle( getDirectory() + "/" + bundleName );
  
          XMLResourceBundle bundle = getCachedBundle( bundleName );
          if ( bundle != null )
              return bundle;
  
          if ( doc != null ) {
              bundle = new XMLResourceBundle( doc, bundleName, null );
              if ( cacheAtStartup )
                  storeTextElements( bundle, bundle.getResource(), "" );
              updateCache( bundleName, bundle );
              return bundle;
          }
          throw new MissingResourceException( "Unable to locate resource: " + bundleName, "XMLResourceBundleFactory", "" );
      }
  
      // Load the XML document based on bundleName
      protected static Document loadResourceBundle( String bundleName ) {
          try {
              DOMParser parser = new DOMParser();
              parser.parse( bundleName );
              return parser.getDocument();
          } catch ( IOException e ) {
              return null;
          }
          catch ( SAXException e ) {
              return null;
          }
      }
  
      public static void setDirectory( String dir ) {
          directory = dir;
      }
  
      public static String getDirectory() {
          return ( directory != null ? directory : "" );
      }
  
      // Steps through the bundle tree and stores all text element values
      // in bundle's cache, and also stores attributes for all element nodes.
      // Parent must be am element-type node.
      private static void storeTextElements( XMLResourceBundle bundle, Node parent, String pathToParent ) {
          NodeList children = parent.getChildNodes();
          int childnum = children.getLength();
  
          for ( int i = 0; i < childnum; i++ ) {
              Node child = children.item( i );
  
              if ( child.getNodeType() == Node.ELEMENT_NODE ) {
                  String pathToChild = pathToParent + '/' + child.getNodeName();
  
                  NamedNodeMap attrs = child.getAttributes();
                  if ( attrs != null ) {
                      Node temp = null;
                      String pathToAttr = null;
                      int attrnum = attrs.getLength();
                      for ( int j = 0; j < attrnum; j++ ) {
                          temp = attrs.item( j );
                          pathToAttr = "/@" + temp.getNodeName();
                          bundle.addToCache( pathToChild + pathToAttr, temp.getNodeValue() );
                      }
                  }
  
                  String childValue = getTextValue( child );
                  if ( childValue != null )
                      bundle.addToCache( pathToChild, childValue );
                  else
                      storeTextElements( bundle, child, pathToChild );
              }
          }
      }
  
      private static String getTextValue( Node element ) {
          NodeList list = element.getChildNodes();
          int listsize = list.getLength();
  
          Node item = null;
          String itemValue = null;
  
          for ( int i = 0; i < listsize; i++ ) {
              item = list.item( i );
              if ( item.getNodeType() != Node.TEXT_NODE ) return null;
              itemValue = item.getNodeValue(); if ( itemValue == null ) return null;
              itemValue = itemValue.trim(); if ( itemValue.length() == 0 ) return null;
              return itemValue;
          }
          return null;
      }
  }
  
  
  
  1.1                  jakarta-avalon/proposal/4.0/src/java/org/apache/aut/i18n/XPathAPI.java
  
  Index: XPathAPI.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included  with this distribution in
   * the LICENSE file.
   */
  package org.apache.aut.i18n;
  
  import org.apache.xalan.xpath.XObject;
  import org.apache.xalan.xpath.XPath;
  import org.apache.xalan.xpath.XPathProcessorImpl;
  import org.apache.xalan.xpath.XPathSupport;
  import org.apache.xalan.xpath.xml.PrefixResolverDefault;
  import org.apache.xalan.xpath.xml.XMLParserLiaisonDefault;
  import org.w3c.dom.Document;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  import org.xml.sax.SAXException;
  
  /**
   * The methods in this class are convenience methods into the
   * low-level XPath API.  We would like to eventually move these
   * methods into the XPath core, but would like to do some peer
   * review first to make sure we have it right.
   * Please note that these methods execute pure XPaths. They do not
   * implement those parts of XPath extended by XSLT, such as the
   * document() function).  If you want to install XSLT functions, you
   * have to use the low-level 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.
   *
   * @author <a href="mailto:mengelhart@earthtrip.com">Mike Engelhart</a>
   * @see http://www.w3.org/TR/xpath
   * @version $Id: XPathAPI.java,v 1.1 2001/02/26 00:39:43 donaldp Exp $
   */
  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.
       */
      public static Node selectSingleNode( Node contextNode, String str )
          throws SAXException {
          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.
       */
      public static Node selectSingleNode( Node contextNode, String str, Node namespaceNode )
          throws SAXException {
          // Have the XObject return its result as a NodeSet.
          NodeList nl = selectNodeList( contextNode, str, namespaceNode );
  
          // Return the first node, or null
          return ( nl.getLength() > 0 ) ? nl.item( 0 ) : null;
      }
  
      /**
       * 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 nodelist, should never be null.
       */
      public static NodeList selectNodeList( Node contextNode, String str )
          throws SAXException {
          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 nodelist, should never be null.
       */
      public static NodeList selectNodeList( Node contextNode, String str, Node namespaceNode )
          throws SAXException {
          // Execute the XPath, and have it return the result
          XObject list = eval( contextNode, str, namespaceNode );
  
          // Have the XObject return its result as a NodeSet.
          return list.nodeset();
  
      }
  
      /**
       * 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.
       * @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.xalan.xpath.XObject
       * @see org.apache.xalan.xpath.XNull
       * @see org.apache.xalan.xpath.XBoolean
       * @see org.apache.xalan.xpath.XNumber
       * @see org.apache.xalan.xpath.XString
       * @see org.apache.xalan.xpath.XRTreeFrag
       */
      public static XObject eval( Node contextNode, String str )
          throws SAXException {
          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.xalan.xpath.XObject
       * @see org.apache.xalan.xpath.XNull
       * @see org.apache.xalan.xpath.XBoolean
       * @see org.apache.xalan.xpath.XNumber
       * @see org.apache.xalan.xpath.XString
       * @see org.apache.xalan.xpath.XRTreeFrag   */
      public static XObject eval( Node contextNode, String str, Node namespaceNode )
          throws SAXException {
          // Since we don't have a XML Parser involved here, install some default support
          // for things like namespaces, etc.
          // (Changed from: XPathSupportDefault xpathSupport = new XPathSupportDefault();
          //    because XPathSupportDefault is weak in a number of areas... perhaps
          //    XPathSupportDefault should be done away with.)
          XPathSupport xpathSupport = new XMLParserLiaisonDefault();
  
          if ( null == namespaceNode )
              namespaceNode = contextNode;
  
          // 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();
  
          // Create a XPath parser.
          XPathProcessorImpl parser = new XPathProcessorImpl( xpathSupport );
          parser.initXPath( xpath, str, prefixResolver );
  
          // Execute the XPath, and have it return the result
          return xpath.execute( xpathSupport, contextNode, prefixResolver );
      }
  }