/******************************************************************************* * 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(); if (declaredMethods != null) { 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); } } }