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.ext;
20  
21  import java.io.IOException;
22  import java.text.MessageFormat;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.faces.application.FacesMessage;
29  import javax.faces.component.NamingContainer;
30  import javax.faces.component.UIColumn;
31  import javax.faces.component.UIComponent;
32  import javax.faces.component.ValueHolder;
33  import javax.faces.component.behavior.ClientBehavior;
34  import javax.faces.component.behavior.ClientBehaviorHolder;
35  import javax.faces.component.html.HtmlOutputLabel;
36  import javax.faces.component.html.HtmlOutputText;
37  import javax.faces.context.FacesContext;
38  import javax.faces.context.ResponseWriter;
39  
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.apache.myfaces.component.html.ext.HtmlMessage;
43  import org.apache.myfaces.component.html.ext.HtmlMessages;
44  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
45  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
46  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlMessageRendererBase;
47  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
48  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.ResourceUtils;
49  
50  /**
51   * @JSFRenderer
52   *   renderKitId = "HTML_BASIC"
53   *   family = "javax.faces.Message"
54   *   type = "org.apache.myfaces.Message"
55   * 
56   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
57   * @version $Revision: 940144 $ $Date: 2010-05-01 20:39:32 -0500 (Sáb, 01 May 2010) $
58   */
59  public class HtmlMessageRenderer
60          extends HtmlMessageRendererBase
61  {
62      private static final Log log = LogFactory.getLog(HtmlMessageRenderer.class);
63  
64      private static final String OUTPUT_LABEL_MAP = HtmlMessageRenderer.class.getName() + ".OUTPUT_LABEL_MAP";
65  
66      @Override
67      public void decode(FacesContext context, UIComponent component)
68      {
69          super.decode(context, component);
70          
71          HtmlRendererUtils.decodeClientBehaviors(context, component);
72      }
73  
74      public void encodeEnd(FacesContext facesContext, UIComponent component)
75              throws IOException
76      {
77          super.encodeEnd(facesContext, component);   //check for NP
78          
79          Map<String, List<ClientBehavior>> behaviors = null;
80          if (component instanceof ClientBehaviorHolder)
81          {
82              behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
83              if (!behaviors.isEmpty())
84              {
85                  ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
86              }
87          }
88  
89          boolean forceSpan = false;
90          if (component instanceof HtmlMessage
91                  && ((HtmlMessage) component).getForceSpan())
92          {
93              forceSpan = true;
94          }
95  
96          renderMessage(facesContext, component, forceSpan, true);
97  
98          /*
99          if (component instanceof HtmlMessage
100                 && ((HtmlMessage)component).getForceSpan())
101         {
102             String forAttr = getFor(component);
103             HtmlMessage htmlMessage = (HtmlMessage) component;
104 
105             UIComponent forComponent = component.findComponent(forAttr);
106 
107             if (forComponent != null)
108             {
109                 String forCompclientId = forComponent.getClientId(facesContext);
110 
111                 ResponseWriter writer = facesContext.getResponseWriter();
112                 writer.startElement(HTML.SPAN_ELEM, null);
113                 writer.writeAttribute(HTML.ID_ATTR, forCompclientId + "_msgFor", null);
114                 if(htmlMessage.getStyleClass()!=null)
115                 writer.writeAttribute(HTML.CLASS_ATTR,htmlMessage.getStyleClass(),null);
116                 if(htmlMessage.getStyle()!=null)
117                 writer.writeAttribute(HTML.STYLE_ATTR,htmlMessage.getStyle(),null);
118                 writer.endElement(HTML.SPAN_ELEM);
119             }
120         }*/
121     }
122 
123     protected String getSummary(FacesContext facesContext,
124                                 UIComponent message,
125                                 FacesMessage facesMessage,
126                                 String msgClientId)
127     {
128         String msgSummary = facesMessage.getSummary();
129         if (msgSummary == null) return null;
130 
131         String inputLabel = null;
132         if (msgClientId != null) inputLabel = findInputLabel(facesContext, msgClientId);
133         if (inputLabel == null) inputLabel = "";
134 
135         if(((message instanceof HtmlMessages && ((HtmlMessages) message).isReplaceIdWithLabel()) ||
136                 (message instanceof HtmlMessage && ((HtmlMessage) message).isReplaceIdWithLabel()))&&
137                 inputLabel.length()!=0)
138             msgSummary = msgSummary.replaceAll(findInputId(facesContext, msgClientId),inputLabel);
139 
140 
141         String summaryFormat;
142         if (message instanceof HtmlMessage)
143         {
144             summaryFormat = ((HtmlMessage)message).getSummaryFormat();
145         }
146         else
147         {
148             summaryFormat = (String)message.getAttributes().get("summaryFormat");
149         }
150 
151         if (summaryFormat == null) return msgSummary;
152 
153         MessageFormat format = new MessageFormat(summaryFormat, facesContext.getViewRoot().getLocale());
154 
155         return format.format(new Object[] {msgSummary, inputLabel});
156     }
157 
158     protected String getDetail(FacesContext facesContext,
159                                UIComponent message,
160                                FacesMessage facesMessage,
161                                String msgClientId)
162     {
163         String msgDetail = facesMessage.getDetail();
164         if (msgDetail == null) return null;
165 
166         String inputLabel = null;
167         if (msgClientId != null) inputLabel = findInputLabel(facesContext, msgClientId);
168         if (inputLabel == null) inputLabel = "";
169 
170         if(((message instanceof HtmlMessages && ((HtmlMessages) message).isReplaceIdWithLabel()) ||
171                 (message instanceof HtmlMessage && ((HtmlMessage) message).isReplaceIdWithLabel()))&&
172                 inputLabel.length()!=0)
173             msgDetail = msgDetail.replaceAll(findInputId(facesContext, msgClientId),inputLabel);
174 
175         String detailFormat;
176         if (message instanceof HtmlMessage)
177         {
178             detailFormat = ((HtmlMessage)message).getDetailFormat();
179         }
180         else
181         {
182             detailFormat = (String)message.getAttributes().get("detailFormat");
183         }
184 
185         if (detailFormat == null) return msgDetail;
186 
187         MessageFormat format = new MessageFormat(detailFormat, facesContext.getViewRoot().getLocale());
188         return format.format(new Object[] {msgDetail, inputLabel});
189     }
190 
191 
192     public static String findInputLabel(FacesContext facesContext, String inputClientId)
193     {
194         Map<String, MessageLabelInfo> outputLabelMap = getOutputLabelMap(facesContext);
195         MessageLabelInfo info = ((MessageLabelInfo)outputLabelMap.get(inputClientId));
196 
197         if(info == null)
198         {
199             UIComponent comp = facesContext.getViewRoot().findComponent(inputClientId);
200 
201             UIComponent parent=comp;
202 
203             while(parent != null && !((parent=parent.getParent())instanceof UIColumn));
204 
205             if(parent != null)
206             {
207                 UIColumn column = (UIColumn) parent;
208 
209                 if(column.getHeader()!=null)
210                 {
211                     UIComponent header = column.getHeader();
212 
213                     return getComponentText(facesContext, header);
214                 }
215             }
216         }
217 
218         return info==null?null:info.getText(facesContext);
219     }
220 
221     public static String findInputId(FacesContext facesContext, String inputClientId)
222     {
223         Map<String, MessageLabelInfo> outputLabelMap = getOutputLabelMap(facesContext);
224         MessageLabelInfo info = ((MessageLabelInfo)outputLabelMap.get(inputClientId));
225 
226         UIComponent comp = null;
227         
228         if(info == null)
229         {
230             comp = facesContext.getViewRoot().findComponent(inputClientId);
231         }
232         else
233         {
234             comp = info.getForComponent();
235         }
236         
237         if ( comp == null ) return null;
238 
239         if ( inputClientId.contains(NamingContainer.SEPARATOR_CHAR + "") ) {
240             // Full client id is being used
241             //
242             return comp.getClientId(facesContext);
243         }
244         else {
245             // Id is being used (or client id is one level deep, which means it doesn't matter)
246             //
247             return comp.getId();
248         }
249     }
250 
251     /**
252      * @param facesContext
253      * @return a Map that reversely maps clientIds of components to their
254      *         corresponding OutputLabel component
255      */
256     @SuppressWarnings("unchecked")
257     private static Map<String, MessageLabelInfo> getOutputLabelMap(FacesContext facesContext)
258     {
259         Map<String, MessageLabelInfo> map = (Map<String, MessageLabelInfo>) facesContext.getExternalContext().getRequestMap().get(OUTPUT_LABEL_MAP);
260         if (map == null)
261         {
262             map = new HashMap<String, MessageLabelInfo>();
263             createOutputLabelMap(facesContext, facesContext.getViewRoot(), map);
264             facesContext.getExternalContext().getRequestMap().put(OUTPUT_LABEL_MAP, map);
265         }
266         return map;
267     }
268 
269     private static void createOutputLabelMap(FacesContext facesContext,
270                                              UIComponent root,
271                                              Map<String, MessageLabelInfo> map)
272     {
273         for (Iterator<UIComponent> it = root.getFacetsAndChildren(); it.hasNext(); )
274         {
275             UIComponent child = (UIComponent)it.next();
276             if (child instanceof HtmlOutputLabel)
277             {
278                 String forAttr = ((HtmlOutputLabel)child).getFor();
279                 if (forAttr != null)
280                 {
281                     UIComponent input = child.findComponent(forAttr);
282                     if (input == null)
283                     {
284                         log.warn("Unable to find component '" + forAttr + "' (calling findComponent on component '" + child.getClientId(facesContext) + "')");
285                     }
286                     else
287                     {
288                         if (child.getValueExpression("value") == null)
289                         {
290                             // If the child uses a ValueExpression, do not evaluate the text
291                             // right now. When getText(FacesContext) is called, do it there.
292                             map.put(input.getClientId(facesContext),
293                                     new MessageDefferedLabelInfo(
294                                             input, child));                            
295                         }
296                         else
297                         {
298                             map.put(input.getClientId(facesContext),
299                                     new MessageTextLabelInfo(
300                                             input,getComponentText(facesContext, (HtmlOutputLabel)child)));
301                         }
302                     }
303                 }
304             }
305             else
306             {
307                 createOutputLabelMap(facesContext, child, map);
308             }
309         }
310     }
311 
312     private static String getComponentText(FacesContext facesContext, UIComponent component)
313     {
314         String text = null;
315 
316         if(component instanceof ValueHolder)
317         {
318             text= RendererUtils.getStringValue(facesContext, component);
319         }
320 
321         if (text == null || text.length() < 1)
322         {
323             StringBuffer buf = new StringBuffer();
324             List<UIComponent> li = component.getChildren();
325 
326             for (int i = 0; i < li.size(); i++)
327             {
328                 UIComponent child = (UIComponent) li.get(i);
329 
330                 if(child instanceof HtmlOutputText)
331                 {
332                     String str = RendererUtils.getStringValue(facesContext, child);
333 
334                     if(str!=null)
335                         buf.append(str);
336                 }
337             }
338 
339             text = buf.toString();
340         }
341         return text;
342     }
343 
344     public static interface MessageLabelInfo
345     {
346         public UIComponent getForComponent();
347         public String getText(FacesContext context);
348     }
349     
350     public final static class MessageTextLabelInfo implements MessageLabelInfo
351     {
352         private final UIComponent _forComponent;
353         private final String _text;
354 
355         public MessageTextLabelInfo(final UIComponent forComponent, final String text)
356         {
357             _forComponent = forComponent;
358             _text = text;
359         }
360 
361         public UIComponent getForComponent()
362         {
363             return _forComponent;
364         }
365 
366         public String getText(FacesContext context)
367         {
368             return _text;
369         }
370     }
371     
372     public final static class MessageDefferedLabelInfo implements MessageLabelInfo
373     {
374         private final UIComponent _forComponent;
375         private final UIComponent _labelComponent;
376         private String _text;
377         
378         public MessageDefferedLabelInfo(final UIComponent forComponent, final UIComponent labelComponent)
379         {
380             _forComponent = forComponent;
381             _labelComponent = labelComponent;
382         }
383 
384         
385         public UIComponent getForComponent()
386         {
387             return _forComponent;
388         }
389 
390         public String getText(FacesContext context)
391         {
392             if (_text == null)
393             {
394                 _text = getComponentText(context, (HtmlOutputLabel)_labelComponent); 
395             }
396             return _text; 
397         }
398     }
399 }