package com.blazebit.blazefaces.apt; import com.blazebit.template.ClassPathTemplateLoader; import com.blazebit.apt.AnnotationProcessingUtils; import com.blazebit.blazefaces.apt.model.Application; import com.blazebit.blazefaces.apt.model.Attribute; import com.blazebit.blazefaces.apt.model.Behavior; import com.blazebit.blazefaces.apt.model.BehaviorRenderer; import com.blazebit.blazefaces.apt.model.Component; import com.blazebit.blazefaces.apt.model.Description; import com.blazebit.blazefaces.apt.model.Factory; import com.blazebit.blazefaces.apt.model.Function; import com.blazebit.blazefaces.apt.model.Icon; import com.blazebit.blazefaces.apt.model.Namespace; import com.blazebit.blazefaces.apt.model.PhaseListener; import com.blazebit.blazefaces.apt.model.Renderer; import com.blazebit.blazefaces.apt.model.SystemEventListener; import com.blazebit.blazefaces.apt.model.Tag; import com.blazebit.blazefaces.apt.model.TagHolder; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.tools.JavaFileObject; import freemarker.template.Configuration; import freemarker.template.ObjectWrapper; import freemarker.template.Template; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Stack; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.StandardLocation; /** * Creates taglib.xml, faces-config.xml and base classes for components and * behaviors. * * @author Christian Beikov * @since 0.1.3 */ @SupportedAnnotationTypes("com.blazebit.blazefaces.apt.*") @SupportedSourceVersion(SourceVersion.RELEASE_6) public class JsfAnnotationProcessor extends AbstractProcessor { public static final String BEHAVIOR_TEMPLATE = "templates/behavior.ftl"; public static final String COMPONENT_TEMPLATE = "templates/component.ftl"; public static final String TAGLIB_TEMPLATE = "templates/taglib.ftl"; public static final String FACES_CONFIG_TEMPLATE = "templates/faces-config.ftl"; private Configuration templateConfiguration; private Map<String, Namespace> namespaces = new HashMap<String, Namespace>(); public JsfAnnotationProcessor() { this(getDefaultConfiguration()); } public JsfAnnotationProcessor(Configuration configuration) { this.templateConfiguration = configuration; } public static Configuration getDefaultConfiguration() { Configuration configuration = new Configuration(); configuration.setTemplateLoader(new ClassPathTemplateLoader(JsfAnnotationProcessor.class.getClassLoader())); configuration.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER); return configuration; } protected List<String> getImports(Template template) { if (BEHAVIOR_TEMPLATE.equals(template.getName())) { return Arrays.asList("java.util.HashMap", "java.util.Map", "java.util.Set", "javax.el.ELContext", "javax.el.ELException", "javax.el.ValueExpression", "javax.faces.FacesException", "javax.faces.context.FacesContext", "javax.faces.component.UIComponentBase"); } else if (COMPONENT_TEMPLATE.equals(template.getName())) { return Arrays.asList("java.util.List", "java.util.ArrayList", "javax.el.ValueExpression"); } return Collections.emptyList(); } protected Map.Entry<String, Namespace> getNamespaceEntry(String packageName) { for (Map.Entry<String, Namespace> entry : namespaces.entrySet()) { if (packageName.startsWith(entry.getKey())) { return entry; } } if(namespaces.size() == 1) { return namespaces.entrySet().iterator().next(); } throw new IllegalArgumentException("Could not find a namespace for the package name '" + packageName + "'"); } protected Namespace getNamespace(String packageName) { for (Map.Entry<String, Namespace> entry : namespaces.entrySet()) { if (packageName.startsWith(entry.getKey())) { return entry.getValue(); } } if(namespaces.size() == 1) { return namespaces.values().iterator().next(); } throw new IllegalArgumentException("Could not find a namespace for the package name '" + packageName + "'"); } protected Behavior getBehavior(String behaviorClass) { for (Behavior behavior : getNamespace(behaviorClass).getBehaviors()) { if (behavior.getClazz().equals(behaviorClass)) { return behavior; } } return null; } protected Namespace processNamespace(PackageElement element) { Namespace namespace = new Namespace(); AnnotationMirror annotation = AnnotationProcessingUtils .findAnnotationMirror(element, JsfNamespace.class); namespace.setPackageName(element.getQualifiedName().toString()); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); if ("name".equals(memberName)) { namespace.setName(entry.getValue().getValue().toString()); } else if ("uri".equals(memberName)) { namespace.setNamespace(entry.getValue().getValue() .toString()); } else if ("shortName".equals(memberName)) { namespace.setShortName(entry.getValue().getValue() .toString()); } } return namespace; } protected Renderer processRenderer(TypeElement typeElement, Namespace namespace) { Renderer renderer = new Renderer(); AnnotationMirror annotation = AnnotationProcessingUtils .findAnnotationMirror(typeElement, JsfRenderer.class); renderer.setClazz(typeElement.getQualifiedName().toString()); renderer.setType(namespace.getPackageName() + ".renderer." + typeElement.getSimpleName().toString()); renderer.setComponentFamily(namespace.getPackageName() + ".component"); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object defaultValue = entry.getKey().getDefaultValue() .getValue(); Object value = entry.getValue().getValue(); boolean isDefault = defaultValue != null && defaultValue.equals(value); if ("type".equals(memberName) && !isDefault) { renderer.setType(entry.getValue().getValue().toString()); } else if ("family".equals(memberName) && !isDefault) { renderer.setComponentFamily(entry.getValue().getValue().toString()); } } return renderer; } protected BehaviorRenderer processBehaviorRenderer(TypeElement typeElement, Namespace namespace) { BehaviorRenderer clientBehaviorRenderer = new BehaviorRenderer(); AnnotationMirror annotation = AnnotationProcessingUtils .findAnnotationMirror(typeElement, JsfBehaviorRenderer.class); clientBehaviorRenderer.setClazz(typeElement.getQualifiedName() .toString()); clientBehaviorRenderer.setType(namespace.getPackageName() + ".behavior.renderer." + typeElement.getSimpleName().toString()); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object defaultValue = entry.getKey().getDefaultValue() .getValue(); Object value = entry.getValue().getValue(); boolean isDefault = defaultValue != null && defaultValue.equals(value); if ("type".equals(memberName) && !isDefault) { clientBehaviorRenderer.setType(entry.getValue().getValue() .toString()); } } return clientBehaviorRenderer; } @SuppressWarnings("unchecked") protected Behavior processBehavior(TypeElement typeElement, Namespace namespace) { Behavior behavior = new Behavior(); AnnotationMirror annotation = AnnotationProcessingUtils .findAnnotationMirror(typeElement, JsfBehavior.class); behavior.getTag().setName( firstToLowerCase(typeElement.getSimpleName().toString())); behavior.setId(namespace.getPackageName() + ".behavior." + typeElement.getSimpleName().toString()); behavior.setClazz(typeElement.getQualifiedName().toString()); behavior.setAbstract(typeElement.getModifiers().contains( Modifier.ABSTRACT)); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object defaultValue = entry.getKey().getDefaultValue() .getValue(); Object value = entry.getValue().getValue(); boolean isDefault = defaultValue != null && defaultValue.equals(value); if ("tag".equals(memberName) && !isDefault) { behavior.getTag().setName( entry.getValue().getValue().toString()); } else if ("id".equals(memberName) && !isDefault) { behavior.setId(entry.getValue().getValue().toString()); } else if ("hints".equals(memberName) && !isDefault) { behavior.setHints(getHints((List<AnnotationValue>) entry.getValue().getValue())); } else if ("description".equals(memberName) && !isDefault) { behavior.getTag().setDescription( getDescription((AnnotationMirror) entry.getValue() .getValue())); } else if ("attributes".equals(memberName)) { mergeAttributes(behavior.getTag().getAttributes(), parseAttributes((List<AnnotationValue>) entry.getValue().getValue())); } else if ("parent".equals(memberName)) { behavior.setParent(entry.getValue().getValue().toString()); } else if ("handler".equals(memberName) && !isDefault) { behavior.setHandler( entry.getValue().getValue().toString()); } else if ("renderer".equals(memberName) && !isDefault) { behavior.setRenderer(entry.getValue().getValue().toString()); } else if ("rendererType".equals(memberName) && !isDefault) { behavior.setRendererType(entry.getValue().getValue().toString()); } } if(behavior.isAbstract()) { // Abstract behaviors are not allowed to have parents behavior.setParent(null); } else { if (behavior.getRenderer() != null && behavior.getRendererType() == null) { for (BehaviorRenderer renderer : namespace.getClientBehaviorRenderers()) { if (renderer.getClazz().equals(behavior.getRenderer())) { behavior.setRendererType(renderer.getType()); break; } } } } if (!behavior.getHints().isEmpty()) { Set<String> imports = new HashSet<String>(behavior.getImports()); imports.add("java.util.Collections"); imports.add("java.util.EnumSet"); imports.add("javax.faces.component.behavior.ClientBehaviorHint"); behavior.setImports(new ArrayList<String>(imports)); } return behavior; } @SuppressWarnings("unchecked") protected Component processComponent(TypeElement typeElement, Namespace namespace) { Component component = new Component(); AnnotationMirror annotation = AnnotationProcessingUtils .findAnnotationMirror(typeElement, JsfComponent.class); component.getTag().setName( firstToLowerCase(typeElement.getSimpleName().toString())); component.setClazz(typeElement.getQualifiedName().toString()); component.setAbstract(typeElement.getModifiers().contains( Modifier.ABSTRACT)); component.setType(namespace.getPackageName() + ".component." + typeElement.getSimpleName().toString()); component.setFamily(namespace.getPackageName() + ".component"); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object defaultValue = entry.getKey().getDefaultValue() .getValue(); Object value = entry.getValue().getValue(); boolean isDefault = defaultValue != null && defaultValue.equals(value); if ("tag".equals(memberName) && !isDefault) { component.getTag().setName( entry.getValue().getValue().toString()); } else if ("type".equals(memberName) && !isDefault) { component.setType(entry.getValue().getValue().toString()); } else if ("family".equals(memberName) && !isDefault) { component.setFamily(entry.getValue().getValue().toString()); } else if ("description".equals(memberName) && !isDefault) { component.getTag().setDescription( getDescription((AnnotationMirror) entry.getValue() .getValue())); } else if ("attributes".equals(memberName)) { mergeAttributes(component.getTag().getAttributes(), parseAttributes((List<AnnotationValue>) entry.getValue().getValue())); } else if ("parent".equals(memberName)) { component.setParent(entry.getValue().getValue().toString()); } else if ("handler".equals(memberName) && !isDefault) { component.setHandler( entry.getValue().getValue().toString()); } else if ("behavior".equals(memberName) && !isDefault) { component.getTag() .setBehavior( getBehavior(entry.getValue().getValue() .toString())); } else if ("renderer".equals(memberName) && !isDefault) { component.setRenderer(entry.getValue().getValue() .toString()); } else if ("rendererType".equals(memberName) && !isDefault) { component.setRendererType(entry.getValue().getValue().toString()); } } if(component.isAbstract()) { // Abstract components are not allowed to have parents component.setParent(null); // also they don't have renderers } else { if (component.getRenderer() != null && component.getRendererType() == null) { for (Renderer renderer : namespace.getRenderers()) { if (renderer.getClazz().equals(component.getRenderer())) { component.setRendererType(renderer.getType()); break; } } } } return component; } protected Function processFunction(ExecutableElement executableElement, Namespace namespace) { Types typeUtils = processingEnv.getTypeUtils(); TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement(); String methodName = executableElement.getSimpleName().toString(); Function function = new Function(); StringBuilder signatureSb = new StringBuilder(); AnnotationMirror annotation = AnnotationProcessingUtils .findAnnotationMirror(executableElement, JsfFunction.class); signatureSb.append(typeUtils.erasure(executableElement.getReturnType()).toString()) .append(" "); signatureSb.append(executableElement.getSimpleName().toString()) .append("("); List<? extends VariableElement> parameters = executableElement .getParameters(); if (!parameters.isEmpty()) { for (int i = 0; i < parameters.size() - 1; i++) { signatureSb.append(typeUtils.erasure(parameters.get(i).asType()).toString()) .append(", "); } signatureSb.append(typeUtils.erasure(parameters.get(parameters.size() - 1) .asType()).toString()); } signatureSb.append(')'); function.setName(methodName); function.setClazz(typeElement.getQualifiedName().toString()); function.setSignature(signatureSb.toString()); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object defaultValue = entry.getKey().getDefaultValue() .getValue(); Object value = entry.getValue().getValue(); boolean isDefault = defaultValue != null && defaultValue.equals(value); if ("name".equals(memberName) && !isDefault) { function.setName(entry.getValue().getValue().toString()); } else if ("description".equals(memberName) && !isDefault) { function.setDescription(getDescription((AnnotationMirror) entry .getValue().getValue())); } } return function; } private SystemEventListener processSystemEventListener(TypeElement typeElement, AnnotationMirror annotation) { SystemEventListener listener = new SystemEventListener(); listener.setSystemEventListenerClass(typeElement.getQualifiedName().toString()); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object value = entry.getValue().getValue(); if ("source".equals(memberName)) { listener.setSourceClass(value.toString()); } else if ("event".equals(memberName)) { listener.setSystemEventClass(value.toString()); } } return listener; } @SuppressWarnings("unchecked") private Collection<SystemEventListener> processSystemEventListeners(TypeElement typeElement) { Collection<SystemEventListener> systemEventListeners = new ArrayList<SystemEventListener>(); AnnotationMirror annotation = AnnotationProcessingUtils .findAnnotationMirror(typeElement, JsfSystemEventListeners.class); List<AnnotationValue> listeners = (List<AnnotationValue>) annotation.getElementValues().get("value").getValue(); for(AnnotationValue listener : listeners) { systemEventListeners.add(processSystemEventListener(typeElement, (AnnotationMirror) listener.getValue())); } return systemEventListeners; } private PhaseListener processPhaseListener(TypeElement typeElement, AnnotationMirror annotation) { return new PhaseListener(typeElement.getQualifiedName().toString()); } protected Attribute processAttribute(Element e) { Types typeUtils = processingEnv.getTypeUtils(); Attribute attribute = new Attribute(); AnnotationMirror annotation = AnnotationProcessingUtils .findAnnotationMirror(e, JsfAttribute.class); switch (e.getKind()) { case METHOD: ExecutableElement executableElement = (ExecutableElement) e; attribute.setName(guessFieldName(executableElement .getSimpleName().toString())); attribute.setType(typeUtils.erasure(executableElement.getReturnType()).toString()); attribute.setObjectType(getObjectType(executableElement.getReturnType().toString())); break; case FIELD: VariableElement variableElement = (VariableElement) e; attribute.setName(variableElement.getSimpleName().toString()); attribute.setType(typeUtils.erasure(variableElement.asType()).toString()); attribute.setObjectType(getObjectType(typeUtils.erasure(variableElement.asType()).toString())); break; default: // Should not happen break; } for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object defaultValue = entry.getKey().getDefaultValue() .getValue(); Object value = entry.getValue().getValue(); boolean isDefault = defaultValue != null && defaultValue.equals(value); if ("name".equals(memberName) && !isDefault) { attribute.setName(entry.getValue().getValue().toString()); } else if ("required".equals(memberName) && !isDefault) { attribute.setRequired(Boolean.valueOf(entry.getValue() .getValue().toString())); } else if ("type".equals(memberName) && !isDefault) { attribute.setType(entry.getValue().getValue().toString()); attribute.setObjectType(getObjectType(entry.getValue().getValue().toString())); } else if ("description".equals(memberName) && !isDefault) { attribute .setDescription(getDescription((AnnotationMirror) entry .getValue().getValue())); } else if ("ignore".equals(memberName)) { attribute.setIgnore(Boolean.valueOf(entry.getValue() .getValue().toString())); } else if ("defaultValue".equals(memberName) && !isDefault) { attribute.setDefaultValue(entry.getValue().getValue() .toString()); } } return attribute; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (annotations.isEmpty()) { return true; } TypeElement typeElement; String typeElementPackage; Map.Entry<String, Namespace> namespaceEntry; String namespacePackage; Namespace namespace; try{ // Phase 1: Create model for found elements for (Element e : roundEnv.getElementsAnnotatedWith(JsfNamespace.class)) { PackageElement packageElement = (PackageElement) e; namespacePackage = packageElement.getQualifiedName().toString(); namespaces.put(namespacePackage, processNamespace(packageElement)); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfRenderer.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getRenderers() .add(processRenderer(typeElement, namespace)); } for (Element e : roundEnv .getElementsAnnotatedWith(JsfBehaviorRenderer.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getClientBehaviorRenderers() .add(processBehaviorRenderer(typeElement, namespace)); } // Do behaviors before components since they can reference them for (Element e : roundEnv.getElementsAnnotatedWith(JsfBehavior.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getBehaviors() .add(processBehavior(typeElement, namespace)); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfComponent.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getComponents() .add(processComponent(typeElement, namespace)); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfFunction.class)) { ExecutableElement executableElement = (ExecutableElement) e; typeElement = (TypeElement) executableElement.getEnclosingElement(); typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getFunctions() .add(processFunction(executableElement, namespace)); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfExceptionHandlerFactory.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getFactory().setExceptionHandlerFactory(typeElementPackage); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfPartialViewContextFactory.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getFactory().setPartialViewContextFactory(typeElementPackage); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfResourceHandler.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getApplication().setResourceHandler(typeElementPackage); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfSystemEventListener.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getApplication() .getSystemEventListeners() .add(processSystemEventListener(typeElement, AnnotationProcessingUtils.findAnnotationMirror(typeElement, JsfSystemEventListener.class))); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfSystemEventListeners.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getApplication() .getSystemEventListeners() .addAll(processSystemEventListeners(typeElement)); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfPhaseListener.class)) { typeElement = (TypeElement) e; typeElementPackage = typeElement.getQualifiedName().toString(); namespaceEntry = getNamespaceEntry(typeElementPackage); namespacePackage = namespaceEntry.getKey(); namespace = namespaceEntry.getValue(); namespace.getPhaseListeners() .add(processPhaseListener(typeElement, AnnotationProcessingUtils.findAnnotationMirror(typeElement, JsfSystemEventListener.class))); } for (Element e : roundEnv.getElementsAnnotatedWith(JsfAttribute.class)) { Tag tag = getTag(e); mergeAttributes(tag.getAttributes(), processAttribute(e)); } // Phase 2: Apply inherited attributes to tags for (Namespace ns : namespaces.values()) { Application app = ns.getApplication(); Factory fact = ns.getFactory(); Map<String, Behavior> behaviorMap = new HashMap<String, Behavior>(); for (Behavior behavior : ns.getBehaviors()) { behaviorMap.put(behavior.getClazz(), behavior); ns.getTags().add(behavior.getTag()); } for (Behavior behavior : ns.getBehaviors()) { inheritAttributes(ns, behavior, behaviorMap); } Map<String, Component> componentMap = new HashMap<String, Component>(); for (Component component : ns.getComponents()) { componentMap.put(component.getClazz(), component); ns.getTags().add(component.getTag()); } for (Component component : ns.getComponents()) { inheritAttributes(ns, component, componentMap); } if (app.getResourceHandler() == null && app.getSystemEventListeners().isEmpty()) { ns.setApplication(null); } if(fact.getExceptionHandlerFactory() == null && fact.getPartialViewContextFactory() == null) { ns.setFactory(null); } } // Phase 3: Generate files return generateFiles(); } catch(Exception ex) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ex.printStackTrace(new PrintStream(baos)); String message = baos.toString(); processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not generate jsf resources\n" + message); return false; } } protected boolean generateFiles() { try { Template taglibTemplate = templateConfiguration .getTemplate(TAGLIB_TEMPLATE); Template facesConfigTemplate = templateConfiguration .getTemplate(FACES_CONFIG_TEMPLATE); Template behaviorTemplate = templateConfiguration .getTemplate(BEHAVIOR_TEMPLATE); Template componentTemplate = templateConfiguration .getTemplate(COMPONENT_TEMPLATE); List<String> componentImports = getImports(componentTemplate); List<String> behaviorImports = getImports(behaviorTemplate); for (Namespace namespace : namespaces.values()) { String taglibName = "META-INF/" + namespace.getName() + "-" + namespace.getShortName() + ".taglib.xml"; String facesConfigName = "META-INF/faces-config.xml"; FileObject taglibFileObject = processingEnv.getFiler() .createResource(StandardLocation.SOURCE_OUTPUT, "", taglibName); FileObject facesConfigFileObject = processingEnv.getFiler() .createResource(StandardLocation.SOURCE_OUTPUT, "", facesConfigName); Writer writer = null; Map<String, Object> map = new HashMap<String, Object>(); map.put("namespace", namespace); try { writer = taglibFileObject.openWriter(); taglibTemplate.process(map, writer); } finally { if (writer != null) { writer.close(); } } try { writer = facesConfigFileObject.openWriter(); facesConfigTemplate.process(map, writer); } finally { if (writer != null) { writer.close(); } } for (Behavior behavior : namespace.getBehaviors()) { if (!behavior.isAbstract()) { String packageName = behavior.getPackage(); String simpleName = behavior.getClazz().substring( packageName.length() + 1) + "Base"; String qualifiedName = packageName + "." + simpleName; JavaFileObject jfo = processingEnv.getFiler() .createSourceFile(qualifiedName); map = new HashMap<String, Object>(); // We are generating the base class so we have to override // the name behavior.setClazz(qualifiedName); map.put("behavior", behavior); // Apply the imports for the template Set<String> imports = new HashSet<String>(); imports.addAll(behaviorImports); imports.addAll(behavior.getImports()); behavior.setImports(new ArrayList<String>(imports)); try { writer = jfo.openWriter(); behaviorTemplate.process(map, writer); } finally { if (writer != null) { writer.close(); } } } } for (Component component : namespace.getComponents()) { if (!component.isAbstract()) { String packageName = component.getPackage(); String simpleName = component.getClazz().substring( packageName.length() + 1) + "Base"; String qualifiedName = packageName + "." + simpleName; JavaFileObject jfo = processingEnv.getFiler() .createSourceFile(qualifiedName); map = new HashMap<String, Object>(); // We are generating the base class so we have to override // the name component.setClazz(qualifiedName); map.put("component", component); // Apply the imports for the template Set<String> imports = new HashSet<String>(); imports.addAll(componentImports); imports.addAll(component.getImports()); component.setImports(new ArrayList<String>(imports)); try { writer = jfo.openWriter(); componentTemplate.process(map, writer); } finally { if (writer != null) { writer.close(); } } } } } } catch (Exception ex) { return false; } return true; } protected Description getDescription(AnnotationMirror descriptionValue) { Description description = new Description(); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : descriptionValue .getElementValues().entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object defaultValue = entry.getKey().getDefaultValue().getValue(); Object value = entry.getValue().getValue(); boolean isDefault = defaultValue != null && defaultValue.equals(value); if ("displayName".equals(memberName) && !isDefault) { description.setDisplayName(entry.getValue().getValue() .toString()); } else if ("description".equals(memberName) && !isDefault) { description.setDescription(entry.getValue().getValue() .toString()); } else if ("smallIcon".equals(memberName) && !isDefault) { description.getIcon().setSmallIcon( entry.getValue().getValue().toString()); } else if ("largeIcon".equals(memberName) && !isDefault) { description.getIcon().setLargeIcon( entry.getValue().getValue().toString()); } } if (description.getIcon().getSmallIcon() == null && description.getIcon().getLargeIcon() == null) { description.setIcon(null); } if (description.getDisplayName() == null && description.getDescription() == null && description.getIcon() == null) { description = null; } return description; } protected String guessFieldName(String methodName) { int offset = 0; if (methodName.startsWith("get") || methodName.startsWith("set")) { offset = 3; } else if (methodName.startsWith("is")) { offset = 2; } return new StringBuilder(methodName.length() - offset) .append(Character.toLowerCase(methodName.charAt(offset))) .append(methodName, offset + 1, methodName.length()) .toString(); } protected Tag getTag(Element e) { while (e.getEnclosingElement() != null && !(e.getEnclosingElement() instanceof PackageElement)) { e = e.getEnclosingElement(); } TypeElement typeElement = (TypeElement) e; String clazzName = typeElement.getQualifiedName().toString(); Namespace namespace = getNamespace(clazzName); for (Behavior behavior : namespace.getBehaviors()) { if (behavior.getClazz().equals(clazzName)) { return behavior.getTag(); } } for (Component component : namespace.getComponents()) { if (component.getClazz().equals(clazzName)) { return component.getTag(); } } return null; } protected Set<String> getParentAttributeNames(TagHolder tagHolder, Map<String, ? extends TagHolder> tagHolderMap) { Elements elementUtils = processingEnv.getElementUtils(); Set<String> list = new LinkedHashSet<String>(); Stack<TypeElement> stack = new Stack<TypeElement>(); TypeElement traverseElement; stack.add(elementUtils.getTypeElement(tagHolder.getParent())); for (TypeMirror typeMirror : stack.peek().getInterfaces()) { stack.add((TypeElement) ((DeclaredType) typeMirror).asElement()); } while (!stack.isEmpty()) { traverseElement = stack.pop(); TagHolder tempTagHolder = tagHolderMap.get(traverseElement.getQualifiedName().toString()); if (tempTagHolder != null) { for (Attribute attribute : tempTagHolder.getTag().getAttributes()) { list.add(attribute.getName()); } } for (Element e : traverseElement.getEnclosedElements()) { if (e.getKind() == ElementKind.METHOD && e.getModifiers().contains(Modifier.PUBLIC) && !e.getModifiers().contains(Modifier.ABSTRACT)) { ExecutableElement method = (ExecutableElement) e; list.add(guessFieldName(method.getSimpleName().toString())); } } if (traverseElement.getSuperclass() instanceof DeclaredType) { stack.add((TypeElement) ((DeclaredType) traverseElement.getSuperclass()).asElement()); } for (TypeMirror typeMirror : traverseElement.getInterfaces()) { stack.add((TypeElement) ((DeclaredType) typeMirror).asElement()); } } return list; } protected Set<TagHolder> getParentTagHolders(Namespace namespace, TagHolder tagHolder, Map<String, ? extends TagHolder> tagHolderMap) { Elements elementUtils = processingEnv.getElementUtils(); Set<TagHolder> list = new LinkedHashSet<TagHolder>(); Stack<TypeElement> stack = new Stack<TypeElement>(); TypeElement traverseElement; TagHolder traverseTagHolder; TagHolder parentTagHolder = tagHolder; do { stack.add(elementUtils.getTypeElement(parentTagHolder.getClazz())); while (!stack.isEmpty()) { traverseElement = stack.pop(); traverseTagHolder = tagHolderMap.get(traverseElement.getQualifiedName().toString()); if(traverseTagHolder == null) { // When we enter any of the two branches we found an interface or a class that isn't compiled with this code if(AnnotationProcessingUtils.findAnnotationMirror(traverseElement, JsfComponent.class) != null) { traverseTagHolder = processComponent(traverseElement, namespace); } else if(AnnotationProcessingUtils.findAnnotationMirror(traverseElement, JsfBehavior.class) != null) { traverseTagHolder = processBehavior(traverseElement, namespace); } // Note that because the elements come from foreign code, we need to parse the attributes within the body ourselves if(traverseTagHolder != null) { for(Element e : traverseElement.getEnclosedElements()) { if(AnnotationProcessingUtils.findAnnotationMirror(e, JsfAttribute.class) != null) { mergeAttributes(traverseTagHolder.getTag().getAttributes(), processAttribute(e)); } } } } if (traverseTagHolder != null) { list.add(traverseTagHolder); } if (traverseElement.getSuperclass() instanceof DeclaredType) { stack.add((TypeElement) ((DeclaredType) traverseElement.getSuperclass()).asElement()); } for (TypeMirror typeMirror : traverseElement.getInterfaces()) { stack.add((TypeElement) ((DeclaredType) typeMirror).asElement()); } } } while ((parentTagHolder = tagHolderMap.get(parentTagHolder.getParent())) != null); list.remove(tagHolder); return list; } protected void inheritAttributes(Namespace namespace, TagHolder tagHolder, Map<String, ? extends TagHolder> tagHolderMap) { if (tagHolder.getParent() == null) { return; } List<Attribute> attributes = tagHolder.getTag().getAttributes(); List<Attribute> inheritedAttributes = new ArrayList<Attribute>(); Set<String> parentFields = getParentAttributeNames(tagHolder, tagHolderMap); for (TagHolder parent : getParentTagHolders(namespace, tagHolder, tagHolderMap)) { for (Attribute parentAttribute : parent.getTag().getAttributes()) { Attribute inheritedAttribute = new Attribute( parentAttribute.getName(), parentAttribute.getDescription(), parentAttribute.isRequired(), parentAttribute.getType(), parentAttribute.getObjectType(), parentAttribute.getDefaultValue()); if (parentFields.contains(parentAttribute.getName())) { // When a parent already has implemented the field, don't // generate in in the component/renderer inheritedAttribute.setIgnore(true); } else { inheritedAttribute.setIgnore(parentAttribute.isIgnore()); } inheritedAttributes.add(inheritedAttribute); } } mergeAttributes(attributes, inheritedAttributes); } protected List<String> getHints(List<AnnotationValue> value) { List<String> hints = new ArrayList<String>(0); for (int i = 0; i < value.size(); i++) { hints.add("ClientBehaviorHint." + value.get(i).getValue().toString()); } return hints; } protected Collection<Attribute> parseAttributes(List<AnnotationValue> list) { List<Attribute> attributes = new ArrayList<Attribute>(list.size()); for (AnnotationValue annotationValue : list) { AnnotationMirror annotation = (AnnotationMirror) annotationValue.getValue(); Attribute attribute = new Attribute(); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : processingEnv.getElementUtils().getElementValuesWithDefaults(annotation).entrySet()) { String memberName = entry.getKey().getSimpleName().toString(); Object defaultValue = entry.getKey().getDefaultValue() .getValue(); Object value = entry.getValue().getValue(); boolean isDefault = defaultValue != null && defaultValue.equals(value); if ("name".equals(memberName) && !isDefault) { attribute.setName(entry.getValue().getValue().toString()); } else if ("required".equals(memberName) && !isDefault) { attribute.setRequired(Boolean.valueOf(entry.getValue() .getValue().toString())); } else if ("type".equals(memberName)) { attribute.setType(entry.getValue().getValue().toString()); attribute.setObjectType(getObjectType(entry.getValue().getValue().toString())); } else if ("description".equals(memberName) && !isDefault) { attribute .setDescription(getDescription((AnnotationMirror) entry .getValue().getValue())); } else if ("ignore".equals(memberName)) { attribute.setIgnore(Boolean.valueOf(entry.getValue() .getValue().toString())); } else if ("defaultValue".equals(memberName) && !isDefault) { attribute.setDefaultValue(entry.getValue().getValue() .toString()); } } attributes.add(attribute); } return attributes; } protected void mergeAttributes(List<Attribute> attributes, Collection<Attribute> inheritedAttributes) { for (Attribute inheritedAttribute : inheritedAttributes) { mergeAttributes(attributes, inheritedAttribute); } } protected void mergeAttributes(List<Attribute> attributes, Attribute inheritedAttribute) { // The attributes in the list are the most concrete ones, meaning that // these are defined in the most concrete component/renderer. // Something that is not defined in these attributes but offered by // the inherited attribute must be merged into the existing attributes boolean found = false; Attribute attribute; for (int i = 0; i < attributes.size(); i++) { attribute = attributes.get(i); if (attribute.getName().equals(inheritedAttribute.getName())) { found = true; attribute.setType(inheritedAttribute.getType()); attribute.setObjectType(inheritedAttribute.getObjectType()); attribute.setRequired(inheritedAttribute.isRequired()); attribute.setIgnore(inheritedAttribute.isIgnore()); if (attribute.getDefaultValue() == null) { attribute.setDefaultValue(inheritedAttribute.getDefaultValue()); } if (attribute.getDescription() == null) { attribute.setDescription(inheritedAttribute.getDescription()); } else { Description description = attribute.getDescription(); Description inheritedDescription = inheritedAttribute.getDescription(); if (description.getDisplayName() == null) { description.setDisplayName(inheritedDescription.getDisplayName()); } if (description.getDescription() == null) { description.setDescription(inheritedDescription.getDescription()); } if (description.getIcon() == null) { description.setIcon(inheritedDescription.getIcon()); } else { Icon icon = description.getIcon(); Icon inheritedIcon = inheritedDescription.getIcon(); if (icon.getSmallIcon() == null) { icon.setSmallIcon(inheritedIcon.getSmallIcon()); } if (icon.getLargeIcon() == null) { icon.setLargeIcon(inheritedIcon.getLargeIcon()); } } } break; } } if (!found) { attributes.add(inheritedAttribute); } } protected static String firstToLowerCase(String string) { return new StringBuilder(string.length()) .append(Character.toLowerCase(string.charAt(0))) .append(string, 1, string.length()).toString(); } protected static String getObjectType(String string) { if ("byte".equals(string)) { return "java.lang.Byte"; } else if ("short".equals(string)) { return "java.lang.Short"; } else if ("int".equals(string)) { return "java.lang.Integer"; } else if ("long".equals(string)) { return "java.lang.Long"; } else if ("float".equals(string)) { return "java.lang.Float"; } else if ("double".equals(string)) { return "java.lang.Double"; } else if ("boolean".equals(string)) { return "java.lang.Boolean"; } else if ("char".equals(string)) { return "java.lang.Character"; } return string; } }