org.ajax4jsf.renderkit.compiler.MethodCallElement.java Source code

Java tutorial

Introduction

Here is the source code for org.ajax4jsf.renderkit.compiler.MethodCallElement.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.ajax4jsf.renderkit.compiler;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.FacesException;
import javax.faces.el.MethodNotFoundException;

import org.ajax4jsf.Messages;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;

/**
 * @author asmirnov@exadel.com (latest modification by $Author: alexsmirnov $)
 * @version $Revision: 1.1.2.1 $ $Date: 2007/01/09 18:57:47 $
 *
 */
public class MethodCallElement extends ElementBase {

    public static final String UTILS_PREFIX = "utils.";

    static final Log _log = LogFactory.getLog(MethodCallElement.class);

    private String _name = null;

    private List parameters = new ArrayList();

    private Invoker invoker = new Invoker();

    private MethodCacheState state = new MethodCacheState();

    /* (non-Javadoc)
     * @see org.ajax4jsf.renderkit.compiler.CompiledXML#encode(javax.faces.render.Renderer, javax.faces.context.FacesContext, javax.faces.component.UIComponent)
     */
    public void encode(TemplateContext context) throws IOException {
        getValue(context);
    }

    /* (non-Javadoc)
     * @see org.ajax4jsf.renderkit.compiler.ElementBase#encode(org.ajax4jsf.renderkit.compiler.TemplateContext, java.lang.String)
     */
    public void encode(TemplateContext context, String breakPoint) throws IOException {
        // Text not contain breakpoints.
        encode(context);
    }

    public Object getValue(TemplateContext context) throws FacesException {
        // prepare method params. we attempt to call 3 signatures :
        // a) name(FacesContext,UIComponent [, param0...])
        // b) name(TempalateContext [,param0...])
        // c) name([param0...])
        state.init(parameters);
        Object[] values = state.computeParameterValues(context);

        InvokeData data = null;
        synchronized (state) {
            state.update(context, values, invoker);
            data = invoker.invokeMethod(context, state);
        }
        return invoker.invokeMethod(data);
        // perform childrens.
        // super.encode(renderer,context,component);
    }

    public void addParameter(MethodParameterElement parameter) {
        parameters.add(parameter);
    }

    /**
     * @return Returns the methodName.
     */
    public String getName() {
        return _name;
    }

    /**
     * @param methodName The methodName to set.
     */
    public void setName(String methodName) {
        if (methodName.startsWith(UTILS_PREFIX)) {
            this._name = methodName.substring(UTILS_PREFIX.length());
            this.invoker = getRendererUtilsInvoker(_name);
        } else if (methodName.indexOf('.') >= 0) {
            this._name = methodName;
            this.invoker = getStaticInvoker(_name);
        } else {
            this._name = methodName;
            this.invoker = getRendererInvoker(_name);
        }
    }

    static Map staticInvokers = new HashMap();

    public StaticInvoker getStaticInvoker(String methodName) {
        StaticInvoker invoker = (StaticInvoker) staticInvokers.get(methodName);
        if (invoker == null) {
            invoker = new StaticInvoker(methodName);
            staticInvokers.put(methodName, invoker);
        }
        return invoker;
    }

    static Map rendererInvokers = new HashMap();

    public RendererInvoker getRendererInvoker(String methodName) {
        RendererInvoker invoker = (RendererInvoker) rendererInvokers.get(methodName);
        if (invoker == null) {
            invoker = new RendererInvoker(false, methodName);
            rendererInvokers.put(methodName, invoker);
        }
        return invoker;
    }

    static Map utilsInvokers = new HashMap();

    public RendererInvoker getRendererUtilsInvoker(String methodName) {
        RendererInvoker invoker = (RendererInvoker) utilsInvokers.get(methodName);
        if (invoker == null) {
            invoker = new RendererInvoker(true, methodName);
            utilsInvokers.put(methodName, invoker);
        }
        return invoker;
    }

    public String getTag() {
        return HtmlCompiler.NS_PREFIX + HtmlCompiler.CALL_TAG;
    }

