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.tag.composite;
20  
21  import java.lang.reflect.Method;
22  import java.net.URL;
23  import java.util.regex.Pattern;
24  
25  import javax.faces.FacesException;
26  import javax.faces.application.Resource;
27  import javax.faces.application.ResourceHandler;
28  import javax.faces.application.ViewHandler;
29  import javax.faces.context.ExternalContext;
30  import javax.faces.context.FacesContext;
31  import javax.faces.view.facelets.ComponentConfig;
32  import javax.faces.view.facelets.FaceletHandler;
33  import javax.faces.view.facelets.Tag;
34  import javax.faces.view.facelets.TagConfig;
35  import javax.faces.view.facelets.TagHandler;
36  
37  import org.apache.myfaces.shared.util.ArrayUtils;
38  import org.apache.myfaces.shared.util.StringUtils;
39  import org.apache.myfaces.shared.util.WebConfigParamUtils;
40  import org.apache.myfaces.view.facelets.tag.TagLibrary;
41  
42  /**
43   * This class create composite component tag handlers for "http://java.sun.com/jsf/composite/"
44   * namespace. Note that the class that create composite component tag handlers using its own 
45   * namespace defined in facelet taglib .xml file see TagLibraryConfig.TagLibraryImpl
46   * 
47   * @author Leonardo Uribe (latest modification by $Author$)
48   * @version $Revision$ $Date$
49   */
50  public class CompositeResourceLibrary implements TagLibrary
51  {
52      public final static String NAMESPACE_PREFIX = "http://xmlns.jcp.org/jsf/composite/";
53      public final static String ALIAS_NAMESPACE_PREFIX = "http://java.sun.com/jsf/composite/";
54      
55      private final ResourceHandler _resourceHandler;
56      private Pattern _acceptPatterns;
57      private String _extension;
58      private String[] _defaultSuffixesArray;
59      private String _namespacePrefix;
60      
61      public CompositeResourceLibrary(FacesContext facesContext, String namespacePrefix)
62      {
63          super();
64          _namespacePrefix = namespacePrefix;
65          _resourceHandler = facesContext.getApplication().getResourceHandler();
66  
67          ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
68          
69          _acceptPatterns = loadAcceptPattern(externalContext);
70  
71          _extension = loadFaceletExtension(externalContext);
72          
73          String defaultSuffixes = WebConfigParamUtils.getStringInitParameter(externalContext,
74                  ViewHandler.DEFAULT_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_SUFFIX );
75          
76          _defaultSuffixesArray = StringUtils.splitShortString(defaultSuffixes, ' ');
77          
78          boolean faceletsExtensionFound = false;
79          for (String ext : _defaultSuffixesArray)
80          {
81              if (_extension.equals(ext))
82              {
83                  faceletsExtensionFound = true;
84                  break;
85              }
86          }
87          if (!faceletsExtensionFound)
88          {
89              _defaultSuffixesArray = (String[]) ArrayUtils.concat(_defaultSuffixesArray, new String[]{_extension});
90          }
91      }
92      
93      /**
94       * Load and compile a regular expression pattern built from the Facelet view mapping parameters.
95       * 
96       * @param context
97       *            the application's external context
98       * 
99       * @return the compiled regular expression
100      */
101     private Pattern loadAcceptPattern(ExternalContext context)
102     {
103         assert context != null;
104 
105         String mappings = context.getInitParameter(ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME);
106         if (mappings == null)
107         {
108             return null;
109         }
110 
111         // Make sure the mappings contain something
112         mappings = mappings.trim();
113         if (mappings.length() == 0)
114         {
115             return null;
116         }
117 
118         return Pattern.compile(toRegex(mappings));
119     }
120 
121     private String loadFaceletExtension(ExternalContext context)
122     {
123         assert context != null;
124 
125         String suffix = context.getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
126         if (suffix == null)
127         {
128             suffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
129         }
130         else
131         {
132             suffix = suffix.trim();
133             if (suffix.length() == 0)
134             {
135                 suffix = ViewHandler.DEFAULT_FACELETS_SUFFIX;
136             }
137         }
138 
139         return suffix;
140     }
141     
142     /**
143      * Convert the specified mapping string to an equivalent regular expression.
144      * 
145      * @param mappings
146      *            le mapping string
147      * 
148      * @return an uncompiled regular expression representing the mappings
149      */
150     private String toRegex(String mappings)
151     {
152         assert mappings != null;
153 
154         // Get rid of spaces
155         mappings = mappings.replaceAll("\\s", "");
156 
157         // Escape '.'
158         mappings = mappings.replaceAll("\\.", "\\\\.");
159 
160         // Change '*' to '.*' to represent any match
161         mappings = mappings.replaceAll("\\*", ".*");
162 
163         // Split the mappings by changing ';' to '|'
164         mappings = mappings.replaceAll(";", "|");
165 
166         return mappings;
167     }
168     
169     public boolean handles(String resourceName)
170     {
171         if (resourceName == null)
172         {
173             return false;
174         }
175         // Check extension first as it's faster than mappings
176         if (resourceName.endsWith(_extension))
177         {
178             // If the extension matches, it's a Facelet viewId.
179             return true;
180         }
181 
182         // Otherwise, try to match the view identifier with the facelet mappings
183         return _acceptPatterns != null && _acceptPatterns.matcher(resourceName).matches();
184     }
185 
186     public boolean containsFunction(String ns, String name)
187     {
188         // Composite component tag library does not suport functions
189         return false;
190     }
191 
192     public boolean containsNamespace(String ns)
193     {
194         if (ns != null && ns.startsWith(_namespacePrefix))
195         {
196             if (ns.length() > _namespacePrefix.length())
197             {
198                 String libraryName = ns.substring(_namespacePrefix.length());
199                 return _resourceHandler.libraryExists(libraryName);
200             }
201         }        
202         return false;
203     }
204 
205     public boolean containsTagHandler(String ns, String localName)
206     {
207         if (ns != null && ns.startsWith(_namespacePrefix))
208         {
209             if (ns.length() > _namespacePrefix.length())
210             {
211                 String libraryName = ns.substring(_namespacePrefix.length());
212                 
213                 for (String defaultSuffix : _defaultSuffixesArray)
214                 {
215                     String resourceName = localName + defaultSuffix;
216                     if (handles(resourceName))
217                     {
218                         Resource compositeComponentResource = 
219                             _resourceHandler.createResource(resourceName, libraryName);
220                         if (compositeComponentResource != null)
221                         {
222                             URL url = compositeComponentResource.getURL();
223                             return (url != null);
224                         }
225                     }
226                 }
227             }
228         }
229         return false;
230     }
231 
232     public Method createFunction(String ns, String name)
233     {
234         // Composite component tag library does not suport functions
235         return null;
236     }
237 
238     public TagHandler createTagHandler(String ns, String localName,
239             TagConfig tag) throws FacesException
240     {
241         if (ns != null && ns.startsWith(_namespacePrefix))
242         {
243             if (ns.length() > _namespacePrefix.length())
244             {
245                 String libraryName = ns.substring(_namespacePrefix.length());
246                 for (String defaultSuffix : _defaultSuffixesArray)
247                 {
248                     String resourceName = localName + defaultSuffix;
249                     if (handles(resourceName))
250                     {
251                         // MYFACES-3308 If a composite component exists, it requires to 
252                         // be always resolved. In other words, it should always exists a default.
253                         // The call here for resourceHandler.createResource, just try to get
254                         // the Resource and if it does not exists, it just returns null.
255                         // The intention of this code is just create an instance and pass to
256                         // CompositeComponentResourceTagHandler. Then, its values 
257                         // (resourceName, libraryName) will be used to derive the real instance
258                         // to use in a view, based on the locale used.
259                         Resource compositeComponentResourceWrapped
260                                 = _resourceHandler.createResource(resourceName, libraryName);
261                         if (compositeComponentResourceWrapped != null)
262                         {
263                             Resource compositeComponentResource
264                                     = new CompositeResouceWrapper(compositeComponentResourceWrapped);
265                             ComponentConfig componentConfig = new ComponentConfigWrapper(tag,
266                                     "javax.faces.NamingContainer", null);
267 
268                             return new CompositeComponentResourceTagHandler(componentConfig,
269                                                                             compositeComponentResource);
270                         }
271                     }
272                 }
273             }
274         }
275         return null;
276     }
277 
278     private static class ComponentConfigWrapper implements ComponentConfig
279     {
280 
281         protected final TagConfig parent;
282 
283         protected final String componentType;
284 
285         protected final String rendererType;
286 
287         public ComponentConfigWrapper(TagConfig parent, String componentType,
288                                       String rendererType)
289         {
290             this.parent = parent;
291             this.componentType = componentType;
292             this.rendererType = rendererType;
293         }
294 
295         public String getComponentType()
296         {
297             return this.componentType;
298         }
299 
300         public String getRendererType()
301         {
302             return this.rendererType;
303         }
304 
305         public FaceletHandler getNextHandler()
306         {
307             return this.parent.getNextHandler();
308         }
309 
310         public Tag getTag()
311         {
312             return this.parent.getTag();
313         }
314 
315         public String getTagId()
316         {
317             return this.parent.getTagId();
318         }
319     }
320 }