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  package org.apache.myfaces.view.facelets.compiler;
20  
21  import org.apache.myfaces.shared.util.ArrayUtils;
22  import org.apache.myfaces.shared.util.StringUtils;
23  import org.apache.myfaces.shared.util.WebConfigParamUtils;
24  import org.apache.myfaces.view.facelets.tag.AbstractTagLibrary;
25  import org.apache.myfaces.view.facelets.tag.TagLibrary;
26  import org.apache.myfaces.view.facelets.tag.composite.CompositeComponentResourceTagHandler;
27  import org.apache.myfaces.view.facelets.tag.composite.CompositeResouceWrapper;
28  import org.apache.myfaces.view.facelets.util.ParameterCheck;
29  import org.apache.myfaces.view.facelets.util.ReflectionUtil;
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 java.lang.reflect.Method;
43  import java.net.URL;
44  import java.util.logging.Logger;
45  import java.util.regex.Pattern;
46  import org.apache.myfaces.config.element.facelets.FaceletBehaviorTag;
47  import org.apache.myfaces.config.element.facelets.FaceletComponentTag;
48  import org.apache.myfaces.config.element.facelets.FaceletConverterTag;
49  import org.apache.myfaces.config.element.facelets.FaceletFunction;
50  import org.apache.myfaces.config.element.facelets.FaceletHandlerTag;
51  import org.apache.myfaces.config.element.facelets.FaceletSourceTag;
52  import org.apache.myfaces.config.element.facelets.FaceletTag;
53  import org.apache.myfaces.config.element.facelets.FaceletTagLibrary;
54  import org.apache.myfaces.config.element.facelets.FaceletValidatorTag;
55  
56  /**
57   * Handles creating a {@link org.apache.myfaces.view.facelets.tag.TagLibrary TagLibrary}
58   * from a {@link java.net.URL URL} source.
59   * 
60   * @author Jacob Hookom
61   * @version $Id$
62   */
63  public final class TagLibraryConfig
64  {
65  
66      //private final static String SUFFIX = ".taglib.xml";
67  
68      //protected final static Logger log = Logger.getLogger("facelets.compiler");
69      protected final static Logger log = Logger.getLogger(TagLibraryConfig.class.getName());
70  
71      private static class TagLibraryImpl extends AbstractTagLibrary
72      {
73          private String _compositeLibraryName;
74          
75          private final ResourceHandler _resourceHandler;
76          private Pattern _acceptPatterns;
77          private String _extension;
78          private String[] _defaultSuffixesArray;
79          
80          public TagLibraryImpl(FacesContext facesContext, String namespace)
81          {
82              super(namespace);
83              _compositeLibraryName = null;
84              _resourceHandler = facesContext.getApplication().getResourceHandler();
85              ExternalContext externalContext = facesContext.getExternalContext();
86              
87              _acceptPatterns = loadAcceptPattern(externalContext);
88  
89              _extension = loadFaceletExtension(externalContext);
90              
91              String defaultSuffixes = WebConfigParamUtils.getStringInitParameter(externalContext,
92                      ViewHandler.DEFAULT_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_SUFFIX );
93              
94              _defaultSuffixesArray = StringUtils.splitShortString(defaultSuffixes, ' ');
95              
96              boolean faceletsExtensionFound = false;
97              for (String ext : _defaultSuffixesArray)
98              {
99                  if (_extension.equals(ext))
100                 {
101                     faceletsExtensionFound = true;
102                     break;
103                 }
104             }
105             if (!faceletsExtensionFound)
106             {
107                 _defaultSuffixesArray = (String[]) ArrayUtils.concat(_defaultSuffixesArray, new String[]{_extension});
108             }
109         }
110         
111         /**
112          * Load and compile a regular expression pattern built from the Facelet view mapping parameters.
113          * 
114          * @param context
115          *            the application's external context
116          * 
117          * @return the compiled regular expression
118          */
119         private Pattern loadAcceptPattern(ExternalContext context)
120         {
121             assert context != null;
122 
123             String mappings = context.getInitParameter(ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME);
124             if (mappings == null)
125             {
126                 return null;
127             }
128 
129             // Make sure the mappings contain something
130             mappings = mappings.trim();
131             if (mappings.length() == 0)
132             {
133                 return null;
134             }
135 
136             return Pattern.compile(toRegex(mappings));
137         }
138 
139         private String loadFaceletExtension(ExternalContext context)
140         {
141             assert context != null;
142 
143             String suffix = context.getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
144             if (suffix == null)
145             {
146                 suffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
147             }
148             else
149             {
150                 suffix = suffix.trim();
151                 if (suffix.length() == 0)
152                 {
153                     suffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
154                 }
155             }
156 
157             return suffix;
158         }
159         
160         /**
161          * Convert the specified mapping string to an equivalent regular expression.
162          * 
163          * @param mappings
164          *            le mapping string
165          * 
166          * @return an uncompiled regular expression representing the mappings
167          */
168         private String toRegex(String mappings)
169         {
170             assert mappings != null;
171 
172             // Get rid of spaces
173             mappings = mappings.replaceAll("\\s", "");
174 
175             // Escape '.'
176             mappings = mappings.replaceAll("\\.", "\\\\.");
177 
178             // Change '*' to '.*' to represent any match
179             mappings = mappings.replaceAll("\\*", ".*");
180 
181             // Split the mappings by changing ';' to '|'
182             mappings = mappings.replaceAll(";", "|");
183 
184             return mappings;
185         }
186         
187         public boolean handles(String resourceName)
188         {
189             if (resourceName == null)
190             {
191                 return false;
192             }
193             // Check extension first as it's faster than mappings
194             if (resourceName.endsWith(_extension))
195             {
196                 // If the extension matches, it's a Facelet viewId.
197                 return true;
198             }
199 
200             // Otherwise, try to match the view identifier with the facelet mappings
201             return _acceptPatterns != null && _acceptPatterns.matcher(resourceName).matches();
202         }
203         
204         @Override
205         public boolean containsTagHandler(String ns, String localName)
206         {
207             boolean result = super.containsTagHandler(ns, localName);
208             
209             if (!result && _compositeLibraryName != null && containsNamespace(ns))
210             {
211                 for (String defaultSuffix : _defaultSuffixesArray)
212                 {
213                     String resourceName = localName + defaultSuffix;
214                     if (handles(resourceName))
215                     {
216                         Resource compositeComponentResource = _resourceHandler.createResource(
217                                 resourceName, _compositeLibraryName);
218                         
219                         if (compositeComponentResource != null)
220                         {
221                             URL url = compositeComponentResource.getURL();
222                             return (url != null);
223                         }
224                     }
225                 }
226             }
227             return result;
228         }
229         
230         @Override
231         public TagHandler createTagHandler(String ns, String localName,
232                 TagConfig tag) throws FacesException
233         {
234             TagHandler tagHandler = super.createTagHandler(ns, localName, tag);
235             
236             if (tagHandler == null && _compositeLibraryName != null && containsNamespace(ns))
237             {
238                 for (String defaultSuffix : _defaultSuffixesArray)
239                 {
240                     String resourceName = localName + defaultSuffix;
241                     if (handles(resourceName))
242                     {
243                         // MYFACES-3308 If a composite component exists, it requires to 
244                         // be always resolved. In other words, it should always exists a default.
245                         // The call here for resourceHandler.createResource, just try to get
246                         // the Resource and if it does not exists, it just returns null.
247                         // The intention of this code is just create an instance and pass to
248                         // CompositeComponentResourceTagHandler. Then, its values 
249                         // (resourceName, libraryName) will be used to derive the real instance
250                         // to use in a view, based on the locale used.
251                         Resource compositeComponentResource = new CompositeResouceWrapper(
252                             _resourceHandler.createResource(resourceName, _compositeLibraryName));
253                         
254                         if (compositeComponentResource != null)
255                         {
256                             ComponentConfig componentConfig = new ComponentConfigWrapper(tag,
257                                     "javax.faces.NamingContainer", null);
258                             
259                             return new CompositeComponentResourceTagHandler(
260                                     componentConfig, compositeComponentResource);
261                         }
262                     }
263                 }
264             }
265             return tagHandler;
266         }
267 
268         public void setCompositeLibrary(String compositeLibraryName)
269         {
270             _compositeLibraryName = compositeLibraryName;
271         }
272 
273         public void putConverter(String name, String id)
274         {
275             ParameterCheck.notNull("name", name);
276             ParameterCheck.notNull("id", id);
277             this.addConverter(name, id);
278         }
279 
280         public void putConverter(String name, String id, Class<? extends TagHandler> handlerClass)
281         {
282             ParameterCheck.notNull("name", name);
283             ParameterCheck.notNull("id", id);
284             ParameterCheck.notNull("handlerClass", handlerClass);
285             this.addConverter(name, id, handlerClass);
286         }
287 
288         public void putValidator(String name, String id)
289         {
290             ParameterCheck.notNull("name", name);
291             ParameterCheck.notNull("id", id);
292             this.addValidator(name, id);
293         }
294 
295         public void putValidator(String name, String id, Class<? extends TagHandler> handlerClass)
296         {
297             ParameterCheck.notNull("name", name);
298             ParameterCheck.notNull("id", id);
299             ParameterCheck.notNull("handlerClass", handlerClass);
300             this.addValidator(name, id, handlerClass);
301         }
302 
303         public void putTagHandler(String name, Class<? extends TagHandler> type)
304         {
305             ParameterCheck.notNull("name", name);
306             ParameterCheck.notNull("type", type);
307             this.addTagHandler(name, type);
308         }
309         
310         public void putComponentFromResourceId(String name, String resourceId)
311         {
312             ParameterCheck.notNull("name", name);
313             ParameterCheck.notNull("resourceId", resourceId);
314             this.addComponentFromResourceId(name, resourceId);
315         }
316 
317         public void putComponent(String name, String componentType, String rendererType)
318         {
319             ParameterCheck.notNull("name", name);
320             ParameterCheck.notNull("componentType", componentType);
321             this.addComponent(name, componentType, rendererType);
322         }
323 
324         public void putComponent(String name, String componentType, String rendererType, 
325                                  Class<? extends TagHandler> handlerClass)
326         {
327             ParameterCheck.notNull("name", name);
328             ParameterCheck.notNull("componentType", componentType);
329             ParameterCheck.notNull("handlerClass", handlerClass);
330             this.addComponent(name, componentType, rendererType, handlerClass);
331         }
332 
333         public void putUserTag(String name, URL source)
334         {
335             ParameterCheck.notNull("name", name);
336             ParameterCheck.notNull("source", source);
337             this.addUserTag(name, source);
338         }
339 
340         public void putFunction(String name, Method method)
341         {
342             ParameterCheck.notNull("name", name);
343             ParameterCheck.notNull("method", method);
344             this.addFunction(name, method);
345         }
346         
347         public void putBehavior(String name, String id)
348         {
349             ParameterCheck.notNull("name", name);
350             ParameterCheck.notNull("id", id);
351             this.addBehavior(name, id);
352         }
353         
354         public void putBehavior(String name, String id, Class<? extends TagHandler> handlerClass)
355         {
356             ParameterCheck.notNull("name", name);
357             ParameterCheck.notNull("id", id);
358             ParameterCheck.notNull("handlerClass", handlerClass);
359             this.addBehavior(name, id, handlerClass);
360         }
361     }
362     
363     private static class ComponentConfigWrapper implements ComponentConfig
364     {
365 
366         protected final TagConfig parent;
367 
368         protected final String componentType;
369 
370         protected final String rendererType;
371 
372         public ComponentConfigWrapper(TagConfig parent, String componentType,
373                 String rendererType)
374         {
375             this.parent = parent;
376             this.componentType = componentType;
377             this.rendererType = rendererType;
378         }
379 
380         public String getComponentType()
381         {
382             return this.componentType;
383         }
384 
385         public String getRendererType()
386         {
387             return this.rendererType;
388         }
389 
390         public FaceletHandler getNextHandler()
391         {
392             return this.parent.getNextHandler();
393         }
394 
395         public Tag getTag()
396         {
397             return this.parent.getTag();
398         }
399 
400         public String getTagId()
401         {
402             return this.parent.getTagId();
403         }
404     }    
405 
406     public TagLibraryConfig()
407     {
408         super();
409     }
410     
411     public static TagLibrary create(FacesContext facesContext, FaceletTagLibrary faceletTagLibrary)
412     {
413         if (isNotEmpty(faceletTagLibrary.getLibraryClass()))
414         {
415             TagLibrary t = null;
416             Class<?> type;
417             try
418             {
419                 type = createClass(TagLibrary.class, faceletTagLibrary.getLibraryClass());
420                 t = (TagLibrary) type.newInstance();
421             }
422             catch (Exception ex)
423             {
424                 throw new FacesException("Cannot instantiate TagLibrary", ex);
425             }
426             // No further processing required.
427             return t;
428         }
429         
430         TagLibraryImpl impl = new TagLibraryImpl(facesContext, faceletTagLibrary.getNamespace());
431         
432         impl.setCompositeLibrary(faceletTagLibrary.getCompositeLibraryName());
433         
434         for (FaceletFunction ff : faceletTagLibrary.getFunctions())
435         {
436             try
437             {
438                 Class<?> functionClass = createClass(Object.class, ff.getFunctionClass());
439                 impl.putFunction(ff.getFunctionName(), createMethod(functionClass, ff.getFunctionSignature()));
440             }
441             catch (Exception ex)
442             {
443                 throw new FacesException("Cannot instantiate Function Class", ex);
444             }
445         }
446         
447         for (FaceletTag ft : faceletTagLibrary.getTags())
448         {
449             try
450             {
451                 if (ft.isHandlerTag())
452                 {
453                     FaceletHandlerTag tag = (FaceletHandlerTag) ft.getTagDefinition();
454                     if (tag.getHandlerClass() != null)
455                     {
456                         Class<? extends TagHandler> handlerClass = 
457                             createClass(TagHandler.class, tag.getHandlerClass());
458                         impl.putTagHandler(ft.getName(), handlerClass);
459                     }
460                 }
461                 else if (ft.isComponentTag())
462                 {
463                     FaceletComponentTag tag = (FaceletComponentTag) ft.getTagDefinition();
464                     if (tag.getHandlerClass() != null)
465                     {
466                         Class<? extends TagHandler> handlerClass = 
467                             createClass(TagHandler.class, tag.getHandlerClass());
468                         impl.putComponent(ft.getName(), tag.getComponentType(), tag.getRendererType(), handlerClass);
469                     }
470                     else if (tag.getResourceId() != null)
471                     {
472                         impl.putComponentFromResourceId(ft.getName(), tag.getResourceId());
473                     }
474                     else 
475                     {
476                         impl.putComponent(ft.getName(), tag.getComponentType(), tag.getRendererType());
477                     }
478                 }
479                 else if (ft.isSourceTag())
480                 {
481                     FaceletSourceTag tag = (FaceletSourceTag) ft.getTagDefinition();
482                     impl.putUserTag(ft.getName(), new URL(tag.getSource()));
483                 }
484                 else if (ft.isConverterTag())
485                 {
486                     FaceletConverterTag tag = (FaceletConverterTag) ft.getTagDefinition();
487                     if (tag.getHandlerClass() != null)
488                     {
489                         Class<? extends TagHandler> handlerClass = 
490                             createClass(TagHandler.class, tag.getHandlerClass());
491                         impl.putConverter(ft.getName(), tag.getConverterId(), handlerClass);
492                     }
493                     else
494                     {
495                         impl.putConverter(ft.getName(), tag.getConverterId());
496                     }
497                 }
498                 else if (ft.isValidatorTag())
499                 {
500                     FaceletValidatorTag tag = (FaceletValidatorTag) ft.getTagDefinition();
501                     if (tag.getHandlerClass() != null)
502                     {
503                         Class<? extends TagHandler> handlerClass = 
504                             createClass(TagHandler.class, tag.getHandlerClass());
505                         impl.putValidator(ft.getName(), tag.getValidatorId(), handlerClass);
506                     }
507                     else
508                     {
509                         impl.putValidator(ft.getName(), tag.getValidatorId());
510                     }
511                 }
512                 else if (ft.isBehaviorTag())
513                 {
514                     FaceletBehaviorTag tag = (FaceletBehaviorTag) ft.getTagDefinition();
515                     if (tag.getHandlerClass() != null)
516                     {
517                         Class<? extends TagHandler> handlerClass = 
518                             createClass(TagHandler.class, tag.getHandlerClass());
519                         impl.putBehavior(ft.getName(), tag.getBehaviorId(), handlerClass);
520                     }
521                     else
522                     {
523                         impl.putBehavior(ft.getName(), tag.getBehaviorId());
524                     }
525                 }
526             }
527             catch (Exception ex)
528             {
529                 throw new FacesException("Cannot instantiate Tag "+ft.getName()+" from namespace "+
530                     faceletTagLibrary.getNamespace(), ex);
531             }
532         }
533         return impl;
534     }
535     
536     @SuppressWarnings("unchecked")
537     private static <T> Class<? extends T> createClass(Class<T> type, String name) throws Exception
538     {
539         Class<? extends T> factory = (Class<? extends T>)ReflectionUtil.forName(name);
540         if (!type.isAssignableFrom(factory))
541         {
542             throw new Exception(name + " must be an instance of " + type.getName());
543         }
544         return factory;
545     }
546 
547     private static Method createMethod(Class<?> type, String s) throws Exception
548     {
549         int pos = s.indexOf(' ');
550         if (pos == -1)
551         {
552             throw new Exception("Must Provide Return Type: " + s);
553         }
554         else
555         {
556             int pos2 = s.indexOf('(', pos + 1);
557             if (pos2 == -1)
558             {
559                 throw new Exception("Must provide a method name, followed by '(': " + s);
560             }
561             else
562             {
563                 String mn = s.substring(pos + 1, pos2).trim();
564                 pos = s.indexOf(')', pos2 + 1);
565                 if (pos == -1)
566                 {
567                     throw new Exception("Must close parentheses, ')' missing: " + s);
568                 }
569                 else
570                 {
571                     String[] ps = s.substring(pos2 + 1, pos).trim().split(",");
572                     Class<?>[] pc;
573                     if (ps.length == 1 && "".equals(ps[0]))
574                     {
575                         pc = new Class[0];
576                     }
577                     else
578                     {
579                         pc = new Class[ps.length];
580                         for (int i = 0; i < pc.length; i++)
581                         {
582                             pc[i] = ReflectionUtil.forName(ps[i].trim());
583                         }
584                     }
585                     try
586                     {
587                         return type.getMethod(mn, pc);
588                     }
589                     catch (NoSuchMethodException e)
590                     {
591                         throw new Exception("No Function Found on type: " + type.getName() + " with signature: "
592                                 + s);
593                     }
594 
595                 }
596 
597             }
598         }
599     }
600 
601     private static boolean isNotEmpty(String value)
602     {
603         return value != null && value.length() > 0;
604     }
605 
606 }