##### Coverage Report - org.webmacro.engine.PropertyOperatorCache

Classes in this File | Line Coverage | Branch Coverage | Complexity
Accessor |
40% | 4/10
N/A | N/A
| 5.327272727272727;5.327
BinaryMethodAccessor |
75% | 6/8
N/A | N/A
| 5.327272727272727;5.327
DirectAccessor |
70% | 17/24
41% | 5/12
| 5.327272727272727;5.327
FieldAccessor |
27% | 3/11
N/A | N/A
| 5.327272727272727;5.327
LengthAccessor |
33% | 2/6
N/A | N/A
| 5.327272727272727;5.327
MethodAccessor |
70% | 45/64
47% | 18/38
| 5.327272727272727;5.327
PropertyOperator |
74% | 188/251
77% | 163/210
| 5.327272727272727;5.327
PropertyOperatorCache |
73% | 48/65
71% | 20/28
| 5.327272727272727;5.327
UnaryMethodAccessor |
100% | 7/7
N/A | N/A
| 5.327272727272727;5.327

1 |  |
     /*
2 |  |
      * Copyright (C) 1998-2000 Semiotek Inc.  All Rights Reserved.
3 |  |
      *
4 |  |
      * Redistribution and use in source and binary forms, with or without
5 |  |
      * modification, are permitted under the terms of either of the following
6 |  |
      * Open Source licenses:
7 |  |
      *
8 |  |
      * The GNU General Public License, version 2, or any later version, as
9 |  |
      * published by the Free Software Foundation
10 |  |
      * (http://www.fsf.org/copyleft/gpl.html);
11 |  |
      *
12 |  |
      *  or
13 |  |
      *
14 |  |
      * The Semiotek Public License (http://webmacro.org/LICENSE.)
15 |  |
      *
16 |  |
      * This software is provided "as is", with NO WARRANTY, not even the
17 |  |
      * implied warranties of fitness to purpose, or merchantability. You
18 |  |
      * assume all risks and liabilities associated with its use.
19 |  |
      *
20 |  |
      * See www.webmacro.org for more information on the WebMacro project.
21 |  |
      */
22 |  |
23 |  |
     package org.webmacro.engine;
24 |  |
25 |  |
     import java.lang.reflect.Field;
26 |  |
     import java.lang.reflect.InvocationTargetException;
27 |  |
     import java.lang.reflect.Method;
28 |  |
     import java.lang.reflect.Modifier;
29 |  |
     import java.util.ArrayList;
30 |  |
     import java.util.Enumeration;
31 |  |
     import java.util.HashMap;
32 |  |
     import java.util.Iterator;
33 |  |
     import java.util.List;
34 |  |
     import java.util.Map;
35 |  |
     import java.util.StringTokenizer;
36 |  |
     import java.util.Vector;
37 |  |
38 |  |
     import org.slf4j.Logger;
39 |  |
     import org.slf4j.LoggerFactory;
40 |  |
     import org.webmacro.Broker;
41 |  |
     import org.webmacro.Context;
42 |  |
     import org.webmacro.InitException;
43 |  |
     import org.webmacro.PropertyException;
44 |  |
     import org.webmacro.resource.CacheManager;
45 |  |
     import org.webmacro.resource.SimpleCacheManager;
46 |  |
     import org.webmacro.util.ArrayIterator;
47 |  |
     import org.webmacro.util.EnumIterator;
48 |  |
     import org.webmacro.util.PrimitiveArrayIterator;
49 |  |
     import org.webmacro.util.PropertyMethod;
50 |  |
     import org.webmacro.util.Settings;
51 |  |
52 |  |
     /**
53 |  |
      * @author Brian Goetz
54 |  |
      * @since 28 Apr 2001
55 |  |
      *
56 |  |
      */
57 |  |
     final public class PropertyOperatorCache
