| /******************************************************************************* |
| * Copyright (c) 2001, 2008 Oracle Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Oracle Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.jsf.core.internal.jem; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jem.internal.proxy.core.IBeanProxy; |
| import org.eclipse.jem.internal.proxy.core.IBeanTypeProxy; |
| import org.eclipse.jem.internal.proxy.core.IFieldProxy; |
| import org.eclipse.jem.internal.proxy.core.IIntegerBeanProxy; |
| import org.eclipse.jem.internal.proxy.core.IMethodProxy; |
| import org.eclipse.jem.internal.proxy.core.IStringBeanProxy; |
| import org.eclipse.jem.internal.proxy.core.ThrowableProxy; |
| import org.eclipse.jst.jsf.common.util.TypeUtil; |
| import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; |
| |
| /** |
| * Convenience methods for using bean proxies |
| * |
| * @author cbateman |
| * |
| */ |
| public final class BeanProxyUtil |
| { |
| |
| /** |
| * A convenience wrapper for manipulating JEM proxy types |
| * |
| */ |
| public static class BeanProxyWrapper |
| { |
| private final static IBeanProxy[] NO_ARGS = new IBeanProxy[0]; |
| private final static IBeanTypeProxy[] NO_ARG_TYPES = new IBeanTypeProxy[0]; |
| |
| private final IBeanTypeProxy _typeProxy; |
| private final IProject _project; |
| private IBeanProxy _instance; |
| |
| /** |
| * @param project |
| * @param typeProxy |
| */ |
| public BeanProxyWrapper(final IProject project, final IBeanTypeProxy typeProxy) |
| { |
| super(); |
| _typeProxy = typeProxy; |
| _project = project; |
| } |
| |
| /** |
| * Initialize a proxied instance of the type referred to by typeProxy. |
| * |
| * @throws ProxyException |
| */ |
| public void init() throws ProxyException |
| { |
| if (_instance == null) |
| { |
| try |
| { |
| _instance = _typeProxy.newInstance(); |
| } |
| catch (final ThrowableProxy e) |
| { |
| throw new ProxyException(e); |
| } |
| catch (final NoClassDefFoundError ndfe) |
| { |
| throw new ProxyException(ndfe); |
| } |
| catch (final ExceptionInInitializerError e) |
| { |
| throw new ProxyException(e); |
| } |
| } |
| } |
| |
| /** |
| * Re-initialize the proxied instance of typeProxy. |
| * |
| * @throws ProxyException |
| */ |
| public void reinit() throws ProxyException |
| { |
| _instance = null; |
| init(); |
| } |
| |
| /** |
| * @return the instance proxy |
| */ |
| public IBeanProxy getInstance() |
| { |
| return _instance; |
| } |
| |
| /** |
| * <p> |
| * Call the method called methodName on the proxied instance. If |
| * args.length is 0 then the zero-argument method is found and called. |
| * </p> |
| * |
| * @param methodName |
| * @param args |
| * @param argTypes |
| * @return the result of calling the method or null if there is no such |
| * method. |
| * @throws ProxyException |
| */ |
| public IBeanProxy call(final String methodName, |
| final IBeanProxy[] args, final IBeanTypeProxy[] argTypes) |
| throws ProxyException |
| { |
| try |
| { |
| final IMethodProxy method = getMethodProxy(methodName, argTypes); |
| /*(argTypes.length == 0) ? _typeProxy |
| .getMethodProxy(methodName) : _typeProxy |
| .getMethodProxy(methodName, argTypes);*/ |
| |
| if (method != null) |
| { |
| method.setAccessible(true); |
| return method.invoke(_instance, args); |
| } |
| } |
| catch (final ThrowableProxy tp) |
| { |
| throw new ProxyException(tp); |
| } |
| catch (final NoClassDefFoundError ndfe) |
| { |
| throw new ProxyException(ndfe); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Convenience method for call(methodName, new {@link IBeanProxy}[0], |
| * new {@link IBeanTypeProxy}[0]) |
| * |
| * @param methodName |
| * @return the proxied return value |
| * @throws ProxyException |
| */ |
| public IBeanProxy call(final String methodName) throws ProxyException |
| { |
| return call(methodName, NO_ARGS, NO_ARG_TYPES); |
| } |
| |
| /** |
| * <p> |
| * Calls the zero-argument method called 'methodName' on the proxied |
| * instance and if it results in a String return value, returns it. If |
| * the method does not return a String value, then null is returned. |
| * </p> |
| * |
| * @param methodName |
| * @return the string value or null. |
| * @throws ProxyException |
| */ |
| public String callStringMethod(final String methodName) |
| throws ProxyException |
| { |
| final IBeanProxy result = call(methodName); |
| |
| if (result instanceof IStringBeanProxy) |
| { |
| return ((IStringBeanProxy) result).stringValue(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * <p> |
| * Calls the zero-argument method called 'methodName' on the proxied |
| * instance and if it results in an Integer value, returns it. If the |
| * method does not return an integer value, then null is returned. |
| * </p> |
| * |
| * @param methodName |
| * @return the integer value or null. |
| * @throws ProxyException |
| */ |
| public Integer callIntMethod(final String methodName) |
| throws ProxyException |
| { |
| final IBeanProxy result = call(methodName, NO_ARGS, NO_ARG_TYPES); |
| |
| if (result instanceof IIntegerBeanProxy) |
| { |
| return Integer.valueOf(((IIntegerBeanProxy) result).intValue()); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Use the typeProxy for the proxied instance to try to acquire the |
| * field called fieldName of type String. Note that this won't find |
| * private fields on supertypes. |
| * |
| * Equivalent to getStringFieldValue(fieldName, _typeProxy); |
| * |
| * @param fieldName |
| * @return the String value of fieldName on the proxied instance or |
| * null. |
| * @throws ProxyException |
| */ |
| public String getStringFieldValue(final String fieldName) |
| throws ProxyException |
| { |
| return getStringFieldValue(fieldName, _typeProxy); |
| } |
| |
| /** |
| * Use the provided typeProxy to acquire the field proxy for the field |
| * proxy called fieldName. Normally, you would use the type proxy of the |
| * instance bean, however there are cases such as acquiring the value a |
| * private field on a supertype where you need the type proxy for the |
| * super type. |
| * |
| * @param fieldName |
| * @param typeProxy |
| * @return the string value or null. |
| * @throws ProxyException |
| */ |
| public String getStringFieldValue(final String fieldName, |
| final IBeanTypeProxy typeProxy) throws ProxyException |
| { |
| final IBeanProxy value = getFieldValue(fieldName, typeProxy); |
| |
| if (value instanceof IStringBeanProxy) |
| { |
| return ((IStringBeanProxy) value).stringValue(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @param fieldName |
| * @param typeProxy |
| * @return the declared field value on the proxied instance called |
| * fieldName or null. |
| * @throws ProxyException |
| */ |
| public IBeanProxy getFieldValue(final String fieldName, |
| final IBeanTypeProxy typeProxy) throws ProxyException |
| { |
| // avoid having JEM log a warning if we can prove the call to find |
| // the field will fail |
| if (!hasField(fieldName)) |
| { |
| return null; |
| } |
| |
| try |
| { |
| final IFieldProxy fieldProxy = typeProxy |
| .getDeclaredFieldProxy(fieldName); |
| |
| if (fieldProxy != null) |
| { |
| fieldProxy.setAccessible(true); |
| return fieldProxy.get(_instance); |
| } |
| } |
| catch (final ThrowableProxy e) |
| { |
| throw new ProxyException(e); |
| } |
| catch (final NoClassDefFoundError ndfe) |
| { |
| throw new ProxyException(ndfe); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Same as {@link #getFieldValue(String, IBeanTypeProxy)} except it will |
| * climb the parent hierarchy looking for the first field called |
| * fieldName. |
| * |
| * @param fieldName |
| * @param typeProxy |
| * @return the proxied value or null |
| * @throws ProxyException |
| */ |
| public IBeanProxy getFieldValueIncludeParents(final String fieldName, |
| final IBeanTypeProxy typeProxy) throws ProxyException |
| { |
| IBeanTypeProxy curType = typeProxy; |
| |
| PARENT_LOOP: while (curType != null) |
| { |
| final IBeanProxy field = getFieldValue(fieldName, curType); |
| if (field != null) |
| { |
| return field; |
| } |
| |
| try |
| { |
| IBeanTypeProxy oldType = curType; |
| curType = curType.getSuperBeanTypeProxy(); |
| // avoid infinite loop: if the parent of curType can't |
| // be resolved, JEM returns the same type, so curType |
| // never becomes null |
| if (oldType == curType) |
| { |
| break PARENT_LOOP; |
| } |
| } |
| catch (final NullPointerException npe) |
| { |
| // suppress npe caused by getSuperBeanTypeProxy |
| // not doing a null check on getSuperType() |
| curType = null; |
| } |
| } |
| |
| // have got to the top of hierarchy and not found the field |
| return null; |
| } |
| |
| /** |
| * @return the proxied instance |
| */ |
| public final IBeanProxy getBeanProxy() |
| { |
| return _instance; |
| } |
| |
| private IMethodProxy getMethodProxy(final String methodName, final IBeanTypeProxy[] argTypes) |
| { |
| IBeanTypeProxy curType = _typeProxy; |
| |
| PARENT_LOOP: while (curType != null) |
| { |
| final IMethodProxy[] declaredMethods = curType.getDeclaredMethods(); |
| final IMethodProxy foundMethod = findMethodInList(methodName, argTypes, declaredMethods); |
| if (foundMethod != null) |
| { |
| return foundMethod; |
| } |
| // avoid infinite loop: if the parent of curType can't |
| // be resolved, JEM returns the same type, so curType |
| // never becomes null |
| IBeanTypeProxy oldType = curType; |
| curType = _typeProxy.getSuperBeanTypeProxy(); |
| if (oldType == curType) |
| { |
| break PARENT_LOOP; |
| } |
| } |
| return null; |
| } |
| |
| private IMethodProxy findMethodInList(final String methodName, final IBeanTypeProxy[] argTypes, |
| final IMethodProxy[] listOfMethods) |
| { |
| METHODS_LOOP: for (final IMethodProxy methodProxy : listOfMethods) |
| { |
| if (methodName.equals(methodProxy.getName())) |
| { |
| final IBeanTypeProxy[] parameterTypes = |
| methodProxy.getParameterTypes(); |
| if (argTypes.length == parameterTypes.length) |
| { |
| for (int i = 0; i < argTypes.length; i++) |
| { |
| if (!argTypes[i].getTypeName().equals(parameterTypes[i].getTypeName())) |
| { |
| // if we find a parameter type mismatch, then |
| // skip this method; it's not it |
| continue METHODS_LOOP; |
| } |
| } |
| // if we get to here, we have a method with right name |
| // and parameters |
| return methodProxy; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private boolean hasField(final String fieldName) |
| { |
| final IType type = lazilyCalculateType(); |
| if (type != null) |
| { |
| final IField field = type.getField(fieldName); |
| return field.exists(); |
| } |
| return false; |
| } |
| |
| private IType _type; |
| private boolean _checkedType; |
| private IType lazilyCalculateType() |
| { |
| if (!_checkedType) |
| { |
| _checkedType = true; |
| final String typeName = _typeProxy.getTypeName(); |
| final IJavaProject javaProject = JavaCore.create(_project); |
| if (typeName != null && typeName.startsWith("L")) //$NON-NLS-1$ |
| { |
| _type = TypeUtil.resolveType(javaProject, typeName); |
| } |
| else |
| { |
| try |
| { |
| _type = javaProject.findType(typeName); |
| } |
| catch (JavaModelException e) |
| { |
| JSFCorePlugin.log(e, "While loading type: "+typeName); //$NON-NLS-1$ |
| } |
| } |
| } |
| return _type; |
| } |
| |
| // private Map<String, List<IMethod>> _methods; |
| // |
| // private Map<String, List<IMethod>> lazilyCalculateMethods(final IType type) |
| // { |
| // if (_methods == null) |
| // { |
| // _methods = new HashMap<String, List<IMethod>>(); |
| // final JDTBeanIntrospector introspector = new JDTBeanIntrospector(type); |
| // final IMethod[] methods = introspector.getAllMethods(); |
| // |
| // for (final IMethod method : methods) |
| // { |
| // List<IMethod> byName = _methods.get(method.getElementName()); |
| // if (byName == null) |
| // { |
| // byName = new ArrayList<IMethod>(); |
| // _methods.put(method.getElementName(), byName); |
| // } |
| // |
| // try { |
| // if (method.exists() |
| // && Flags.isPublic(method.getFlags())) |
| // { |
| // byName.add(method); |
| // } |
| // } catch (JavaModelException e) { |
| // JSFCorePlugin.log(e, "While getting flags on method: "+method.getElementName()); |
| // } |
| // } |
| // } |
| // return _methods; |
| // } |
| |
| } |
| |
| /** |
| * Checked exception the wraps problems thrown by JEM proxying into a single |
| * exception |
| * |
| */ |
| public static class ProxyException extends Exception |
| { |
| private static final long serialVersionUID = -1526057761795574331L; |
| |
| /** |
| * @param message |
| * @param cause |
| */ |
| public ProxyException(final String message, final Throwable cause) |
| { |
| super(message, cause); |
| } |
| |
| /** |
| * @param cause |
| */ |
| public ProxyException(final Throwable cause) |
| { |
| super(cause); |
| } |
| |
| } |
| } |