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.renderkit.html;
20  
21  import java.io.IOException;
22  import java.util.Map;
23  import java.util.logging.Logger;
24  
25  import javax.faces.FacesException;
26  import javax.faces.application.FacesMessage;
27  import javax.faces.application.ProjectStage;
28  import javax.faces.application.Resource;
29  import javax.faces.component.UIComponent;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseWriter;
32  import javax.faces.event.ComponentSystemEvent;
33  import javax.faces.event.ComponentSystemEventListener;
34  import javax.faces.event.ListenerFor;
35  import javax.faces.event.PostAddToViewEvent;
36  import javax.faces.event.PreRenderViewEvent;
37  import javax.faces.render.Renderer;
38  import javax.faces.view.Location;
39  
40  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
41  import org.apache.myfaces.shared.renderkit.JSFAttr;
42  import org.apache.myfaces.shared.renderkit.RendererUtils;
43  import org.apache.myfaces.shared.renderkit.html.HTML;
44  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
45  import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
46  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
47  
48  /**
49   * Renderer used by h:outputScript component
50   *
51   * @author Leonardo Uribe (latest modification by $Author$)
52   * @version $Revision$ $Date$
53   * @since 2.0
54   */
55  @JSFRenderer(renderKitId = "HTML_BASIC", family = "javax.faces.Output", type = "javax.faces.resource.Script")
56  @ListenerFor(systemEventClass = PostAddToViewEvent.class)
57  public class HtmlScriptRenderer extends Renderer implements ComponentSystemEventListener
58  {
59      private static final Logger log = Logger.getLogger(HtmlScriptRenderer.class.getName());
60  
61      @Override
62      public void processEvent(ComponentSystemEvent event)
63      {
64          if (event instanceof PostAddToViewEvent)
65          {
66              UIComponent component = event.getComponent();
67              String target = (String) component.getAttributes().get(JSFAttr.TARGET_ATTR);
68              if (target != null)
69              {
70                  FacesContext facesContext = FacesContext.getCurrentInstance();
71  
72                  Location location = (Location) component.getAttributes().get(CompositeComponentELUtils.LOCATION_KEY);
73                  if (location != null)
74                  {
75                      UIComponent ccParent
76                              = CompositeComponentELUtils.getCompositeComponentBasedOnLocation(facesContext, location);
77                      if (ccParent != null)
78                      {
79                          component.getAttributes().put(
80                                  CompositeComponentELUtils.CC_FIND_COMPONENT_EXPRESSION,
81                                  ComponentSupport.getFindComponentExpression(facesContext, ccParent));
82                      }
83                  }
84  
85                  facesContext.getViewRoot().addComponentResource(facesContext, component, target);
86              }
87          }
88  
89          if (event instanceof PreRenderViewEvent)
90          {
91              //TODO target check here
92              UIComponent component = event.getComponent();
93              String target = (String) component.getAttributes().get(JSFAttr.TARGET_ATTR);
94              if (target != null)
95              {
96                  FacesContext facesContext = FacesContext.getCurrentInstance();
97                  UIComponent uiTarget = facesContext.getViewRoot().getFacet(target);
98                  if (uiTarget == null)
99                  {
100                     throw new FacesException("Target for component not found");
101                 }
102             }
103         }
104     }
105 
106     @Override
107     public boolean getRendersChildren()
108     {
109         return true;
110     }
111 
112     @Override
113     public void encodeChildren(FacesContext facesContext, UIComponent component)
114             throws IOException
115     {
116         if (facesContext == null)
117         {
118             throw new NullPointerException("context");
119         }
120         if (component == null)
121         {
122             throw new NullPointerException("component");
123         }
124 
125         Map<String, Object> componentAttributesMap = component.getAttributes();
126         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
127         boolean hasChildren = component.getChildCount() > 0;
128 
129         if (resourceName != null && (!"".equals(resourceName)))
130         {
131             if (hasChildren)
132             {
133                 log.info("Component with resourceName " + resourceName +
134                         " and child components found. Child components will be ignored.");
135             }
136         }
137         else
138         {
139             if (hasChildren)
140             {
141                 // Children are encoded as usual. Usually the layout is
142                 // <script type="text/javascript">
143                 // ...... some javascript .......
144                 // </script>
145                 ResponseWriter writer = facesContext.getResponseWriter();
146                 writer.startElement(HTML.SCRIPT_ELEM, component);
147                 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
148                 RendererUtils.renderChildren(facesContext, component);
149                 writer.endElement(HTML.SCRIPT_ELEM);
150             }
151             else
152             {
153                 if (!facesContext.getApplication().getProjectStage().equals(
154                         ProjectStage.Production))
155                 {
156                     facesContext.addMessage(component.getClientId(facesContext),
157                             new FacesMessage("Component with no name and no body content, so nothing rendered."));
158                 }
159             }
160         }
161     }
162 
163     @Override
164     public void encodeEnd(FacesContext facesContext, UIComponent component)
165             throws IOException
166     {
167         super.encodeEnd(facesContext, component); //check for NP
168 
169         Map<String, Object> componentAttributesMap = component.getAttributes();
170         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
171         String libraryName = (String) componentAttributesMap.get(JSFAttr.LIBRARY_ATTR);
172 
173         if (resourceName == null)
174         {
175             //log.warn("Trying to encode resource represented by component" +
176             //        component.getClientId() + " without resourceName."+
177             //        " It will be silenty ignored.");
178             return;
179         }
180         if ("".equals(resourceName))
181         {
182             return;
183         }
184 
185         String additionalQueryParams = null;
186         int index = resourceName.indexOf('?');
187         if (index >= 0)
188         {
189             additionalQueryParams = resourceName.substring(index + 1);
190             resourceName = resourceName.substring(0, index);
191         }
192 
193         Resource resource;
194         if (libraryName == null)
195         {
196             if (ResourceUtils.isRenderedScript(facesContext, libraryName, resourceName))
197             {
198                 //Resource already founded
199                 return;
200             }
201             resource = facesContext.getApplication().getResourceHandler()
202                     .createResource(resourceName);
203         }
204         else
205         {
206             if (ResourceUtils.isRenderedScript(facesContext, libraryName, resourceName))
207             {
208                 //Resource already founded
209                 return;
210             }
211             resource = facesContext.getApplication().getResourceHandler()
212                     .createResource(resourceName, libraryName);
213 
214         }
215 
216         if (resource == null)
217         {
218             //no resource found
219             log.warning("Resource referenced by resourceName " + resourceName +
220                     (libraryName == null ? "" : " and libraryName " + libraryName) +
221                     " not found in call to ResourceHandler.createResource." +
222                     " It will be silenty ignored.");
223             return;
224         }
225         else
226         {
227             if (ResourceUtils.isRenderedScript(facesContext, resource.getLibraryName(), resource.getResourceName()))
228             {
229                 //Resource already founded
230                 return;
231             }
232 
233             // Rendering resource
234             ResourceUtils.markScriptAsRendered(facesContext, libraryName, resourceName);
235             ResourceUtils.markScriptAsRendered(facesContext, resource.getLibraryName(), resource.getResourceName());
236             ResponseWriter writer = facesContext.getResponseWriter();
237             writer.startElement(HTML.SCRIPT_ELEM, component);
238 // We can't render the content type, because usually it returns "application/x-javascript"
239 // and this is not compatible with IE. We should force render "text/javascript".
240             writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
241             String path = resource.getRequestPath();
242             if (additionalQueryParams != null)
243             {
244                 path = path + ((path.indexOf('?') >= 0) ? "&amp;" : "?") + additionalQueryParams;
245             }
246             writer.writeURIAttribute(HTML.SRC_ATTR, facesContext.getExternalContext().encodeResourceURL(path), null);
247             writer.endElement(HTML.SCRIPT_ELEM);
248         }
249     }
250 
251 }