58 |  |
     {
59 |  |
60 |  |
        private CacheManager _cache;
61 | 2 |
        static Logger _log =  LoggerFactory.getLogger(PropertyOperatorCache.class);
62 |  |
        private HashMap _restrictedClasses;
63 |  |
64 |  |
        public PropertyOperatorCache()
65 | 6 |
        {
66 | 6 |
        }
67 |  |
68 |  |
        final public void init(Broker b, Settings config) throws InitException
69 |  |
        {
70 |  |
           String cacheManager;
71 |  |
72 |  |
73 | 6 |
           cacheManager = b.getSetting("PropertyOperator.CacheManager");
74 | 6 |
           if (cacheManager == null || cacheManager.equals(""))
75 |  |
           {
76 | 0 |
              _log.info("CachingProvider: " +
77 |  |
                      "No cache manager specified for PropertyOperator, " +
78 |  |
                      "using SimpleCacheManager");
79 | 0 |
              _cache = new SimpleCacheManager();
80 |  |
           }
81 |  |
           else
82 |  |
           {
83 |  |
              try
84 |  |
              {
85 | 6 |
                 _cache = (CacheManager) b.classForName(cacheManager).newInstance();
86 |  |
              }
87 | 0 |
              catch (Exception e)
88 |  |
              {
89 | 0 |
                 _log.warn("Unable to load cache manager " + cacheManager
90 |  |
                 + " for PropertyOperator, using SimpleCacheManager.  Reason:\n" + e);
91 | 0 |
                 _cache = new SimpleCacheManager();
92 | 6 |
              }
93 |  |
           }
94 | 6 |
           _cache.init(b, config, "PropertyOperator");
95 | 6 |
           _restrictedClasses = new HashMap(11);
96 | 6 |
           String restrictList = b.getSetting("RestrictedClasses");
97 | 6 |
           if (restrictList != null)
98 |  |
           {
99 | 6 |
              StringTokenizer stok = new StringTokenizer(restrictList, ",");
100 | 12 |
              while (stok.hasMoreTokens())
101 |  |
              {
102 | 6 |
                 String className = stok.nextToken();
103 |  |
                 try
104 |  |
                 {
105 | 6 |
                    Class c = Class.forName(className);
106 | 6 |
                    String okMethList = b.getSetting(
107 |  |
                            "RestrictedClasses.AllowedMethods." + className);
108 | 6 |
                    ArrayList okMeths = null;
109 | 6 |
                    if (okMethList != null)
110 |  |
                    {
111 | 6 |
                       okMeths = new ArrayList(20);
112 | 6 |
                       StringTokenizer stok2 = new StringTokenizer(okMethList, ",");
113 | 36 |
                       while (stok2.hasMoreTokens())
114 |  |
                       {
115 | 30 |
                          okMeths.add(stok2.nextToken());
116 |  |
                       }
117 |  |
                    }
118 | 6 |
                    _restrictedClasses.put(c, okMeths);
119 |  |
                 }
120 | 0 |
                 catch (Exception e)
121 |  |
                 {
122 | 0 |
                    _log.error("Configuration error: restricted class " + className
123 |  |
                    + " cannot be loaded", e);
124 | 6 |
                 }
125 | 6 |
              }
126 |  |
           }
127 | 6 |
        }
128 |  |
129 |  |
        Map getRestrictedClassMap()
130 |  |
        {
131 | 82248 |
           return _restrictedClasses;
132 |  |
        }
133 |  |
134 |  |
        final public PropertyOperator getOperator(final Class type)
135 |  |
            throws PropertyException
136 |  |
        {
137 | 133540 |
           Object o = _cache.get(type);
138 | 133540 |
           if (o == null)
139 |  |
           {
140 | 540 |
              PropertyOperator po = new PropertyOperator(type, this);
141 | 540 |
              _cache.put(type, po);
142 | 540 |
              return po;
143 |  |
           }
144 |  |
           else
145 | 133000 |
              return (PropertyOperator) o;
146 |  |
        }
147 |  |
148 |  |
        final public PropertyOperator getOperator(final Object obj)
149 |  |
            throws PropertyException
150 |  |
        {
151 | 133540 |
           Class type = obj.getClass();
152 |  |
           // added code to handle static classes.  Static classes must be wrapped
153 |  |
           // in a StaticClassWrapper
154 |  |
           // 1Jun2001 - Keats
155 | 133540 |
           if (type == org.webmacro.engine.StaticClassWrapper.class)
156 |  |
           {
157 | 0 |
              type = ((org.webmacro.engine.StaticClassWrapper) obj).get();
158 |  |
           }
159 | 133540 |
           return getOperator(type);
160 |  |
        }
161 |  |
162 |  |
        /**
163 |  |
         * Attempt to retrieve a property using the rules of property
164 |  |
         * introspection described above. Begin reading names at position
165 |  |
         * start in the array of names.
166 |  |
         *
167 |  |
         * @param context is used to resolve sub-properties in arguments
168 |  |
         * @param instance is the root of introspection
169 |  |
         * @param names property names, one per array entry
170 |  |
         * @return the property described by the names, inside the instance
171 |  |
         * @exception PropertyException the property we'd like to look at
172 |  |
         * @exception SecurityException you are not permitted to try
173 |  |
         */
174 |  |
        final public Object getProperty(final Context context,
175 |  |
                                        final Object instance,
176 |  |
                                        final Object[] names,
177 |  |
                                        int start)
178 |  |
            throws PropertyException
179 |  |
        {
180 | 106792 |
           if (instance == null)
181 |  |
           {
182 | 0 |
              return null;
183 |  |
           }
184 |  |
           else
185 |  |
           {
186 | 106792 |
              return getOperator(instance).getProperty(
187 |  |
              context, instance, names, start, names.length - 1);
188 |  |
           }
189 |  |
        }
190 |  |
191 |  |
        /**
192 |  |
         * Calls getProperty(context, instance, names, 0).
193 |  |
         */
194 |  |
        final public Object getProperty(final Context context,
195 |  |
                                        final Object instance,
196 |  |
                                        final Object[] names)
197 |  |
            throws PropertyException
198 |  |
        {
199 | 0 |
           return getProperty(context, instance, names, 0);
200 |  |
        }
201 |  |
202 |  |
203 |  |
        /**
204 |  |
         * Given a property description name, attempt to set the property
205 |  |
         * value to the supplied object.
206 |  |
         *
207 |  |
         * @param context An object containing a property
208 |  |
         * @param names The string names of that property
209 |  |
         * @param value the new value the property is to be set to
210 |  |
         * @exception PropertyException not possible to set the property
211 |  |
         * @exception SecurityException you are not permitted to try
212 |  |
         */
213 |  |
        final public boolean setProperty(final Context context,
214 |  |
                                         Object instance,
215 |  |
                                         final Object[] names,
216 |  |
                                         int start,
217 |  |
                                         final Object value)
218 |  |
            throws PropertyException
219 |  |
        {
220 |  |
           try
221 |  |
           {
222 | 192 |
              if (instance == null)
223 |  |
              {
224 | 0 |
                 return false;
225 |  |
              }
226 | 192 |
              return getOperator(instance)
227 |  |
              .setProperty(context, instance, names, value, start);
228 |  |
           }
229 | 0 |
           catch (PropertyException e)
230 |  |
           {
231 | 0 |
              throw e;
232 |  |
           }
233 | 0 |
           catch (NoSuchMethodException e)
234 |  |
           {
235 | 0 |
              throw new PropertyException("No method to access property: " + e, e);
236 |  |
           }
237 |  |
        }
238 |  |
239 |  |
        /**
240 |  |
         * Calls setProperty(context, names, 0, value)
241 |  |
         */
242 |  |
        final public boolean setProperty(final Context context,
243 |  |
                                         final Object instance,
244 |  |
                                         final Object[] names,
245 |  |
                                         final Object value)
246 |  |
             throws PropertyException
247 |  |
        {
248 | 0 |
           return setProperty(context, instance, names, 0, value);
249 |  |
        }
250 |  |
251 |  |
        /**
252 |  |
         * Evaluate the supplied object and work out a way to return it
253 |  |
         * as an iterator.
254 |  |
         *
255 |  |
         * @param instance an object believed to represent a list
256 |  |
         * @return an Iterator that iterates through that list
257 |  |
         * @exception PropertyException could not extract iterator from instance
258 |  |
         */
259 |  |
        final public Iterator getIterator(Object instance)
260 |  |
            throws PropertyException
261 |  |
        {
262 | 2168 |
           if (instance instanceof Object[])
263 | 2 |
              return new ArrayIterator((Object[]) instance);
264 | 2166 |
           else if (instance.getClass().isArray())
265 | 0 |
              return new PrimitiveArrayIterator(instance);
266 | 2166 |
           else if (instance instanceof Iterator)
267 | 6 |
              return (Iterator) instance;
268 | 2160 |
           else if (instance instanceof Enumeration)
269 | 2070 |
              return new EnumIterator((Enumeration) instance);
270 |  |
           else
271 | 90 |
              return getOperator(instance).findIterator(instance);
272 |  |
        }
273 |  |
274 |  |
     }
275 |  |
276 |  |
277 |  |
     /**
278 |  |
      * This class knows how to extract properties from objects efficiently.
279 |  |
      * <p>
280 |  |
      * A simple property "Bar" can be accessed in a sub-object "Foo" (and
281 |  |
      * in one case, sub-object "Baz") using any of the following method
282 |  |
      * signatures, listed in the order that they will be tried:
283 |  |
      * <ul>
284 |  |
      * <li>Foo.Bar
285 |  |
      * <li>Foo.getBar() / Foo.setBar(object)
286 |  |
      * <li>Foo.getBar("Baz") / Foo.setBar("Baz", object)
287 |  |
      * <li>Foo.get("Bar") / Foo.put("Bar",object)
288 |  |
      * </ul>
289 |  |
      * <p>
290 |  |
      * The PropertyOperator is capable of extracting an iterator from an object
291 |  |
      * if in any of the following conditions are true, listed in the order that
292 |  |
      * they will be tried:
293 |  |
      * <ul>
294 |  |
      * <li>The object itself is an array
295 |  |
      * <li>The object itself is an Iterator
296 |  |
      * <li>The object itself is an Enumeration
297 |  |
      * <li>The object has an "Iterator iterator()" method
298 |  |
      * <li>The object has an "Enumeration elements()" method
299 |  |
      * </ul>
300 |  |
      * <p>
301 |  |
      * You can specify a long list of property names, and the above methods
302 |  |
      * will be recursively applied. For example, if your list were
303 |  |
      * Order,Customer,Fred,Name,Last the PropertyOperator might be
304 |  |
      * able to access it as:<pre>
305 |  |
      *     Order.getCustomer("Fred").getName().Last
306 |  |
      * </pre>
307 |  |
      */
308 |  |
309 |  |
     final class PropertyOperator
