001 /* $Id: RulesBase.java 729099 2008-12-23 20:39:37Z rahul $ 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 020 package org.apache.commons.digester; 021 022 023 import java.util.ArrayList; 024 import java.util.HashMap; 025 import java.util.List; 026 027 028 /** 029 * <p>Default implementation of the <code>Rules</code> interface that supports 030 * the standard rule matching behavior. This class can also be used as a 031 * base class for specialized <code>Rules</code> implementations.</p> 032 * 033 * <p>The matching policies implemented by this class support two different 034 * types of pattern matching rules:</p> 035 * <ul> 036 * <li><em>Exact Match</em> - A pattern "a/b/c" exactly matches a 037 * <code><c></code> element, nested inside a <code><b></code> 038 * element, which is nested inside an <code><a></code> element.</li> 039 * <li><em>Tail Match</em> - A pattern "*/a/b" matches a 040 * <code><b></code> element, nested inside an <code><a></code> 041 * element, no matter how deeply the pair is nested.</li> 042 * </ul> 043 * 044 * <p>Note that wildcard patterns are ignored if an explicit match can be found 045 * (and when multiple wildcard patterns match, only the longest, ie most 046 * explicit, pattern is considered a match).</p> 047 * 048 * <p>See the package documentation for package org.apache.commons.digester 049 * for more information.</p> 050 */ 051 052 public class RulesBase implements Rules { 053 054 055 // ----------------------------------------------------- Instance Variables 056 057 058 /** 059 * The set of registered Rule instances, keyed by the matching pattern. 060 * Each value is a List containing the Rules for that pattern, in the 061 * order that they were orginally registered. 062 */ 063 protected HashMap<String, List<Rule>> cache = new HashMap<String, List<Rule>>(); 064 065 066 /** 067 * The Digester instance with which this Rules instance is associated. 068 */ 069 protected Digester digester = null; 070 071 072 /** 073 * The namespace URI for which subsequently added <code>Rule</code> 074 * objects are relevant, or <code>null</code> for matching independent 075 * of namespaces. 076 */ 077 protected String namespaceURI = null; 078 079 080 /** 081 * The set of registered Rule instances, in the order that they were 082 * originally registered. 083 */ 084 protected ArrayList<Rule> rules = new ArrayList<Rule>(); 085 086 087 // ------------------------------------------------------------- Properties 088 089 090 /** 091 * Return the Digester instance with which this Rules instance is 092 * associated. 093 */ 094 public Digester getDigester() { 095 096 return (this.digester); 097 098 } 099 100 101 /** 102 * Set the Digester instance with which this Rules instance is associated. 103 * 104 * @param digester The newly associated Digester instance 105 */ 106 public void setDigester(Digester digester) { 107 108 this.digester = digester; 109 for (Rule rule : rules) { 110 rule.setDigester(digester); 111 } 112 113 } 114 115 116 /** 117 * Return the namespace URI that will be applied to all subsequently 118 * added <code>Rule</code> objects. 119 */ 120 public String getNamespaceURI() { 121 122 return (this.namespaceURI); 123 124 } 125 126 127 /** 128 * Set the namespace URI that will be applied to all subsequently 129 * added <code>Rule</code> objects. 130 * 131 * @param namespaceURI Namespace URI that must match on all 132 * subsequently added rules, or <code>null</code> for matching 133 * regardless of the current namespace URI 134 */ 135 public void setNamespaceURI(String namespaceURI) { 136 137 this.namespaceURI = namespaceURI; 138 139 } 140 141 142 // --------------------------------------------------------- Public Methods 143 144 145 /** 146 * Register a new Rule instance matching the specified pattern. 147 * 148 * @param pattern Nesting pattern to be matched for this Rule 149 * @param rule Rule instance to be registered 150 */ 151 public void add(String pattern, Rule rule) { 152 // to help users who accidently add '/' to the end of their patterns 153 int patternLength = pattern.length(); 154 if (patternLength>1 && pattern.endsWith("/")) { 155 pattern = pattern.substring(0, patternLength-1); 156 } 157 158 159 List<Rule> list = cache.get(pattern); 160 if (list == null) { 161 list = new ArrayList<Rule>(); 162 cache.put(pattern, list); 163 } 164 list.add(rule); 165 rules.add(rule); 166 if (this.digester != null) { 167 rule.setDigester(this.digester); 168 } 169 if (this.namespaceURI != null) { 170 rule.setNamespaceURI(this.namespaceURI); 171 } 172 173 } 174 175 176 /** 177 * Clear all existing Rule instance registrations. 178 */ 179 public void clear() { 180 181 cache.clear(); 182 rules.clear(); 183 184 } 185 186 187 /** 188 * Return a List of all registered Rule instances that match the specified 189 * nesting pattern, or a zero-length List if there are no matches. If more 190 * than one Rule instance matches, they <strong>must</strong> be returned 191 * in the order originally registered through the <code>add()</code> 192 * method. 193 * 194 * @param pattern Nesting pattern to be matched 195 * 196 * @deprecated Call match(namespaceURI,pattern) instead. 197 */ 198 public List<Rule> match(String pattern) { 199 200 return (match(null, pattern)); 201 202 } 203 204 205 /** 206 * Return a List of all registered Rule instances that match the specified 207 * nesting pattern, or a zero-length List if there are no matches. If more 208 * than one Rule instance matches, they <strong>must</strong> be returned 209 * in the order originally registered through the <code>add()</code> 210 * method. 211 * 212 * @param namespaceURI Namespace URI for which to select matching rules, 213 * or <code>null</code> to match regardless of namespace URI 214 * @param pattern Nesting pattern to be matched 215 */ 216 public List<Rule> match(String namespaceURI, String pattern) { 217 218 // List rulesList = (List) this.cache.get(pattern); 219 List<Rule> rulesList = lookup(namespaceURI, pattern); 220 if ((rulesList == null) || (rulesList.size() < 1)) { 221 // Find the longest key, ie more discriminant 222 String longKey = ""; 223 for (String key : cache.keySet()) { 224 if (key.startsWith("*/")) { 225 if (pattern.equals(key.substring(2)) || 226 pattern.endsWith(key.substring(1))) { 227 if (key.length() > longKey.length()) { 228 // rulesList = (List) this.cache.get(key); 229 rulesList = lookup(namespaceURI, key); 230 longKey = key; 231 } 232 } 233 } 234 } 235 } 236 if (rulesList == null) { 237 rulesList = new ArrayList<Rule>(); 238 } 239 return (rulesList); 240 241 } 242 243 244 /** 245 * Return a List of all registered Rule instances, or a zero-length List 246 * if there are no registered Rule instances. If more than one Rule 247 * instance has been registered, they <strong>must</strong> be returned 248 * in the order originally registered through the <code>add()</code> 249 * method. 250 */ 251 public List<Rule> rules() { 252 253 return (this.rules); 254 255 } 256 257 258 // ------------------------------------------------------ Protected Methods 259 260 261 /** 262 * Return a List of Rule instances for the specified pattern that also 263 * match the specified namespace URI (if any). If there are no such 264 * rules, return <code>null</code>. 265 * 266 * @param namespaceURI Namespace URI to match, or <code>null</code> to 267 * select matching rules regardless of namespace URI 268 * @param pattern Pattern to be matched 269 */ 270 protected List<Rule> lookup(String namespaceURI, String pattern) { 271 272 // Optimize when no namespace URI is specified 273 List<Rule> list = this.cache.get(pattern); 274 if (list == null) { 275 return (null); 276 } 277 if ((namespaceURI == null) || (namespaceURI.length() == 0)) { 278 return (list); 279 } 280 281 // Select only Rules that match on the specified namespace URI 282 ArrayList<Rule> results = new ArrayList<Rule>(); 283 for (Rule item : list) { 284 if ((namespaceURI.equals(item.getNamespaceURI())) || 285 (item.getNamespaceURI() == null)) { 286 results.add(item); 287 } 288 } 289 return (results); 290 291 } 292 293 294 }