1 package org.apache.commons.digester3;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import static java.lang.System.arraycopy;
23 import static java.lang.String.format;
24 import static org.apache.commons.beanutils.ConstructorUtils.getAccessibleConstructor;
25 import static org.apache.commons.beanutils.ConvertUtils.convert;
26
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Method;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31
32 import net.sf.cglib.proxy.Callback;
33 import net.sf.cglib.proxy.Enhancer;
34 import net.sf.cglib.proxy.Factory;
35 import net.sf.cglib.proxy.MethodInterceptor;
36 import net.sf.cglib.proxy.MethodProxy;
37
38 import org.xml.sax.Attributes;
39 import org.xml.sax.SAXException;
40
41
42
43
44
45 public class ObjectCreateRule
46 extends Rule
47 {
48 private static class DeferredConstructionCallback implements MethodInterceptor
49 {
50 Constructor<?> constructor;
51 Object[] constructorArgs;
52 ArrayList<RecordedInvocation> invocations = new ArrayList<RecordedInvocation>();
53 Object delegate;
54
55 DeferredConstructionCallback( Constructor<?> constructor, Object[] constructorArgs )
56 {
57 this.constructor = constructor;
58 this.constructorArgs = constructorArgs;
59 }
60
61 public Object intercept( Object obj, Method method, Object[] args, MethodProxy proxy )
62 throws Throwable
63 {
64 boolean hasDelegate = delegate != null;
65 if ( !hasDelegate )
66 {
67 invocations.add( new RecordedInvocation( method, args ) );
68 }
69 if ( hasDelegate )
70 {
71 return proxy.invoke( delegate, args );
72 }
73 return proxy.invokeSuper( obj, args );
74 }
75
76 void establishDelegate()
77 throws Exception
78 {
79 convertTo( constructor.getParameterTypes(), constructorArgs );
80 delegate = constructor.newInstance( constructorArgs );
81 for ( RecordedInvocation invocation : invocations )
82 {
83 invocation.getInvokedMethod().invoke( delegate, invocation.getArguments() );
84 }
85 constructor = null;
86 constructorArgs = null;
87 invocations = null;
88 }
89 }
90
91 private static class ProxyManager
92 {
93 private final Class<?> clazz;
94 private final Constructor<?> constructor;
95 private final Object[] templateConstructorArguments;
96 private final Digester digester;
97 private final boolean hasDefaultConstructor;
98 private Factory factory;
99
100 ProxyManager( Class<?> clazz, Constructor<?> constructor, Object[] constructorArguments, Digester digester )
101 {
102 this.clazz = clazz;
103 hasDefaultConstructor = getAccessibleConstructor( clazz, new Class[0] ) != null;
104 this.constructor = constructor;
105 Class<?>[] argTypes = constructor.getParameterTypes();
106 templateConstructorArguments = new Object[argTypes.length];
107 if ( constructorArguments == null )
108 {
109 for ( int i = 0; i < templateConstructorArguments.length; i++ )
110 {
111 if ( argTypes[i].equals( boolean.class ) )
112 {
113 templateConstructorArguments[i] = Boolean.FALSE;
114 continue;
115 }
116 if ( argTypes[i].isPrimitive() )
117 {
118 templateConstructorArguments[i] = convert( "0", argTypes[i] );
119 continue;
120 }
121 templateConstructorArguments[i] = null;
122 }
123 }
124 else
125 {
126 if ( constructorArguments.length != argTypes.length )
127 {
128 throw new IllegalArgumentException(
129 format( "wrong number of constructor arguments specified: %s instead of %s",
130 constructorArguments.length, argTypes.length ) );
131 }
132 arraycopy( constructorArguments, 0, templateConstructorArguments, 0, constructorArguments.length );
133 }
134 convertTo( argTypes, templateConstructorArguments );
135 this.digester = digester;
136 }
137
138 Object createProxy()
139 {
140 Object[] constructorArguments = new Object[templateConstructorArguments.length];
141 arraycopy( templateConstructorArguments, 0, constructorArguments, 0, constructorArguments.length );
142 digester.pushParams( constructorArguments );
143
144 DeferredConstructionCallback callback =
145 new DeferredConstructionCallback( constructor, constructorArguments );
146
147 Object result;
148
149 if ( factory == null )
150 {
151 Enhancer enhancer = new Enhancer();
152 enhancer.setSuperclass( clazz );
153 enhancer.setCallback( callback );
154 enhancer.setClassLoader( digester.getClassLoader() );
155 enhancer.setInterceptDuringConstruction( false );
156 if ( hasDefaultConstructor )
157 {
158 result = enhancer.create();
159 }
160 else
161 {
162 result = enhancer.create( constructor.getParameterTypes(), constructorArguments );
163 }
164 factory = (Factory) result;
165 return result;
166 }
167
168 if ( hasDefaultConstructor )
169 {
170 result = factory.newInstance( callback );
171 }
172 else
173 {
174 result = factory.newInstance( constructor.getParameterTypes(),
175 constructorArguments, new Callback[] { callback } );
176 }
177 return result;
178 }
179
180 void finalize( Object proxy )
181 throws Exception
182 {
183 digester.popParams();
184 ( (DeferredConstructionCallback) ( (Factory) proxy ).getCallback( 0 ) ).establishDelegate();
185 }
186 }
187
188
189
190
191
192
193
194
195 public ObjectCreateRule( String className )
196 {
197 this( className, (String) null );
198 }
199
200
201
202
203
204
205 public ObjectCreateRule( Class<?> clazz )
206 {
207 this( clazz.getName(), (String) null );
208 this.clazz = clazz;
209 }
210
211
212
213
214
215
216
217
218 public ObjectCreateRule( String className, String attributeName )
219 {
220 this.className = className;
221 this.attributeName = attributeName;
222 }
223
224
225
226
227
228
229
230 public ObjectCreateRule( String attributeName, Class<?> clazz )
231 {
232 this( clazz != null ? clazz.getName() : null, attributeName );
233 this.clazz = clazz;
234 }
235
236
237
238
239
240
241 protected String attributeName = null;
242
243
244
245
246 protected Class<?> clazz = null;
247
248
249
250
251 protected String className = null;
252
253
254
255
256
257
258 private Class<?>[] constructorArgumentTypes;
259
260
261
262
263
264
265 private Object[] defaultConstructorArguments;
266
267
268
269
270
271
272 private ProxyManager proxyManager;
273
274
275
276
277
278
279
280
281
282 public void setConstructorArgumentTypes( Class<?>... constructorArgumentTypes )
283 {
284 if ( constructorArgumentTypes == null )
285 {
286 throw new IllegalArgumentException( "Parameter 'constructorArgumentTypes' must not be null" );
287 }
288
289 this.constructorArgumentTypes = constructorArgumentTypes;
290 }
291
292
293
294
295
296
297
298
299
300
301 public void setDefaultConstructorArguments( Object... constructorArguments )
302 {
303 if ( constructorArguments == null )
304 {
305 throw new IllegalArgumentException( "Parameter 'constructorArguments' must not be null" );
306 }
307
308 this.defaultConstructorArguments = constructorArguments;
309 }
310
311
312
313
314 @Override
315 public void begin( String namespace, String name, Attributes attributes )
316 throws Exception
317 {
318 Class<?> clazz = this.clazz;
319
320 if ( clazz == null )
321 {
322
323 String realClassName = className;
324 if ( attributeName != null )
325 {
326 String value = attributes.getValue( attributeName );
327 if ( value != null )
328 {
329 realClassName = value;
330 }
331 }
332 if ( getDigester().getLogger().isDebugEnabled() )
333 {
334 getDigester().getLogger().debug( format( "[ObjectCreateRule]{%s} New '%s'",
335 getDigester().getMatch(),
336 realClassName ) );
337 }
338
339
340 clazz = getDigester().getClassLoader().loadClass( realClassName );
341 }
342 Object instance;
343 if ( constructorArgumentTypes == null || constructorArgumentTypes.length == 0 )
344 {
345 if ( getDigester().getLogger().isDebugEnabled() )
346 {
347 getDigester()
348 .getLogger()
349 .debug( format( "[ObjectCreateRule]{%s} New '%s' using default empty constructor",
350 getDigester().getMatch(),
351 clazz.getName() ) );
352 }
353
354 instance = clazz.newInstance();
355 }
356 else
357 {
358 if ( proxyManager == null )
359 {
360 Constructor<?> constructor = getAccessibleConstructor( clazz, constructorArgumentTypes );
361
362 if ( constructor == null )
363 {
364 throw new SAXException(
365 format( "[ObjectCreateRule]{%s} Class '%s' does not have a construcor with types %s",
366 getDigester().getMatch(),
367 clazz.getName(),
368 Arrays.toString( constructorArgumentTypes ) ) );
369 }
370 proxyManager = new ProxyManager( clazz, constructor, defaultConstructorArguments, getDigester() );
371 }
372 instance = proxyManager.createProxy();
373 }
374 getDigester().push( instance );
375 }
376
377
378
379
380 @Override
381 public void end( String namespace, String name )
382 throws Exception
383 {
384 Object top = getDigester().pop();
385
386 if ( proxyManager != null )
387 {
388 proxyManager.finalize( top );
389 }
390
391 if ( getDigester().getLogger().isDebugEnabled() )
392 {
393 getDigester().getLogger().debug( format( "[ObjectCreateRule]{%s} Pop '%s'",
394 getDigester().getMatch(),
395 top.getClass().getName() ) );
396 }
397 }
398
399
400
401
402 @Override
403 public String toString()
404 {
405 return format( "ObjectCreateRule[className=%s, attributeName=%s]", className, attributeName );
406 }
407
408 private static void convertTo( Class<?>[] types, Object[] array )
409 {
410 if ( array.length != types.length )
411 {
412 throw new IllegalArgumentException();
413 }
414
415 for ( int i = 0; i < array.length; i++ )
416 {
417
418 if ( array[i] == null
419 || ( array[i] instanceof String && !String.class.isAssignableFrom( types[i] ) ) )
420 {
421 array[i] = convert( (String) array[i], types[i] );
422 }
423 }
424 }
425
426 }