View Javadoc

1   package org.apache.commons.digester3.binder;
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.Collection;
24  import java.util.HashMap;
25  import java.util.LinkedList;
26  import java.util.Map;
27  
28  import org.apache.commons.digester3.Digester;
29  import org.apache.commons.digester3.Rule;
30  import org.apache.commons.digester3.RuleSet;
31  
32  /**
33   * {@link RuleSet} implementation that allows register {@link RuleProvider} instances
34   * and add rules to the {@link Digester}.
35   *
36   * @since 3.0
37   */
38  final class FromBinderRuleSet
39      implements RuleSet
40  {
41  
42      /**
43       * The data structure where storing the providers binding.
44       */
45      private final Collection<AbstractBackToLinkedRuleBuilder<? extends Rule>> providers =
46          new LinkedList<AbstractBackToLinkedRuleBuilder<? extends Rule>>();
47  
48      /**
49       * Index for quick-retrieve provider.
50       */
51      private final Map<Key, Collection<AbstractBackToLinkedRuleBuilder<? extends Rule>>> providersIndex =
52          new HashMap<Key, Collection<AbstractBackToLinkedRuleBuilder<? extends Rule>>>();
53  
54      /**
55       * Register the given rule builder and returns it.
56       *
57       * @param <R> The Digester rule type
58       * @param <RB> The Digester rule builder type
59       * @param ruleBuilder The input rule builder instance.
60       */
61      public <R extends Rule, RB extends AbstractBackToLinkedRuleBuilder<R>> void registerProvider( RB ruleBuilder )
62      {
63          this.providers.add( ruleBuilder );
64  
65          Key key = new Key( ruleBuilder.getPattern(), ruleBuilder.getNamespaceURI() );
66  
67          // O(1)
68          Collection<AbstractBackToLinkedRuleBuilder<? extends Rule>> indexedProviders = this.providersIndex.get( key );
69          if ( indexedProviders == null )
70          {
71              indexedProviders = new ArrayList<AbstractBackToLinkedRuleBuilder<? extends Rule>>();
72              this.providersIndex.put( key, indexedProviders ); // O(1)
73          }
74          indexedProviders.add( ruleBuilder );
75      }
76  
77      /**
78       * Returns the first instance of {@link RuleProvider} assignable to the input type.
79       *
80       * This method is useful for rules that requires be unique in the pattern,
81       * like {@link org.apache.commons.digester3.SetPropertiesRule}
82       * and {@link org.apache.commons.digester3.SetNestedPropertiesRule}.
83       *
84       * @param <R> The Digester rule type
85       * @param <RB> The Digester rule builder type
86       * @param keyPattern the rule pattern
87       * @param namespaceURI the namespace URI (can be null)
88       * @param type the rule builder type the client is looking for
89       * @return the rule builder of input type, if any
90       */
91      public <R extends Rule, RB extends AbstractBackToLinkedRuleBuilder<R>> RB getProvider( String keyPattern,
92      /* @Nullable */String namespaceURI, Class<RB> type )
93      {
94          Key key = new Key( keyPattern, namespaceURI );
95  
96          // O(1)
97          Collection<AbstractBackToLinkedRuleBuilder<? extends Rule>> indexedProviders = this.providersIndex.get( key );
98  
99          if ( indexedProviders == null || indexedProviders.isEmpty() )
100         {
101             return null;
102         }
103 
104         // FIXME O(n) not so good
105         for ( AbstractBackToLinkedRuleBuilder<? extends Rule> ruleProvider : indexedProviders )
106         {
107             if ( type.isInstance( ruleProvider ) )
108             {
109                 return type.cast( ruleProvider );
110             }
111         }
112 
113         return null;
114     }
115 
116     /**
117      * Clean the provider index.
118      */
119     public void clear()
120     {
121         providers.clear();
122         providersIndex.clear();
123     }
124 
125     /**
126      * {@inheritDoc}
127      */
128     public void addRuleInstances( Digester digester )
129     {
130         for ( AbstractBackToLinkedRuleBuilder<? extends Rule> provider : providers )
131         {
132             digester.addRule( provider.getPattern(), provider.get() );
133         }
134     }
135 
136     /**
137      * {@inheritDoc}
138      */
139     public String getNamespaceURI()
140     {
141         return null;
142     }
143 
144     /**
145      * Used to associate pattern/namespaceURI
146      */
147     private static final class Key
148     {
149 
150         private final String pattern;
151 
152         private final String namespaceURI;
153 
154         public Key( String pattern, String namespaceURI )
155         {
156             this.pattern = pattern;
157             this.namespaceURI = namespaceURI;
158         }
159 
160         public String getPattern()
161         {
162             return pattern;
163         }
164 
165         public String getNamespaceURI()
166         {
167             return namespaceURI;
168         }
169 
170         /**
171          * {@inheritDoc}
172          */
173         @Override
174         public int hashCode()
175         {
176             final int prime = 31;
177             int result = 1;
178             result = prime * result + ( ( namespaceURI == null ) ? 0 : namespaceURI.hashCode() );
179             result = prime * result + ( ( pattern == null ) ? 0 : pattern.hashCode() );
180             return result;
181         }
182 
183         /**
184          * {@inheritDoc}
185          */
186         @Override
187         public boolean equals( Object obj )
188         {
189             if ( this == obj )
190             {
191                 return true;
192             }
193 
194             if ( obj == null )
195             {
196                 return false;
197             }
198 
199             if ( getClass() != obj.getClass() )
200             {
201                 return false;
202             }
203 
204             Key other = (Key) obj;
205             if ( namespaceURI == null )
206             {
207                 if ( other.getNamespaceURI() != null )
208                 {
209                     return false;
210                 }
211             }
212             else if ( !namespaceURI.equals( other.getNamespaceURI() ) )
213             {
214                 return false;
215             }
216 
217             if ( pattern == null )
218             {
219                 if ( other.getPattern() != null )
220                 {
221                     return false;
222                 }
223             }
224             else if ( !pattern.equals( other.getPattern() ) )
225             {
226                 return false;
227             }
228 
229             return true;
230         }
231 
232         /**
233          * {@inheritDoc}
234          */
235         @Override
236         public String toString()
237         {
238             return "Key [pattern=" + pattern + ", namespaceURI=" + namespaceURI + "]";
239         }
240 
241     }
242 
243 }