/*
* $Id$
*
* License Agreement.
*
* Rich Faces - Natural Ajax for Java Server Faces (JSF)
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.richfaces.cdk.apt.processors;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import org.richfaces.cdk.CdkException;
import org.richfaces.cdk.annotations.Facet;
import org.richfaces.cdk.annotations.JsfComponent;
import org.richfaces.cdk.apt.SourceUtils;
import org.richfaces.cdk.apt.SourceUtils.BeanProperty;
import org.richfaces.cdk.model.ClassName;
import org.richfaces.cdk.model.ComponentLibrary;
import org.richfaces.cdk.model.ComponentModel;
import org.richfaces.cdk.model.EventModel;
import org.richfaces.cdk.model.FacesId;
import org.richfaces.cdk.model.FacetModel;
/**
* <p class="changed_added_4_0">
* That class process component-related annotations such as {@link org.richfaces.cdk.annotations.JsfComponent} or
* {@link javax.faces.component.FacesComponent} and stores information in model.
* </p>
*
* @author asmirnov@exadel.com
*
*/
public class ComponentProcessor extends ProcessorBase implements CdkAnnotationProcessor {
public static final String COMPONENT_FAMILY = "COMPONENT_FAMILY";
public static final String COMPONENT_TYPE = "COMPONENT_TYPE";
private Set<Name> processedNames = new HashSet<Name>();
public void process(Element element, ComponentLibrary library) {
SourceUtils sourceUtils = getSourceUtils();
if (sourceUtils.isAnnotationPresent(element, JsfComponent.class)) {
TypeElement componentElement = (TypeElement) element;
AnnotationMirror annotation = sourceUtils.getAnnotationMirror(componentElement, JsfComponent.class);
if (wasComponentProcessed(componentElement)) {
return;
}
// Process class-level annotations.
ComponentModel component = new ComponentModel();
// Should that component be generated ?
setClassNames(componentElement, component, annotation);
setComponentProperties(componentElement, component, annotation);
library.getComponents().add(component);
// Process the second level annotations.
for (AnnotationMirror subcomponent : sourceUtils.getAnnotationValues(annotation, "components",
AnnotationMirror.class)) {
ComponentModel subcomponentModel = new ComponentModel();
subcomponentModel.setBaseClass(component.getTargetClass());
if (!sourceUtils.isDefaultValue(subcomponent, "generate")) {
subcomponentModel.setTargetClass(sourceUtils.getAnnotationValue(subcomponent, "generate", ClassName.class));
subcomponentModel.setGenerate(true);
}
setComponentProperties(null, subcomponentModel, subcomponent);
setDescription(subcomponentModel, annotation, getDocComment(componentElement));
library.getComponents().add(subcomponentModel);
}
}
}
private boolean wasComponentProcessed(TypeElement componentElement) {
boolean wasProcessed = processedNames.contains(componentElement.getQualifiedName());
processedNames.add(componentElement.getQualifiedName());
return wasProcessed;
}
/**
* <p class="changed_added_4_0">
* process annotation and set component model properties.
* </p>
*
* @param componentElement
* @param component
* @param annotation
* @throws CdkException
*/
void setComponentProperties(TypeElement componentElement, ComponentModel component, AnnotationMirror annotation)
throws CdkException {
SourceUtils sourceUtils = getSourceUtils();
setComponentType(componentElement, component, annotation);
setComponeneFamily(componentElement, component, annotation);
setDescription(component, annotation, getDocComment(componentElement));
if (!sourceUtils.isDefaultValue(annotation, "renderer")) {
setRendererType(component, sourceUtils.getAnnotationValue(annotation, "renderer", AnnotationMirror.class));
}
processFacets(componentElement, component, annotation);
processAttributes(componentElement, component, annotation);
processEvents(componentElement, component, annotation);
for (TypeMirror atributesInterface : sourceUtils.getAnnotationValues(annotation, "interfaces", TypeMirror.class)) {
if (TypeKind.DECLARED.equals(atributesInterface.getKind())) {
processFacetsFromType(sourceUtils.asTypeElement(atributesInterface), component, sourceUtils);
} else {
// TODO - record warning.
}
}
setTagInfo(annotation, component);
// TODO - process @Test annotations.
}
private void setRendererType(ComponentModel component, AnnotationMirror annotation) {
SourceUtils sourceUtils = getSourceUtils();
if (!sourceUtils.isDefaultValue(annotation, "type")) {
component.setRendererType(sourceUtils.getAnnotationValue(annotation, "type", FacesId.class));
}
if (!sourceUtils.isDefaultValue(annotation, "template")) {
component.setRendererTemplate(sourceUtils.getAnnotationValue(annotation, "template", String.class));
}
}
void setComponentType(TypeElement componentElement, ComponentModel component, AnnotationMirror annotation) {
component.setId(FacesId.parseId(getAnnotationPropertyOrConstant(componentElement, annotation, "type", COMPONENT_TYPE)));
}
final void processFacets(TypeElement componentElement, ComponentModel component, AnnotationMirror annotation) {
SourceUtils sourceUtils = getSourceUtils();
processFacetsFromType(componentElement, component, sourceUtils);
for (AnnotationMirror facet : sourceUtils.getAnnotationValues(annotation, "facets", AnnotationMirror.class)) {
if (!sourceUtils.isDefaultValue(facet, "name")) {
String name = sourceUtils.getAnnotationValue(facet, "name", String.class);
FacetModel facetModel = component.getOrCreateFacet(name);
processFacet(facet, facetModel, null);
} else {
throw new CdkException("Facet name should be set");
}
}
}
private void processFacetsFromType(TypeElement componentElement, ComponentModel component, SourceUtils sourceUtils) {
if (null != componentElement) {
Set<BeanProperty> properties = sourceUtils.getBeanPropertiesAnnotatedWith(Facet.class, componentElement);
// TODO - encapsulate attribute builder into utility class.
for (BeanProperty beanProperty : properties) {
AnnotationMirror facet = beanProperty.getAnnotationMirror(Facet.class);
FacetModel facetModel = component.getOrCreateFacet(beanProperty.getName());
facetModel.setDescription(beanProperty.getDocComment());
processFacet(facet, facetModel, beanProperty.getDocComment());
if (!beanProperty.isExists()) {
facetModel.setGenerate(true);
}
}
}
}
final void processFacet(AnnotationMirror facet, FacetModel facetModel, String docComment) {
SourceUtils sourceUtils = getSourceUtils();
if (!sourceUtils.isDefaultValue(facet, "description")) {
setDescription(facetModel, facet, docComment);
}
if (!sourceUtils.isDefaultValue(facet, "generate")) {
facetModel.setGenerate(sourceUtils.getAnnotationValue(facet, "generate", Boolean.class));
}
}
final void setComponeneFamily(TypeElement componentElement, ComponentModel component, AnnotationMirror annotation) {
if (null != componentElement) {
component.setFamily(FacesId.parseId(getAnnotationPropertyOrConstant(componentElement, annotation, "family",
COMPONENT_FAMILY)));
}
}
final void processEvents(TypeElement componentElement, ComponentModel component, AnnotationMirror annotation) {
SourceUtils sourceUtils = getSourceUtils();
for (AnnotationMirror event : sourceUtils.getAnnotationValues(annotation, "fires", AnnotationMirror.class)) {
EventModel model = new EventModel();
sourceUtils.setModelProperty(model, event, "type", "value");
sourceUtils.setModelProperty(model, event, "listenerInterface", "listener");
sourceUtils.setModelProperty(model, event, "listenerMethod");
sourceUtils.setModelProperty(model, event, "sourceInterface", "source");
component.getEvents().add(model);
}
}
@Override
public final Class<? extends Annotation> getProcessedAnnotation() {
return JsfComponent.class;
}
}