310 |  |
     {
311 |  |
312 |  |
        /**
313 |  |
         * My accessors for fields, and binary methods.
314 |  |
         */
315 | 540 |
        final private HashMap _unaryAccessors = new HashMap();
316 |  |
317 |  |
        /**
318 |  |
         * Accessors that require an additional property name.
319 |  |
         */
320 | 540 |
        final private HashMap _binaryAccessors = new HashMap();
321 |  |
322 |  |
        /**
323 |  |
         * Accessors for direct method calls.
324 |  |
         */
325 | 540 |
        final private HashMap _directAccessors = new HashMap();
326 |  |
327 |  |
        /**
328 |  |
         * Hash table accessor.
329 |  |
         */
330 |  |
        private BinaryMethodAccessor _hashAccessor;
331 |  |
332 |  |
        /**
333 |  |
         * The iterator method we found.
334 |  |
         */
335 | 540 |
        private Method iteratorMethod = null;
336 |  |
337 |  |
        /**
338 |  |
         * An accessor for array lengths.
339 |  |
         */
340 | 2 |
        private static LengthAccessor _lengthAccessor = new LengthAccessor();
341 |  |
342 |  |
        /**
343 |  |
         * The property operator cache.
344 |  |
         */
345 |  |
        final private PropertyOperatorCache _cache;
346 |  |
347 |  |
        /**
348 |  |
         * Get the public methods for the named class. The vector meths
349 |  |
         * will be populated by a list of all the methods. Note that a method
350 |  |
         * may appear more than once in the vector if it is declared in more
351 |  |
         * than one superclass or interface.
352 |  |
         * @exception SecurityException
353 |  |
         */
354 |  |
        private void getAllMethods(HashMap meths, Class c)
355 |  |
        {
356 | 3182 |
           if (Modifier.isPublic(c.getModifiers()))
357 |  |
           {
358 | 2956 |
              Method m[] = c.getDeclaredMethods();
359 | 68854 |
              for (int i = 0; i < m.length; i++)
360 |  |
              {
361 | 65898 |
                 if (Modifier.isPublic(m[i].getModifiers()))
362 |  |
                 {
363 | 49920 |
                    addMethod(meths, m[i]);
364 |  |
                 }
365 |  |
              }
366 |  |
           }
367 | 3182 |
           Class iface[] = c.getInterfaces();
368 | 4350 |
           for (int i = 0; i < iface.length; i++)
369 |  |
           {
370 | 1168 |
              getAllMethods(meths, iface[i]);
371 |  |
           }
372 |  |
373 | 3182 |
           Class sup = c.getSuperclass();
374 | 3182 |
           if (sup != null)
375 |  |
           {
376 | 1474 |
              getAllMethods(meths, sup);
377 |  |
           }
378 | 3182 |
        }
379 |  |
380 |  |
        /**
381 |  |
         * The lhs precedes the rhs if it has fewer parameters. If the lhs
382 |  |
         * has the same number of parameters then the lhs precedes the rhs
383 |  |
         * if it can be used anywhere the rhs can be used--meaning that for
384 |  |
         * each and every term, the lhs is the same or more specific than
385 |  |
         * the rhs. If they have the same number of parameters but are not
386 |  |
         * related at all, put the lhs later.
387 |  |
         */
388 |  |
        private int precedes(Class[] lhs, Class[] rhs)
389 |  |
        {
390 |  |
391 | 26432 |
           if (lhs.length == rhs.length)
392 |  |
           {
393 | 31152 |
              for (int i = 0; i < lhs.length; i++)
394 |  |
              {
395 |  |
396 | 13866 |
                 if (!rhs[i].equals(lhs[i]))
397 |  |
                 {
398 |  |
399 | 1692 |
                    if (lhs[i].isAssignableFrom(rhs[i]))
400 |  |
                    {
401 |  |
                       // rhs is more specific than lhs
402 | 60 |
                       return 1;
403 |  |
                    }
404 |  |
405 | 1632 |
                    if (rhs[i].isAssignableFrom(lhs[i]))
406 |  |
                    {
407 |  |
                       // lhs is more specific than rhs
408 | 4 |
                       return -1;
409 |  |
                    }
410 |  |
411 |  |
                    // not related by inheritance, put lhs later on
412 | 1628 |
                    return 1;
413 |  |
414 |  |
                 }
415 |  |
              }
416 | 17286 |
              return 0; // all the same
417 |  |
           }
418 |  |
           else
419 |  |
           {
420 | 7454 |
              return (lhs.length < rhs.length) ? -1 : 1;
421 |  |
           }
422 |  |
        }
423 |  |
424 |  |
        boolean isMethodAllowed(Method m)
425 |  |
        {
426 | 82242 |
           Class c = m.getDeclaringClass();
427 | 82242 |
           Map restrictedClasses = _cache.getRestrictedClassMap();
428 | 82242 |
           if (restrictedClasses.containsKey(c))
429 |  |
           {
430 |  |
              // this class is restricted, check if method is allowed
431 | 372 |
              List okMeths = (List) restrictedClasses.get(c);
432 | 372 |
              return (okMeths != null && okMeths.contains(m.getName()));
433 |  |
           }
434 | 81870 |
           return true;
435 |  |
        }
436 |  |
437 |  |
        boolean isMethodRestricted(Class c, String name)
438 |  |
        {
439 |  |
           // check if object is restricted
440 | 6 |
           Map restrictedClassMap = _cache.getRestrictedClassMap();
441 | 6 |
           if (!restrictedClassMap.containsKey(c))
442 |  |
           {
443 | 6 |
              return false; // there are no restrictions on this class
444 |  |
           }
445 |  |
           else
446 |  |
           {
447 |  |
              // restricted class, check method
448 | 0 |
              Method[] meths = c.getMethods();
449 | 0 |
              for (int i = 0; i < meths.length; i++)
450 |  |
              {
451 | 0 |
                 if (meths[i].getName().equals(name))
452 |  |
                 {
453 |  |
                    // check if method is explicitly allowed
454 | 0 |
                    List l = (List) restrictedClassMap.get(c);
455 | 0 |
                    if (l != null)
456 | 0 |
                       return !l.contains(name);
457 |  |
                 }
458 |  |
              }
459 |  |
           }
460 | 0 |
           return false;
461 |  |
        }
462 |  |
463 |  |
        private void addMethod(HashMap hm, Method m)
464 |  |
        {
465 | 49920 |
           if (!isMethodAllowed(m)) return;
466 | 49608 |
           String name = m.getName();
467 | 49608 |
           Object o = hm.get(name);
468 | 49608 |
           if (o == null)
469 |  |
           {
470 | 28414 |
              hm.put(name, m);
471 | 28414 |
              return;
472 |  |
           }
473 |  |
474 |  |
           Vector v;
475 | 21194 |
           if (o instanceof Method)
476 |  |
           {
477 | 14502 |
              v = new Vector();
478 | 14502 |
              v.addElement(o);
479 | 14502 |
              hm.put(name, v);
480 |  |
           }
481 |  |
           else
482 |  |
           {
483 | 6692 |
              v = (Vector) o;
484 |  |
           }
485 |  |
486 | 21194 |
           Class ptypes[] = m.getParameterTypes();
487 | 28750 |
           for (int i = 0; i < v.size(); i++)
488 |  |
           {
489 | 26432 |
              Class curTypes[] = ((Method) v.elementAt(i)).getParameterTypes();
490 |  |
491 | 26432 |
              int order = precedes(ptypes, curTypes);
492 |  |
493 | 26432 |
              if (order < 0)
494 |  |
              {
495 | 1590 |
                 v.insertElementAt(m, i);
496 | 1590 |
                 return;
497 |  |
              }
498 | 24842 |
              else if (order == 0)
499 |  |
              {
500 |  |
                 // ignore duplicate method
501 | 17286 |
                 return;
502 |  |
              }
503 |  |
           }
504 | 2318 |
           v.addElement(m);
505 | 2318 |
        }
506 |  |
507 |  |
        /**
508 |  |
         * Get all the public methods of the supplied class. They will be
509 |  |
         * returned in arbitrary alphabetical order, but where there are
510 |  |
         * multiple methods with the same name, they will be returned in
511 |  |
         * order of precedence: least arguments first, and most specific
512 |  |
         * arguments before less specific arguments. See precedes().
513 |  |
         */
514 |  |
        private Vector getMethods(Class c)
515 |  |
        {
516 | 540 |
           Vector v = new Vector();
517 | 540 |
           HashMap h = new HashMap();
518 | 540 |
           getAllMethods(h, c);
519 | 540 |
           Iterator iter = h.values().iterator();
520 | 28954 |
           while (iter.hasNext())
521 |  |
           {
522 | 28414 |
              Object elem = iter.next();
523 |  |
524 | 28414 |
              if (elem instanceof Method)
525 |  |
              {
526 | 13912 |
                 v.addElement(elem);
527 |  |
              }
528 |  |
              else
529 |  |
              {
530 | 14502 |
                 Vector v1 = (Vector) elem;
531 | 32912 |
                 for (int i = 0; i < v1.size(); i++)
532 |  |
                 {
533 | 18410 |
                    v.addElement(v1.elementAt(i));
534 |  |
                 }
535 |  |
              }
536 | 28414 |
           }
537 | 540 |
           return v;
538 |  |
        }
539 |  |
540 |  |
541 |  |
        /**
542 |  |
         * Construct a property operator for the target class.
543 |  |
         * @exception SecurityException
544 |  |
         */
545 |  |
        public PropertyOperator(final Class target, PropertyOperatorCache cache)
546 |  |
            throws PropertyException
547 | 540 |
        {
548 |  |
549 |  |
           Accessor acc;
550 |  |
551 |  |
           // Save the cache
552 | 540 |
           _cache = cache;
553 |  |
554 |  |
           // introspect fields first
555 |  |
556 | 540 |
           Field[] fields = target.getFields();
557 | 892 |
           for (int i = 0; i < fields.length; i++)
558 |  |
           {
559 | 352 |
              if (Modifier.isPublic(fields[i].getModifiers()))
560 |  |
              {
561 | 352 |
                 acc = new FieldAccessor(fields[i]);
562 | 352 |
                 _unaryAccessors.put(acc.getName(), acc);
563 |  |
              }
564 |  |
           }
565 | 540 |
           if (target.isArray())
566 | 0 |
              _unaryAccessors.put("length", _lengthAccessor);
567 |  |
568 |  |
           // introspect methods second
569 |  |
570 | 540 |
           Vector methods = getMethods(target);
571 |  |
572 |  |
           Method meth;
573 |  |
           Class[] params;
574 |  |
           String name,propName;
575 |  |
576 | 32862 |
           for (int i = 0; i < methods.size(); i++)
577 |  |
           {
578 | 32322 |
              meth = ((Method) methods.elementAt(i));
579 | 32322 |
              if (!isMethodAllowed(meth)) continue;
580 | 32322 |
              name = meth.getName();
581 | 32322 |
              params = meth.getParameterTypes();
582 | 32322 |
              int plength = params.length;
583 |  |
584 |  |
              // add direct accessor
585 | 32322 |
              acc = (Accessor) _directAccessors.get(name);
586 | 32322 |
              if (acc != null)
587 |  |
              {
588 | 3908 |
                 ((DirectAccessor) acc).addMethod(meth, params);
589 |  |
              }
590 |  |
              else
591 |  |
              {
592 | 28414 |
                 acc = new DirectAccessor(name, meth, params);
593 | 28414 |
                 _directAccessors.put(name, acc);
594 |  |
              }
595 |  |
596 |  |
              // check for get/set/put method
597 | 32322 |
              if ((name.startsWith("get") ||
598 |  |
              name.startsWith("set")) || name.equals("put"))
599 |  |
              {
600 |  |
601 | 14628 |
                 propName = name.substring(3);
602 |  |
603 | 14628 |
                 if (((plength == 0) && name.startsWith("get")) ||
604 |  |
                 ((plength == 1) && name.startsWith("set")))
605 |  |
                 {
606 |  |
607 |  |
                    // unary get/set method
608 | 11940 |
                    acc = (Accessor) _unaryAccessors.get(propName);
609 | 11940 |
                    if (acc != null)
610 |  |
                    {
611 | 1738 |
                       if (acc instanceof MethodAccessor)
612 |  |
                       {
613 | 1738 |
                          ((MethodAccessor) acc).addMethod(meth, params);
614 |  |
                       }
615 |  |
                    }
616 |  |
                    else
617 |  |
                    {
618 | 10202 |
                       acc = new UnaryMethodAccessor(propName, meth, params);
619 | 10202 |
                       _unaryAccessors.put(propName, acc);
620 |  |
                    }
621 |  |
                 }
622 | 2688 |
                 else if ((plength > 0)
623 |  |
                 && (params[0].isInstance("string") &&
624 |  |
                 (((plength == 2) && name.equals("put"))
625 |  |
                 || ((plength == 1) && name.equals("get")))))
626 |  |
                 {
627 |  |
                    // hashtable get/put
628 | 38 |
                    if (_hashAccessor != null)
629 |  |
                    {
630 | 12 |
                       _hashAccessor.addMethod(meth, params);
631 |  |
                    }
632 |  |
                    else
633 |  |
                    {
634 | 26 |
                       _hashAccessor = new BinaryMethodAccessor(propName, meth, params);
635 |  |
                    }
636 |  |
                 }
637 | 2650 |
                 else if ((plength > 0) && (params[0].isInstance("string")) &&
638 |  |
                 (((plength == 1) && name.startsWith("get")) ||
639 |  |
                 ((plength == 2) && name.startsWith("set"))))
640 |  |
                 {
641 |  |
                    // binary get/set method
642 | 498 |
                    acc = (Accessor) _binaryAccessors.get(propName);
643 | 498 |
                    if (acc != null)
644 |  |
                    {
645 | 132 |
                       ((MethodAccessor) acc).addMethod(meth, params);
646 |  |
                    }
647 |  |
                    else
648 |  |
                    {
649 | 366 |
                       acc = new BinaryMethodAccessor(propName, meth, params);
650 | 366 |
                       _binaryAccessors.put(propName, acc);
651 |  |
                    }
652 |  |
                 }
653 |  |
              }
654 | 17694 |
              else if (name.startsWith("is") && meth.getReturnType() == java.lang.Boolean.TYPE)
655 |  |
              {
656 | 582 |
                 if (plength == 0)
657 |  |
                 {
658 |  |
                    // unary accessor method
659 | 550 |
                    propName = name.substring(2);
660 | 550 |
                    acc = (Accessor) _unaryAccessors.get(propName);
661 | 550 |
                    if (acc != null)
662 |  |
                    {
663 | 42 |
                       if (acc instanceof MethodAccessor)
664 |  |
                       {
665 | 42 |
                          ((MethodAccessor) acc).addMethod(meth, params);
666 |  |
                       }
667 |  |
                    }
668 |  |
                    else
669 |  |
                    {
670 | 508 |
                       acc = new UnaryMethodAccessor(propName, meth, params);
671 | 508 |
                       _unaryAccessors.put(propName, acc);
672 |  |
                    }
673 |  |
                 }
674 |  |
              }
675 | 17112 |
              else if (name.equals("elements") ||
676 |  |
              name.equals("enumeration") ||
677 |  |
              name.equals("iterator") ||
678 |  |
              name.equals("toArray"))
679 |  |
              {
680 | 20 |
                 if (params.length == 0)
681 |  |
                 {
682 | 16 |
                    Class returnType = meth.getReturnType();
683 |  |
684 |  |
                    // iterator supercedes enumeration supercedes Object[]
685 | 16 |
                    Class iterClass = Iterator.class;
686 | 16 |
                    boolean iterA = iterClass.isAssignableFrom(returnType);
687 | 16 |
                    if (
688 |  |
                    iterA ||
689 |  |
                    (((iteratorMethod == null) ||
690 |  |
                    iteratorMethod.getName().equals("toArray")) &&
691 |  |
                    Object[].class.isAssignableFrom(returnType) ||
692 |  |
                    Enumeration.class.isAssignableFrom(returnType)))
693 |  |
                    {
694 | 12 |
                       iteratorMethod = meth;
695 |  |
                    }
696 |  |
697 |  |
                 }
698 |  |
              }
699 |  |
           }
700 | 540 |
        }
701 |  |
702 |  |
703 |  |
        /**
704 |  |
         * Locate the requested property as follows: beginning from instance,
705 |  |
         * look at names[start] and resolve it, and continue resolving names
706 |  |
         * recursively until names[end] has been resolved. Return that.
707 |  |
         * @param instance object to start from
708 |  |
         * @param names the property names we are searching for
709 |  |
         * @param start which name to look for first
710 |  |
         * @param end which name to look for lst
711 |  |
         * @exception PropertyException error resolving a name
712 |  |
         * @return the property requested
713 |  |
         *
714 |  |
         */
715 |  |
        public Object getProperty(final Context context,
716 |  |
                                  final Object instance,
717 |  |
                                  final Object[] names,
718 |  |
                                  int start, int end)
719 |  |
            throws PropertyException
720 |  |
        {
721 |  |
           String propName;
722 | 133258 |
           Object nextPropValue = null;
723 | 133258 |
           Accessor acc = null;
724 |  |
725 | 133258 |
           if (names[start] instanceof String)
726 |  |
           {
727 | 52468 |
              propName = (String) names[start];
728 |  |
           }
729 | 80790 |
           else if (names[start] instanceof PropertyMethod)
730 |  |
           {
731 | 80790 |
              PropertyMethod pm = (PropertyMethod) names[start];
732 | 80790 |
              propName = pm.getName();
733 | 80790 |
              acc = (Accessor) _directAccessors.get(propName);
734 | 80790 |
              Object[] args = pm.getArguments(context);
735 | 80770 |
              if (acc == null)
736 |  |
              {
737 | 2 |
                 if (isMethodRestricted(instance.getClass(), propName))
738 | 0 |
                    throw new PropertyException.RestrictedMethodException(
739 |  |
                    pm.toString(),
740 |  |
                    fillInName(names, start),
741 |  |
                    instance.getClass().getName());
742 | 2 |
                 throw new PropertyException.NoSuchMethodException(
743 |  |
                 pm.toString(),
744 |  |
                 fillInName(names, start),
745 |  |
                 instance.getClass().getName());
746 |  |
              }
747 |  |
              try
748 |  |
              {
749 | 80768 |
                 nextPropValue = acc.get(instance, args);
750 | 80748 |
                 start++;
751 |  |
              }
752 | 0 |
              catch (NoSuchMethodException e)
753 |  |
              {
754 | 0 |
                 throw new PropertyException.NoSuchMethodException(
755 |  |
                 pm.toString(),
756 |  |
                 fillInName(names, start),
757 |  |
                 instance.getClass().getName());
758 | 80748 |
              }
759 |  |
760 | 80748 |
           }
761 |  |
           else
762 |  |
           {
763 | 0 |
              propName = names[start].toString();
764 |  |
           }
765 |  |
766 |  |
           // unary?
767 | 133216 |
           if (acc == null)
768 |  |
           {
769 | 52468 |
              acc = (Accessor) _unaryAccessors.get(propName);
770 | 52468 |
              if (acc != null)
771 |  |
              {
772 |  |
                 try
773 |  |
                 {
774 | 51296 |
                    nextPropValue = acc.get(instance);
775 | 51296 |
                    start++;
776 |  |
                 }
777 | 0 |
                 catch (NoSuchMethodException e)
778 |  |
                 {
779 | 0 |
                    acc = null;
780 | 51296 |
                 }
781 |  |
              }
782 |  |
           }
783 |  |
784 |  |
           // binary?
785 | 133216 |
           if (acc == null)
786 |  |
           {
787 | 1172 |
              acc = (Accessor) _binaryAccessors.get(propName);
788 |  |
              //            if ((acc != null) && ((start + 1) <= end))
789 | 1172 |
              if ((acc != null) && ((start + 1) <= names.length))
790 |  |
              {
791 |  |
                 try
792 |  |
                 {
793 | 0 |
                    nextPropValue = acc.get(instance, (String) names[start + 1]);
794 | 0 |
                    start += 2;
795 |  |
                 }
796 | 0 |
                 catch (NoSuchMethodException e)
797 |  |
                 {
798 | 0 |
                    acc = null;
799 |  |
                 }
800 | 0 |
                 catch (ClassCastException e)
801 |  |
                 {
802 |  |
                    // names[start + 1] was not a String, just move on
803 |  |
                    // this catch is more efficient than using instanceof
804 |  |
                    // since 90% of the time it really will be a string
805 | 0 |
                    acc = null;
806 | 0 |
                 }
807 |  |
              }
808 |  |
              else
809 |  |
              {
810 | 1172 |
                 acc = null;
811 |  |
              }
812 |  |
           }
813 |  |
814 |  |
           // hash?
815 | 133216 |
           if (acc == null)
816 |  |
           {
817 | 1172 |
              acc = _hashAccessor;
818 |  |
              try
819 |  |
              {
820 | 1172 |
                 if (acc != null)
821 |  |
                 {
822 | 1170 |
                    nextPropValue = acc.get(instance, propName);
823 | 1170 |
                    start++;
824 |  |
                 }
825 |  |
              }
826 | 0 |
              catch (NoSuchMethodException e)
827 |  |
              {
828 | 0 |
                 acc = null;
829 | 1172 |
              }
830 |  |
           }
831 |  |
832 | 133216 |
           if (acc == null)
833 |  |
           {
834 |  |
              // user tried to access a property of a property that doesn't exist
835 |  |
              // ex:  $TestObject.FirstName.NotThere
836 |  |
837 |  |
              // check if object is restricted
838 | 2 |
              if (isMethodRestricted(instance.getClass(), "get" + propName)
839 |  |
              || isMethodRestricted(instance.getClass(), "set" + propName))
840 | 0 |
                 throw new PropertyException.RestrictedPropertyException(
841 |  |
                 propName, fillInName(names, start),
842 |  |
                 instance.getClass().getName());
843 | 2 |
              throw new PropertyException.NoSuchPropertyException(
844 |  |
              propName, fillInName(names, start),
845 |  |
              instance.getClass().getName());
846 |  |
           }
847 |  |
848 | 133214 |
           if (start <= end)
849 |  |
           {
850 |  |
              try
851 |  |
              {
852 | 26466 |
                 return _cache.getOperator(nextPropValue)
853 |  |
                 .getProperty(context, nextPropValue, names, start, end);
854 |  |
              }
855 | 0 |
              catch (NullPointerException e)
856 |  |
              {
857 |  |
                 // $Foo.getNull().SomeProperty is what makes this happen
858 | 0 |
                 throw new PropertyException("$" + fillInName(names, start) + " is null.  Cannot access ." + names[end]);
859 |  |
              }
860 |  |
           }
861 |  |
           else
862 |  |
           {
863 | 106748 |
              return nextPropValue;
864 |  |
           }
865 |  |
        }
866 |  |
867 |  |
        /**
868 |  |
         * given an object[] of names, append them together up to index
869 |  |
         * <code>end</code> in the form of
870 |  |
         * <code>Name1.Name2.Name3.NameN</code>
871 |  |
         */
872 |  |
        private static final String fillInName(Object[] names, int end)
873 |  |
        {
874 | 4 |
           StringBuffer sb = new StringBuffer();
875 | 8 |
           for (int x = 0; x < end; x++)
876 |  |
           {
877 | 4 |
              if (x > 0)
878 | 0 |
                 sb.append(".");
879 | 4 |
              sb.append(names[x]);
880 |  |
           }
881 |  |
882 | 4 |
           return sb.toString();
883 |  |
        }
884 |  |
885 |  |
        /**
886 |  |
         * This method behaves a lot like getProperty, but it's tricker. It
887 |  |
         * first tries to resolve the property using a direct method, then
888 |  |
         * it falls back and tries a binary approach last. In order to do
889 |  |
         * this it has to recurse into the direct approach, then detect if
890 |  |
         * that failed and try the binary approach. It relies on getProperty
891 |  |
         * for navigation, which is why getProperty takes start/end args.
892 |  |
         * @param instance the object to start from
893 |  |
         * @param names path to a property we would like to set
894 |  |
         * @param value the value we'd like to set it to
895 |  |
         * @param pos   we could set names[pos] from here
896 |  |
         * @return true if we succeeded in setting, false otherwise
897 |  |
         */
898 |  |
        public boolean setProperty(Context context,
899 |  |
                                   Object instance,
900 |  |
                                   Object[] names,
901 |  |
                                   Object value,
902 |  |
                                   int pos)
903 |  |
            throws PropertyException, NoSuchMethodException
904 |  |
        {
905 |  |
           // names[pos] is what we could set from here
906 |  |
907 | 192 |
           int binPos = names.length - 2;
908 |  |
909 |  |
           // if we're not yet at the binary-settable parent, go there
910 | 192 |
           if (pos < binPos)
911 |  |
           {
912 | 0 |
              Object grandparent = getProperty(context, instance, names,
913 |  |
              pos, binPos - 1);
914 | 0 |
              PropertyOperator po = _cache.getOperator(grandparent);
915 | 0 |
              return po.setProperty(context, grandparent, names, value, binPos);
916 |  |
           }
917 |  |
918 |  |
           // if we're at the binary-settable parent, try direct first
919 | 192 |
           if (pos == binPos)
920 |  |
           {
921 |  |
922 |  |
              // try direct -- move to direct parent and try from there
923 | 0 |
              Object parent = null;
924 |  |
              try
925 |  |
              {
926 | 0 |
                 parent = getProperty(context, instance, names, pos, pos);
927 | 0 |
                 if (parent != null)
928 |  |
                 {
929 | 0 |
                    PropertyOperator po = _cache.getOperator(parent);
930 | 0 |
                    if (po.setProperty(context, parent, names, value, pos + 1))
931 |  |
                    {
932 | 0 |
                       return true;
933 |  |
                    }
934 |  |
                 }
935 |  |
              }
936 | 0 |
              catch (NoSuchMethodException e)
937 |  |
              {
938 |  |
                 // oh well, keep trying: XXX this makes binOp expensive
939 | 0 |
              }
940 |  |
941 |  |
              // if direct failed, try binary
942 | 0 |
              Accessor binOp = (Accessor) _binaryAccessors.get(names[pos]);
943 | 0 |
              if (binOp != null)
944 |  |
              {
945 |  |
                 try
946 |  |
                 {
947 | 0 |
                    return binOp.set(instance, (String) names[pos + 1], value);
948 |  |
                 }
949 | 0 |
                 catch (ClassCastException e)
950 |  |
                 {
951 |  |
                    // names[pos+1] was not a string, just move on
952 | 0 |
                    return false;
953 |  |
                 }
954 | 0 |
                 catch (NoSuchMethodException e)
955 |  |
                 {
956 | 0 |
                    return false;
957 |  |
                 }
958 |  |
              }
959 |  |
960 | 0 |
              return false;
961 |  |
           }
962 |  |
963 |  |
           // we're the direct parent, use unaryOp or hash method
964 | 192 |
           Accessor unaryOp = (Accessor) _unaryAccessors.get(names[pos]);
965 |  |
           try
966 |  |
           {
967 | 192 |
              if ((unaryOp != null) && unaryOp.set(instance, value))
968 |  |
              {
969 | 192 |
                 return true;
970 |  |
              }
971 | 0 |
              if (_hashAccessor != null)
972 |  |
              {
973 | 0 |
                 return _hashAccessor.set(instance, (String) names[pos], value);
974 |  |
              }
975 |  |
           }
976 | 0 |
           catch (NoSuchMethodException e)
977 |  |
           {
978 |  |
              // fall through
979 |  |
           }
980 | 0 |
           catch (ClassCastException e)
981 |  |
           {
982 |  |
              // names[pos] was not a string, fall through
983 | 0 |
           }
984 | 0 |
           return false;
985 |  |
        }
986 |  |
987 |  |
        /**
988 |  |
         * Introspect the current object and return an Iterator representation
989 |  |
         * of it, if possible.
990 |  |
         * @param instance the object we think contains a list
991 |  |
         * @exception PropertyException current object is not any sort of list
992 |  |
         * @return an iterator representing the current object's list
993 |  |
         */
994 |  |
        public Iterator findIterator(Object instance)
995 |  |
            throws PropertyException
996 |  |
        {
997 | 90 |
           if (iteratorMethod != null)
998 |  |
           {
999 | 86 |
              Object ret = invoke(iteratorMethod, instance, null);
1000 | 86 |
              if (ret instanceof Iterator)
1001 |  |
              {
1002 | 86 |
                 return (Iterator) ret;
1003 |  |
              }
1004 | 0 |
              else if (ret instanceof Enumeration)
1005 |  |
              {
1006 | 0 |
                 return new EnumIterator((Enumeration) ret);
1007 |  |
              }
1008 | 0 |
              else if (ret instanceof Object[])
1009 |  |
              {
1010 | 0 |
                 return new ArrayIterator((Object[]) ret);
1011 |  |
              }
1012 |  |
           }
1013 | 4 |
           throw new PropertyException((instance == null ? "null" : instance.getClass().getName()) + " is not a list");
1014 |  |
        }
1015 |  |
1016 |  |
        /**
1017 |  |
         * Invoke a method on an instance, with arguments--generate
1018 |  |
         * PropertyException rather than the default Java exceptions.
1019 |  |
         * @param meth the method to invoke
1020 |  |
         * @param instance the object to invoke it on
1021 |  |
         * @param args arguments for the method
1022 |  |
         * @return return value of the method
1023 |  |
         */
1024 |  |
        static Object invoke(Method meth, Object instance, Object[] args)
1025 |  |
            throws PropertyException
1026 |  |
        {
1027 |  |
           try
1028 |  |
           {
1029 | 133512 |
              Object obj = meth.invoke(instance, args);
1030 |  |
1031 |  |
              // if the method's return type is void return the VoidMacro
1032 |  |
              // instance, instead of the 'null' we used to return here
1033 |  |
              // otherwise, just return whatever the method returned
1034 | 133492 |
              if (obj == null
1035 |  |
              && meth.getReturnType() == java.lang.Void.TYPE)
1036 | 192 |
                 return org.webmacro.engine.VoidMacro.instance;
1037 |  |
              else
1038 | 133300 |
                 return obj;
1039 |  |
1040 |  |
           }
1041 | 0 |
           catch (IllegalAccessException e)
1042 |  |
           {
1043 | 0 |
              throw new PropertyException(
1044 |  |
              "You don't have permission to access the requested method (" +
1045 |  |
              meth + " in class " + instance.getClass() +
1046 |  |
              " on object " + instance + "). Private/protected/package access " +
1047 |  |
              " values cannot be accessed via property introspection.", e);
1048 |  |
           }
1049 | 0 |
           catch (IllegalArgumentException e)
1050 |  |
           {
1051 | 0 |
              throw new PropertyException(
1052 |  |
              "Some kind of error occurred processing your request: this " +
1053 |  |
              "indicates a failure in PropertyOperator.java that should be " +
1054 |  |
              "reported: attempt to access method " + meth + " on object " +
1055 |  |
              instance + " with " + args.length + " parameters " +
1056 |  |
              " threw an exception: " + e, e);
1057 |  |
           }
1058 | 20 |
           catch (InvocationTargetException e)
1059 |  |
           {
1060 |  |
              // if this is a wrapped UndefinedVariableException, unwrap and rethrow it
1061 | 20 |
              if (e.getTargetException() instanceof PropertyException.UndefinedVariableException)
1062 | 0 |
                 throw (PropertyException) e.getTargetException();
1063 | 20 |
              throw new PropertyException(
1064 |  |
              "Attempt to invoke method " + meth + " on object "
1065 |  |
              + instance.getClass().getName() +
1066 |  |
              " raised an exception: " + e.getTargetException().getClass().getName(),
1067 |  |
              e.getTargetException());
1068 |  |
           }
1069 | 0 |
           catch (NullPointerException e)
1070 |  |
           {
1071 | 0 |
              throw new PropertyException(
1072 |  |
              "NullPointerException thrown from method " + meth +
1073 |  |
              " on object " + instance + " -- most likely you have attempted " +
1074 |  |
              "to use an undefined value, or a failure in that method.", e);
1075 |  |
           }
1076 |  |
        }
1077 |  |
1078 |  |
     }
