/*******************************************************************************
* 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.config.core.definition;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.jboss.tools.cdi.internal.core.impl.definition.AbstractMemberDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.DefinitionContext;
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.ParameterDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.TypeDefinition;
import org.jboss.tools.cdi.seam.config.core.CDISeamConfigCorePlugin;
import org.jboss.tools.cdi.seam.config.core.ConfigDefinitionContext;
import org.jboss.tools.cdi.seam.config.core.util.Util;
import org.jboss.tools.cdi.seam.config.core.xml.SAXElement;
import org.jboss.tools.cdi.seam.config.core.xml.SAXNode;
import org.jboss.tools.common.java.IAnnotationDeclaration;
import org.jboss.tools.common.java.IJavaAnnotation;
import org.jboss.tools.common.java.IParametedType;
import org.jboss.tools.common.java.ParametedType;
import org.jboss.tools.common.java.TypeDeclaration;
import org.jboss.tools.common.model.XModelObject;
/**
*
* @author Viacheslav Kabanovich
*
*/
public class SeamBeansDefinition {
IResource resource;
XModelObject file;
List<SAXNodeProblem> unresolvedNodes = new ArrayList<SAXNodeProblem>();
Set<String> possibleTypeNames = new HashSet<String>();
Set<SeamBeanDefinition> beanDefinitions = new HashSet<SeamBeanDefinition>();
Set<SeamVirtualFieldDefinition> virtualFieldDefinitions = new HashSet<SeamVirtualFieldDefinition>();
List<TypeDefinition> typeDefinitions = new ArrayList<TypeDefinition>();
List<IType> replacedAndModified = new ArrayList<IType>();
public SeamBeansDefinition() {}
public void setResource(IResource resource) {
this.resource = resource;
}
public void setFileObject(XModelObject file) {
this.file = file;
}
public XModelObject getFileObject() {
return file;
}
public IResource getResource() {
return resource;
}
public List<SAXNodeProblem> getUnresolvedNodes() {
return unresolvedNodes;
}
/**
* Returns type names that could resolve unresolved nodes if such types existed.
*
* @return
*/
public Set<String> getPossibleTypeNames() {
return possibleTypeNames;
}
public void addUnresolvedNode(SAXNodeProblem problem) {
unresolvedNodes.add(problem);
}
public void addUnresolvedNode(SAXNode node, String problemId, String message) {
addUnresolvedNode(new SAXNodeProblem(node, problemId, message));
}
public void addPossibleTypeNames(Set<String> types) {
possibleTypeNames.addAll(types);
}
public void addBeanDefinition(SeamBeanDefinition def) {
beanDefinitions.add(def);
}
public Set<SeamBeanDefinition> getBeanDefinitions() {
return beanDefinitions;
}
public void addVirtualField(SeamVirtualFieldDefinition def) {
virtualFieldDefinitions.add(def);
}
public Set<SeamVirtualFieldDefinition> getVirtualFieldDefinitions() {
return virtualFieldDefinitions;
}
public List<TypeDefinition> getTypeDefinitions() {
return typeDefinitions;
}
public void buildTypeDefinitions(ConfigDefinitionContext context) {
typeDefinitions.clear();
for (SeamBeanDefinition def: beanDefinitions) {
IType type = def.getType();
ConfigTypeDefinition typeDef = new ConfigTypeDefinition();
typeDef.setFileObject(file);
boolean replaces = def.getReplacesLocation() != null;
boolean modifies = def.getModifiesLocation() != null;
if(replaces || modifies) {
replacedAndModified.add(type);
((DefinitionContext)context.getRootContext()).veto(type);
}
//Initialize typeDef taking into account replaces and modifies
int flags = AbstractMemberDefinition.FLAG_ALL_MEMBERS;
if(!modifies) {
//For replacing or created - no annotations loaded.
flags |= AbstractMemberDefinition.FLAG_NO_ANNOTATIONS;
}
typeDef.setType(type, context.getRootContext(), flags);
def.setConfigType(typeDef);
mergeTypeDefinition(def, typeDef, context);
typeDefinitions.add(typeDef);
}
for (SeamVirtualFieldDefinition def: virtualFieldDefinitions) {
IType type = def.getType();
ConfigVirtualFieldDefinition typeDef = new ConfigVirtualFieldDefinition();
typeDef.setFileObject(file);
typeDef.setConfig(def);
int flags = AbstractMemberDefinition.FLAG_NO_ANNOTATIONS;
typeDef.setType(type, context.getRootContext(), flags);
mergeAnnotations(def, typeDef, context);
//That is how field producers differ from class beans. They do not need a bean constructor.
typeDef.setBeanConstructor(true);
typeDefinitions.add(typeDef);
}
}
public void clean(ConfigDefinitionContext context) {
List<IType> ds = replacedAndModified;
replacedAndModified = new ArrayList<IType>();
for (IType type: ds) {
((DefinitionContext)context.getRootContext()).unveto(type);
}
}
public SeamMemberDefinition findExactly(int offset) {
for (SeamBeanDefinition b: beanDefinitions) {
SeamMemberDefinition d = b.findExactly(offset);
if(d != null) return d;
}
return null;
}
private void mergeTypeDefinition(SeamBeanDefinition def, ConfigTypeDefinition typeDef, ConfigDefinitionContext context) {
typeDef.setConfig(def);
mergeAnnotations(def, typeDef, context);
List<FieldDefinition> fieldDefs = typeDef.getFields();
//virtual field definitions are used for injections of inline beans as values of collections and maps.
List<FieldDefinition> virtualDefs = new ArrayList<FieldDefinition>();
for (FieldDefinition fieldDef:fieldDefs) {
String n = fieldDef.getField().getElementName();
SeamFieldDefinition f = def.getField(n);
if(f != null) {
ParametedType t = null;
IParametedType collection = null;
IParametedType map = null;
IParametedType object = null;
try {
String returnType = fieldDef.getField().getTypeSignature();
t = context.getRootContext().getProject().getTypeFactory().getParametedType(fieldDef.getField(), returnType);
object = context.getRootContext().getProject().getTypeFactory().getParametedType(fieldDef.getField(), "QObject;");
} catch (CoreException e) {
CDISeamConfigCorePlugin.getDefault().logError(e);
}
if(t != null && t.getType() != null) {
collection = getCollection(t);
map = getMap(t);
}
List<SeamFieldValueDefinition> vs = f.getValueDefinitions();
if(collection != null) {
List<? extends IParametedType> ps = t.getParameters();
IParametedType elementType = ps.isEmpty() ? object : ps.get(0);
for (SeamFieldValueDefinition v: vs) {
ConfigFieldDefinition virtual = new ConfigFieldDefinition(file);
virtual.setTypeDefinition(fieldDef.getTypeDefinition());
virtual.setField(fieldDef.getField(), context.getRootContext(), AbstractMemberDefinition.FLAG_NO_ANNOTATIONS);
virtual.setOverridenType(new TypeDeclaration((ParametedType)elementType, fieldDef.getField().getResource(), 0, 0));
virtual.setConfig(v);
virtualDefs.add(virtual);
v.setRequiredType(elementType);
mergeAnnotations(v, virtual, context);
}
} else if(map != null) {
List<? extends IParametedType> ps = t.getParameters();
IParametedType keyType = ps.isEmpty() ? object : ps.get(0);
IParametedType valueType = ps.size() < 2 ? object : ps.get(1);
for (SeamFieldValueDefinition v: vs) {
ConfigFieldDefinition virtual = new ConfigFieldDefinition(file);
virtual.setTypeDefinition(fieldDef.getTypeDefinition());
virtual.setField(fieldDef.getField(), context.getRootContext(), AbstractMemberDefinition.FLAG_NO_ANNOTATIONS);
IParametedType vType = Util.isKey((SAXElement)v.getNode()) ? keyType : valueType;
v.setRequiredType(vType);
virtual.setOverridenType(new TypeDeclaration((ParametedType)vType, fieldDef.getField().getResource(), 0, 0));
virtual.setConfig(v);
virtualDefs.add(virtual);
mergeAnnotations(v, virtual, context);
}
} else {
for (SeamFieldValueDefinition v: vs) {
v.setRequiredType(t);
}
}
((ConfigFieldDefinition)fieldDef).setConfig(f);
mergeAnnotations(f, fieldDef, context);
if(!vs.isEmpty() && collection == null && map == null) {
mergeAnnotations(vs.get(0), fieldDef, context);
}
}
}
fieldDefs.addAll(virtualDefs);
List<MethodDefinition> methodDefs = typeDef.getMethods();
for (MethodDefinition methodDef: methodDefs) {
IMethod method = methodDef.getMethod();
if(method == null) continue;
SeamMethodDefinition m = def.getMethod(method);
if(m != null) {
((ConfigMethodDefinition)methodDef).setConfig(m);
mergeAnnotations(m, methodDef, context);
List<ParameterDefinition> psDefs = methodDef.getParameters();
List<SeamParameterDefinition> ps = m.getParameters();
if(ps.size() != psDefs.size()) {
//do nothing
} else {
for (int i = 0; i < ps.size(); i++) {
mergeAnnotations(ps.get(i), psDefs.get(i), context);
}
}
}
}
typeDef.checkConstructor();
}
private void mergeAnnotations(SeamMemberDefinition def, AbstractMemberDefinition memberDef, ConfigDefinitionContext context) {
Map<String, IJavaAnnotation> annotations = def.getAnnotations();
for (String typeName: annotations.keySet()) {
IJavaAnnotation ja = annotations.get(typeName);
IAnnotationDeclaration current = memberDef.getAnnotation(typeName);
if(current != null) memberDef.removeAnnotation(current);
memberDef.addAnnotation(ja, context.getRootContext());
}
}
private IParametedType getCollection(ParametedType t) {
for (IParametedType i: t.getAllTypes()) {
if("java.util.Collection".equals(i.getType().getFullyQualifiedName())) {
return i;
}
}
return null;
}
private IParametedType getMap(ParametedType t) {
for (IParametedType i: t.getAllTypes()) {
if("java.util.Map".equals(i.getType().getFullyQualifiedName())) {
return i;
}
}
return null;
}
}