Source for java.beans.IndexedPropertyDescriptor

   1: /* IndexedPropertyDescriptor.java --
   2:    Copyright (C) 1998, 2003 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10:  
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.beans;
  40: 
  41: import java.lang.reflect.Array;
  42: import java.lang.reflect.Method;
  43: 
  44: /**
  45:  * IndexedPropertyDescriptor describes information about a JavaBean
  46:  * indexed property, by which we mean an array-like property that
  47:  * has been exposed via a pair of get and set methods and another
  48:  * pair that allows you to get to the property by an index.<P>
  49:  *
  50:  * An example property would have four methods like this:<P>
  51:  * <CODE>FooBar[] getFoo()</CODE><BR>
  52:  * <CODE>void setFoo(FooBar[])</CODE><BR>
  53:  * <CODE>FooBar getFoo(int)</CODE><BR>
  54:  * <CODE>void setFoo(int,FooBar)</CODE><P>
  55:  *
  56:  * The constraints put on get and set methods are:<P>
  57:  * <OL>
  58:  * <LI>There must be at least a get(int) or a set(int,...) method.
  59:  * Nothing else is required.  <B>Spec note:</B>One nice restriction
  60:  * would be that if there is a get() there must be a get(int), same
  61:  * with set, but that is not in the spec and is fairly harmless.)</LI>
  62:  * <LI>A get array method must have signature
  63:  *     <CODE>&lt;propertyType&gt;[] &lt;getMethodName&gt;()</CODE></LI>
  64:  * <LI>A set array method must have signature
  65:  *     <CODE>void &lt;setMethodName&gt;(&lt;propertyType&gt;[])</CODE></LI>
  66:  * <LI>A get index method must have signature
  67:  *     <CODE>&lt;propertyType&gt; &lt;getMethodName&gt;(int)</CODE></LI>
  68:  * <LI>A set index method must have signature
  69:  *     <CODE>void &lt;setMethodName&gt;(int,&lt;propertyType&gt;)</CODE></LI>
  70:  * <LI>All these methods may throw any exception.</LI>
  71:  * <LI>All these methods must be public.</LI>
  72:  * </OL>
  73:  *
  74:  * @author John Keiser
  75:  * @since JDK1.1
  76:  */
  77: public class IndexedPropertyDescriptor extends PropertyDescriptor
  78: {
  79:   private Class<?> indexedPropertyType;
  80:   private Method setIndex;
  81:   private Method getIndex;
  82: 
  83:   /**
  84:    * Create a new IndexedPropertyDescriptor by introspection.
  85:    * This form of constructor creates the PropertyDescriptor by
  86:    * looking for getter methods named <CODE>get&lt;name&gt;()</CODE>
  87:    * and setter methods named
  88:    * <CODE>set&lt;name&gt;()</CODE> in class
  89:    * <CODE>&lt;beanClass&gt;</CODE>, where &lt;name&gt; has its
  90:    * first letter capitalized by the constructor.<P>
  91:    *
  92:    * <B>Implementation note:</B> If there is a get(int) method,
  93:    * then the return type of that method is used to find the
  94:    * remaining methods.  If there is no get method, then the
  95:    * set(int) method is searched for exhaustively and that type
  96:    * is used to find the others.<P>
  97:    *
  98:    * <B>Spec note:</B>
  99:    * If there is no get(int) method and multiple set(int) methods with
 100:    * the same name and the correct parameters (different type of course),
 101:    * then an IntrospectionException is thrown.  While Sun's spec
 102:    * does not state this, it can make Bean behavior different on
 103:    * different systems (since method order is not guaranteed) and as
 104:    * such, can be treated as a bug in the spec.  I am not aware of
 105:    * whether Sun's implementation catches this.
 106:    *
 107:    * @param name the programmatic name of the property, usually
 108:    *             starting with a lowercase letter (e.g. fooManChu
 109:    *             instead of FooManChu).
 110:    * @param beanClass the class the get and set methods live in.
 111:    *
 112:    * @exception IntrospectionException if the methods are not found or
 113:    *            invalid.
 114:    */
 115:   public IndexedPropertyDescriptor(String name, Class<?> beanClass)
 116:     throws IntrospectionException
 117:   {
 118:     super(name);
 119:     String capitalized;
 120:     try
 121:       {
 122:         capitalized = Character.toUpperCase(name.charAt(0))
 123:           + name.substring(1);
 124:       }
 125:     catch(StringIndexOutOfBoundsException e)
 126:       {
 127:         capitalized = "";
 128:       }
 129:     findMethods(beanClass, "get" + capitalized, "set" + capitalized,
 130:                 "get" + capitalized, "set" + capitalized);
 131:   }
 132: 
 133:   /**
 134:    * Create a new IndexedPropertyDescriptor by introspection.
 135:    * This form of constructor allows you to specify the
 136:    * names of the get and set methods to search for.<P>
 137:    *
 138:    * <B>Implementation note:</B> If there is a get(int) method,
 139:    * then the return type of that method is used to find the
 140:    * remaining methods.  If there is no get method, then the
 141:    * set(int) method is searched for exhaustively and that type
 142:    * is used to find the others.<P>
 143:    *
 144:    * <B>Spec note:</B>
 145:    * If there is no get(int) method and multiple set(int) methods with
 146:    * the same name and the correct parameters (different type of course),
 147:    * then an IntrospectionException is thrown.  While Sun's spec
 148:    * does not state this, it can make Bean behavior different on
 149:    * different systems (since method order is not guaranteed) and as
 150:    * such, can be treated as a bug in the spec.  I am not aware of
 151:    * whether Sun's implementation catches this.
 152:    *
 153:    * @param name the programmatic name of the property, usually
 154:    *             starting with a lowercase letter (e.g. fooManChu
 155:    *             instead of FooManChu).
 156:    * @param beanClass the class the get and set methods live in.
 157:    * @param getMethodName the name of the get array method.
 158:    * @param setMethodName the name of the set array method.
 159:    * @param getIndexName the name of the get index method.
 160:    * @param setIndexName the name of the set index method.
 161:    *
 162:    * @exception IntrospectionException if the methods are not found or invalid.
 163:    */
 164:   public IndexedPropertyDescriptor(String name, Class<?> beanClass,
 165:                                    String getMethodName, String setMethodName,
 166:                                    String getIndexName, String setIndexName)
 167:     throws IntrospectionException
 168:   {
 169:     super(name);
 170:     findMethods(beanClass, getMethodName, setMethodName, getIndexName,
 171:                 setIndexName);
 172:   }
 173: 
 174:   /**
 175:    * Create a new PropertyDescriptor using explicit Methods.
 176:    * Note that the methods will be checked for conformance to standard
 177:    * Property method rules, as described above at the top of this class.
 178:    * 
 179:    * @param name the programmatic name of the property, usually
 180:    *             starting with a lowercase letter (e.g. fooManChu
 181:    *             instead of FooManChu).
 182:    * @param getMethod the get array method.
 183:    * @param setMethod the set array method.
 184:    * @param getIndex the get index method.
 185:    * @param setIndex the set index method.
 186:    *
 187:    * @exception IntrospectionException if the methods are not found or invalid.
 188:    */
 189:   public IndexedPropertyDescriptor(String name, Method getMethod,
 190:                                    Method setMethod, Method getIndex,
 191:                                    Method setIndex)
 192:     throws IntrospectionException
 193:   {
 194:     super(name);
 195:     if(getMethod != null && getMethod.getParameterTypes().length > 0)
 196:       throw new IntrospectionException("get method has parameters");
 197:     if(getMethod != null && setMethod.getParameterTypes().length != 1)
 198:       throw new IntrospectionException("set method does not have exactly one parameter");
 199:     if(getMethod != null && setMethod != null)
 200:       {
 201:         if(!getMethod.getReturnType().equals(setMethod.getParameterTypes()[0]))
 202:           {
 203:             throw new IntrospectionException("set and get methods do not "
 204:                                              + "share the same type");
 205:           }
 206:         if(!getMethod.getDeclaringClass().isAssignableFrom
 207:            (setMethod.getDeclaringClass())
 208:            && !setMethod.getDeclaringClass().isAssignableFrom
 209:            (getMethod.getDeclaringClass()))
 210:           {
 211:             throw new IntrospectionException("set and get methods are not in "
 212:                                              + "the same class.");
 213:           }
 214:       }
 215: 
 216:     if (getIndex != null
 217:         && (getIndex.getParameterTypes().length != 1
 218:          || !(getIndex.getParameterTypes()[0]).equals(java.lang.Integer.TYPE)))
 219:       {
 220:         throw new IntrospectionException("get index method has wrong "
 221:                                          + "parameters");
 222:       }
 223:     if (setIndex != null
 224:        && (setIndex.getParameterTypes().length != 2
 225:          || !(setIndex.getParameterTypes()[0]).equals(java.lang.Integer.TYPE)))
 226:       {
 227:         throw new IntrospectionException("set index method has wrong "
 228:                                          + "parameters");
 229:       }
 230:     if (getIndex != null && setIndex != null)
 231:       {
 232:         if(!getIndex.getReturnType().equals(setIndex.getParameterTypes()[1]))
 233:           {
 234:             throw new IntrospectionException("set index methods do not share "
 235:                                              + "the same type");
 236:           }
 237:         if(!getIndex.getDeclaringClass().isAssignableFrom
 238:            (setIndex.getDeclaringClass())
 239:            && !setIndex.getDeclaringClass().isAssignableFrom
 240:            (getIndex.getDeclaringClass()))
 241:           {
 242:             throw new IntrospectionException("get and set index methods are "
 243:                                              + "not in the same class.");
 244:           }
 245:       }
 246: 
 247:     if (getIndex != null && getMethod != null
 248:         && !getIndex.getDeclaringClass().isAssignableFrom
 249:         (getMethod.getDeclaringClass())
 250:         && !getMethod.getDeclaringClass().isAssignableFrom
 251:         (getIndex.getDeclaringClass()))
 252:       {
 253:         throw new IntrospectionException("methods are not in the same class.");
 254:       }
 255: 
 256:     if (getIndex != null && getMethod != null
 257:         && !Array.newInstance(getIndex.getReturnType(),0)
 258:         .getClass().equals(getMethod.getReturnType()))
 259:       {
 260:         throw new IntrospectionException("array methods do not match index "
 261:                                          + "methods.");
 262:       }
 263: 
 264:     this.getMethod = getMethod;
 265:     this.setMethod = setMethod;
 266:     this.getIndex = getIndex;
 267:     this.setIndex = setIndex;
 268:     this.indexedPropertyType = getIndex != null ? getIndex.getReturnType()
 269:                                              : setIndex.getParameterTypes()[1];
 270:     this.propertyType = getMethod != null ? getMethod.getReturnType()
 271:       : (setMethod != null ? setMethod.getParameterTypes()[0]
 272:          : Array.newInstance(this.indexedPropertyType,0).getClass());
 273:   }
 274: 
 275:   public Class<?> getIndexedPropertyType()
 276:   {
 277:     return indexedPropertyType;
 278:   }
 279: 
 280:   public Method getIndexedReadMethod()
 281:   {
 282:     return getIndex;
 283:   }
 284: 
 285:   /**
 286:    * Sets the method that is used to read an indexed property.
 287:    *
 288:    * @param m the method to set
 289:    */
 290:   public void setIndexedReadMethod(Method m) throws IntrospectionException
 291:   {
 292:     getIndex = m;
 293:   }
 294: 
 295:   public Method getIndexedWriteMethod()
 296:   {
 297:     return setIndex;
 298:   }
 299: 
 300:   /**
 301:    * Sets the method that is used to write an indexed property.
 302:    *
 303:    * @param m the method to set
 304:    */
 305:   public void setIndexedWriteMethod(Method m) throws IntrospectionException
 306:   {
 307:     setIndex = m;
 308:   }
 309: 
 310:   private void findMethods(Class beanClass, String getMethodName,
 311:                            String setMethodName, String getIndexName,
 312:                            String setIndexName)
 313:     throws IntrospectionException
 314:   {
 315:     try
 316:       {
 317:         if(getIndexName != null)
 318:           {
 319:             try
 320:               {
 321:                 Class[] getArgs = new Class[1];
 322:                 getArgs[0] = java.lang.Integer.TYPE;
 323:                 getIndex = beanClass.getMethod(getIndexName,getArgs);
 324:                 indexedPropertyType = getIndex.getReturnType();
 325:               }
 326:             catch(NoSuchMethodException E)
 327:               {
 328:               }
 329:           }
 330:         if(getIndex != null)
 331:           {
 332:             if(setIndexName != null)
 333:               {
 334:                 try
 335:                   {
 336:                     Class[] setArgs = new Class[2];
 337:                     setArgs[0] = java.lang.Integer.TYPE;
 338:                     setArgs[1] = indexedPropertyType;
 339:                     setIndex = beanClass.getMethod(setIndexName,setArgs);
 340:                     if(!setIndex.getReturnType().equals(java.lang.Void.TYPE))
 341:                       {
 342:                         throw new IntrospectionException(setIndexName
 343:                                                 + " has non-void return type");
 344:                       }
 345:                   }
 346:                 catch(NoSuchMethodException E)
 347:                   {
 348:                   }
 349:               }
 350:           }
 351:         else if(setIndexName != null)
 352:           {
 353:             Method[] m = beanClass.getMethods();
 354:             for(int i=0;i<m.length;i++)
 355:               {
 356:                 Method current = m[i];
 357:                 if(current.getName().equals(setIndexName)
 358:                    && current.getParameterTypes().length == 2
 359:                    && (current.getParameterTypes()[0])
 360:                    .equals(java.lang.Integer.TYPE)
 361:                    && current.getReturnType().equals(java.lang.Void.TYPE))
 362:                   {
 363:                     if(setIndex != null)
 364:                       {
 365:                         throw new IntrospectionException("Multiple, different "
 366:                                      + "set methods found that fit the bill!");
 367:                       }
 368:                     else
 369:                       {
 370:                         setIndex = current;
 371:                         indexedPropertyType = current.getParameterTypes()[1];
 372:                       }
 373:                   }
 374:               }
 375:             if(setIndex == null)
 376:               {
 377:                 throw new IntrospectionException("Cannot find get or set "
 378:                                                  + "methods.");
 379:               }
 380:           }
 381:         else
 382:           {
 383:            throw new IntrospectionException("Cannot find get or set methods.");
 384:           }
 385: 
 386:         Class arrayType = Array.newInstance(indexedPropertyType,0).getClass();
 387: 
 388:         Class[] setArgs = new Class[1];
 389:         setArgs[0] = arrayType;
 390:         try
 391:           {
 392:             setMethod = beanClass.getMethod(setMethodName,setArgs);
 393:             if (!setMethod.getReturnType().equals(java.lang.Void.TYPE))
 394:               {
 395:                 setMethod = null;
 396:               }
 397:           }
 398:         catch(NoSuchMethodException E)
 399:           {
 400:           }
 401: 
 402:         Class[] getArgs = new Class[0];
 403:         try
 404:           {
 405:             getMethod = beanClass.getMethod(getMethodName,getArgs);
 406:             if (!getMethod.getReturnType().equals(arrayType))
 407:               {
 408:                 getMethod = null;
 409:               }
 410:           }
 411:         catch(NoSuchMethodException E)
 412:           {
 413:           }
 414:       }
 415:     catch(SecurityException E)
 416:       {
 417:         throw new IntrospectionException("SecurityException while trying to "
 418:                                          + "find methods.");
 419:       }
 420:   }
 421: }