/*******************************************************************************
* Copyright (c) 2009 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.internal.core.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.jboss.tools.cdi.core.CDIConstants;
import org.jboss.tools.cdi.core.CDICorePlugin;
import org.jboss.tools.cdi.core.CDIUtil;
import org.jboss.tools.cdi.core.IBean;
import org.jboss.tools.cdi.core.IBeanMethod;
import org.jboss.tools.cdi.core.ICDIProject;
import org.jboss.tools.cdi.core.IClassBean;
import org.jboss.tools.cdi.core.IInitializerMethod;
import org.jboss.tools.cdi.core.IInjectionPoint;
import org.jboss.tools.cdi.core.IInterceptorBinding;
import org.jboss.tools.cdi.core.IInterceptorBindingDeclaration;
import org.jboss.tools.cdi.core.IObserverMethod;
import org.jboss.tools.cdi.core.IParameter;
import org.jboss.tools.cdi.core.IProducer;
import org.jboss.tools.cdi.core.IQualifierDeclaration;
import org.jboss.tools.cdi.core.IScope;
import org.jboss.tools.cdi.core.IScopeDeclaration;
import org.jboss.tools.cdi.core.IStereotype;
import org.jboss.tools.cdi.core.IStereotypeDeclaration;
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.FieldDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.MethodDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.TypeDefinition;
import org.jboss.tools.common.java.IAnnotationDeclaration;
import org.jboss.tools.common.java.IParametedType;
import org.jboss.tools.common.java.ITypeDeclaration;
import org.jboss.tools.common.java.ParametedType;
import org.jboss.tools.common.java.TypeDeclaration;
import org.jboss.tools.common.text.ITextSourceReference;
import org.jboss.tools.common.util.BeanUtil;
/**
*
* @author Viacheslav Kabanovich
*
*/
public class ClassBean extends AbstractBeanElement implements IClassBean {
protected ClassBean superClassBean = null;
protected Map<String, ClassBean> specializingClassBeans = new HashMap<String, ClassBean>();
protected List<BeanField> fields = new ArrayList<BeanField>();
protected List<BeanMethod> methods = new ArrayList<BeanMethod>();
protected IScope scope = null;
public ClassBean() {}
@Override
public boolean exists() {
IType t = getBeanClass();
return t != null && t.exists();
}
public void setDefinition(TypeDefinition definition) {
setSourcePath(definition.getType().getPath());
super.setDefinition(definition);
List<MethodDefinition> ms = definition.getMethods();
for (MethodDefinition m: ms) {
if(!m.getMethod().exists()) {
//update may be run on project that was not rebuilt
continue;
}
BeanMethod bm = null;
if(m.getProducesAnnotation() != null) {
bm = newProducerMethod(m);
} else if(m.getInjectAnnotation() != null) {
bm = new InitializerMethod();
} else if(m.isObserver()) {
bm = new ObserverMethod();
} else if(m.isDisposer()) {
bm = new DisposerMethod();
} else {
//add other cases
bm = new BeanMethod();
}
bm.setClassBean(this);
bm.setDefinition(m);
methods.add(bm);
}
List<FieldDefinition> fs = definition.getFields();
for (FieldDefinition f: fs) {
if(!f.getField().exists()) {
//update may be run on project that was not rebuilt
continue;
}
BeanField bf = null;
if(f.getProducesAnnotation() != null) {
bf = newProducerField(f);
} else if(f.getInjectAnnotation() != null) {
bf = new InjectionPointField();
} else {
//add observer case
bf = new BeanField();
}
bf.setClassBean(this);
bf.setDefinition(f);
fields.add(bf);
}
}
protected ProducerMethod newProducerMethod(MethodDefinition m) {
return new ProducerMethod();
}
protected ProducerField newProducerField(FieldDefinition f) {
return new ProducerField();
}
public TypeDefinition getDefinition() {
return (TypeDefinition)definition;
}
public ICDIProject getDeclaringProject() {
ICDIProject result = definition.getDeclaringProject().getDelegate();
if(result == null) {
result = getCDIProject();
}
return result;
}
public Collection<IBeanMethod> getBeanConstructors() {
Collection<IBeanMethod> result = new ArrayList<IBeanMethod>();
IBeanMethod defaultConstructor = null;
for (BeanMethod m: methods) {
if(m.getDefinition().isConstructor()) {
if(m.getAnnotation(CDIConstants.INJECT_ANNOTATION_TYPE_NAME)==null && m.getMethod().getNumberOfParameters()==0) {
defaultConstructor = m;
} else {
result.add(m);
}
}
}
// If a bean class does not explicitly declare a constructor using @Inject, the constructor that accepts no parameters is the bean constructor.
if(result.isEmpty() && defaultConstructor!=null) {
result.add(defaultConstructor);
}
return result;
}
public void setSuperClassBean(IClassBean bean) {
if(!(bean instanceof ClassBean) || bean == this) return;
if(bean.getSuperClassBean() != null) {
HashSet<IClassBean> beans = new HashSet<IClassBean>();
beans.add(this);
IClassBean b = bean;
while(b != null) {
if(beans.contains(b)) {
bean = null;
break;
}
b = b.getSuperClassBean();
}
}
superClassBean = (ClassBean)bean;
if(superClassBean != null && isSpecializing()) {
superClassBean.addSpecializingClassBean(this);
}
if(superClassBean != null) {
Map<String, ProducerMethod> thisProducers = getProducerMethodsForSignatures();
Map<String, ProducerMethod> superProducers = superClassBean.getProducerMethodsForSignatures();
for (String s: thisProducers.keySet()) {
ProducerMethod thisProducer = thisProducers.get(s);
ProducerMethod superProducer = superProducers.get(s);
if(thisProducer != null && superProducer != null) {
if(thisProducer.getSpecializesAnnotationDeclaration() != null) {
thisProducer.setSpecializedBean(superProducer);
}
}
}
}
}
Map<String, ProducerMethod> getProducerMethodsForSignatures() {
Map<String, ProducerMethod> result = new HashMap<String, ProducerMethod>();
for (BeanMethod b: methods) {
if(b instanceof ProducerMethod) {
String s = b.getMethod().getElementName();
try {
s += ":" + b.getMethod().getSignature();
} catch (JavaModelException e) {
CDICorePlugin.getDefault().logError(e);
}
result.put(s, (ProducerMethod)b);
}
}
return result;
}
synchronized void addSpecializingClassBean(ClassBean bean) {
if(specializingClassBeans == null) {
specializingClassBeans = new Hashtable<String, ClassBean>();
}
specializingClassBeans.put(bean.getBeanClass().getFullyQualifiedName(), bean);
}
public ClassBean getSuperClassBean() {
return superClassBean;
}
public Collection<IBeanMethod> getDisposers() {
Collection<IBeanMethod> result = new ArrayList<IBeanMethod>();
for (BeanMethod m: methods) {
if(m.isDisposer()) {
result.add(m);
}
}
return result;
}
public static Collection<IInterceptorBindingDeclaration> getInterceptorBindingDeclarations(AbstractMemberDefinition definition) {
Collection<IInterceptorBindingDeclaration> result = new ArrayList<IInterceptorBindingDeclaration>();
List<IAnnotationDeclaration> as = definition.getAnnotations();
for (IAnnotationDeclaration a: as) {
if(a instanceof InterceptorBindingDeclaration) {
result.add((InterceptorBindingDeclaration)a);
}
}
return result;
}
/*
* (non-Javadoc)
* @see org.jboss.tools.cdi.core.IClassBean#getInterceptorBindings()
*/
public Collection<IInterceptorBinding> getInterceptorBindings() {
return CDIUtil.getAllInterceptorBindings(this);
}
public Collection<IObserverMethod> getObserverMethods() {
Collection<IObserverMethod> result = new ArrayList<IObserverMethod>();
for (BeanMethod m: methods) {
if(m.isObserver() && m instanceof IObserverMethod) {
result.add((IObserverMethod)m);
}
}
return result;
}
public Collection<IProducer> getProducers() {
Collection<IProducer> result = new ArrayList<IProducer>();
for (BeanMethod m: methods) {
if(m instanceof IProducer) {
result.add((IProducer)m);
}
}
for (BeanField f: fields) {
if(f instanceof IProducer) {
result.add((IProducer)f);
}
}
return result;
}
public Collection<ITypeDeclaration> getAllTypeDeclarations() {
Collection<IParametedType> ps = getDefinition().getInheritedTypes();
Set<ITypeDeclaration> result = new HashSet<ITypeDeclaration>();
for (IParametedType p: ps) {
if(p instanceof TypeDeclaration) {
result.add((TypeDeclaration)p);
}
}
IParametedType p = getDefinition().getParametedType();
if(p != null) {
if(p instanceof TypeDeclaration) {
result.add((TypeDeclaration)p);
}
}
return result;
}
public IAnnotationDeclaration getAlternativeDeclaration() {
return getDefinition().getAlternativeAnnotation();
}
public IType getBeanClass() {
return ((TypeDefinition)definition).getType();
}
@Override
public Collection<IInitializerMethod> getInitializers() {
Collection<IInitializerMethod> result = new ArrayList<IInitializerMethod>();
for (BeanMethod m: methods) {
if(m instanceof IInitializerMethod) {
result.add((IInitializerMethod)m);
}
}
return result;
}
public Collection<IInjectionPoint> getInjectionPoints() {
return getInjectionPoints(true);
}
/**
* If all=false, injection points of producer methods are not included.
*
* @param all
* @return
*/
public Collection<IInjectionPoint> getInjectionPoints(boolean all) {
Collection<IInjectionPoint> result = new ArrayList<IInjectionPoint>();
for (BeanField f: fields) {
if(f instanceof IInjectionPoint) {
result.add((IInjectionPoint)f);
}
}
for (BeanMethod m: methods) {
if(!all && (m instanceof IBean)) {
continue;
}
List<IParameter> ps = m.getParameters();
for (IParameter p: ps) {
if(p instanceof IInjectionPoint) {
result.add((IInjectionPoint)p);
}
}
}
return result;
}
public Collection<IParametedType> getLegalTypes() {
Set<IParametedType> result = new HashSet<IParametedType>();
AnnotationDeclaration d = getDefinition().getTypedAnnotation();
Collection<IParametedType> all = getAllTypes();
if(d != null) {
result.addAll(getRestrictedTypeDeclarations(all));
ParametedType object = getObjectType(getBeanClass());
if(object != null) {
result.add(object);
}
return result;
}
return all;
}
/*
* (non-Javadoc)
* @see org.jboss.tools.cdi.core.IBean#getAllTypes()
*/
public Collection<IParametedType> getAllTypes() {
return getDefinition().getAllTypes();
}
public Collection<ITypeDeclaration> getRestrictedTypeDeclaratios() {
return getRestrictedTypeDeclarations(getAllTypes());
}
public String getName() {
ClassBean specialized = getSpecializedBean();
if(specialized != null) {
String name = specialized.getName();
if(name != null) {
return name;
}
}
AnnotationDeclaration named = findNamedAnnotation();
if(named == null) return null;
String name = BeanUtil.getDefaultBeanName(((AbstractTypeDefinition)definition).getType());
Object value = named.getMemberValue(null, true);
if(value != null && value.toString().trim().length() > 0) {
return value.toString().trim();
}
return name;
}
public ITextSourceReference getNameLocation(boolean stereotypeLocation) {
return (stereotypeLocation) ? CDIUtil.getNamedDeclaration(this) : findNamedAnnotation();
}
public ClassBean getSpecializedBean() {
if(getDefinition().getSpecializesAnnotation() == null) {
return null;
}
return superClassBean;
}
public synchronized Collection<ClassBean> getSpecializingBeans() {
return specializingClassBeans == null ? Collections.<ClassBean>emptySet() : specializingClassBeans.values();
}
public IAnnotationDeclaration getSpecializesAnnotationDeclaration() {
return getDefinition().getSpecializesAnnotation();
}
public boolean isDependent() {
IScope scope = getScope();
return scope != null && CDIConstants.DEPENDENT_ANNOTATION_TYPE_NAME.equals(scope.getSourceType().getFullyQualifiedName());
}
synchronized boolean hasEnabledSpecializingClassBean() {
if(specializingClassBeans != null) {
for (ClassBean sb: specializingClassBeans.values()) {
if(sb.hasEnabledSpecializingClassBean() || sb.isEnabled()) return true;
}
}
return false;
}
public boolean isEnabled() {
if(hasEnabledSpecializingClassBean()) {
return false;
}
if(isAlternative()) {
if(getAnnotation(CDIConstants.PRIORITY_ANNOTATION_TYPE_NAME) != null) {
return true;
}
if(getCDIProject().isClassAlternativeActivated(getDefinition().getQualifiedName())) {
return true;
}
for (IStereotypeDeclaration d: getStereotypeDeclarations(true)) {
IStereotype s = d.getStereotype();
if(s != null && s.isAlternative() && !getCDIProject().getAlternatives(s.getSourceType().getFullyQualifiedName()).isEmpty()) {
return true;
}
}
return false;
}
return true;
}
public boolean isSpecializing() {
return getDefinition().getSpecializesAnnotation() != null;
}
public IScope getScope() {
if(scope == null) {
computeScope();
}
return scope;
}
protected void computeScope() {
//1. Declaration of scope in the class.
Collection<IScopeDeclaration> scopes = getScopeDeclarations();
if(!scopes.isEmpty()) {
scope = scopes.iterator().next().getScope();
return;
}
//2. Declaration of inheritable scope in a superclass.
ClassBean scb = getSuperClassBean();
while(scb != null) {
scopes = scb.getScopeDeclarations();
if(!scopes.isEmpty()) {
scope = scopes.iterator().next().getScope();
if(scope.getInheritedDeclaration() == null) {
scope = getCDIProject().getScope(CDIConstants.DEPENDENT_ANNOTATION_TYPE_NAME);
return;
} else {
return;
}
}
scb = scb.getSuperClassBean();
}
//3. Get default scope from stereotype.
Set<IScope> defaults = new HashSet<IScope>();
for (IStereotypeDeclaration d: getStereotypeDeclarations()) {
IStereotype s = d.getStereotype();
IScope sc = s.getScope();
if(sc != null) {
defaults.add(sc);
}
}
scb = getSuperClassBean();
while(scb != null) {
for (IStereotypeDeclaration d: scb.getStereotypeDeclarations()) {
IStereotype s = d.getStereotype();
if(s.getInheritedDeclaration() == null) {
continue;
}
IScope sc = s.getScope();
if(sc != null) {
defaults.add(sc);
}
}
scb = scb.getSuperClassBean();
}
if(defaults.size() == 1) {
scope = defaults.iterator().next();
} else if(defaults.size() > 1) {
scope = getCDIProject().getScope(CDIConstants.DEPENDENT_ANNOTATION_TYPE_NAME);
} else {
//4. Scope is @Dependent
scope = getCDIProject().getScope(CDIConstants.DEPENDENT_ANNOTATION_TYPE_NAME);
}
}
protected Collection<IQualifierDeclaration> getInheritedQualifierDeclarations() {
if(superClassBean == null) return Collections.emptySet();
Collection<IQualifierDeclaration> result = new ArrayList<IQualifierDeclaration>();
for (IQualifierDeclaration d: superClassBean.getQualifierDeclarations(true)) {
if(d.getQualifier() != null && d.getQualifier().getInheritedDeclaration() != null) {
result.add(d);
} else if(isSpecializing()) {
result.add(d);
}
}
return result;
}
protected Collection<IInterceptorBindingDeclaration> getInheritedInterceptorBindingDeclarations() {
if(superClassBean == null) return Collections.emptyList();
Set<IInterceptorBindingDeclaration> result = new HashSet<IInterceptorBindingDeclaration>();
for (IInterceptorBindingDeclaration d: superClassBean.getInterceptorBindingDeclarations(true)) {
if(d.getInterceptorBinding() != null && d.getInterceptorBinding().getInheritedDeclaration() != null) {
result.add(d);
} else if(isSpecializing()) {
result.add(d);
}
}
return result;
}
public Set<IStereotypeDeclaration> getInheritedStereotypDeclarations() {
if(superClassBean == null) return Collections.emptySet();
Set<IStereotypeDeclaration> result = new HashSet<IStereotypeDeclaration>();
for (IStereotypeDeclaration d: superClassBean.getStereotypeDeclarations(true)) {
if(d.getStereotype() != null && d.getStereotype().getInheritedDeclaration() != null) {
result.add(d);
} else if(isSpecializing()) {
result.add(d);
}
}
return result;
}
/*
* (non-Javadoc)
* @see org.jboss.tools.cdi.core.IBean#isNullable()
*/
public boolean isNullable() {
return true;
}
/*
* (non-Javadoc)
* @see org.jboss.tools.cdi.core.IBean#isSelectedAlternative()
*/
public boolean isSelectedAlternative() {
if(getDefinition().getAlternativeAnnotation() != null) {
if(getCDIProject().isTypeAlternative(getBeanClass().getFullyQualifiedName())) {
return true;
}
if(getAnnotation(CDIConstants.PRIORITY_ANNOTATION_TYPE_NAME) != null) {
return true;
}
}
for (IStereotypeDeclaration d: getStereotypeDeclarations(true)) {
IStereotype s = d.getStereotype();
if(s != null && s.isAlternative() &&
getCDIProject().isStereotypeAlternative(s.getSourceType().getFullyQualifiedName()) ) return true;
}
return false;
}
/*
* (non-Javadoc)
* @see org.jboss.tools.cdi.core.IClassBean#getAllMethods()
*/
public Collection<IBeanMethod> getAllMethods() {
return new ArrayList<IBeanMethod>(methods);
}
/*
* (non-Javadoc)
* @see org.jboss.tools.cdi.core.IBean#getSimpleJavaName()
*/
@Override
public String getElementName() {
String result = getBeanClass().getElementName();
if(result.length() == 0) {
result = getBeanClass().getFullyQualifiedName();
}
return result;
}
/*
* (non-Javadoc)
* @see org.jboss.tools.cdi.core.IClassBean#getSuperType()
*/
public ParametedType getSuperType() {
return getDefinition().getSuperType();
}
/*
* (non-Javadoc)
* @see org.jboss.tools.common.java.IJavaMemberReference#getSourceMember()
*/
@Override
public IMember getSourceMember() {
return getBeanClass();
}
/*
* (non-Javadoc)
* @see org.jboss.tools.common.java.IJavaReference#getSourceElement()
*/
@Override
public IJavaElement getSourceElement() {
return getSourceMember();
}
public synchronized void cleanCache() {
specializingClassBeans = null;
scope = null;
getDefinition().resetParametedType();
for (BeanMethod m: methods) {
m.setMethod(m.getMethod()); // type update
}
for (BeanField f: fields) {
f.setField(f.getField()); // type update
}
}
@Override
public Integer getPriority() {
IAnnotationDeclaration d = getAnnotation(CDIConstants.PRIORITY_ANNOTATION_TYPE_NAME);
if(d instanceof AnnotationDeclaration) {
Object o = ((AnnotationDeclaration)d).getMemberConstantValue(null);
if(o == null) {
o = d.getMemberValue(null);
}
if(o instanceof Integer) {
return (Integer)o;
}
}
return null;
}
}