blob: ac189b31d94c015aa554370f6fd242d35e0df774 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}
}