Coverage Report - org.webmacro.engine.PropertyOperatorCache
 
Classes in this File Line Coverage Branch Coverage Complexity
Accessor
40%
4/10
N/A
5.327
BinaryMethodAccessor
100%
8/8
N/A
5.327
DirectAccessor
100%
24/24
91%
11/12
5.327
FieldAccessor
81%
9/11
N/A
5.327
LengthAccessor
50%
3/6
N/A
5.327
MethodAccessor
60%
39/64
52%
20/38
5.327
PropertyOperator
83%
209/251
81%
171/210
5.327
PropertyOperatorCache
78%
51/65
75%
21/28
5.327
UnaryMethodAccessor
100%
7/7
N/A
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  40
    static Logger _log =  LoggerFactory.getLogger(PropertyOperatorCache.class);
 62  
    private HashMap _restrictedClasses;
 63  
    
 64  
    public PropertyOperatorCache()
 65  63
    {
 66  63
    }
 67  
    
 68  
    final public void init(Broker b, Settings config) throws InitException
 69  
    {
 70  
       String cacheManager;
 71  
       
 72  
       
 73  63
       cacheManager = b.getSetting("PropertyOperator.CacheManager");
 74  63
       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  63
             _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  63
          }
 93  
       }
 94  63
       _cache.init(b, config, "PropertyOperator");
 95  63
       _restrictedClasses = new HashMap(11);
 96  63
       String restrictList = b.getSetting("RestrictedClasses");
 97  63
       if (restrictList != null)
 98  
       {
 99  63
          StringTokenizer stok = new StringTokenizer(restrictList, ",");
 100  126
          while (stok.hasMoreTokens())
 101  
          {
 102  63
             String className = stok.nextToken();
 103  
             try
 104  
             {
 105  63
                Class c = Class.forName(className);
 106  63
                String okMethList = b.getSetting(
 107  
                        "RestrictedClasses.AllowedMethods." + className);
 108  63
                ArrayList okMeths = null;
 109  63
                if (okMethList != null)
 110  
                {
 111  63
                   okMeths = new ArrayList(20);
 112  63
                   StringTokenizer stok2 = new StringTokenizer(okMethList, ",");
 113  378
                   while (stok2.hasMoreTokens())
 114  
                   {
 115  315
                      okMeths.add(stok2.nextToken());
 116  
                   }
 117  
                }
 118  63
                _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  63
             }
 125  63
          }
 126  
       }
 127  63
    }
 128  
    
 129  
    Map getRestrictedClassMap()
 130  
    {
 131  4563
       return _restrictedClasses;
 132  
    }
 133  
    
 134  
    final public PropertyOperator getOperator(final Class type)
 135  
        throws PropertyException
 136  
    {
 137  2298
       Object o = _cache.get(type);
 138  2298
       if (o == null)
 139  
       {
 140  58
          PropertyOperator po = new PropertyOperator(type, this);
 141  58
          _cache.put(type, po);
 142  58
          return po;
 143  
       }
 144  
       else
 145  2240
          return (PropertyOperator) o;
 146  
    }
 147  
    
 148  
    final public PropertyOperator getOperator(final Object obj)
 149  
        throws PropertyException
 150  
    {
 151  2300
       Class type = obj.getClass();
 152  
       // added code to handle static classes.  Static classes must be wrapped
 153  
       // in a StaticClassWrapper
 154  
       // 1Jun2001 - Keats
 155  2298
       if (type == org.webmacro.engine.StaticClassWrapper.class)
 156  
       {
 157  3
          type = ((org.webmacro.engine.StaticClassWrapper) obj).get();
 158  
       }
 159  2298
       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  1496
       if (instance == null)
 181  
       {
 182  0
          return null;
 183  
       }
 184  
       else
 185  
       {
 186  1496
          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  564
          if (instance == null)
 223  
          {
 224  0
             return false;
 225  
          }
 226  564
          return getOperator(instance)
 227  
          .setProperty(context, instance, names, value, start);
 228  
       }
 229  2
       catch (PropertyException e)
 230  
       {
 231  2
          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  582
       if (instance instanceof Object[])
 263  479
          return new ArrayIterator((Object[]) instance);
 264  103
       else if (instance.getClass().isArray())
 265  0
          return new PrimitiveArrayIterator(instance);
 266  103
       else if (instance instanceof Iterator)
 267  1
          return (Iterator) instance;
 268  102
       else if (instance instanceof Enumeration)
 269  3
          return new EnumIterator((Enumeration) instance);
 270  
       else
 271  99
          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  58
    final private HashMap _unaryAccessors = new HashMap();
 316  
    
 317  
    /**
 318  
     * Accessors that require an additional property name.
 319  
     */
 320  58
    final private HashMap _binaryAccessors = new HashMap();
 321  
    
 322  
    /**
 323  
     * Accessors for direct method calls.
 324  
     */
 325  58
    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  58
    private Method iteratorMethod = null;
 336  
    
 337  
    /**
 338  
     * An accessor for array lengths.
 339  
     */
 340  24
    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  301
       if (Modifier.isPublic(c.getModifiers()))
 357  
       {
 358  295
          Method m[] = c.getDeclaredMethods();
 359  3772
          for (int i = 0; i < m.length; i++)
 360  
          {
 361  3477
             if (Modifier.isPublic(m[i].getModifiers()))
 362  
             {
 363  2941
                addMethod(meths, m[i]);
 364  
             }
 365  
          }
 366  
       }
 367  301
       Class iface[] = c.getInterfaces();
 368  451
       for (int i = 0; i < iface.length; i++)
 369  
       {
 370  150
          getAllMethods(meths, iface[i]);
 371  
       }
 372  
       
 373  301
       Class sup = c.getSuperclass();
 374  301
       if (sup != null)
 375  
       {
 376  93
          getAllMethods(meths, sup);
 377  
       }
 378  301
    }
 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  1912
       if (lhs.length == rhs.length)
 392  
       {
 393  2350
          for (int i = 0; i < lhs.length; i++)
 394  
          {
 395  
             
 396  1061
             if (!rhs[i].equals(lhs[i]))
 397  
             {
 398  
                
 399  239
                if (lhs[i].isAssignableFrom(rhs[i]))
 400  
                {
 401  
                   // rhs is more specific than lhs
 402  21
                   return 1;
 403  
                }
 404  
                
 405  218
                if (rhs[i].isAssignableFrom(lhs[i]))
 406  
                {
 407  
                   // lhs is more specific than rhs
 408  5
                   return -1;
 409  
                }
 410  
                
 411  
                // not related by inheritance, put lhs later on
 412  213
                return 1;
 413  
                
 414  
             }
 415  
          }
 416  1289
          return 0; // all the same
 417  
       }
 418  
       else
 419  
       {
 420  384
          return (lhs.length < rhs.length) ? -1 : 1;
 421  
       }
 422  
    }
 423  
    
 424  
    boolean isMethodAllowed(Method m)
 425  
    {
 426  4541
       Class c = m.getDeclaringClass();
 427  4541
       Map restrictedClasses = _cache.getRestrictedClassMap();
 428  4541
       if (restrictedClasses.containsKey(c))
 429  
       {
 430  
          // this class is restricted, check if method is allowed
 431  62
          List okMeths = (List) restrictedClasses.get(c);
 432  62
          return (okMeths != null && okMeths.contains(m.getName()));
 433  
       }
 434  4479
       return true;
 435  
    }
 436  
    
 437  
    boolean isMethodRestricted(Class c, String name)
 438  
    {
 439  
       // check if object is restricted
 440  22
       Map restrictedClassMap = _cache.getRestrictedClassMap();
 441  22
       if (!restrictedClassMap.containsKey(c))
 442  
       {
 443  22
          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  2941
       if (!isMethodAllowed(m)) return;
 466  2889
       String name = m.getName();
 467  2889
       Object o = hm.get(name);
 468  2889
       if (o == null)
 469  
       {
 470  1267
          hm.put(name, m);
 471  1267
          return;
 472  
       }
 473  
       
 474  
       Vector v;
 475  1622
       if (o instanceof Method)
 476  
       {
 477  710
          v = new Vector();
 478  710
          v.addElement(o);
 479  710
          hm.put(name, v);
 480  
       }
 481  
       else
 482  
       {
 483  912
          v = (Vector) o;
 484  
       }
 485  
       
 486  1622
       Class ptypes[] = m.getParameterTypes();
 487  2085
       for (int i = 0; i < v.size(); i++)
 488  
       {
 489  1912
          Class curTypes[] = ((Method) v.elementAt(i)).getParameterTypes();
 490  
          
 491  1912
          int order = precedes(ptypes, curTypes);
 492  
          
 493  1912
          if (order < 0)
 494  
          {
 495  160
             v.insertElementAt(m, i);
 496  160
             return;
 497  
          }
 498  1752
          else if (order == 0)
 499  
          {
 500  
             // ignore duplicate method
 501  1289
             return;
 502  
          }
 503  
       }
 504  173
       v.addElement(m);
 505  173
    }
 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  58
       Vector v = new Vector();
 517  58
       HashMap h = new HashMap();
 518  58
       getAllMethods(h, c);
 519  58
       Iterator iter = h.values().iterator();
 520  1325
       while (iter.hasNext())
 521  
       {
 522  1267
          Object elem = iter.next();
 523  
          
 524  1267
          if (elem instanceof Method)
 525  
          {
 526  557
             v.addElement(elem);
 527  
          }
 528  
          else
 529  
          {
 530  710
             Vector v1 = (Vector) elem;
 531  1753
             for (int i = 0; i < v1.size(); i++)
 532  
             {
 533  1043
                v.addElement(v1.elementAt(i));
 534  
             }
 535  
          }
 536  1267
       }
 537  58
       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  58
    {
 548  
       
 549  
       Accessor acc;
 550  
       
 551  
       // Save the cache
 552  58
       _cache = cache;
 553  
       
 554  
       // introspect fields first
 555  
       
 556  58
       Field[] fields = target.getFields();
 557  227
       for (int i = 0; i < fields.length; i++)
 558  
       {
 559  169
          if (Modifier.isPublic(fields[i].getModifiers()))
 560  
          {
 561  169
             acc = new FieldAccessor(fields[i]);
 562  169
             _unaryAccessors.put(acc.getName(), acc);
 563  
          }
 564  
       }
 565  58
       if (target.isArray())
 566  1
          _unaryAccessors.put("length", _lengthAccessor);
 567  
       
 568  
       // introspect methods second
 569  
       
 570  58
       Vector methods = getMethods(target);
 571  
       
 572  
       Method meth;
 573  
       Class[] params;
 574  
       String name,propName;
 575  
       
 576  1658
       for (int i = 0; i < methods.size(); i++)
 577  
       {
 578  1600
          meth = ((Method) methods.elementAt(i));
 579  1600
          if (!isMethodAllowed(meth)) continue;
 580  1600
          name = meth.getName();
 581  1600
          params = meth.getParameterTypes();
 582  1600
          int plength = params.length;
 583  
          
 584  
          // add direct accessor
 585  1600
          acc = (Accessor) _directAccessors.get(name);
 586  1600
          if (acc != null)
 587  
          {
 588  333
             ((DirectAccessor) acc).addMethod(meth, params);
 589  
          }
 590  
          else
 591  
          {
 592  1267
             acc = new DirectAccessor(name, meth, params);
 593  1267
             _directAccessors.put(name, acc);
 594  
          }
 595  
          
 596  
          // check for get/set/put method
 597  1600
          if ((name.startsWith("get") ||
 598  
          name.startsWith("set")) || name.equals("put"))
 599  
          {
 600  
             
 601  321
             propName = name.substring(3);
 602  
             
 603  321
             if (((plength == 0) && name.startsWith("get")) ||
 604  
             ((plength == 1) && name.startsWith("set")))
 605  
             {
 606  
                
 607  
                // unary get/set method
 608  198
                acc = (Accessor) _unaryAccessors.get(propName);
 609  198
                if (acc != null)
 610  
                {
 611  28
                   if (acc instanceof MethodAccessor)
 612  
                   {
 613  28
                      ((MethodAccessor) acc).addMethod(meth, params);
 614  
                   }
 615  
                }
 616  
                else
 617  
                {
 618  170
                   acc = new UnaryMethodAccessor(propName, meth, params);
 619  170
                   _unaryAccessors.put(propName, acc);
 620  
                }
 621  
             }
 622  123
             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  31
                if (_hashAccessor != null)
 629  
                {
 630  14
                   _hashAccessor.addMethod(meth, params);
 631  
                }
 632  
                else
 633  
                {
 634  17
                   _hashAccessor = new BinaryMethodAccessor(propName, meth, params);
 635  
                }
 636  
             }
 637  92
             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  49
                acc = (Accessor) _binaryAccessors.get(propName);
 643  49
                if (acc != null)
 644  
                {
 645  10
                   ((MethodAccessor) acc).addMethod(meth, params);
 646  
                }
 647  
                else
 648  
                {
 649  39
                   acc = new BinaryMethodAccessor(propName, meth, params);
 650  39
                   _binaryAccessors.put(propName, acc);
 651  
                }
 652  
             }
 653  
          }
 654  1279
          else if (name.startsWith("is") && meth.getReturnType() == java.lang.Boolean.TYPE)
 655  
          {
 656  41
             if (plength == 0)
 657  
             {
 658  
                // unary accessor method
 659  37
                propName = name.substring(2);
 660  37
                acc = (Accessor) _unaryAccessors.get(propName);
 661  37
                if (acc != null)
 662  
                {
 663  0
                   if (acc instanceof MethodAccessor)
 664  
                   {
 665  0
                      ((MethodAccessor) acc).addMethod(meth, params);
 666  
                   }
 667  
                }
 668  
                else
 669  
                {
 670  37
                   acc = new UnaryMethodAccessor(propName, meth, params);
 671  37
                   _unaryAccessors.put(propName, acc);
 672  
                }
 673  
             }
 674  
          }
 675  1238
          else if (name.equals("elements") ||
 676  
          name.equals("enumeration") ||
 677  
          name.equals("iterator") ||
 678  
          name.equals("toArray"))
 679  
          {
 680  30
             if (params.length == 0)
 681  
             {
 682  22
                Class returnType = meth.getReturnType();
 683  
                
 684  
                // iterator supercedes enumeration supercedes Object[]
 685  22
                Class iterClass = Iterator.class;
 686  22
                boolean iterA = iterClass.isAssignableFrom(returnType);
 687  22
                if (
 688  
                iterA ||
 689  
                (((iteratorMethod == null) ||
 690  
                iteratorMethod.getName().equals("toArray")) &&
 691  
                Object[].class.isAssignableFrom(returnType) ||
 692  
                Enumeration.class.isAssignableFrom(returnType)))
 693  
                {
 694  18
                   iteratorMethod = meth;
 695  
                }
 696  
                
 697  
             }
 698  
          }
 699  
       }
 700  58
    }
 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  1636
       Object nextPropValue = null;
 723  1636
       Accessor acc = null;
 724  
       
 725  1636
       if (names[start] instanceof String)
 726  
       {
 727  1338
          propName = (String) names[start];
 728  
       }
 729  298
       else if (names[start] instanceof PropertyMethod)
 730  
       {
 731  298
          PropertyMethod pm = (PropertyMethod) names[start];
 732  298
          propName = pm.getName();
 733  298
          acc = (Accessor) _directAccessors.get(propName);
 734  298
          Object[] args = pm.getArguments(context);
 735  298
          if (acc == null)
 736  
          {
 737  8
             if (isMethodRestricted(instance.getClass(), propName))
 738  0
                throw new PropertyException.RestrictedMethodException(
 739  
                pm.toString(),
 740  
                fillInName(names, start),
 741  
                instance.getClass().getName());
 742  8
             throw new PropertyException.NoSuchMethodException(
 743  
             pm.toString(),
 744  
             fillInName(names, start),
 745  
             instance.getClass().getName());
 746  
          }
 747  
          try
 748  
          {
 749  290
             nextPropValue = acc.get(instance, args);
 750  277
             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  277
          }
 759  
          
 760  277
       }
 761  
       else
 762  
       {
 763  0
          propName = names[start].toString();
 764  
       }
 765  
       
 766  
       // unary?
 767  1615
       if (acc == null)
 768  
       {
 769  1338
          acc = (Accessor) _unaryAccessors.get(propName);
 770  1338
          if (acc != null)
 771  
          {
 772  
             try
 773  
             {
 774  1023
                nextPropValue = acc.get(instance);
 775  1023
                start++;
 776  
             }
 777  0
             catch (NoSuchMethodException e)
 778  
             {
 779  0
                acc = null;
 780  1023
             }
 781  
          }
 782  
       }
 783  
       
 784  
       // binary?
 785  1615
       if (acc == null)
 786  
       {
 787  315
          acc = (Accessor) _binaryAccessors.get(propName);
 788  
          //            if ((acc != null) && ((start + 1) <= end))
 789  315
          if ((acc != null) && ((start + 1) <= names.length))
 790  
          {
 791  
             try
 792  
             {
 793  14
                nextPropValue = acc.get(instance, (String) names[start + 1]);
 794  14
                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  14
             }
 807  
          }
 808  
          else
 809  
          {
 810  301
             acc = null;
 811  
          }
 812  
       }
 813  
       
 814  
       // hash?
 815  1615
       if (acc == null)
 816  
       {
 817  301
          acc = _hashAccessor;
 818  
          try
 819  
          {
 820  301
             if (acc != null)
 821  
             {
 822  294
                nextPropValue = acc.get(instance, propName);
 823  294
                start++;
 824  
             }
 825  
          }
 826  0
          catch (NoSuchMethodException e)
 827  
          {
 828  0
             acc = null;
 829  301
          }
 830  
       }
 831  
       
 832  1615
       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  7
          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  7
          throw new PropertyException.NoSuchPropertyException(
 844  
          propName, fillInName(names, start),
 845  
          instance.getClass().getName());
 846  
       }
 847  
       
 848  1608
       if (start <= end)
 849  
       {
 850  
          try
 851  
          {
 852  134
             return _cache.getOperator(nextPropValue)
 853  
             .getProperty(context, nextPropValue, names, start, end);
 854  
          }
 855  2
          catch (NullPointerException e)
 856  
          {
 857  
             // $Foo.getNull().SomeProperty is what makes this happen
 858  2
             throw new PropertyException("$" + fillInName(names, start) + " is null.  Cannot access ." + names[end]);
 859  
          }
 860  
       }
 861  
       else
 862  
       {
 863  1474
          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  17
       StringBuffer sb = new StringBuffer();
 875  37
       for (int x = 0; x < end; x++)
 876  
       {
 877  20
          if (x > 0)
 878  3
             sb.append(".");
 879  20
          sb.append(names[x]);
 880  
       }
 881  
       
 882  17
       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  571
       int binPos = names.length - 2;
 908  
       
 909  
       // if we're not yet at the binary-settable parent, go there
 910  571
       if (pos < binPos)
 911  
       {
 912  2
          Object grandparent = getProperty(context, instance, names,
 913  
          pos, binPos - 1);
 914  2
          PropertyOperator po = _cache.getOperator(grandparent);
 915  2
          return po.setProperty(context, grandparent, names, value, binPos);
 916  
       }
 917  
       
 918  
       // if we're at the binary-settable parent, try direct first
 919  569
       if (pos == binPos)
 920  
       {
 921  
          
 922  
          // try direct -- move to direct parent and try from there
 923  6
          Object parent = null;
 924  
          try
 925  
          {
 926  6
             parent = getProperty(context, instance, names, pos, pos);
 927  6
             if (parent != null)
 928  
             {
 929  5
                PropertyOperator po = _cache.getOperator(parent);
 930  5
                if (po.setProperty(context, parent, names, value, pos + 1))
 931  
                {
 932  1
                   return true;
 933  
                }
 934  
             }
 935  
          }
 936  0
          catch (NoSuchMethodException e)
 937  
          {
 938  
             // oh well, keep trying: XXX this makes binOp expensive
 939  5
          }
 940  
          
 941  
          // if direct failed, try binary
 942  5
          Accessor binOp = (Accessor) _binaryAccessors.get(names[pos]);
 943  5
          if (binOp != null)
 944  
          {
 945  
             try
 946  
             {
 947  5
                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  563
       Accessor unaryOp = (Accessor) _unaryAccessors.get(names[pos]);
 965  
       try
 966  
       {
 967  563
          if ((unaryOp != null) && unaryOp.set(instance, value))
 968  
          {
 969  217
             return true;
 970  
          }
 971  344
          if (_hashAccessor != null)
 972  
          {
 973  338
             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  6
       }
 984  6
       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  99
       if (iteratorMethod != null)
 998  
       {
 999  99
          Object ret = invoke(iteratorMethod, instance, null);
 1000  99
          if (ret instanceof Iterator)
 1001  
          {
 1002  99
             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  0
       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  1959
          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  1952
          if (obj == null
 1035  
          && meth.getReturnType() == java.lang.Void.TYPE)
 1036  158
             return org.webmacro.engine.VoidMacro.instance;
 1037  
          else
 1038  1794
             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  7
       catch (InvocationTargetException e)
 1059  
       {
 1060  
          // if this is a wrapped UndefinedVariableException, unwrap and rethrow it
 1061  7
          if (e.getTargetException() instanceof PropertyException.UndefinedVariableException)
 1062  0
             throw (PropertyException) e.getTargetException();
 1063  7
          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  1723
    {
 1096  1723
       _name = name;
 1097  1723
    }
 1098  
    
 1099  
    final String getName()
 1100  
    {
 1101  175
       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  169
       super(f.getName());
 1169  169
       _field = f;
 1170  169
    }
 1171  
    
 1172  
    final Object get(final Object instance)
 1173  
        throws PropertyException
 1174  
    {
 1175  
       try
 1176  
       {
 1177  208
          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  108
          _field.set(instance, value);
 1193  
       }
 1194  2
       catch (Exception e)
 1195  
       {
 1196  2
          throw new PropertyException("Unable to write field " + _field +
 1197  
          " on object " + instance + " of " + instance.getClass(),
 1198  
          e);
 1199  106
       }
 1200  106
       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  24
       super("length");
 1214  24
    }
 1215  
    
 1216  
    final Object get(final Object instance)
 1217  
        throws PropertyException
 1218  
    {
 1219  
       try
 1220  
       {
 1221  1
          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  1267
    Vector _methods = new Vector();
 1246  
    
 1247  
    DirectAccessor(final String name, final Method m, final Class[] params)
 1248  
    {
 1249  1267
       super(name);
 1250  1267
       addMethod(m, params);
 1251  1267
    }
 1252  
    
 1253  
    final void addMethod(final Method m, Class[] params)
 1254  
    {
 1255  1600
       _methods.addElement(m);
 1256  1600
    }
 1257  
    
 1258  
    final Object get(Object instance, Object[] args)
 1259  
        throws PropertyException, NoSuchMethodException
 1260  
    {
 1261  290
       Class[] types = new Class[args.length];
 1262  463
       for (int i = 0; i < args.length; i++)
 1263  
       {
 1264  
          try
 1265  
          {
 1266  173
             types[i] = args[i].getClass();
 1267  
          }
 1268  10
          catch (NullPointerException e)
 1269  
          {
 1270  10
             types[i] = null;
 1271  163
          }
 1272  
       }
 1273  
       
 1274  303
       for (int i = 0; i < _methods.size(); i++)
 1275  
       {
 1276  297
          Method m = (Method) _methods.elementAt(i);
 1277  297
          Class[] sig = m.getParameterTypes();
 1278  297
          if (IntrospectionUtils.matches(sig, types))
 1279  
          {
 1280  284
             return PropertyOperator.invoke(m, instance, args);
 1281  
          }
 1282  
       }
 1283  
       
 1284  
       // not found
 1285  
       
 1286  6
       StringBuffer arglist = new StringBuffer();
 1287  6
       Method m = (Method) _methods.firstElement();
 1288  24
       for (int i = 0; i < args.length; i++)
 1289  
       {
 1290  18
          if (i > 0)
 1291  
          {
 1292  12
             arglist.append(", ");
 1293  
          }
 1294  18
          arglist.append((args[i] == null) ? "null" : args[i].getClass().getName());
 1295  
       }
 1296  
       
 1297  6
       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  263
    private Method[] _setMethods = null; // may be multiple set methods
 1311  263
    private Class[] _setParams = null;  // variable arg type for set meth N
 1312  263
    private Class[] _setPrimitiveType = null;
 1313  263
    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  263
       super(name);
 1323  263
       addMethod(m, params);
 1324  263
    }
 1325  
    
 1326  
    private Class getWrapperClass(Class s)
 1327  
    {
 1328  24
       if (s == Integer.TYPE)
 1329  15
          return Integer.class;
 1330  9
       else if (s == Boolean.TYPE)
 1331  1
          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  315
       final int setArgsLength = numArgsSet();
 1355  315
       final int getArgsLength = numArgsGet();
 1356  
       
 1357  315
       if (params.length == getArgsLength)
 1358  
       {
 1359  246
          _getMethod = m;
 1360  
       }
 1361  69
       else if (params.length == setArgsLength)
 1362  
       {
 1363  69
          setCount++;
 1364  69
          if (_setMethods == null)
 1365  
          {
 1366  69
             _setMethods = new Method[1];
 1367  69
             _setParams = new Class[1];
 1368  69
             _setPrimitiveType = new Class[1];
 1369  
          }
 1370  0
          else if (_setMethods.length <= setCount)
 1371  
          {
 1372  0
             Method[] tmpMethods = new Method[(setCount + 1) * 2];
 1373  0
             Class[] tmpParams = new Class[(setCount + 1) * 2];
 1374  0
             Class[] tmpPrimitive = new Class[(setCount + 1) * 2];
 1375  0
             System.arraycopy(_setMethods, 0, tmpMethods, 0, _setMethods.length);
 1376  0
             System.arraycopy(_setParams, 0, tmpParams, 0, _setParams.length);
 1377  0
             System.arraycopy(_setPrimitiveType, 0, tmpPrimitive, 0, _setParams.length);
 1378  0
             _setMethods = tmpMethods;
 1379  0
             _setParams = tmpParams;
 1380  0
             _setPrimitiveType = tmpPrimitive;
 1381  
          }
 1382  
          
 1383  
          // record the method, and the type of the variable parameter
 1384  69
          _setMethods[setCount - 1] = m;
 1385  69
          _setParams[setCount - 1] = params[setArgsLength - 1];
 1386  69
          if (_setParams[setCount - 1].isPrimitive())
 1387  24
             _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  315
    }
 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  458
       for (int i = 0; i < setCount; i++)
 1403  
       {
 1404  456
          Object arg = args[args.length - 1];
 1405  
          // XXX: null values are blocked by the next line
 1406  456
          if (_setParams[i].isPrimitive())
 1407  
          {
 1408  7
             if (arg.getClass() == _setPrimitiveType[i])
 1409  
             {
 1410  
                try
 1411  
                {
 1412  5
                   PropertyOperator.invoke(_setMethods[i], inst, args);
 1413  5
                   return true;
 1414  
                }
 1415  0
                catch (Exception e)
 1416  
                {
 1417  
                   // ignore exception
 1418  0
                }
 1419  
             }
 1420  
          }
 1421  449
          else if (arg == null
 1422  
          || _setParams[i].isInstance(args[args.length - 1]))
 1423  
          {
 1424  449
             PropertyOperator.invoke(_setMethods[i], inst, args);
 1425  449
             return true;
 1426  
          }
 1427  
       }
 1428  2
       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  207
       super(name, m, params);
 1440  207
    }
 1441  
    
 1442  
    final int numArgsGet()
 1443  
    {
 1444  235
       return 0;
 1445  
    }
 1446  
    
 1447  
    final int numArgsSet()
 1448  
    {
 1449  235
       return 1;
 1450  
    }
 1451  
    
 1452  
    final Object get(final Object instance)
 1453  
        throws PropertyException, NoSuchMethodException
 1454  
    {
 1455  814
       return PropertyOperator.invoke(_getMethod, instance, null);
 1456  
    }
 1457  
    
 1458  
    final boolean set(final Object instance, final Object value)
 1459  
        throws PropertyException, NoSuchMethodException
 1460  
    {
 1461  113
       Object[] args =
 1462  
       {value};
 1463  113
       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  56
       super(name, m, params);
 1475  56
    }
 1476  
    
 1477  
    final int numArgsGet()
 1478  
    {
 1479  80
       return 1;
 1480  
    }
 1481  
    
 1482  
    final int numArgsSet()
 1483  
    {
 1484  80
       return 2;
 1485  
    }
 1486  
    
 1487  
    final Object get(final Object instance, String prop)
 1488  
        throws PropertyException, NoSuchMethodException
 1489  
    {
 1490  308
       Object[] args =
 1491  
       {prop};
 1492  308
       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  343
       Object[] args =
 1499  
       {prop, value};
 1500  343
       return setImpl(instance, args);
 1501  
    }
 1502  
 }