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 jakarta.faces.application.FacesMessage;
26  import jakarta.faces.application.ProjectStage;
27  import jakarta.faces.application.Resource;
28  import jakarta.faces.component.UIComponent;
29  import jakarta.faces.context.FacesContext;
30  import jakarta.faces.context.ResponseWriter;
31  import jakarta.faces.event.ComponentSystemEvent;
32  import jakarta.faces.event.ComponentSystemEventListener;
33  import jakarta.faces.event.ListenerFor;
34  import jakarta.faces.event.PostAddToViewEvent;
35  import jakarta.faces.render.Renderer;
36  import jakarta.faces.view.Location;
37  
38  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
39  import org.apache.myfaces.shared.renderkit.JSFAttr;
40  import org.apache.myfaces.shared.renderkit.RendererUtils;
41  import org.apache.myfaces.shared.renderkit.html.HTML;
42  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
43  import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
44  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
45  
46  /**
47   * Renderer used by h:outputStylesheet component 
48   * 
49   * Note: originally this component required PostBuildComponentTreeOnRestoreViewEvent,
50   * but that is no longer true because a tag handler implementing RelocatableResourceHandler
51   * interface was associated, so the component is really marked and refreshed.
52   * 
53   * @since 2.0
54   * @author Leonardo Uribe (latest modification by $Author$)
55   * @version $Revision$ $Date$
56   */
57  @JSFRenderer(renderKitId = "HTML_BASIC", family = "jakarta.faces.Output", type = "jakarta.faces.resource.Stylesheet")
58  @ListenerFor(systemEventClass = PostAddToViewEvent.class)
59  public class HtmlStylesheetRenderer extends Renderer implements
60      ComponentSystemEventListener
61  {
62      private static final Logger log = Logger.getLogger(HtmlStylesheetRenderer.class.getName());
63          
64      @Override
65      public void processEvent(ComponentSystemEvent event)
66      {
67          if (event instanceof PostAddToViewEvent)
68          {
69              UIComponent component = event.getComponent();
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, "head");
86          }
87      }
88      
89      @Override
90      public boolean getRendersChildren()
91      {
92          return true;
93      }
94  
95      @Override
96      public void encodeChildren(FacesContext facesContext, UIComponent component)
97              throws IOException
98      {
99          if (facesContext == null)
100         {
101             throw new NullPointerException("context");
102         }
103         if (component == null)
104         {
105             throw new NullPointerException("component");
106         }
107 
108         Map<String, Object> componentAttributesMap = component.getAttributes();
109         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
110         boolean hasChildren = component.getChildCount() > 0;
111         
112         if (resourceName != null && (!"".equals(resourceName)) )
113         {
114             if (hasChildren)
115             {
116                 log.info("Component with resourceName "+ resourceName + 
117                         " and child components found. Child components will be ignored.");
118             }
119         }
120         else
121         {
122             if (hasChildren)
123             {
124                 ResponseWriter writer = facesContext.getResponseWriter();
125                 writer.startElement(HTML.STYLE_ELEM, component);
126                 writer.writeAttribute(HTML.TYPE_ATTR, HTML.STYLE_TYPE_TEXT_CSS, null);
127                 RendererUtils.renderChildren(facesContext, component);
128                 writer.endElement(HTML.STYLE_ELEM);
129             }
130             else
131             {
132                 if (!facesContext.isProjectStage(ProjectStage.Production))
133                 {
134                     facesContext.addMessage(component.getClientId(facesContext), 
135                             new FacesMessage("Component with no name and no body content, so nothing rendered."));
136                 }
137             }            
138         }
139     }
140     
141     @Override
142     public void encodeEnd(FacesContext facesContext, UIComponent component)
143             throws IOException
144     {
145         super.encodeEnd(facesContext, component); //check for NP
146         
147         Map<String, Object> componentAttributesMap = component.getAttributes();
148         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
149         String libraryName = (String) componentAttributesMap.get(JSFAttr.LIBRARY_ATTR);
150 
151         if (resourceName == null)
152         {
153             //log.warn("Trying to encode resource represented by component" + 
154             //        component.getClientId() + " without resourceName."+
155             //        " It will be silenty ignored.");
156             return;
157         }
158         if ("".equals(resourceName))
159         {
160             return;
161         }
162         
163         String additionalQueryParams = null;
164         int index = resourceName.indexOf('?');
165         if (index >= 0)
166         {
167             additionalQueryParams = resourceName.substring(index + 1);
168             resourceName = resourceName.substring(0, index);
169         }
170         
171         Resource resource;
172         if (libraryName == null)
173         {
174             if (ResourceUtils.isRenderedStylesheet(facesContext, libraryName, resourceName))
175             {
176                 //Resource already founded
177                 return;
178             }
179             resource = facesContext.getApplication().getResourceHandler()
180                     .createResource(resourceName);
181         }
182         else
183         {
184             if (ResourceUtils.isRenderedStylesheet(facesContext, libraryName, resourceName))
185             {
186                 //Resource already founded
187                 return;
188             }
189             resource = facesContext.getApplication().getResourceHandler()
190                     .createResource(resourceName, libraryName);
191 
192         }
193         
194         if (resource == null)
195         {
196             //no resource found
197             log.warning("Resource referenced by resourceName "+ resourceName +
198                     (libraryName == null ? "" : " and libraryName " + libraryName) +
199                     " not found in call to ResourceHandler.createResource."+
200                     " It will be silenty ignored.");
201             return;
202         }
203         else
204         {
205             if (ResourceUtils.isRenderedStylesheet(facesContext, resource.getLibraryName(), resource.getResourceName()))
206             {
207                 //Resource already founded
208                 return;
209             }
210 
211             // Rendering resource
212             ResourceUtils.markStylesheetAsRendered(facesContext, libraryName, resourceName);
213             ResourceUtils.markStylesheetAsRendered(facesContext, resource.getLibraryName(), resource.getResourceName());
214             ResponseWriter writer = facesContext.getResponseWriter();
215             writer.startElement(HTML.LINK_ELEM, component);
216             writer.writeAttribute(HTML.REL_ATTR, HTML.STYLESHEET_VALUE,null );
217             String media = (String) component.getAttributes().get("media");
218             //writer.writeAttribute("media", media == null ? "screen" : media ,null );
219             if (media != null)
220             {
221                 writer.writeAttribute("media", media ,null );
222             }
223             writer.writeAttribute(HTML.TYPE_ATTR, 
224                     (resource.getContentType() == null ? HTML.STYLE_TYPE_TEXT_CSS
225                             : resource.getContentType()) , null);
226             String path = resource.getRequestPath();
227             if (additionalQueryParams != null)
228             {
229                 path = path + ((path.indexOf('?') >= 0) ? "&amp;" : "?") + additionalQueryParams;
230             }
231             writer.writeURIAttribute(HTML.HREF_ATTR,
232                     facesContext.getExternalContext().encodeResourceURL(path), null);
233             writer.endElement(HTML.LINK_ELEM);
234         }
235     }
236 }