    /* (non-Javadoc)
     * @see org.ajax4jsf.renderkit.compiler.ElementBase#setParent(org.ajax4jsf.renderkit.compiler.PreparedTemplate)
     */
    public void setParent(PreparedTemplate parent) throws SAXException {
        super.setParent(parent);
        if (getName() == null) {
            throw new SAXException(Messages.getMessage(Messages.NO_NAME_ATTRIBUTE_ERROR, getTag()));
        }
    }

    /* (non-Javadoc)
        * @see org.ajax4jsf.renderkit.compiler.ElementBase#getString(org.ajax4jsf.renderkit.compiler.TemplateContext)
        */
    public String getString(TemplateContext context) throws FacesException {
        Object result = getValue(context);
        if (null == result || result.toString().length() == 0) {
            result = "";
        }
        return result.toString();
    }

    /* (non-Javadoc)
     * @see org.ajax4jsf.renderkit.compiler.ElementBase#getAllowedClasses()
     */
    protected Class[] getAllowedClasses() {
        // TODO Auto-generated method stub
        return new Class[] { MethodParameterElement.class, ResourceElement.class };
    }

}

class InvokeData {
    TemplateContext context;
    Method method;
    Object object;
    Object[] arguments;

    InvokeData(TemplateContext context, Method method, Object object, Object[] arguments) {
        this.context = context;
        this.method = method;
        this.object = object;
        this.arguments = (Object[]) arguments.clone();
    }
}

class Invoker {
    String methodName;

    InvokeData invokeMethod(TemplateContext context, MethodCacheState state) {
        throw new FacesException(Messages.getMessage(Messages.RENDERER_METHOD_NOT_SET_ERROR));
    }

    void handleInvocationTargetException(TemplateContext context, InvocationTargetException e) {
    }

    void handleIllegalAccessException(TemplateContext context, IllegalAccessException e) {
    }

    void handleMethodNotFoundException(TemplateContext context) throws MethodNotFoundException {
    }

    InvokeData invokeMethod(TemplateContext context, Map methods, Class cls, Object object,
            MethodCacheState state) {
        Method method = provideMethod(methods, cls, object, state);
        return new InvokeData(context, method, object, state.current.arguments);
    }

    Object invokeMethod(InvokeData data) {
        if (data.method != null) {
            try {
                return data.method.invoke(data.object, data.arguments);
            } catch (InvocationTargetException e) {
                handleInvocationTargetException(data.context, e);
            } catch (IllegalAccessException e) {
                handleIllegalAccessException(data.context, e);
            }
        }
        handleMethodNotFoundException(data.context);
        return null;
    }

    public Class getInvokedClass(TemplateContext context) {
        return null;
    }

    private Method provideMethod(Map methods, Class cls, Object object, MethodCacheState state) {
        if (state.method != null)
            return state.method;

        if (methods.size() > 0) {
            for (int i = 0; i < state.signatures.length; i++) {
                state.method = (Method) methods.get(getClassesKey(state.signatures[i].arguments));
                if (state.method != null) {
                    state.current = state.signatures[i];
                    return state.method;
                }
            }
        }

        if (cls == null && object != null)
            cls = object.getClass();
        Method[] ms = cls.getMethods();
        for (int m = 0; m < ms.length; m++) {
            if (!ms[m].getName().equals(methodName))
                continue;
            if (object == null && !Modifier.isStatic(ms[m].getModifiers()))
                continue;
            Class[] cs = ms[m].getParameterTypes();
            Signature s = getMatchingArguments(cs, state.signatures);
            if (s == null)
                continue;
            state.current = s;
            state.method = ms[m];
            methods.put(getClassesKey(s.arguments), ms[m]);
            return state.method;
        }

        return null;
    }

    private String getClassesKey(Object[] args) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < args.length; i++) {
            String dk = args[i] == null ? "null" : args[i].getClass().getName();
            sb.append(";").append(dk);
        }
        return sb.toString();
    }

    private Signature getMatchingArguments(Class[] cs, Signature[] sgs) {
        for (int i = 0; i < sgs.length; i++) {
            if (isMatching(cs, sgs[i].arguments))
                return sgs[i];
        }
        return null;
    }

    static boolean isMatching(Class[] cs, Object[] args) {
        if (cs.length != args.length)
            return false;
        for (int i = 0; i < cs.length; i++) {
            if (args[i] != null && !cs[i].isAssignableFrom(args[i].getClass()))
                return false;
        }
        return true;
    }
}