1079 |  |
1080 |  |
1081 |  |
     // helper classes
1082 |  |
1083 |  |
1084 |  |
     /**
1085 |  |
      * An accessor represents one particular operation that can be
1086 |  |
      * performed on one particular class: getting/setting a field, or
1087 |  |
      * getting/setting via a method.
1088 |  |
      */
1089 |  |
     abstract class Accessor
1090 |  |
     {
1091 |  |
1092 |  |
        private String _name;
1093 |  |
1094 |  |
        Accessor(String name)
1095 | 39870 |
        {
1096 | 39870 |
           _name = name;
1097 | 39870 |
        }
1098 |  |
1099 |  |
        final String getName()
1100 |  |
        {
1101 | 352 |
           return _name;
1102 |  |
        }
1103 |  |
1104 |  |
        public final String toString()
1105 |  |
        {
1106 | 0 |
           return "Accessor:" + _name;
1107 |  |
        }
1108 |  |
1109 |  |
        /**
1110 |  |
         * Unary get
1111 |  |
         */
1112 |  |
        Object get(Object instance)
1113 |  |
            throws PropertyException, NoSuchMethodException
1114 |  |
        {
1115 | 0 |
           throw new PropertyException("BUG in PropertyOperator.java!");
1116 |  |
        }
1117 |  |
1118 |  |
        /**
1119 |  |
         * Unary set
1120 |  |
         */
1121 |  |
        boolean set(Object instance, Object value)
1122 |  |
            throws PropertyException, NoSuchMethodException
1123 |  |
        {
1124 | 0 |
           throw new PropertyException("BUG in PropertyOperator.java!");
1125 |  |
        }
1126 |  |
1127 |  |
        /**
1128 |  |
         * Binary get.
1129 |  |
         */
1130 |  |
        Object get(Object instance, String subName)
1131 |  |
            throws PropertyException, NoSuchMethodException
1132 |  |
        {
1133 | 0 |
           throw new PropertyException("BUG in PropertyOperator.java!");
1134 |  |
        }
1135 |  |
1136 |  |
        /**
1137 |  |
         * Binary.
1138 |  |
         */
1139 |  |
        boolean set(Object instance, String subName, Object value)
1140 |  |
            throws PropertyException, NoSuchMethodException
1141 |  |
        {
1142 | 0 |
           throw new PropertyException("BUG in PropertyOperator.java!");
1143 |  |
        }
1144 |  |
1145 |  |
1146 |  |
        /**
1147 |  |
         * Direct get
1148 |  |
         */
1149 |  |
        Object get(Object instance, Object[] args)
1150 |  |
            throws PropertyException, NoSuchMethodException
1151 |  |
        {
1152 | 0 |
           throw new PropertyException("BUG in PropertyOperator.java!");
1153 |  |
        }
1154 |  |
1155 |  |
     }
