You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by st...@apache.org on 2011/08/03 16:59:48 UTC

svn commit: r1153525 - in /maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection: Reflector.java ReflectorException.java

Author: stephenc
Date: Wed Aug  3 14:59:47 2011
New Revision: 1153525

URL: http://svn.apache.org/viewvc?rev=1153525&view=rev
Log:
John Casey wrote this originally, and has given the OK to pull into Apache

Added:
    maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java
    maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java

Added: maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java?rev=1153525&view=auto
==============================================================================
--- maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java (added)
+++ maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java Wed Aug  3 14:59:47 2011
@@ -0,0 +1,670 @@
+package org.codehaus.plexus.util.reflection;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class used to instantiate an object using reflection. This utility
+ * hides many of the gory details needed to do this.
+ *
+ * @author John Casey
+ */
+public final class Reflector
+{
+    private static final String CONSTRUCTOR_METHOD_NAME = "$$CONSTRUCTOR$$";
+
+    private static final String GET_INSTANCE_METHOD_NAME = "getInstance";
+
+    private HashMap classMaps = new HashMap();
+
+    /** Ensure no instances of Reflector are created...this is a utility. */
+    public Reflector()
+    {
+    }
+
+    /**
+     * Create a new instance of a class, given the array of parameters... Uses
+     * constructor caching to find a constructor that matches the parameter
+     * types, either specifically (first choice) or abstractly...
+     *
+     * @param theClass
+     *            The class to instantiate
+     * @param params
+     *            The parameters to pass to the constructor
+     * @return The instantiated object
+     * @throws ReflectorException
+     *             In case anything goes wrong here...
+     */
+    public Object newInstance( Class theClass, Object[] params )
+        throws ReflectorException
+    {
+        if ( params == null )
+        {
+            params = new Object[0];
+        }
+
+        Class[] paramTypes = new Class[params.length];
+
+        for ( int i = 0, len = params.length; i < len; i++ )
+        {
+            paramTypes[i] = params[i].getClass();
+        }
+
+        try
+        {
+            Constructor con = getConstructor( theClass, paramTypes );
+
+            if ( con == null )
+            {
+                StringBuffer buffer = new StringBuffer();
+
+                buffer.append( "Constructor not found for class: " );
+                buffer.append( theClass.getName() );
+                buffer.append( " with specified or ancestor parameter classes: " );
+
+                for ( int i = 0; i < paramTypes.length; i++ )
+                {
+                    buffer.append( paramTypes[i].getName() );
+                    buffer.append( ',' );
+                }
+
+                buffer.setLength( buffer.length() - 1 );
+
+                throw new ReflectorException( buffer.toString() );
+            }
+
+            return con.newInstance( params );
+        }
+        catch ( InstantiationException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+        catch ( InvocationTargetException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+        catch ( IllegalAccessException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+    }
+
+    /**
+     * Retrieve the singleton instance of a class, given the array of
+     * parameters... Uses constructor caching to find a constructor that matches
+     * the parameter types, either specifically (first choice) or abstractly...
+     *
+     * @param theClass
+     *            The class to retrieve the singleton of
+     * @param initParams
+     *            The parameters to pass to the constructor
+     * @return The singleton object
+     * @throws ReflectorException
+     *             In case anything goes wrong here...
+     */
+    public Object getSingleton( Class theClass, Object[] initParams )
+        throws ReflectorException
+    {
+        Class[] paramTypes = new Class[initParams.length];
+
+        for ( int i = 0, len = initParams.length; i < len; i++ )
+        {
+            paramTypes[i] = initParams[i].getClass();
+        }
+
+        try
+        {
+            Method method = getMethod( theClass, GET_INSTANCE_METHOD_NAME, paramTypes );
+
+            return method.invoke( null, initParams );
+        }
+        catch ( InvocationTargetException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+        catch ( IllegalAccessException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+    }
+
+    /**
+     * Invoke the specified method on the specified target with the specified
+     * params...
+     *
+     * @param target
+     *            The target of the invocation
+     * @param methodName
+     *            The method name to invoke
+     * @param params
+     *            The parameters to pass to the method invocation
+     * @return The result of the method call
+     * @throws ReflectorException
+     *             In case of an error looking up or invoking the method.
+     */
+    public Object invoke( Object target, String methodName, Object[] params )
+        throws ReflectorException
+    {
+        if ( params == null )
+        {
+            params = new Object[0];
+        }
+
+        Class[] paramTypes = new Class[params.length];
+
+        for ( int i = 0, len = params.length; i < len; i++ )
+        {
+            paramTypes[i] = params[i].getClass();
+        }
+
+        try
+        {
+            Method method = getMethod( target.getClass(), methodName, paramTypes );
+
+            if ( method == null )
+            {
+                StringBuffer buffer = new StringBuffer();
+
+                buffer.append( "Singleton-producing method named '" ).append( methodName )
+                      .append( "' not found with specified parameter classes: " );
+
+                for ( int i = 0; i < paramTypes.length; i++ )
+                {
+                    buffer.append( paramTypes[i].getName() );
+                    buffer.append( ',' );
+                }
+
+                buffer.setLength( buffer.length() - 1 );
+
+                throw new ReflectorException( buffer.toString() );
+            }
+
+            return method.invoke( target, params );
+        }
+        catch ( InvocationTargetException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+        catch ( IllegalAccessException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+    }
+
+    public Object getStaticField( Class targetClass, String fieldName )
+        throws ReflectorException
+    {
+        try
+        {
+            Field field = targetClass.getField( fieldName );
+
+            return field.get( null );
+        }
+        catch ( SecurityException e )
+        {
+            throw new ReflectorException( e );
+        }
+        catch ( NoSuchFieldException e )
+        {
+            throw new ReflectorException( e );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            throw new ReflectorException( e );
+        }
+        catch ( IllegalAccessException e )
+        {
+            throw new ReflectorException( e );
+        }
+    }
+
+    public Object getField( Object target, String fieldName )
+        throws ReflectorException
+    {
+        return getField( target, fieldName, false );
+    }
+
+    public Object getField( Object target, String fieldName, boolean breakAccessibility )
+        throws ReflectorException
+    {
+        Class targetClass = target.getClass();
+        while ( targetClass != null )
+        {
+            try
+            {
+                Field field = targetClass.getDeclaredField( fieldName );
+
+                boolean accessibilityBroken = false;
+                if ( !field.isAccessible() && breakAccessibility )
+                {
+                    field.setAccessible( true );
+                    accessibilityBroken = true;
+                }
+
+                Object result = field.get( target );
+
+                if ( accessibilityBroken )
+                {
+                    field.setAccessible( false );
+                }
+
+                return result;
+            }
+            catch ( SecurityException e )
+            {
+                throw new ReflectorException( e );
+            }
+            catch ( NoSuchFieldException e )
+            {
+                if ( targetClass == Object.class )
+                    throw new ReflectorException( e );
+                targetClass = targetClass.getSuperclass();
+            }
+            catch ( IllegalAccessException e )
+            {
+                throw new ReflectorException( e );
+            }
+        }
+        // Never reached, but needed to satisfy compiler
+        return null;
+    }
+
+    /**
+     * Invoke the specified static method with the specified params...
+     *
+     * @param targetClass
+     *            The target class of the invocation
+     * @param methodName
+     *            The method name to invoke
+     * @param params
+     *            The parameters to pass to the method invocation
+     * @return The result of the method call
+     * @throws ReflectorException
+     *             In case of an error looking up or invoking the method.
+     */
+    public Object invokeStatic( Class targetClass, String methodName, Object[] params )
+        throws ReflectorException
+    {
+        if ( params == null )
+        {
+            params = new Object[0];
+        }
+
+        Class[] paramTypes = new Class[params.length];
+
+        for ( int i = 0, len = params.length; i < len; i++ )
+        {
+            paramTypes[i] = params[i].getClass();
+        }
+
+        try
+        {
+            Method method = getMethod( targetClass, methodName, paramTypes );
+
+            if ( method == null )
+            {
+                StringBuffer buffer = new StringBuffer();
+
+                buffer.append( "Singleton-producing method named \'" + methodName
+                    + "\' not found with specified parameter classes: " );
+
+                for ( int i = 0; i < paramTypes.length; i++ )
+                {
+                    buffer.append( paramTypes[i].getName() );
+                    buffer.append( ',' );
+                }
+
+                buffer.setLength( buffer.length() - 1 );
+
+                throw new ReflectorException( buffer.toString() );
+            }
+
+            return method.invoke( null, params );
+        }
+        catch ( InvocationTargetException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+        catch ( IllegalAccessException ex )
+        {
+            throw new ReflectorException( ex );
+        }
+    }
+
+    /**
+     * Return the constructor, checking the cache first and storing in cache if
+     * not already there..
+     *
+     * @param targetClass
+     *            The class to get the constructor from
+     * @param params
+     *            The classes of the parameters which the constructor should
+     *            match.
+     * @return the Constructor object that matches.
+     * @throws ReflectorException
+     *             In case we can't retrieve the proper constructor.
+     */
+    public Constructor getConstructor( Class targetClass, Class[] params )
+        throws ReflectorException
+    {
+        Map constructorMap = getConstructorMap( targetClass );
+
+        StringBuffer key = new StringBuffer( 200 );
+
+        key.append( "(" );
+
+        for ( int i = 0, len = params.length; i < len; i++ )
+        {
+            key.append( params[i].getName() );
+            key.append( "," );
+        }
+
+        if ( params.length > 0 )
+        {
+            key.setLength( key.length() - 1 );
+        }
+
+        key.append( ")" );
+
+        Constructor constructor = null;
+
+        String paramKey = key.toString();
+
+        synchronized ( paramKey.intern() )
+        {
+            constructor = (Constructor) constructorMap.get( paramKey );
+
+            if ( constructor == null )
+            {
+                Constructor[] cands = targetClass.getConstructors();
+
+                for ( int i = 0, len = cands.length; i < len; i++ )
+                {
+                    Class[] types = cands[i].getParameterTypes();
+
+                    if ( params.length != types.length )
+                    {
+                        continue;
+                    }
+
+                    for ( int j = 0, len2 = params.length; j < len2; j++ )
+                    {
+                        if ( !types[j].isAssignableFrom( params[j] ) )
+                        {
+                            continue;
+                        }
+                    }
+
+                    // we got it, so store it!
+                    constructor = cands[i];
+                    constructorMap.put( paramKey, constructor );
+                }
+            }
+        }
+
+        if ( constructor == null )
+        {
+            throw new ReflectorException( "Error retrieving constructor object for: " + targetClass.getName()
+                + paramKey );
+        }
+
+        return constructor;
+    }
+
+    public Object getObjectProperty( Object target, String propertyName )
+        throws ReflectorException
+    {
+        Object returnValue = null;
+
+        if ( propertyName == null || propertyName.trim().length() < 1 )
+        {
+            throw new ReflectorException( "Cannot retrieve value for empty property." );
+        }
+
+        String beanAccessor = "get" + Character.toUpperCase( propertyName.charAt( 0 ) );
+        if ( propertyName.trim().length() > 1 )
+        {
+            beanAccessor += propertyName.substring( 1 ).trim();
+        }
+
+        Class targetClass = target.getClass();
+        Class[] emptyParams = {};
+
+        Method method = _getMethod( targetClass, beanAccessor, emptyParams );
+        if ( method == null )
+        {
+            method = _getMethod( targetClass, propertyName, emptyParams );
+        }
+        if ( method != null )
+        {
+            try
+            {
+                returnValue = method.invoke( target, new Object[] {} );
+            }
+            catch ( IllegalAccessException e )
+            {
+                throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'"
+                    + targetClass + "\'", e );
+            }
+            catch ( InvocationTargetException e )
+            {
+                throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'"
+                    + targetClass + "\'", e );
+            }
+        }
+
+        if ( method != null )
+        {
+            try
+            {
+                returnValue = method.invoke( target, new Object[] {} );
+            }
+            catch ( IllegalAccessException e )
+            {
+                throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'"
+                    + targetClass + "\'", e );
+            }
+            catch ( InvocationTargetException e )
+            {
+                throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'"
+                    + targetClass + "\'", e );
+            }
+        }
+        else
+        {
+            returnValue = getField( target, propertyName, true );
+            if ( method == null && returnValue == null )
+            {
+                // TODO: Check if exception is the right action! Field exists, but contains null
+                throw new ReflectorException( "Neither method: \'" + propertyName + "\' nor bean accessor: \'"
+                    + beanAccessor + "\' can be found for class: \'" + targetClass
+                    + "\', and retrieval of field: \'" + propertyName + "\' returned null as value." );
+            }
+        }
+
+        return returnValue;
+    }
+
+    /**
+     * Return the method, checking the cache first and storing in cache if not
+     * already there..
+     *
+     * @param targetClass
+     *            The class to get the method from
+     * @param params
+     *            The classes of the parameters which the method should match.
+     * @return the Method object that matches.
+     * @throws ReflectorException
+     *             In case we can't retrieve the proper method.
+     */
+    public Method getMethod( Class targetClass, String methodName, Class[] params )
+        throws ReflectorException
+    {
+        Method method = _getMethod( targetClass, methodName, params );
+
+        if ( method == null )
+        {
+            throw new ReflectorException( "Method: \'" + methodName + "\' not found in class: \'" + targetClass
+                + "\'" );
+        }
+
+        return method;
+    }
+
+    private Method _getMethod( Class targetClass, String methodName, Class[] params )
+        throws ReflectorException
+    {
+        Map methodMap = getMethodMap( targetClass, methodName );
+
+        StringBuffer key = new StringBuffer( 200 );
+
+        key.append( "(" );
+
+        for ( int i = 0, len = params.length; i < len; i++ )
+        {
+            key.append( params[i].getName() );
+            key.append( "," );
+        }
+
+        key.append( ")" );
+
+        Method method = null;
+
+        String paramKey = key.toString();
+
+        synchronized ( paramKey.intern() )
+        {
+            method = (Method) methodMap.get( paramKey );
+
+            if ( method == null )
+            {
+                Method[] cands = targetClass.getMethods();
+
+                for ( int i = 0, len = cands.length; i < len; i++ )
+                {
+                    String name = cands[i].getName();
+
+                    if ( !methodName.equals( name ) )
+                    {
+                        continue;
+                    }
+
+                    Class[] types = cands[i].getParameterTypes();
+
+                    if ( params.length != types.length )
+                    {
+                        continue;
+                    }
+
+                    for ( int j = 0, len2 = params.length; j < len2; j++ )
+                    {
+                        if ( !types[j].isAssignableFrom( params[j] ) )
+                        {
+                            continue;
+                        }
+                    }
+
+                    // we got it, so store it!
+                    method = cands[i];
+                    methodMap.put( paramKey, method );
+                }
+            }
+        }
+
+        return method;
+    }
+
+    /**
+     * Retrieve the cache of constructors for the specified class.
+     *
+     * @param theClass
+     *            the class to lookup.
+     * @return The cache of constructors.
+     * @throws ReflectorException
+     *             in case of a lookup error.
+     */
+    private Map getConstructorMap( Class theClass )
+        throws ReflectorException
+    {
+        return getMethodMap( theClass, CONSTRUCTOR_METHOD_NAME );
+    }
+
+    /**
+     * Retrieve the cache of methods for the specified class and method name.
+     *
+     * @param theClass
+     *            the class to lookup.
+     * @param methodName
+     *            The name of the method to lookup.
+     * @return The cache of constructors.
+     * @throws ReflectorException
+     *             in case of a lookup error.
+     */
+    private Map getMethodMap( Class theClass, String methodName )
+        throws ReflectorException
+    {
+        Map methodMap = null;
+
+        if ( theClass == null )
+        {
+            return null;
+        }
+
+        String className = theClass.getName();
+
+        synchronized ( className.intern() )
+        {
+            Map classMethods = (Map) classMaps.get( className );
+
+            if ( classMethods == null )
+            {
+                classMethods = new HashMap();
+                methodMap = new HashMap();
+                classMethods.put( methodName, methodMap );
+
+                classMaps.put( className, classMethods );
+            }
+            else
+            {
+                String key = className + "::" + methodName;
+
+                synchronized ( key.intern() )
+                {
+                    methodMap = (Map) classMethods.get( methodName );
+
+                    if ( methodMap == null )
+                    {
+                        methodMap = new HashMap();
+                        classMethods.put( methodName, methodMap );
+                    }
+                }
+            }
+        }
+
+        return methodMap;
+    }
+}
\ No newline at end of file

Added: maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java?rev=1153525&view=auto
==============================================================================
--- maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java (added)
+++ maven/sandbox/trunk/plexus-utils-commons-bridge/plexus-utils-commons-bridge/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java Wed Aug  3 14:59:47 2011
@@ -0,0 +1,74 @@
+package org.codehaus.plexus.util.reflection;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Exception indicating that an error has occurred while instantiating a class
+ * with the Reflector class. This exception is meant to put a more user-friendly
+ * face on the myriad other exceptions throws during reflective object creation.
+ *
+ * @author John Casey
+ */
+public class ReflectorException
+    extends Exception
+{
+    /**
+     * Create a new ReflectorException.
+     */
+    public ReflectorException()
+    {
+    }
+
+    /**
+     * Create a new ReflectorException with the specified message.
+     *
+     * @param msg
+     *            The message.
+     */
+    public ReflectorException( String msg )
+    {
+        super( msg );
+    }
+
+    /**
+     * Create a new ReflectorException with the specified root cause.
+     *
+     * @param root
+     *            The root cause.
+     */
+    public ReflectorException( Throwable root )
+    {
+        super( root );
+    }
+
+    /**
+     * Create a new ReflectorException with the specified message and root
+     * cause.
+     *
+     * @param msg
+     *            The message.
+     * @param root
+     *            The root cause.
+     */
+    public ReflectorException( String msg, Throwable root )
+    {
+        super( msg, root );
+    }
+}
\ No newline at end of file