| /*********************************************************************** |
| * Copyright (c) 2008 by SAP AG, Walldorf. |
| * 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: |
| * SAP AG - initial API and implementation |
| ***********************************************************************/ |
| package org.eclipse.jst.jee.model.internal.common; |
| |
| import java.util.Collection; |
| import java.util.List; |
| |
| import org.eclipse.jdt.core.IAnnotation; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IImportDeclaration; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMemberValuePair; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jst.javaee.core.Description; |
| import org.eclipse.jst.javaee.core.EjbLocalRef; |
| import org.eclipse.jst.javaee.core.InjectionTarget; |
| import org.eclipse.jst.javaee.core.JavaeeFactory; |
| import org.eclipse.jst.javaee.core.ResAuthType; |
| import org.eclipse.jst.javaee.core.ResSharingScopeType; |
| import org.eclipse.jst.javaee.core.ResourceRef; |
| import org.eclipse.jst.javaee.core.RunAs; |
| import org.eclipse.jst.javaee.core.SecurityRole; |
| import org.eclipse.jst.javaee.core.SecurityRoleRef; |
| import org.eclipse.jst.javaee.ejb.SessionBean; |
| |
| /** |
| * @author Kiril Mitov k.mitov@sap.com |
| * |
| */ |
| public abstract class AbstractAnnotationFactory { |
| |
| /** |
| * @param value |
| * @return true if "value" is an array of objects. |
| */ |
| protected static boolean isArrayOfObject(Object value) { |
| return Object[].class.isInstance(value); |
| } |
| |
| protected Object getAnnotatedValue(String name, IMemberValuePair[] memberValuePairs) throws JavaModelException { |
| for (IMemberValuePair pair : memberValuePairs) { |
| if (name.equals(pair.getMemberName())) { |
| return pair.getValue(); |
| } |
| } |
| return null; |
| } |
| |
| protected boolean containsImport(ICompilationUnit unit, String importt) throws JavaModelException { |
| for (IImportDeclaration declaration : unit.getImports()) { |
| if (declaration.getElementName().equals(importt)) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Resolve the given <code>toResolve</code> string to an IType in the |
| * context of <code>declaringType</code> |
| * |
| * @param declaringType |
| * @param toResolve |
| * the type that should be resolved. |
| * @return the resolved type or <code>null</code> if such type can not be |
| * resolved. Returns <code>null</code> if toResolve is |
| * <code>null</code> |
| * @throws JavaModelException |
| */ |
| protected IType resolveType(IType declaringType, String toResolve) throws JavaModelException { |
| if (toResolve == null) |
| return null; |
| String[][] fullTypeName = declaringType.resolveType(toResolve); |
| if (fullTypeName != null) |
| return declaringType.getJavaProject().findType(fullTypeName[0][0], fullTypeName[0][1]); |
| return null; |
| } |
| |
| protected void processEjbAnnotation(IAnnotation annotation, List<EjbLocalRef> localRefs, IMember member, |
| Collection<IType> dependedTypes) throws JavaModelException { |
| int memberType = member.getElementType(); |
| IMemberValuePair[] pairs = annotation.getMemberValuePairs(); |
| IType declaringType = (IType) (member.getElementType() == IJavaElement.TYPE ? member : member |
| .getDeclaringType()); |
| |
| String beanInterfaceValue = (String) getAnnotatedValue("beanInterface", pairs); |
| beanInterfaceValue = internalProcessInjection(beanInterfaceValue, member, dependedTypes); |
| if (beanInterfaceValue == null) |
| return; |
| |
| /* |
| * The name of the reference should be the value of the "name" |
| * attribute. If there is no "name" attribute then the name of the |
| * reference is the qualified name of the member. Check this at |
| * Enterprise Java Beans, 3.0, Section 14.1.5.3 |
| */ |
| String refName = (String) getAnnotatedValue("name", pairs); |
| if (refName == null) { |
| refName = getMemberQualifiedName(member); |
| } |
| EjbLocalRef ref = ref = JavaeeFactory.eINSTANCE.createEjbLocalRef(); |
| ref.setEjbRefName(refName); |
| localRefs.add(ref); |
| ref.setLocal(beanInterfaceValue); |
| ref.setLocalHome(beanInterfaceValue); |
| ref.setEjbLink((String) getAnnotatedValue("beanName", pairs)); |
| ref.setMappedName((String) getAnnotatedValue("mappedName", pairs)); |
| if (memberType == IJavaElement.METHOD || memberType == IJavaElement.FIELD) { |
| InjectionTarget injectionTarget = JavaeeFactory.eINSTANCE.createInjectionTarget(); |
| int index = refName.indexOf('/'); |
| injectionTarget.setInjectionTargetClass(refName.substring(0, index)); |
| injectionTarget.setInjectionTargetName(refName.substring(index + 1)); |
| ref.getInjectionTargets().add(injectionTarget); |
| } |
| } |
| |
| /** |
| * Resource annotation can be placed on class, method, field. |
| * |
| * Checks are made if the resource annotation is valid. |
| * <p> |
| * If on class there should be a "type" attribute. If on method the method |
| * must have one param with type that is an interface. If on field the field |
| * type must be an interface. |
| * |
| * If the type of the method/field can not be resolved the result will |
| * contain the unresolved value. |
| * |
| * In case the type of method/field is array, wildcard, simple type this is |
| * not a place for the annotation. |
| * </p> |
| * |
| * <p> |
| * In case of method/field the type specified using the "type" attribute has |
| * a higher priority that the method/field type. |
| * </p> |
| * |
| * <p> |
| * Only resolved types are added to dependedTypes |
| * </p> |
| * |
| * @param sessionBean |
| * @param member |
| * @param annotation |
| * @param dependedTypes |
| * @throws JavaModelException |
| */ |
| protected void processResourceRefAnnotation(IAnnotation annotation, List<ResourceRef> resourceRefs, IMember member, |
| Collection<IType> dependedTypes) throws JavaModelException { |
| |
| IMemberValuePair[] pairs = annotation.getMemberValuePairs(); |
| String specifiedType = (String) getAnnotatedValue("type", pairs); |
| specifiedType = internalProcessInjection(specifiedType, member, dependedTypes); |
| if (specifiedType == null) |
| return; |
| String name = (String) getAnnotatedValue("name", pairs); |
| if (name == null) |
| name = getMemberQualifiedName(member); |
| ResourceRef ref = JavaeeFactory.eINSTANCE.createResourceRef(); |
| ref.setResRefName(name); |
| ref.setResType(specifiedType); |
| ref.setMappedName((String) getAnnotatedValue("mappedName", pairs)); |
| String description = (String) getAnnotatedValue("description", pairs); |
| if (description != null) { |
| Description desc = JavaeeFactory.eINSTANCE.createDescription(); |
| desc.setValue(description); |
| ref.getDescriptions().clear(); |
| ref.getDescriptions().add(desc); |
| } |
| String value = (String) getAnnotatedValue("authenticationType", pairs); |
| /* |
| * the default value is AuthenticationType.APPLICATION which is handled |
| * by the EMF. no need to check for this value |
| */ |
| if ("AuthenticationType.CONTAINER".equals(value)) { |
| ref.setResAuth(ResAuthType.CONTAINER_LITERAL); |
| } else if ("CONTAINER".equals(value) |
| && containsImport(member.getCompilationUnit(), "AuthenticationType.CONTAINER")) { |
| ref.setResAuth(ResAuthType.CONTAINER_LITERAL); |
| } |
| Boolean shareable = (Boolean) getAnnotatedValue("shareable", pairs); |
| /* |
| * The default value for sharable is true. Check and process only |
| * unsharable |
| */ |
| if (Boolean.FALSE.equals(shareable)) |
| ref.setResSharingScope(ResSharingScopeType.UNSHAREABLE_LITERAL); |
| |
| resourceRefs.add(ref); |
| } |
| |
| /** |
| * The method has the task of processing the member along with the specified |
| * member and return a String. The result is to be used as a reference value |
| * for the injection on this member. Usage are the |
| * |
| * @EJB and |
| * @Resource annotations. |
| * |
| * <p> |
| * If the specifiedType is <code>null</code> and member is of type |
| * IJavaElement.TYPE the method returns <code>null</code> |
| * </p> |
| * |
| * <p> |
| * If the type of the member can be resolved and is an interface the method |
| * returns <code>null</code>. Here the "type" of the member is the result |
| * from {@link #getUnresolvedType(IMember)} |
| * </p> |
| * |
| * |
| * Only if the specifiedType can be calculated and is resolved it is added |
| * to the dependedTypes. If the specifiedType can not be resolved nothing is |
| * added to dependedTypes. |
| * |
| * @see {@link #processEjbAnnotation(IAnnotation, SessionBean, IMember, Collection)} |
| * @see #processResourceRefAnnotation(SessionBean, IMember, IAnnotation, |
| * Collection) |
| * |
| * @param specifiedType |
| * @param member |
| * @param dependedTypes |
| * @return |
| * @throws JavaModelException |
| */ |
| private String internalProcessInjection(String specifiedType, IMember member, Collection<IType> dependedTypes) |
| throws JavaModelException { |
| boolean methodOrField = member.getElementType() == IJavaElement.METHOD |
| || member.getElementType() == IJavaElement.FIELD; |
| IType declaringType = (IType) (member.getElementType() == IJavaElement.TYPE ? member : member |
| .getDeclaringType()); |
| String memberType = getUnresolvedType(member); |
| // not type for this member can be retrieved. If member is a method or |
| // field this means there is an error. |
| if (getClassTypeSignature(memberType) == null && methodOrField) |
| return null; |
| |
| // both type are null. This is not a valid case. This will hapen for a |
| // type without specified type. |
| if (specifiedType == null && memberType == null) |
| return null; |
| |
| IType resolvedType = resolveType(declaringType, memberType); |
| // we were able to get a type for the param of a method or type of |
| // a field. |
| // check if it is an interface. It might not be resolved, but we have a |
| // value |
| // for unresolved. |
| if (methodOrField) { |
| // if the resolved type is not null and it is not an interface this |
| // annotation is not valid |
| if (resolvedType != null) { |
| if (resolvedType.isInterface()) |
| memberType = resolvedType.getFullyQualifiedName(); |
| else |
| // invalid - if the method is with param that is not an |
| // interface. Or the type of the field is not an interface. |
| return null; |
| } |
| } |
| // from now one use only the specified type for type resolving. If there |
| // is no specified type use the member type. The check for whether they |
| // were both null is previously made |
| IType resolvedSpecifiedType = null; |
| if (specifiedType == null) { |
| specifiedType = memberType; |
| resolvedSpecifiedType = resolvedType; |
| } else |
| resolvedSpecifiedType = resolveType(declaringType, specifiedType); |
| if (resolvedSpecifiedType != null) { |
| if (resolvedSpecifiedType.isInterface()) { |
| specifiedType = resolvedSpecifiedType.getFullyQualifiedName(); |
| dependedTypes.add(resolvedSpecifiedType); |
| } else |
| // we have resolved the specified type and it is not an |
| // interface. Not a valid annotation. |
| return null; |
| } |
| return specifiedType; |
| } |
| |
| /** |
| * This method returns a qualified name for this member. The name is to be |
| * used as ejb-ref-name. |
| * |
| * If the member is a type then fullyQualifiedName of the type is returned. |
| * |
| * If the member is a <code>field</code> declared in a <code>type</code> |
| * then the result is |
| * <code>type.getFullyQualifiedName() +"/" + field.elementName</code> |
| * |
| * If the member is a <code>method</code> declared in a <code>type</code> |
| * and method name begins with "set" then: for type name = |
| * "org.eclipse.Bean" and method name = "setMethodOne()" the result is |
| * "org.eclipse.Bean/methodOne" |
| * |
| * Check this at Enterprise Java Beans, 3.0, Section 14.1.5.3 |
| * |
| * @param member |
| * @return |
| */ |
| private String getMemberQualifiedName(IMember member) { |
| String memberName = member.getElementName(); |
| int elementType = member.getElementType(); |
| if (elementType == IJavaElement.METHOD && memberName.startsWith("set")) { |
| char ch = Character.toLowerCase(memberName.charAt(3)); |
| memberName = ch + memberName.substring(4); |
| } |
| return elementType == IJavaElement.TYPE ? ((IType) member).getFullyQualifiedName() : member.getDeclaringType() |
| .getFullyQualifiedName() |
| + "/" + memberName; |
| } |
| |
| /** |
| * Return the javaee type of this member. For types return <code>null</code>. |
| * For methods with one param return the java type of this param. For fields |
| * return the return the java type of the field. |
| * |
| * If the result is <code>null</code> then this member is not valid and a |
| * javaee type can not be returned. This may happen for a method with more |
| * then one param or for a field with a class type or primitive type |
| * |
| * @param member |
| * @param memberType |
| * |
| * @return |
| * @throws JavaModelException |
| */ |
| private String getUnresolvedType(IMember member) throws JavaModelException { |
| int memberType = member.getElementType(); |
| IType declaringType = member.getDeclaringType(); |
| String unresolvedTypeName = null; |
| if (memberType == IJavaElement.FIELD) { |
| unresolvedTypeName = Signature.toString(((IField) member).getTypeSignature()); |
| } else if (memberType == IJavaElement.METHOD) { |
| IMethod method = (IMethod) member; |
| if (method.getNumberOfParameters() != 1) |
| return null; |
| unresolvedTypeName = Signature.toString(method.getParameterTypes()[0]); |
| } else if (memberType == IJavaElement.TYPE) |
| return null; |
| return unresolvedTypeName; |
| } |
| |
| /** |
| * Returns the type signature for toResolve only if toResolve is a class or |
| * interface. |
| * |
| * @param toResolve |
| * @return <code>null</code> if toResolve is <code>null</code> or simple |
| * type, array type, wildcard type |
| * |
| */ |
| private String getClassTypeSignature(String toResolve) { |
| if (toResolve == null) |
| return null; |
| toResolve = Signature.createTypeSignature(toResolve, false); |
| if (Signature.getTypeSignatureKind(toResolve) != Signature.CLASS_TYPE_SIGNATURE) |
| return null; |
| return toResolve; |
| } |
| |
| protected void processDeclareRoles(Result result, List<SecurityRoleRef> securityRoleRefs, IAnnotation annotation, |
| IType type) throws JavaModelException { |
| IMemberValuePair[] pairs = annotation.getMemberValuePairs(); |
| Object values = getAnnotatedValue("value", pairs); |
| if (!isArrayOfObject(values)) |
| return; |
| for (Object roleName : (Object[]) values) { |
| SecurityRole role = JavaeeFactory.eINSTANCE.createSecurityRole(); |
| role.setRoleName((String) roleName); |
| result.getAdditional().add(role); |
| SecurityRoleRef ref = JavaeeFactory.eINSTANCE.createSecurityRoleRef(); |
| ref.setRoleName((String) roleName); |
| securityRoleRefs.add(ref); |
| } |
| } |
| |
| protected void processResourcesAnnotation(IAnnotation annotation, List<ResourceRef> resourceRefs, IType type, |
| Collection<IType> dependedTypes) throws JavaModelException { |
| IMemberValuePair[] pairs = annotation.getMemberValuePairs(); |
| if (!isArrayOfObject(pairs[0].getValue())) |
| return; |
| Object[] values = (Object[]) pairs[0].getValue(); |
| for (Object resourceAnnotation : values) { |
| processResourceRefAnnotation((IAnnotation) resourceAnnotation, resourceRefs, type, dependedTypes); |
| } |
| } |
| |
| protected void processRunAs(IAnnotation annotation,RunAs runAs) throws JavaModelException { |
| IMemberValuePair[] pairs = annotation.getMemberValuePairs(); |
| if (pairs.length == 1) { |
| String value = (String) getAnnotatedValue("value", pairs); |
| runAs.setRoleName(value); |
| } |
| } |
| } |