1156 |  |
1157 |  |
1158 |  |
     /**
1159 |  |
      * An accessor that knows how to get/set from a field
1160 |  |
      */
1161 |  |
     final class FieldAccessor extends Accessor
1162 |  |
     {
1163 |  |
1164 |  |
        private Field _field;
1165 |  |
1166 |  |
        FieldAccessor(final Field f)
1167 |  |
        {
1168 | 352 |
           super(f.getName());
1169 | 352 |
           _field = f;
1170 | 352 |
        }
1171 |  |
1172 |  |
        final Object get(final Object instance)
1173 |  |
            throws PropertyException
1174 |  |
        {
1175 |  |
           try
1176 |  |
           {
1177 | 0 |
              return _field.get(instance);
1178 |  |
           }
1179 | 0 |
           catch (Exception e)
1180 |  |
           {
1181 | 0 |
              throw new PropertyException("Unable to read field " + _field +
1182 |  |
              " on object " + instance + " of " + instance.getClass(),
1183 |  |
              e);
1184 |  |
           }
1185 |  |
        }
1186 |  |
1187 |  |
        final boolean set(final Object instance, final Object value)
1188 |  |
            throws PropertyException
1189 |  |
        {
1190 |  |
           try
1191 |  |
           {
1192 | 0 |
              _field.set(instance, value);
1193 |  |
           }
1194 | 0 |
           catch (Exception e)
1195 |  |
           {
1196 | 0 |
              throw new PropertyException("Unable to write field " + _field +
1197 |  |
              " on object " + instance + " of " + instance.getClass(),
1198 |  |
              e);
1199 | 0 |
           }
1200 | 0 |
           return true;
1201 |  |
        }
1202 |  |
     }
