diff options
Diffstat (limited to 'jsf/plugins/org.eclipse.jst.jsf.core')
6 files changed, 725 insertions, 11 deletions
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/META-INF/MANIFEST.MF b/jsf/plugins/org.eclipse.jst.jsf.core/META-INF/MANIFEST.MF index 91302a143..6caff1255 100644 --- a/jsf/plugins/org.eclipse.jst.jsf.core/META-INF/MANIFEST.MF +++ b/jsf/plugins/org.eclipse.jst.jsf.core/META-INF/MANIFEST.MF @@ -37,4 +37,5 @@ Export-Package: org.eclipse.jst.jsf.core.internal;x-friends:="org.eclipse.jst.js org.eclipse.jst.jsf.core.internal.project.facet;x-friends:="org.eclipse.jst.jsf.core.tests,org.eclipse.jst.jsf.ui,org.eclipse.jst.jsf.ui.tests", org.eclipse.jst.jsf.core.internal.provisional.jsfappconfig, org.eclipse.jst.jsf.core.internal.provisional.jsflibraryregistry, - org.eclipse.jst.jsf.core.internal.types + org.eclipse.jst.jsf.core.internal.types, + org.eclipse.jst.jsf.core.internal.provisional.util diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanIntrospector.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanIntrospector.java new file mode 100644 index 000000000..e2d77a695 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanIntrospector.java @@ -0,0 +1,196 @@ +package org.eclipse.jst.jsf.core.internal.provisional.util; + +import java.beans.Introspector; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; + +/** + * A class that does bean introspector on a JDT IType + * + * @author cbateman + * + */ +public class JDTBeanIntrospector +{ + private final static String GET_PREFIX = "get"; + private final static String SET_PREFIX = "set"; + private final static String IS_PREFIX = "is"; + + private final IType _type; + + /** + * @param type + */ + public JDTBeanIntrospector(IType type) + { + _type = type; + } + + /** + * @return an map of all properties with the property names + * as keys and the values being JDTBeanProperty objects representing + * the properties. + */ + public Map getProperties() + { + final Map propertiesWorkingCopy = new HashMap(); + final IMethod[] methods = getAllMethods(); + + for (int i = 0; i < methods.length; i++) + { + final IMethod method = methods[i]; + + try + { + processPropertyMethod(method, propertiesWorkingCopy); + } + catch (JavaModelException jme) + { + // log and then proceed to next method + JSFCorePlugin.log(jme, "Error processing IMethod for bean property info"); + } + } + + final Map properties = new HashMap(); + + for (final Iterator it = propertiesWorkingCopy.keySet().iterator(); it.hasNext();) + { + final String key = (String) it.next(); + JDTBeanPropertyWorkingCopy wcopy = + (JDTBeanPropertyWorkingCopy) propertiesWorkingCopy.get(key); + properties.put(key, wcopy.toValueObject()); + } + + return properties; + } + + private void processPropertyMethod(IMethod method, Map properties) throws JavaModelException + { + // to be a bean method, it must not a constructor, must be public + // and must not be static + if (!method.isConstructor() + && (method.getFlags() & Flags.AccPublic) != 0 + && (method.getFlags() & Flags.AccStatic) == 0) + { + final String methodName = method.getElementName(); + final String returnType = method.getReturnType(); + + // either starts with get or is boolean and starts with is + + // is access must start with 'is', have a boolean return type and no parameters + final boolean startsWithIs = methodName.startsWith(IS_PREFIX) + && Signature.SIG_BOOLEAN.equals(returnType) + && method.getNumberOfParameters() == 0 + && methodName.length() > IS_PREFIX.length(); + + // get accessor must start with 'get', have no parameters and return non-void + final boolean startsWithGet = (methodName.startsWith(GET_PREFIX) + && method.getNumberOfParameters() == 0) + && !Signature.SIG_VOID.equals(returnType) + && methodName.length() > GET_PREFIX.length(); + + // mutator must start with 'set' and have one parameter and a void return type + final boolean startsWithSet = methodName.startsWith(SET_PREFIX) + && method.getNumberOfParameters() == 1 + && Signature.SIG_VOID.equals(returnType) + && methodName.length() > SET_PREFIX.length(); + + if (startsWithGet || startsWithSet || startsWithIs) + { + final String propertyName = + Introspector.decapitalize(methodName.substring(startsWithIs ? 2 : 3)); + + JDTBeanPropertyWorkingCopy workingCopy = + (JDTBeanPropertyWorkingCopy) properties.get(propertyName); + + if (workingCopy == null) + { + workingCopy = new JDTBeanPropertyWorkingCopy(_type); + properties.put(propertyName, workingCopy); + } + + if (startsWithIs) + { + workingCopy.setIsGetter(method); + } + else if (startsWithGet) + { + workingCopy.setGetter(method); + } + else if (startsWithSet) + { + workingCopy.addSetter(method); + } + } + } + } + + + /** + * @return all methods for the type including inherited ones + */ + public IMethod[] getAllMethods() + { + IMethod[] methods = new IMethod[0]; + + try + { + // type not resolved so don't proceed + if (_type != null) + { + // TODO: type hierarchy is potentially expensive, should + // cache once and listen for changes + ITypeHierarchy hierarchy = _type.newSupertypeHierarchy(new NullProgressMonitor()); + + methods = getAllMethods(hierarchy, _type); + } + } + catch(JavaModelException jme) + { + JSFCorePlugin.log(jme, "Error getting type information for bean"); + } + + return methods; + } + + /** + * @param typeHierarchy + * @param type + * @return all methods of the type and it's super types + */ + private static IMethod[] getAllMethods(final ITypeHierarchy typeHierarchy, final IType type) + { + final List methods = new ArrayList(); + final IType[] superTypes = typeHierarchy.getAllSuperclasses(type); + final IType[] closure = new IType[superTypes.length+1]; + closure[0] = type; + System.arraycopy(superTypes, 0, closure, 1, superTypes.length); + + for (int i = 0; i < superTypes.length; i++) + { + try { + final IType superType = closure[i]; + methods.addAll(Arrays.asList(superType.getMethods())); + } catch (JavaModelException e) { + JSFCorePlugin.log(e, "Error getting super type information for bean"); + } + } + + return (IMethod[]) methods.toArray(new IMethod[0]); + } + + +} diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanProperty.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanProperty.java new file mode 100644 index 000000000..f71953ee0 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanProperty.java @@ -0,0 +1,136 @@ +package org.eclipse.jst.jsf.core.internal.provisional.util; + +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; + +/** + * Represents a single bean property backed by JDT data + * + * @author cbateman + * + */ +public class JDTBeanProperty +{ + /** + * the IMethod for the accessor (either is or get) + */ + private IMethod _getter; + + /** + * the IMethod for a "set" accessor method + */ + private IMethod _setter; + + /** + * The IType that this property belongs to + */ + protected final IType _type; + + /** + * @param type + */ + protected JDTBeanProperty(IType type) + { + _type = type; + } + + /** + * @return true if this property is readable + */ + public boolean isReadable() + { + return _getter != null; + } + + /** + * @return true if this property is writable + */ + public boolean isWritable() + { + return _setter != null; + } + + + /** + * @return the get accessor IMethod or null if none + */ + public IMethod getGetter() { + return _getter; + } + + + + /** + * Set the get accessor IMethod + * @param getter -- maybe null to indicate none + */ + void setGetter(IMethod getter) { + _getter = getter; + } + + + /** + * @return the set mutator IMethod or null if none + */ + public IMethod getSetter() { + return _setter; + } + + /** + * @param setter + */ + void setSetter(IMethod setter) { + _setter = setter; + } + + /** + * @return the IType for this property's type or null if it + * cannot determined. Note that null does not necessarily indicate an error + * since some types like arrays of things do not have corresponding JDT IType's + */ + public IType getType() + { + final String typeSignature = getTypeSignature(); + return TypeUtil.resolveType(_type, typeSignature); + } + + /** + * @return the fully resolved (if possible) type signature for + * the property or null if unable to determine + */ + public String getTypeSignature() + { + try + { + String unResolvedSig = getUnresolvedType(); + final String signature = TypeUtil.resolveTypeSignature(_type, unResolvedSig); + return signature; + } + catch (JavaModelException jme) + { + JSFCorePlugin.log(jme, "Error resolving bean property type signature"); + return null; + } + } + + private String getUnresolvedType() throws JavaModelException + { + String typeSig = null; + + // first decide which method to use; getter always gets precendence + if (_getter != null) + { + typeSig = _getter.getReturnType(); + } + // TODO: if no getter or setter could we have been created? + // use setter + else + { + typeSig = _setter.getParameterTypes()[0]; + } + + return typeSig; + } +} diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanPropertyWorkingCopy.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanPropertyWorkingCopy.java new file mode 100644 index 000000000..d2c8926d4 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/JDTBeanPropertyWorkingCopy.java @@ -0,0 +1,151 @@ +package org.eclipse.jst.jsf.core.internal.provisional.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; + +/** + * A writable version of the JDTBeanProperty object + * + * @author cbateman + * + */ +public class JDTBeanPropertyWorkingCopy extends JDTBeanProperty +{ + private final List _setters; + + /** + * the IMethod for the boolean "is" accessor method + */ + private IMethod _isGetter; + + /** + * Constructor + * @param type + */ + public JDTBeanPropertyWorkingCopy(IType type) + { + super(type); + _setters = new ArrayList(); + } + + /** + * @return the bean properties spawned from this working copy + * Normally, there is only one property in the array, however, + * since this working copy represents all properties with the same + * name, there could be multiple properties since setters can + * be overloaded by name and could result in zero or one readable + * properties plus zero or more write-only properties with the same + * name. I can't see anywhere in the spec that covers this + * boundary case + */ + public JDTBeanProperty toValueObject() + { + // if the isGetter is present that it takes precedence + // over the the normal getter + IMethod getter = getIsGetter() != null ? + getIsGetter() : getGetter(); + IMethod matchedSetter = null; + + if (getter != null) + { + matchedSetter = determineMatchedSetter(getter); + } + // if there's no getter than pick any setter: there + // are bigger problem when there's no getter than + // ambiguous setters + else if (_setters.size() > 0) + { + matchedSetter = (IMethod) _setters.get(0); + } + + JDTBeanProperty beanProp = new JDTBeanProperty(_type); + beanProp.setGetter(getter); + beanProp.setSetter(matchedSetter); + return beanProp; + } + + private IMethod determineMatchedSetter(IMethod getter) + { + IMethod matchedSetter = null; + + try + { + final String getterSig = + TypeUtil.resolveTypeSignature(_type, getter.getReturnType()); + + FIND_MATCHING_SETTER:for + (final Iterator it = _setters.iterator(); it.hasNext();) + { + final IMethod setter = (IMethod) it.next(); + if (setter.getNumberOfParameters() == 1) + { + final String paramSig = + TypeUtil.resolveTypeSignature + (_type,setter.getParameterTypes()[0]); + + if (paramSig.equals(getterSig)) + { + // we've found our match since only one + // setter with the same name as the getter + // can have the same matching type for a + // single arg method + matchedSetter = setter; + break FIND_MATCHING_SETTER; + } + } + } + } + catch (JavaModelException jme) + { + JSFCorePlugin.log(jme, "Error determining getter return type, bean properties analysis may be inaccurate"); + } + + return matchedSetter; + } + + //@Override + public void setGetter(IMethod getter) { + super.setGetter(getter); + } + + /** + * @param isGetter + */ + public void setIsGetter(IMethod isGetter) { + _isGetter = isGetter; + } + + /** + * @param setter + */ + public void addSetter(IMethod setter) { + if (setter != null + && setter.getNumberOfParameters() == 1) + { + _setters.add(setter); + } + } + + /** + * Not supported on working copy. This is synthetically generated + * on toValueObject() + * @return nothing; throws exception + */ + public final IMethod getSetter() + { + throw new UnsupportedOperationException("Setter not calculated in working copy. Call toValueObject().getSetter()"); + } + + /** + * @return the "is" getter method or null if not found + */ + public IMethod getIsGetter() { + return _isGetter; + } +} diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/TypeUtil.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/TypeUtil.java new file mode 100644 index 000000000..9af926b09 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/provisional/util/TypeUtil.java @@ -0,0 +1,232 @@ +/******************************************************************************* + * Copyright (c) 2006 Oracle Corporation. + * 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: + * Cameron Bateman/Oracle - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.jst.jsf.core.internal.provisional.util; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; + +/** + * Utility for handling IType's and type signatures + * + * @author cbateman + * + */ +public final class TypeUtil +{ + static IType resolveType(final IType owningType, final String typeSignature) + { + // if type signature is already resolved then simply look it up + if (typeSignature.charAt(0) == Signature.C_RESOLVED + || (Signature.getTypeSignatureKind(typeSignature) == Signature.ARRAY_TYPE_SIGNATURE + && Signature.getElementType(typeSignature).charAt(0) == Signature.C_RESOLVED)) + { + IType type = null; + + try + { + type = owningType.getJavaProject(). + findType(getFullyQualifiedName(typeSignature)); + } + catch (JavaModelException jme) + { + // do nothing; return type == null; + } + + return type; + } + + + return resolveTypeRelative(owningType, typeSignature); + } + + + /** + * @param owningType + * @param typeSignature + * @return the resolved type signature for typeSignature in owningType + */ + public static String resolveTypeSignature(final IType owningType, final String typeSignature) + { + final int sigKind = Signature.getTypeSignatureKind(typeSignature); + + switch (sigKind) + { + case Signature.BASE_TYPE_SIGNATURE: + return typeSignature; + + case Signature.ARRAY_TYPE_SIGNATURE: + { + final String elementType = Signature.getElementType(typeSignature); + + if (Signature.getTypeSignatureKind(elementType) == Signature.BASE_TYPE_SIGNATURE) + { + return typeSignature; + } + + final String resolvedElementType = resolveSignatureRelative(owningType, elementType); + String resultType = ""; + for (int i = 0; i < Signature.getArrayCount(typeSignature);i++) + { + resultType+=Signature.C_ARRAY; + } + + return resultType+resolvedElementType; + } + + case Signature.CLASS_TYPE_SIGNATURE: + return resolveSignatureRelative(owningType, typeSignature); + + default: + return typeSignature; + } + } + + /** + * @param owningType -- type relative to which typeSignature will be resolved + * @param typeSignature -- non-array type signature + * @return the resolved type signature if possible or typeSignature if not + */ + private static String resolveSignatureRelative(final IType owningType, final String typeSignature) + { + String adjustedTypeSignature = typeSignature; + + // if already fully resolved, return the input + if (adjustedTypeSignature.charAt(0) == Signature.C_RESOLVED) + { + return typeSignature; + } + + IType resolvedType = resolveTypeRelative(owningType, adjustedTypeSignature); + + if (resolvedType != null) + { + String resolvedTypeSignature = + Signature.createTypeSignature + (resolvedType.getFullyQualifiedName(), true); + + return resolvedTypeSignature; + } + + if (Signature.getTypeSignatureKind(typeSignature) == + Signature.CLASS_TYPE_SIGNATURE) + { + // TODO: is there a better way to handle a failure to resolve + // than just garbage out? + JSFCorePlugin.log(new Exception("Failed to resolve type: "+typeSignature), "Failed to resolve type: "+typeSignature); + } + + return typeSignature; + } + + private static IType resolveTypeRelative(final IType owningType, final String typeSignature) + { + final String fullName = getFullyQualifiedName(typeSignature); + + IType resolvedType = null; + + try + { + String[][] resolved = owningType.resolveType(fullName); + + if (resolved != null && resolved.length > 0) + { + resolvedType = owningType.getJavaProject().findType(resolved[0][0], resolved[0][1]); + } + else + { + resolvedType = resolveInParents(owningType, fullName); + } + } + catch (JavaModelException jme) + { + // do nothing; newType == null + } + + return resolvedType; + } + + /** + * @param type + * @return a type signature for a type + */ + public static String getSignature(IType type) + { + final String fullyQualifiedName = type.getFullyQualifiedName(); + return Signature.createTypeSignature(fullyQualifiedName, true); + } + + + /** + * @param owner + * @param unresolvedSignature + * @return the resolved method signature for unresolvedSignature in owner + */ + public static String resolveMethodSignature(final IType owner, + final String unresolvedSignature) + { + // get the list of parameters + final String[] parameters = + Signature.getParameterTypes(unresolvedSignature); + + for (int i = 0; i < parameters.length; i++) + { + // try to full resolve the type + parameters[i] = resolveTypeSignature(owner, parameters[i]); + } + + // resolve return type + final String resolvedReturn = + resolveTypeSignature(owner, + Signature.getReturnType(unresolvedSignature)); + + return Signature.createMethodSignature(parameters, resolvedReturn); + } + + private static String getFullyQualifiedName(final String typeSignature) + { + final String packageName = Signature.getSignatureQualifier(typeSignature); + final String typeName = Signature.getSignatureSimpleName(typeSignature); + return "".equals(packageName) ? typeName : packageName + "." + typeName; + } + + private static IType resolveInParents(IType childType, String fullyQualifiedName) + throws JavaModelException + { + IType resolvedType = null; + + // not resolved? try the supertypes + final ITypeHierarchy typeHierarchy = + childType.newSupertypeHierarchy(new NullProgressMonitor()); + IType[] superTypes = typeHierarchy.getAllSupertypes(childType); + String[][] resolved; + + LOOP_UNTIL_FIRST_MATCH: + for (int i = 0; i < superTypes.length; i++) + { + IType type = superTypes[i]; + resolved = type.resolveType(fullyQualifiedName); + + if (resolved != null && resolved.length > 0) + { + resolvedType = childType.getJavaProject().findType(resolved[0][0], resolved[0][1]); + break LOOP_UNTIL_FIRST_MATCH; + } + } + + return resolvedType; + } +} diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/types/TypeComparator.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/types/TypeComparator.java index efe7dc341..d1fa3866a 100644 --- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/types/TypeComparator.java +++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/types/TypeComparator.java @@ -76,17 +76,15 @@ public final class TypeComparator { continue CHECK_CANDIDATES; } - else + + final Diagnostic test = + methodSignaturesMatch(curSatisfyType, testType); + + if (test.getSeverity() == Diagnostic.OK) { - final Diagnostic test = - methodSignaturesMatch(curSatisfyType, testType); - - if (test.getSeverity() == Diagnostic.OK) - { - result = Diagnostic.OK_INSTANCE; - // found a match so break - break MAIN_LOOP; - } + result = Diagnostic.OK_INSTANCE; + // found a match so break + break MAIN_LOOP; } } |