/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/util/org/apache/slide/util/jinx/Resolver.java,v 1.1 2004/03/25 16:18:11 juergen Exp $
 * $Revision: 1.1 $
 * $Date: 2004/03/25 16:18:11 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 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.
 */

package org.apache.slide.util.jinx;

import java.io.File;
import java.lang.reflect.Modifier;
import org.apache.slide.util.cli.Abort;
import org.apache.slide.util.reflect.Function;
import org.apache.slide.util.reflect.Primitives;
import org.apache.tools.ant.Project;

/** resolves overloaded function names */
public class Resolver {
    private final Project project;
    
    public Resolver(Project project) {
        this.project = project;
    }
    
    /** CAUTION: in-place conversion of actuals */
    public Function run(Function[] fns, Object[] actuals) throws Abort {
        Function fn;
        
        fn = doRun(fns, actuals);
        checkModifiers(fn.getModifiers());
        return fn;
    }
    
    private void checkModifiers(int modif) throws Abort {
        if (!Modifier.isPublic(modif)) {
            throw new Abort("public function expected");
        }
        if (Modifier.isAbstract(modif)) {
            throw new Abort("none-abstract function expected");
        }
    }
    
    // CAUTION: in-place conversion of actuals
    private Function doRun(Function[] fns, Object[] actuals) throws Abort {
        Function fn;
        Class[] formals;
        Object[] converted;
        Object[] foundActuals;
        Function foundFn;
        int i;
        
        foundActuals = null;
        foundFn = null;
        for (i = 0; i < fns.length; i++) {
            fn = fns[i];
            formals = fn.getParameterTypes();
            converted = convert(actuals, formals);
            if (converted != null) {
                if (foundActuals != null) {
                    throw new Abort("ambiguous arguments");
                }
                foundActuals = converted;
                foundFn = fn;
            }
        }
        if (foundActuals == null) {
            throw new Abort("argument type mismatch");
        }
        System.arraycopy(foundActuals, 0, actuals, 0, foundActuals.length);
        return foundFn;
    }
    
    //--
    
    private static final Object FAILED = new Object();
    
    /** @return null if not convertible */
    private Object[] convert(Object[] actuals, Class[] formals) {
        Object[] results;
        int i;
        
        if (actuals.length != formals.length) {
            return null;
        }
        results = new Object[actuals.length];
        for (i = 0; i < results.length; i++) {
            results[i] = convert(actuals[i], formals[i]);
            if (results[i] == FAILED) {
                return null;
            }
        }
        return results;
    }
    
    /** @return FAILED if not convertible */
    private Object convert(Object actualObj, Class formal) {
        String actualStr;
        
        if (actualObj == null) {
            return null;
        }
        if (formal.isPrimitive()) {
            formal = Primitives.p.wrap(formal);
        }
        if (formal.isInstance(actualObj)) {
            return actualObj;
        }
        if (actualObj instanceof String) {
            actualStr = (String) actualObj;
            if (Boolean.class.equals(formal)) {
                if (actualStr.equals("" + true)) {
                    return Boolean.TRUE;
                }
                if (actualStr.equals("" + false)) {
                    return Boolean.FALSE;
                }
            }
            if (Integer.class.equals(formal)) {
                try {
                    return new Integer(actualStr);
                } catch (NumberFormatException e) {
                    // fall through
                }
            }
            if (String.class.equals(formal)) {
                return actualStr;
            }
            if (File.class.equals(formal)) {
                return project.resolveFile(actualStr);
            }
        }
        return FAILED;
    }
}
