/*******************************************************************************
* Copyright (c) 2011 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.cdi.seam.solder.core;
import java.beans.Introspector;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.JavaModelException;
import org.jboss.tools.cdi.core.CDIConstants;
import org.jboss.tools.cdi.core.IRootDefinitionContext;
import org.jboss.tools.cdi.core.extension.ICDIExtension;
import org.jboss.tools.cdi.core.extension.feature.IProcessAnnotatedTypeFeature;
import org.jboss.tools.cdi.internal.core.impl.definition.AbstractMemberDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.AbstractTypeDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.AnnotationDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.FieldDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.MethodDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.PackageDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.ParameterDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.TypeDefinition;
import org.jboss.tools.common.java.IAnnotated;
import org.jboss.tools.common.java.IAnnotationDeclaration;
import org.jboss.tools.common.java.ParametedType;
import org.jboss.tools.common.java.TypeDeclaration;
import org.jboss.tools.common.java.impl.AnnotationLiteral;
import org.jboss.tools.common.util.BeanUtil;
import org.jboss.tools.common.util.EclipseJavaUtil;
/**
* Implements support for org.jboss.seam.solder.core.CoreExtension
*
* For @Veto and @Requires marks bean definition as vetoed.
*
* For @FullyQualified and @Named on packages, adds fake @Named to bean
*
* For @Exact marks parameter or field type as overridden.
*
* @author Viacheslav Kabanovich
*
*/
public class CDISeamSolderCoreExtension implements ICDIExtension, IProcessAnnotatedTypeFeature {
public CDISeamSolderCoreExtension() {
}
protected Version getVersion() {
return Version.instance;
}
public void processAnnotatedType(TypeDefinition typeDefinition, IRootDefinitionContext context) {
if(processVeto(typeDefinition, context)) {
return;
}
if(processRequires(typeDefinition, context)) {
return;
}
processNames(typeDefinition, context);
processExact(typeDefinition, context);
}
// @Veto
private boolean processVeto(TypeDefinition typeDefinition, IRootDefinitionContext context) {
if (typeDefinition
.isAnnotationPresent(getVersion().getVetoAnnotationTypeName())
|| (typeDefinition.getPackageDefinition() != null && typeDefinition
.getPackageDefinition()
.isAnnotationPresent(
getVersion().getVetoAnnotationTypeName()))) {
typeDefinition.veto();
return true;
}
return false;
}
// @Requires
private boolean processRequires(TypeDefinition typeDefinition, IRootDefinitionContext context) {
Set<String> requiredClasses = new HashSet<String>();
requiredClasses.addAll(getRequiredClasses(typeDefinition));
PackageDefinition pkg = typeDefinition.getPackageDefinition();
if(pkg != null) {
requiredClasses.addAll(getRequiredClasses(pkg));
}
if (!requiredClasses.isEmpty()) {
for (String c : requiredClasses) {
if (context.getProject().getType(c) == null) {
typeDefinition.veto();
return true;
}
}
}
return false;
}
// @FullyQualified @Named
private void processNames(TypeDefinition typeDefinition, IRootDefinitionContext context) {
PackageDefinition p = typeDefinition.getPackageDefinition();
IAnnotationDeclaration namedOnPackage = null;
IAnnotationDeclaration fullyQualifiedOnPackage = null;
if(p != null) {
namedOnPackage = p.getAnnotation(CDIConstants.NAMED_QUALIFIER_TYPE_NAME);
fullyQualifiedOnPackage = p.getAnnotation(getVersion().getFullyQualifiedAnnotationTypeName());
}
processNames(typeDefinition, context, namedOnPackage, fullyQualifiedOnPackage, p);
List<FieldDefinition> fs = typeDefinition.getFields();
for (FieldDefinition f: fs) {
if(f.isAnnotationPresent(CDIConstants.PRODUCES_ANNOTATION_TYPE_NAME)) {
processNames(f, context, null, fullyQualifiedOnPackage, p);
}
}
List<MethodDefinition> ms = typeDefinition.getMethods();
for (MethodDefinition m: ms) {
if(m.isAnnotationPresent(CDIConstants.PRODUCES_ANNOTATION_TYPE_NAME)) {
processNames(m, context, null, fullyQualifiedOnPackage, p);
}
}
}
private void processNames(AbstractMemberDefinition d, IRootDefinitionContext context,
IAnnotationDeclaration namedOnPackage, IAnnotationDeclaration fullyQualifiedOnPackage, PackageDefinition p) {
IAnnotationDeclaration named = d.getAnnotation(CDIConstants.NAMED_QUALIFIER_TYPE_NAME);
IAnnotationDeclaration fullyQualified = d.getAnnotation(getVersion().getFullyQualifiedAnnotationTypeName());
String beanName = null;
if((fullyQualified != null || fullyQualifiedOnPackage != null) && (named != null || namedOnPackage != null)) {
//@FullyQualified
if(named == null) named = namedOnPackage;
String pkg = resolvePackageName(fullyQualified, fullyQualifiedOnPackage, d.getTypeDefinition(), p);
String simpleName = getSimpleBeanName(d, named);
beanName = (simpleName == null) ? null : pkg.length() > 0 ? pkg + "." + simpleName : simpleName;
} else if(named == null && namedOnPackage != null) {
// @Named on package only
beanName = getSimpleBeanName(d, namedOnPackage);
}
if(beanName != null) {
AnnotationDefinition n = context.getAnnotation(CDIConstants.NAMED_QUALIFIER_TYPE_NAME);
if(n != null) {
AnnotationLiteral l = new AnnotationLiteral(d.getResource(), 0, 0, beanName, IMemberValuePair.K_STRING, n.getType());
if(named != null) d.removeAnnotation(named);
d.addAnnotation(l, context);
}
}
}
private List<String> getRequiredClasses(IAnnotated d) {
IAnnotationDeclaration requires = d
.getAnnotation(getVersion().getRequiresAnnotationTypeName());
return requires != null ? getArrayValue(requires) : new ArrayList<String>();
}
private List<String> getArrayValue(IAnnotationDeclaration d) {
Object value = d.getMemberValue(null);
List<String> result = new ArrayList<String>();
if (value instanceof Object[]) {
Object[] array = (Object[]) value;
for (int i = 0; i < array.length; i++) {
if (array[i] != null)
result.add(array[i].toString());
}
} else if (value instanceof String) {
result.add(value.toString());
}
return result;
}
// @Exact
private void processExact(TypeDefinition typeDefinition, IRootDefinitionContext context) {
List<FieldDefinition> fs = typeDefinition.getFields();
for (FieldDefinition f : fs) {
TypeDeclaration exact = getExactType(f, typeDefinition, context);
if (exact != null) {
f.setOverridenType(exact);
}
}
List<MethodDefinition> ms = typeDefinition.getMethods();
for (MethodDefinition m : ms) {
List<ParameterDefinition> ps = m.getParameters();
for (ParameterDefinition p : ps) {
TypeDeclaration exact = getExactType(p, typeDefinition, context);
if (exact != null) {
p.setOverridenType(exact);
}
}
}
}
private TypeDeclaration getExactType(IAnnotated annotated, TypeDefinition declaringType, IRootDefinitionContext context) {
IAnnotationDeclaration a = annotated.getAnnotation(getVersion().getExactAnnotationTypeName());
if(a != null) {
Object o = a.getMemberValue(null);
if(o != null) {
String s = o.toString();
if(s.length() > 0) {
try {
ParametedType p = context.getProject().getTypeFactory().getParametedType(declaringType.getType(), "Q" + s + ";");
int b = a.getStartPosition();
int e = b + a.getLength();
if(b >= 0 && e > b) {
String content = declaringType.getContent().substring(b, e);
int i = content.indexOf(s);
if(i >= 0) {
b = i;
e = i + s.length();
}
}
return new TypeDeclaration(p, a.getResource(), b, e - b);
} catch (JavaModelException e) {
CDISeamSolderCorePlugin.getDefault().logError(e);
}
}
}
}
return null;
}
private String resolvePackageName(IAnnotationDeclaration fullyQualified, IAnnotationDeclaration fullyQualifiedOnPackage, AbstractTypeDefinition t, PackageDefinition p) {
String contextClass = null;
IAnnotationDeclaration a = fullyQualified != null ? fullyQualified : fullyQualifiedOnPackage;
if(a != null) {
contextClass = getStringValue(a);
}
if(contextClass == null) {
contextClass = t == null ? "" : t.getQualifiedName();
} else if(fullyQualified != null && t != null) {
String resolved = EclipseJavaUtil.resolveType(t.getType(), contextClass);
if(resolved != null) contextClass = resolved;
} else if(fullyQualifiedOnPackage != null) {
contextClass = p.resolveType(contextClass);
}
if("java.lang.Class".equals(contextClass)) {
contextClass = t.getType().getFullyQualifiedName();
}
int dot = contextClass.lastIndexOf('.');
return dot < 0 ? "" : contextClass.substring(0, dot);
}
private String getSimpleBeanName(AbstractMemberDefinition d, IAnnotationDeclaration named) {
String simpleName = null;
if(named != null) {
simpleName = getStringValue(named);
}
if(simpleName != null && simpleName.length() > 0) {
//do nothing
} else if(d instanceof TypeDefinition) {
simpleName = Introspector.decapitalize(((TypeDefinition)d).getType().getElementName());
} else if(d instanceof FieldDefinition) {
simpleName = ((FieldDefinition)d).getField().getElementName();
} else if(d instanceof MethodDefinition) {
MethodDefinition m = (MethodDefinition)d;
String mn = m.getMethod().getElementName();
if(BeanUtil.isGetter(m.getMethod())) {
simpleName = BeanUtil.getPropertyName(mn);
} else {
simpleName = mn;
}
}
return simpleName;
}
private String getStringValue(IAnnotationDeclaration a) {
Object o = a.getMemberValue(null);
return o == null ? null : o.toString();
}
}