001: /* $Id: RulesBase.java 467222 2006-10-24 03:17:11Z markt $
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: package org.apache.tomcat.util.digester;
020:
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: /**
027: * <p>Default implementation of the <code>Rules</code> interface that supports
028: * the standard rule matching behavior. This class can also be used as a
029: * base class for specialized <code>Rules</code> implementations.</p>
030: *
031: * <p>The matching policies implemented by this class support two different
032: * types of pattern matching rules:</p>
033: * <ul>
034: * <li><em>Exact Match</em> - A pattern "a/b/c" exactly matches a
035: * <code><c></code> element, nested inside a <code><b></code>
036: * element, which is nested inside an <code><a></code> element.</li>
037: * <li><em>Tail Match</em> - A pattern "*/a/b" matches a
038: * <code><b></code> element, nested inside an <code><a></code>
039: * element, no matter how deeply the pair is nested.</li>
040: * </ul>
041: */
042:
043: public class RulesBase implements Rules {
044:
045: // ----------------------------------------------------- Instance Variables
046:
047: /**
048: * The set of registered Rule instances, keyed by the matching pattern.
049: * Each value is a List containing the Rules for that pattern, in the
050: * order that they were orginally registered.
051: */
052: protected HashMap cache = new HashMap();
053:
054: /**
055: * The Digester instance with which this Rules instance is associated.
056: */
057: protected Digester digester = null;
058:
059: /**
060: * The namespace URI for which subsequently added <code>Rule</code>
061: * objects are relevant, or <code>null</code> for matching independent
062: * of namespaces.
063: */
064: protected String namespaceURI = null;
065:
066: /**
067: * The set of registered Rule instances, in the order that they were
068: * originally registered.
069: */
070: protected ArrayList rules = new ArrayList();
071:
072: // ------------------------------------------------------------- Properties
073:
074: /**
075: * Return the Digester instance with which this Rules instance is
076: * associated.
077: */
078: public Digester getDigester() {
079:
080: return (this .digester);
081:
082: }
083:
084: /**
085: * Set the Digester instance with which this Rules instance is associated.
086: *
087: * @param digester The newly associated Digester instance
088: */
089: public void setDigester(Digester digester) {
090:
091: this .digester = digester;
092: Iterator items = rules.iterator();
093: while (items.hasNext()) {
094: Rule item = (Rule) items.next();
095: item.setDigester(digester);
096: }
097:
098: }
099:
100: /**
101: * Return the namespace URI that will be applied to all subsequently
102: * added <code>Rule</code> objects.
103: */
104: public String getNamespaceURI() {
105:
106: return (this .namespaceURI);
107:
108: }
109:
110: /**
111: * Set the namespace URI that will be applied to all subsequently
112: * added <code>Rule</code> objects.
113: *
114: * @param namespaceURI Namespace URI that must match on all
115: * subsequently added rules, or <code>null</code> for matching
116: * regardless of the current namespace URI
117: */
118: public void setNamespaceURI(String namespaceURI) {
119:
120: this .namespaceURI = namespaceURI;
121:
122: }
123:
124: // --------------------------------------------------------- Public Methods
125:
126: /**
127: * Register a new Rule instance matching the specified pattern.
128: *
129: * @param pattern Nesting pattern to be matched for this Rule
130: * @param rule Rule instance to be registered
131: */
132: public void add(String pattern, Rule rule) {
133: // to help users who accidently add '/' to the end of their patterns
134: int patternLength = pattern.length();
135: if (patternLength > 1 && pattern.endsWith("/")) {
136: pattern = pattern.substring(0, patternLength - 1);
137: }
138:
139: List list = (List) cache.get(pattern);
140: if (list == null) {
141: list = new ArrayList();
142: cache.put(pattern, list);
143: }
144: list.add(rule);
145: rules.add(rule);
146: if (this .digester != null) {
147: rule.setDigester(this .digester);
148: }
149: if (this .namespaceURI != null) {
150: rule.setNamespaceURI(this .namespaceURI);
151: }
152:
153: }
154:
155: /**
156: * Clear all existing Rule instance registrations.
157: */
158: public void clear() {
159:
160: cache.clear();
161: rules.clear();
162:
163: }
164:
165: /**
166: * Return a List of all registered Rule instances that match the specified
167: * nesting pattern, or a zero-length List if there are no matches. If more
168: * than one Rule instance matches, they <strong>must</strong> be returned
169: * in the order originally registered through the <code>add()</code>
170: * method.
171: *
172: * @param pattern Nesting pattern to be matched
173: *
174: * @deprecated Call match(namespaceURI,pattern) instead.
175: */
176: public List match(String pattern) {
177:
178: return (match(null, pattern));
179:
180: }
181:
182: /**
183: * Return a List of all registered Rule instances that match the specified
184: * nesting pattern, or a zero-length List if there are no matches. If more
185: * than one Rule instance matches, they <strong>must</strong> be returned
186: * in the order originally registered through the <code>add()</code>
187: * method.
188: *
189: * @param namespaceURI Namespace URI for which to select matching rules,
190: * or <code>null</code> to match regardless of namespace URI
191: * @param pattern Nesting pattern to be matched
192: */
193: public List match(String namespaceURI, String pattern) {
194:
195: // List rulesList = (List) this.cache.get(pattern);
196: List rulesList = lookup(namespaceURI, pattern);
197: if ((rulesList == null) || (rulesList.size() < 1)) {
198: // Find the longest key, ie more discriminant
199: String longKey = "";
200: Iterator keys = this .cache.keySet().iterator();
201: while (keys.hasNext()) {
202: String key = (String) keys.next();
203: if (key.startsWith("*/")) {
204: if (pattern.equals(key.substring(2))
205: || pattern.endsWith(key.substring(1))) {
206: if (key.length() > longKey.length()) {
207: // rulesList = (List) this.cache.get(key);
208: rulesList = lookup(namespaceURI, key);
209: longKey = key;
210: }
211: }
212: }
213: }
214: }
215: if (rulesList == null) {
216: rulesList = new ArrayList();
217: }
218: return (rulesList);
219:
220: }
221:
222: /**
223: * Return a List of all registered Rule instances, or a zero-length List
224: * if there are no registered Rule instances. If more than one Rule
225: * instance has been registered, they <strong>must</strong> be returned
226: * in the order originally registered through the <code>add()</code>
227: * method.
228: */
229: public List rules() {
230:
231: return (this .rules);
232:
233: }
234:
235: // ------------------------------------------------------ Protected Methods
236:
237: /**
238: * Return a List of Rule instances for the specified pattern that also
239: * match the specified namespace URI (if any). If there are no such
240: * rules, return <code>null</code>.
241: *
242: * @param namespaceURI Namespace URI to match, or <code>null</code> to
243: * select matching rules regardless of namespace URI
244: * @param pattern Pattern to be matched
245: */
246: protected List lookup(String namespaceURI, String pattern) {
247:
248: // Optimize when no namespace URI is specified
249: List list = (List) this .cache.get(pattern);
250: if (list == null) {
251: return (null);
252: }
253: if ((namespaceURI == null) || (namespaceURI.length() == 0)) {
254: return (list);
255: }
256:
257: // Select only Rules that match on the specified namespace URI
258: ArrayList results = new ArrayList();
259: Iterator items = list.iterator();
260: while (items.hasNext()) {
261: Rule item = (Rule) items.next();
262: if ((namespaceURI.equals(item.getNamespaceURI()))
263: || (item.getNamespaceURI() == null)) {
264: results.add(item);
265: }
266: }
267: return (results);
268:
269: }
270:
271: }
|