/*******************************************************************************
* Copyright © 2006, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.javart.resources;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.DecimalFormatSymbols;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.eclipse.edt.javart.Constants;
import org.eclipse.edt.javart.messages.Message;
import org.eclipse.edt.javart.util.JavartUtil;
/**
* LocalizedText contains information that varies by Locale.
*/
public class LocalizedText implements Serializable
{
private static final long serialVersionUID = Constants.SERIAL_VERSION_UID;
/**
* The properties of the RunUnit.
*/
private JavartProperties properties;
/**
* Constants for IBM NLS codes supported by EGL.
*/
public static final String NLS_ENU = "ENU";
public static final String NLS_DEU = "DEU";
public static final String NLS_DES = "DES";
public static final String NLS_ESP = "ESP";
public static final String NLS_FRA = "FRA";
public static final String NLS_ITA = "ITA";
public static final String NLS_PTB = "PTB";
public static final String NLS_KOR = "KOR";
public static final String NLS_JPN = "JPN";
public static final String NLS_CHS = "CHS";
public static final String NLS_CHT = "CHT";
public static final String NLS_RUS = "RUS";
public static final String NLS_PLK = "PLK";
public static final String NLS_CZE = "CZE";
public static final String NLS_HUN = "HUN";
public static final String NLS_ARA = "ARA";
/**
* The constant for the CHS (simplified Chinese) environment.
*/
public static final int CHS = 1;
/**
* The constant for the CHT (traditional Chinese) environment.
*/
public static final int CHT = 2;
/**
* The constant for the DES (Swiss German) environment.
*/
public static final int DES = 3;
/**
* The constant for the DEU (German) environment.
*/
public static final int DEU = 4;
/**
* The constant for the ENU (US English) environment.
*/
public static final int ENU = 5;
/**
* The constant for the ESP (Spanish) environment.
*/
public static final int ESP = 6;
/**
* The constant for the FRA (French) environment.
*/
public static final int FRA = 7;
/**
* The constant for the ITA (Italian) environment.
*/
public static final int ITA = 8;
/**
* The constant for the JPN (Japanese) environment.
*/
public static final int JPN = 9;
/**
* The constant for the KOR (Korean) environment.
*/
public static final int KOR = 10;
/**
* The constant for the PTB (Brazillian Portugese) environment.
*/
public static final int PTB = 11;
/**
* The constant for the RUS (Russian) environment.
*/
public static final int RUS = 12;
/**
* The constant for the HUN (Hungarian) environment.
*/
public static final int HUN = 13;
/**
* The constant for the CZE (Czech) environment.
*/
public static final int CZE = 14;
/**
* The constant for the PLK (Polish) environment.
*/
public static final int PLK = 15;
/**
* The constant for the ARA (Arabic) environment.
*/
public static final int ARA = 16;
/**
* This means we haven't yet looked up the currency location.
*/
private static final int CURRENCY_LOCATION_UNKNOWN = -1;
/**
* This means currencyLocation=NONE.
*/
public static final int CURRENCY_LOCATION_NONE = 0;
/**
* This means currencyLocation=FRONT.
*/
public static final int CURRENCY_LOCATION_FRONT = 1;
/**
* This means currencyLocation=BACK.
*/
public static final int CURRENCY_LOCATION_BACK = 2;
/**
* The NLS code being used.
*/
private String nlsCode;
/**
* A value representing the language of the NLS code.
*/
private int languageID;
/**
* The Locale associated with the NLS code.
*/
private Locale locale;
/**
* The actual NLS code that was passed to the constructor. It's kept around
* so it can be written to the trace file, which might reveal errors.
*/
private String inputNlsCode;
/**
* The actual Locale that was passed to the constructor. It's kept around so
* it can be written to the trace file, which might reveal errors.
*/
private Locale inputLocale;
/**
* An object used to provide locale-specific text elements.
*/
private DecimalFormatSymbols decimalFormatSymbols = null;
/**
* The decimal symbol, gotten from the properties file or the Locale.
*/
private char decimalSymbol = '\uFFFF';
/**
* The monetary decimal separator, either the decimalSymbol or gotten from the Locale.
*/
private char monetaryDecimalSeparator = '\uFFFF';
/**
* The currency symbol, gotten from the properties file or the Locale.
*/
private String currencySymbol = null;
/**
* Where to put the currency symbol (one of the CURRENCY_LOCATION constants).
*/
private int currencyLocation = CURRENCY_LOCATION_UNKNOWN;
/**
* The separator symbol, gotten from the properties file or the Locale.
*/
private char separatorSymbol = '\uFFFF';
/**
* The short Gregorian date mask, gotten from the properties file or the Locale.
*/
private String shortGregorianDateMask = null;
/**
* The long Gregorian date mask, gotten from the properties file or the Locale.
*/
private String longGregorianDateMask = null;
/**
* The short Julian date mask, gotten from the properties file or the Locale.
*/
private String shortJulianDateMask = null;
/**
* The long Julian date mask, gotten from the properties file or the Locale.
*/
private String longJulianDateMask = null;
/**
* A date-time formatter for the Locale.
*/
private DateFormat dateFormatter;
/**
* True if the decimal symbol was set in the properties.
*/
private boolean decimalSymbolSetInProperties;
/**
* The messages from the file named by the user.messages.file property,
* in the Locale associated with the NLS code.
*/
private transient ResourceBundle userMessages;
/**
* The EGL messages, in the Locale associated with the NLS code.
*/
private transient ResourceBundle eglMessages;
/**
* A cache of ResourceBundles. The EGL MessageBundle objects are stored
* with their Locale as the key. User-defined MessageBundle objects are
* stored with their name plus their Locale as the key.
*/
private static HashMap bundleCache = new HashMap( 32 );
/**
* Constructs a LocalizedText based on a set of properties. The egl.nls.code
* property will determine the NLS code, which determines the Locale.
*/
public LocalizedText( JavartProperties properties )
{
this.properties = properties;
inputNlsCode = properties.get( "egl.nls.code" );
inputLocale = null;
if ( inputNlsCode == null )
{
// Use the default Locale.
locale = Locale.getDefault();
setNlsCodeFromLocale();
}
else
{
// Make sure the NLS code from the properties is valid.
if ( inputNlsCode.equals( NLS_ENU ) || inputNlsCode.equals( NLS_DEU )
|| inputNlsCode.equals( NLS_DES ) || inputNlsCode.equals( NLS_ESP )
|| inputNlsCode.equals( NLS_FRA ) || inputNlsCode.equals( NLS_ITA )
|| inputNlsCode.equals( NLS_PTB ) || inputNlsCode.equals( NLS_KOR )
|| inputNlsCode.equals( NLS_JPN ) || inputNlsCode.equals( NLS_CHS )
|| inputNlsCode.equals( NLS_CHT ) || inputNlsCode.equals( NLS_RUS )
|| inputNlsCode.equals( NLS_CZE ) || inputNlsCode.equals( NLS_PLK )
|| inputNlsCode.equals( NLS_HUN ) || inputNlsCode.equals( NLS_ARA ) )
{
nlsCode = inputNlsCode;
}
else
{
nlsCode = NLS_ENU;
}
setLocaleFromNlsCode();
}
setLanguageID();
}
/**
* Switches to the given Locale. The NLS code will be updated too.
*
* @param loc the Locale to use.
*/
public void switchLocale( Locale loc )
{
inputNlsCode = null;
inputLocale = loc;
if ( !locale.equals( loc ) )
{
locale = loc;
setNlsCodeFromLocale();
setLanguageID();
// Clear all cached values that depend on the old settings.
decimalFormatSymbols = null;
decimalSymbol = '\uFFFF';
monetaryDecimalSeparator = '\uFFFF';
currencySymbol = null;
separatorSymbol = '\uFFFF';
shortGregorianDateMask = null;
longGregorianDateMask = null;
shortJulianDateMask = null;
longJulianDateMask = null;
dateFormatter = null;
userMessages = null;
eglMessages = null;
}
}
/**
* Returns trace info for this object.
*
* @return trace info for this object.
*/
public String getInfo()
{
String inputLocaleText = null;
if ( inputLocale != null )
{
inputLocaleText = inputLocale.getLanguage() + "." + inputLocale.getCountry();
}
return "NLS code: " + nlsCode + " (" + inputNlsCode + "), Locale: " + locale
+ " (" + inputLocaleText + ')';
}
/**
* Returns the Locale being used.
*
* @return the Locale being used.
*/
public Locale getLocale()
{
return locale;
}
/**
* Returns the NLS code being used.
*
* @return the NLS code being used.
*/
public String getNlsCode()
{
return nlsCode;
}
/**
* Sets the nlsCode to match the Locale. Sets it to ENU if no match is found.
*/
private void setNlsCodeFromLocale()
{
String language = locale.getLanguage();
String country = locale.getCountry();
if ( language.equals( "en" ) )
nlsCode = NLS_ENU;
else if ( language.equals( "de" ) )
{
if ( country.equals( "CH" ) )
nlsCode = NLS_DES;
else
nlsCode = NLS_DEU;
}
else if ( language.equals( "es" ) )
nlsCode = NLS_ESP;
else if ( language.equals( "pt" ) )
nlsCode = NLS_PTB;
else if ( language.equals( "ko" ) )
nlsCode = NLS_KOR;
else if ( language.equals( "fr" ) )
nlsCode = NLS_FRA;
else if ( language.equals( "it" ) )
nlsCode = NLS_ITA;
else if ( language.equals( "ja" ) )
nlsCode = NLS_JPN;
else if ( language.equals( "zh" ) )
{
// Use Traditional Chinese for Taiwan and Hong Kong, and Simplified
// Chinese for all other Chinese locales.
if ( country.equals( "TW" ) || country.equals( "HK" ) )
nlsCode = NLS_CHT;
else
nlsCode = NLS_CHS;
}
else if ( language.equals( "ru" ) )
nlsCode = NLS_RUS;
else if ( language.equals( "pl" ) )
nlsCode = NLS_PLK;
else if ( language.equals( "hu" ) )
nlsCode = NLS_HUN;
else if ( language.equals( "cs" ) )
nlsCode = NLS_CZE;
else if ( language.equals( "ar" ) )
nlsCode = NLS_ARA;
else
nlsCode = NLS_ENU;
}
/**
* Sets the locale based on the nlsCode. If the NLS code is not supported,
* use the default Locale.
*/
private void setLocaleFromNlsCode()
{
locale = getLocaleFromNlsCode( nlsCode );
}
public static Locale getLocaleFromNlsCode( String nlsCode )
{
Locale locale;
if ( nlsCode.equals( NLS_ENU ) )
locale = Locale.US;
else if ( nlsCode.equals( NLS_DEU ) )
locale = Locale.GERMAN;
else if ( nlsCode.equals( NLS_DES ) )
locale = new Locale( "de", "CH", "" );
else if ( nlsCode.equals( NLS_ESP ) )
locale = new Locale( "es", "", "" );
else if ( nlsCode.equals( NLS_FRA ) )
locale = Locale.FRENCH;
else if ( nlsCode.equals( NLS_ITA ) )
locale = Locale.ITALIAN;
else if ( nlsCode.equals( NLS_PTB ) )
locale = new Locale( "pt", "BR", "" );
else if ( nlsCode.equals( NLS_KOR ) )
locale = Locale.KOREAN;
else if ( nlsCode.equals( NLS_JPN ) )
locale = Locale.JAPANESE;
else if ( nlsCode.equals( NLS_CHS ) )
locale = Locale.SIMPLIFIED_CHINESE;
else if ( nlsCode.equals( NLS_CHT ) )
locale = Locale.TRADITIONAL_CHINESE;
else if ( nlsCode.equals( NLS_RUS ) )
locale = new Locale( "ru", "", "" );
else if ( nlsCode.equals( NLS_PLK ) )
locale = new Locale( "pl", "", "" );
else if ( nlsCode.equals( NLS_HUN ) )
locale = new Locale( "hu", "", "" );
else if ( nlsCode.equals( NLS_CZE ) )
locale = new Locale( "cs", "", "" );
else if ( nlsCode.equals( NLS_ARA ) )
locale = new Locale( "ar", "", "" );
else
locale = Locale.getDefault();
return locale;
}
/**
* Returns the symbol used to designate currency. ("$" for ENU.)
*
* @return the currency symbol used in this NLS environment.
*/
public String getCurrencySymbol()
{
// See if we've cached the value.
if ( currencySymbol != null )
{
return currencySymbol;
}
// Look in the properties file first.
currencySymbol = properties.get( "egl.nls.currency" );
if ( currencySymbol != null )
{
return currencySymbol;
}
// Get the symbol used in this Locale.
if ( decimalFormatSymbols == null )
{
decimalFormatSymbols = new DecimalFormatSymbols( locale );
}
currencySymbol = decimalFormatSymbols.getCurrencySymbol();
return currencySymbol;
}
/**
* Returns one of the CURRENCY_LOCATION constants.
*
* @return the constant that tells us where to put the currency symbol.
*/
public int getCurrencyLocation()
{
// See if we've cached the value.
if ( currencyLocation != CURRENCY_LOCATION_UNKNOWN )
{
return currencyLocation;
}
// Look in the properties file.
String property = properties.get( "egl.nls.currency.location" );
if ( property != null )
{
if ( property.equals( "FRONT" ) )
{
currencyLocation = CURRENCY_LOCATION_FRONT;
}
else if ( property.equals( "BACK" ) )
{
currencyLocation = CURRENCY_LOCATION_BACK;
}
else
{
// Default to NONE.
currencyLocation = CURRENCY_LOCATION_NONE;
}
}
else
{
// The property isn't set. Default to NONE.
currencyLocation = CURRENCY_LOCATION_NONE;
}
return currencyLocation;
}
/**
* Returns the symbol used to separate the whole part of a real number
* from the fractional part. ('.' for ENU.)
*
* @return the decimal symbol used in this NLS environment.
*/
public char getDecimalSymbol()
{
// See if we've cached the value.
if ( decimalSymbol != '\uFFFF' )
{
return decimalSymbol;
}
// Look in the properties file first.
String decimalString = properties.get( "egl.nls.number.decimal" );
if ( decimalString != null && decimalString.length() > 0 )
{
decimalSymbol = decimalString.charAt( 0 );
decimalSymbolSetInProperties = true;
return decimalSymbol;
}
decimalSymbolSetInProperties = false;
// Get the symbol used in this Locale.
if ( decimalFormatSymbols == null )
{
decimalFormatSymbols = new DecimalFormatSymbols( locale );
}
decimalSymbol = decimalFormatSymbols.getDecimalSeparator();
return decimalSymbol;
}
/**
* Returns the symbol used to separate the whole part of a real number
* from the fractional part in a monetary value. ('.' for ENU.)
*
* @return the monetary decimal separator used in this NLS environment.
*/
public char getMonetaryDecimalSeparator()
{
// See if we've cached the value.
if ( monetaryDecimalSeparator != '\uFFFF' )
{
return monetaryDecimalSeparator;
}
// We don't have a property for the monetaryDecimalSeparator. If the
// properties included a setting for decimalSymbol, use it. Otherwise
// get the value from the Locale.
getDecimalSymbol();
if ( decimalSymbolSetInProperties )
{
monetaryDecimalSeparator = decimalSymbol;
return monetaryDecimalSeparator;
}
// Get the symbol used in this Locale.
if ( decimalFormatSymbols == null )
{
decimalFormatSymbols = new DecimalFormatSymbols( locale );
}
monetaryDecimalSeparator = decimalFormatSymbols.getMonetaryDecimalSeparator();
return monetaryDecimalSeparator;
}
/**
* Determines whether <code>mask</code> is a valid Gregorian date mask. A
* valid mask contains dd, MM, and yy (short format) or yyyy (long format).
* A separator must appear between each part.
*
* @param mask
* the mask to examine.
* @param shortFormat
* true if the mask uses a 2-digit year.
* @return true if the mask is a valid Gregorian date mask.
*/
public static boolean isGregorianDateMask( String mask, boolean shortFormat )
{
char quoteChar = '\'';
int count_y = 0, count_m = 0, count_d = 0, count_separator = 0;
boolean inQuote = false;
int length = mask.length();
for ( int i = 0; i < length; )
{
char ch = mask.charAt( i++ );
if ( ch == quoteChar )
{
if ( i < length )
{
ch = mask.charAt( i );
if ( ch == quoteChar )
{
count_separator++;
i++;
continue;
}
}
if ( !inQuote )
{
inQuote = true;
}
else
{
inQuote = false;
}
continue;
}
if ( inQuote )
{
count_separator++;
}
else if ( ch == 'y' )
{
count_y++;
}
else if ( ch == 'M' )
{
count_m++;
}
else if ( ch == 'd' )
{
count_d++;
}
else if ( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') )
{
return false;
}
else
{
count_separator++;
}
}
if ( count_d == 2 && count_m == 2 && count_y == (shortFormat ? 2 : 4)
&& count_separator == 2 )
{
return true;
}
else
{
return false;
}
}
/**
* Determines whether <code>mask</code> is a valid Julian date mask. A
* valid mask contains DDD and yy (short format) or yyyy (long format). A
* separator must appear between the two parts.
*
* @param mask
* the mask to examine.
* @param shortFormat
* true if the mask uses a 2-digit year.
* @return true if the mask is a valid Julian date mask.
*/
private static boolean isJulianDateMask( String mask, boolean shortFormat )
{
char quoteChar = '\'';
int count_y = 0, count_d = 0, count_separator = 0;
boolean inQuote = false;
int length = mask.length();
for ( int i = 0; i < length; )
{
char ch = mask.charAt( i++ );
if ( ch == quoteChar )
{
if ( i < length )
{
ch = mask.charAt( i );
if ( ch == quoteChar )
{
count_separator++;
i++;
continue;
}
}
if ( !inQuote )
{
inQuote = true;
}
else
{
inQuote = false;
}
continue;
}
if ( inQuote )
{
count_separator++;
}
else if ( ch == 'y' )
{
count_y++;
}
else if ( ch == 'D' )
{
count_d++;
}
else if ( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') )
{
return false;
}
else
{
count_separator++;
}
}
if ( count_d == 3 && count_y == (shortFormat ? 2 : 4)
&& count_separator == 1 )
{
return true;
}
else
{
return false;
}
}
/**
* Returns the long Gregorian date mask defined for use in this RunUnit.
* If a mask is defined in the properties file, it is verified using
* <code>isGregorianDateMask</code>. If the mask is invalid, or if it's
* short/medium/long, an attempt is made to get the mask based on the Locale.
* If that fails, <code>MM-dd-yyyy</code> is returned.
*
* @return the Gregorian date mask defined for this NLS environment.
* @see JavartUtil#isGregorianDateMask
*/
public String getLongGregorianDateMask()
{
// Look in the cache first.
if ( longGregorianDateMask != null )
{
return longGregorianDateMask;
}
// Now try the properties file.
String mask = properties.get( "egl.datemask.gregorian.long." + nlsCode );
int style = java.text.DateFormat.MEDIUM;
if ( mask != null )
{
// It might be a mask or short, medium, long.
if ( mask.equals( "short" ) )
{
style = java.text.DateFormat.SHORT;
}
else if ( mask.equals( "medium" ) )
{
style = java.text.DateFormat.MEDIUM;
}
else if ( mask.equals( "long" ) )
{
style = java.text.DateFormat.LONG;
}
else if ( isGregorianDateMask( mask, false ) )
{
// It's a valid mask.
longGregorianDateMask = mask;
return mask;
}
}
// No mask was defined in the properties file. Get a DateFormat for
// this Locale.
java.text.DateFormat formatter =
java.text.DateFormat.getDateInstance( style, locale );
// The default value.
String def = "MM-dd-yyyy";
// Cast it to a SimpleDateFormat.
SimpleDateFormat sdf;
try
{
sdf = (SimpleDateFormat)formatter;
}
catch ( ClassCastException cce )
{
// If we can't get a SimpleDateFormat for our Locale, there's
// nothing we can do but return the default mask.
return def;
}
// Get the formatter's pattern and turn it into a date mask.
mask = parseDateFormatPattern( sdf.toPattern(), false );
if ( isGregorianDateMask( mask, false ) )
{
longGregorianDateMask = mask;
return mask;
}
else
{
// It's not valid, so return the default value.
longGregorianDateMask = def;
return def;
}
}
/**
* Returns the long Julian date mask defined for use in this RunUnit.
* If a mask is defined in the properties file, it is verified using
* <code>isJulianDateMask</code>. If the mask is invalid, or if it's
* short/medium/long, an attempt is made to get the mask based on the
* Locale. If that fails, <code>yyyy-DDD</code> is returned.
*
* @return the Julian date mask defined for this NLS environment.
* @see JavartUtil#isJulianDateMask
*/
public String getLongJulianDateMask()
{
// Look in the cache first.
if ( longJulianDateMask != null )
{
return longJulianDateMask;
}
// Now try the properties file.
String mask = properties.get( "egl.datemask.julian.long." + nlsCode );
int style = java.text.DateFormat.MEDIUM;
if ( mask != null )
{
// It might be a mask or short, medium, long.
if ( mask.equals( "short" ) )
{
style = java.text.DateFormat.SHORT;
}
else if ( mask.equals( "medium" ) )
{
style = java.text.DateFormat.MEDIUM;
}
else if ( mask.equals( "long" ) )
{
style = java.text.DateFormat.LONG;
}
else if ( isJulianDateMask( mask, false ) )
{
// It's a valid mask.
longJulianDateMask = mask;
return mask;
}
}
// No mask was defined in the properties file. Get a DateFormat for
// this Locale.
java.text.DateFormat formatter =
java.text.DateFormat.getDateInstance( style, locale );
// The default value.
String def = "yyyy-DDD";
// Cast it to a SimpleDateFormat.
SimpleDateFormat sdf;
try
{
sdf = (SimpleDateFormat)formatter;
}
catch ( ClassCastException cce )
{
// If we can't get a SimpleDateFormat for our Locale, there's
// nothing we can do but return the default mask.
return def;
}
// Get the formatter's pattern and turn it into a date mask.
mask = parseDateFormatPattern( sdf.toPattern(), false );
if ( isJulianDateMask( mask, false ) )
{
longJulianDateMask = mask;
return mask;
}
else
{
// It's not valid, so return the default value.
longJulianDateMask = def;
return def;
}
}
/**
* Returns the symbol used to separate the thousands place from the hundreds
* place in a number. (',' for ENU.)
*
* @return the separator symbol used in this NLS environment.
*/
public char getSeparatorSymbol()
{
// See if we've cached the value.
if ( separatorSymbol != '\uFFFF' )
{
return separatorSymbol;
}
// Look in the properties file first.
String separatorString = properties.get( "egl.nls.number.separator" );
if ( separatorString != null && separatorString.length() > 0 )
{
separatorSymbol = separatorString.charAt( 0 );
return separatorSymbol;
}
// Get the symbol used in this Locale.
if ( decimalFormatSymbols == null )
{
decimalFormatSymbols = new DecimalFormatSymbols( locale );
}
separatorSymbol = decimalFormatSymbols.getGroupingSeparator();
return separatorSymbol;
}
/**
* Returns the short Gregorian date mask defined for use in this RunUnit.
* If a mask is defined in the properties file, it is verified using
* <code>isGregorianDateMask</code>. If the mask is invalid,
* an attempt is made to get the mask based on the Locale. If
* that fails, <code>MM-dd-yy</code> is returned.
*
* @return the Gregorian date mask defined for this NLS environment.
* @see JavartUtil#isGregorianDateMask
*/
public String getShortGregorianDateMask()
{
// Look in the cache first.
if ( shortGregorianDateMask != null )
{
return shortGregorianDateMask;
}
String def = "MM-dd-yy";
// Now try the properties file.
String mask = properties.get( "egl.datemask.gregorian.short." + nlsCode );
if ( mask != null )
{
// See if the mask is valid.
if ( isGregorianDateMask( mask, true ) )
{
// It's valid.
shortGregorianDateMask = mask;
return mask;
}
}
// No mask was defined in the properties file. Get a DateFormat for
// this Locale.
java.text.DateFormat formatter =
java.text.DateFormat.getDateInstance( java.text.DateFormat.SHORT, locale );
// Cast it to a SimpleDateFormat.
SimpleDateFormat sdf;
try
{
sdf = (SimpleDateFormat)formatter;
}
catch ( ClassCastException cce )
{
// If we can't get a SimpleDateFormat for our Locale, there's
// nothing we can do but return the default mask.
return def;
}
// Get the formatter's pattern and turn it into a date mask.
mask = parseDateFormatPattern( sdf.toPattern(), true );
if ( isGregorianDateMask( mask, true ) )
{
shortGregorianDateMask = mask;
return mask;
}
else
{
// It's not valid, so return the default value.
shortGregorianDateMask = def;
return def;
}
}
/**
* Returns the short Julian date mask defined for use in this RunUnit.
* If a mask is defined in the properties file, it is verified using
* <code>isJulianDateMask</code>. If the mask is invalid,
* an attempt is made to get the mask based on the Locale. If
* that fails, <code>yy-DDD</code> is returned.
*
* @return the Julian date mask defined for this NLS environment.
* @see JavartUtil#isJulianDateMask
*/
public String getShortJulianDateMask()
{
// Look in the cache first.
if ( shortJulianDateMask != null )
{
return shortJulianDateMask;
}
String def = "yy-DDD";
// Now try the properties file.
String mask = properties.get( "egl.datemask.julian.short." + nlsCode );
if ( mask != null )
{
// See if the mask is valid.
if ( isJulianDateMask( mask, true ) )
{
// It's valid.
shortJulianDateMask = mask;
return mask;
}
}
// No mask was defined in the properties file. Get a DateFormat for
// this Locale.
java.text.DateFormat formatter =
java.text.DateFormat.getDateInstance( java.text.DateFormat.SHORT, locale );
// Cast it to a SimpleDateFormat.
SimpleDateFormat sdf;
try
{
sdf = (SimpleDateFormat)formatter;
}
catch ( ClassCastException cce )
{
// If we can't get a SimpleDateFormat for our Locale, there's
// nothing we can do but return the default mask.
return def;
}
// Get the formatter's pattern and turn it into a date mask.
mask = parseDateFormatPattern( sdf.toPattern(), true );
if ( isJulianDateMask( mask, true ) )
{
shortJulianDateMask = mask;
return mask;
}
else
{
// It's not valid, so return the default value.
shortJulianDateMask = def;
return def;
}
}
/**
* @return true if the language specified by the NLS code always uses
* single-byte characters.
*/
public boolean isSingleByteLanguage()
{
switch ( languageID )
{
case CHS:
case CHT:
case JPN:
case KOR:
return false;
default:
return true;
}
}
/**
* Returns true if <code>b</code> is an SBCS character in the ASCII
* codepage that corresponds to this object's NLS code.
* <code>b</code> must be the first byte of the character.
*
* @param b the character to examine.
* @return true if <code>b</code> is an SBCS character.
*/
public boolean isAsciiSingleByteChar( byte b )
{
switch ( languageID )
{
case CHS:
case CHT:
// The first byte of Simplified Chinese (codepage 1386, GBK)
// and Traditional Chinese (codepage 950, BIG-5) DBCS
// characters on ASCII systems range from 0x81 to 0xFE. We
// support another Simplified Chinese codepage (1381, IBM GB),
// in which DBCS characters range from 0x8C to 0xFE. This
// routine should work correctly for that one as well
return (b < (byte)0x81 || (byte)0xFE < b);
case JPN:
// The first byte of Japanese DBCS characters on ASCII
// systems range from 0x81 to 0x9F and 0xE0 to 0xFC.
return (b < (byte)0x81 || ((byte)0x9F < b && b < (byte)0xE0) || (byte)0xFC < b);
case KOR:
// The first byte of Korean DBCS characters on ASCII
// systems range from 0x8F to 0xFE.
return (b < (byte)0x8F || (byte)0xFE < b);
default:
// The other environments always use SBCS characters.
return true;
}
}
/**
* Parses <code>pattern</code> into an equivalent EGL date mask.
* <code>pattern</code> is expected to be a time pattern string
* of the type used by java.text.SimpleDateFormat. The result is
* not garaunteed to be a valid mask and should be validated using
* <code>isJulianDateMask</code> or <code>isGregorianDateMask</code>.
*
* <P> The parsing is done according to these rules:
* <UL>
* <LI> One or more of M (month) becomes MM.
* <LI> One or more of D (day of year) becomes DDD.
* <LI> One or more of d (day of month) becomes dd.
* <LI> One or more of y (year) becomes yy or yyyy, depending on
* <code>shortFormat</code>.
* <LI> Characters between the fields are kept as separators, but there
* must be exactly one between any two fields, and none at the front
* or end of the pattern.
* </UL>
*
* @param pattern the pattern to be parsed.
* @param shortFormat true if a two-digit year is desired, false
* for a four-digit year.
* @return the VG equivalent of <code>pattern</code>, or
* <code>pattern</code> itself if there
* is no VG equivalent.
* @see JavartUtil#isGregorianDateMask
* @see JavartUtil#isJulianDateMask
* @see java.text.SimpleDateFormat
*/
public static String parseDateFormatPattern( String pattern, boolean shortFormat )
{
char quoteChar = '\'';
StringBuilder buf = new StringBuilder();
int count_y = 0, count_m = 0, count_d = 0, count_D = 0;
boolean inQuote = false;
int length = pattern.length();
for ( int i = 0; i < length; )
{
char ch = pattern.charAt( i++ );
if ( ch == quoteChar )
{
buf.append( ch );
if ( i < length )
{
ch = pattern.charAt( i );
if ( ch == quoteChar )
{
buf.append( ch );
i++;
continue;
}
}
if ( !inQuote )
{
inQuote = true;
}
else
{
inQuote = false;
}
continue;
}
if ( inQuote )
{
buf.append( ch );
continue;
}
else if ( ch == 'y' )
{
if ( count_y == 0 )
{
if ( shortFormat )
buf.append( "yy" );
else
buf.append( "yyyy" );
}
count_y++;
continue;
}
else if ( ch == 'M' )
{
if ( count_m == 0 )
{
buf.append( "MM" );
}
count_m++;
continue;
}
else if ( ch == 'd' )
{
if ( count_d == 0 )
{
buf.append( "dd" );
}
count_d++;
continue;
}
else if ( ch == 'D' )
{
if ( count_D == 0 )
{
buf.append( "DDD" );
}
count_D++;
continue;
}
else
{
buf.append( ch );
}
}
return buf.toString();
}
/**
* Sets the languageID field to the constant that corresponds to the
* NLS code.
*/
private void setLanguageID()
{
if ( nlsCode.equals( NLS_CHS ) )
{
languageID = CHS;
}
else if ( nlsCode.equals( NLS_CHT ) )
{
languageID = CHT;
}
else if ( nlsCode.equals( NLS_DES ) )
{
languageID = DES;
}
else if ( nlsCode.equals( NLS_DEU ) )
{
languageID = DEU;
}
else if ( nlsCode.equals( NLS_ENU ) )
{
languageID = ENU;
}
else if ( nlsCode.equals( NLS_ESP ) )
{
languageID = ESP;
}
else if ( nlsCode.equals( NLS_FRA ) )
{
languageID = FRA;
}
else if ( nlsCode.equals( NLS_ITA ) )
{
languageID = ITA;
}
else if ( nlsCode.equals( NLS_JPN ) )
{
languageID = JPN;
}
else if ( nlsCode.equals( NLS_KOR ) )
{
languageID = KOR;
}
else if ( nlsCode.equals( NLS_PTB ) )
{
languageID = PTB;
}
else if ( nlsCode.equals( NLS_RUS ) )
{
languageID = RUS;
}
else if ( nlsCode.equals( NLS_PLK ) )
{
languageID = PLK;
}
else if ( nlsCode.equals( NLS_HUN ) )
{
languageID = HUN;
}
else if ( nlsCode.equals( NLS_CZE ) )
{
languageID = CZE;
}
else if ( nlsCode.equals( NLS_ARA ) )
{
languageID = ARA;
}
else
{
// Default to ENU.
languageID = ENU;
}
}
/**
* Returns the languageID: the constant that corresponds to the NLS code.
*/
public int getLanguageID()
{
return languageID;
}
/**
* Returns the text of the message with the given ID in this object's
* Locale. Checks the user message file first, and then the message bundle.
* Returns null if there's no message with the given ID.
*
* @param id the message ID.
* @return the text of the message.
*/
public String getMessage( String id )
{
if ( eglMessages == null )
{
// The messages need to be loaded. Load the user messages first.
boolean userMessagesMissing = false;
String userMessagesFile = properties.get( "egl.messages.file" );
if ( userMessagesFile != null )
{
// Check the cache.
userMessages =
(ResourceBundle)bundleCache.get( userMessagesFile + locale );
if ( userMessages == null )
{
// Not found, so load it and store in the cache for next time.
try
{
userMessages =
ResourceBundle.getBundle( userMessagesFile, locale );
bundleCache.put( userMessagesFile + locale, userMessages );
}
catch ( MissingResourceException mrx )
{
// File not found. We'll deal with this problem below.
userMessagesMissing = true;
}
}
}
// Load the EGL messages.
eglMessages = (ResourceBundle)bundleCache.get( locale );
if ( eglMessages == null )
{
eglMessages =
ResourceBundle.getBundle( "org.eclipse.edt.javart.messages.MessageBundle", locale );
bundleCache.put( locale, eglMessages );
}
if ( userMessagesMissing )
{
// Return the message for "properties file not found".
String text = getMessageText( Message.PROPERTIES_FILE_MISSING );
MessageFormat mf = new MessageFormat( text, locale );
return mf.format( new Object[] { userMessagesFile + ".properties" } );
}
}
return getMessageText( id );
}
/**
* Returns the text for the message ID. This assumes the bundles have
* already been loaded.
*
* @param id the message ID.
* @return the message text, or null if there's no such message.
*/
private String getMessageText( String id )
{
// Look in the userMessages then the eglMessages.
if ( userMessages != null )
{
try
{
return userMessages.getString( id );
}
catch ( MissingResourceException mrx )
{
// Ignore.
}
}
try
{
return eglMessages.getString( id );
}
catch ( MissingResourceException mrx )
{
// There's no such message.
return null;
}
}
/**
* Returns a date-time formatter for the Locale.
*/
public DateFormat getDateFormatter()
{
if ( dateFormatter == null )
{
dateFormatter = DateFormat.getDateTimeInstance( DateFormat.DEFAULT, DateFormat.DEFAULT, locale );
}
return dateFormatter;
}
/**
* Returns the text of the message with the given ID in this object's
* Locale. Checks the user message file first, and then the message bundle.
* Returns null if there's no message with the given ID.
*
* @param id the message ID.
* @param inserts the message inserts.
* @return the text of the message.
*/
public String getMessage( String id, Object... inserts )
{
String text = getMessage( id );
if ( inserts == null || text == null || inserts.length == 0 )
{
return text;
}
MessageFormat mf = new MessageFormat( text, locale );
return mf.format( inserts );
}
}