Hi,

Following is a small patch to the Digester class to allow processing of
recursive XML structures.
In order to process a tree-like XML file, you sometimes need to discriminate
between a root entity and a node. For instance, with an XML input like:

<tree>
    <node>
        <node>
            <node>
            </node>
            <node>
            </node>
        </node>
        <node>
        </node>
    </node>
</tree>

The first level of node is the root of the tree, and must be unique, when
the other nodes can be multiple on the same level.
In order to differentiate between the two significances of the same tag, I
wanted to be able to write the following rules:

  digester.push(tree);

  // Rule set #1 for root
  digester.addObjectCreate("*/tree/node", "Node");
  digester.addSetProperties("*/tree/node");
  digester.addSetNext("*/tree/node", "setRoot");

  // Rule set #2 for node
  digester.addObjectCreate("*/node", "Node");
  digester.addSetProperties("*/node");
  digester.addSetNext("*/node", "addNode");

But in the current digester implementation, the getRules() method returns
the first rules set found in the HashMap, instead of taking the most
discriminant (ie. the one with the longest matching pattern), giving random
results!

Here is the patch to Digester.getRules() that returns the most discriminant
pattern macthing set of rules:

----------------------------------------------------------
    /**
     * Return the set of rules that apply to the specified match position.
     * The selected rules are those that match exactly, or those rules
     * that specify a suffix match and the tail of the rule matches the
     * current match position.  Exact matches have precedence over
     * suffix matches. Then longest suffix match is preferred.
     *
     * @param match The current match position
     */
    private List getRules(String match) {

        List rulesList = (List) this.rules.get(match);
        if (rulesList == null) {
            // Find longest key, ie more discriminant
            String longKey = "";
            Iterator keys = this.rules.keySet().iterator();
            while (keys.hasNext()) {
                String key = (String) keys.next();
                if (key.startsWith("*/")) {
                    if (match.endsWith(key.substring(1))) {
                        if (key.length() > longKey.length()) {
                            rulesList = (List) this.rules.get(key);
                            longKey = key;
                        }
                     }
                 }
             }
         }

    }

----------------------------------------------------------

Pierre Métras


Reply via email to