/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.gwt.widgets.client.utils;
import com.google.gwt.http.client.Header;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Window;
import org.pentaho.gwt.widgets.client.i18n.WidgetsLocalizedMessages;
import org.pentaho.gwt.widgets.client.i18n.WidgetsLocalizedMessagesSingleton;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @deprecated Please use org.pentaho.gwt.widgets.client.utils.localization.ResourceBundle instead. We are gradually
* moving to a more logical breakdown of GWT modules for more succinct reuse.
*
* This class is a ResourceBundle for GWT projects. Provided with a resource's base-name it will fetch and
* merge resources as follows:
*
* 1. base-name.properties 2. base-name_xx.properties (where XX = language, such as en) 3.
* base-name_xx_yy.properties (where yy = country, such as US)
*
* When each new resource is fetched it is merged with previous resources. Resource collisions are resolved
* by overwriting existing resources with new resources. In this way we are able to provide language/country
* overrides above the default bundles.
*
* @author Michael D'Amour
*/
@Deprecated
public class MessageBundle {
private static final Map<String, String> bundleCache = new HashMap<String, String>();
private static final WidgetsLocalizedMessages MSGS = WidgetsLocalizedMessagesSingleton.getInstance().getMessages();
public static final String PROPERTIES_EXTENSION = ".properties"; //$NON-NLS-1$
private HashMap<String, String> bundle = new HashMap<String, String>();
private RequestCallback baseCallback = null;
private RequestCallback langCallback = null;
private RequestCallback langCountryCallback = null;
private String path = null;
private String bundleName = null;
private IMessageBundleLoadCallback bundleLoadCallback = null;
private String localeName = "default"; //$NON-NLS-1$
private String currentAttemptUrl = null;
private class FakeResponse extends Response {
private String text;
public FakeResponse( String text ) {
this.text = text;
}
public String getHeader( String arg0 ) {
return null;
}
public Header[] getHeaders() {
return null;
}
public String getHeadersAsString() {
return null;
}
public int getStatusCode() {
return Response.SC_OK;
}
public String getStatusText() {
return null;
}
public String getText() {
return text;
}
}
/**
* The MessageBundle class fetches localized properties files by using the GWT RequestBuilder against the supplied
* path. Ideally the path should be relative, but absolute paths are accepted. When the ResourceBundle has fetched and
* loaded all available resources it will notify the caller by way of IMessageBundleLoadCallback. This is necessary
* due to the asynchronous nature of the loading process. Care should be taken to be sure not to request resources
* until loading has finished as inconsistent and incomplete results will be likely.
*
* @param path
* The path to the resources (mantle/messages)
* @param bundleName
* The base name of the set of resource bundles, for example 'messages'
* @param bundleLoadCallback
* The callback to invoke when the bundle has finished loading
*/
@Deprecated
public MessageBundle( String path, String bundleName, IMessageBundleLoadCallback bundleLoadCallback ) {
this.path = path;
this.bundleName = bundleName;
this.bundleLoadCallback = bundleLoadCallback;
// get the locale meta property if the url parameter is missing
this.localeName = StringUtils.defaultIfEmpty( Window.Location.getParameter( "locale" ), getLanguagePreference() ); //$NON-NLS-1$
initCallbacks();
// decompose locale
// _en_US
// 1. bundleName.properties
// 2. bundleName_en.properties
// 3. bundleName_en_US.properties
// always fetch the base first
currentAttemptUrl = path + bundleName + PROPERTIES_EXTENSION + getUrlExtras();
if ( bundleCache.containsKey( currentAttemptUrl ) ) {
baseCallback.onResponseReceived( null, new FakeResponse( bundleCache.get( currentAttemptUrl ) ) );
} else {
RequestBuilder requestBuilder = new RequestBuilder( RequestBuilder.GET, currentAttemptUrl );
try {
requestBuilder.sendRequest( null, baseCallback );
} catch ( RequestException e ) {
Window.alert( "base load " + MSGS.error() + ":" + e.getMessage() ); //$NON-NLS-1$ //$NON-NLS-2$
fireBundleLoadCallback();
}
}
}
@Deprecated
private native String getUrlExtras()/*-{
return (document.all) ? "?rand="+(Math.random()*10000) : "";
}-*/;
@Deprecated
private void initCallbacks() {
baseCallback = new RequestCallback() {
public void onError( Request request, Throwable exception ) {
Window.alert( "baseCallback " + MSGS.error() + ":" + exception.getMessage() ); //$NON-NLS-1$ //$NON-NLS-2$
fireBundleLoadCallback();
}
public void onResponseReceived( Request request, Response response ) {
String propertiesFileText = response.getText();
// build a simple map of key/value pairs from the properties file
if ( response.getStatusCode() == Response.SC_OK ) {
bundle = PropertiesUtil.buildProperties( propertiesFileText, bundle );
if ( response instanceof FakeResponse == false ) {
// this is a real bundle load
bundleCache.put( currentAttemptUrl, propertiesFileText );
}
} else {
// put empty bundle in cache (not found, but we want to remember it was not found)
bundleCache.put( currentAttemptUrl, "" ); //$NON-NLS-1$
}
// now fetch the the lang/country variants
if ( localeName.equalsIgnoreCase( "default" ) ) { //$NON-NLS-1$
// process only bundleName.properties
fireBundleLoadCallback();
return;
} else {
StringTokenizer st = new StringTokenizer( localeName, '_' );
if ( st.countTokens() > 0 ) {
String lang = st.tokenAt( 0 );
// 2. fetch bundleName_lang.properties
// 3. fetch bundleName_lang_country.properties
currentAttemptUrl = path + bundleName + "_" + lang + PROPERTIES_EXTENSION + getUrlExtras(); //$NON-NLS-1$
// IE caches the file and causes an issue with the request
if ( bundleCache.containsKey( currentAttemptUrl ) ) {
langCallback.onResponseReceived( null, new FakeResponse( bundleCache.get( currentAttemptUrl ) ) );
} else {
RequestBuilder requestBuilder = new RequestBuilder( RequestBuilder.GET, currentAttemptUrl );
// Caching causing some strange behavior with IE6.
// TODO: Investigate caching issue.
requestBuilder.setHeader( "Cache-Control", "no-cache" ); //$NON-NLS-1$ //$NON-NLS-2$
try {
requestBuilder.sendRequest( null, langCallback );
} catch ( RequestException e ) {
Window.alert( "lang " + MSGS.error() + ":" + e.getMessage() ); //$NON-NLS-1$ //$NON-NLS-2$
fireBundleLoadCallback();
}
}
} else if ( st.countTokens() == 0 ) {
// already fetched
fireBundleLoadCallback();
return;
}
}
}
};
langCallback = new RequestCallback() {
public void onError( Request request, Throwable exception ) {
Window.alert( "langCallback " + MSGS.error() + ":" + exception.getMessage() ); //$NON-NLS-1$ //$NON-NLS-2$
fireBundleLoadCallback();
}
public void onResponseReceived( Request request, Response response ) {
String propertiesFileText = response.getText();
// build a simple map of key/value pairs from the properties file
if ( response.getStatusCode() == Response.SC_OK ) {
bundle = PropertiesUtil.buildProperties( propertiesFileText, bundle );
if ( response instanceof FakeResponse == false ) {
// this is a real bundle load
bundleCache.put( currentAttemptUrl, propertiesFileText );
}
} else {
// put empty bundle in cache (not found, but we want to remember it was not found)
bundleCache.put( currentAttemptUrl, "" ); //$NON-NLS-1$
}
StringTokenizer st = new StringTokenizer( localeName, '_' );
if ( st.countTokens() == 2 ) {
// 3. fetch bundleName_lang_country.properties
currentAttemptUrl = path + bundleName + "_" + localeName + PROPERTIES_EXTENSION + getUrlExtras(); //$NON-NLS-1$
if ( bundleCache.containsKey( currentAttemptUrl ) ) {
langCountryCallback.onResponseReceived( null, new FakeResponse( bundleCache.get( currentAttemptUrl ) ) );
} else {
RequestBuilder requestBuilder = new RequestBuilder( RequestBuilder.GET, currentAttemptUrl );
try {
requestBuilder.sendRequest( null, langCountryCallback );
} catch ( RequestException e ) {
Window.alert( "langCountry " + MSGS.error() + ":" + e.getMessage() ); //$NON-NLS-1$ //$NON-NLS-2$
fireBundleLoadCallback();
}
}
} else {
// already fetched
fireBundleLoadCallback();
return;
}
}
};
langCountryCallback = new RequestCallback() {
public void onError( Request request, Throwable exception ) {
Window.alert( "langCountryCallback " + MSGS.error() + ":" + exception.getMessage() ); //$NON-NLS-1$ //$NON-NLS-2$
fireBundleLoadCallback();
}
public void onResponseReceived( Request request, Response response ) {
String propertiesFileText = response.getText();
// build a simple map of key/value pairs from the properties file
if ( response.getStatusCode() == Response.SC_OK ) {
bundle = PropertiesUtil.buildProperties( propertiesFileText, bundle );
if ( response instanceof FakeResponse == false ) {
// this is a real bundle load
bundleCache.put( currentAttemptUrl, propertiesFileText );
}
} else {
// put empty bundle in cache (not found, but we want to remember it was not found)
bundleCache.put( currentAttemptUrl, "" ); //$NON-NLS-1$
}
fireBundleLoadCallback();
}
};
}
@Deprecated
private void fireBundleLoadCallback() {
if ( bundleLoadCallback != null ) {
bundleLoadCallback.bundleLoaded( bundleName );
}
}
/**
* This method returns the value for the given key with UTF-8 respected if supplied in \\uXXXX style format (single
* forward slash, u, followed by 4 digits). UTF-8 escaped values are replaced with entity escaping, such as 'ā'
* for proper consumption by web browsers.
*
* @param key
* The name of the resource being requested
* @return The UTF-8 friendly value found for the given key
*/
@Deprecated
public String getString( String key ) {
String resource = bundle.get( key );
if ( resource == null ) {
return key;
}
return decodeUTF8( bundle.get( key ) );
}
/**
* This method return the value for the given key with UTF-8 respected and will replace {n} tokens with the parameters
* that are passed in.
*
* @param key
* The name of the resource being requested
* @param parameters
* The values to replace occurrences of {n} in the found resource
* @return The UTF-8 friendly value found for the given key
*/
@Deprecated
public String getString( String key, String... parameters ) {
String resource = bundle.get( key );
if ( resource == null ) {
return key;
}
for ( int i = 0; i < parameters.length; i++ ) {
resource = resource.replace( "{" + i + "}", parameters[i] ); //$NON-NLS-1$ //$NON-NLS-2$
}
return decodeUTF8( resource );
}
/**
* This method return the set of keys for the MessageBundle
*
* @return The key set for the message bundle
*/
@Deprecated
public Set<String> getKeys() {
return bundle.keySet();
}
@Deprecated
private String decodeUTF8( String str ) {
if ( str == null ) {
return str;
}
while ( str.indexOf( "\\u" ) != -1 ) { //$NON-NLS-1$
int index = str.indexOf( "\\u" ); //$NON-NLS-1$
String hex = str.substring( index + 2, index + 6 );
str = str.substring( 0, index ) + "" + hex + ";" + str.substring( index + 6 ); //$NON-NLS-1$ //$NON-NLS-2$
}
return str;
}
@Deprecated
private static native String getLanguagePreference()
/*-{
var m = $doc.getElementsByTagName('meta');
for(var i in m) {
if(m[i].name == 'gwt:property' && m[i].content.indexOf('locale=') != -1) {
return m[i].content.substring(m[i].content.indexOf('=')+1);
}
}
return "default";
}-*/;
}