001package 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 022import 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 */ 029public 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}