/******************************************************************************* * 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.scanner; import java.io.ByteArrayInputStream; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IResource; import org.eclipse.jdt.core.IField; 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.jface.text.IDocument; import org.eclipse.osgi.util.NLS; import org.jboss.tools.cdi.core.CDIConstants; import org.jboss.tools.cdi.core.CDICoreNature; import org.jboss.tools.cdi.internal.core.impl.definition.AnnotationDefinition; import org.jboss.tools.cdi.seam.config.core.CDISeamConfigConstants; 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.definition.AbstractSeamFieldDefinition; import org.jboss.tools.cdi.seam.config.core.definition.SeamBeanDefinition; import org.jboss.tools.cdi.seam.config.core.definition.SeamBeansDefinition; import org.jboss.tools.cdi.seam.config.core.definition.SeamFieldDefinition; import org.jboss.tools.cdi.seam.config.core.definition.SeamFieldValueDefinition; import org.jboss.tools.cdi.seam.config.core.definition.SeamMethodDefinition; import org.jboss.tools.cdi.seam.config.core.definition.SeamParameterDefinition; import org.jboss.tools.cdi.seam.config.core.definition.SeamVirtualFieldDefinition; import org.jboss.tools.cdi.seam.config.core.util.Util; import org.jboss.tools.cdi.seam.config.core.validation.SeamConfigValidationMessages; import org.jboss.tools.cdi.seam.config.core.xml.SAXAttribute; import org.jboss.tools.cdi.seam.config.core.xml.SAXElement; import org.jboss.tools.cdi.seam.config.core.xml.SAXParser; import org.jboss.tools.cdi.seam.config.core.xml.SAXText; import org.jboss.tools.common.java.IJavaAnnotation; import org.jboss.tools.common.java.impl.AnnotationLiteral; /** * * @author Viacheslav Kabanovich * */ public class SeamDefinitionBuilder { static int IN_ANNOTATION_TYPE = 1; CDICoreNature project; ConfigDefinitionContext context; IResource resource; SeamBeansDefinition result; SAXElement root; public SeamBeansDefinition createDefinition(IResource resource, IDocument document, CDICoreNature project, ConfigDefinitionContext context) { this.project = project; this.context = context; this.resource = resource; result = new SeamBeansDefinition(); result.setResource(resource); if(document.get().indexOf("<") >= 0) { // file can be empty SAXParser parser = new SAXParser(); String text = document.get(); ByteArrayInputStream s = new ByteArrayInputStream(text.getBytes()); root = parser.parse(s, document); scanRoot(); } return result; } private void scanRoot() { if(root == null) return; List<SAXElement> es = root.getChildElements(); for (SAXElement element: es) { scanElement(element); } } private void scanElement(SAXElement element) { if(!Util.isConfigRelevant(element)) return; IType type = Util.resolveType(element, project); if(type == null) { reportUnresolvedType(element); result.addPossibleTypeNames(Util.getPossibleTypeNames(element)); return; } TypeCheck typeCheck = new TypeCheck(type, element); if(typeCheck.isCorrupted) return; if(typeCheck.isAnnotation) { scanAnnotation(element, type); } else if(Util.hasProducesChild(element)) { SeamVirtualFieldDefinition f = scanVirtualProducerField(element); if(f != null) { result.addVirtualField(f); } } else { scanBean(element, type, false); } } private void reportUnresolvedType(SAXElement element) { Set<String> ps = Util.getPossibleTypeNames(element); if(ps.size() == 1) { String type = ps.iterator().next(); String message = NLS.bind(SeamConfigValidationMessages.UNRESOLVED_TYPE, type); result.addUnresolvedNode(element, CDISeamConfigConstants.ERROR_UNRESOLVED_TYPE, message); } else { String type = element.getLocalName(); String message = NLS.bind(SeamConfigValidationMessages.UNRESOLVED_TYPE, type); result.addUnresolvedNode(element, CDISeamConfigConstants.ERROR_UNRESOLVED_TYPE, message); } } private void scanAnnotation(SAXElement element, IType type) { context.getRootContext().getAnnotationKind(type); // kick it AnnotationDefinition def = new AnnotationDefinition(); def.setType(type, context.getRootContext(), 0); List<SAXElement> es = element.getChildElements(); //children should be annotation declarations. for (SAXElement c: es) { IJavaAnnotation a = loadAnnotationDeclaration(c, IN_ANNOTATION_TYPE); if(a != null) def.addAnnotation(a, context.getRootContext()); } def.revalidateKind(context.getRootContext()); context.addAnnotation(type.getFullyQualifiedName(), def); } private SeamBeanDefinition scanBean(SAXElement element, IType type, boolean inline) { addDependency(type); SeamBeanDefinition def = new SeamBeanDefinition(); def.setResource(resource); def.setInline(inline); def.setNode(element); def.setType(type); result.addBeanDefinition(def); List<SAXElement> es = element.getChildElements(); for (SAXElement c: es) { if(!Util.isConfigRelevant(c)) continue; if(Util.containsEEPackage(c)) { if(CDISeamConfigConstants.KEYWORD_REPLACES.equals(c.getLocalName())) { def.setReplaces(c); continue; } if(CDISeamConfigConstants.KEYWORD_MODIFIES.equals(c.getLocalName())) { def.setModifies(c); continue; } if(Util.isParameters(c)) { SeamMethodDefinition md = scanConstructor(c, type); if(md != null) def.addMethod(md); continue; } } IType t = Util.resolveType(c, project); if(t != null) { IJavaAnnotation a = loadAnnotationDeclaration(c, IN_ANNOTATION_TYPE); if(a != null) def.addAnnotation(a); continue; } IMember m = null; if(c.getURI() != null && c.getURI().equals(element.getURI())) try { m = Util.resolveMember(type, c); } catch (JavaModelException e) { CDISeamConfigCorePlugin.getDefault().logError(e); } if(m instanceof IField) { def.addField(scanField(c, (IField)m)); } else if(m instanceof IMethod) { SeamMethodDefinition md = scanMethod(c, type); if(md != null) def.addMethod(md); } else { reportUnresolvedMember(c, type); } } Set<String> as = element.getAttributeNames(); for (String name: as) { SAXAttribute a = element.getAttribute(name); IField f = type.getField(name); if(f == null || !f.exists()) { reportUnresolvedField(a, type); } else { def.addField(scanField(a, f)); } } return def; } void reportUnresolvedMember(SAXElement c, IType type) { String message = NLS.bind(SeamConfigValidationMessages.UNRESOLVED_MEMBER, c.getLocalName(), type.getElementName()); result.addUnresolvedNode(c, CDISeamConfigConstants.ERROR_UNRESOLVED_MEMBER, message); } void reportUnresolvedMethod(SAXElement c, IType type, String params) { String message = NLS.bind(SeamConfigValidationMessages.UNRESOLVED_METHOD, c.getLocalName() + "(" + params + ")", type.getElementName()); result.addUnresolvedNode(c, CDISeamConfigConstants.ERROR_UNRESOLVED_METHOD, message); } void reportUnresolvedConstructor(SAXElement c, IType type, String params) { String message = NLS.bind(SeamConfigValidationMessages.UNRESOLVED_CONSTRUCTOR, type.getElementName() + "(" + params + ")"); result.addUnresolvedNode(c, CDISeamConfigConstants.ERROR_UNRESOLVED_CONSTRUCTOR, message); } void reportUnresolvedField(SAXAttribute c, IType type) { String message = NLS.bind(SeamConfigValidationMessages.UNRESOLVED_FIELD, c.getName(), type.getElementName()); result.addUnresolvedNode(c, CDISeamConfigConstants.ERROR_UNRESOLVED_MEMBER, message); } void reportUnresolvedMethod(SAXAttribute c, IType type) { String message = NLS.bind(SeamConfigValidationMessages.UNRESOLVED_METHOD, c.getName() + "()", type.getElementName()); result.addUnresolvedNode(c, CDISeamConfigConstants.ERROR_UNRESOLVED_METHOD, message); } private SeamVirtualFieldDefinition scanVirtualProducerField(SAXElement element) { SeamVirtualFieldDefinition def = new SeamVirtualFieldDefinition(); def.setResource(resource); def.setNode(element); IType type = Util.resolveType(element, project); if(type == null) { reportUnresolvedType(element); return null; } def.setType(type); scanFieldContent(def, element); return def; } private SeamFieldDefinition scanField(SAXElement element, IField field) { SeamFieldDefinition def = new SeamFieldDefinition(); def.setResource(resource); def.setNode(element); def.setField(field); scanFieldContent(def, element); return def; } private void scanFieldContent(AbstractSeamFieldDefinition def, SAXElement element) { if(Util.hasText(element)) { def.addValue(element.getTextNode()); } List<SAXElement> es = element.getChildElements(); for (SAXElement c: es) { if(!Util.isConfigRelevant(c)) continue; if(Util.isValue(c)) { if(Util.hasText(c)) { def.addValue(c.getTextNode()); } else { scanFieldValue(def, c); } continue; } else if(Util.isEntry(c)) { scanEntry(def, c); continue; } IType t = Util.resolveType(c, project); if(t != null) { IJavaAnnotation a = loadAnnotationDeclaration(c, IN_ANNOTATION_TYPE); if(a != null) def.addAnnotation(a); continue; } else { reportUnresolvedType(c); } } } private SeamFieldDefinition scanField(SAXAttribute a, IField field) { SeamFieldDefinition def = new SeamFieldDefinition(); def.setResource(resource); def.setNode(a); def.setField(field); def.addValue(a); return def; } /** * Scan field value for inline bean declarations. * @param element */ private void scanFieldValue(AbstractSeamFieldDefinition def, SAXElement element) { if(!Util.isConfigRelevant(element)) return; List<SAXElement> es = element.getChildElements(); for (SAXElement c: es) { if(!Util.isConfigRelevant(c)) continue; IType type = Util.resolveType(c, project); if(type == null) continue; TypeCheck typeCheck = new TypeCheck(type, c); if(typeCheck.isCorrupted) return; if(!typeCheck.isAnnotation) { SeamBeanDefinition inline = scanBean(c, type, true); IJavaAnnotation q = createInlineBeanQualifier(); if(q != null) { SeamFieldValueDefinition vdef = new SeamFieldValueDefinition(); vdef.setResource(resource); vdef.setNode(element); inline.addAnnotation(q); vdef.addAnnotation(q); vdef.setInlineBean(inline); IJavaAnnotation inject = createInject(element); if(inject != null) { vdef.addAnnotation(inject); } def.addValueDefinition(vdef); } } } } private void scanEntry(AbstractSeamFieldDefinition def, SAXElement element) { List<SAXElement> es = element.getChildElements(); SAXText key = null; SAXText value = null; for (SAXElement c: es) { if(!Util.isConfigRelevant(c)) continue; if(Util.isKey(c)) { if(Util.hasText(c)) { key = c.getTextNode(); } else { scanFieldValue(def, c); } } if(Util.isValue(c)) { if(Util.hasText(c)) { value = c.getTextNode(); } else { scanFieldValue(def, c); } } } if(key != null && value != null) { def.addValue(key, value); } } private SeamMethodDefinition scanMethod(SAXElement element, IType type) { SeamMethodDefinition def = new SeamMethodDefinition(); def.setResource(resource); def.setNode(element); StringBuilder paramPresentation = new StringBuilder(); List<SAXElement> es = element.getChildElements(); for (SAXElement c: es) { if(!Util.isConfigRelevant(c)) continue; if(Util.isParameters(c)) { List<SAXElement> ps = c.getChildElements(); for (SAXElement p: ps) { SeamParameterDefinition pd = scanParameter(p, paramPresentation); if(pd != null) def.addParameter(pd); } continue; } else if(Util.isArray(c)) { SeamParameterDefinition pd = scanParameter(c, paramPresentation); if(pd != null) def.addParameter(pd); continue; } IType t = Util.resolveType(c, project); if(t != null) { IJavaAnnotation a = loadAnnotationDeclaration(c, IN_ANNOTATION_TYPE); if(a != null) def.addAnnotation(a); continue; } else { reportUnresolvedType(c); } } IMethod method = null; try { method = Util.findMethod(def, type, element.getLocalName(), context.getRootContext()); } catch (JavaModelException e) { CDISeamConfigCorePlugin.getDefault().logError(e); } if(method != null) { def.setMethod(method); } else { reportUnresolvedMethod(element, type, paramPresentation.toString()); def = null; } return def; } private SeamMethodDefinition scanConstructor(SAXElement element, IType type) { SeamMethodDefinition def = new SeamMethodDefinition(); def.setResource(resource); def.setNode(element); StringBuilder paramPresentation = new StringBuilder(); if(Util.isParameters(element)) { List<SAXElement> ps = element.getChildElements(); for (SAXElement p: ps) { SeamParameterDefinition pd = scanParameter(p, paramPresentation); if(pd != null) def.addParameter(pd); } } else if(Util.isArray(element)) { SeamParameterDefinition pd = scanParameter(element, paramPresentation); if(pd != null) def.addParameter(pd); } IJavaAnnotation inject = createInject(element); if(inject != null) def.addAnnotation(inject); IMethod method = null; try { method = Util.findMethod(def, type, null, context.getRootContext()); } catch (JavaModelException e) { CDISeamConfigCorePlugin.getDefault().logError(e); } if(method != null) { def.setMethod(method); } else { reportUnresolvedConstructor(element, type, paramPresentation.toString()); def = null; } return def; } private SeamParameterDefinition scanParameter(SAXElement element, StringBuilder paramPresentation) { if(!Util.isConfigRelevant(element)) return null; SeamParameterDefinition def = new SeamParameterDefinition(); def.setResource(resource); def.setNode(element); if(Util.isArray(element)) { if(element.hasAttribute(CDISeamConfigConstants.ATTR_DIMENSIONS)) { def.setDimensions(element.getAttribute(CDISeamConfigConstants.ATTR_DIMENSIONS).getValue()); } else { def.setDimensions("1"); } List<SAXElement> es = element.getChildElements(); for (SAXElement c: es) { if(!Util.isConfigRelevant(c)) continue; if(paramPresentation.length() > 0) paramPresentation.append(","); paramPresentation.append(c.getLocalName()); for (int q = 0; q < def.getDimensions(); q++) paramPresentation.append("[]"); IType type = Util.resolveType(c, project); if(type == null) { reportUnresolvedType(c); continue; } TypeCheck typeCheck = new TypeCheck(type, c); if(typeCheck.isCorrupted) continue; if(typeCheck.isAnnotation) { IJavaAnnotation a = loadAnnotationDeclaration(c, IN_ANNOTATION_TYPE); if(a != null) def.addAnnotation(a); } else { def.setType(type); } } } else { if(paramPresentation.length() > 0) paramPresentation.append(","); paramPresentation.append(element.getLocalName()); IType type = Util.resolveType(element, project); if(type == null) { reportUnresolvedType(element); return null; } def.setType(type); List<SAXElement> es = element.getChildElements(); for (SAXElement c: es) { if(!Util.isConfigRelevant(c)) { continue; //report? } if(Util.containsEEPackage(c)) continue; //we are not interested yet IType t = Util.resolveType(c, project); if(t != null) { IJavaAnnotation a = loadAnnotationDeclaration(c, IN_ANNOTATION_TYPE); if(a != null) def.addAnnotation(a); continue; } } } return def; } private IJavaAnnotation loadAnnotationDeclaration(SAXElement element, int contextKind) { if(!Util.isConfigRelevant(element)) return null; IType type = Util.resolveType(element, project); if(type == null) { if(contextKind == IN_ANNOTATION_TYPE) { reportUnresolvedType(element); } return null; } TypeCheck typeCheck = new TypeCheck(type, element); if(typeCheck.isCorrupted) return null; if(typeCheck.isAnnotation) { addDependency(type); context.getRootContext().getAnnotationKind(type); // kick it String value = null; SAXText text = element.getTextNode(); if(text != null && text.getValue() != null && text.getValue().trim().length() > 0) { value = text.getValue(); } AnnotationLiteral literal = new AnnotationLiteral(resource, element.getLocation().getStartPosition(), element.getLocation().getLength(), value, IMemberValuePair.K_STRING, type); Set<String> ns = element.getAttributeNames(); for (String n: ns) { SAXAttribute attr = element.getAttribute(n); String v = attr.getValue(); literal.addMemberValuePair(n, v, IMemberValuePair.K_STRING); IMethod m = type.getMethod(n, new String[0]); if(!m.exists()) { reportUnresolvedMethod(attr, type); } } return literal; } else if(contextKind == IN_ANNOTATION_TYPE) { result.addUnresolvedNode(element, CDISeamConfigConstants.ERROR_ANNOTATION_EXPECTED, SeamConfigValidationMessages.ANNOTATION_EXPECTED); } return null; } class TypeCheck { boolean isCorrupted = false; boolean isAnnotation = false; TypeCheck(IType type, SAXElement element) { try { isAnnotation = type.isAnnotation(); } catch (JavaModelException e) { CDISeamConfigCorePlugin.getDefault().logError(e); reportUnresolvedType(element); isCorrupted = true; } } } static long inlineBeanCount = 0; IJavaAnnotation createInlineBeanQualifier() { IType type = project.getType(CDISeamConfigConstants.INLINE_BEAN_QUALIFIER); if(type == null) { type = project.getType(CDISeamConfigConstants.INLINE_BEAN_QUALIFIER_30); } if(type == null) { return null; } long id = inlineBeanCount++; return new AnnotationLiteral(resource, 0, 0, "" + id, IMemberValuePair.K_STRING, type); } IJavaAnnotation createInject(SAXElement forElement) { IType type = project.getType(CDIConstants.INJECT_ANNOTATION_TYPE_NAME); return (type == null) ? null : new AnnotationLiteral(resource, forElement.getLocation().getStartPosition(), forElement.getLocation().getLength(), null, 0, type); } private void addDependency(IType type) { if(!type.exists() || type.isBinary()) return; if(!resource.exists() || resource.getName().endsWith(".jar")) return; //beans.xml depends on type context.getRootContext().addDependency(type.getResource().getFullPath(), resource.getFullPath()); //though type does not depend on beans.xml it has to be revalidated. Maybe it should be method addValidationDependency. context.getRootContext().addDependency(resource.getFullPath(), type.getResource().getFullPath()); } }