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 org.apache.commons.digester3.Rule;
23  
24  /**
25   * Builder invoked to bind one or more rules to a pattern.
26   *
27   * @since 3.0
28   */
29  public final class LinkedRuleBuilder
30  {
31  
32      private final RulesBinder mainBinder;
33  
34      private final FromBinderRuleSet fromBinderRuleSet;
35  
36      private final ClassLoader classLoader;
37  
38      private final String keyPattern;
39  
40      private String namespaceURI;
41  
42      LinkedRuleBuilder( final RulesBinder mainBinder, final FromBinderRuleSet fromBinderRuleSet,
43                         final ClassLoader classLoader, final String keyPattern )
44      {
45          this.mainBinder = mainBinder;
46          this.fromBinderRuleSet = fromBinderRuleSet;
47          this.classLoader = classLoader;
48          this.keyPattern = keyPattern;
49      }
50  
51      /**
52       * Construct rule that automatically sets a property from the body text, taking the property
53       * name the same as the current element.
54       *
55       * @return a new {@link BeanPropertySetterBuilder} instance.
56       */
57      public BeanPropertySetterBuilder setBeanProperty()
58      {
59          return addProvider( new BeanPropertySetterBuilder( keyPattern, namespaceURI, mainBinder, this ) );
60      }
61  
62      /**
63       * Calls a method on an object on the stack (normally the top/parent object), passing arguments collected from
64       * subsequent {@link #callParam()} rule or from the body of this element.
65       *
66       * @param methodName Method name of the parent object to call
67       * @return a new {@link CallMethodBuilder} instance.
68       */
69      public CallMethodBuilder callMethod( String methodName )
70      {
71          if ( methodName == null || methodName.length() == 0 )
72          {
73              mainBinder.addError( "{ forPattern( \"%s\" ).callMethod( String ) } empty 'methodName' not allowed",
74                                   keyPattern );
75          }
76  
77          return this.addProvider( new CallMethodBuilder( keyPattern, namespaceURI, mainBinder, this, methodName,
78                                                          classLoader ) );
79      }
80  
81      /**
82       * Saves a parameter for use by a surrounding {@link #callMethod(String)}.
83       *
84       * @return a new {@link CallParamBuilder} instance.
85       */
86      public CallParamBuilder callParam()
87      {
88          return this.addProvider( new CallParamBuilder( keyPattern, namespaceURI, mainBinder, this ) );
89      }
90  
91      /**
92       * Construct a "call parameter" rule that will save the body text of this element as the parameter value.
93       *
94       * @return a new {@link PathCallParamBuilder} instance.
95       */
96      public PathCallParamBuilder callParamPath()
97      {
98          return addProvider( new PathCallParamBuilder( keyPattern, namespaceURI, mainBinder, this ) );
99      }
100 
101     /**
102      * Uses an {@link org.apache.commons.digester3.ObjectCreationFactory} to create a new object which it
103      * pushes onto the object stack.
104      *
105      * When the element is complete, the object will be popped.
106      *
107      * @return a new {@link FactoryCreateBuilder} instance.
108      */
109     public FactoryCreateBuilder factoryCreate()
110     {
111         return addProvider( new FactoryCreateBuilder( keyPattern, namespaceURI, mainBinder, this, classLoader ) );
112     }
113 
114     /**
115      * Construct an object.
116      *
117      * @return a new {@link ObjectCreateBuilder} instance.
118      */
119     public ObjectCreateBuilder createObject()
120     {
121         return addProvider( new ObjectCreateBuilder( keyPattern, namespaceURI, mainBinder, this, classLoader ) );
122     }
123 
124     /**
125      * Saves a parameter for use by a surrounding {@link #callMethod(String)}.
126      *
127      * @param <T> The parameter type to pass along
128      * @param paramObj The parameter to pass along
129      * @return a new {@link ObjectParamBuilder} instance.
130      */
131     public <T> ObjectParamBuilder<T> objectParam( /* @Nullable */T paramObj )
132     {
133         return addProvider( new ObjectParamBuilder<T>( keyPattern, namespaceURI, mainBinder, this, paramObj ) );
134     }
135 
136     /**
137      * Sets properties on the object at the top of the stack,
138      * based on child elements with names matching properties on that  object.
139      *
140      * @return a new {@link NestedPropertiesBuilder} instance.
141      */
142     public NestedPropertiesBuilder setNestedProperties()
143     {
144         // that would be useful when adding rules via automatically generated rules binding (such annotations)
145         NestedPropertiesBuilder nestedPropertiesBuilder =
146             fromBinderRuleSet.getProvider( keyPattern, namespaceURI, NestedPropertiesBuilder.class );
147         if ( nestedPropertiesBuilder != null )
148         {
149             return nestedPropertiesBuilder;
150         }
151 
152         return addProvider( new NestedPropertiesBuilder( keyPattern, namespaceURI, mainBinder, this ) );
153     }
154 
155     /**
156      * Calls a method on the (top-1) (parent) object, passing the top object (child) as an argument,
157      * commonly used to establish parent-child relationships.
158      *
159      * @param methodName Method name of the parent method to call
160      * @return a new {@link SetNextBuilder} instance.
161      */
162     public SetNextBuilder setNext( String methodName )
163     {
164         if ( methodName == null || methodName.length() == 0 )
165         {
166             mainBinder.addError( "{ forPattern( \"%s\" ).setNext( String ) } empty 'methodName' not allowed",
167                                  keyPattern );
168         }
169         return this.addProvider( new SetNextBuilder( keyPattern, namespaceURI, mainBinder, this, methodName,
170                                                      classLoader ) );
171     }
172 
173     /**
174      * Sets properties on the object at the top of the stack, based on attributes with corresponding names.
175      *
176      * @return a new {@link SetPropertiesBuilder} instance.
177      */
178     public SetPropertiesBuilder setProperties()
179     {
180         // that would be useful when adding rules via automatically generated rules binding (such annotations)
181         SetPropertiesBuilder setPropertiesBuilder =
182             fromBinderRuleSet.getProvider( keyPattern, namespaceURI, SetPropertiesBuilder.class );
183         if ( setPropertiesBuilder != null )
184         {
185             return setPropertiesBuilder;
186         }
187 
188         return addProvider( new SetPropertiesBuilder( keyPattern, namespaceURI, mainBinder, this ) );
189     }
190 
191     /**
192      * Sets an individual property on the object at the top of the stack, based on attributes with specified names.
193      *
194      * @param attributePropertyName Name of the attribute that will contain the name of the property to be set
195      * @return a new {@link SetPropertyBuilder} instance.
196      */
197     public SetPropertyBuilder setProperty( String attributePropertyName )
198     {
199         if ( attributePropertyName == null || attributePropertyName.length() == 0 )
200         {
201             mainBinder
202                 .addError( "{ forPattern( \"%s\" ).setProperty( String ) } empty 'attributePropertyName' not allowed",
203                            keyPattern );
204         }
205 
206         return addProvider( new SetPropertyBuilder( keyPattern,
207                                                     namespaceURI,
208                                                     mainBinder,
209                                                     this,
210                                                     attributePropertyName ) );
211     }
212 
213     /**
214      * Calls a method on the root object on the stack, passing the top object (child) as an argument.
215      *
216      * @param methodName Method name of the parent method to call
217      * @return a new {@link SetRootBuilder} instance.
218      */
219     public SetRootBuilder setRoot( String methodName )
220     {
221         if ( methodName == null || methodName.length() == 0 )
222         {
223             mainBinder.addError( "{ forPattern( \"%s\" ).setRoot( String ) } empty 'methodName' not allowed",
224                                  keyPattern );
225         }
226 
227         return addProvider( new SetRootBuilder( keyPattern, namespaceURI, mainBinder, this, methodName, classLoader ) );
228     }
229 
230     /**
231      * Calls a "set top" method on the top (child) object, passing the (top-1) (parent) object as an argument.
232      *
233      * @param methodName Method name of the "set parent" method to call
234      * @return a new {@link SetTopBuilder} instance.
235      */
236     public SetTopBuilder setTop( String methodName )
237     {
238         if ( methodName == null || methodName.length() == 0 )
239         {
240             mainBinder.addError( "{ forPattern( \"%s\" ).setTop( String ) } empty 'methodName' not allowed",
241                                  keyPattern );
242         }
243 
244         return addProvider( new SetTopBuilder( keyPattern, namespaceURI, mainBinder, this, methodName, classLoader ) );
245     }
246 
247     /**
248      * A Digester rule which allows the user to pre-declare a class which is to
249      * be referenced later at a plugin point by a PluginCreateRule.
250      *
251      * NOTE: when using this rule, make sure {@link org.apache.commons.digester3.Digester} instances
252      * will be created using {@link org.apache.commons.digester3.plugins.PluginRules} rules strategy.
253      *
254      * @return a new {@link PluginDeclarationRuleBuilder} instance.
255      */
256     public PluginDeclarationRuleBuilder declarePlugin()
257     {
258         return addProvider( new PluginDeclarationRuleBuilder( keyPattern, namespaceURI, mainBinder, this ) );
259     }
260 
261     /**
262      * A Digester rule which allows the user to declare a plugin.
263      *
264      * NOTE: when using this rule, make sure {@link org.apache.commons.digester3.Digester} instances
265      * will be created using {@link org.apache.commons.digester3.plugins.PluginRules} rules strategy.
266      *
267      * @return a new {@link PluginDeclarationRuleBuilder} instance.
268      */
269     public PluginCreateRuleBuilder createPlugin()
270     {
271         return addProvider( new PluginCreateRuleBuilder( keyPattern, namespaceURI, mainBinder, this ) );
272     }
273 
274     /**
275      * A rule implementation that creates a DOM Node containing the XML at the element that matched the rule.
276      *
277      * @return a new {@link NodeCreateRuleProvider} instance.
278      */
279     public NodeCreateRuleProvider createNode()
280     {
281         return addProvider( new NodeCreateRuleProvider( keyPattern, namespaceURI, mainBinder, this ) );
282     }
283 
284     /**
285      * Add a custom user rule in the specified pattern.
286      *
287      * <b>WARNING</b> keep away from this method as much as you can, since there's the risk
288      * same input {@link Rule} instance is plugged to more than one Digester;
289      * use {@link #addRuleCreatedBy(RuleProvider)} instead!!!
290      *
291      * @see #addRuleCreatedBy(RuleProvider)
292      * @see Rule#setDigester(org.apache.commons.digester3.Digester)
293      * @param <R> The rule type
294      * @param rule The custom user rule
295      * @return a new {@link ByRuleBuilder} instance.
296      */
297     public <R extends Rule> ByRuleBuilder<R> addRule( R rule )
298     {
299         if ( rule == null )
300         {
301             mainBinder.addError( "{ forPattern( \"%s\" ).addRule( R ) } NULL rule not valid", keyPattern );
302         }
303 
304         return this.addProvider( new ByRuleBuilder<R>( keyPattern, namespaceURI, mainBinder, this, rule ) );
305     }
306 
307     /**
308      * Add a custom user rule in the specified pattern built by the given provider.
309      *
310      * @param <R> The rule type
311      * @param provider The rule provider
312      * @return a new {@link ByRuleProviderBuilder} instance.
313      */
314     public <R extends Rule> ByRuleProviderBuilder<R> addRuleCreatedBy( RuleProvider<R> provider )
315     {
316         if ( provider == null )
317         {
318             mainBinder.addError( "{ forPattern( \"%s\" ).addRuleCreatedBy() } null rule provider not valid",
319                                  keyPattern );
320         }
321 
322         return addProvider( new ByRuleProviderBuilder<R>( keyPattern, namespaceURI, mainBinder, this, provider ) );
323     }
324 
325     /**
326      * Sets the namespace URI for the current rule pattern.
327      *
328      * @param namespaceURI the namespace URI associated to the rule pattern.
329      * @return this {@link LinkedRuleBuilder} instance
330      */
331     public LinkedRuleBuilder withNamespaceURI( /* @Nullable */String namespaceURI )
332     {
333         if ( namespaceURI == null || namespaceURI.length() > 0 )
334         {
335             this.namespaceURI = namespaceURI;
336         }
337         else
338         {
339             // ignore empty namespaces, null is better
340             this.namespaceURI = null;
341         }
342 
343         return this;
344     }
345 
346     /**
347      * Add a provider in the data structure where storing the providers binding.
348      *
349      * @param <R> The rule will be created by the given provider
350      * @param provider The provider has to be stored in the data structure
351      * @return The provider itself has to be stored in the data structure
352      */
353     private <R extends Rule, RB extends AbstractBackToLinkedRuleBuilder<R>> RB addProvider( RB provider )
354     {
355         fromBinderRuleSet.registerProvider( provider );
356         return provider;
357     }
358 
359 }