1203 |  |
1204 |  |
1205 |  |
     /**
1206 |  |
      * An accessor that knows how to get/set from a field
1207 |  |
      */
1208 |  |
     final class LengthAccessor extends Accessor
1209 |  |
     {
1210 |  |
1211 |  |
        LengthAccessor()
1212 |  |
        {
1213 | 2 |
           super("length");
1214 | 2 |
        }
1215 |  |
1216 |  |
        final Object get(final Object instance)
1217 |  |
            throws PropertyException
1218 |  |
        {
1219 |  |
           try
1220 |  |
           {
1221 | 0 |
              return new Integer(java.lang.reflect.Array.getLength(instance));
1222 |  |
           }
1223 | 0 |
           catch (Exception e)
1224 |  |
           {
1225 | 0 |
              throw new PropertyException("Unable to fetch length of object "
1226 |  |
              + instance + " of " + instance.getClass(),
1227 |  |
              e);
1228 |  |
           }
1229 |  |
        }
1230 |  |
1231 |  |
        final boolean set(final Object instance, final Object value)
1232 |  |
            throws PropertyException
1233 |  |
        {
1234 | 0 |
           throw new PropertyException("Cannot set length of array");
1235 |  |
        }
1236 |  |
     }
1237 |  |
1238 |  |
1239 |  |
     /**
1240 |  |
      * accessor for direct method calls, rather than property-style
1241 |  |
      */
