001 package org.apache.commons.digester3.binder;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.commons.digester3.Rule;
023
024 /**
025 * Builder invoked to bind one or more rules to a pattern.
026 *
027 * @since 3.0
028 */
029 public final class LinkedRuleBuilder
030 {
031
032 private final RulesBinder mainBinder;
033
034 private final FromBinderRuleSet fromBinderRuleSet;
035
036 private final ClassLoader classLoader;
037
038 private final String keyPattern;
039
040 private String namespaceURI;
041
042 LinkedRuleBuilder( final RulesBinder mainBinder, final FromBinderRuleSet fromBinderRuleSet,
043 final ClassLoader classLoader, final String keyPattern )
044 {
045 this.mainBinder = mainBinder;
046 this.fromBinderRuleSet = fromBinderRuleSet;
047 this.classLoader = classLoader;
048 this.keyPattern = keyPattern;
049 }
050
051 /**
052 * Construct rule that automatically sets a property from the body text, taking the property
053 * name the same as the current element.
054 *
055 * @return a new {@link BeanPropertySetterBuilder} instance.
056 */
057 public BeanPropertySetterBuilder setBeanProperty()
058 {
059 return addProvider( new BeanPropertySetterBuilder( keyPattern, namespaceURI, mainBinder, this ) );
060 }
061
062 /**
063 * Calls a method on an object on the stack (normally the top/parent object), passing arguments collected from
064 * subsequent {@link #callParam()} rule or from the body of this element.
065 *
066 * @param methodName Method name of the parent object to call
067 * @return a new {@link CallMethodBuilder} instance.
068 */
069 public CallMethodBuilder callMethod( String methodName )
070 {
071 if ( methodName == null || methodName.length() == 0 )
072 {
073 mainBinder.addError( "{ forPattern( \"%s\" ).callMethod( String ) } empty 'methodName' not allowed",
074 keyPattern );
075 }
076
077 return this.addProvider( new CallMethodBuilder( keyPattern, namespaceURI, mainBinder, this, methodName,
078 classLoader ) );
079 }
080
081 /**
082 * Saves a parameter for use by a surrounding {@link #callMethod(String)}.
083 *
084 * @return a new {@link CallParamBuilder} instance.
085 */
086 public CallParamBuilder callParam()
087 {
088 return this.addProvider( new CallParamBuilder( keyPattern, namespaceURI, mainBinder, this ) );
089 }
090
091 /**
092 * Construct a "call parameter" rule that will save the body text of this element as the parameter value.
093 *
094 * @return a new {@link PathCallParamBuilder} instance.
095 */
096 public PathCallParamBuilder callParamPath()
097 {
098 return addProvider( new PathCallParamBuilder( keyPattern, namespaceURI, mainBinder, this ) );
099 }
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 }