| package org.apache.commons.digester3; |
| |
| /* |
| * 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. |
| */ |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.xml.sax.Attributes; |
| |
| /** |
| * <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.digester3 for more information. |
| * </p> |
| */ |
| |
| public class RulesBase |
| extends AbstractRulesImpl |
| { |
| |
| // ----------------------------------------------------- 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 subset of registered Rule instances with wildcard pattern. |
| */ |
| protected List<String> wildcardCache = new LinkedList<String>(); |
| |
| /** |
| * The set of registered Rule instances, in the order that they were originally registered. |
| */ |
| protected ArrayList<Rule> rules = new ArrayList<Rule>(); |
| |
| // ------------------------------------------------------------- Properties |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setDigester( final Digester digester ) |
| { |
| super.setDigester( digester ); |
| for ( final Rule rule : rules ) |
| { |
| rule.setDigester( digester ); |
| } |
| } |
| |
| // --------------------------------------------------------- Public Methods |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void registerRule( String pattern, final Rule rule ) |
| { |
| // to help users who accidently add '/' to the end of their patterns |
| final 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>(); |
| if ( pattern.startsWith( "*/" ) ) |
| { |
| wildcardCache.add( pattern.substring( 1 ) ); |
| } |
| cache.put( pattern, list ); |
| } |
| list.add( rule ); |
| rules.add( rule ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void clear() |
| { |
| wildcardCache.clear(); |
| cache.clear(); |
| rules.clear(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public List<Rule> match( final String namespaceURI, final String pattern, final String name, final Attributes attributes ) |
| { |
| // 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 ( final String key : wildcardCache ) |
| { |
| if ( ( pattern.equals( key.substring( 1 ) ) || pattern.endsWith( key ) ) |
| && key.length() > longKey.length() ) |
| { |
| longKey = key; |
| } |
| } |
| if ( !longKey.isEmpty() ) |
| { |
| rulesList = lookup( namespaceURI, "*" + longKey ); |
| } |
| } |
| if ( rulesList == null ) |
| { |
| rulesList = new ArrayList<Rule>(); |
| } |
| return ( rulesList ); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| 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 |
| * @return a List of Rule instances for the specified pattern that also match the specified namespace URI (if any) |
| */ |
| protected List<Rule> lookup( final String namespaceURI, final String pattern ) |
| { |
| // Optimize when no namespace URI is specified |
| final List<Rule> list = this.cache.get( pattern ); |
| if ( list == null ) |
| { |
| return ( null ); |
| } |
| if ( ( namespaceURI == null ) || ( namespaceURI.isEmpty() ) ) |
| { |
| return ( list ); |
| } |
| |
| // Select only Rules that match on the specified namespace URI |
| final ArrayList<Rule> results = new ArrayList<Rule>(); |
| for ( final Rule item : list ) |
| { |
| if ( ( namespaceURI.equals( item.getNamespaceURI() ) ) || ( item.getNamespaceURI() == null ) ) |
| { |
| results.add( item ); |
| } |
| } |
| return ( results ); |
| } |
| |
| } |