javacommons/utils/javasrc/com/nokia/mj/impl/utils/ResourceLoaderAvkon.java
changeset 80 d6dafc5d983f
equal deleted inserted replaced
78:71ad690e91f5 80:d6dafc5d983f
       
     1 /*
       
     2 * Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 package com.nokia.mj.impl.utils;
       
    20 
       
    21 import java.io.InputStream;
       
    22 import java.io.InputStreamReader;
       
    23 import java.io.IOException;
       
    24 import java.util.Hashtable;
       
    25 
       
    26 /**
       
    27  * Resource loader to get localised strings and Formatter patterns.
       
    28  * <br>
       
    29  * Usage:
       
    30  * <pre>
       
    31  *   ResourceLoader res = ResourceLoader.getInstance("javainstaller", "qtn_java_installer_");
       
    32  *   Label subjectLabel = createLabel(
       
    33  *       res.format("subject").arg(certificate.getSubject()).toString(),
       
    34  *       horizontalSpan, labelStyle);
       
    35  * </pre>
       
    36  * @see ResourceLoader
       
    37  */
       
    38 class ResourceLoaderAvkon extends ResourceLoader
       
    39 {
       
    40     /** Localisation resource basepath */
       
    41     private static final String LOC_RESOURCE_BASE = "/resources/com/nokia/mj/impl/";
       
    42 
       
    43     /**
       
    44      * Resource string map for the resources that have been fetched
       
    45      * at least once.
       
    46      */
       
    47     private Hashtable iResourceMap = new Hashtable();
       
    48 
       
    49     /**
       
    50      * Resource string maps for resources read from each resource file
       
    51      * specified in comma separated list of resource file names.
       
    52      */
       
    53     private Hashtable[] iResourceMaps = null;
       
    54 
       
    55     /** Resource name prefix passed when ResourceLoader is constructed. */
       
    56     private String iPrefix = null;
       
    57 
       
    58     /** Array of resource name prefixes. */
       
    59     private String[] iPrefixes = null;
       
    60 
       
    61     /** Platform localisation type. */
       
    62     private int iLocType = -1;
       
    63 
       
    64     /*** ----------------------------- PUBLIC ------------------------------ */
       
    65 
       
    66     /**
       
    67      * Returns a resource loader instance.
       
    68      *
       
    69      * @param avkonFileName name of the Avkon resource file
       
    70      * @param avkonPrefix prefix added before each id when retrieving
       
    71      * @param qtFileName name of the Qt resource file
       
    72      * @param qtPrefix prefix added before each id when retrieving
       
    73      * @return resource loader instance
       
    74      */
       
    75     public static ResourceLoader getInstance(String avkonFileName,
       
    76                                              String avkonPrefix,
       
    77                                              String qtFileName,
       
    78                                              String qtPrefix)
       
    79     {
       
    80         // Construct key from filenames and prefixes, this is the same
       
    81         // between platforms.
       
    82         String key = (new StringBuffer()).append(avkonFileName).append(":")
       
    83             .append(avkonPrefix).append(":").append(qtFileName).append(":")
       
    84             .append(qtPrefix).toString();
       
    85         ResourceLoader result = (ResourceLoader)iResourceLoaders.get(key);
       
    86         if (result == null)
       
    87         {
       
    88             result = new ResourceLoaderAvkon(
       
    89                 avkonFileName, avkonPrefix, qtFileName, qtPrefix);
       
    90             iResourceLoaders.put(key, result);
       
    91         }
       
    92         return result;
       
    93     }
       
    94 
       
    95     /**
       
    96      * Returns a resource loader instance.
       
    97      *
       
    98      * @param resourceName comma separated list of resource file names
       
    99      * @param prefix comma separated list of prefixes added before each
       
   100      * id when retrieving
       
   101      * @return resource loader instance
       
   102      */
       
   103     public static ResourceLoader getInstance(String resourceName, String prefix)
       
   104     {
       
   105         String key = resourceName + ":" + prefix;
       
   106         ResourceLoader result = (ResourceLoader)iResourceLoaders.get(key);
       
   107         if (result == null)
       
   108         {
       
   109             result = new ResourceLoaderAvkon(resourceName, prefix);
       
   110             iResourceLoaders.put(key, result);
       
   111         }
       
   112         return result;
       
   113     }
       
   114 
       
   115     /**
       
   116      * Get a string formatter of a given resource id.
       
   117      *
       
   118      * @param id comma separated list of resource ids
       
   119      * @return formatter instance
       
   120      * @see Formatter
       
   121      */
       
   122     public Formatter format(String id)
       
   123     {
       
   124         return new FormatterAvkon(string(id), iLocType);
       
   125     }
       
   126 
       
   127     /**
       
   128      * Get a string formatter of a given resource id.
       
   129      *
       
   130      * @param id resource id
       
   131      * @return formatter instance
       
   132      * @see Formatter
       
   133      */
       
   134     public Formatter format(Id id)
       
   135     {
       
   136         return new FormatterAvkon(string(id.getString(iLocType)), iLocType);
       
   137     }
       
   138 
       
   139     /**
       
   140      * Formats localised text with specified parameters from an array.
       
   141      *
       
   142      * @param id comma separated list of resource ids
       
   143      * @param textParameters parameters to be filled into the text
       
   144      * @return localised text formatted with the provided parameters
       
   145      * @see Formatter
       
   146      */
       
   147     public String format(String id, Object[] textParameters)
       
   148     {
       
   149         return new FormatterAvkon(string(id), iLocType).format(textParameters);
       
   150     }
       
   151 
       
   152     /**
       
   153      * Formats localised text with specified parameters from an array.
       
   154      *
       
   155      * @param id resource id
       
   156      * @param textParameters parameters to be filled into the text
       
   157      * @return localised text formatted with the provided parameters
       
   158      * @see Formatter
       
   159      */
       
   160     public String format(Id id, Object[] textParameters)
       
   161     {
       
   162         return new FormatterAvkon(string(id.getString(iLocType)), iLocType).format(textParameters);
       
   163     }
       
   164 
       
   165 
       
   166     /*** ----------------------------- PRIVATE ---------------------------- */
       
   167 
       
   168     /**
       
   169      * Get a plain string resource with a given resource id.
       
   170      *
       
   171      * @param id resource id, either with prefix or without
       
   172      * @return resource string, or the id if does not exist
       
   173      */
       
   174     private String string(String id)
       
   175     {
       
   176         String str = (String)iResourceMap.get(id);
       
   177         if (str == null)
       
   178         {
       
   179             str = getResourceStringByList(id);
       
   180             // Put to resource map with original key for quick retrieval.
       
   181             str = decode(str);
       
   182             str = replaceCharacterCodes(str);
       
   183             iResourceMap.put(id, str);
       
   184         }
       
   185         return str;
       
   186     }
       
   187 
       
   188     /**
       
   189      * Private constructor. Loads localisation resource file.
       
   190      * On Avkon UI it's resources are loaded. On Qt platfor it's
       
   191      * resource is first read and if that fails Avkon one is read.
       
   192      *
       
   193      * @param avkonFileName Avkon localisation resource file.
       
   194      * @param avkonPrefix   Avkon logical string prefix.
       
   195      * @param qtFileName    Qt localisation resource file.
       
   196      * @param qtPrefix      Qt logical string prefix.
       
   197      */
       
   198     private ResourceLoaderAvkon(String avkonFileName, String avkonPrefix,
       
   199                                 String qtFileName, String qtPrefix)
       
   200     {
       
   201         String localeId = getLocaleIdQt();
       
   202         if (localeId == null)
       
   203         {
       
   204             iLocType = AVKON;
       
   205             setPrefix(avkonPrefix);
       
   206             loadFiles(avkonFileName, true);
       
   207         }
       
   208         else
       
   209         {
       
   210             if (!loadFiles(qtFileName, false))
       
   211             {
       
   212                 // Fallback to Avkon
       
   213                 iLocType = AVKON;
       
   214                 setPrefix(avkonPrefix);
       
   215                 loadFiles(avkonFileName, true);
       
   216             }
       
   217             else
       
   218             {
       
   219                 iLocType = QT;
       
   220                 setPrefix(qtPrefix);
       
   221             }
       
   222         }
       
   223     }
       
   224 
       
   225     /**
       
   226      * Creates resource loader, using the current locale of the environment.
       
   227      *
       
   228      * @param resourceName name of the resource
       
   229      * @param aPrefix prefix added before each id when retrieving
       
   230      */
       
   231     private ResourceLoaderAvkon(String resourceName, String aPrefix)
       
   232     {
       
   233         iLocType = AVKON;
       
   234         setPrefix(aPrefix);
       
   235         loadFiles(resourceName, true); // Avkon
       
   236     }
       
   237 
       
   238     /**
       
   239      * Initializes member variables according to given prefix.
       
   240      *
       
   241      * @param aPrefix comma separated list of text id prefixes
       
   242      */
       
   243     private void setPrefix(String aPrefix)
       
   244     {
       
   245         iPrefix = aPrefix;
       
   246         iPrefixes = Tokenizer.split(iPrefix, SEPARATOR);
       
   247         if (iPrefixes == null)
       
   248         {
       
   249             iPrefixes = new String[0];
       
   250         }
       
   251         for (int i = 0; i < iPrefixes.length; i++)
       
   252         {
       
   253             iPrefixes[i] = iPrefixes[i].trim();
       
   254         }
       
   255     }
       
   256 
       
   257     /**
       
   258      * Loads the resources from .loc type files.
       
   259      *
       
   260      * @param resourceNames comma separated list of resource filenames.
       
   261      * @param avkon true if Avkon based localisation is in use
       
   262      * @param true if operation succeed.
       
   263      */
       
   264     private boolean loadFiles(String resourceNames, boolean avkon)
       
   265     {
       
   266         if (resourceNames == null)
       
   267         {
       
   268             return false;
       
   269         }
       
   270         boolean result = false;
       
   271         String[] filenames = Tokenizer.split(resourceNames, SEPARATOR);
       
   272         iResourceMaps = new Hashtable[filenames.length];
       
   273         for (int i = 0; i < filenames.length; i++)
       
   274         {
       
   275             iResourceMaps[i] = new Hashtable();
       
   276             if (loadFile(filenames[i].trim(), avkon, iResourceMaps[i]))
       
   277             {
       
   278                 // Return true if loading succeeds for at least one file.
       
   279                 result = true;
       
   280             }
       
   281         }
       
   282         return result;
       
   283     }
       
   284 
       
   285     /**
       
   286      * Loads the resources from .loc type file.
       
   287      *
       
   288      * @param resourceName name of the resource file
       
   289      * @param avkon true if Avkon based localisation is in use
       
   290      * @param resourceMap Map where the resources are stored to
       
   291      * @param true if operation succeed.
       
   292      */
       
   293     private boolean loadFile(String resourceName, boolean avkon, Hashtable resourceMap)
       
   294     {
       
   295         InputStream is = null;
       
   296 
       
   297         if (!avkon)  // Qt resources.
       
   298         {
       
   299             String langName = getLocaleIdQt();
       
   300 
       
   301             // Emulator returns falsely en_GB as engineering English locale name.
       
   302             if (langName.equals("en_GB"))
       
   303             {
       
   304                 langName = "en";
       
   305             }
       
   306 
       
   307             // Load with real locale id
       
   308             is = this.getClass().getResourceAsStream(
       
   309                 LOC_RESOURCE_BASE + resourceName + "_" + langName + ".loc");
       
   310 
       
   311             if (is == null)
       
   312             {
       
   313                 // Load the reference without locale id
       
   314                 is = this.getClass().getResourceAsStream(
       
   315                          LOC_RESOURCE_BASE + resourceName + ".loc");
       
   316             }
       
   317             if (is == null)
       
   318             {
       
   319                 /*
       
   320                  * Does not exist. No need to continue as avkon file cannot
       
   321                  * found using qt name.
       
   322                  */
       
   323                 return false;
       
   324             }
       
   325         }
       
   326         else  // Avkon resources.
       
   327         {
       
   328             // Load with real locale id
       
   329             is = this.getClass().getResourceAsStream(
       
   330                  LOC_RESOURCE_BASE + resourceName + "_" + getLocaleId() + ".loc");
       
   331 
       
   332             if (is == null)
       
   333             {
       
   334                 // Load the engineering english
       
   335                 is = this.getClass().getResourceAsStream(
       
   336                          LOC_RESOURCE_BASE + resourceName + "_sc" + ".loc");
       
   337             }
       
   338             if (is == null)
       
   339             {
       
   340                 // Load the reference engineering english
       
   341                 is = this.getClass().getResourceAsStream(
       
   342                          LOC_RESOURCE_BASE + resourceName + ".loc");
       
   343             }
       
   344             if (is == null)
       
   345             {
       
   346                 Logger.WLOG(Logger.EUtils,
       
   347                             "ResourceLoaderAvkon: Cannot load resource file: " +
       
   348                             resourceName);
       
   349                 return false;
       
   350             }
       
   351         }
       
   352 
       
   353         try
       
   354         {
       
   355             // Loc-files area always on UTF8 format
       
   356             LineReader lr = new LineReader(
       
   357                 new BufferedReader(new InputStreamReader(is, "UTF-8")));
       
   358             String line;
       
   359 
       
   360             while ((line = lr.readLine()) != null)
       
   361             {
       
   362                 // Ignore lines which are not #define's
       
   363                 if (!line.startsWith("#define "))
       
   364                 {
       
   365                     continue;
       
   366                 }
       
   367                 try
       
   368                 {
       
   369                     // Skip "#define" + any whitespace
       
   370                     line = line.substring(line.indexOf(' ')).trim();
       
   371 
       
   372                     int idEnd = line.indexOf(' ');
       
   373                     String id = line.substring(0, idEnd);
       
   374 
       
   375                     int strStart = line.indexOf('"', idEnd);
       
   376                     int strEnd = line.lastIndexOf('"');
       
   377                     String str = line.substring(strStart + 1, strEnd);
       
   378 
       
   379                     str = decode(str);
       
   380                     str = replaceCharacterCodes(str);
       
   381                     resourceMap.put(id, str);
       
   382 
       
   383                 }
       
   384                 catch (IndexOutOfBoundsException ex)
       
   385                 {
       
   386                     String error = "ResourceLoaderAvkon: Incorrect line " +
       
   387                         resourceName + ":" + lr.getLineNumber() + "\"" +
       
   388                         line + "\"";
       
   389                     Logger.WLOG(Logger.EUtils, error);
       
   390                 }
       
   391             }
       
   392             is.close();
       
   393 
       
   394         }
       
   395         catch (IOException ex)
       
   396         {
       
   397             Logger.WLOG(Logger.EUtils,
       
   398                         "ResourceLoaderAvkon: Resource file " + resourceName +
       
   399                         " handling failed: " + ex.getMessage());
       
   400         }
       
   401 
       
   402         return true;
       
   403     }
       
   404 
       
   405     /**
       
   406      * Decode given string. Decoding means unescaping escaped characters.
       
   407      * Currently \n, \t, \', \\ and \" patterns are decoded to respective
       
   408      * characters.
       
   409      *
       
   410      * @param str to be decoded.
       
   411      * @return decoded String.
       
   412      */
       
   413     private String decode(String str)
       
   414     {
       
   415         str = replacePattern(str, "\\n", '\n');
       
   416         str = replacePattern(str, "\\\\", '\\');
       
   417         str = replacePattern(str, "\\\"", '\"');
       
   418         str = replacePattern(str, "\\t", '\t');
       
   419         str = replacePattern(str, "\\'", '\'');
       
   420 
       
   421         return str;
       
   422     }
       
   423 
       
   424     /**
       
   425      * Replace all occurrences of the pattern in the given String.
       
   426      *
       
   427      * @param resource string to be replaced.
       
   428      * @param pattern to replace.
       
   429      * @param replacement replacement character.
       
   430      * @return String where all occurrences of the pattern are replaced.
       
   431      */
       
   432     private String replacePattern(
       
   433         String resource, String pattern, char replacement)
       
   434     {
       
   435         StringBuffer sb = new StringBuffer();
       
   436 
       
   437         int startIndex = resource.indexOf(pattern);
       
   438         if (startIndex != -1)
       
   439         {
       
   440             sb.append(resource.substring(0, startIndex)).append(replacement);
       
   441             startIndex = startIndex + pattern.length();
       
   442             int endIndex = 0;
       
   443 
       
   444             while ((endIndex = resource.indexOf(pattern, startIndex)) != -1)
       
   445             {
       
   446                 sb.append(resource.substring(startIndex, endIndex))
       
   447                 .append(replacement);
       
   448                 startIndex = endIndex + pattern.length();
       
   449             }
       
   450 
       
   451             if (startIndex < resource.length())
       
   452             {
       
   453                 sb.append(resource.substring(startIndex, resource.length()));
       
   454             }
       
   455             return sb.toString();
       
   456         }
       
   457         return resource;
       
   458     }
       
   459 
       
   460     /**
       
   461      * Replace character codes. They are given as <0x0000> format. Where 0x0000
       
   462      * contains character unicode as hex representation.
       
   463      *
       
   464      * @param str to replace characters.
       
   465      * @return String where characters are replaced.
       
   466      */
       
   467     private String replaceCharacterCodes(String str)
       
   468     {
       
   469         StringBuffer sb = new StringBuffer();
       
   470         int startIndex = str.indexOf("<0x");
       
   471 
       
   472         if (startIndex != -1 && str.charAt(startIndex + 7) == '>')
       
   473         {
       
   474             sb.append(str.substring(0, startIndex));
       
   475             try
       
   476             {
       
   477                 int charint = Integer.parseInt(
       
   478                                   str.substring(startIndex + 3, startIndex + 7), 16);
       
   479                 sb.append((char) charint);
       
   480 
       
   481                 int endIndex = 0;
       
   482                 startIndex += 7;
       
   483 
       
   484                 while ((endIndex = str.indexOf("<0x", startIndex)) != -1
       
   485                         && str.charAt(endIndex + 7) == '>')
       
   486                 {
       
   487                     sb.append(str.substring(startIndex + 1, endIndex));
       
   488 
       
   489                     charint = Integer.parseInt(
       
   490                                   str.substring(endIndex + 3, endIndex + 7), 16);
       
   491                     sb.append((char) charint);
       
   492                     startIndex = endIndex + 7;
       
   493                 }
       
   494             }
       
   495             catch (NumberFormatException nfe)
       
   496             {
       
   497                 Logger.ELOG(Logger.EUtils,
       
   498                             "ResourceLoaderAvkon: Cannot replace character from string: " +
       
   499                             str);
       
   500                 return str;
       
   501             }
       
   502 
       
   503             if (startIndex < str.length())
       
   504             {
       
   505                 sb.append(str.substring(startIndex + 1, str.length()));
       
   506             }
       
   507             return sb.toString();
       
   508         }
       
   509         return str;
       
   510     }
       
   511 
       
   512     /**
       
   513      * Get a string from resource map with a given comma
       
   514      * separated resource id list.
       
   515      *
       
   516      * @param id comma separated resource id list, either with prefix or without
       
   517      * @return resource string, or the id if does not exist
       
   518      */
       
   519     private String getResourceStringByList(String id)
       
   520     {
       
   521         String str = null;
       
   522         String[] ids = Tokenizer.split(id, SEPARATOR);
       
   523         for (int i = 0; i < ids.length && str == null; i++)
       
   524         {
       
   525             // Search text ids from resource maps.
       
   526             for (int j = 0; j < iResourceMaps.length && str == null; j++)
       
   527             {
       
   528                 str = (String)iResourceMaps[j].get(ids[i]);
       
   529                 if (str == null && iPrefixes != null && iPrefixes.length > j)
       
   530                 {
       
   531                     // Try with prefix.
       
   532                     str = (String)iResourceMaps[j].get(
       
   533                         iPrefixes[j] + ids[i]);
       
   534                 }
       
   535             }
       
   536         }
       
   537         if (str == null)
       
   538         {
       
   539             // Not found. Use the id itself.
       
   540             str = id;
       
   541             Logger.WLOG(Logger.EUtils,
       
   542                         "ResourceLoaderAvkon: Cannot find resource: " + id +
       
   543                         ", prefix: " + iPrefix);
       
   544         }
       
   545         return str;
       
   546     }
       
   547 }