1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.apt.processor;
21
22 import org.antlr.stringtemplate.StringTemplate;
23 import org.antlr.stringtemplate.StringTemplateGroup;
24 import org.apache.myfaces.tobago.apt.annotation.Behavior;
25 import org.apache.myfaces.tobago.apt.annotation.DynamicExpression;
26 import org.apache.myfaces.tobago.apt.annotation.Tag;
27 import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
28 import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
29 import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
30 import org.apache.myfaces.tobago.apt.generate.ClassInfo;
31 import org.apache.myfaces.tobago.apt.generate.ComponentInfo;
32 import org.apache.myfaces.tobago.apt.generate.ComponentPropertyInfo;
33 import org.apache.myfaces.tobago.apt.generate.PropertyInfo;
34
35 import javax.annotation.processing.SupportedAnnotationTypes;
36 import javax.annotation.processing.SupportedSourceVersion;
37 import javax.faces.component.UIComponent;
38 import javax.lang.model.SourceVersion;
39 import javax.lang.model.element.Element;
40 import javax.lang.model.element.ExecutableElement;
41 import javax.lang.model.element.TypeElement;
42 import javax.lang.model.type.TypeKind;
43 import javax.lang.model.type.TypeMirror;
44 import javax.tools.FileObject;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.io.InputStreamReader;
48 import java.io.Reader;
49 import java.io.Writer;
50 import java.lang.reflect.Method;
51 import java.lang.reflect.Modifier;
52 import java.util.HashMap;
53 import java.util.HashSet;
54 import java.util.List;
55 import java.util.Locale;
56 import java.util.Map;
57 import java.util.Set;
58
59 @SupportedSourceVersion(SourceVersion.RELEASE_8)
60 @SupportedAnnotationTypes({
61 "org.apache.myfaces.tobago.apt.annotation.Tag",
62 "org.apache.myfaces.tobago.apt.annotation.TagAttribute",
63 "org.apache.myfaces.tobago.apt.annotation.UIComponentTag",
64 "org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute",
65 "org.apache.myfaces.tobago.apt.annotation.Taglib",
66 "org.apache.myfaces.tobago.apt.annotation.SimpleTag"})
67 public class ClassesGenerator extends AbstractGenerator {
68
69 private StringTemplateGroup componentStringTemplateGroup;
70 private Set<String> ignoredProperties;
71
72 @Override
73 public void configure() {
74
75 info("Generating the classes *Component");
76
77 final InputStream componentStream
78 = getClass().getClassLoader().getResourceAsStream("org/apache/myfaces/tobago/apt/component.stg");
79 final Reader componentReader = new InputStreamReader(componentStream);
80 componentStringTemplateGroup = new StringTemplateGroup(componentReader);
81
82 ignoredProperties = new HashSet<>();
83 ignoredProperties.add("id");
84 ignoredProperties.add("rendered");
85 ignoredProperties.add("binding");
86 }
87
88 @Override
89 public void generate() {
90 for (final TypeElement element : getTypes()) {
91 if (element.getAnnotation(UIComponentTag.class) != null) {
92 try {
93 createTagOrComponent(element);
94 } catch (final IOException | ClassNotFoundException | RuntimeException e) {
95 throw new TobagoGeneratorException(
96 "Error during processing of " + element.getAnnotation(UIComponentTag.class).uiComponent(), e);
97 }
98 }
99 }
100 }
101
102 private void createTagOrComponent(final TypeElement declaration) throws IOException, ClassNotFoundException {
103 final UIComponentTag componentTag = declaration.getAnnotation(UIComponentTag.class);
104 final Map<String, PropertyInfo> properties = new HashMap<>();
105 addProperties(declaration, properties);
106
107 if (componentTag.generate()) {
108 final Tag tag = declaration.getAnnotation(Tag.class);
109 final String generic = "org.apache.myfaces.tobago.internal.component.AbstractUI"
110 + tag.name().substring(0, 1).toUpperCase() + tag.name().substring(1);
111 final StringTemplate componentStringTemplate = componentStringTemplateGroup.getInstanceOf("component");
112 final ComponentInfo componentInfo = new ComponentInfo(declaration, componentTag);
113 String componentBaseClass = componentTag.uiComponentBaseClass();
114 if ("".equals(componentBaseClass)) {
115 componentBaseClass = generic;
116 }
117 componentInfo.setSuperClass(componentBaseClass);
118
119
120 if (!componentBaseClass.equals(generic)) {
121 warn("**********************************************************************************");
122 warn("generic name is unequal to the defined name: " + componentBaseClass + " != " + generic);
123 warn("**********************************************************************************");
124 }
125
126 componentInfo.setDescription(getDescription(declaration));
127 componentInfo.setDeprecated(declaration.getAnnotation(Deprecated.class) != null);
128 for (final String interfaces : componentTag.interfaces()) {
129 componentInfo.addInterface(interfaces);
130 }
131
132 if (componentTag.behaviors().length > 0) {
133 for (final Behavior behavior : componentTag.behaviors()) {
134
135
136
137 componentInfo.getBehaviors().add(behavior.name());
138 if (behavior.isDefault()) {
139 if (componentInfo.getDefaultBehavior() != null) {
140 throw new TobagoGeneratorException("defaultBehavior '" + componentInfo.getDefaultBehavior()
141 + "' will be overwritten with '" + behavior.name()
142 + "' in component '" + componentInfo.getSourceClass() + "'");
143 }
144 componentInfo.setDefaultBehavior(behavior.name());
145 }
146 }
147 if (componentInfo.getDefaultBehavior() == null) {
148 throw new TobagoGeneratorException(
149 "defaultBehavior not set in component '" + componentInfo.getSourceClass() + "'");
150 }
151 }
152
153 final Class<? extends UIComponent> facesClass
154 = Class.forName(componentTag.uiComponentFacesClass()).asSubclass(UIComponent.class);
155
156 for (final PropertyInfo info : properties.values()) {
157 final String infoType = info.getType();
158 final String methodName
159 = ((infoType.equals("java.lang.Boolean") || infoType.equals("boolean")) ? "is" : "get")
160 + info.getUpperCamelCaseName();
161
162 boolean generate = info.isGenerate();
163
164 try {
165 final Method method = facesClass.getMethod(methodName);
166 if (!Modifier.isAbstract(method.getModifiers())) {
167 generate = false;
168 }
169 } catch (final NoSuchMethodException e) {
170
171
172 }
173
174
175
176
177 if (generate) {
178 addPropertyToComponent(componentInfo, info);
179 }
180
181 }
182
183 componentStringTemplate.setAttribute("componentInfo", componentInfo);
184 writeFile(componentInfo, componentStringTemplate);
185 }
186 }
187
188 private ComponentPropertyInfo addPropertyToComponent(final ComponentInfo componentInfo, final PropertyInfo info) {
189
190 final ComponentPropertyInfo componentPropertyInfo = (ComponentPropertyInfo) info.fill(new ComponentPropertyInfo());
191 componentInfo.addImport(componentPropertyInfo.getUnmodifiedType());
192 componentInfo.addImport("javax.faces.context.FacesContext");
193
194
195
196 if ("requiredMessage".equals(info.getName())) {
197 componentInfo.setMessages(true);
198 }
199 componentInfo.addPropertyInfo(componentPropertyInfo);
200 return componentPropertyInfo;
201 }
202
203 protected void addProperties(final TypeElement type, final Map<String, PropertyInfo> properties) {
204
205 addProperties(type.getInterfaces(), properties);
206 addProperties(type.getSuperclass(), properties);
207
208 final List<? extends Element> members = processingEnv.getElementUtils().getAllMembers(type);
209 for (final Element member : members) {
210 if (member instanceof ExecutableElement) {
211 final ExecutableElement executableElement = (ExecutableElement) member;
212 addProperty(executableElement, properties);
213 }
214 }
215 }
216
217 protected void addProperties(
218 final List<? extends TypeMirror> interfaces, final Map<String, PropertyInfo> properties) {
219
220 for (final TypeMirror typeMirror : interfaces) {
221 addProperties(typeMirror, properties);
222 }
223 }
224
225 protected void addProperties(final TypeMirror typeMirror, final Map<String, PropertyInfo> properties) {
226
227 if (typeMirror.getKind() != TypeKind.NONE) {
228 addProperties((TypeElement) (processingEnv.getTypeUtils().asElement(typeMirror)), properties);
229 }
230 }
231
232 protected void addProperty(final ExecutableElement declaration, final Map<String, PropertyInfo> properties) {
233 final TagAttribute tagAttribute = declaration.getAnnotation(TagAttribute.class);
234 final UIComponentTagAttribute uiComponentTagAttribute = declaration.getAnnotation(UIComponentTagAttribute.class);
235 if (uiComponentTagAttribute != null) {
236 final String simpleName = declaration.getSimpleName().toString();
237 if (simpleName.startsWith("set") || simpleName.startsWith("get")) {
238 final String name = simpleName.substring(3, 4).toLowerCase(Locale.ENGLISH) + simpleName.substring(4);
239
240 if (ignoredProperties.contains(name)) {
241
242 return;
243 }
244 final PropertyInfo propertyInfo = new PropertyInfo(name);
245 propertyInfo.setAllowedValues(uiComponentTagAttribute.allowedValues());
246 if (tagAttribute != null) {
247 propertyInfo.setBodyContent(tagAttribute.bodyContent());
248 propertyInfo.setTagAttribute(true);
249 }
250 final String type;
251 if (uiComponentTagAttribute.expression().isMethodExpression()) {
252 propertyInfo.setMethodExpressionRequired(true);
253 type = "javax.el.MethodExpression";
254 } else {
255 if (uiComponentTagAttribute.expression() == DynamicExpression.VALUE_EXPRESSION_REQUIRED) {
256 propertyInfo.setValueExpressionRequired(true);
257 } else if (uiComponentTagAttribute.expression() == DynamicExpression.PROHIBITED) {
258 propertyInfo.setLiteralOnly(true);
259 }
260
261 if (uiComponentTagAttribute.type().length > 1) {
262 type = "java.lang.Object";
263 } else {
264 type = uiComponentTagAttribute.type()[0];
265 }
266 }
267 propertyInfo.setType(type);
268 propertyInfo.setDefaultValue(
269 uiComponentTagAttribute.defaultValue().length() > 0 ? uiComponentTagAttribute.defaultValue() : null);
270 propertyInfo.setDefaultCode(
271 uiComponentTagAttribute.defaultCode().length() > 0 ? uiComponentTagAttribute.defaultCode() : null);
272 propertyInfo.setMethodSignature(uiComponentTagAttribute.methodSignature());
273 propertyInfo.setDeprecated(declaration.getAnnotation(Deprecated.class) != null);
274 propertyInfo.setDescription(getDescription(declaration));
275 propertyInfo.setTransient(uiComponentTagAttribute.isTransient());
276 propertyInfo.setGenerate(uiComponentTagAttribute.generate());
277
278
279
280 properties.put(name, propertyInfo);
281 }
282 }
283 }
284
285 private String getDescription(final Element element) {
286 String comment = processingEnv.getElementUtils().getDocComment(element);
287 if (comment != null) {
288 final int index = comment.indexOf('@');
289 if (index != -1) {
290 comment = comment.substring(0, index);
291 }
292 comment = comment.trim();
293 if (comment.length() > 0) {
294 return comment;
295 }
296 }
297 return null;
298 }
299
300 private void writeFile(final ClassInfo info, final StringTemplate stringTemplate) throws IOException {
301 final FileObject resource = processingEnv.getFiler().createSourceFile(
302 info.getPackageName() + '.' + info.getClassName());
303 info("Writing to file: " + resource.toUri());
304 try (Writer writer = resource.openWriter()) {
305 writer.append(stringTemplate.toString());
306 }
307 }
308 }