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    }