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.trinidad.util;
20  
21  import javax.faces.application.FacesMessage;
22  import javax.faces.component.UIComponent;
23  import javax.faces.context.FacesContext;
24  
25  import javax.el.ValueExpression;
26  
27  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
28  
29  /**
30   * Factory class to return {@link FacesMessage} objects.
31   * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/util/MessageFactory.java#0 $) $Date: 30-nov-2005.11:48:33 $
32   */
33  public final class MessageFactory
34  {
35    private MessageFactory()
36    {
37    }
38  
39    /**
40     * Creates a FacesMessage for the given Throwable.
41     * The severity is {@link FacesMessage#SEVERITY_ERROR}
42     * @param error The root cause of this Exception will be used.
43     */
44    public static FacesMessage getMessage(Throwable error)
45    {
46      _LOG.fine(error);
47  
48      Throwable unwrap = ComponentUtils.unwrap(error);
49      String detail = unwrap.getLocalizedMessage();
50      if (detail == null)
51      {
52        // this is unusual. It is probably an unexpected RT error
53        // in the framework. log at warning level:
54        detail = unwrap.getClass().getName();
55        _LOG.warning(error);
56      }
57      // bug 4733165:
58      FacesMessage message =
59        new FacesMessage(FacesMessage.SEVERITY_ERROR, null, detail);
60      return message;
61    }
62  
63    /**
64     * Creates a FacesMessage containing formatted text and
65     * a severity of SEVERITY_ERROR.
66     *
67     * @param context faces context
68     * @param messageId the bundle key for the translated string
69     * @param parameters parameters to be substituted in the placeholders
70     *        of the translated string.
71     * @return a FacesMessage object
72     */
73    public static FacesMessage getMessage(
74      FacesContext context,
75      String messageId,
76      Object[] parameters
77      )
78    {
79        return  getMessage(context, FacesMessage.SEVERITY_ERROR,
80                           messageId, parameters, null);
81    }
82  
83    /**
84     * Creates a FacesMessage containing formatted text and
85     * a severity of SEVERITY_ERROR.
86     *
87     * @param context faces context
88     * @param messageId the bundle key for the translated string
89     * @param parameters parameters to be substituted in the placeholders
90     *        of the translated string.
91     * @param component The component generating the message
92     *                  (allows label tracking)
93     * @return a FacesMessage object
94     */
95    public static FacesMessage getMessage(
96      FacesContext context,
97      String messageId,
98      Object[] parameters,
99      UIComponent component
100     )
101   {
102     return _createFacesMessage(context, FacesMessage.SEVERITY_ERROR, messageId,
103                                parameters, _getLabel(component));
104   }
105 
106 
107   /**
108    * Creates a FacesMessage containing formatted text and
109    * a severity of SEVERITY_ERROR.
110    *
111    * @param context faces context
112    * @param messageId the bundle key for the translated string
113    * @param parameters parameters to be substituted in the placeholders
114    *        of the translated string.
115    * @param label The label of the component generating the message
116    * @return a FacesMessage object
117    */
118   public static FacesMessage getMessage(
119     FacesContext context,
120     String messageId,
121     Object[] parameters,
122     Object label
123     )
124   {
125     return _createFacesMessage(context, FacesMessage.SEVERITY_ERROR, messageId,
126                                parameters, label);
127   }
128 
129   /**
130    * Creates a FacesMessage containing formatted text.
131    *
132    * @param context faces context
133    * @param severity the message severity
134    * @param messageId the bundle key for the translated string
135    * @param parameters parameters to be substituted in the placeholders
136    *        of the translated string.
137    * @return a FacesMessage object
138    */
139   public static FacesMessage getMessage(
140     FacesContext context,
141     FacesMessage.Severity severity,
142     String messageId,
143     Object[] parameters
144     )
145   {
146       return  getMessage(context, severity,
147                          messageId, parameters, null);
148   }
149 
150   /**
151    * Creates a FacesMessage containing formatted text.
152    *
153    * @param context faces context
154    * @param severity the message severity
155    * @param messageId the bundle key for the translated string
156    * @param parameters parameters to be substituted in the placeholders
157    *        of the translated string.
158    * @param component The component generating the message
159    *                  (allows label tracking)
160    * @return a FacesMessage object
161    */
162   public static FacesMessage getMessage(
163     FacesContext context,
164     FacesMessage.Severity severity,
165     String messageId,
166     Object[] parameters,
167     UIComponent component
168     )
169   {
170     return _createFacesMessage(context, severity, messageId,
171                                parameters, _getLabel(component));
172   }
173 
174 
175  /**
176    * Creates a FacesMessage without any parameters, and
177    * a severity of SEVERITY_ERROR.
178    *
179    * @param context faces context
180    * @param messageId the bundle key for the translated string
181    * @return a FacesMessage object
182    */
183   public static FacesMessage getMessage(
184     FacesContext context,
185     String messageId
186     )
187   {
188     return getMessage(context, messageId, (Object[]) null, (UIComponent) null);
189   }
190 
191  /**
192    * Returns the localized string
193    * @param context
194    * @param messageId
195    * @return String
196    */
197   public static String getString(
198     FacesContext context,
199     String messageId
200     )
201     {
202       return  LocaleUtils.__getSummaryString(context, messageId);
203     }
204 
205 
206  /**
207    * Creates a FacesMessage without any parameters, and
208    * a severity of SEVERITY_ERROR.
209    *
210    * @param context faces context
211    * @param messageId the bundle key for the translated string
212    * @param component The component generating the message
213    *                  (allows label tracking)
214    * @return a FacesMessage object
215    */
216   public static FacesMessage getMessage(
217     FacesContext context,
218     String messageId,
219     UIComponent component
220     )
221   {
222     return getMessage(context, messageId, (Object[]) null, component);
223   }
224 
225   /**
226    * Creates a FacesMessage using a single parameter.
227    *
228    * @param context faces context
229    * @param messageId the bundle key for the translated string
230    * @param parameter parameter to be substituted for "{0}"
231    * @param label the label of the creating component
232    * @return a FacesMessage object
233    */
234   public static FacesMessage getMessage(
235     FacesContext context,
236     String messageId,
237     Object parameter,
238     Object label
239     )
240   {
241     return getMessage(context, messageId, new Object[]{parameter}, label);
242   }
243 
244   /**
245    * Creates a FacesMessage using a single parameter.
246    *
247    * @param context faces context
248    * @param messageId the bundle key for the translated string
249    * @param parameter parameter to be substituted for "{0}"
250    * @return a FacesMessage object
251    */
252   public static FacesMessage getMessage(
253     FacesContext context,
254     String messageId,
255     Object parameter
256     )
257   {
258     return getMessage(context, messageId, new Object[]{parameter});
259   }
260 
261   /**
262    * <p>Gets the translation summary and detail text from the message bundle.
263    * If <code>customMessagePattern</code> is set, then it is used as the
264    * detail part of the faces message. The summary and detail string are
265    * formatted based on the supplied <code>parameters</code>. Returns a
266    * FacesMessage using the formatted summary and detail message with
267    * severity set to error.</p>
268    *
269    * @param context faces context
270    * @param messageId the bundle key for the translated string
271    * @param customMessagePattern Custom error message. It can also contain
272    *        placeholders which will be formatted with the supplied parameters.
273    *        This customizes the detail part of the {@link FacesMessage}.
274    *        If value is null. Then picksup translation summary and detail from
275    *        the bundle, which is then formatted and used in construction of faces
276    *        message.
277    * @param parameters parameters to be substituted in the placeholders
278    *        of the translated string.
279    * @return a FacesMessage object
280    */
281   public static FacesMessage getMessage(
282     FacesContext context,
283     String messageId,
284     Object customMessagePattern,
285     Object[] parameters)
286   {
287     return getMessage(context, messageId, customMessagePattern,
288                       parameters, null);
289   }
290 
291   /**
292    * <p>Gets the translation summary and detail text from the message bundle.
293    * If <code>customMessagePattern</code> is set, then it is used as the
294    * detail part of the faces message. The summary and detail string are
295    * formatted based on the supplied <code>parameters</code>. Returns a
296    * FacesMessage using the formatted summary and detail message with
297    * severity set to error.</p>
298    *
299    * @param context faces context
300    * @param messageId the bundle key for the translated string
301    * @param customMessagePattern Custom error message. It can also contain
302    *        placeholders which will be formatted with the supplied parameters.
303    *        This customizes the detail part of the {@link FacesMessage}.
304    *        If value is null. Then picksup translation summary and detail from
305    *        the bundle, which is then formatted and used in construction of faces
306    *        message.
307    * @param parameters parameters to be substituted in the placeholders
308    *        of the translated string.
309    * @param component The component generating the message
310    *                  (allows label tracking)
311    * @return a FacesMessage object
312    */
313   public static FacesMessage getMessage(
314     FacesContext context,
315     String messageId,
316     Object customMessagePattern,
317     Object[] parameters,
318     UIComponent component)
319   {
320     return getMessage(context, messageId, customMessagePattern,
321                       parameters, _getLabel(component));
322   }
323 
324   /**
325    * <p>Gets the translation summary and detail text from the message bundle.
326    * If <code>customMessagePattern</code> is set, then it is used as the
327    * detail part of the faces message. The summary and detail string are
328    * formatted based on the supplied <code>parameters</code>. Returns a
329    * FacesMessage using the formatted summary and detail message with
330    * severity set to error.</p>
331    *
332    * @param context faces context
333    * @param messageId the bundle key for the translated string
334    * @param customMessagePattern Custom error message. It can also contain
335    *        placeholders which will be formatted with the supplied parameters.
336    *        This customizes the detail part of the {@link FacesMessage}.
337    *        If value is null. Then picksup translation summary and detail from
338    *        the bundle, which is then formatted and used in construction of faces
339    *        message.
340    * @param parameters parameters to be substituted in the placeholders
341    *        of the translated string.
342    * @param label the label of the creating component
343    * @return a FacesMessage object
344    */
345   public static FacesMessage getMessage(
346     FacesContext context,
347     String messageId,
348     Object customMessagePattern,
349     Object[] parameters,
350     Object label)
351   {
352     if (null != customMessagePattern)
353     {
354       String summary = LocaleUtils.__getSummaryString(context, messageId);
355 
356       ErrorMessages msgs = _getErrorMessage(summary, customMessagePattern,
357                                             parameters);
358 
359       return _createFacesMessage(FacesMessage.SEVERITY_ERROR, msgs, label);
360     }
361 
362     return _createFacesMessage(context, FacesMessage.SEVERITY_ERROR, messageId,
363                                parameters, label);
364   }
365 
366   private static FacesMessage _createFacesMessage(
367     FacesContext context,
368     FacesMessage.Severity severity,
369     String messageId,
370     Object[] parameters,
371     Object label
372     )
373   {
374     ErrorMessages errMsgs
375       = LocaleUtils.__getErrorMessages(context, messageId);
376 
377     ErrorMessages formattedErrMsgs
378       = _getBindingOrFormattedErrorMessages(errMsgs, parameters);
379     return _createFacesMessage(severity, formattedErrMsgs, label);
380   }
381 
382 //  private static FacesMessage _createFacesMessage(
383 //    FacesMessage.Severity severity,
384 //    ErrorMessages messageStrings,
385 //    UIComponent component
386 //    )
387 //  {
388 //    return _createFacesMessage(severity, messageStrings, _getLabel(component));
389 //  }
390 
391   private static FacesMessage _createFacesMessage(
392     FacesMessage.Severity severity,
393     ErrorMessages messageStrings,
394     Object label
395     )
396   {
397     if (messageStrings instanceof BindingErrorMessages)
398     {
399        return new BindingFacesMessage(severity, messageStrings, label);
400     }
401 
402     String summary = messageStrings.getMessage();
403     String detail  = messageStrings.getDetailMessage();
404 
405     return new LabeledFacesMessage(severity, summary, detail, label);
406   }
407 
408   private static Object[] _getProcessedBindings(
409     FacesContext facesContext,
410     Object[] parameters)
411   {
412     FacesContext context = facesContext;
413     Object[] resolvedParameters = new Object[parameters.length];
414     for (int i = 0; i < parameters.length; i++)
415     {
416       Object o = parameters[i];
417       if (o instanceof ValueExpression)
418       {
419         if (context == null)
420           context = FacesContext.getCurrentInstance();
421         o = ((ValueExpression) o).getValue(context.getELContext());
422       }
423 
424       resolvedParameters[i] = o;
425     }
426     return resolvedParameters;
427   }
428 
429   private static String _getFormattedString(String pattern, Object parameters[])
430   {
431     if (parameters == null)
432       return pattern;
433 
434     FastMessageFormat formatter = new FastMessageFormat(pattern);
435     String fmtedMsgStr = formatter.format(parameters);
436     return fmtedMsgStr;
437   }
438 
439   private static boolean _containsBinding(Object[] parameters)
440   {
441     if (parameters == null)
442       return false;
443 
444     for (int i = 0; i < parameters.length; i++)
445     {
446       if (parameters[i] instanceof ValueExpression)
447         return true;
448     }
449 
450     return false;
451   }
452 
453   //A Factory
454   private static ErrorMessages _getErrorMessage(
455     String summary,
456     Object customMessagePattern,
457     Object[] parameters)
458   {
459     _assertIsValidCustomMessageType(customMessagePattern);
460     boolean isCustomMsgValueBound = (customMessagePattern instanceof ValueExpression);
461     boolean containsBinding = _containsBinding(parameters);
462     if (isCustomMsgValueBound || containsBinding)
463     {
464       if (!(isCustomMsgValueBound))
465       {
466         String customMesg = (String)customMessagePattern;
467         return new BindingErrorMessages(summary, customMesg, parameters);
468       }
469       else
470       {
471         ValueExpression customMessage = (ValueExpression)customMessagePattern;
472         return new CustomDetailErrorMessage(summary, customMessage,
473                                             parameters, containsBinding);
474       }
475     }
476     else
477     {
478       String detailMsgPattern = (String)customMessagePattern;
479       ErrorMessages errorMsg = new FormattedErrorMessages(summary,
480                                                         detailMsgPattern,
481                                                         parameters);
482       return errorMsg;
483     }
484   }
485 
486   private static Object _getLabel(UIComponent component)
487   {
488     Object o = null;
489     if (component != null)
490     {
491       o = component.getAttributes().get("label");
492       if (o == null)
493         o = component.getValueExpression ("label");
494     }
495     return o;
496   }
497 
498   private static void _assertIsValidCustomMessageType(Object customMessagePattern)
499   {
500     if (!(customMessagePattern instanceof ValueExpression  ||
501          customMessagePattern instanceof String))
502          throw new IllegalArgumentException(_LOG.getMessage(
503            "CUSTOM_MESSAGE_SHOULD_BE_VALUEBINDING_OR_STRING_TYPE"));
504   }
505 
506   private static ErrorMessages _getBindingOrFormattedErrorMessages(
507     ErrorMessages unFormattedErrorMessages,
508     Object[] parameters)
509   {
510 
511     if (!_containsBinding(parameters))
512       return new FormattedErrorMessages(unFormattedErrorMessages.getMessage(),
513                                         unFormattedErrorMessages.getDetailMessage(),
514                                         parameters);
515     else
516       return new BindingErrorMessages(unFormattedErrorMessages.getMessage(),
517                                       unFormattedErrorMessages.getDetailMessage(),
518                                       parameters);
519   }
520 
521   private static class BindingFacesMessage extends LabeledFacesMessage
522   {
523     public BindingFacesMessage(FacesMessage.Severity severity,
524                                ErrorMessages messageStrings,
525                                Object label)
526     {
527       super(severity, null, null, label);
528       _messageStrings = messageStrings;
529     }
530 
531     @Override
532     public String getDetail()
533     {
534       return _messageStrings.getDetailMessage();
535     }
536 
537     @Override
538     public String getSummary()
539     {
540       return _messageStrings.getMessage();
541     }
542 
543     public void setDetail()
544     {
545       throw new UnsupportedOperationException();
546     }
547 
548     public void setSummary()
549     {
550       throw new UnsupportedOperationException();
551     }
552 
553     private final ErrorMessages _messageStrings;
554     private static final long serialVersionUID = 1L;
555   }
556 
557   private static class BindingErrorMessages extends ErrorMessages
558   {
559     BindingErrorMessages(
560       String messageFormat, String detailMessageFormat, Object[] parameters)
561     {
562       super(messageFormat, detailMessageFormat);
563       _parameters = parameters;
564       if (parameters == null)
565         throw new NullPointerException();
566     }
567 
568     @Override
569     public String getMessage()
570     {
571       String pattern = super.getMessage();
572       return _getFormattedString(pattern, getResolvedParameters());
573     }
574 
575     @Override
576     public String getDetailMessage()
577     {
578       String pattern = super.getDetailMessage();
579       return _getFormattedString(pattern, getResolvedParameters());
580     }
581 
582     protected final Object[] getResolvedParameters()
583     {
584       return _getProcessedBindings(null, _parameters);
585     }
586 
587     protected final Object[] getParameters()
588     {
589       return _parameters;
590     }
591 
592     private Object[] _parameters;
593     private static final long serialVersionUID = 1L;
594   }
595 
596   // Though it may not be exactly correct to extend BindingErrorMessages
597   // as parameters might not have value binding. This inheritance keeps
598   // it simple.
599   private static class CustomDetailErrorMessage extends BindingErrorMessages
600   {
601 
602     CustomDetailErrorMessage(
603       String messageFormat,
604       ValueExpression customDetailErrorMessage,
605       Object[] parameters,
606       boolean hasBoundParameters
607     )
608     {
609       super(messageFormat, null, parameters);
610       _customDetailErrorMessage = customDetailErrorMessage;
611       _hasBoundParameters = hasBoundParameters;
612     }
613 
614     // Currently only detail message can be customized. So we override the
615     // detail message. If summary is to be overridden we have to do the
616     // same to it also.
617     @Override
618     public String getDetailMessage()
619     {
620       FacesContext context    = FacesContext.getCurrentInstance();
621       String detailMsgPattern = (String)_customDetailErrorMessage.getValue(context.getELContext());
622       if(detailMsgPattern == null)
623       {
624         // Set a default message that might get used by FacesException
625         // constructor for example. This will often happen because
626         // ValidatorException constructor will call this method to
627         // get the exception message for its parent. So by default
628         // we'll use the EL String.
629         // Note that by default
630         detailMsgPattern = _customDetailErrorMessage.getExpressionString();
631 
632         // Since that string will get parsed by FastMessageFormat, the { }
633         // of the EL must be escaped
634         //detailMsgPattern = '\'' + detailMsgPattern + '\'';
635 
636         // No need to format this string, just return it here.
637         return detailMsgPattern;
638       }
639 
640       Object[] params = super.getParameters();
641 
642       if (_hasBoundParameters)
643          params = getResolvedParameters();
644 
645       return _getFormattedString(detailMsgPattern, params);
646     }
647 
648     private ValueExpression _customDetailErrorMessage;
649     private boolean _hasBoundParameters;
650     private static final long serialVersionUID = 1L;
651   }
652 
653   private static class FormattedErrorMessages extends ErrorMessages
654   {
655     FormattedErrorMessages(String summary, String detail, Object[] parameters)
656     {
657       super(summary, detail);
658       _parameters = parameters;
659     }
660 
661     @Override
662     public String getMessage()
663     {
664       return _getFormattedString(super.getMessage(), _parameters);
665     }
666 
667     @Override
668     public String getDetailMessage()
669     {
670       return _getFormattedString(super.getDetailMessage(), _parameters);
671     }
672 
673     Object[] _parameters;
674     private static final long serialVersionUID = 1L;
675   }
676 
677   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(MessageFactory.class);
678 }