1242 |  |
     final class DirectAccessor extends Accessor
1243 |  |
     {
1244 |  |
1245 | 28414 |
        Vector _methods = new Vector();
1246 |  |
1247 |  |
        DirectAccessor(final String name, final Method m, final Class[] params)
1248 |  |
        {
1249 | 28414 |
           super(name);
1250 | 28414 |
           addMethod(m, params);
1251 | 28414 |
        }
1252 |  |
1253 |  |
        final void addMethod(final Method m, Class[] params)
1254 |  |
        {
1255 | 32322 |
           _methods.addElement(m);
1256 | 32322 |
        }
1257 |  |
1258 |  |
        final Object get(Object instance, Object[] args)
1259 |  |
            throws PropertyException, NoSuchMethodException
1260 |  |
        {
1261 | 80768 |
           Class[] types = new Class[args.length];
1262 | 142914 |
           for (int i = 0; i < args.length; i++)
1263 |  |
           {
1264 |  |
              try
1265 |  |
              {
1266 | 62146 |
                 types[i] = args[i].getClass();
1267 |  |
              }
1268 | 52 |
              catch (NullPointerException e)
1269 |  |
              {
1270 | 52 |
                 types[i] = null;
1271 | 62094 |
              }
1272 |  |
           }
1273 |  |
1274 | 93112 |
           for (int i = 0; i < _methods.size(); i++)
1275 |  |
           {
1276 | 93112 |
              Method m = (Method) _methods.elementAt(i);
1277 | 93112 |
              Class[] sig = m.getParameterTypes();
1278 | 93112 |
              if (IntrospectionUtils.matches(sig, types))
1279 |  |
              {
1280 | 80768 |
                 return PropertyOperator.invoke(m, instance, args);
1281 |  |
              }
1282 |  |
           }
1283 |  |
1284 |  |
           // not found
1285 |  |
1286 | 0 |
           StringBuffer arglist = new StringBuffer();
1287 | 0 |
           Method m = (Method) _methods.firstElement();
1288 | 0 |
           for (int i = 0; i < args.length; i++)
1289 |  |
           {
1290 | 0 |
              if (i > 0)
1291 |  |
              {
1292 | 0 |
                 arglist.append(", ");
1293 |  |
              }
1294 | 0 |
              arglist.append((args[i] == null) ? "null" : args[i].getClass().getName());
1295 |  |
           }
1296 |  |
1297 | 0 |
           throw new PropertyException
1298 |  |
           .NoSuchMethodWithArgumentsException(getName(),
1299 |  |
           m.getDeclaringClass().getName(),
1300 |  |
           arglist.toString());
1301 |  |
        }
1302 |  |
1303 |  |
     }
1304 |  |
1305 |  |
1306 |  |
     abstract class MethodAccessor extends Accessor
