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.EnumSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import jakarta.faces.component.UIComponent;
30  import jakarta.faces.component.ValueHolder;
31  import jakarta.faces.component.behavior.ClientBehavior;
32  import jakarta.faces.component.behavior.ClientBehaviorHolder;
33  import jakarta.faces.component.html.HtmlOutputLabel;
34  import jakarta.faces.component.search.SearchExpressionContext;
35  import jakarta.faces.component.search.SearchExpressionHint;
36  import jakarta.faces.context.FacesContext;
37  import jakarta.faces.context.ResponseWriter;
38  
39  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
40  import org.apache.myfaces.shared.component.EscapeCapable;
41  import org.apache.myfaces.shared.renderkit.JSFAttr;
42  import org.apache.myfaces.shared.renderkit.RendererUtils;
43  import org.apache.myfaces.shared.renderkit.html.CommonEventUtils;
44  import org.apache.myfaces.shared.renderkit.html.CommonPropertyUtils;
45  import org.apache.myfaces.shared.renderkit.html.HTML;
46  import org.apache.myfaces.shared.renderkit.html.HtmlRenderer;
47  import org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils;
48  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
49  
50  /**
51   * 
52   * @author Thomas Spiegl (latest modification by $Author$)
53   * @author Anton Koinov
54   * @author Martin Marinschek
55   * @version $Revision$ $Date$
56   */
57  @JSFRenderer(renderKitId = "HTML_BASIC", family = "jakarta.faces.Output", type = "jakarta.faces.Label")
58  public class HtmlLabelRenderer extends HtmlRenderer
59  {
60      private static final Logger log = Logger.getLogger(HtmlLabelRenderer.class.getName());
61  
62      @Override
63      protected boolean isCommonPropertiesOptimizationEnabled(FacesContext facesContext)
64      {
65          return true;
66      }
67  
68      @Override
69      protected boolean isCommonEventsOptimizationEnabled(FacesContext facesContext)
70      {
71          return true;
72      }
73  
74      @Override
75      public void decode(FacesContext context, UIComponent component)
76      {
77          // Check for npe
78          super.decode(context, component);
79          
80          HtmlRendererUtils.decodeClientBehaviors(context, component);
81      }
82  
83      @Override
84      public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
85      {
86          super.encodeBegin(facesContext, uiComponent); // check for NP
87  
88          ResponseWriter writer = facesContext.getResponseWriter();
89  
90          Map<String, List<ClientBehavior>> behaviors = null;
91          if (uiComponent instanceof ClientBehaviorHolder)
92          {
93              behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
94              if (!behaviors.isEmpty())
95              {
96                  ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, writer);
97              }
98          }
99          
100         encodeBefore(facesContext, writer, uiComponent);
101 
102         writer.startElement(HTML.LABEL_ELEM, uiComponent);
103         if (uiComponent instanceof ClientBehaviorHolder)
104         {
105             if (!behaviors.isEmpty())
106             {
107                 HtmlRendererUtils.writeIdAndName(writer, uiComponent, facesContext);
108             }
109             else
110             {
111                 HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
112             }
113             long commonPropertiesMarked = 0L;
114             if (isCommonPropertiesOptimizationEnabled(facesContext))
115             {
116                 commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
117             }
118             if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
119             {
120                 CommonPropertyUtils.renderEventProperties(writer, 
121                         commonPropertiesMarked, uiComponent);
122                 CommonPropertyUtils.renderFocusBlurEventProperties(writer,
123                         commonPropertiesMarked, uiComponent);
124             }
125             else
126             {
127                 if (isCommonEventsOptimizationEnabled(facesContext))
128                 {
129                     Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(uiComponent);
130                     CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
131                             commonPropertiesMarked, commonEventsMarked, uiComponent, behaviors);
132                     CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
133                         facesContext, writer, commonPropertiesMarked, commonEventsMarked, uiComponent, behaviors);
134                 }
135                 else
136                 {
137                     HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, uiComponent, behaviors);
138                     HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchangeAndOnselect(
139                             facesContext, writer,
140                             uiComponent, behaviors);
141                 }
142             }
143             if (isCommonPropertiesOptimizationEnabled(facesContext))
144             {
145                 CommonPropertyUtils.renderLabelPassthroughPropertiesWithoutEvents(writer, 
146                         commonPropertiesMarked, uiComponent);
147             }
148             else
149             {
150                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
151                                                        HTML.LABEL_PASSTHROUGH_ATTRIBUTES_WITHOUT_EVENTS);
152             }
153         }
154         else
155         {
156             HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
157             if (isCommonPropertiesOptimizationEnabled(facesContext))
158             {
159                 CommonPropertyUtils.renderLabelPassthroughProperties(writer, 
160                         CommonPropertyUtils.getCommonPropertiesMarked(uiComponent), uiComponent);
161             }
162             else
163             {
164                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.LABEL_PASSTHROUGH_ATTRIBUTES);
165             }
166         }
167 
168         String forAttr = getFor(uiComponent);
169 
170         if (forAttr != null)
171         {
172             writer.writeAttribute(HTML.FOR_ATTR, getClientId(facesContext, uiComponent, forAttr), JSFAttr.FOR_ATTR);
173         }
174         else
175         {
176             if (log.isLoggable(Level.WARNING))
177             {
178                 log.warning("Attribute 'for' of label component with id " + uiComponent.getClientId(facesContext)
179                         + " is not defined");
180             }
181         }
182 
183         // MyFaces extension: Render a label text given by value
184         // TODO: Move to extended component
185         if (uiComponent instanceof ValueHolder)
186         {
187             String text = RendererUtils.getStringValue(facesContext, uiComponent);
188             if (text != null)
189             {
190                 boolean escape;
191                 if (uiComponent instanceof HtmlOutputLabel || uiComponent instanceof EscapeCapable)
192                 {
193                     escape = ((HtmlOutputLabel)uiComponent).isEscape();
194                 }
195                 else
196                 {
197                     escape = RendererUtils.getBooleanAttribute(uiComponent,
198                                                                org.apache.myfaces.shared.renderkit.JSFAttr.ESCAPE_ATTR,
199                                                                true); //default is to escape
200                 }                
201                 if (escape)
202                 {
203                     writer.writeText(text, org.apache.myfaces.shared.renderkit.JSFAttr.VALUE_ATTR);
204                 }
205                 else
206                 {
207                     writer.write(text);
208                 }
209             }
210         }
211 
212         writer.flush(); // close start tag
213 
214         encodeAfterStart(facesContext, writer, uiComponent);
215     }
216 
217     /**
218      * @throws IOException  
219      */
220     protected void encodeAfterStart(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent)
221         throws IOException
222     {
223     }
224 
225     /**
226      * @throws IOException  
227      */
228     protected void encodeBefore(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent)
229         throws IOException
230     {
231     }
232 
233     protected String getFor(UIComponent component)
234     {
235         if (component instanceof HtmlOutputLabel)
236         {
237             return ((HtmlOutputLabel)component).getFor();
238         }
239 
240         return (String)component.getAttributes().get(JSFAttr.FOR_ATTR);
241 
242     }
243 
244     private static final Set<SearchExpressionHint> EXPRESSION_HINTS =
245             EnumSet.of(SearchExpressionHint.RESOLVE_SINGLE_COMPONENT, SearchExpressionHint.IGNORE_NO_RESULT);
246     
247     protected String getClientId(FacesContext facesContext, UIComponent uiComponent, String forAttr)
248     {
249         SearchExpressionContext searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
250                 facesContext, uiComponent, EXPRESSION_HINTS, null);
251 
252         return facesContext.getApplication().getSearchExpressionHandler().resolveClientId(
253                 searchExpressionContext, forAttr);
254     }
255 
256     @Override
257     public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
258     {
259         super.encodeEnd(facesContext, uiComponent); // check for NP
260 
261         ResponseWriter writer = facesContext.getResponseWriter();
262 
263         encodeBeforeEnd(facesContext, writer, uiComponent);
264 
265         writer.endElement(HTML.LABEL_ELEM);
266 
267         encodeAfter(facesContext, writer, uiComponent);
268     }
269 
270     /**
271      * @throws IOException  
272      */
273     protected void encodeBeforeEnd(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent)
274         throws IOException
275     {
276     }
277 
278     /**
279      * @throws IOException  
280      */
281     protected void encodeAfter(FacesContext facesContext, ResponseWriter writer, UIComponent uiComponent)
282         throws IOException
283     {
284     }
285 }