package org.codehaus.plexus.util.introspection;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed 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.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import org.codehaus.plexus.util.StringUtils;

/**
 * Using simple dotted expressions extract the values from a MavenProject
 * instance, For example we might want to extract a value like:
 * project.build.sourceDirectory
 *
 * @author <a href="mailto:jason@maven.org">Jason van Zyl </a>
 * @version $Id$
 */
public class ReflectionValueExtractor
{
    private static Class[] args = new Class[ 0 ];

    private static Object[] params = new Object[ 0 ];

    private static ClassMap classMap;

    private static Map classMaps = new HashMap();

    private ReflectionValueExtractor()
    {
    }
    
    public static Object evaluate( String expression, Object root )
        throws Exception
    {
        return evaluate( expression, root, true );
    }

    // TODO: don't throw Exception
    public static Object evaluate( String expression, Object root, boolean trimRootToken )
        throws Exception
    {
        // if the root token refers to the supplied root object parameter, remove it.
        if ( trimRootToken )
        {
            expression = expression.substring( expression.indexOf( '.' ) + 1 );
        }

        Object value = root;

        // ----------------------------------------------------------------------
        // Walk the dots and retrieve the ultimate value desired from the
        // MavenProject instance.
        // ----------------------------------------------------------------------

        StringTokenizer parser = new StringTokenizer( expression, "." );

        while ( parser.hasMoreTokens() )
        {
            String token = parser.nextToken();

            if ( value == null )
            {
                return null;
            }

            classMap = getClassMap( value.getClass() );

            String methodBase = StringUtils.capitalizeFirstLetter( token );
            
            String methodName = "get" + methodBase;

            Method method = classMap.findMethod( methodName, args );
            
            if ( method == null )
            {
                // perhaps this is a boolean property??
                methodName = "is" + methodBase;
                
                method = classMap.findMethod( methodName, args );
            }

            if ( method == null )
            {
                return null;
            }

            value = method.invoke( value, params );
        }

        return value;
    }

    private static ClassMap getClassMap( Class clazz )
    {
        classMap = (ClassMap) classMaps.get( clazz );

        if ( classMap == null )
        {
            classMap = new ClassMap( clazz );

            classMaps.put( clazz, classMap );
        }

        return classMap;
    }
}

@@ -714,16 +714,20 @@
 va.util.
+Weak
 HashMap;
@@ -1155,16 +1155,22 @@
 tic 
+final 
 Class%5B%5D 
 args
@@ -1165,20 +1165,26 @@
 Class%5B%5D 
-args
+CLASS_ARGS
  = new C
@@ -1210,24 +1210,30 @@
 vate static 
+final 
 Object%5B%5D par
@@ -1229,22 +1229,27 @@
 bject%5B%5D 
-params
+OBJECT_ARGS
  = new O
@@ -1269,42 +1269,209 @@
     
-private static ClassMap classMap;%0A
+/**%0A     * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected.%0A     * This approach prevents permgen space overflows due to retention of discarded%0A     * classloaders.%0A     */
 %0A   
@@ -1486,16 +1486,22 @@
  static 
+final 
 Map clas
@@ -1512,16 +1512,20 @@
 s = new 
+Weak
 HashMap(
@@ -2685,32 +2685,41 @@
  %7D%0A%0A            
+ClassMap 
 classMap = getCl
@@ -2942,36 +2942,42 @@
 od( methodName, 
-args
+CLASS_ARGS
  );%0A            
@@ -3208,12 +3208,18 @@
 me, 
-args
+CLASS_ARGS
  );%0A
@@ -3371,14 +3371,19 @@
 ue, 
-params
+OBJECT_ARGS
  );%0A
@@ -3483,32 +3483,41 @@
 )%0A    %7B%0A        
+ClassMap 
 classMap = (Clas

Fix memleak