View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.apt.processor;
21  
22  import org.apache.commons.io.IOUtils;
23  import org.apache.commons.lang.StringUtils;
24  import org.apache.myfaces.tobago.apt.AnnotationUtils;
25  import org.apache.myfaces.tobago.apt.annotation.BodyContent;
26  import org.apache.myfaces.tobago.apt.annotation.BodyContentDescription;
27  import org.apache.myfaces.tobago.apt.annotation.ExtensionTag;
28  import org.apache.myfaces.tobago.apt.annotation.Facet;
29  import org.apache.myfaces.tobago.apt.annotation.Preliminary;
30  import org.apache.myfaces.tobago.apt.annotation.SimpleTag;
31  import org.apache.myfaces.tobago.apt.annotation.Tag;
32  import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
33  import org.apache.myfaces.tobago.apt.annotation.Taglib;
34  import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
35  import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
36  import org.apache.myfaces.tobago.apt.annotation.ValidatorTag;
37  import org.apache.myfaces.tobago.apt.generate.ClassUtils;
38  import org.w3c.dom.Comment;
39  import org.w3c.dom.Document;
40  import org.w3c.dom.Element;
41  
42  import javax.annotation.processing.SupportedAnnotationTypes;
43  import javax.annotation.processing.SupportedOptions;
44  import javax.lang.model.element.ExecutableElement;
45  import javax.lang.model.element.PackageElement;
46  import javax.lang.model.element.TypeElement;
47  import javax.tools.FileObject;
48  import javax.tools.StandardLocation;
49  import javax.xml.parsers.DocumentBuilder;
50  import javax.xml.parsers.DocumentBuilderFactory;
51  import javax.xml.parsers.ParserConfigurationException;
52  import javax.xml.transform.OutputKeys;
53  import javax.xml.transform.Transformer;
54  import javax.xml.transform.TransformerException;
55  import javax.xml.transform.TransformerFactory;
56  import javax.xml.transform.dom.DOMSource;
57  import javax.xml.transform.stream.StreamResult;
58  import java.io.IOException;
59  import java.io.Writer;
60  import java.util.ArrayList;
61  import java.util.Arrays;
62  import java.util.Collections;
63  import java.util.Comparator;
64  import java.util.HashSet;
65  import java.util.List;
66  import java.util.Locale;
67  import java.util.Map;
68  import java.util.Set;
69  
70  @SupportedAnnotationTypes({
71      "org.apache.myfaces.tobago.apt.annotation.Tag",
72      "org.apache.myfaces.tobago.apt.annotation.TagAttribute",
73      "org.apache.myfaces.tobago.apt.annotation.Taglib"})
74  @SupportedOptions({
75      TaglibGenerator.JSF_VERSION,
76      TaglibGenerator.TARGET_TAGLIB})
77  public class TaglibGenerator extends AbstractGenerator {
78  
79    static final String TARGET_TAGLIB = "targetTaglib";
80  
81    private Set<String> tagSet = new HashSet<String>();
82    private Set<String> attributeSet = new HashSet<String>();
83    private String currentTag;
84  
85    private String jsfVersion;
86    private String targetTaglib;
87  
88    public void configure() {
89      final Map<String, String> options = processingEnv.getOptions();
90      jsfVersion = options.get(JSF_VERSION);
91      targetTaglib = options.get(TARGET_TAGLIB);
92  
93      info("Generating the *.tld and *.taglib.xml");
94      info("Options:");
95      info(JSF_VERSION + ": " + jsfVersion);
96      info(TARGET_TAGLIB + ": " + targetTaglib);
97    }
98  
99    public void generate()
100       throws IOException, TransformerException, ParserConfigurationException, ClassNotFoundException {
101     for (final PackageElement packageElement : getPackages()) {
102       final Taglib taglibAnnotation = packageElement.getAnnotation(Taglib.class);
103 
104       createTaglib(taglibAnnotation, packageElement, Type.JSP);
105       createTaglib(taglibAnnotation, packageElement, Type.FACELETS);
106     }
107   }
108 
109   protected void createTaglib(final Taglib taglibAnnotation, final PackageElement packageElement, final Type type)
110       throws ParserConfigurationException, ClassNotFoundException, IOException, TransformerException {
111     resetDuplicateList();
112     final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
113     dbf.setValidating(false);
114 
115     // building the XML document
116 
117     final DocumentBuilder parser = dbf.newDocumentBuilder();
118     final Document document = parser.newDocument();
119 
120     final Element taglib = type.createTaglib(document);
121     final String description = processingEnv.getElementUtils().getDocComment(packageElement);
122 
123     addComment("The next tags are commented because of MYFACES-3537. "
124         + "The application will not run with MyFaces before 2.0.14/2.1.8. "
125         + "This also affects WebSphere 8.5", taglib, document);
126     addComment("<description>" + description + "</description>", taglib, document);
127     addComment("<display-name>" + taglibAnnotation.displayName() + "</display-name>", taglib, document);
128 
129 /* XXX disabled, because of the bug explained in the comment above.
130     if (description != null) {
131       addLeafCDATAElement(description, "description", taglib, document);
132     }
133     addLeafTextElement(taglibAnnotation.displayName(), "display-name", taglib, document);
134 */
135 
136     type.addMisc(taglib, document, taglibAnnotation);
137 
138     type.addListeners(taglib, document, taglibAnnotation);
139 
140     for (final TypeElement typeElement : getTypes()) {
141       if (processingEnv.getElementUtils().getPackageOf(typeElement).equals(packageElement)) {
142         appendTag(typeElement, taglib, document, type);
143       }
144     }
145     document.appendChild(taglib);
146 
147     // writing the XML document
148 
149     Writer writer = null;
150     try {
151       final String name
152           = type.filename(targetTaglib, packageElement.getQualifiedName().toString(), taglibAnnotation.name());
153       final FileObject resource = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", name);
154       info("Writing to file: " + resource.toUri());
155       writer = resource.openWriter();
156 
157       final TransformerFactory transFactory = TransformerFactory.newInstance();
158       transFactory.setAttribute("indent-number", 2);
159       final Transformer transformer = transFactory.newTransformer();
160       transformer.setOutputProperty(OutputKeys.INDENT, "yes");
161       transformer.transform(new DOMSource(document), new StreamResult(writer));
162     } finally {
163       IOUtils.closeQuietly(writer);
164     }
165   }
166 
167   protected void appendTag(
168       final TypeElement typeElement, final Element parent, final Document document, final Type type)
169       throws ClassNotFoundException {
170     final Tag annotationTag = typeElement.getAnnotation(Tag.class);
171     if (annotationTag != null) {
172       checkDuplicates(annotationTag.name());
173       resetAttributeDuplicateList();
174       // TODO configure replacement
175       final String className;
176       if (typeElement.getAnnotation(SimpleTag.class) != null || typeElement.getAnnotation(ValidatorTag.class) != null) {
177         className = AnnotationUtils.generatedTagName(typeElement);
178       } else if (typeElement.getAnnotation(ExtensionTag.class) != null) {
179         className = typeElement.getQualifiedName().toString();
180       } else if (typeElement.getAnnotation(UIComponentTag.class) != null) {
181         className = "org.apache.myfaces.tobago.internal.taglib." + StringUtils.capitalize(annotationTag.name())
182             + "Tag";
183       } else {
184         throw new RuntimeException("Not supported: " + typeElement.getQualifiedName());
185       }
186       info("Replacing: " + typeElement.getQualifiedName() + " -> " + className);
187       final Element tag = createTag(typeElement, annotationTag, className, document, false, type);
188       addAttributes(typeElement, tag, document, type);
189       parent.appendChild(tag);
190       if (annotationTag.deprecatedName() != null && annotationTag.deprecatedName().length() > 0) {
191         final Element deprecatedTag = createTag(typeElement, annotationTag, className, document, true, type);
192         addAttributes(typeElement, deprecatedTag, document, type);
193         parent.appendChild(deprecatedTag);
194       }
195     }
196   }
197 
198   protected Element createTag(
199       final TypeElement typeElement, final Tag annotationTag, final String className, final Document document,
200       final boolean deprecated, final Type type) {
201     final Element tagElement = document.createElement("tag");
202     addDescription(typeElement, tagElement, document, deprecated);
203     type.addTagContent(typeElement, tagElement, document, deprecated, annotationTag, className);
204     return tagElement;
205   }
206 
207   private void checkAttributeDuplicates(final String attributeName) {
208     if (attributeSet.contains(attributeName)) {
209       throw new IllegalArgumentException("Attribute " + attributeName + " in tag " + currentTag + " already defined!");
210     } else {
211       attributeSet.add(attributeName);
212     }
213   }
214 
215   private void checkDuplicates(final String tagName) {
216     currentTag = tagName;
217     if (tagSet.contains(tagName)) {
218       throw new IllegalArgumentException("tag with name " + tagName + " already defined!");
219     } else {
220       tagSet.add(tagName);
221     }
222   }
223 
224   protected void addDescription(
225       final javax.lang.model.element.Element typeElement, final Element element, final Document document) {
226     addDescription(typeElement, element, document, false);
227   }
228 
229   protected void addDescription(
230       final javax.lang.model.element.Element typeElement, final Element element, final Document document,
231       final boolean deprecated) {
232     final StringBuilder description = new StringBuilder();
233     final Deprecated deprecatedAnnotation = typeElement.getAnnotation(Deprecated.class);
234     String comment = processingEnv.getElementUtils().getDocComment(typeElement);
235     final String deprecationComment = deprecationComment(comment);
236 
237     if (deprecatedAnnotation != null || deprecationComment != null) {
238       description.append("<p>**** @deprecated. Will be removed in a future version **** </p>");
239     }
240     if (deprecated) {
241       final Tag annotationTag = typeElement.getAnnotation(Tag.class);
242       description.append("<p>**** @deprecated. Will be removed in a future version. Use ");
243       description.append(annotationTag.name());
244       description.append(" instead. **** </p>");
245     }
246     if (deprecationComment != null) {
247       description.append("<p>").append(deprecationComment).append("</p>");
248     }
249 
250     final Preliminary preliminary = typeElement.getAnnotation(Preliminary.class);
251     if (preliminary != null) {
252       description.append("<p>**** Preliminary. Maybe subject to changed in a future version");
253       if (preliminary.value().length() > 0) {
254         description.append(": ");
255         description.append(preliminary.value());
256       }
257       description.append(" **** </p>");
258     }
259     if (comment != null) {
260       // remove @param section
261       final int index = comment.indexOf(" @");
262       if (index != -1) {
263         comment = comment.substring(0, index);
264       }
265       comment = comment.trim();
266       if (comment.length() > 0) {
267         //description.append("<p>");
268         description.append(comment);
269         //description.append("</p>");
270       }
271     }
272     final UIComponentTag componentTag = typeElement.getAnnotation(UIComponentTag.class);
273     if (componentTag != null) {
274       description.append(createDescription(componentTag));
275     }
276     final UIComponentTagAttribute attributeTag = typeElement.getAnnotation(UIComponentTagAttribute.class);
277     if (attributeTag != null) {
278       if (null != attributeTag.type() && attributeTag.type().length > 0) {
279         description.append("<br />Type: <code>")
280             .append(attributeTag.type().length == 1 ? attributeTag.type()[0] : Arrays.toString(attributeTag.type()))
281             .append("</code>");
282       }
283       if (StringUtils.isNotEmpty(attributeTag.defaultValue())) {
284         description.append("<br />Default: <code>")
285             .append(attributeTag.defaultValue())
286             .append("</code>");
287       }
288       if (attributeTag.allowedValues().length > 0) {
289         description.append("<br />Allowed Values: <code>")
290             .append(Arrays.toString(attributeTag.allowedValues()))
291             .append("</code>");
292       }
293     }
294     final ExtensionTag extensionTag = typeElement.getAnnotation(ExtensionTag.class);
295     if (extensionTag != null) {
296       final String baseName = extensionTag.baseClassName();
297       description.append("<p><b>Extended tag: </b>");
298       description.append(baseName);
299       description.append("</p>");
300 
301       final TypeElement declaration = getInterfaceDeclaration(baseName + "Declaration");
302       if (declaration != null) {
303         final UIComponentTag baseComponentTag = declaration.getAnnotation(UIComponentTag.class);
304         if (baseComponentTag != null) {
305           description.append(createDescription(baseComponentTag));
306         }
307       }
308     }
309     if (description.length() > 0) {
310       addLeafCDATAElement(description.toString(), "description", element, document);
311     }
312   }
313 
314   private String deprecationComment(String string) {
315     if (string == null) {
316       return null;
317     }
318     final String deprecated = "@deprecated";
319     final int begin = string.indexOf(deprecated);
320     if (begin > -1) {
321       string = string.substring(begin + deprecated.length());
322       final int end = string.indexOf("@");
323       if (end > -1) {
324         string = string.substring(0, end);
325       }
326       return string.trim();
327     } else {
328       return null;
329     }
330   }
331 
332   private TypeElement getInterfaceDeclaration(final String name) {
333     for (final TypeElement type : getTypes()) {
334       if (name.equals(type.getQualifiedName().toString())) {
335         return type;
336       }
337     }
338     return null;
339   }
340 
341   private String createDescription(final UIComponentTag componentTag) {
342     final StringBuilder description = new StringBuilder();
343     description.append("<p><b>UIComponentClass: </b>");
344     description.append(componentTag.uiComponent());
345     description.append("</p>");
346     description.append("<p><b>RendererType: </b>");
347     description.append(componentTag.rendererType());
348     description.append("</p>");
349     final Facet[] facets = componentTag.facets();
350     if (facets.length > 0) {
351       description.append("<p><b>Supported facets:</b></p>");
352       description.append("<dl>");
353       for (final Facet facet : facets) {
354         description.append("<dt><b>");
355         description.append(facet.name());
356         description.append("</b></dt>");
357         description.append("<dd>");
358         description.append(facet.description());
359         description.append("</dd>");
360       }
361       description.append("</dl>");
362     }
363     return description.toString();
364   }
365 
366   protected void addAttributes(
367       final TypeElement typeElement, final Element tagElement, final Document document, final Type type)
368       throws ClassNotFoundException {
369 
370     for (final javax.lang.model.element.Element element : getAllMembers(typeElement)) {
371       if (element instanceof ExecutableElement) {
372         final ExecutableElement executableElement = (ExecutableElement) element;
373         if (executableElement.getAnnotation(TagAttribute.class) == null
374             && executableElement.getAnnotation(UIComponentTagAttribute.class) == null) {
375           continue;
376         }
377         addAttribute(executableElement, tagElement, document, type);
378       }
379     }
380   }
381 
382   private List<? extends javax.lang.model.element.Element> getAllMembers(final TypeElement type) {
383     final List<? extends javax.lang.model.element.Element> members
384         = new ArrayList<javax.lang.model.element.Element>(processingEnv.getElementUtils().getAllMembers(type));
385     Collections.sort(members, new Comparator<javax.lang.model.element.Element>() {
386       public int compare(final javax.lang.model.element.Element d1, final javax.lang.model.element.Element d2) {
387         return d1.getSimpleName().toString().compareTo(d2.getSimpleName().toString());
388       }
389     });
390     return members;
391   }
392 
393   private void resetDuplicateList() {
394     tagSet = new HashSet<String>();
395   }
396 
397   private void resetAttributeDuplicateList() {
398     attributeSet = new HashSet<String>();
399   }
400 
401   protected void addAttribute(
402       final ExecutableElement element, final Element tagElement, final Document document, final Type type)
403       throws ClassNotFoundException {
404     final TagAttribute tagAttribute = element.getAnnotation(TagAttribute.class);
405     if (tagAttribute != null) {
406       final String simpleName = element.getSimpleName().toString();
407       if (simpleName.startsWith("set") || simpleName.startsWith("get")) {
408         final Element attribute = document.createElement("attribute");
409         String attributeName = simpleName.substring(3, 4).toLowerCase(Locale.ENGLISH) + simpleName.substring(4);
410         if (tagAttribute.name().length() > 0) {
411           attributeName = tagAttribute.name();
412         }
413         checkAttributeDuplicates(attributeName);
414         addDescription(element, attribute, document, false);
415         addLeafTextElement(attributeName, "name", attribute, document);
416 
417         addLeafTextElement(Boolean.toString(tagAttribute.required()), "required", attribute, document);
418         final UIComponentTagAttribute componentTagAttribute = element.getAnnotation(UIComponentTagAttribute.class);
419         type.addAttributeType(attribute, tagAttribute, componentTagAttribute, document, attributeName);
420         tagElement.appendChild(attribute);
421       } else {
422         throw new IllegalArgumentException("Only setter allowed found: " + simpleName);
423       }
424     }
425   }
426 
427   protected static void addComment(final String text, final org.w3c.dom.Element parent, final Document document) {
428     final Comment comment = document.createComment(text);
429     parent.appendChild(comment);
430   }
431 
432   protected static void addLeafTextElement(
433       final String text, final String node, final org.w3c.dom.Element parent, final Document document) {
434     final org.w3c.dom.Element element = document.createElement(node);
435     element.appendChild(document.createTextNode(text));
436     parent.appendChild(element);
437   }
438 
439   protected static void addLeafCDATAElement(
440       final String text, final String node, final org.w3c.dom.Element parent, final Document document) {
441     final org.w3c.dom.Element element = document.createElement(node);
442     element.appendChild(document.createCDATASection(text));
443     parent.appendChild(element);
444   }
445 
446   protected static enum Type {
447     JSP,
448     FACELETS;
449 
450     public String filename(String target, final String path, final String name) {
451       target = StringUtils.isNotBlank(target) ? target + '/' : "";
452       switch (this) {
453         case JSP:
454           return target + path.replace('.', '/') + '/' + name + ".tld";
455         case FACELETS:
456           return target + name + ".taglib.xml";
457         default:
458           throw new IllegalArgumentException("Program error");
459       }
460     }
461 
462     public Element createTaglib(final Document document) {
463       final Element taglib;
464       switch (this) {
465         case JSP:
466           taglib = document.createElement("taglib");
467           taglib.setAttribute("xmlns", "http://java.sun.com/xml/ns/javaee");
468           taglib.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
469           taglib.setAttribute("xsi:schemaLocation",
470               "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd");
471           taglib.setAttribute("version", "2.1");
472           break;
473         case FACELETS:
474           taglib = document.createElement("facelet-taglib");
475           taglib.setAttribute("xmlns", "http://java.sun.com/xml/ns/javaee");
476           taglib.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
477           taglib.setAttribute("xsi:schemaLocation",
478               "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd");
479           taglib.setAttribute("version", "2.0");
480           break;
481         default:
482           throw new IllegalArgumentException("Program error");
483       }
484       return taglib;
485     }
486 
487     public void addMisc(final Element taglib, final Document document, final Taglib taglibAnnotation) {
488       switch (this) {
489         case JSP:
490           addLeafTextElement("1.2", "tlib-version", taglib, document);
491           addLeafTextElement(taglibAnnotation.shortName(), "short-name", taglib, document);
492           addLeafTextElement(taglibAnnotation.uri(), "uri", taglib, document);
493           break;
494         case FACELETS:
495           addLeafTextElement(taglibAnnotation.uri(), "namespace", taglib, document);
496           break;
497         default:
498           throw new IllegalArgumentException("Program error");
499       }
500     }
501 
502     public void addListeners(final Element taglib, final Document document, final Taglib taglibAnnotation) {
503       switch (this) {
504         case JSP:
505           for (final String listenerClass : taglibAnnotation.listener()) {
506             final Element listener = document.createElement("listener");
507             addLeafTextElement(listenerClass, "listener-class", listener, document);
508             taglib.appendChild(listener);
509           }
510           break;
511         case FACELETS:
512           break;
513         default:
514           throw new IllegalArgumentException("Program error");
515       }
516     }
517 
518     public void addTagContent(
519         final TypeElement typeElement, final Element tagElement, final Document document, final boolean deprecated,
520         final Tag annotationTag, final String className) {
521       switch (this) {
522         case JSP:
523           if (deprecated) {
524             addLeafTextElement(annotationTag.deprecatedName(), "name", tagElement, document);
525           } else {
526             addLeafTextElement(annotationTag.name(), "name", tagElement, document);
527           }
528           addLeafTextElement(className, "tag-class", tagElement, document);
529           final String tagExtraInfo = annotationTag.tagExtraInfoClassName();
530           if (tagExtraInfo != null && tagExtraInfo.length() > 0) {
531             // TODO check tagExtraInfo extends TagExtraInfo
532             addLeafTextElement(tagExtraInfo, "tei-class", tagElement, document);
533           }
534           final BodyContent bodyContent = annotationTag.bodyContent();
535           final BodyContentDescription contentDescription = typeElement.getAnnotation(BodyContentDescription.class);
536           // TODO more error checking
537           if (contentDescription != null) {
538             if (bodyContent.equals(BodyContent.JSP) && contentDescription.contentType().length() > 0) {
539               throw new IllegalArgumentException(
540                   "contentType " + contentDescription.contentType() + " for bodyContent JSP not allowed!");
541             } else if (bodyContent.equals(BodyContent.TAGDEPENDENT) && contentDescription.contentType().length() == 0) {
542               throw new IllegalArgumentException("contentType should set for tagdependent bodyContent");
543             }
544           }
545           addLeafTextElement(bodyContent.toString(), "body-content", tagElement, document);
546           break;
547         case FACELETS:
548           if (deprecated) {
549             addLeafTextElement(annotationTag.deprecatedName(), "tag-name", tagElement, document);
550           } else {
551             addLeafTextElement(annotationTag.name(), "tag-name", tagElement, document);
552           }
553 
554           final UIComponentTag componentTag = typeElement.getAnnotation(UIComponentTag.class);
555           if (componentTag != null) {
556             final Element componentElement = document.createElement("component");
557             tagElement.appendChild(componentElement);
558             addLeafTextElement(
559                 AnnotationUtils.componentType(componentTag), "component-type", componentElement, document);
560             if (StringUtils.isNotBlank(componentTag.rendererType())) {
561               addLeafTextElement(componentTag.rendererType(), "renderer-type", componentElement, document);
562             }
563             addLeafTextElement(componentTag.faceletHandler(), "handler-class", componentElement, document);
564           }
565 
566           final ExtensionTag extensionTag = typeElement.getAnnotation(ExtensionTag.class);
567           if (extensionTag != null) {
568             final Element componentElement = document.createElement("component");
569             tagElement.appendChild(componentElement);
570             addLeafTextElement(extensionTag.componentType(), "component-type", componentElement, document);
571             addLeafTextElement(extensionTag.rendererType(), "renderer-type", componentElement, document);
572             addLeafTextElement(extensionTag.faceletHandler(), "handler-class", componentElement, document);
573           }
574 
575           final SimpleTag simpleTag = typeElement.getAnnotation(SimpleTag.class);
576           if (simpleTag != null) {
577             addLeafTextElement(simpleTag.faceletHandler(), "handler-class", tagElement, document);
578           }
579 
580           final ValidatorTag validatorTag = typeElement.getAnnotation(ValidatorTag.class);
581           if (validatorTag != null) {
582             final Element validatorElement = document.createElement("validator");
583             tagElement.appendChild(validatorElement);
584             addLeafTextElement(validatorTag.validatorId(), "validator-id", validatorElement, document);
585             if (StringUtils.isNotBlank(validatorTag.faceletHandler())) {
586               addLeafTextElement(validatorTag.faceletHandler(), "handler-class", validatorElement, document);
587             }
588           }
589           break;
590         default:
591           throw new IllegalArgumentException("Program error");
592       }
593     }
594 
595     public void addAttributeType(
596         final Element attribute, final TagAttribute tagAttribute, final UIComponentTagAttribute componentTagAttribute,
597         final Document document, final String attributeName) {
598       switch (this) {
599         case JSP:
600           if (!tagAttribute.rtexprvalue()) {
601             if (componentTagAttribute != null) {
602               if (componentTagAttribute.expression().isMethodExpression()) {
603                 final Element deferredMethod = document.createElement("deferred-method");
604                 addLeafTextElement(
605                     componentTagAttribute.methodReturnType() + " " + attributeName + "("
606                         + StringUtils.join(componentTagAttribute.methodSignature(), ", ")
607                         + ")", "method-signature", deferredMethod, document);
608                 attribute.appendChild(deferredMethod);
609               } else if (componentTagAttribute.expression().isValueExpression()) {
610                 final Element deferredValue = document.createElement("deferred-value");
611                 String clazz;
612                 if (componentTagAttribute.type().length == 1
613                     // XXX This is because an enum will not be converted in JSP with the PropertyEditor
614                     && !"org.apache.myfaces.tobago.layout.TextAlign".equals(componentTagAttribute.type()[0])
615                     && !"org.apache.myfaces.tobago.model.SuggestFilter".equals(componentTagAttribute.type()[0])) {
616                   clazz = componentTagAttribute.type()[0];
617                   final Class wrapper = ClassUtils.getWrapper(clazz);
618                   if (wrapper != null) {
619                     clazz = wrapper.getName(); // primitive types aren't allowed here
620       /*                } else {
621                       XXX what is with inner classes and arrays?
622                       if (clazz.endsWith("[]")) {
623                         Class.forName(clazz.substring(0, clazz.length() - 2)); // type check
624                       } else {
625                         Class.forName(clazz); // type check
626                       }
627       */
628                   }
629                 } else {
630                   clazz = "java.lang.Object";
631                 }
632                 addLeafTextElement(clazz, "type", deferredValue, document);
633                 attribute.appendChild(deferredValue);
634               }
635             } else {
636               final Element deferredValue = document.createElement("deferred-value");
637               addLeafTextElement(tagAttribute.type(), "type", deferredValue, document);
638               attribute.appendChild(deferredValue);
639             }
640           }
641           if (tagAttribute.rtexprvalue()) {
642             addLeafTextElement(Boolean.toString(tagAttribute.rtexprvalue()), "rtexprvalue", attribute, document);
643           }
644           break;
645         case FACELETS:
646           if (!tagAttribute.rtexprvalue()) {
647             if (componentTagAttribute != null) {
648               if (componentTagAttribute.expression().isMethodExpression()) {
649                 // todo
650               } else if (componentTagAttribute.expression().isValueExpression()) {
651                 String clazz;
652                 if (componentTagAttribute.type().length == 1) {
653                   clazz = componentTagAttribute.type()[0];
654                   final Class wrapper = ClassUtils.getWrapper(clazz);
655                   if (wrapper != null) {
656                     clazz = wrapper.getName(); // primitive types aren't allowed here
657       /*                } else {
658                       XXX what is with inner classes and arrays?
659                       if (clazz.endsWith("[]")) {
660                         Class.forName(clazz.substring(0, clazz.length() - 2)); // type check
661                       } else {
662                         Class.forName(clazz); // type check
663                       }
664       */
665                   }
666                 } else {
667                   clazz = "java.lang.Object";
668                 }
669                 addLeafTextElement(clazz, "type", attribute, document);
670               }
671             } else {
672               addLeafTextElement(tagAttribute.type(), "type", attribute, document);
673             }
674           }
675           break;
676         default:
677           throw new IllegalArgumentException("Program error");
678       }
679 
680     }
681   }
682 }