001 package org.apache.myfaces.tobago.component; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more 005 * contributor license agreements. See the NOTICE file distributed with 006 * this work for additional information regarding copyright ownership. 007 * The ASF licenses this file to You under the Apache License, Version 2.0 008 * (the "License"); you may not use this file except in compliance with 009 * the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 import javax.el.ELContext; 021 import javax.el.ELException; 022 import javax.el.MethodExpression; 023 import javax.el.MethodInfo; 024 import javax.el.MethodNotFoundException; 025 import javax.faces.FacesException; 026 import javax.faces.component.StateHolder; 027 import javax.faces.context.FacesContext; 028 import javax.faces.el.EvaluationException; 029 import javax.faces.el.MethodBinding; 030 031 @SuppressWarnings("deprecation") 032 public class MethodBindingToMethodExpression extends MethodExpression implements StateHolder { 033 private MethodBinding methodBinding; 034 035 private boolean transientFlag; 036 037 private transient MethodInfo methodInfo; 038 039 /** 040 * No-arg constructor used during restoreState 041 */ 042 public MethodBindingToMethodExpression() { 043 } 044 045 /** 046 * Creates a new instance of MethodBindingToMethodExpression 047 * @param methodBinding The MethodBinding to wrap. 048 */ 049 public MethodBindingToMethodExpression(MethodBinding methodBinding) { 050 checkNullArgument(methodBinding, "methodBinding"); 051 this.methodBinding = methodBinding; 052 } 053 054 /** 055 * Return the wrapped MethodBinding. 056 * @return the wrapped MethodBinding 057 */ 058 public MethodBinding getMethodBinding() { 059 return methodBinding; 060 } 061 062 void setMethodBinding(MethodBinding methodBinding) { 063 this.methodBinding = methodBinding; 064 } 065 066 /** 067 * Note: MethodInfo.getParamTypes() may incorrectly return an empty class array if invoke() has not been called. 068 * 069 * @throws IllegalStateException if expected params types have not been determined. 070 */ 071 public MethodInfo getMethodInfo(ELContext context) throws ELException { 072 checkNullArgument(context, "elcontext"); 073 checkNullState(methodBinding, "methodBinding"); 074 075 if (methodInfo == null) { 076 final FacesContext facesContext = (FacesContext) context.getContext(FacesContext.class); 077 if (facesContext != null) { 078 methodInfo = invoke(new Invoker<MethodInfo>() { 079 public MethodInfo invoke() { 080 return new MethodInfo(null, methodBinding.getType(facesContext), null); 081 } 082 }); 083 } 084 } 085 return methodInfo; 086 } 087 088 public Object invoke(ELContext context, final Object[] params) throws ELException { 089 checkNullArgument(context, "elcontext"); 090 checkNullState(methodBinding, "methodBinding"); 091 final FacesContext facesContext = (FacesContext) context.getContext(FacesContext.class); 092 if (facesContext != null) { 093 return invoke(new Invoker<Object>() { 094 public Object invoke() { 095 return methodBinding.invoke(facesContext, params); 096 } 097 }); 098 } 099 return null; 100 } 101 102 public boolean isLiteralText() { 103 if (methodBinding == null) { 104 throw new IllegalStateException("methodBinding is null"); 105 } 106 String expr = methodBinding.getExpressionString(); 107 return !(expr.startsWith("#{") && expr.endsWith("}")); 108 } 109 110 public String getExpressionString() { 111 return methodBinding.getExpressionString(); 112 } 113 114 public Object saveState(FacesContext context) { 115 if (!isTransient()) { 116 if (methodBinding instanceof StateHolder) { 117 Object[] state = new Object[2]; 118 state[0] = methodBinding.getClass().getName(); 119 state[1] = ((StateHolder) methodBinding).saveState(context); 120 return state; 121 } else { 122 return methodBinding; 123 } 124 } 125 return null; 126 } 127 128 public void restoreState(FacesContext context, Object state) { 129 if (state instanceof MethodBinding) { 130 methodBinding = (MethodBinding) state; 131 methodInfo = null; 132 } else if (state != null) { 133 Object[] values = (Object[]) state; 134 methodBinding = (MethodBinding) newInstance(values[0].toString()); 135 ((StateHolder) methodBinding).restoreState(context, values[1]); 136 methodInfo = null; 137 } 138 } 139 140 public void setTransient(boolean transientFlag) { 141 this.transientFlag = transientFlag; 142 } 143 144 public boolean isTransient() { 145 return transientFlag; 146 } 147 148 @Override 149 public int hashCode() { 150 final int prime = 31; 151 int result = 1; 152 result = prime * result + ((methodBinding == null) ? 0 : methodBinding.hashCode()); 153 return result; 154 } 155 156 @Override 157 public boolean equals(Object obj) { 158 if (this == obj) { 159 return true; 160 } 161 if (obj == null) { 162 return false; 163 } 164 if (getClass() != obj.getClass()) { 165 return false; 166 } 167 final MethodBindingToMethodExpression other = (MethodBindingToMethodExpression) obj; 168 if (methodBinding == null) { 169 if (other.methodBinding != null) { 170 return false; 171 } 172 } else if (!methodBinding.equals(other.methodBinding)) { 173 return false; 174 } 175 return true; 176 } 177 178 private void checkNullState(Object notNullInstance, String instanceName) { 179 if (notNullInstance == null) { 180 throw new IllegalStateException(instanceName + " is null"); 181 } 182 } 183 184 private void checkNullArgument(Object notNullInstance, String instanceName) { 185 if (notNullInstance == null) { 186 throw new IllegalArgumentException(instanceName + " is null"); 187 } 188 } 189 190 private <T> T invoke(Invoker<T> invoker) { 191 try { 192 return invoker.invoke(); 193 } catch (javax.faces.el.MethodNotFoundException e) { 194 throw new MethodNotFoundException(e.getMessage(), e); 195 } catch (EvaluationException e) { 196 throw new ELException(e.getMessage(), e); 197 } 198 } 199 200 private interface Invoker<T> { 201 T invoke(); 202 } 203 204 private static Object newInstance(String type) { 205 if (type == null) { 206 throw new NullPointerException("type"); 207 } 208 try { 209 try { 210 return Class.forName(type, false, Thread.currentThread().getContextClassLoader()).newInstance(); 211 } catch (ClassNotFoundException e) { 212 // ignore 213 return Class.forName(type, false, MethodBindingToMethodExpression.class.getClassLoader()).newInstance(); 214 } 215 } catch (ClassNotFoundException e) { 216 throw new FacesException(e); 217 } catch (NoClassDefFoundError e) { 218 throw new FacesException(e); 219 } catch (InstantiationException e) { 220 throw new FacesException(e); 221 } catch (IllegalAccessException e) { 222 throw new FacesException(e); 223 } 224 } 225 226 }