craigmcc    01/08/25 22:09:36

  Modified:    digester/src/java/org/apache/commons/digester Digester.java
                        Rule.java Rules.java RulesBase.java
  Log:
  Add a new feature to Digester - the ability to declare that Rule instances
  match *only* elements from a corresponding namespace, without changing the
  functionality observed by applications that do not care about this.
  
  BACKGROUND:  The original implementation of Digester created matching
  patterns based on the "qName" version of element names (i.e. including the
  namespace prefix).  However, to be a "good XML citizen", it should be
  possible to create rules that match based on the corresponding namespace
  URI plus the "localname" part of the element name.  In that way, XML
  document authors are free to use any prefix they like, as long as they
  declare their namespaces in the usual fashion.
  
  To declare that Rule instances apply *only* to a particular namespace URI,
  call digester.setRuleNamespaceURI() before calling the corresponding
  addRule() method.  If you call setRuleNamespaceURI(null), rules will match
  regardless of namespace URI -- in other words, the current behavior.
  
  WARNING 1:  I had to deprecate Rules.match(String) because it is
  insufficient to handle this case.  Any existing implementation of Rules
  should be modified to imlement Rules.match(String,String) instead.
  
  WARNING 2:  I haven't yet added unit tests to verify that this stuff works
  correctly, but will be doing that soon (plus using this feature in my
  "workflow" project).
  
  Revision  Changes    Path
  1.15      +163 -38   
