001    /*
002     * Copyright 2005 Stephen J. McConnell.
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.lang;
020    
021    import java.beans.Expression;
022    import java.io.Serializable;
023    import java.lang.reflect.Field;
024    import java.lang.reflect.Array;
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.Map;
028    
029    import net.dpml.util.PropertyResolver;
030    
031    /**
032     * A object resolvable from primitive arguments.
033     *
034     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
035     * @version 1.1.0
036     */
037    public class Construct implements Value, Serializable
038    {
039       /**
040        * Serial version identifier.
041        */
042        static final long serialVersionUID = 1L;
043    
044       /**
045        * Utility operation that consolidates an array of values and supplimentary
046        * arguments to an array of objects.
047        * 
048        * @param map a map of keys and values used in symbolic target resolution
049        * @param params the value array
050        * @param args supplimentary arguments
051        * @return the consolidated argument array
052        * @exception Exception if an error occurs in argument resolution
053        */
054        public static Object[] getArgs( Map map, Value[] params, Object[] args ) throws Exception
055        {
056            ArrayList list = new ArrayList();
057            for( int i=0; i < params.length; i++ )
058            {
059                Value value = params[i];
060                Object object = value.resolve( map );
061                if( null != object )
062                {
063                    list.add( object );
064                }
065            }
066            for( int i=0; i < args.length; i++ )
067            {
068                Object value = args[i];
069                if( null != value )
070                {
071                    list.add( value );
072                }
073            }
074            return list.toArray();
075        }
076        
077        private final String m_method;
078        private final String m_target;
079        private final String m_value;
080        private final Value[] m_args;
081        private final boolean m_compound;
082    
083       /**
084        * Create a new construct using the default java.lang.String class as the base type.
085        * @param value the construct value
086        */
087        public Construct( String value )
088        {
089            this( null, null, value );
090        }
091    
092       /**
093        * Create a new construct using a supplied target defintion.  The target argument 
094        * may be either a classname or a symbolic reference in the form ${[key]}.  If the 
095        * argument is symbolic it resolved relative to a context map supplied by the 
096        * application resolving construct values.
097        *
098        * @param target a classname or symbolic reference
099        * @param value the construct value
100        */
101        public Construct( String target, String value )
102        {
103            this( target, null, value );
104        }
105    
106       /**
107        * Create a new construct using a supplied target defintion.  The target argument 
108        * may be either a classname or a symbolic reference in the form ${[key]}.  If the 
109        * argument is symbolic it is resolved relative to a context map supplied by the 
110        * application resolving construct values.  If the construct value is symbolic
111        * the implementation will attempt to expand the reference relative to a context
112        * map (if supplied) otherwise the implementation will attempt to expand the value 
113        * using system properties.
114        *
115        * @param target a classname or symbolic reference
116        * @param method the method to invoke on the target
117        * @param value the construct value
118        */
119        public Construct( String target, String method, String value )
120        {
121            m_target = target;
122            m_method = method;
123            m_value = value;
124            m_args = new Value[0];
125            m_compound = false;
126        }
127        
128       /**
129        * Create a new construct using a supplied target defintion.  The target argument 
130        * may be either a classname or a symbolic reference in the form ${[key]}.  If the 
131        * argument is symbolic it is resolved relative to a context map supplied by the 
132        * application resolving construct values. Instance values resolved from the 
133        * supplied Value[] will be used as constructor arguments when resolving the target.
134        *
135        * @param target the construct classname
136        * @param args an array of unresolved parameter values
137        */
138        public Construct( String target, Value[] args )
139        {
140            this( target, null, args );
141        }
142    
143       /**
144        * Create a new construct using a supplied target defintion.  The target argument 
145        * may be either a classname or a symbolic reference in the form ${[key]}.  If the 
146        * argument is symbolic it is resolved relative to a context map supplied by the 
147        * application resolving construct values. Instance values resolved from the 
148        * supplied Value[] will be used as method arguments when resolving the target.
149        *
150        * @param target the construct classname
151        * @param method the method to invoke on the target
152        * @param args an array of unresolved parameter values
153        */
154        public Construct( String target, String method, Value[] args )
155        {
156            if( null == args )
157            {
158                m_args = new Value[0];
159            }
160            else
161            {
162                m_args = args;
163            }
164            m_value = null;
165            m_target = target;
166            m_method = method;
167            m_compound = true;
168        }
169        
170       /**
171        * Creation of a new construct using a value directive.
172        * @param directive the value directive
173        */
174        public Construct( ValueDirective directive )
175        {
176            m_value = directive.getBaseValue();
177            m_target = directive.getTargetExpression();
178            m_method = directive.getMethodName();
179            m_compound = directive.isCompound();
180            ValueDirective[] values = directive.getValueDirectives();
181            int n = values.length;
182            m_args = new Value[ n ];
183            for( int i=0; i<n; i++ )
184            {
185                ValueDirective value = values[i];
186                m_args[i] = new Construct( value );
187            }
188        }
189        
190       /**
191        * Return TRUE if this construct is a compund construct else FALSE.
192        * @return TRUE if this ia a compound construct
193        */
194        public boolean isCompound()
195        {
196            return m_compound;
197        }
198    
199       /**
200        * Return the method name to be applied to the target object.
201        * @return the method name
202        */
203        public String getMethodName()
204        {
205            return m_method;
206        }
207    
208       /**
209        * Return the set of nested values within this value.
210        * @return the nested values array
211        */
212        public Value[] getValues()
213        {
214            return m_args;
215        }
216    
217       /**
218        * Return the classname of the resolved value.
219        * @return the classname
220        */
221        public String getBaseValue()
222        {
223            return m_value;
224        }
225    
226       /**
227        * Return the classname of the resolved value.
228        * @return the classname
229        */
230        public String getTargetExpression()
231        {
232            return m_target;
233        }
234    
235       /**
236        * Resolve an instance from the value using the context classloader.
237        * @return the resolved instance
238        * @exception Exception if an error occurs during value resolution
239        */
240        public Object resolve() throws Exception
241        {
242            return resolve( null );
243        }
244        
245       /**
246        * Resolve an instance from the value using a supplied map.
247        * @param map the context map
248        * @return the resolved instance
249        * @exception Exception if an error occurs during value resolution
250        */
251        public Object resolve( Map map ) throws Exception
252        {
253            return resolve( map, false );
254        }
255        
256       /**
257        * Resolve an instance from the value using a supplied isolation policy.
258        * @param isolate the isolation policy
259        * @return the resolved instance
260        * @exception Exception if an error occurs during value resolution
261        */
262        public Object resolve( boolean isolate ) throws Exception
263        {
264            return resolve( null, isolate );
265        }
266        
267       /**
268        * Resolve an instance from the value using a supplied context map. If any 
269        * target expressions in immediate or nested values contain a symbolic
270        * expression the value will be resolved using the supplied map.
271        *
272        * @param map the context map
273        * @param isolate the isolation policy
274        * @return the resolved instance
275        * @exception Exception if error occurs during instance resolution
276        */
277        public Object resolve( Map map, boolean isolate ) throws Exception
278        {
279            return resolve( null, map, null, isolate );
280        }
281    
282       /**
283        * Resolve an instance from the value using a supplied context map. If any 
284        * target expressions in immediate or nested values contain a symbolic
285        * expression the value will be resolved using the supplied map.
286        *
287        * @param classname the default classname
288        * @param map the context map
289        * @param isolate the isolation policy
290        * @return the resolved instance
291        * @exception Exception if error occurs during instance resolution
292        */
293        public Object resolve( String classname, Map map, boolean isolate ) throws Exception
294        {
295            return resolve( classname, map, null, isolate );
296        }
297    
298       /**
299        * Resolve an instance from the value.
300        * @param map the context map
301        * @param classloader the classloader to use
302        * @param isolate the isolation policy
303        * @return the resolved instance
304        * @exception Exception if an error occurs during value resolution
305        */
306        private Object resolve( String classname, Map map, ClassLoader classloader, boolean isolate ) throws Exception
307        {
308            ClassLoader loader = resolveClassLoader( classloader );
309            Object target = getTargetObject( classname, map, loader );
310            if( isCompound() )
311            {
312                if( null == target )
313                {
314                    throw new NullPointerException( "target" );
315                }
316                else
317                {
318                    return resolveCompoundExpression( target, map, loader );
319                }
320            }
321            else
322            {
323                Object value = resolveBaseValue( map );
324                if( null == target )
325                {
326                    return value;
327                }
328                else
329                {
330                    return resolveSimpleExpression( target, map, loader );
331                }
332            }
333        }
334        
335        private Object resolveBaseValue( Map map )
336        {
337            return expandSymbols( map, m_value );
338        }
339    
340        private Object resolveSimpleExpression( Object target, Map map, ClassLoader classloader ) throws Exception
341        {
342            String method = getMethodName();
343            Object value = expandSymbols( map, m_value );
344            boolean isaClass = ( target.getClass() == Class.class );
345            if( null == method )
346            {
347                if( isaClass )
348                {
349                    method = "new";
350                }
351                else
352                {
353                    final String error = 
354                      "Target expression '"
355                      + m_target
356                      + "' resolving to an instance of the class ["
357                      + target.getClass() 
358                      + "] canot be resolved due to missing method declaration.";
359                    throw new ValueException( error );
360                }
361            }
362            else
363            {
364                if( isaClass && ( null == value ) )
365                {
366                    // check if the method name is a static field
367                    Class c = (Class) target;
368                    try
369                    {
370                        Field field = c.getField( method );
371                        return field.get( c );
372                    }
373                    catch( NoSuchFieldException e )
374                    {
375                        // assume its a method
376                    }
377                }
378            }
379            
380            if( value == null )
381            {
382                Expression expression = new Expression( target, method, new Object[0] );
383                try
384                {
385                    return expression.getValue();
386                }
387                catch( Throwable e )
388                {
389                    final String error = 
390                      "Internal error while evalating simple expression using:"
391                      + "\n target: " 
392                      + m_target 
393                      + " (" 
394                      + target 
395                      + ")"
396                      + "\n method: " 
397                      + m_method 
398                      + " (" 
399                      + method 
400                      + ")";
401                    throw new ValueException( error, e );
402                }
403            }
404            else
405            {
406                Expression expression =  new Expression( target, method, new Object[]{value} );
407                try
408                {
409                    return expression.getValue();
410                }
411                catch( Throwable e )
412                {
413                    final String error = 
414                      "Internal error while evaluating expression using:"
415                      + "\n target: " + m_target + " (" + target + ")"
416                      + "\n method: " + m_method + " (" + method + ")"
417                      + "\n value: " + m_value + " (" + value.getClass().getName() + ")";
418                    throw new ValueException( error, e );
419                }
420            }
421        }
422        
423        private Object resolveCompoundExpression( Object target, Map map, ClassLoader classloader ) throws Exception
424        {
425            Value[] args = getValues();
426            Object[] instances = getInstanceValues( map, classloader, args );
427            String method = getMethodName();
428            boolean isaClass = ( target.getClass() == Class.class );
429            
430            //
431            // check if we are dealing with an array class and if so return and 
432            // array created from the array of nested values
433            //
434            
435            if( isaClass ) 
436            {
437                Class c = (Class) target;
438                if( c.isArray() )
439                {
440                    Class type = c.getComponentType();
441                    if( type.isPrimitive() )
442                    {
443                        return buildPrimitiveArray( type, instances );
444                    }
445                    else
446                    {
447                        Object[] result = 
448                          (Object[]) Array.newInstance( type, instances.length );
449                        for( int i=0; i<instances.length; i++ )
450                        {
451                            Object instance = instances[i];
452                            if( type.isAssignableFrom( instance.getClass() ) )
453                            {
454                                result[i] = instances[i];
455                            }
456                            else
457                            {
458                                final String error =
459                                  "Array [" 
460                                  + type.getName() 
461                                  + "] contains an invalid element [" 
462                                  + instance.getClass().getName() 
463                                  + "].";
464                                throw new ValueException( error );
465                            }
466                        }
467                        return result;
468                    }
469                }
470            }
471            
472            // otherwise its a regular expression
473            
474            if( null == method )
475            {
476                if( isaClass )
477                {
478                    method = "new";
479                }
480                else
481                {
482                    final String error = 
483                      "Missing method declaration in a composite value construct."
484                      + "\nTarget: " 
485                      + target 
486                      + " (" + target.getClass().getName() 
487                      + ")";
488                    throw new ValueException( error );
489                }
490            }
491            else
492            {
493                if( isaClass && ( instances.length == 0 ) )
494                {
495                    // check if the method name is a static field
496                    Class c = (Class) target;
497                    try
498                    {
499                        Field field = c.getField( method );
500                        return field.get( c );
501                    }
502                    catch( NoSuchFieldException e )
503                    {
504                        // assume its a method
505                    }
506                }
507            }
508            Expression expression = new Expression( target, method, instances );
509            try
510            {
511                return expression.getValue();
512            }
513            catch( Throwable e )
514            {
515                StringBuffer buffer = new StringBuffer();
516                buffer.append( "Internal error while evaluating compound expression." );
517                buffer.append( "\n target: " + m_target + " (" + target + ")" );
518                buffer.append( "\n method: " + m_method + " (" + method + ")" );
519                for( int i=0; i<instances.length; i++ )
520                {
521                    buffer.append( 
522                      "\n param " 
523                      + ( i+1 ) 
524                      + ": " 
525                      + instances[i].getClass().getName()
526                    );
527                }
528                String error = buffer.toString();
529                throw new ValueException( error, e );
530            }
531        }
532    
533        private Object buildPrimitiveArray( Class type, Object[] instances ) throws ValueException
534        {
535            Object result = Array.newInstance( type, instances.length );
536            if( Integer.TYPE == type )
537            {
538                return buildIntArray( instances );
539            }
540            else if( Short.TYPE == type )
541            {
542                return buildShortArray( instances );
543            }
544            else if( Long.TYPE == type )
545            {
546                return buildLongArray( instances );
547            }
548            else if( Byte.TYPE == type )
549            {
550                return buildByteArray( instances );
551            }
552            else if( Double.TYPE == type )
553            {
554                return buildDoubleArray( instances );
555            }
556            else if( Float.TYPE == type )
557            {
558                return buildFloatArray( instances );
559            }
560            else if( Character.TYPE == type )
561            {
562                return buildCharacterArray( instances );
563            }
564            else if( Boolean.TYPE == type )
565            {
566                return buildBooleanArray( instances );
567            }
568            else
569            {
570                final String error = 
571                  "Primitive array class [" 
572                  + type.getName() 
573                  + "] is not recognized.";
574                throw new UnsupportedOperationException( error );
575            }
576        }
577        
578        private Object buildIntArray( Object[] instances ) throws ValueException
579        {
580            Object result = Array.newInstance( Integer.TYPE, instances.length );
581            for( int i=0; i<instances.length; i++ )
582            {
583                Object instance = instances[i];
584                if( instance instanceof Integer )
585                {
586                    Integer integer = (Integer) instance;
587                    int v = integer.intValue();
588                    Array.setInt( result, i, v );
589                }
590                else
591                {
592                    final String error = 
593                      "Supplied int array argument class ["
594                      + instance.getClass().getName()
595                      + "] is not an Integer.";
596                    throw new ValueException( error );
597                }
598            }
599            return result;
600        }
601        
602        private Object buildShortArray( Object[] instances ) throws ValueException
603        {
604            Object result = Array.newInstance( Short.TYPE, instances.length );
605            for( int i=0; i<instances.length; i++ )
606            {
607                Object instance = instances[i];
608                if( instance instanceof Short )
609                {
610                    Short primitive = (Short) instance;
611                    short v = primitive.shortValue();
612                    Array.setShort( result, i, v );
613                }
614                else
615                {
616                    final String error = 
617                      "Supplied short array argument class ["
618                      + instance.getClass().getName()
619                      + "] is not an Short.";
620                    throw new ValueException( error );
621                }
622            }
623            return result;
624        }
625        
626        private Object buildLongArray( Object[] instances ) throws ValueException
627        {
628            Object result = Array.newInstance( Long.TYPE, instances.length );
629            for( int i=0; i<instances.length; i++ )
630            {
631                Object instance = instances[i];
632                if( instance instanceof Long )
633                {
634                    Long primitive = (Long) instance;
635                    long v = primitive.longValue();
636                    Array.setLong( result, i, v );
637                }
638                else
639                {
640                    final String error = 
641                      "Supplied long array argument class ["
642                      + instance.getClass().getName()
643                      + "] is not an instance of Long.";
644                    throw new ValueException( error );
645                }
646            }
647            return result;
648        }
649        
650        private Object buildByteArray( Object[] instances ) throws ValueException
651        {
652            Object result = Array.newInstance( Byte.TYPE, instances.length );
653            for( int i=0; i<instances.length; i++ )
654            {
655                Object instance = instances[i];
656                if( instance instanceof Byte )
657                {
658                    Byte primitive = (Byte) instance;
659                    byte v = primitive.byteValue();
660                    Array.setByte( result, i, v );
661                }
662                else
663                {
664                    final String error = 
665                      "Supplied byte array argument class ["
666                      + instance.getClass().getName()
667                      + "] is not an instance of Byte.";
668                    throw new ValueException( error );
669                }
670            }
671            return result;
672        }
673        
674        private Object buildDoubleArray( Object[] instances ) throws ValueException
675        {
676            Object result = Array.newInstance( Double.TYPE, instances.length );
677            for( int i=0; i<instances.length; i++ )
678            {
679                Object instance = instances[i];
680                if( instance instanceof Double )
681                {
682                    Double primitive = (Double) instance;
683                    double v = primitive.doubleValue();
684                    Array.setDouble( result, i, v );
685                }
686                else
687                {
688                    final String error = 
689                      "Supplied double array argument class ["
690                      + instance.getClass().getName()
691                      + "] is not an instance of Double.";
692                    throw new ValueException( error );
693                }
694            }
695            return result;
696        }
697        
698        private Object buildFloatArray( Object[] instances ) throws ValueException
699        {
700            Object result = Array.newInstance( Float.TYPE, instances.length );
701            for( int i=0; i<instances.length; i++ )
702            {
703                Object instance = instances[i];
704                if( instance instanceof Float )
705                {
706                    Float primitive = (Float) instance;
707                    float v = primitive.floatValue();
708                    Array.setFloat( result, i, v );
709                }
710                else
711                {
712                    final String error = 
713                      "Supplied float array argument class ["
714                      + instance.getClass().getName()
715                      + "] is not an instance of Float.";
716                    throw new ValueException( error );
717                }
718            }
719            return result;
720        }
721        
722        private Object buildCharacterArray( Object[] instances ) throws ValueException
723        {
724            Object result = Array.newInstance( Character.TYPE, instances.length );
725            for( int i=0; i<instances.length; i++ )
726            {
727                Object instance = instances[i];
728                if( instance instanceof Character )
729                {
730                    Character primitive = (Character) instance;
731                    char v = primitive.charValue();
732                    Array.setChar( result, i, v );
733                }
734                else
735                {
736                    final String error = 
737                      "Supplied char array argument class ["
738                      + instance.getClass().getName()
739                      + "] is not an instance of Character.";
740                    throw new ValueException( error );
741                }
742            }
743            return result;
744        }
745        
746        private Object buildBooleanArray( Object[] instances ) throws ValueException
747        {
748            Object result = Array.newInstance( Boolean.TYPE, instances.length );
749            for( int i=0; i<instances.length; i++ )
750            {
751                Object instance = instances[i];
752                if( instance instanceof Boolean )
753                {
754                    Boolean primitive = (Boolean) instance;
755                    boolean v = primitive.booleanValue();
756                    Array.setBoolean( result, i, v );
757                }
758                else
759                {
760                    final String error = 
761                      "Supplied boolean array argument class ["
762                      + instance.getClass().getName()
763                      + "] is not an instance of Boolean.";
764                    throw new ValueException( error );
765                }
766            }
767            return result;
768        }
769        
770        private Object[] getInstanceValues( 
771          Map map, ClassLoader classloader, Value[] args ) throws Exception
772        {
773            Object[] instances = new Object[ args.length ];
774            for( int i=0; i < args.length; i++ )
775            {
776                Value value = args[i];
777                if( value instanceof Construct )
778                {
779                    Construct construct = (Construct) value;
780                    instances[i] = construct.resolve( null, map, classloader, false );
781                }
782                else
783                {
784                    instances[i] = value.resolve( map );
785                }
786            }
787            return instances;
788        }
789    
790        private Object expandSymbols( Map map, String value )
791        {
792            if( null == value )
793            {
794                return null;
795            }
796            else
797            {
798                return parseSymbolicValue( map, value );
799            }
800        }
801        
802        private Object parseSymbolicValue( Map map, String value )
803        {
804            if( null == map )
805            {
806                return PropertyResolver.resolve( value );
807            }
808            if( value.startsWith( "${" ) && value.endsWith( "}" ) )
809            {
810                String pre = value.substring( 2 );
811                String key = pre.substring( 0, pre.length() -1 );
812                if( map.containsKey( key ) )
813                {
814                    return map.get( key );
815                }
816                else
817                {
818                    return PropertyResolver.resolve( value );
819                }
820            }
821            else
822            {
823                return PropertyResolver.resolve( value );
824            }
825        }
826        
827        /**
828         * Return the instance class using the context classloader.
829         * @return the target object or class
830         * @exception ValueException if target related error occurs
831         */
832        private Object getTargetObject( String classname, Map map, ClassLoader loader ) throws ValueException
833        {
834            if( null == m_target )
835            {
836                return getTargetObject( map, loader, classname );
837            }
838            else
839            {
840                return getTargetObject( map, loader, m_target );
841            }
842        }
843        
844        /**
845         * Return the instance class using the context classloader.
846         * @return the target object or class
847         * @exception ValueException if target related error occurs
848         */
849        private Object getTargetObject( Map map, ClassLoader loader, String target ) throws ValueException
850        {
851            if( null == target )
852            {
853                return null;
854            }
855            else if( target.startsWith( "${" ) )
856            {
857                if( null != map )
858                {
859                    String pre = target.substring( 2 );
860                    String key = pre.substring( 0, pre.length() -1 );
861                    if( map.containsKey( key ) )
862                    {
863                        return map.get( key );
864                    }
865                    else
866                    {
867                        final String error = 
868                          "Unresolvable target symbolic expression ["
869                          + target
870                          + "].";
871                        throw new ValueException( error );
872                    }
873                }
874                else
875                {
876                    String resolved = PropertyResolver.resolve( target );
877                    return getTargetObject( map, loader, resolved );
878                }
879            }
880            else
881            {
882                if( target.endsWith( "[]" ) )
883                {
884                    int n = target.length() - 2;
885                    String componentClassname = target.substring( 0, n );
886                    Class componentClass = resolveType( loader, componentClassname );
887                    return Array.newInstance( componentClass, 0 ).getClass();
888                }
889                else
890                {
891                    return resolveClass( loader, target );
892                }
893            }
894        }
895        
896        /**
897         * Return the instance class using the context classloader.
898         * @return the class
899         * @exception ComponentException if the parameter class cannot be resolved
900         */
901        private Class resolveClass( ClassLoader loader, String classname ) throws ValueException
902        {
903            try
904            {
905                //return loader.loadClass( classname ); // fails under JDK6, see bug parade 6446627 
906                return Class.forName( classname, false, loader );
907            }
908            catch( final ClassNotFoundException e )
909            {
910                if( classname.equals( "int" ) )
911                {
912                    return Integer.class;
913                }
914                else if( classname.equals( "short" ) )
915                {
916                    return Short.class;
917                }
918                else if( classname.equals( "long" ) )
919                {
920                    return Long.class;
921                }
922                else if( classname.equals( "byte" ) )
923                {
924                    return Byte.class;
925                }
926                else if( classname.equals( "double" ) )
927                {
928                    return Double.class;
929                }
930                else if( classname.equals( "float" ) )
931                {
932                    return Float.class;
933                }
934                else if( classname.equals( "char" ) )
935                {
936                    return Character.class;
937                }
938                else if( classname.equals( "boolean" ) )
939                {
940                    return Boolean.class;
941                }
942                else
943                {
944                    final String error =
945                      "Class not found ["
946                      + classname 
947                      + "].";
948                   throw new ValueException( error, e );
949                }
950            }
951        }
952    
953        /**
954         * Return the instance class using the context classloader.
955         * @return the class
956         * @exception ComponentException if the parameter class cannot be resolved
957         */
958        private Class resolveType( ClassLoader loader, String classname ) throws ValueException
959        {
960            try
961            {
962                return loader.loadClass( classname );
963            }
964            catch( final ClassNotFoundException e )
965            {
966                if( classname.equals( "int" ) )
967                {
968                    return Integer.TYPE;
969                }
970                else if( classname.equals( "short" ) )
971                {
972                    return Short.TYPE;
973                }
974                else if( classname.equals( "long" ) )
975                {
976                    return Long.TYPE;
977                }
978                else if( classname.equals( "byte" ) )
979                {
980                    return Byte.TYPE;
981                }
982                else if( classname.equals( "double" ) )
983                {
984                    return Double.TYPE;
985                }
986                else if( classname.equals( "float" ) )
987                {
988                    return Float.TYPE;
989                }
990                else if( classname.equals( "char" ) )
991                {
992                    return Character.TYPE;
993                }
994                else if( classname.equals( "boolean" ) )
995                {
996                    return Boolean.TYPE;
997                }
998                else
999                {
1000                    final String error =
1001                      "Class not found ["
1002                      + classname 
1003                      + "].";
1004                   throw new ValueException( error, e );
1005                }
1006            }
1007        }
1008        
1009        private ClassLoader resolveClassLoader( ClassLoader classloader )
1010        {
1011            if( null != classloader )
1012            {
1013                return classloader;
1014            }
1015            else
1016            {
1017                ClassLoader loader = Thread.currentThread().getContextClassLoader();
1018                if( null == loader )
1019                {
1020                    return Construct.class.getClassLoader();
1021                }
1022                else
1023                {
1024                    return loader;
1025                }
1026            }
1027        }
1028        
1029       /**
1030        * Return a string representation of the construct.
1031        * @return the string value
1032        */
1033        public String toString()
1034        {
1035            if( !m_compound )
1036            {
1037                return "construct "
1038                  + " target: " + m_target 
1039                  + " method: " + m_method 
1040                  + " value: " + m_value;
1041            }
1042            else
1043            {
1044                return "construct "
1045                  + " target: " + m_target 
1046                  + " method: " + m_method 
1047                  + " values: " + m_args.length;
1048            }
1049        }
1050        
1051       /**
1052        * Compare this instance with a supplied object for equality.
1053        * @param other the other object
1054        * @return true if the supplied instance is equal to this instance
1055        */
1056        public boolean equals( Object other )
1057        {
1058            if( null == other )
1059            {
1060                return false;
1061            }
1062            if( other instanceof Construct )
1063            {
1064                Construct construct = (Construct) other;
1065                if( !equals( m_target, construct.m_target ) )
1066                {
1067                    return false;
1068                }
1069                if( m_compound != construct.m_compound )
1070                {
1071                    return false;
1072                }
1073                if( !equals( m_method, construct.m_method ) )
1074                {
1075                    return false;
1076                }
1077                if( m_compound )
1078                {
1079                    return Arrays.equals( m_args, construct.m_args );
1080                }
1081                else
1082                {
1083                    return equals( m_value, construct.m_value );
1084                }
1085            }
1086            else
1087            {
1088                return false;
1089            }
1090        }
1091        
1092       /**
1093        * Compute the instance hashcode value.
1094        * @return the hashcode
1095        */
1096        public int hashCode()
1097        {
1098            int hash = 0;
1099            if( null != m_target )
1100            {
1101                hash ^= m_target.hashCode();
1102            }
1103            if( null != m_method )
1104            {
1105                hash ^= m_method.hashCode();
1106            }
1107            if( m_compound )
1108            {
1109                for( int i=0; i<m_args.length; i++ )
1110                {
1111                    hash ^= m_args[i].hashCode();
1112                }
1113            }
1114            else
1115            {
1116                if( m_value != null )
1117                {
1118                    hash ^= m_value.hashCode();
1119                }
1120            }
1121            return hash;
1122        }
1123        
1124        private boolean equals( Object a, Object b )
1125        {
1126            if( null == a )
1127            {
1128                return ( null == b );
1129            }
1130            else
1131            {
1132                return a.equals( b );
1133            }
1134        }
1135    }