View Javadoc

1   package org.apache.commons.digester3;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.LinkedList;
25  import java.util.List;
26  
27  import org.xml.sax.Attributes;
28  
29  /**
30   * <p>
31   * Default implementation of the <code>Rules</code> interface that supports the standard rule matching behavior. This
32   * class can also be used as a base class for specialized <code>Rules</code> implementations.
33   * </p>
34   * <p>
35   * The matching policies implemented by this class support two different types of pattern matching rules:
36   * </p>
37   * <ul>
38   * <li><em>Exact Match</em> - A pattern "a/b/c" exactly matches a <code>&lt;c&gt;</code> element, nested inside a
39   * <code>&lt;b&gt;</code> element, which is nested inside an <code>&lt;a&gt;</code> element.</li>
40   * <li><em>Tail Match</em> - A pattern "&#42;/a/b" matches a <code>&lt;b&gt;</code> element, nested inside an
41   * <code>&lt;a&gt;</code> element, no matter how deeply the pair is nested.</li>
42   * </ul>
43   * <p>
44   * Note that wildcard patterns are ignored if an explicit match can be found (and when multiple wildcard patterns match,
45   * only the longest, ie most explicit, pattern is considered a match).
46   * </p>
47   * <p>
48   * See the package documentation for package org.apache.commons.digester3 for more information.
49   * </p>
50   */
51  
52  public class RulesBase
53      extends AbstractRulesImpl
54  {
55  
56      // ----------------------------------------------------- Instance Variables
57  
58      /**
59       * The set of registered Rule instances, keyed by the matching pattern. Each value is a List containing the Rules
60       * for that pattern, in the order that they were orginally registered.
61       */
62      protected HashMap<String, List<Rule>> cache = new HashMap<String, List<Rule>>();
63  
64      /**
65       * The subset of registered Rule instances with wildcard pattern.
66       */
67      protected List<String> wildcardCache = new LinkedList<String>();
68  
69      /**
70       * The set of registered Rule instances, in the order that they were originally registered.
71       */
72      protected ArrayList<Rule> rules = new ArrayList<Rule>();
73  
74      // ------------------------------------------------------------- Properties
75  
76      /**
77       * {@inheritDoc}
78       */
79      @Override
80      public void setDigester( Digester digester )
81      {
82          super.setDigester( digester );
83          for ( Rule rule : rules )
84          {
85              rule.setDigester( digester );
86          }
87      }
88  
89      // --------------------------------------------------------- Public Methods
90  
91      /**
92       * {@inheritDoc}
93       */
94      @Override
95      protected void registerRule( String pattern, Rule rule )
96      {
97          // to help users who accidently add '/' to the end of their patterns
98          int patternLength = pattern.length();
99          if ( patternLength > 1 && pattern.endsWith( "/" ) )
100         {
101             pattern = pattern.substring( 0, patternLength - 1 );
102         }
103 
104         List<Rule> list = cache.get( pattern );
105         if ( list == null )
106         {
107             list = new ArrayList<Rule>();
108             if ( pattern.startsWith( "*/" ) )
109             {
110                 wildcardCache.add( pattern.substring( 1 ) );
111             }
112             cache.put( pattern, list );
113         }
114         list.add( rule );
115         rules.add( rule );
116     }
117 
118     /**
119      * {@inheritDoc}
120      */
121     public void clear()
122     {
123         wildcardCache.clear();
124         cache.clear();
125         rules.clear();
126     }
127 
128     /**
129      * {@inheritDoc}
130      */
131     public List<Rule> match( String namespaceURI, String pattern, String name, Attributes attributes )
132     {
133         // List rulesList = (List) this.cache.get(pattern);
134         List<Rule> rulesList = lookup( namespaceURI, pattern );
135         if ( ( rulesList == null ) || ( rulesList.size() < 1 ) )
136         {
137             // Find the longest key, ie more discriminant
138             String longKey = "";
139             for ( String key : wildcardCache )
140             {
141                 if ( ( pattern.equals( key.substring( 1 ) ) || pattern.endsWith( key ) )
142                     && key.length() > longKey.length() )
143                 {
144                     longKey = key;
145                 }
146             }
147             if ( longKey.length() > 0 )
148             {
149                 rulesList = lookup( namespaceURI, "*" + longKey );
150             }
151         }
152         if ( rulesList == null )
153         {
154             rulesList = new ArrayList<Rule>();
155         }
156         return ( rulesList );
157     }
158 
159     /**
160      * {@inheritDoc}
161      */
162     public List<Rule> rules()
163     {
164         return ( this.rules );
165     }
166 
167     // ------------------------------------------------------ Protected Methods
168 
169     /**
170      * Return a List of Rule instances for the specified pattern that also match the specified namespace URI (if any).
171      * If there are no such rules, return <code>null</code>.
172      *
173      * @param namespaceURI Namespace URI to match, or <code>null</code> to select matching rules regardless of namespace
174      *            URI
175      * @param pattern Pattern to be matched
176      * @return a List of Rule instances for the specified pattern that also match the specified namespace URI (if any)
177      */
178     protected List<Rule> lookup( String namespaceURI, String pattern )
179     {
180         // Optimize when no namespace URI is specified
181         List<Rule> list = this.cache.get( pattern );
182         if ( list == null )
183         {
184             return ( null );
185         }
186         if ( ( namespaceURI == null ) || ( namespaceURI.length() == 0 ) )
187         {
188             return ( list );
189         }
190 
191         // Select only Rules that match on the specified namespace URI
192         ArrayList<Rule> results = new ArrayList<Rule>();
193         for ( Rule item : list )
194         {
195             if ( ( namespaceURI.equals( item.getNamespaceURI() ) ) || ( item.getNamespaceURI() == null ) )
196             {
197                 results.add( item );
198             }
199         }
200         return ( results );
201     }
202 
203 }