| /* $Id$ |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| package org.apache.commons.digester; |
| |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| |
| /** |
| * <p>Default implementation of the <code>Rules</code> interface that supports |
| * the standard rule matching behavior. This class can also be used as a |
| * base class for specialized <code>Rules</code> implementations.</p> |
| * |
| * <p>The matching policies implemented by this class support two different |
| * types of pattern matching rules:</p> |
| * <ul> |
| * <li><em>Exact Match</em> - A pattern "a/b/c" exactly matches a |
| * <code><c></code> element, nested inside a <code><b></code> |
| * element, which is nested inside an <code><a></code> element.</li> |
| * <li><em>Tail Match</em> - A pattern "*/a/b" matches a |
| * <code><b></code> element, nested inside an <code><a></code> |
| * element, no matter how deeply the pair is nested.</li> |
| * </ul> |
| * |
| * <p>Note that wildcard patterns are ignored if an explicit match can be found |
| * (and when multiple wildcard patterns match, only the longest, ie most |
| * explicit, pattern is considered a match).</p> |
| * |
| * <p>See the package documentation for package org.apache.commons.digester |
| * for more information.</p> |
| */ |
| |
| public class RulesBase implements Rules { |
| |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| |
| /** |
| * The set of registered Rule instances, keyed by the matching pattern. |
| * Each value is a List containing the Rules for that pattern, in the |
| * order that they were orginally registered. |
| */ |
| protected HashMap<String, List<Rule>> cache = new HashMap<String, List<Rule>>(); |
| |
| |
| /** |
| * The Digester instance with which this Rules instance is associated. |
| */ |
| protected Digester digester = null; |
| |
| |
| /** |
| * 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. |
| */ |
| protected ArrayList<Rule> rules = new ArrayList<Rule>(); |
| |
| |
| // ------------------------------------------------------------- Properties |
| |
| |
| /** |
| * Return the Digester instance with which this Rules instance is |
| * associated. |
| */ |
| public Digester getDigester() { |
| |
| return (this.digester); |
| |
| } |
| |
| |
| /** |
| * Set the Digester instance with which this Rules instance is associated. |
| * |
| * @param digester The newly associated Digester instance |
| */ |
| public void setDigester(Digester digester) { |
| |
| this.digester = digester; |
| for (Rule rule : rules) { |
| rule.setDigester(digester); |
| } |
| |
| } |
| |
| |
| /** |
| * 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 |
| |
| |
| /** |
| * Register a new Rule instance matching the specified pattern. |
| * |
| * @param pattern Nesting pattern to be matched for this Rule |
| * @param rule Rule instance to be registered |
| */ |
| public void add(String pattern, Rule rule) { |
| // to help users who accidently add '/' to the end of their patterns |
| int patternLength = pattern.length(); |
| if (patternLength>1 && pattern.endsWith("/")) { |
| pattern = pattern.substring(0, patternLength-1); |
| } |
| |
| |
| List<Rule> list = cache.get(pattern); |
| if (list == null) { |
| list = new ArrayList<Rule>(); |
| cache.put(pattern, list); |
| } |
| list.add(rule); |
| rules.add(rule); |
| if (this.digester != null) { |
| rule.setDigester(this.digester); |
| } |
| if (this.namespaceURI != null) { |
| rule.setNamespaceURI(this.namespaceURI); |
| } |
| |
| } |
| |
| |
| /** |
| * Clear all existing Rule instance registrations. |
| */ |
| public void clear() { |
| |
| cache.clear(); |
| rules.clear(); |
| |
| } |
| |
| |
| /** |
| * 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 pattern Nesting pattern to be matched |
| * |
| * @deprecated Call match(namespaceURI,pattern) instead. |
| */ |
| @Deprecated |
| public List<Rule> 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<Rule> match(String namespaceURI, String pattern) { |
| |
| // List rulesList = (List) this.cache.get(pattern); |
| List<Rule> rulesList = lookup(namespaceURI, pattern); |
| if ((rulesList == null) || (rulesList.size() < 1)) { |
| // Find the longest key, ie more discriminant |
| String longKey = ""; |
| for (String key : cache.keySet()) { |
| if (key.startsWith("*/")) { |
| if (pattern.equals(key.substring(2)) || |
| pattern.endsWith(key.substring(1))) { |
| if (key.length() > longKey.length()) { |
| // rulesList = (List) this.cache.get(key); |
| rulesList = lookup(namespaceURI, key); |
| longKey = key; |
| } |
| } |
| } |
| } |
| } |
| if (rulesList == null) { |
| rulesList = new ArrayList<Rule>(); |
| } |
| return (rulesList); |
| |
| } |
| |
| |
| /** |
| * Return a List of all registered Rule instances, or a zero-length List |
| * if there are no registered Rule instances. If more than one Rule |
| * instance has been registered, they <strong>must</strong> be returned |
| * in the order originally registered through the <code>add()</code> |
| * method. |
| */ |
| public List<Rule> rules() { |
| |
| return (this.rules); |
| |
| } |
| |
| |
| // ------------------------------------------------------ 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<Rule> lookup(String namespaceURI, String pattern) { |
| |
| // Optimize when no namespace URI is specified |
| List<Rule> 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<Rule> results = new ArrayList<Rule>(); |
| for (Rule item : list) { |
| if ((namespaceURI.equals(item.getNamespaceURI())) || |
| (item.getNamespaceURI() == null)) { |
| results.add(item); |
| } |
| } |
| return (results); |
| |
| } |
| |
| |
| } |