001    package org.apache.myfaces.tobago.util;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.commons.beanutils.ConstructorUtils;
021    import org.apache.myfaces.tobago.application.LabelValueBindingFacesMessage;
022    import org.apache.myfaces.tobago.application.LabelValueExpressionFacesMessage;
023    import org.apache.myfaces.tobago.compat.FacesUtils;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    
027    import javax.faces.application.FacesMessage;
028    import javax.faces.component.UIComponent;
029    import javax.faces.context.FacesContext;
030    import java.text.MessageFormat;
031    import java.util.Locale;
032    import java.util.MissingResourceException;
033    import java.util.ResourceBundle;
034    
035    /**
036     * Utility to create and add {@link FacesMessage} object to the context.
037     * The message will be internationalized with a bundle in the following order:
038     * <ol>
039     * <li>Application bundle</li>
040     * <li>Tobago bundle</li>
041     * <li>Default JSF bundle</li>
042     * </ol>
043     */
044    public class MessageUtils {
045    
046      private static final Logger LOG = LoggerFactory.getLogger(MessageUtils.class);
047    
048      private static final String DETAIL_SUFFIX = "_detail";
049    
050      public static void addMessage(
051          FacesContext facesContext, UIComponent component, FacesMessage.Severity severity,
052          String messageId, Object[] args) {
053        facesContext.addMessage(component.getClientId(facesContext),
054            getMessage(facesContext, facesContext.getViewRoot().getLocale(), severity, messageId, args));
055      }
056    
057      public static FacesMessage getMessage(
058          FacesContext facesContext, Locale locale,
059          FacesMessage.Severity severity, String messageId, Object... args) {
060    
061        ResourceBundle appBundle = getApplicationBundle(facesContext, locale);
062        String summary = getBundleString(appBundle, messageId);
063        String detail = getBundleString(appBundle, messageId + DETAIL_SUFFIX);
064    
065        if (summary == null || detail == null) {
066          ResourceBundle tobagoBundle = getTobagoBundle();
067          if (summary == null) {
068            summary = getBundleString(tobagoBundle, messageId);
069          }
070          if (detail == null) {
071            detail = getBundleString(tobagoBundle, messageId + DETAIL_SUFFIX);
072          }
073    
074          if (summary == null || detail == null) {
075            ResourceBundle defBundle = getDefaultBundle(facesContext, locale);
076            if (summary == null) {
077              summary = getBundleString(defBundle, messageId);
078            }
079            if (detail == null) {
080              detail = getBundleString(defBundle, messageId + DETAIL_SUFFIX);
081            }
082          }
083        }
084    
085        if (summary == null && detail == null) {
086          //Neither detail nor summary found
087          facesContext.getExternalContext().log("No message with id " + messageId + " found in any bundle");
088          return new FacesMessage(severity, messageId, null);
089        }
090    
091        if (FacesUtils.supportsEL()) {
092          if (args != null && args.length > 0) {
093            MessageFormat format;
094            if (summary != null) {
095              format = new MessageFormat(summary, locale);
096              summary = format.format(args);
097            }
098    
099            if (detail != null) {
100              format = new MessageFormat(detail, locale);
101              detail = format.format(args);
102            }
103          }
104          return new LabelValueExpressionFacesMessage(severity, summary, detail);
105        } else {
106          return new LabelValueBindingFacesMessage(severity, summary, detail, locale, args);
107        }
108      }
109    
110      private static String getBundleString(ResourceBundle bundle, String key) {
111        try {
112          return bundle == null ? null : bundle.getString(key);
113        } catch (MissingResourceException e) {
114          return null;
115        }
116      }
117    
118      private static ResourceBundle getApplicationBundle(FacesContext facesContext, Locale locale) {
119        String bundleName = facesContext.getApplication().getMessageBundle();
120        return bundleName != null ? getBundle(facesContext, locale, bundleName) : null;
121      }
122    
123      private static ResourceBundle getTobagoBundle() {
124        // XXX This is ugly, can be removed after merging tobago-jsf-compat to tobago-core
125        try {
126          Class clazz = Class.forName("org.apache.myfaces.tobago.context.TobagoResourceBundle");
127          Object bundle = ConstructorUtils.invokeConstructor(clazz, new Object[0]);
128          return (ResourceBundle) bundle;
129        } catch (Exception e) {
130          LOG.error("Can't create TobagoResourceBundle, but it should be in the core.", e);
131          return null; // should not be possible.
132        }
133      }
134    
135      private static ResourceBundle getDefaultBundle(FacesContext facesContext, Locale locale) {
136        return getBundle(facesContext, locale, FacesMessage.FACES_MESSAGES);
137      }
138    
139      private static ResourceBundle getBundle(FacesContext facesContext, Locale locale, String bundleName) {
140        try {
141          return ResourceBundle.getBundle(bundleName, locale, MessageUtils.class.getClassLoader());
142        } catch (MissingResourceException ignore2) {
143          try {
144            return ResourceBundle.getBundle(bundleName, locale, Thread.currentThread().getContextClassLoader());
145          } catch (MissingResourceException damned) {
146            facesContext.getExternalContext().log("resource bundle " + bundleName + " could not be found");
147            return null;
148          }
149        }
150      }
151    
152      public static String getLabel(FacesContext facesContext, UIComponent component) {
153        Object label = component.getAttributes().get("label");
154        if (label != null) {
155          return label.toString();
156        }
157        if (FacesUtils.hasValueBindingOrValueExpression(component, "label")) {
158          return FacesUtils.getExpressionString(component, "label");
159        }
160        return component.getClientId(facesContext);
161      }
162    
163      public static String getFormatedMessage(String message, Locale locale, Object... args) {
164        if (args != null && args.length > 0 && message != null) {
165          MessageFormat format = new MessageFormat(message, locale);
166          return format.format(args);
167        }
168        return message;
169      }
170    }