/* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2011, IBM Corporation */ package com.phonegap.plugins.globalization; import com.phonegap.json4j.JSONArray; import com.phonegap.json4j.JSONObject; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; import net.rim.device.api.i18n.Locale; import net.rim.device.api.i18n.SimpleDateFormat; import net.rim.device.api.util.TimeZoneUtilities; import javax.microedition.global.Formatter; import java.util.Date; import java.util.Calendar; import java.util.TimeZone; import java.lang.Long; public class Globalization extends Plugin { /** * Executes the requested action and returns a PluginResult. * * @param action * The action to execute. * @param data * JSONArry of arguments for the action. * @param callbackId * The callback ID to be invoked upon action completion * @return A PluginResult object with a status and message. */ public PluginResult execute(String action, JSONArray data, String callbackId) { JSONObject obj = new JSONObject(); try { if (action.equals(Resources.GETLOCALENAME)) { obj = getLocaleName(); } else if (action.equalsIgnoreCase(Resources.DATETOSTRING)) { obj = getDateToString(data); } else if (action.equalsIgnoreCase(Resources.STRINGTODATE)) { obj = getStringToDate(data); } else if (action.equalsIgnoreCase(Resources.GETDATEPATTERN)) { obj = getDatePattern(data); } else if (action.equalsIgnoreCase(Resources.GETDATENAMES)) { obj = getDateNames(data); } else if (action.equalsIgnoreCase(Resources.ISDAYLIGHTSAVINGSTIME)) { obj = getIsDayLightSavingsTime(data); } else if (action.equalsIgnoreCase(Resources.GETFIRSTDAYOFWEEK)) { obj = getFirstDayOfWeek(data); } else if (action.equalsIgnoreCase(Resources.NUMBERTOSTRING)) { obj = getNumberToString(data); } else if (action.equalsIgnoreCase(Resources.STRINGTONUMBER)) { obj = getStringToNumber(data); } else if (action.equalsIgnoreCase(Resources.GETNUMBERPATTERN)) { obj = getNumberPattern(data); } else if (action.equalsIgnoreCase(Resources.GETCURRENCYPATTERN)) { obj = getCurrencyPattern(data); } else { return new PluginResult(PluginResult.Status.INVALID_ACTION); } } catch (GlobalizationError ge) { return new PluginResult(PluginResult.Status.ERROR, ge.getErrorCode()); } catch (Exception e) { return new PluginResult(PluginResult.Status.JSON_EXCEPTION); } return new PluginResult(PluginResult.Status.OK, obj); } /* * Returns the string identifier for the client's current locale setting. * * @return JSONObject Object.value {String}: The locale identifier * * @throws GlobalizationError.UNKNOWN_ERROR */ private JSONObject getLocaleName() throws GlobalizationError { JSONObject obj = new JSONObject(); try { return obj.put("value", Locale.getDefault().toString()); } catch (Exception e) { throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR); } } /* * Returns a date formatted as a string according to the client's user * preferences and calendar using the time zone of the client. * * @return JSONObject Object.value {String}: The localized date string * * @throws GlobalizationError.FORMATTING_ERROR */ private JSONObject getDateToString(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); try { Date date = new Date(Long.parseLong(options.getJSONObject(0) .get(Resources.DATE).toString())); // get formatting pattern from BB device SimpleDateFormat fmt = new SimpleDateFormat( Util.getBlackBerryDatePattern(options)); // return formatted date return obj.put("value", fmt.format(date)); } catch (Exception ge) { throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR); } } /* * Parses a date formatted as a string according to the * client's user preferences and calendar using the time zone of the client * and returns the corresponding date object * * @return JSONObject * Object.year {Number}: The four digit year * Object.month {Number}: The month from (0 - 11) * Object.day {Number}: The day from (1 - 31) * Object.hour {Number}: The hour from (0 - 23) * Object.minute {Number}: The minute from (0 - 59) * Object.second {Number}: The second from (0 - 59) * Object.millisecond {Number}: The milliseconds (from 0 - 999), * not available on all platforms * * @throws GlobalizationError.PARSING_ERROR */ private JSONObject getStringToDate(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); try { // get formatting pattern from BB device SimpleDateFormat fmt = new SimpleDateFormat( Util.getBlackBerryDatePattern(options)); // Manually parse string based on user preferences or Locale default String userDate = options.getJSONObject(0) .get(Resources.DATESTRING).toString().trim(); Calendar date = Util.dateParserBB(userDate, fmt.toPattern()); if (date == null) { // date was unparsable throw new Exception(); } // return properties; obj.put("year", date.get(Calendar.YEAR)); obj.put("month", date.get(Calendar.MONTH)); // returns 0-11 obj.put("day", date.get(Calendar.DAY_OF_MONTH)); obj.put("hour", date.get(Calendar.HOUR)); obj.put("minute", date.get(Calendar.MINUTE)); obj.put("second", date.get(Calendar.SECOND)); obj.put("millisecond", date.get(Calendar.MILLISECOND)); return obj; } catch (Exception ge) { throw new GlobalizationError(GlobalizationError.PARSING_ERROR); } } /* * Returns a pattern string for formatting and parsing dates * according to the client's user preferences. * * @return JSONObject * Object.pattern {String}: The date and time pattern for * formatting and parsing dates. The * patterns follow Unicode Technical Standard #35 * http://unicode.org/reports/tr35/tr35-4.html * Object.timezone {String}: The abbreviated name of the * time zone on the client * Object.utc_offset {Number}: The current difference in seconds * between the client's time zon and * coordinated universal time. * Object.dst_offset {Number}: The current daylight saving time * offset in seconds between the client's * non-daylight saving's time zone and the * client's daylight saving's time zone. * * @throws GlobalizationError.PATTERN_ERROR */ private JSONObject getDatePattern(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); try { // TimeZone from users device TimeZone tz = Calendar.getInstance().getTimeZone(); // Daylight boolean daylight = tz.useDaylightTime(); // set dst_offset int dst_offset = 0; // defaulted to zero if (daylight) { Calendar c = Calendar.getInstance(); dst_offset = (tz.getOffset(1, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH), c.get(Calendar.DAY_OF_WEEK), c.get(Calendar.MILLISECOND))) / 1000; } obj.put("pattern", Util.getBlackBerryDatePattern(options)); obj.put("timezone", TimeZoneUtilities.getDisplayName(tz, TimeZoneUtilities.SHORT)); obj.put("utc_offset", tz.getRawOffset() / 1000); obj.put("dst_offset", dst_offset); return obj; } catch (Exception ge) { throw new GlobalizationError(GlobalizationError.PATTERN_ERROR); } } /* * Returns an array of either the names of the months or days of the week * according to the client's user preferences and calendar * * @return JSONObject * Object.value {Array{String}}: The array of names starting from * either the first month in the year or the first * day of the week. * * @throws GlobalizationError.UNKNOWN_ERROR */ private JSONObject getDateNames(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); JSONArray value = new JSONArray(); try { int type = 0; // default wide int item = 0; // default months // get options if available if (options.getJSONObject(0).length() > 0) { // get type if available if (!((JSONObject) options.getJSONObject(0).get( Resources.OPTIONS)).isNull(Resources.TYPE)) { String t = (String) ((JSONObject) options.getJSONObject(0) .get(Resources.OPTIONS)).get(Resources.TYPE); if (t.equalsIgnoreCase(Resources.NARROW)) { type++; } // DateUtils.LENGTH_MEDIUM } // get item if available if (!((JSONObject) options.getJSONObject(0).get( Resources.OPTIONS)).isNull(Resources.ITEM)) { String t = (String) ((JSONObject) options.getJSONObject(0) .get(Resources.OPTIONS)).get(Resources.ITEM); if (t.equalsIgnoreCase(Resources.DAYS)) { item += 10; } // Days of week start at 1 } } // determine return value int method = item + type; if (method == 1) { value = Util.getDateNameString(Resources.MONTHS, "MMM"); }// months and narrow else if (method == 10) { value = Util.getDateNameString(Resources.DAYS, "EEEE"); }// days and wide else if (method == 11) { value = Util.getDateNameString(Resources.DAYS, "EEE"); }// days and narrow else { value = Util.getDateNameString(Resources.MONTHS, "MMMM"); }// default: months and wide if (value == null) { throw new Exception(); } // return array of names return obj.put("value", value); } catch (Exception ge) { throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR); } } /* * Returns whether daylight savings time is in effect for a * given date using the client's time zone and calendar. * * @return JSONObject * Object.dst {Boolean}: The value "true" indicates that daylight * savings time is in effect for the given date * and "false" indicates that it is not. * * @throws GlobalizationError.UNKNOWN_ERROR * * Note: Functionality to determine if date is within day light savings * time is not available in this API version */ private JSONObject getIsDayLightSavingsTime(JSONArray options) throws GlobalizationError { throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR); } /* * Returns the first day of the week according to the client's user * preferences and calendar. The days of the week are numbered starting from * 1 where 1 is considered to be Sunday. * * @return JSONObject Object.value {Number}: The number of the first day of * the week. * * @throws GlobalizationError.UNKNOWN_ERROR */ private JSONObject getFirstDayOfWeek(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); try { JSONObject result = Util.getLocaleData(Locale.getDefault() .toString()); if (result == null || result.length() <= 0) { throw new Exception(); } return obj.put("value", Integer.valueOf(result .getString(Resources.JSON_FIRISTDAYOFWEEK))); } catch (Exception e) { throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR); } } /* * Returns a number formatted as a string according to the * client's user preferences. * * @return JSONObject * Object.value {String}: The formatted number string. * * @throws GlobalizationError.FORMATTING_ERROR */ private JSONObject getNumberToString(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); String value; try { // Initialize formatter Formatter fmt = new Formatter(Locale.getDefault().toString()); // obtain user supplied number double num = Double.parseDouble(options.getJSONObject(0) .get(Resources.NUMBER).toString()); // format based on options if available value = fmt.formatNumber(num); if (options.getJSONObject(0).length() > 1) { // options were included if (!((JSONObject) options.getJSONObject(0).get( Resources.OPTIONS)).isNull(Resources.TYPE)) { String fmtOpt = (String) ((JSONObject) options .getJSONObject(0).get(Resources.OPTIONS)) .get(Resources.TYPE); if (fmtOpt.equalsIgnoreCase(Resources.CURRENCY)) { value = fmt.formatCurrency(num); } else if (fmtOpt.equalsIgnoreCase(Resources.PERCENT)) { // convert double to long // used 1 decimal places as a default value = fmt.formatPercentage((float) num, 1); } } } return obj.put("value", value); } catch (Exception ge) { throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR); } } /* * Parses a number formatted as a string according to the * client's user preferences and returns the corresponding number. * * @return JSONObject * Object.value {Number}: The parsed number. * * @throws GlobalizationError.PARSING_ERROR */ private JSONObject getStringToNumber(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); double value = 0; try { // format based on options if available String num = options.getJSONObject(0).get(Resources.NUMBERSTRING) .toString().trim(); if (options.getJSONObject(0).length() > 1) { // options were included if (!((JSONObject) options.getJSONObject(0).get( Resources.OPTIONS)).isNull(Resources.TYPE)) { String fmtOpt = (String) ((JSONObject) options .getJSONObject(0).get(Resources.OPTIONS)) .get(Resources.TYPE); // remove unwanted symbols if (fmtOpt.equalsIgnoreCase(Resources.CURRENCY)) { value = (Double.parseDouble(Util.removeSymbols(num))); } else if (fmtOpt.equalsIgnoreCase(Resources.PERCENT)) { value = (Double.parseDouble(Util.removeSymbols(num)) / 100); } } } else { value = Double.parseDouble(num); // decimal default } return obj.put("value", value); } catch (Exception ge) { throw new GlobalizationError(GlobalizationError.PARSING_ERROR); } } /* * Returns a pattern string for formatting and parsing numbers * according to the client's user preferences. * * @return JSONObject * Object.pattern {String}: The number pattern for formatting * and parsing numbers. The patterns follow * Unicode Technical Standard #35. * http://unicode.org/reports/tr35/tr35-4.html * Object.symbol {String}: The symbol to be used when * formatting and parsing e.g., percent or * currency symbol. * Object.fraction {Number}: The number of fractional digits * to use when parsing and formatting numbers. * Object.rounding {Number}: The rounding increment to use * when parsing and formatting. * Object.positive {String}: The symbol to use for positive * numbers when parsing and formatting. * Object.negative: {String}: The symbol to use for negative * numbers when parsing and formatting. * Object.decimal: {String}: The decimal symbol to use for * parsing and formatting. * Object.grouping: {String}: The grouping symbol to use * for parsing and formatting. * * @throws GlobalizationError.PATTERN_ERROR */ private JSONObject getNumberPattern(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); try { JSONObject result = Util.getLocaleData(Locale.getDefault() .toString()); String symbol = Resources.JSON_DECIMALSYMBOL; // get Date value + options (if available) if (options.getJSONObject(0).length() > 0) { // options were included if (!((JSONObject) options.getJSONObject(0).get( Resources.OPTIONS)).isNull(Resources.TYPE)) { String fmtOpt = (String) ((JSONObject) options .getJSONObject(0).get(Resources.OPTIONS)) .get(Resources.TYPE); if (fmtOpt.equalsIgnoreCase(Resources.CURRENCY)) { symbol = Resources.JSON_CURRENCYSYMBOL; } else if (fmtOpt.equalsIgnoreCase(Resources.PERCENT)) { symbol = Resources.JSON_PERCENTSYMBOL; } } } // return properties obj.put("pattern", result.getString(Resources.JSON_PATTERN)); obj.put("symbol", result.getString(symbol)); obj.put("fraction", Integer.valueOf(result.getString(Resources.JSON_FRACTION))); obj.put("rounding", Integer.valueOf(result.getString(Resources.JSON_ROUNDING))); obj.put("positive", result.getString(Resources.JSON_POSITIVE)); obj.put("negative", result.getString(Resources.JSON_NEGATIVE)); obj.put("decimal", result.getString(Resources.JSON_DECIMALSYMBOL)); obj.put("grouping", result.getString(Resources.JSON_GROUPING)); return obj; } catch (Exception ge) { throw new GlobalizationError(GlobalizationError.PATTERN_ERROR); } } /* * Returns a pattern string for formatting and parsing * currency values according to the client's user preferences and ISO 4217 * currency code. * * @return JSONObject = * Object.pattern {String}: The currency pattern for formatting * and parsing currency values. The patterns * follow Unicode Technical Standard #35 * http://unicode.org/reports/tr35/tr35-4.html * Object.code {String}: The ISO 4217 currency code for the * pattern. * Object.fraction {Number}: The number of fractional digits * to use when parsing and formatting currency. * Object.rounding {Number}: The rounding increment to use * when parsing and formatting. * Object.decimal: {String}: The decimal symbol to use for * parsing and formatting. * Object.grouping: {String}: The grouping symbol to use * for parsing and formatting. * * @throws GlobalizationError.FORMATTING_ERROR */ private JSONObject getCurrencyPattern(JSONArray options) throws GlobalizationError { JSONObject obj = new JSONObject(); try { JSONObject result = Util.getCurrencyData(Locale.getDefault() .toString(), options.getJSONObject(0).getString(Resources.CURRENCYCODE)); // return properties obj.put("pattern", result.getString(Resources.JSON_CURRENCYPATTERN)); obj.put("code", result.getString(Resources.JSON_CURRENCYCODE)); obj.put("fraction", Integer.valueOf(result .getString(Resources.JSON_CURRENCYFRACTION))); obj.put("rounding", Integer.valueOf(result .getString(Resources.JSON_CURRENCYROUNDING))); obj.put("decimal", result.getString(Resources.JSON_CURRENCYDECIMAL)); obj.put("grouping", result.getString(Resources.JSON_CURRENCYGROUPING)); return obj; } catch (Exception ge) { throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR); } } }