Java tutorial
/** * 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); } }