class RendererInvoker extends Invoker {
    boolean utils = false;
    Map renderers = new HashMap();

    public RendererInvoker(boolean utils, String methodName) {
        this.utils = utils;
        this.methodName = methodName;
    }

    public Class getInvokedClass(TemplateContext context) {
        Object object = getInvokedObject(context);
        return (object != null) ? object.getClass() : null;
    }

    private Object getInvokedObject(TemplateContext context) {
        if (utils) {
            return context.getRenderer().getUtils();
        } else {
            return context.getRenderer();
        }
    }

    InvokeData invokeMethod(TemplateContext context, MethodCacheState state) {
        Object object = getInvokedObject(context);
        Map methods = getMethods(object);
        return invokeMethod(context, methods, null, object, state);
    }

    private Map getMethods(Object object) {
        if (object == null)
            return null;
        Map methods = (Map) renderers.get(object);
        if (methods == null) {
            methods = new HashMap();
            renderers.put(object, methods);
        }
        return methods;
    }

    void handleInvocationTargetException(TemplateContext context, InvocationTargetException e) {
        String logMessage = (utils)
                ? Messages.getMessage(Messages.METHOD_CALL_ERROR_1, methodName, context.getComponent().getId())
                : Messages.getMessage(Messages.METHOD_CALL_ERROR_1a, methodName, context.getComponent().getId());
        String excMessage = (utils)
                ? Messages.getMessage(Messages.METHOD_CALL_ERROR_2,
                        new Object[] { methodName, context.getComponent().getId(), e.getCause().getMessage() })
                : Messages.getMessage(Messages.METHOD_CALL_ERROR_2a,
                        new Object[] { methodName, context.getComponent().getId(), e.getCause().getMessage() });
        MethodCallElement._log.error(logMessage, e);
        throw new FacesException(excMessage, e);
    }

    void handleIllegalAccessException(TemplateContext context, IllegalAccessException e) {
        String logMessage = (utils)
                ? Messages.getMessage(Messages.getMessage(Messages.METHOD_CALL_ERROR_3, methodName,
                        context.getComponent().getId()))
                : Messages.getMessage(Messages.getMessage(Messages.METHOD_CALL_ERROR_3a, methodName,
                        context.getComponent().getId()));
        String excMessage = (utils)
                ? Messages.getMessage(Messages.getMessage(Messages.METHOD_CALL_ERROR_4,
                        new Object[] { methodName, context.getComponent().getId(), e.getCause().getMessage() }))
                : Messages.getMessage(Messages.getMessage(Messages.METHOD_CALL_ERROR_4a,
                        new Object[] { methodName, context.getComponent().getId(), e.getCause().getMessage() }));
        MethodCallElement._log.error(logMessage, e);
        throw new FacesException(excMessage, e);
    }

    void handleMethodNotFoundException(TemplateContext context) throws MethodNotFoundException {
        String logMessage = (utils)
                ? Messages.getMessage(Messages.getMessage(Messages.METHOD_CALL_ERROR_5, methodName,
                        context.getComponent().getId()))
                : Messages.getMessage(Messages.getMessage(Messages.METHOD_CALL_ERROR_5a, methodName,
                        context.getComponent().getId()));
        String excMessage = (utils)
                ? Messages.getMessage(Messages.getMessage(Messages.METHOD_CALL_ERROR_6, methodName))
                : Messages.getMessage(Messages.getMessage(Messages.METHOD_CALL_ERROR_6a, methodName));
        MethodCallElement._log.error(logMessage);
        throw new FacesException(excMessage);
    }
}

class StaticInvoker extends Invoker {
    String className;
    Class cls;
    Map methods = new HashMap();

    StaticInvoker(String methodName) {
        this.methodName = methodName;
        int i = methodName.lastIndexOf('.');
        className = methodName.substring(0, i);
        this.methodName = methodName.substring(i + 1);
        try {
            cls = Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            //ignore, throw exception when invoking
        }
    }