1307 |  |
     {
1308 |  |
1309 |  |
        protected Method _getMethod;           // only one get method allowed
1310 | 11102 |
        private Method[] _setMethods = null; // may be multiple set methods
1311 | 11102 |
        private Class[] _setParams = null;  // variable arg type for set meth N
1312 | 11102 |
        private Class[] _setPrimitiveType = null;
1313 | 11102 |
        private int setCount = 0;            // how many set methods do we have
1314 |  |
1315 |  |
        abstract int numArgsGet();
1316 |  |
1317 |  |
        abstract int numArgsSet();
1318 |  |
1319 |  |
        MethodAccessor(final String name, final Method m, final Class[] params)
1320 |  |
            throws PropertyException
1321 |  |
        {
1322 | 11102 |
           super(name);
1323 | 11102 |
           addMethod(m, params);
1324 | 11102 |
        }
1325 |  |
1326 |  |
        private Class getWrapperClass(Class s)
1327 |  |
        {
1328 | 218 |
           if (s == Integer.TYPE)
1329 | 118 |
              return Integer.class;
1330 | 100 |
           else if (s == Boolean.TYPE)
1331 | 92 |
              return Boolean.class;
1332 | 8 |
           else if (s == Character.TYPE)
1333 | 0 |
              return Character.class;
1334 | 8 |
           else if (s == Long.TYPE)
1335 | 8 |
              return Long.class;
1336 | 0 |
           else if (s == Short.TYPE)
1337 | 0 |
              return Short.class;
1338 | 0 |
           else if (s == Double.TYPE)
1339 | 0 |
              return Double.class;
1340 | 0 |
           else if (s == Float.TYPE)
1341 | 0 |
              return Float.class;
1342 | 0 |
           else if (s == Void.TYPE)
1343 | 0 |
              return Void.class;
1344 | 0 |
           else if (s == Byte.TYPE)
1345 | 0 |
              return Byte.class;
1346 |  |
           else
1347 | 0 |
              return null;
1348 |  |
        }
1349 |  |
1350 |  |
        final void addMethod(final Method m, Class[] params)
1351 |  |
            throws PropertyException
1352 |  |
        {
1353 |  |
1354 | 13026 |
           final int setArgsLength = numArgsSet();
1355 | 13026 |
           final int getArgsLength = numArgsGet();
1356 |  |
1357 | 13026 |
           if (params.length == getArgsLength)
1358 |  |
           {
1359 | 10780 |
              _getMethod = m;
1360 |  |
           }
1361 | 2246 |
           else if (params.length == setArgsLength)
1362 |  |
           {
1363 | 2246 |
              setCount++;
1364 | 2246 |
              if (_setMethods == null)
1365 |  |
              {
1366 | 2126 |
                 _setMethods = new Method[1];
1367 | 2126 |
                 _setParams = new Class[1];
1368 | 2126 |
                 _setPrimitiveType = new Class[1];
1369 |  |
              }
1370 | 120 |
              else if (_setMethods.length <= setCount)
1371 |  |
              {
1372 | 120 |
                 Method[] tmpMethods = new Method[(setCount + 1) * 2];
1373 | 120 |
                 Class[] tmpParams = new Class[(setCount + 1) * 2];
1374 | 120 |
                 Class[] tmpPrimitive = new Class[(setCount + 1) * 2];
1375 | 120 |
                 System.arraycopy(_setMethods, 0, tmpMethods, 0, _setMethods.length);
1376 | 120 |
                 System.arraycopy(_setParams, 0, tmpParams, 0, _setParams.length);
1377 | 120 |
                 System.arraycopy(_setPrimitiveType, 0, tmpPrimitive, 0, _setParams.length);
1378 | 120 |
                 _setMethods = tmpMethods;
1379 | 120 |
                 _setParams = tmpParams;
1380 | 120 |
                 _setPrimitiveType = tmpPrimitive;
1381 |  |
              }
1382 |  |
1383 |  |
              // record the method, and the type of the variable parameter
1384 | 2246 |
              _setMethods[setCount - 1] = m;
1385 | 2246 |
              _setParams[setCount - 1] = params[setArgsLength - 1];
1386 | 2246 |
              if (_setParams[setCount - 1].isPrimitive())
1387 | 218 |
                 _setPrimitiveType[setCount - 1]
1388 |  |
                 = getWrapperClass(_setParams[setCount - 1]);
1389 |  |
1390 |  |
           }
1391 |  |
           else
1392 |  |
           {
1393 | 0 |
              throw new PropertyException("PropertyOperator FAILED for method "
1394 |  |
              + m + "--please report this bug!");
1395 |  |
           }
1396 | 13026 |
        }
1397 |  |
1398 |  |
        final boolean setImpl(final Object inst, final Object[] args)
1399 |  |
            throws PropertyException, NoSuchMethodException
1400 |  |
        {
1401 |  |
           //which method to use? check params for first match
1402 | 192 |
           for (int i = 0; i < setCount; i++)
1403 |  |
           {
1404 | 192 |
              Object arg = args[args.length - 1];
1405 |  |
              // XXX: null values are blocked by the next line
1406 | 192 |
              if (_setParams[i].isPrimitive())
1407 |  |
              {
1408 | 0 |
                 if (arg.getClass() == _setPrimitiveType[i])
1409 |  |
                 {
1410 |  |
                    try
1411 |  |
                    {
1412 | 0 |
                       PropertyOperator.invoke(_setMethods[i], inst, args);
1413 | 0 |
                       return true;
1414 |  |
                    }
1415 | 0 |
                    catch (Exception e)
1416 |  |
                    {
1417 |  |
                       // ignore exception
1418 | 0 |
                    }
1419 |  |
                 }
1420 |  |
              }
1421 | 192 |
              else if (arg == null
1422 |  |
              || _setParams[i].isInstance(args[args.length - 1]))
1423 |  |
              {
1424 | 192 |
                 PropertyOperator.invoke(_setMethods[i], inst, args);
1425 | 192 |
                 return true;
1426 |  |
              }
1427 |  |
           }
1428 | 0 |
           return false;
1429 |  |
        }
1430 |  |
1431 |  |
     }
1432 |  |
1433 |  |
     final class UnaryMethodAccessor extends MethodAccessor
1434 |  |
     {
1435 |  |
1436 |  |
        UnaryMethodAccessor(final String name, final Method m, final Class[] params)
1437 |  |
            throws PropertyException
1438 |  |
        {
1439 | 10710 |
           super(name, m, params);
1440 | 10710 |
        }
1441 |  |
1442 |  |
        final int numArgsGet()
1443 |  |
        {
1444 | 12490 |
           return 0;
1445 |  |
        }
1446 |  |
1447 |  |
        final int numArgsSet()
1448 |  |
        {
1449 | 12490 |
           return 1;
1450 |  |
        }
1451 |  |
1452 |  |
        final Object get(final Object instance)
1453 |  |
            throws PropertyException, NoSuchMethodException
1454 |  |
        {
1455 | 51296 |
           return PropertyOperator.invoke(_getMethod, instance, null);
1456 |  |
        }
1457 |  |
1458 |  |
        final boolean set(final Object instance, final Object value)
1459 |  |
            throws PropertyException, NoSuchMethodException
1460 |  |
        {
1461 | 192 |
           Object[] args =
1462 |  |
           {value};
1463 | 192 |
           return setImpl(instance, args);
1464 |  |
        }
1465 |  |
1466 |  |
     }
1467 |  |
1468 |  |
     final class BinaryMethodAccessor extends MethodAccessor
1469 |  |
     {
1470 |  |
1471 |  |
        BinaryMethodAccessor(String name, Method m, Class[] params)
1472 |  |
            throws PropertyException
1473 |  |
        {
1474 | 392 |
           super(name, m, params);
1475 | 392 |
        }
1476 |  |
1477 |  |
        final int numArgsGet()
1478 |  |
        {
1479 | 536 |
           return 1;
1480 |  |
        }
1481 |  |
1482 |  |
        final int numArgsSet()
1483 |  |
        {
1484 | 536 |
           return 2;
1485 |  |
        }
1486 |  |
1487 |  |
        final Object get(final Object instance, String prop)
1488 |  |
            throws PropertyException, NoSuchMethodException
1489 |  |
        {
1490 | 1170 |
           Object[] args =
1491 |  |
           {prop};
1492 | 1170 |
           return PropertyOperator.invoke(_getMethod, instance, args);
1493 |  |
        }
1494 |  |
1495 |  |
        final boolean set(final Object instance, String prop, Object value)
1496 |  |
            throws PropertyException, NoSuchMethodException
1497 |  |
        {
1498 | 0 |
           Object[] args =
1499 |  |
           {prop, value};
1500 | 0 |
           return setImpl(instance, args);
1501 |  |
        }
1502 |  |
     }