jakarta-commons/digester/src/java/org/apache/commons/digester/Digester.java
  
  Index: Digester.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Digester.java,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- Digester.java     2001/08/20 19:18:42     1.14
  +++ Digester.java     2001/08/26 05:09:36     1.15
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Digester.java,v
 1.14 2001/08/20 19:18:42 craigmcc Exp $
  - * $Revision: 1.14 $
  - * $Date: 2001/08/20 19:18:42 $
  + * $Header: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Digester.java,v
 1.15 2001/08/26 05:09:36 craigmcc Exp $
  + * $Revision: 1.15 $
  + * $Date: 2001/08/26 05:09:36 $
    *
    * ====================================================================
    *
  @@ -106,7 +106,7 @@
    *
    * @author Craig McClanahan
    * @author Scott Sanders
  - * @version $Revision: 1.14 $ $Date: 2001/08/20 19:18:42 $
  + * @version $Revision: 1.15 $ $Date: 2001/08/26 05:09:36 $
    */
   
   public class Digester extends DefaultHandler {
  @@ -209,6 +209,17 @@
   
   
       /**
  +     * Registered namespaces we are currently processing.  The key is the
  +     * namespace prefix that was declared in the document.  The value is an
  +     * ArrayStack of the namespace URIs this prefix has been mapped to --
  +     * the top Stack element is the most current one.  (This architecture
  +     * is required because documents can declare nested uses of the same
  +     * prefix for different Namespace URIs).
  +     */
  +    protected HashMap namespaces = new HashMap();
  +
  +
  +    /**
        * The parameters stack being utilized by CallMethodRule and
        * CallParamRule rules.
        */
  @@ -267,6 +278,27 @@
   
   
       /**
  +     * Return the currently mapped namespace URI for the specified prefix,
  +     * if any; otherwise return <code>null</code>.  These mappings come and
  +     * go dynamically as the document is parsed.
  +     *
  +     * @param prefix Prefix to look up
  +     */
  +    public String findNamespaceURI(String prefix) {
  +
  +        ArrayStack stack = (ArrayStack) namespaces.get(prefix);
  +        if (stack == null)
  +            return (null);
  +        try {
  +            return ((String) stack.peek());
  +        } catch (EmptyStackException e) {
  +            return (null);
  +        }
  +
  +    }
  +
  +
  +    /**
        * Return the class loader to be used for instantiating application objects
        * when required.  This is determined based upon the following rules:
        * <ul>
  @@ -382,6 +414,32 @@
   
   
       /**
  +     * Return the namespace URI that will be applied to all subsequently
  +     * added <code>Rule</code> objects.
  +     */
  +    public String getRuleNamespaceURI() {
  +
  +        return (getRules().getNamespaceURI());
  +
  +    }
  +
  +
  +    /**
  +     * Set the namespace URI that will be applied to all subsequently
  +     * added <code>Rule</code> objects.
  +     *
  +     * @param ruleNamespaceURI Namespace URI that must match on all
  +     *  subsequently added rules, or <code>null</code> for matching
  +     *  regardless of the current namespace URI
  +     */
  +    public void setRuleNamespaceURI(String ruleNamespaceURI) {
  +
  +        getRules().setNamespaceURI(ruleNamespaceURI);
  +
  +    }
  +
  +
  +    /**
        * Return the SAXParser we will use to parse the input stream.  If there
        * is a problem creating the parser, return <code>null</code>.
        */
  @@ -511,7 +569,7 @@
        }
   
   
  -    // ---------------------------------------------- DocumentHandler Methods
  +    // ------------------------------------------------- ContentHandler Methods
   
   
       /**
  @@ -527,8 +585,8 @@
       public void characters(char buffer[], int start, int length)
         throws SAXException {
   
  -     //      if (debug >= 3)
  -     //          log("characters(" + new String(buffer, start, length) + ")");
  +        if (debug >= 3)
  +            log("characters(" + new String(buffer, start, length) + ")");
   
        bodyText.append(buffer, start, length);
   
  @@ -542,8 +600,8 @@
        */
       public void endDocument() throws SAXException {
   
  -     //      if (debug >= 3)
  -     //          log("endDocument()");
  +     if (debug >= 3)
  +            log("endDocument()");
   
        if (getCount() > 1)
            log("endDocument():  " + getCount() + " elements left");
  @@ -583,11 +641,12 @@
       public void endElement(String namespaceURI, String localName,
                              String qName) throws SAXException {
   
  -     if (debug >= 3)
  -         log("endElement(" + match + ")");
  -     List rules = getRules().match(match);
  +        if (debug >= 3)
  +            log("endElement(" + namespaceURI + "," + localName +
  +                "," + qName + ")");
   
        // Fire "body" events for all relevant rules
  +     List rules = getRules().match(namespaceURI, match);
        if (rules != null) {
            String bodyText = this.bodyText.toString().trim();
            for (int i = 0; i < rules.size(); i++) {
  @@ -633,6 +692,33 @@
   
   
       /**
  +     * Process notification that a namespace prefix is going out of scope.
  +     *
  +     * @param prefix Prefix that is going out of scope
  +     *
  +     * @exception SAXException if a parsing error is to be reported
  +     */
  +    public void endPrefixMapping(String prefix) throws SAXException {
  +
  +        if (debug >= 3)
  +            log("endPrefixMapping(" + prefix + ")");
  +
  +        // Deregister this prefix mapping
  +        ArrayStack stack = (ArrayStack) namespaces.get(prefix);
  +        if (stack == null)
  +            return;
  +        try {
  +            stack.pop();
  +            if (stack.empty())
  +                namespaces.remove(prefix);
  +        } catch (EmptyStackException e) {
  +            throw new SAXException("endPrefixMapping popped too many times");
  +        }
  +
  +    }
  +
  +
  +    /**
        * Process notification of ignorable whitespace received from the body of
        * an XML element.
        *
  @@ -645,9 +731,9 @@
       public void ignorableWhitespace(char buffer[], int start, int len)
         throws SAXException {
   
  -     //      if (debug >= 3)
  -     //          log("ignorableWhitespace(" +
  -     //              new String(buffer, start, len) + ")");
  +        if (debug >= 3)
  +            log("ignorableWhitespace(" +
  +                     new String(buffer, start, len) + ")");
   
        ;       // No processing required
   
  @@ -665,8 +751,8 @@
       public void processingInstruction(String target, String data)
         throws SAXException {
   
  -     //      if (debug >= 3)
  -     //          log("processingInstruction('" + target + "', '" + data + "')");
  +        if (debug >= 3)
  +         log("processingInstruction('" + target + "','" + data + "')");
   
        ;       // No processing is required
   
  @@ -680,8 +766,8 @@
        */
       public void setDocumentLocator(Locator locator) {
   
  -     //      if (debug >= 3)
  -     //          log("setDocumentLocator()");
  +        if (debug >= 3)
  +         log("setDocumentLocator(" + locator + ")");
   
        this.locator = locator;
   
  @@ -689,15 +775,34 @@
   
   
       /**
  +     * Process notification of a skipped entity.
  +     *
  +     * @param name Name of the skipped entity
  +     *
  +     * @exception SAXException if a parsing error is to be reported
  +     */
  +    public void skippedEntity(String name) {
  +
  +        if (debug >= 3)
  +            log("skippedEntity(" + name + ")");
  +
  +        ; // No processing required
  +
  +    }
  +
  +
  +    /**
        * Process notification of the beginning of the document being reached.
        *
        * @exception SAXException if a parsing error is to be reported
        */
       public void startDocument() throws SAXException {
   
  -     //      if (debug >= 3)
  -     //          log("startDocument()");
  +        if (debug >= 3)
  +            log("startDocument()");
   
  +        ; // No processing required
  +
       }
   
   
  @@ -718,20 +823,15 @@
                                String qName, Attributes list)
           throws SAXException {
   
  +        if (debug >= 3)
  +            log("startElement(" + namespaceURI + "," + localName + "," +
  +                qName + ")");
  +
        // Save the body text accumulated for our surrounding element
        bodyTexts.push(bodyText);
        bodyText.setLength(0);
   
        // Compute the current matching rule
  -        /*
  -        if (!namespaceAware &&
  -            ((localName == null) || (localName.length() < 1)))
  -            localName = qName;
  -     if (match.length() > 0)
  -         match += "/" + localName;
  -     else
  -         match = localName;
  -        */
           StringBuffer sb = new StringBuffer(match);
           if (match.length() > 0)
               sb.append('/');
  @@ -746,7 +846,7 @@
            log("startElement(" + match + ")");
   
        // Fire "begin" events for all relevant rules
  -     List rules = getRules().match(match);
  +     List rules = getRules().match(namespaceURI, match);
        if (rules != null) {
            String bodyText = this.bodyText.toString();
            for (int i = 0; i < rules.size(); i++) {
  @@ -764,8 +864,33 @@
   
       }
   
  +
  +    /**
  +     * Process notification that a namespace prefix is coming in to scope.
  +     *
  +     * @param prefix Prefix that is being declared
  +     * @param namespaceURI Corresponding namespace URI being mapped to
  +     *
  +     * @exception SAXException if a parsing error is to be reported
  +     */
  +    public void startPrefixMapping(String prefix, String namespaceURI)
  +        throws SAXException {
  +
  +        if (debug >= 3)
  +            log("startPrefixMapping(" + prefix + "," + namespaceURI + ")");
  +
  +        // Register this prefix mapping
  +        ArrayStack stack = (ArrayStack) namespaces.get(prefix);
  +        if (stack == null) {
  +            stack = new ArrayStack();
  +            namespaces.put(prefix, stack);
  +        }
  +        stack.push(namespaceURI);
  +
  +    }
  +
   
  -    // --------------------------------------------------- DTDHandler Methods
  +    // ----------------------------------------------------- DTDHandler Methods
   
   
       /**
  @@ -777,9 +902,9 @@
        */
       public void notationDecl(String name, String publicId, String systemId) {
   
  -     if (debug >= 1)
  -         log("notationDecl('" + name + "', '" + publicId + "', '" +
  -             systemId + "')");
  +     if (debug >= 3)
  +         log("notationDecl(" + name + "," + publicId + "," +
  +             systemId + ")");
   
       }
   
  @@ -795,9 +920,9 @@
       public void unparsedEntityDecl(String name, String publicId,
                                   String systemId, String notation) {
   
  -     if (debug >= 1)
  -         log("unparsedEntityDecl('" + name + "', '" + publicId + "', '" +
  -             systemId + "', '" + notation + "')");
  +     if (debug >= 3)
  +         log("unparsedEntityDecl(" + name + "," + publicId + "," +
  +             systemId + "," + notation + ")");
   
       }
   
  
  
  
  1.3       +46 -4     
jakarta-commons/digester/src/java/org/apache/commons/digester/Rule.java
  
  Index: Rule.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Rule.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Rule.java 2001/05/12 17:25:54     1.2
  +++ Rule.java 2001/08/26 05:09:36     1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Rule.java,v 
1.2 2001/05/12 17:25:54 sanders Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/05/12 17:25:54 $
  + * $Header: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Rule.java,v 
1.3 2001/08/26 05:09:36 craigmcc Exp $
  + * $Revision: 1.3 $
  + * $Date: 2001/08/26 05:09:36 $
    *
    * ====================================================================
    *
  @@ -71,7 +71,7 @@
    * a corresponding nested pattern of XML elements has been matched.
    *
    * @author Craig McClanahan
  - * @version $Revision: 1.2 $ $Date: 2001/05/12 17:25:54 $
  + * @version $Revision: 1.3 $ $Date: 2001/08/26 05:09:36 $
    */
   
   public abstract class Rule {
  @@ -100,6 +100,48 @@
        * The Digester with which this Rule is associated.
        */
       protected Digester digester = null;
  +
  +
  +    /**
  +     * The namespace URI for which this Rule is relevant, if any.
  +     */
  +    protected String namespaceURI = null;
  +
  +
  +    // ------------------------------------------------------------- Properties
  +
  +
  +    /**
  +     * Return the Digester with which this Rule is associated.
  +     */
  +    public Digester getDigester() {
  +
  +        return (this.digester);
  +
  +    }
  +
  +
  +    /**
  +     * Return the namespace URI for which this Rule is relevant, if any.
  +     */
  +    public String getNamespaceURI() {
  +
  +        return (this.namespaceURI);
  +
  +    }
  +
  +
  +    /**
  +     * Set the namespace URI for which this Rule is relevant, if any.
  +     *
  +     * @param namespaceURI Namespace URI for which this Rule is relevant,
  +     *  or <code>null</code> to match independent of namespace.
  +     */
  +    public void setNamespaceURI(String namespaceURI) {
  +
  +        this.namespaceURI = null;
  +
  +    }
   
   
       // --------------------------------------------------------- Public Methods
  
  
  
  1.2       +39 -4     
jakarta-commons/digester/src/java/org/apache/commons/digester/Rules.java
  
  Index: Rules.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Rules.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Rules.java        2001/08/04 23:14:57     1.1
  +++ Rules.java        2001/08/26 05:09:36     1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Rules.java,v 
1.1 2001/08/04 23:14:57 craigmcc Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/04 23:14:57 $
  + * $Header: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/Rules.java,v 
1.2 2001/08/26 05:09:36 craigmcc Exp $
  + * $Revision: 1.2 $
  + * $Date: 2001/08/26 05:09:36 $
    *
    * ====================================================================
    *
  @@ -73,7 +73,7 @@
    * during parsing.
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.1 $ $Date: 2001/08/04 23:14:57 $
  + * @version $Revision: 1.2 $ $Date: 2001/08/26 05:09:36 $
    */
   
   public interface Rules {
  @@ -97,6 +97,24 @@
       public void setDigester(Digester digester);
   
   
  +    /**
  +     * Return the namespace URI that will be applied to all subsequently
  +     * added <code>Rule</code> objects.
  +     */
  +    public String getNamespaceURI();
  +
  +
  +    /**
  +     * Set the namespace URI that will be applied to all subsequently
  +     * added <code>Rule</code> objects.
  +     *
  +     * @param namespaceURI Namespace URI that must match on all
  +     *  subsequently added rules, or <code>null</code> for matching
  +     *  regardless of the current namespace URI
  +     */
  +    public void setNamespaceURI(String namespaceURI);
  +
  +
       // --------------------------------------------------------- Public Methods
   
   
  @@ -123,8 +141,25 @@
        * method.
        *
        * @param pattern Nesting pattern to be matched
  +     *
  +     * @deprecated Call match(namespaceURI,pattern) instead.
        */
       public List match(String pattern);
  +
  +
  +    /**
  +     * Return a List of all registered Rule instances that match the specified
  +     * nesting pattern, or a zero-length List if there are no matches.  If more
  +     * than one Rule instance matches, they <strong>must</strong> be returned
  +     * in the order originally registered through the <code>add()</code>
  +     * method.
  +     *
  +     * @param namespaceURI Namespace URI for which to select matching rules,
  +     *  or <code>null</code> to match regardless of namespace URI
  +     * @param pattern Nesting pattern to be matched
  +     */
  +    public List match(String namespaceURI, String pattern);
  +
   
   
       /**
  
  
  
  1.2       +96 -6     
jakarta-commons/digester/src/java/org/apache/commons/digester/RulesBase.java
  
  Index: RulesBase.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/RulesBase.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RulesBase.java    2001/08/04 23:14:57     1.1
  +++ RulesBase.java    2001/08/26 05:09:36     1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/RulesBase.java,v
 1.1 2001/08/04 23:14:57 craigmcc Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/04 23:14:57 $
  + * $Header: 
/home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/RulesBase.java,v
 1.2 2001/08/26 05:09:36 craigmcc Exp $
  + * $Revision: 1.2 $
  + * $Date: 2001/08/26 05:09:36 $
    *
    * ====================================================================
    *
  @@ -86,7 +86,7 @@
    * </ul>
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.1 $ $Date: 2001/08/04 23:14:57 $
  + * @version $Revision: 1.2 $ $Date: 2001/08/26 05:09:36 $
    */
   
   public class RulesBase implements Rules {
  @@ -110,6 +110,14 @@
   
   
       /**
  +     * The namespace URI for which subsequently added <code>Rule</code>
  +     * objects are relevant, or <code>null</code> for matching independent
  +     * of namespaces.
  +     */
  +    protected String namespaceURI = null;
  +
  +
  +    /**
        * The set of registered Rule instances, in the order that they were
        * originally registered.
        */
  @@ -142,6 +150,32 @@
       }
   
   
  +    /**
  +     * Return the namespace URI that will be applied to all subsequently
  +     * added <code>Rule</code> objects.
  +     */
  +    public String getNamespaceURI() {
  +
  +        return (this.namespaceURI);
  +
  +    }
  +
  +
  +    /**
  +     * Set the namespace URI that will be applied to all subsequently
  +     * added <code>Rule</code> objects.
  +     *
  +     * @param namespaceURI Namespace URI that must match on all
  +     *  subsequently added rules, or <code>null</code> for matching
  +     *  regardless of the current namespace URI
  +     */
  +    public void setNamespaceURI(String namespaceURI) {
  +
  +        this.namespaceURI = namespaceURI;
  +
  +    }
  +
  +
       // --------------------------------------------------------- Public Methods
   
   
  @@ -159,6 +193,7 @@
               cache.put(pattern, list);
           }
           list.add(rule);
  +        rule.setNamespaceURI(namespaceURI);
           rules.add(rule);
   
       }
  @@ -183,10 +218,31 @@
        * method.
        *
        * @param pattern Nesting pattern to be matched
  +     *
  +     * @deprecated Call match(namespaceURI,pattern) instead.
        */
       public List match(String pattern) {
  +
  +        return (match(null, pattern));
  +
  +    }
  +
  +
  +    /**
  +     * Return a List of all registered Rule instances that match the specified
  +     * nesting pattern, or a zero-length List if there are no matches.  If more
  +     * than one Rule instance matches, they <strong>must</strong> be returned
  +     * in the order originally registered through the <code>add()</code>
  +     * method.
  +     *
  +     * @param namespaceURI Namespace URI for which to select matching rules,
  +     *  or <code>null</code> to match regardless of namespace URI
  +     * @param pattern Nesting pattern to be matched
  +     */
  +    public List match(String namespaceURI, String pattern) {
   
  -        List rulesList = (List) this.cache.get(pattern);
  +        // List rulesList = (List) this.cache.get(pattern);
  +        List rulesList = lookup(namespaceURI, pattern);
        if (rulesList == null) {
               // Find the longest key, ie more discriminant
               String longKey = "";
  @@ -196,7 +252,8 @@
                if (key.startsWith("*/")) {
                    if (pattern.endsWith(key.substring(1))) {
                           if (key.length() > longKey.length()) {
  -                            rulesList = (List) this.cache.get(key);
  +                            // rulesList = (List) this.cache.get(key);
  +                            rulesList = lookup(namespaceURI, key);
                               longKey = key;
                           }
                    }
  @@ -221,6 +278,39 @@
   
       }
   
  +
  +    // ------------------------------------------------------ Protected Methods
  +
  +
  +    /**
  +     * Return a List of Rule instances for the specified pattern that also
  +     * match the specified namespace URI (if any).  If there are no such
  +     * rules, return <code>null</code>.
  +     *
  +     * @param namespaceURI Namespace URI to match, or <code>null</code> to
  +     *  select matching rules regardless of namespace URI
  +     * @param pattern Pattern to be matched
  +     */
  +    protected List lookup(String namespaceURI, String pattern) {
  +
  +        // Optimize when no namespace URI is specified
  +        List list = (List) this.cache.get(pattern);
  +        if (list == null)
  +            return (null);
  +        if ((namespaceURI == null) || (namespaceURI.length() == 0))
  +            return (list);
  +
  +        // Select only Rules that match on the specified namespace URI
  +        ArrayList results = new ArrayList();
  +        Iterator items = list.iterator();
  +        while (items.hasNext()) {
  +            Rule item = (Rule) items.next();
  +            if (namespaceURI.equals(item.getNamespaceURI()))
  +                results.add(item);
  +        }
  +        return (results);
  +
  +    }
   
   
   }
  
  
  

Reply via email to