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