/*******************************************************************************
* Copyright (c) 2014 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.deltaspike.core;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.jboss.tools.cdi.core.CDICoreNature;
import org.jboss.tools.cdi.core.IClassBean;
import org.jboss.tools.cdi.core.IRootDefinitionContext;
import org.jboss.tools.cdi.core.extension.ICDIExtension;
import org.jboss.tools.cdi.core.extension.feature.IBuildParticipantFeature;
import org.jboss.tools.cdi.core.extension.feature.IProcessAnnotatedTypeFeature;
import org.jboss.tools.cdi.core.extension.feature.IValidatorFeature;
import org.jboss.tools.cdi.deltaspike.core.validation.DeltaspikeValidationMessages;
import org.jboss.tools.cdi.internal.core.impl.CDIProject;
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.DefinitionContext;
import org.jboss.tools.cdi.internal.core.impl.definition.TypeDefinition;
import org.jboss.tools.cdi.internal.core.scanner.FileSet;
import org.jboss.tools.cdi.internal.core.validation.CDICoreValidator;
import org.jboss.tools.common.java.IAnnotationDeclaration;
import org.jboss.tools.common.java.IParametedType;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.preferences.SeverityPreferences;
/**
*
* @author Viacheslav Kabanovich
*
*/
@SuppressWarnings("restriction")
public class DeltaspikePartialbeanExtension implements ICDIExtension, IBuildParticipantFeature, IProcessAnnotatedTypeFeature, IValidatorFeature, DeltaspikeConstants {
public static String ID = "org.apache.deltaspike.partialbean.impl.PartialBeanBindingExtension"; //$NON-NLS-1$
DeltaspikePartialbeanDefinitionContext context = new DeltaspikePartialbeanDefinitionContext();
public static DeltaspikePartialbeanExtension getExtension(CDICoreNature project) {
return (DeltaspikePartialbeanExtension)project.getExtensionManager().getExtensionByRuntime(ID);
}
public DeltaspikePartialbeanExtension() {}
public DeltaspikePartialbeanDefinitionContext getContext() {
return context;
}
@Override
public void processAnnotatedType(TypeDefinition typeDefinition,
IRootDefinitionContext context) {
//we cannot process here interfaces
//but can process abstract classes
IAnnotationDeclaration d = findAnnotationAnnotatedWithPartialBeanBindingType(typeDefinition, context);
if(d != null) {
DeltaspikePartialbeanDefinitionContext contextCopy = (DeltaspikePartialbeanDefinitionContext)this.context.getWorkingCopy();
DeltaspikePartialbeanBindingConfiguration c = contextCopy.getConfiguration(d.getTypeName());
if(typeDefinition.isAbstract()) {
typeDefinition.setBeanConstructor(true);
c.addPartialBean(typeDefinition);
} else if(isImplementingInvocationHandler(typeDefinition)) {
c.addInvocationHandler(typeDefinition);
} else {
c.addInvalidPartialBean(typeDefinition);
}
addToDependencies(c, typeDefinition, context);
}
}
@Override
public void beginVisiting() {
}
@Override
public void visitJar(IPath path, IPackageFragmentRoot root, XModelObject beansXML) {
}
@Override
public void visit(IFile file, IPath src, IPath webinf) {
}
@Override
public void buildDefinitions() {
}
@Override
public void buildDefinitions(FileSet fileSet) {
DeltaspikePartialbeanDefinitionContext workingCopy = (DeltaspikePartialbeanDefinitionContext)context.getWorkingCopy();
Map<IPath, List<IType>> is = fileSet.getInterfaces();
for (IPath p: is.keySet()) {
for (IType type: is.get(p)) {
InterfaceDefinition def = new InterfaceDefinition(type, workingCopy);
IAnnotationDeclaration d = findAnnotationAnnotatedWithPartialBeanBindingType(def, workingCopy.getRootContext());
if(d != null) {
TypeDefinition typeDefinition = new TypeDefinition();
typeDefinition.setType(type, workingCopy.getRootContext(), 0);
typeDefinition.setBeanConstructor(true);
DeltaspikePartialbeanBindingConfiguration c = workingCopy.getConfiguration(d.getTypeName());
c.addPartialBean(typeDefinition);
((DefinitionContext)context.getRootContext().getWorkingCopy()).addType(type.getPath(), type.getFullyQualifiedName(), typeDefinition);
addToDependencies(c, typeDefinition, workingCopy.getRootContext());
}
}
}
}
@Override
public void buildBeans(CDIProject target) {
}
@Override
public void validateResource(IFile file, CDICoreValidator validator) {
IPath path = file.getFullPath();
for (DeltaspikePartialbeanBindingConfiguration c: context.partialbeanBindingConfigurations.values()) {
if(c.getInvolvedTypes().contains(path)) {
for (TypeDefinition def: c.getInvocationHandlers().values()) {
if(file.equals(def.getResource())) {
reportIgnoredBindings(file, validator, def);
IAnnotationDeclaration dc = findAnnotationAnnotatedWithPartialBeanBindingType(def, context.getRootContext());
if(c.getInvocationHandlers().size() > 1) {
validator.addError(DeltaspikeValidationMessages.MULTIPLE_PARTIAL_BEAN_HANDLERS,
DeltaspikeSeverityPreferences.MULTIPLE_PARTIAL_BEAN_HANDLERS,
new String[]{dc.getTypeName()},
dc, file);
}
IClassBean cb = context.getRootContext().getProject().getDelegate().getBeanClass(def.getType());
if(cb != null && !cb.getScope().isNorlmalScope()) {
validator.addError(DeltaspikeValidationMessages.INVALID_PARTIAL_BEAN_HANDLER,
DeltaspikeSeverityPreferences.INVALID_PARTIAL_BEAN_HANDLER,
new String[]{},
dc, file);
}
}
}
for (TypeDefinition def: c.getPartialBeans().values()) {
if(file.equals(def.getResource())) {
reportIgnoredBindings(file, validator, def);
IAnnotationDeclaration dc = findAnnotationAnnotatedWithPartialBeanBindingType(def, context.getRootContext());
if(c.getInvocationHandlers().isEmpty()) {
validator.addError(DeltaspikeValidationMessages.MISSING_PARTIAL_BEAN_HANDLER,
DeltaspikeSeverityPreferences.MISSING_PARTIAL_BEAN_HANDLER,
new String[]{def.getQualifiedName(), dc.getTypeName()},
dc, file);
}
}
}
for (TypeDefinition def: c.getInvalidPartialBeans().values()) {
if(file.equals(def.getResource())) {
reportIgnoredBindings(file, validator, def);
IAnnotationDeclaration dc = findAnnotationAnnotatedWithPartialBeanBindingType(def, context.getRootContext());
validator.addError(DeltaspikeValidationMessages.ILLEGAL_PARTIAL_BEAN,
DeltaspikeSeverityPreferences.ILLEGAL_PARTIAL_BEAN,
new String[]{dc.getTypeName()},
dc, file);
}
}
}
}
}
private void reportIgnoredBindings(IFile file, CDICoreValidator validator, TypeDefinition def) {
List<IAnnotationDeclaration> dcs = getAllAnnotationsAnnotatedWithPartialBeanBindingType(def, context.getRootContext());
if(dcs.size() > 1) {
IAnnotationDeclaration d0 = dcs.remove(0);
for (IAnnotationDeclaration dc: dcs) {
validator.addError(DeltaspikeValidationMessages.MULTIPLE_PARTIAL_BEAN_BINDINGS,
DeltaspikeSeverityPreferences.MULTIPLE_PARTIAL_BEAN_BINDINGS,
new String[]{dc.getTypeName(), d0.getTypeName()},
dc, file);
}
}
}
@Override
public SeverityPreferences getSeverityPreferences() {
return DeltaspikeSeverityPreferences.getInstance();
}
private IAnnotationDeclaration findAnnotationAnnotatedWithPartialBeanBindingType(AbstractTypeDefinition t, IRootDefinitionContext context) {
for (IAnnotationDeclaration d: t.getAnnotations()) {
if(d.getTypeName() != null) {
AnnotationDefinition a = context.getAnnotation(d.getTypeName());
if(a != null && a.isAnnotationPresent(PARTIALBEAN_BINDING_ANNOTATION_TYPE_NAME)) {
return d;
}
}
}
return null;
}
private List<IAnnotationDeclaration> getAllAnnotationsAnnotatedWithPartialBeanBindingType(AbstractTypeDefinition t, IRootDefinitionContext context) {
List<IAnnotationDeclaration> result = new ArrayList<IAnnotationDeclaration>();
for (IAnnotationDeclaration d: t.getAnnotations()) {
if(d.getTypeName() != null) {
AnnotationDefinition a = context.getAnnotation(d.getTypeName());
if(a != null && a.isAnnotationPresent(PARTIALBEAN_BINDING_ANNOTATION_TYPE_NAME)) {
result.add(d);
}
}
}
return result;
}
private boolean isImplementingInvocationHandler(TypeDefinition typeDefinition) {
for (IParametedType t: typeDefinition.getAllTypes()) {
IType type = t.getType();
if(type != null && INVOCATION_HANDLER_TYPE.equals(type.getFullyQualifiedName())) {
return true;
}
}
return false;
}
private void addToDependencies(DeltaspikePartialbeanBindingConfiguration c, AbstractMemberDefinition def, IRootDefinitionContext context) {
IResource r = def.getResource();
if(r != null && r.exists() && !c.getInvolvedTypes().contains(r.getFullPath())) {
IPath newPath = r.getFullPath();
Set<IPath> ps = c.getInvolvedTypes();
for (IPath p: ps) {
context.addDependency(p, newPath);
context.addDependency(newPath, p);
}
ps.add(newPath);
}
}
}