PageRenderTime 190ms CodeModel.GetById 130ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre3/bsh/Reflect.java

#
Java | 989 lines | 605 code | 129 blank | 255 comment | 176 complexity | 5c12a78b9a23f4a2cbfe4d08be7c479e MD5 | raw file
  1/*****************************************************************************
  2 *                                                                           *
  3 *  This file is part of the BeanShell Java Scripting distribution.          *
  4 *  Documentation and updates may be found at http://www.beanshell.org/      *
  5 *                                                                           *
  6 *  Sun Public License Notice:                                               *
  7 *                                                                           *
  8 *  The contents of this file are subject to the Sun Public License Version  *
  9 *  1.0 (the "License"); you may not use this file except in compliance with *
 10 *  the License. A copy of the License is available at http://www.sun.com    * 
 11 *                                                                           *
 12 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 13 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 14 *  (C) 2000.  All Rights Reserved.                                          *
 15 *                                                                           *
 16 *  GNU Public License Notice:                                               *
 17 *                                                                           *
 18 *  Alternatively, the contents of this file may be used under the terms of  *
 19 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 20 *  provisions of LGPL are applicable instead of those above. If you wish to *
 21 *  allow use of your version of this file only under the  terms of the LGPL *
 22 *  and not to allow others to use your version of this file under the SPL,  *
 23 *  indicate your decision by deleting the provisions above and replace      *
 24 *  them with the notice and other provisions required by the LGPL.  If you  *
 25 *  do not delete the provisions above, a recipient may use your version of  *
 26 *  this file under either the SPL or the LGPL.                              *
 27 *                                                                           *
 28 *  Patrick Niemeyer (pat@pat.net)                                           *
 29 *  Author of Learning Java, O'Reilly & Associates                           *
 30 *  http://www.pat.net/~pat/                                                 *
 31 *                                                                           *
 32 *****************************************************************************/
 33
 34package bsh;
 35
 36import java.lang.reflect.*;
 37import java.io.*;
 38import java.util.Vector;
 39
 40/**
 41    All of the reflection API code lies here.  It is in the form
 42	of static utilities.  See the design note about object wrappers 
 43	in LHS.java for lamentations regarding this.
 44
 45	Note: More work to do in here to fix up the extended signature matching.
 46	need to work in a search along with findMostSpecificSignature...
 47*/
 48class Reflect {
 49
 50    /**
 51		Invoke method on object.
 52		invocation may be static (through the object instance) or dynamic.
 53		Object may be This type.
 54		
 55		The This handling is necessary here (previously thought it might 
 56		not be).
 57		@param callerInfo will be passed along in the caes where the method
 58		is a bsh scripted method.  It may be null to indicate no caller info.
 59	*/
 60/*
 61	In the case where this method calls a bsh scripted method the callstack
 62	is currently lost
 63*/
 64    public static Object invokeObjectMethod(
 65		Interpreter interpreter, Object object, String methodName, 
 66		Object[] args, SimpleNode callerInfo 
 67	) 
 68		throws ReflectError, InvocationTargetException, EvalError 
 69	{
 70        /*
 71		Interpreter.debug("invoke Method " + methodName + " on object " 
 72			+ object + " with args (");
 73		*/
 74
 75		if ( object instanceof This && !showThisMethod( methodName) ) {
 76			// This .invokeMethod() just calls the namespace invokeMethod
 77			return ((This)object).invokeMethod( 
 78				methodName, args, interpreter, null, callerInfo );
 79        } else
 80			return invokeMethod( 
 81				object.getClass(), object, methodName, args, false );
 82    }
 83
 84	/**
 85		Allow invocations of these method names on This type objects.
 86		Don't give bsh.This a chance to override their behavior.
 87	*/
 88	private static boolean showThisMethod( String name ) {
 89		return ( name.equals("getClass") || name.equals("invokeMethod") );
 90	}
 91
 92    /** 
 93		Invoke a static method.  No object instance is provided.
 94	*/
 95    public static Object invokeStaticMethod(
 96		Class clas, String methodName, Object [] args)
 97        throws ReflectError, InvocationTargetException, EvalError
 98    {
 99        Interpreter.debug("invoke static Method");
100        return invokeMethod( clas, null, methodName, args, true );
101    }
102
103    public static Object getIndex(Object array, int index)
104        throws ReflectError, TargetError
105    {
106        try {
107            Object val = Array.get(array, index);
108            return wrapPrimitive(val, array.getClass().getComponentType());
109        }
110        catch( ArrayIndexOutOfBoundsException  e1 ) {
111			throw new TargetError( "Array Index", e1 );
112        } catch(Exception e) {
113            throw new ReflectError("Array access:" + e);
114        }
115    }
116
117    public static void setIndex(Object array, int index, Object val)
118        throws ReflectError, TargetError
119    {
120        try {
121            val = unwrapPrimitive(val);
122            Array.set(array, index, val);
123        }
124        catch( ArrayStoreException e2 ) {
125			throw new TargetError( "Array store exception", e2 );
126        } catch( IllegalArgumentException e1 ) {
127			throw new TargetError( "Illegal Argument", 
128				new ArrayStoreException( e1.toString() ) );
129        } catch(Exception e) {
130            throw new ReflectError("Array access:" + e);
131        }
132    }
133
134    public static Object getStaticField(Class clas, String fieldName)
135        throws ReflectError
136    {
137        return getFieldValue(clas, null, fieldName);
138    }
139
140    public static Object getObjectField(Object object, String fieldName)
141        throws ReflectError
142    {
143		if ( object instanceof This )
144			return ((This)object).namespace.getVariable( fieldName );
145		else {
146			try {
147				return getFieldValue(object.getClass(), object, fieldName);
148			} catch ( ReflectError e ) {
149				// no field, try property acces
150
151				if ( hasObjectPropertyGetter( object.getClass(), fieldName ) )
152					return getObjectProperty( object, fieldName );
153				else
154					throw e;
155			}
156		}
157    }
158
159    static LHS getLHSStaticField(Class clas, String fieldName)
160        throws ReflectError
161    {
162        Field f = getField(clas, fieldName);
163        return new LHS(f);
164    }
165
166	/**
167		Get an LHS reference to an object field.
168
169		This method also deals with the field style property access.
170		In the field does not exist we check for a property setter.
171	*/
172    static LHS getLHSObjectField(Object object, String fieldName)
173        throws ReflectError
174    {
175		if ( object instanceof This )
176			return new LHS(((This)object).namespace, fieldName );
177
178		try {
179			Field f = getField(object.getClass(), fieldName);
180			return new LHS(object, f);
181		} catch ( ReflectError e ) {
182			// not a field, try property access
183
184			if ( hasObjectPropertySetter( object.getClass(), fieldName ) )
185				return new LHS( object, fieldName );
186			else
187				throw e;
188		}
189    }
190
191    private static Object getFieldValue(
192		Class clas, Object object, String fieldName) throws ReflectError
193    {
194        try {
195            Field f = getField(clas, fieldName);
196
197            if ( f == null )
198                throw new ReflectError("internal: field not found:"+fieldName);
199
200            Object value = f.get(object);
201            Class returnType = f.getType();
202            return wrapPrimitive(value, returnType);
203
204        }
205        catch(NullPointerException e) {
206            throw new ReflectError(
207				"???" + fieldName + " is not a static field.");
208        }
209        catch(IllegalAccessException e) {
210            throw new ReflectError("Can't access field: " + fieldName);
211        }
212    }
213
214	/**
215		All field lookup should come through here.
216		i.e. this method owns Class getField();
217	*/
218    private static Field getField(Class clas, String fieldName)
219        throws ReflectError
220    {
221        try
222        {
223			if ( Capabilities.haveAccessibility() )
224				return findAccessibleField( clas, fieldName );
225			else
226				// this one only finds public 
227				return clas.getField(fieldName);
228        }
229        catch(NoSuchFieldException e)
230        {
231			// try declaredField
232            throw new ReflectError("No such field: " + fieldName );
233        }
234    }
235
236	/**
237		Used when accessibility capability is available to locate an occurrance
238		of the field in the most derived class or superclass and set its 
239		accessibility flag.
240		Note that this method is not needed in the simple non accessible
241		case because we don't have to hunt for fields.
242		Note that classes may declare overlapping private fields, so the 
243		distinction about the most derived is important.  Java doesn't normally
244		allow this kind of access (super won't show private variables) so 
245		there is no real syntax for specifying which class scope to use...
246
247		Need to improve this to handle interfaces.
248	*/
249	private static Field findAccessibleField( Class clas, String fieldName ) 
250		throws NoSuchFieldException
251	{
252		while ( clas != null )
253		{
254			try {
255				Field field = clas.getDeclaredField(fieldName);
256				if ( ReflectManager.RMSetAccessible( field ) )
257					return field;
258				// else fall through
259			}
260			catch(NoSuchFieldException e) { }
261
262			clas = clas.getSuperclass();
263		}
264		throw new NoSuchFieldException( fieldName );
265	}
266
267    /**
268        The full blown invoke method.  Everybody should come here.
269		The invoked method may be static or dynamic unless onlyStatic is set
270		(in which case object may be null).
271
272		@param onlyStatic 
273			The method located must be static, the object param may be null.
274
275		Note: Method invocation could probably be speeded up if we eliminated
276		the throwing of exceptions in the search for the proper method.
277		We could probably cache our knowledge of method structure as well.
278    */
279    private static Object invokeMethod(
280		Class clas, Object object, String name, Object[] args,
281		boolean onlyStatic
282	)
283        throws ReflectError, InvocationTargetException, EvalError
284    {
285		if ( object == Primitive.NULL )
286			throw new TargetError("Attempt to invoke method "
287				+name+" on null value", new NullPointerException() );
288		if ( object == Primitive.VOID )
289			throw new EvalError("Attempt to invoke method "
290				+name+" on undefined variable or class name" );
291
292        if (args == null)
293            args = new Object[] { };
294
295        // Simple sanity check for voids
296        // (maybe this should have been caught further up?)
297        for(int i=0; i<args.length; i++)
298            if(args[i] == Primitive.VOID)
299                throw new ReflectError("Attempt to pass void argument " +
300                    "(position " + i + ") to method: " + name);
301
302        Class returnType = null;
303        Object returnValue = null;
304
305        Class[] types = getTypes(args);
306        unwrapPrimitives(args);
307
308        try
309        {
310			// Try the easy case: Look for an accessible version of the 
311			// direct match.
312
313			Method m = null;
314			try {
315				m  = findAccessibleMethod(clas, name, types, onlyStatic);
316			} catch ( SecurityException e ) { }
317
318			if ( m == null )
319				Interpreter.debug("Exact method " + 
320					StringUtil.methodString(name, types) +
321					" not found in '" + clas.getName() + "'" );
322
323			// Next look for an assignable match
324            if ( m == null ) {
325
326				// If no args stop here
327				if ( types.length == 0 )
328					throw new ReflectError(
329						"No args "+ ( onlyStatic ? "static " : "" )
330						+"method " + StringUtil.methodString(name, types) + 
331						" not found in class'" + clas.getName() + "'");
332
333				// try to find an assignable method
334				Method[] methods = clas.getMethods();
335				if ( onlyStatic )
336					// only try the static methods
337					methods = retainStaticMethods( methods );
338
339				m = findMostSpecificMethod(name, types, methods);
340
341				// try to find an extended method
342				methods = clas.getMethods();
343				if ( m == null )
344					m = findExtendedMethod(name, args, methods);
345
346				// If we found an assignable method, make sure it's accessible
347				if ( m != null ) {
348					try {
349						m = findAccessibleMethod( clas, m.getName(), 
350							m.getParameterTypes(), onlyStatic);
351					} catch ( SecurityException e ) { }
352				}
353            }
354
355			// Found something?
356			if (m == null )
357				throw new ReflectError(
358					( onlyStatic ? "Static method " : "Method " )
359					+ StringUtil.methodString(name, types) + 
360					" not found in class'" + clas.getName() + "'");
361
362			// Invoke it
363            returnValue =  m.invoke(object, args);
364            if(returnValue == null)
365                returnValue = Primitive.NULL;
366            returnType = m.getReturnType();
367
368        } catch(IllegalAccessException e) {
369            throw new ReflectError( 
370				"Cannot access method " + StringUtil.methodString(name, types) +
371                " in '" + clas.getName() + "' :" + e);
372        }
373
374        return wrapPrimitive(returnValue, returnType);
375    }
376
377	/**
378		Return only the static methods
379	*/
380	private static Method [] retainStaticMethods( Method [] methods ) {
381		Vector v = new Vector();
382		for(int i=0; i<methods.length; i++)
383			if ( Modifier.isStatic( methods[i].getModifiers() ) )
384				v.addElement( methods[i] );
385
386		Method [] ma = new Method [ v.size() ];
387		v.copyInto( ma );
388		return ma;
389	}
390
391	/**
392		Locate a version of the method with the exact signature specified 
393		that is accessible via a public interface or through a public 
394		superclass.
395
396		This solves the problem that arises when a package private class
397		or private inner class implements a public interface or derives from
398		a public type.
399
400		@param onlyStatic the method located must be static.
401		@returns null on not found
402	*/
403	static Method findAccessibleMethod( 
404		Class clas, String name, Class [] types, boolean onlyStatic ) 
405	{
406		Method meth = null;
407		Vector classQ = new Vector();
408
409		classQ.addElement( clas );
410		Method found = null;
411		while ( classQ.size() > 0 ) 
412		{
413			Class c = (Class)classQ.firstElement();
414			classQ.removeElementAt(0);
415
416			// Is this it?
417			// Is the class public or can we use accessibility?
418			if ( Modifier.isPublic( c.getModifiers() )
419				|| ( Capabilities.haveAccessibility() 
420					&& ReflectManager.RMSetAccessible( c ) ) )
421			{
422				try {
423					meth = c.getDeclaredMethod( name, types );
424
425					// Is the method public or are we in accessibility mode?
426					if ( Modifier.isPublic( meth.getModifiers() )  
427						|| ( Capabilities.haveAccessibility() 
428							&& ReflectManager.RMSetAccessible( meth ) ) )
429					{
430						found = meth; // Yes, it is.
431						break;
432					}
433				} catch ( NoSuchMethodException e ) { 
434					// ignore and move on
435				}
436			}
437			// No, it is not.
438			
439			// Is this a class?
440			if ( !c.isInterface() ) {
441				Class superclass = c.getSuperclass();
442				if ( superclass != null )
443					classQ.addElement((Object)superclass);
444			}
445
446			// search all of its interfaces breadth first
447			Class [] intfs = c.getInterfaces();
448			for( int i=0; i< intfs.length; i++ )
449				classQ.addElement((Object)intfs[i]);
450		}
451
452		/* 
453			If we found one and it satisfies onlyStatic return it
454			
455			Note: I don't believe it is necessary to check for the static
456			condition in the above search because the Java compiler will not
457			let dynamic and static methods hide/override one another.  So
458			we simply check what is found, if any, at the end.
459		*/
460		if ( found != null &&
461			( !onlyStatic || Modifier.isStatic( found.getModifiers() ) ) )
462			return found;
463		
464		// Didn't find one
465		/*
466		Interpreter.debug(
467			"Can't find publically accessible "+
468			( onlyStatic ? " static " : "" )
469			+" version of method: "+
470			StringUtil.methodString(name, types) +
471			" in interfaces or class hierarchy of class "+clas.getName() );
472		*/
473
474		return null;
475	}
476
477    private static Object wrapPrimitive(
478		Object value, Class returnType) throws ReflectError
479    {
480        if(value == null)
481            return Primitive.NULL;
482
483        if(returnType == Void.TYPE)
484            return Primitive.VOID;
485
486        else
487            if(returnType.isPrimitive())
488            {
489                if(value instanceof Number)
490                    return new Primitive((Number)value);
491                if(value instanceof Boolean)
492                    return new Primitive((Boolean)value);
493                if(value instanceof Character)
494                    return new Primitive((Character)value);
495
496                throw new ReflectError("Something bad happened");
497            }
498            else
499                return value;
500    }
501
502    public static Class[] getTypes( Object[] args)
503    {
504        if(args == null)
505            return new Class[0];
506
507        Class[] types = new Class[args.length];
508
509        for(int i=0; i<args.length; i++)
510        {
511			if ( args[i] == null )
512				throw new InterpreterError("Null arg in getTypes()");
513
514            if(args[i] instanceof Primitive)
515                types[i] = ((Primitive)args[i]).getType();
516            else
517                types[i] = args[i].getClass();
518        }
519
520        return types;
521    }
522
523    /*
524        Replace Primitive wrappers with their java.lang wrapper values
525
526        These barf if one of the args is void...  maybe these should throw
527        an exception on void arg to force the rest of the code to clean up.
528        There are places where we don't check right now... (constructors, index)
529    */
530    private static void unwrapPrimitives(Object[] args)
531    {
532        for(int i=0; i<args.length; i++)
533            args[i] = unwrapPrimitive(args[i]);
534    }
535
536    private static Object unwrapPrimitive(Object arg)
537    {
538        if(arg instanceof Primitive)
539            return((Primitive)arg).getValue();
540        else
541            return arg;
542    }
543
544    static Object constructObject(String clas, Object[] args)
545        throws ReflectError, InvocationTargetException
546    {
547		Class c = BshClassManager.classForName( clas );
548		if ( c == null )
549			throw new ReflectError("Class not found: "+clas); 
550
551		return constructObject( c, args );
552	}
553
554	/**
555		Primary object constructor
556	*/
557    static Object constructObject(Class clas, Object[] args)
558        throws ReflectError, InvocationTargetException
559    {
560        // simple sanity check for arguments
561        for(int i=0; i<args.length; i++)
562            if(args[i] == Primitive.VOID)
563                throw new ReflectError("Attempt to pass void argument " +
564                    "(position " + i + ") to constructor for: " + clas);
565
566		if ( clas.isInterface() )
567			throw new ReflectError(
568				"Can't create instance of an interface: "+clas);
569
570        Object obj = null;
571        Class[] types = getTypes(args);
572        unwrapPrimitives(args);
573        Constructor con = null;
574
575		/* 
576			Find an appropriate constructor
577			use declared here to see package and private as well
578			(there are no inherited constructors to worry about) 
579		*/
580		Constructor[] constructors = clas.getDeclaredConstructors();
581		Interpreter.debug("Looking for most specific constructor: "+clas);
582		con = findMostSpecificConstructor(types, constructors);
583
584		if ( con == null )
585			if ( types.length == 0 )
586				throw new ReflectError(
587					"Can't find default constructor for: "+clas);
588			else
589				con = findExtendedConstructor(args, constructors);
590
591		if(con == null)
592			throw new ReflectError("Can't find constructor: " 
593				+ clas );
594
595        try {
596            obj = con.newInstance(args);
597        } catch(InstantiationException e) {
598            throw new ReflectError("the class is abstract ");
599        } catch(IllegalAccessException e) {
600            throw new ReflectError(
601				"we don't have permission to create an instance");
602        } catch(IllegalArgumentException e) {
603            throw new ReflectError("the number of arguments was wrong");
604        } 
605		if (obj == null)
606            throw new ReflectError("couldn't construct the object");
607
608        return obj;
609    }
610
611    /**
612        Implement JLS 15.11.2 for method resolution
613		@param onlyStatic  only static methods will be considered.
614		@returns null on no match
615    */
616    static Method findMostSpecificMethod(
617		String name, Class[] idealMatch, Method[] methods )
618    {
619		// Pull out the method signatures whos name matches
620		Vector sigs = new Vector();
621		Vector meths = new Vector();
622		for(int i=0; i<methods.length; i++)
623			// method matches name 
624			if ( methods[i].getName().equals( name )  ) 
625			{
626				meths.addElement( methods[i] );
627				sigs.addElement( methods[i].getParameterTypes() );
628			}
629
630		Class [][] candidates = new Class [ sigs.size() ][];
631		sigs.copyInto( candidates );
632
633		Interpreter.debug("Looking for most specific method: "+name);
634		int match = findMostSpecificSignature( idealMatch, candidates );
635		if ( match == -1 )
636			return null;
637		else
638			return (Method)meths.elementAt( match );
639    }
640
641	/**
642		This uses the NameSpace.getAssignableForm() method to determine
643		compatability of args.  This allows special (non standard Java) bsh 
644		widening operations...
645
646		@returns null on not found
647	*/
648    static Method findExtendedMethod(
649		String name, Object[] args, Method[] methods)
650    {
651        Method bestMatch = null;
652        Object[] tempArgs = new Object[args.length];
653
654        for(int i = 0; i < methods.length; i++) {
655            Method currentMethod = methods[i];
656            if ( name.equals( currentMethod.getName() )) {
657                Class[] parameters = currentMethod.getParameterTypes();
658		
659				if ( parameters.length != args.length )
660					continue;
661                try {
662                    for(int j = 0; j < parameters.length; j++)
663                        tempArgs[j] = NameSpace.getAssignableForm( 
664							args[j], parameters[j]);
665
666                    // if you get here, all the arguments were assignable
667                    System.arraycopy(tempArgs, 0, args, 0, args.length);
668                    return currentMethod;
669                } catch(EvalError e) {
670                    // do nothing (exception breaks you out of the for loop).
671                }
672            }
673        }
674
675        return null;
676    }
677
678    /*
679        This method should exactly parallel findMostSpecificMethod()
680    */
681    static Constructor findMostSpecificConstructor(Class[] idealMatch,
682        Constructor[] constructors)
683    {
684
685		Class [][] candidates = new Class [ constructors.length ] [];
686		for(int i=0; i< candidates.length; i++ )
687			candidates[i] = constructors[i].getParameterTypes();
688
689		int match = findMostSpecificSignature( idealMatch, candidates );
690		if ( match == -1 )
691			return null;
692		else
693			return constructors[ match ];
694    }
695
696
697	/**
698		This uses the NameSpace.getAssignableForm() method to determine
699		compatability of args.  This allows special (non standard Java) bsh 
700		widening operations...
701	*/
702    static Constructor findExtendedConstructor(
703		Object[] args, Constructor[] constructors )
704    {
705        Constructor bestMatch = null;
706        Object[] tempArgs = new Object[args.length];
707
708        for(int i = 0; i < constructors.length; i++)
709        {
710            Constructor currentConstructor = constructors[i];
711            Class[] parameters = currentConstructor.getParameterTypes();
712			if ( parameters.length != args.length )
713				continue;
714            try {
715                for(int j = 0; j < parameters.length; j++)
716                    tempArgs[j] = 
717						NameSpace.getAssignableForm(args[j], parameters[j]);
718
719                // if you get here, all the arguments were assignable
720                System.arraycopy(tempArgs, 0, args, 0, args.length);
721                return currentConstructor;
722            }
723            catch(EvalError e)
724            {
725                // do nothing (exception breaks you out of the for loop).
726            }
727        }
728
729        return null;
730    }
731
732
733
734	/**
735        Implement JLS 15.11.2
736		Return the index of the most specific arguments match or -1 if no	
737		match is found.
738	*/
739	static int findMostSpecificSignature(
740		Class [] idealMatch, Class [][] candidates )
741	{
742		Class [] bestMatch = null;
743		int bestMatchIndex = -1;
744
745		for (int i=0; i < candidates.length; i++) {
746			Class[] targetMatch = candidates[i];
747
748            /*
749                If idealMatch fits targetMatch and this is the first match 
750				or targetMatch is more specific than the best match, make it 
751				the new best match.
752            */
753			if ( isAssignable(idealMatch, targetMatch ) &&
754				((bestMatch == null) ||
755					isAssignable( targetMatch, bestMatch )))
756			{
757				bestMatch = targetMatch;
758				bestMatchIndex = i;
759			}
760		}
761
762		if ( bestMatch != null ) {
763			/*
764			Interpreter.debug("best match: " 
765				+ StringUtil.methodString("args",bestMatch));
766			*/
767				
768			return bestMatchIndex;
769		}
770		else {
771			Interpreter.debug("no match found");
772			return -1;
773		}
774	}
775
776	/**
777		Determine if the 'from' signature is assignable to the 'to' signature
778		'from' arg types, 'to' candidate types
779		null value in 'to' type parameter indicates loose type.
780
781		null value in either arg is considered empty array
782	*/
783    static boolean isAssignable(Class[] from, Class[] to)
784    {
785		if ( from == null )
786			from = new Class[0];
787		if ( to == null )
788			to = new Class[0];
789
790        if (from.length != to.length)
791            return false;
792
793        for(int i=0; i<from.length; i++)
794        {
795			// Null type indicates loose type.  Match anything.
796			if ( to[i] == null )
797				continue;
798
799            // Let null arg type match any reference type
800            if (from[i] == null) {
801
802                if (!(to[i].isPrimitive()))
803                    continue;
804                else
805                    return false;
806            }
807
808            if(!isAssignableFrom(to[i], from[i]))
809                return false;
810        }
811
812        return true;
813    }
814
815    /**
816		This base method is meant to address a deficiency of 
817		Class.isAssignableFrom() which does not take primitive widening 
818		conversions into account.
819
820		Note that the getAssigbableForm() method in NameSpace is the primary
821		bsh method for checking assignability.  It adds extended bsh
822		conversions, etc.
823
824		@param lhs assigning from rhs to lhs
825		@param rhs assigning from rhs to lsh
826	*/
827    static boolean isAssignableFrom(Class lhs, Class rhs)
828    {
829        if(lhs.isPrimitive() && rhs.isPrimitive())
830        {
831            if(lhs == rhs)
832                return true;
833
834            // handle primitive widening conversions - JLS 5.1.2
835            if((rhs == Byte.TYPE) && (lhs == Short.TYPE || lhs == Integer.TYPE ||
836                lhs == Long.TYPE || lhs == Float.TYPE || lhs == Double.TYPE))
837                    return true;
838
839            if((rhs == Short.TYPE) && (lhs == Integer.TYPE || lhs == Long.TYPE ||
840                lhs == Float.TYPE || lhs == Double.TYPE))
841                    return true;
842
843            if((rhs == Character.TYPE) && (lhs == Integer.TYPE || lhs == Long.TYPE ||
844                lhs == Float.TYPE || lhs == Double.TYPE))
845                    return true;
846
847            if((rhs == Integer.TYPE) && (lhs == Long.TYPE || lhs == Float.TYPE ||
848                lhs == Double.TYPE))
849                    return true;
850
851            if((rhs == Long.TYPE) && (lhs == Float.TYPE || lhs == Double.TYPE))
852                return true;
853
854            if((rhs == Float.TYPE) && (lhs == Double.TYPE))
855                return true;
856        }
857        else
858            if(lhs.isAssignableFrom(rhs))
859                return true;
860
861        return false;
862    }
863
864	private static String accessorName( String getorset, String propName ) {
865        return getorset 
866			+ String.valueOf(Character.toUpperCase(propName.charAt(0))) 
867			+ propName.substring(1);
868	}
869
870    public static boolean hasObjectPropertyGetter( 
871		Class clas, String propName ) 
872	{
873		String getterName = accessorName("get", propName );
874		try {
875			clas.getMethod( getterName, new Class [0] );
876			return true;
877		} catch ( NoSuchMethodException e ) {
878			return false;
879		}
880	}
881
882    public static boolean hasObjectPropertySetter( 
883		Class clas, String propName ) 
884	{
885		String setterName = accessorName("set", propName );
886		Class [] sig = new Class [] { clas };
887		Method [] methods = clas.getMethods();
888
889		// we don't know the right hand side of the assignment yet.
890		// has at least one setter of the right name?
891		for(int i=0; i<methods.length; i++)
892			if ( methods[i].getName().equals( setterName ) )
893				return true;
894		return false;
895	}
896
897    public static Object getObjectProperty(
898		Object obj, String propName)
899        throws ReflectError
900    {
901        String accessorName = accessorName( "get", propName );
902        Object[] args = new Object[] { };
903
904        Interpreter.debug("property access: ");
905        try {
906			try {
907            	// null interpreter, accessor doesn't need to know
908				// null callerInfo
909				return invokeObjectMethod(null, obj, accessorName, args, null);
910			} catch ( EvalError e ) {
911				// what does this mean?
912				throw new ReflectError("getter: "+e);
913			}
914        }
915        catch(InvocationTargetException e)
916        {
917            throw new ReflectError(
918			"Property accessor threw exception:" + e );
919        }
920    }
921
922    public static void setObjectProperty(
923		Object obj, String propName, Object value)
924        throws ReflectError, EvalError
925    {
926        String accessorName = accessorName( "set", propName );
927        Object[] args = new Object[] { value };
928
929        Interpreter.debug("property access: ");
930        try {
931            // null interpreter, accessor doesn't need to know
932			// null callerInfo
933            invokeObjectMethod(null, obj, accessorName, args, null);
934        }
935        catch(InvocationTargetException e)
936        {
937            throw new EvalError("Property accessor threw exception!");
938        }
939    }
940
941    /** 
942		This method is meant to convert a JVM-array class name to the correct
943    	'fully-qualified name' for the array class - JLS 6.7
944	*/
945    public static String normalizeClassName(Class type)
946    {
947        if(!type.isArray())
948            return type.getName();
949
950        StringBuffer className = new StringBuffer();
951        try
952        {
953            className.append(getArrayBaseType(type).getName());
954            for(int i = 0; i < getArrayDimensions(type); i++)
955                className.append("[]");
956        }
957        catch(Exception e) { }
958
959        return className.toString();
960    }
961
962	/**[
963		returns the dimensionality of the Class
964		returns 0 if the Class is not an array class
965	*/
966    public static int getArrayDimensions(Class arrayClass)
967    {
968        if(!arrayClass.isArray())
969            return 0;
970
971        return arrayClass.getName().lastIndexOf('[') + 1;
972    }
973
974    /**
975
976		Returns the base type of an array Class.
977    	throws ReflectError if the Class is not an array class.
978	*/
979    public static Class getArrayBaseType(Class arrayClass) throws ReflectError
980    {
981        if(!arrayClass.isArray())
982            throw new ReflectError("The class is not an array.");
983
984		return arrayClass.getComponentType();
985
986    }
987
988}
989