1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.view.facelets.compiler;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.lang.reflect.Method;
24 import java.net.URL;
25 import java.net.URLConnection;
26 import java.util.Collection;
27 import java.util.logging.Level;
28 import java.util.logging.Logger;
29 import java.util.regex.Pattern;
30
31 import javax.faces.FacesException;
32 import javax.faces.application.Resource;
33 import javax.faces.application.ResourceHandler;
34 import javax.faces.application.ViewHandler;
35 import javax.faces.context.ExternalContext;
36 import javax.faces.context.FacesContext;
37 import javax.faces.view.facelets.ComponentConfig;
38 import javax.faces.view.facelets.FaceletHandler;
39 import javax.faces.view.facelets.Tag;
40 import javax.faces.view.facelets.TagConfig;
41 import javax.faces.view.facelets.TagHandler;
42 import javax.xml.parsers.ParserConfigurationException;
43 import javax.xml.parsers.SAXParser;
44 import javax.xml.parsers.SAXParserFactory;
45
46 import org.apache.myfaces.config.ConfigFilesXmlValidationUtils;
47 import org.apache.myfaces.shared.config.MyfacesConfig;
48 import org.apache.myfaces.shared.util.ArrayUtils;
49 import org.apache.myfaces.shared.util.ClassUtils;
50 import org.apache.myfaces.shared.util.StringUtils;
51 import org.apache.myfaces.shared.util.WebConfigParamUtils;
52 import org.apache.myfaces.spi.FaceletConfigResourceProvider;
53 import org.apache.myfaces.spi.FaceletConfigResourceProviderFactory;
54 import org.apache.myfaces.view.facelets.tag.AbstractTagLibrary;
55 import org.apache.myfaces.view.facelets.tag.TagLibrary;
56 import org.apache.myfaces.view.facelets.tag.composite.CompositeComponentResourceTagHandler;
57 import org.apache.myfaces.view.facelets.tag.composite.CompositeResouceWrapper;
58 import org.apache.myfaces.view.facelets.util.ParameterCheck;
59 import org.apache.myfaces.view.facelets.util.ReflectionUtil;
60 import org.xml.sax.Attributes;
61 import org.xml.sax.InputSource;
62 import org.xml.sax.Locator;
63 import org.xml.sax.SAXException;
64 import org.xml.sax.SAXParseException;
65 import org.xml.sax.XMLReader;
66 import org.xml.sax.helpers.DefaultHandler;
67
68
69
70
71
72
73
74
75 public final class TagLibraryConfig
76 {
77
78
79
80
81 protected final static Logger log = Logger.getLogger(TagLibraryConfig.class.getName());
82
83 private static class TagLibraryImpl extends AbstractTagLibrary
84 {
85 private String _compositeLibraryName;
86
87 private final ResourceHandler _resourceHandler;
88 private Pattern _acceptPatterns;
89 private String _extension;
90 private String[] _defaultSuffixesArray;
91
92 public TagLibraryImpl(FacesContext facesContext, String namespace)
93 {
94 super(namespace);
95 _compositeLibraryName = null;
96 _resourceHandler = facesContext.getApplication().getResourceHandler();
97 ExternalContext externalContext = facesContext.getExternalContext();
98
99 _acceptPatterns = loadAcceptPattern(externalContext);
100
101 _extension = loadFaceletExtension(externalContext);
102
103 String defaultSuffixes = WebConfigParamUtils.getStringInitParameter(externalContext,
104 ViewHandler.DEFAULT_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_SUFFIX );
105
106 _defaultSuffixesArray = StringUtils.splitShortString(defaultSuffixes, ' ');
107
108 boolean faceletsExtensionFound = false;
109 for (String ext : _defaultSuffixesArray)
110 {
111 if (_extension.equals(ext))
112 {
113 faceletsExtensionFound = true;
114 break;
115 }
116 }
117 if (!faceletsExtensionFound)
118 {
119 _defaultSuffixesArray = (String[]) ArrayUtils.concat(_defaultSuffixesArray, new String[]{_extension});
120 }
121 }
122
123
124
125
126
127
128
129
130
131 private Pattern loadAcceptPattern(ExternalContext context)
132 {
133 assert context != null;
134
135 String mappings = context.getInitParameter(ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME);
136 if (mappings == null)
137 {
138 return null;
139 }
140
141
142 mappings = mappings.trim();
143 if (mappings.length() == 0)
144 {
145 return null;
146 }
147
148 return Pattern.compile(toRegex(mappings));
149 }
150
151 private String loadFaceletExtension(ExternalContext context)
152 {
153 assert context != null;
154
155 String suffix = context.getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
156 if (suffix == null)
157 {
158 suffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
159 }
160 else
161 {
162 suffix = suffix.trim();
163 if (suffix.length() == 0)
164 {
165 suffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
166 }
167 }
168
169 return suffix;
170 }
171
172
173
174
175
176
177
178
179
180 private String toRegex(String mappings)
181 {
182 assert mappings != null;
183
184
185 mappings = mappings.replaceAll("\\s", "");
186
187
188 mappings = mappings.replaceAll("\\.", "\\\\.");
189
190
191 mappings = mappings.replaceAll("\\*", ".*");
192
193
194 mappings = mappings.replaceAll(";", "|");
195
196 return mappings;
197 }
198
199 public boolean handles(String resourceName)
200 {
201 if (resourceName == null)
202 {
203 return false;
204 }
205
206 if (resourceName.endsWith(_extension))
207 {
208
209 return true;
210 }
211
212
213 return _acceptPatterns != null && _acceptPatterns.matcher(resourceName).matches();
214 }
215
216 @Override
217 public boolean containsTagHandler(String ns, String localName)
218 {
219 boolean result = super.containsTagHandler(ns, localName);
220
221 if (!result && _compositeLibraryName != null && containsNamespace(ns))
222 {
223 for (String defaultSuffix : _defaultSuffixesArray)
224 {
225 String resourceName = localName + defaultSuffix;
226 if (handles(resourceName))
227 {
228 Resource compositeComponentResource = _resourceHandler.createResource(
229 resourceName, _compositeLibraryName);
230
231 if (compositeComponentResource != null)
232 {
233 URL url = compositeComponentResource.getURL();
234 return (url != null);
235 }
236 }
237 }
238 }
239 return result;
240 }
241
242 @Override
243 public TagHandler createTagHandler(String ns, String localName,
244 TagConfig tag) throws FacesException
245 {
246 TagHandler tagHandler = super.createTagHandler(ns, localName, tag);
247
248 if (tagHandler == null && _compositeLibraryName != null && containsNamespace(ns))
249 {
250 for (String defaultSuffix : _defaultSuffixesArray)
251 {
252 String resourceName = localName + defaultSuffix;
253 if (handles(resourceName))
254 {
255
256
257
258
259
260
261
262
263 Resource compositeComponentResource = new CompositeResouceWrapper(
264 _resourceHandler.createResource(resourceName, _compositeLibraryName));
265
266 if (compositeComponentResource != null)
267 {
268 ComponentConfig componentConfig = new ComponentConfigWrapper(tag,
269 "javax.faces.NamingContainer", null);
270
271 return new CompositeComponentResourceTagHandler(
272 componentConfig, compositeComponentResource);
273 }
274 }
275 }
276 }
277 return tagHandler;
278 }
279
280 public void setCompositeLibrary(String compositeLibraryName)
281 {
282 _compositeLibraryName = compositeLibraryName;
283 }
284
285 public void putConverter(String name, String id)
286 {
287 ParameterCheck.notNull("name", name);
288 ParameterCheck.notNull("id", id);
289 this.addConverter(name, id);
290 }
291
292 public void putConverter(String name, String id, Class<? extends TagHandler> handlerClass)
293 {
294 ParameterCheck.notNull("name", name);
295 ParameterCheck.notNull("id", id);
296 ParameterCheck.notNull("handlerClass", handlerClass);
297 this.addConverter(name, id, handlerClass);
298 }
299
300 public void putValidator(String name, String id)
301 {
302 ParameterCheck.notNull("name", name);
303 ParameterCheck.notNull("id", id);
304 this.addValidator(name, id);
305 }
306
307 public void putValidator(String name, String id, Class<? extends TagHandler> handlerClass)
308 {
309 ParameterCheck.notNull("name", name);
310 ParameterCheck.notNull("id", id);
311 ParameterCheck.notNull("handlerClass", handlerClass);
312 this.addValidator(name, id, handlerClass);
313 }
314
315 public void putTagHandler(String name, Class<? extends TagHandler> type)
316 {
317 ParameterCheck.notNull("name", name);
318 ParameterCheck.notNull("type", type);
319 this.addTagHandler(name, type);
320 }
321
322 public void putComponent(String name, String componentType, String rendererType)
323 {
324 ParameterCheck.notNull("name", name);
325 ParameterCheck.notNull("componentType", componentType);
326 this.addComponent(name, componentType, rendererType);
327 }
328
329 public void putComponent(String name, String componentType, String rendererType,
330 Class<? extends TagHandler> handlerClass)
331 {
332 ParameterCheck.notNull("name", name);
333 ParameterCheck.notNull("componentType", componentType);
334 ParameterCheck.notNull("handlerClass", handlerClass);
335 this.addComponent(name, componentType, rendererType, handlerClass);
336 }
337
338 public void putUserTag(String name, URL source)
339 {
340 ParameterCheck.notNull("name", name);
341 ParameterCheck.notNull("source", source);
342 this.addUserTag(name, source);
343 }
344
345 public void putFunction(String name, Method method)
346 {
347 ParameterCheck.notNull("name", name);
348 ParameterCheck.notNull("method", method);
349 this.addFunction(name, method);
350 }
351
352 public void putBehavior(String name, String id)
353 {
354 ParameterCheck.notNull("name", name);
355 ParameterCheck.notNull("id", id);
356 this.addBehavior(name, id);
357 }
358
359 public void putBehavior(String name, String id, Class<? extends TagHandler> handlerClass)
360 {
361 ParameterCheck.notNull("name", name);
362 ParameterCheck.notNull("id", id);
363 ParameterCheck.notNull("handlerClass", handlerClass);
364 this.addBehavior(name, id, handlerClass);
365 }
366 }
367
368 private static class ComponentConfigWrapper implements ComponentConfig
369 {
370
371 protected final TagConfig parent;
372
373 protected final String componentType;
374
375 protected final String rendererType;
376
377 public ComponentConfigWrapper(TagConfig parent, String componentType,
378 String rendererType)
379 {
380 this.parent = parent;
381 this.componentType = componentType;
382 this.rendererType = rendererType;
383 }
384
385 public String getComponentType()
386 {
387 return this.componentType;
388 }
389
390 public String getRendererType()
391 {
392 return this.rendererType;
393 }
394
395 public FaceletHandler getNextHandler()
396 {
397 return this.parent.getNextHandler();
398 }
399
400 public Tag getTag()
401 {
402 return this.parent.getTag();
403 }
404
405 public String getTagId()
406 {
407 return this.parent.getTagId();
408 }
409 }
410
411 private static class LibraryHandler extends DefaultHandler
412 {
413 private final URL source;
414
415 private final FacesContext facesContext;
416
417 private TagLibrary library;
418
419 private final StringBuffer buffer;
420
421 private Locator locator;
422
423 private String tagName;
424
425 private String converterId;
426
427 private String validatorId;
428
429 private String behaviorId;
430
431 private String componentType;
432
433 private String rendererType;
434
435 private String functionName;
436
437 private Class<? extends TagHandler> handlerClass;
438
439 private Class<?> functionClass;
440
441 private String functionSignature;
442
443 private String compositeLibraryName;
444
445 public LibraryHandler(FacesContext facesContext, URL source)
446 {
447 this.source = source;
448 this.buffer = new StringBuffer(64);
449 this.facesContext = facesContext;
450 }
451
452 public TagLibrary getLibrary()
453 {
454 return this.library;
455 }
456
457 public void endElement(String uri, String localName, String qName) throws SAXException
458 {
459 try
460 {
461 if ("facelet-taglib".equals(qName))
462 {
463
464 }
465 else if ("library-class".equals(qName))
466 {
467 this.processLibraryClass();
468 }
469 else if ("namespace".equals(qName))
470 {
471 this.library = new TagLibraryImpl(facesContext, this.captureBuffer());
472 if (this.compositeLibraryName != null)
473 {
474 ((TagLibraryImpl)this.library).setCompositeLibrary(compositeLibraryName);
475 }
476 }
477 else if ("composite-library-name".equals(qName))
478 {
479 this.compositeLibraryName = this.captureBuffer();
480 if (this.library != null)
481 {
482 ((TagLibraryImpl)this.library).setCompositeLibrary(compositeLibraryName);
483 }
484 }
485 else if ("component-type".equals(qName))
486 {
487 this.componentType = this.captureBuffer();
488 }
489 else if ("renderer-type".equals(qName))
490 {
491 this.rendererType = this.captureBuffer();
492 }
493 else if ("tag-name".equals(qName))
494 {
495 this.tagName = this.captureBuffer();
496 }
497 else if ("function-name".equals(qName))
498 {
499 this.functionName = this.captureBuffer();
500 }
501 else if ("function-class".equals(qName))
502 {
503 String className = this.captureBuffer();
504 this.functionClass = createClass(Object.class, className);
505 }
506 else if ("description".equals(qName))
507 {
508
509 }
510 else if ("display-name".equals(qName))
511 {
512
513 }
514 else if ("icon".equals(qName))
515 {
516
517 }
518 else
519 {
520
521
522
523 if (this.library == null)
524 {
525 throw new IllegalStateException("No <namespace> element");
526 }
527
528 TagLibraryImpl impl = (TagLibraryImpl) this.library;
529
530 if ("tag".equals(qName))
531 {
532 if (this.handlerClass != null)
533 {
534 impl.putTagHandler(this.tagName, this.handlerClass);
535 }
536 }
537 else if ("handler-class".equals(qName))
538 {
539 String cName = this.captureBuffer();
540 this.handlerClass = createClass(TagHandler.class, cName);
541 }
542 else if ("component".equals(qName))
543 {
544 if (this.handlerClass != null)
545 {
546 impl.putComponent(this.tagName, this.componentType, this.rendererType, this.handlerClass);
547 this.handlerClass = null;
548 }
549 else
550 {
551 impl.putComponent(this.tagName, this.componentType, this.rendererType);
552 }
553 }
554 else if ("converter-id".equals(qName))
555 {
556 this.converterId = this.captureBuffer();
557 }
558 else if ("converter".equals(qName))
559 {
560 if (this.handlerClass != null)
561 {
562 impl.putConverter(this.tagName, this.converterId, handlerClass);
563 this.handlerClass = null;
564 }
565 else
566 {
567 impl.putConverter(this.tagName, this.converterId);
568 }
569 this.converterId = null;
570 }
571 else if ("validator-id".equals(qName))
572 {
573 this.validatorId = this.captureBuffer();
574 }
575 else if ("validator".equals(qName))
576 {
577 if (this.handlerClass != null)
578 {
579 impl.putValidator(this.tagName, this.validatorId, handlerClass);
580 this.handlerClass = null;
581 }
582 else
583 {
584 impl.putValidator(this.tagName, this.validatorId);
585 }
586 this.validatorId = null;
587 }
588 else if ("behavior-id".equals(qName))
589 {
590 this.behaviorId = this.captureBuffer();
591 }
592 else if ("behavior".equals(qName))
593 {
594 if (this.handlerClass != null)
595 {
596 impl.putBehavior(this.tagName, this.behaviorId, handlerClass);
597 this.handlerClass = null;
598 }
599 else
600 {
601 impl.putBehavior(this.tagName, this.behaviorId);
602 }
603 this.behaviorId = null;
604 }
605 else if ("source".equals(qName))
606 {
607 String path = this.captureBuffer();
608 URL url = new URL(this.source, path);
609 impl.putUserTag(this.tagName, url);
610 }
611 else if ("function-signature".equals(qName))
612 {
613 this.functionSignature = this.captureBuffer();
614 Method m = createMethod(this.functionClass, this.functionSignature);
615 impl.putFunction(this.functionName, m);
616 }
617 }
618 }
619 catch (Exception e)
620 {
621 SAXException saxe = new SAXException("Error Handling [" + this.source + "@"
622 + this.locator.getLineNumber() + "," + this.locator.getColumnNumber() + "] <" + qName + ">");
623 saxe.initCause(e);
624 throw saxe;
625 }
626 }
627
628 private String captureBuffer() throws Exception
629 {
630 String s = this.buffer.toString().trim();
631 if (s.length() == 0)
632 {
633 throw new Exception("Value Cannot be Empty");
634 }
635 this.buffer.setLength(0);
636 return s;
637 }
638
639 @SuppressWarnings("unchecked")
640 private static <T> Class<? extends T> createClass(Class<T> type, String name) throws Exception
641 {
642 Class<? extends T> factory = (Class<? extends T>)ReflectionUtil.forName(name);
643 if (!type.isAssignableFrom(factory))
644 {
645 throw new Exception(name + " must be an instance of " + type.getName());
646 }
647 return factory;
648 }
649
650 private static Method createMethod(Class<?> type, String s) throws Exception
651 {
652 int pos = s.indexOf(' ');
653 if (pos == -1)
654 {
655 throw new Exception("Must Provide Return Type: " + s);
656 }
657 else
658 {
659 int pos2 = s.indexOf('(', pos + 1);
660 if (pos2 == -1)
661 {
662 throw new Exception("Must provide a method name, followed by '(': " + s);
663 }
664 else
665 {
666 String mn = s.substring(pos + 1, pos2).trim();
667 pos = s.indexOf(')', pos2 + 1);
668 if (pos == -1)
669 {
670 throw new Exception("Must close parentheses, ')' missing: " + s);
671 }
672 else
673 {
674 String[] ps = s.substring(pos2 + 1, pos).trim().split(",");
675 Class<?>[] pc;
676 if (ps.length == 1 && "".equals(ps[0]))
677 {
678 pc = new Class[0];
679 }
680 else
681 {
682 pc = new Class[ps.length];
683 for (int i = 0; i < pc.length; i++)
684 {
685 pc[i] = ReflectionUtil.forName(ps[i].trim());
686 }
687 }
688 try
689 {
690 return type.getMethod(mn, pc);
691 }
692 catch (NoSuchMethodException e)
693 {
694 throw new Exception("No Function Found on type: " + type.getName() + " with signature: "
695 + s);
696 }
697
698 }
699
700 }
701 }
702 }
703
704 private void processLibraryClass() throws Exception
705 {
706 String name = this.captureBuffer();
707 Class<?> type = createClass(TagLibrary.class, name);
708 this.library = (TagLibrary) type.newInstance();
709 }
710
711 public InputSource resolveEntity(String publicId, String systemId) throws SAXException
712 {
713 if ("-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN".equals(publicId))
714 {
715 URL url = ClassUtils.getResource("org/apache/myfaces/resource/facelet-taglib_1_0.dtd");
716 return new InputSource(url.toExternalForm());
717 }
718 return null;
719 }
720
721 public void characters(char[] ch, int start, int length) throws SAXException
722 {
723 this.buffer.append(ch, start, length);
724 }
725
726 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
727 {
728 this.buffer.setLength(0);
729 if ("tag".equals(qName))
730 {
731 this.handlerClass = null;
732 this.componentType = null;
733 this.rendererType = null;
734 this.tagName = null;
735 }
736 else if ("function".equals(qName))
737 {
738 this.functionName = null;
739 this.functionClass = null;
740 this.functionSignature = null;
741 }
742 }
743
744 public void error(SAXParseException e) throws SAXException
745 {
746 SAXException saxe = new SAXException("Error Handling [" + this.source + "@" + e.getLineNumber() + ","
747 + e.getColumnNumber() + "]");
748 saxe.initCause(e);
749 throw saxe;
750 }
751
752 public void setDocumentLocator(Locator locator)
753 {
754 this.locator = locator;
755 }
756
757 public void fatalError(SAXParseException e) throws SAXException
758 {
759 throw e;
760 }
761
762 public void warning(SAXParseException e) throws SAXException
763 {
764 throw e;
765 }
766 }
767
768 public TagLibraryConfig()
769 {
770 super();
771 }
772
773 public static TagLibrary create(FacesContext facesContext, URL url) throws IOException
774 {
775 InputStream is = null;
776 TagLibrary t = null;
777 URLConnection conn = null;
778 try
779 {
780 ExternalContext externalContext = facesContext.getExternalContext();
781 boolean schemaValidating = false;
782
783
784 if (MyfacesConfig.getCurrentInstance(externalContext).isValidateXML())
785 {
786 String version = ConfigFilesXmlValidationUtils.getFaceletTagLibVersion(url);
787 schemaValidating = "2.0".equals(version);
788 if (schemaValidating)
789 {
790 ConfigFilesXmlValidationUtils.validateFaceletTagLibFile(url, externalContext, version);
791 }
792 }
793
794
795 LibraryHandler handler = new LibraryHandler(facesContext, url);
796 SAXParser parser = createSAXParser(handler, externalContext, schemaValidating);
797 conn = url.openConnection();
798 conn.setUseCaches(false);
799 is = conn.getInputStream();
800 parser.parse(is, handler);
801 t = handler.getLibrary();
802 }
803 catch (SAXException e)
804 {
805 IOException ioe = new IOException("Error parsing [" + url + "]: ");
806 ioe.initCause(e);
807 throw ioe;
808 }
809 catch (ParserConfigurationException e)
810 {
811 IOException ioe = new IOException("Error parsing [" + url + "]: ");
812 ioe.initCause(e);
813 throw ioe;
814 }
815 finally
816 {
817 if (is != null)
818 {
819 is.close();
820 }
821 }
822 return t;
823 }
824
825 public void loadImplicit(FacesContext facesContext, Compiler compiler) throws IOException
826 {
827
828
829 ExternalContext externalContext = facesContext.getExternalContext();
830 FaceletConfigResourceProvider provider = FaceletConfigResourceProviderFactory.
831 getFacesConfigResourceProviderFactory(externalContext).
832 createFaceletConfigResourceProvider(externalContext);
833 Collection<URL> urls = provider.getFaceletTagLibConfigurationResources(externalContext);
834 for (URL url : urls)
835 {
836 try
837 {
838
839 TagLibrary tl = create(facesContext, url);
840 if (tl != null)
841 {
842 compiler.addTagLibrary(tl);
843 }
844 if (log.isLoggable(Level.FINE))
845 {
846
847 log.fine("Added Library from: " + url);
848 }
849 }
850 catch (Exception e)
851 {
852
853 log.log(Level.SEVERE, "Error Loading Library: " + url, e);
854 }
855 }
856 }
857
858 private static final SAXParser createSAXParser(LibraryHandler handler, ExternalContext externalContext,
859 boolean schemaValidating)
860 throws SAXException, ParserConfigurationException
861 {
862 SAXParserFactory factory = SAXParserFactory.newInstance();
863
864 if (MyfacesConfig.getCurrentInstance(externalContext).isValidateXML() && !schemaValidating)
865 {
866
867 factory.setNamespaceAware(false);
868 factory.setFeature("http://xml.org/sax/features/validation", true);
869 factory.setValidating(true);
870 }
871 else
872 {
873
874 factory.setNamespaceAware(true);
875 factory.setFeature("http://xml.org/sax/features/validation", false);
876 factory.setValidating(false);
877 }
878
879 SAXParser parser = factory.newSAXParser();
880 XMLReader reader = parser.getXMLReader();
881 reader.setErrorHandler(handler);
882 reader.setEntityResolver(handler);
883 return parser;
884 }
885
886 }