    InvokeData invokeMethod(TemplateContext context, MethodCacheState state) {
        if (cls == null)
            throw new FacesException(className, new ClassNotFoundException(className));
        return invokeMethod(context, methods, cls, null, state);
    }

    void handleInvocationTargetException(TemplateContext context, InvocationTargetException e) {
        MethodCallElement._log.error(
                Messages.getMessage(Messages.METHOD_CALL_ERROR_1a, methodName, context.getComponent().getId()), e);
        throw new FacesException(Messages.getMessage(Messages.METHOD_CALL_ERROR_2a,
                new Object[] { methodName, context.getComponent().getId(), e.getCause().getMessage() }), e);
    }

    void handleIllegalAccessException(TemplateContext context, IllegalAccessException e) {
        MethodCallElement._log.error(
                Messages.getMessage(Messages.METHOD_CALL_ERROR_3a, methodName, context.getComponent().getId()), e);
        throw new FacesException(Messages.getMessage(Messages.METHOD_CALL_ERROR_4a,
                new Object[] { methodName, context.getComponent().getId(), e.getCause().getMessage() }), e);
    }

    void handleMethodNotFoundException(TemplateContext context) throws MethodNotFoundException {
        MethodCallElement._log.error(
                Messages.getMessage(Messages.METHOD_CALL_ERROR_5a, methodName, context.getComponent().getId()));
        throw new MethodNotFoundException(Messages.getMessage(Messages.METHOD_CALL_ERROR_6a, methodName));
    }
}

class MethodCacheState {
    private MethodParameterElement[] elements;
    private Class[] parameterClasses;
    private Object[] parameterValues;
    private Class lastCallingClass = null;
    public Method method;
    public Signature current;
    public Signature[] signatures;

    public void init(List parameters) {
        if (elements != null)
            return;
        synchronized (this) {
            if (elements != null)
                return;
            int size = parameters.size();
            parameterClasses = new Class[size];
            parameterValues = new Object[size];
            signatures = new Signature[3];
            signatures[0] = new Signature2(size);
            signatures[1] = new Signature1(size);
            signatures[2] = new Signature0(size);
            elements = (MethodParameterElement[]) parameters.toArray(new MethodParameterElement[0]);
        }
    }

    public Object[] computeParameterValues(TemplateContext context) {
        Object[] ps = new Object[elements.length];
        for (int i = 0; i < elements.length; i++) {
            ps[i] = elements[i].valueGetter.getValueOrDefault(context);
        }
        return ps;
    }

    public void update(TemplateContext context, Object[] values, Invoker invoker) {
        boolean changed = false;
        for (int i = 0; i < elements.length; i++) {
            Object parametr = values[i];
            Class c = (parametr == null) ? null : parametr.getClass();
            if (c != parameterClasses[i]) {
                parameterClasses[i] = c;
                changed = true;
            }
            parameterValues[i] = parametr;
        }
        if (method != null && !changed && lastCallingClass != invoker.getInvokedClass(context)) {
            lastCallingClass = invoker.getInvokedClass(context);
            changed = true;
        }
        if (changed || current == null) {
            for (int i = 0; i < signatures.length; i++) {
                signatures[i].update(context, parameterValues);
            }
            method = null;
            current = null;
        } else {
            current.update(context, parameterValues);
        }
    }

}

abstract class Signature {
    Object[] arguments;

    Signature() {
    }

    void update(TemplateContext context, Object[] parameters) {
    }
}

class Signature0 extends Signature {
    Signature0(int size) {
        arguments = new Object[size];
    }

    void update(TemplateContext context, Object[] parameters) {
        System.arraycopy(parameters, 0, arguments, 0, parameters.length);
    }

}

class Signature1 extends Signature {
    Signature1(int size) {
        arguments = new Object[size + 1];
    }

    void update(TemplateContext context, Object[] parameters) {
        arguments[0] = context;
        System.arraycopy(parameters, 0, arguments, 1, parameters.length);
    }

}

class Signature2 extends Signature {
    Signature2(int size) {
        arguments = new Object[size + 2];
    }

    void update(TemplateContext context, Object[] parameters) {
        arguments[0] = context.getFacesContext();
        arguments[1] = context.getComponent();
        System.arraycopy(parameters, 0, arguments, 2, parameters.length);
    }
}