javacommons/utils/javasrc/com/nokia/mj/impl/utils/Formatter.java
changeset 80 d6dafc5d983f
parent 72 1f0034e370aa
equal deleted inserted replaced
78:71ad690e91f5 80:d6dafc5d983f
    22 
    22 
    23 /**
    23 /**
    24  * Utility for formatting text strings from a pattern with positional
    24  * Utility for formatting text strings from a pattern with positional
    25  * parameters.
    25  * parameters.
    26  * <br>
    26  * <br>
    27  * Supported parameters are:
    27  * Supported parameters in Avkon environment are:
    28  * <UL>
    28  * <UL>
    29  * <LI>%nU - String in position n
    29  * <LI>%nU - String in position n
    30  * <LI>%U - Next string
    30  * <LI>%U - Next string
    31  * <LI>%Ln - Integer in position n
    31  * <LI>%Ln - Integer in position n
    32  * <LI>%nN - Integer in position n
    32  * <LI>%nN - Integer in position n
    34  * <LI>%nC - Character in position n
    34  * <LI>%nC - Character in position n
    35  * <LI>%C - Next character
    35  * <LI>%C - Next character
    36  * <LI>%n - Parameter in position n
    36  * <LI>%n - Parameter in position n
    37  * </UL>
    37  * </UL>
    38  * <br>
    38  * <br>
       
    39  * Supported parameters in Qt environment are:
       
    40  * <UL>
       
    41  * <LI>%1, %2, %3, ... - for String parameters
       
    42  * <LI>%L1, %L2, %L3, ... - for Integer parameters (%1, %2, %3, ... is also ok)
       
    43  * <LI>%Ln - for Integers indicating plurality
       
    44  * </UL>
       
    45  * <br>
       
    46  * Text parameter indices start from 0 when Avkon is used and from 1
       
    47  * when Qt is used. One text can only contain one %Ln parameter.
       
    48  * <br>
    39  * Example of usage:
    49  * Example of usage:
    40  * <pre>
    50  * <pre>
    41  *   Formatter formatter = new Formatter("You have %N email(s) left");
    51  *   Formatter formatter = resourceLoader.format("You have %N email(s) left");
    42  *   String message = formatter.arg(5).toString();
    52  *   String message = formatter.arg(5).toString();
    43  *   // Becomes :You have 5 email(s) left
    53  *   // Becomes: You have 5 email(s) left
    44  *
    54  *
    45  *   Formatter formatter = new Formatter("There are %N0 files in folder \"%U1\"");
    55  *   Formatter formatter = new Formatter("There are %N0 files in folder \"%U1\"");
    46  *   String message = formatter.arg(5).arg("photos").toString();
    56  *   String message = formatter.arg(5).arg("photos").toString();
    47  *   // Becomes :There are 5 files in folder "photos"
    57  *   // Becomes: There are 5 files in folder "photos"
    48  * </pre>
    58  * </pre>
    49  * <br>
    59  * <br>
    50  * Limitation: more than 10 positional arguments are not supported (only 0...9)
       
    51  * <br>
       
    52  * Note that Formatter supports Avkon and Qt based localisation.
       
    53  * Text parameter indices start from 0 when Avkon is used and from 1
       
    54  * when Qt is used.
       
    55  */
    60  */
    56 public class Formatter
    61 abstract public class Formatter
    57 {
    62 {
    58     /** Original pattern string */
       
    59     private String pattern;
       
    60 
       
    61     /** String with latest replacements */
       
    62     private String replaced;
       
    63 
       
    64     /**
       
    65      * Platform localisation type.
       
    66      * Either ResourceLoader.AVKON or ResourceLoader.QT. */
       
    67     private final int locType;
       
    68 
       
    69     /**
       
    70      * The first text parameter replacement index. For Avkon based
       
    71      * localisation this is 0, for Qt based localisation this is 1.
       
    72      */
       
    73     private final int startIndex;
       
    74 
       
    75     /** Next replacement index */
       
    76     private int nextIndex;
       
    77 
       
    78     /*** ----------------------------- PUBLIC ------------------------------ */
    63     /*** ----------------------------- PUBLIC ------------------------------ */
    79 
    64 
    80     /**
    65     /**
    81      * Create a new formatter
    66      * Set the plurality for this Formatter. Note that this method does
    82      *
    67      * nothing when Avkon localisation is used.
    83      * @param pattern formatter pattern
    68      *
    84      */
    69      * @param n number indicating plurality
    85     Formatter(String aPattern)
    70      * @return same formatter
    86     {
    71      */
    87         this(aPattern, ResourceLoader.AVKON);
    72     public Formatter argn(int n)
    88     }
    73     {
    89 
    74         return this;
    90     /**
       
    91      * Create a new formatter
       
    92      *
       
    93      * @param pattern formatter pattern
       
    94      * @param aLocType platform localisation type
       
    95      */
       
    96     Formatter(String aPattern, int aLocType)
       
    97     {
       
    98         pattern = aPattern;
       
    99         replaced = aPattern;
       
   100         locType = aLocType;
       
   101         startIndex = (locType == ResourceLoader.QT? 1: 0);
       
   102         nextIndex = startIndex;
       
   103     }
    75     }
   104 
    76 
   105     /**
    77     /**
   106      * Replace the lowest numbered parameter in the string, which is not yet
    78      * Replace the lowest numbered parameter in the string, which is not yet
   107      * replaced.
    79      * replaced.
   108      *
    80      *
   109      * @param string string to replace at the argument
    81      * @param string string to replace at the argument
   110      * @return same formatter
    82      * @return same formatter
   111      */
    83      */
   112     public Formatter arg(String string)
    84     abstract public Formatter arg(String string);
   113     {
       
   114         // Try to replace with patterns %[N...N]n, %nU, %n, %U
       
   115         String maxPattern = findMaxPattern();
       
   116         if ((maxPattern != null && replace(maxPattern, string)) ||
       
   117                 replace("%" + nextIndex + "U", string) ||
       
   118                 replace("%" + nextIndex, string) ||
       
   119                 replace("%U", string))
       
   120         {
       
   121             nextIndex++;
       
   122         }
       
   123         else
       
   124         {
       
   125             Logger.WLOG(Logger.EUtils,
       
   126                         "String replacement failed on parameter " +
       
   127                         nextIndex + ": " + pattern);
       
   128         }
       
   129         return this;
       
   130     }
       
   131 
    85 
   132     /**
    86     /**
   133      * Replace the least numbered parameter in the string, which is not yet
    87      * Replace the least numbered parameter in the string, which is not yet
   134      * replaced.
    88      * replaced.
   135      *
    89      *
   136      * @param number number to replace at the argument
    90      * @param number number to replace at the argument
   137      * @return same formatter
    91      * @return same formatter
   138      */
    92      */
   139     public Formatter arg(int number)
    93     abstract public Formatter arg(int number);
   140     {
       
   141         String localisedNumber = _formatInteger(number);
       
   142 
       
   143         // Try to replace with patterns %[N...N]n, %Ln, %nN, %n, %N
       
   144         String maxPattern = findMaxPattern();
       
   145         if ((maxPattern != null && replace(maxPattern, localisedNumber)) ||
       
   146                 replace("%" + "L" + nextIndex, localisedNumber) ||
       
   147                 replace("%" + nextIndex + "N", localisedNumber) ||
       
   148                 replace("%" + nextIndex, localisedNumber) ||
       
   149                 replace("%N", localisedNumber))
       
   150         {
       
   151             nextIndex++;
       
   152 
       
   153         }
       
   154         else
       
   155         {
       
   156             Logger.WLOG(Logger.EUtils,
       
   157                         "Integer replacement failed on parameter " +
       
   158                         nextIndex + ": " + pattern);
       
   159         }
       
   160         return this;
       
   161     }
       
   162 
    94 
   163     /**
    95     /**
   164      * Replace the least numbered parameter in the string, which is not yet
    96      * Replace the least numbered parameter in the string, which is not yet
   165      * replaced.
    97      * replaced.
   166      *
    98      *
   167      * @param ch character to replace at the argument
    99      * @param ch character to replace at the argument
   168      * @return same formatter
   100      * @return same formatter
   169      */
   101      */
   170     public Formatter arg(char ch)
   102     abstract public Formatter arg(char ch);
   171     {
       
   172         String chString = new String(new char[] { ch });
       
   173 
       
   174         // Try to replace with patterns %nC, %n, %C
       
   175         if (replace("%" + nextIndex + "C", chString) ||
       
   176                 replace("%" + nextIndex, chString) ||
       
   177                 replace("%C", chString))
       
   178         {
       
   179             nextIndex++;
       
   180 
       
   181         }
       
   182         else
       
   183         {
       
   184             Logger.WLOG(Logger.EUtils,
       
   185                         "Character replacement failed on parameter " +
       
   186                         nextIndex + ": " + pattern);
       
   187         }
       
   188         return this;
       
   189     }
       
   190 
   103 
   191     /**
   104     /**
   192      * Replace the least numbered parameter in the string, which is not yet
   105      * Replace the least numbered parameter in the string, which is not yet
   193      * replaced. Date is formatted according to current device date format.
   106      * replaced. Date is formatted according to current device date format.
   194      *
   107      *
   195      * @param date date to replace at the argument
   108      * @param date date to replace at the argument
   196      * @return same formatter
   109      * @return same formatter
   197      */
   110      */
   198     public Formatter arg(Date date)
   111     abstract public Formatter arg(Date date);
   199     {
   112 
   200         String dateString = _formatDate(date.getTime());
   113     /**
   201         return arg(dateString);
   114      * Replace the least numbered parameter in the string, which is not yet
   202     }
   115      * replaced.
   203 
   116      *
   204     /**
   117      * @param o object which will be used for argument position
   205      * Replace the least numbered parameter in the string, which is not yet
       
   206      * replaced.
       
   207      *
       
   208      * @param o object whose toString() will be used for argument position
       
   209      * @return same formatter
   118      * @return same formatter
   210      */
   119      */
   211     public Formatter arg(Object o)
   120     public Formatter arg(Object o)
   212     {
   121     {
   213         if (o != null)
   122         if (o != null)
   237      * Convert the current pattern to string, along with parameter
   146      * Convert the current pattern to string, along with parameter
   238      * replacements.
   147      * replacements.
   239      *
   148      *
   240      * @return string where parameters are replaced
   149      * @return string where parameters are replaced
   241      */
   150      */
   242     public String toString()
   151     abstract public String toString();
   243     {
       
   244         String result = replaced;
       
   245 
       
   246         // Reset for next usage
       
   247         replaced = pattern;
       
   248         nextIndex = startIndex;
       
   249 
       
   250         return result;
       
   251     }
       
   252 
   152 
   253     /**
   153     /**
   254      * Formats localised text with specified parameters from an array.
   154      * Formats localised text with specified parameters from an array.
   255      *
   155      *
   256      * Note that the arg().arg().toString() is preferred method of
   156      * Note that the arg().arg().toString() is preferred method of
   263     {
   163     {
   264         if (textParameters != null)
   164         if (textParameters != null)
   265         {
   165         {
   266             for (int i = 0; i < textParameters.length; i++)
   166             for (int i = 0; i < textParameters.length; i++)
   267             {
   167             {
   268                 if (textParameters[i] instanceof String)
   168                 arg(textParameters[i]);
   269                 {
       
   270                     arg((String)textParameters[i]);
       
   271                 }
       
   272                 else if (textParameters[i] instanceof Integer)
       
   273                 {
       
   274                     arg(((Integer)textParameters[i]).intValue());
       
   275                 }
       
   276                 else if (textParameters[i] instanceof Date)
       
   277                 {
       
   278                     arg((Date)textParameters[i]);
       
   279                 }
       
   280                 else
       
   281                 {
       
   282                     arg(textParameters[i]);
       
   283                 }
       
   284             }
   169             }
   285         }
   170         }
   286         return toString();
   171         return toString();
   287     }
   172     }
   288     /**
   173     /**
   293      * @return A string identical with the provided string but with the
   178      * @return A string identical with the provided string but with the
   294      *         european digits (if any) converted to arabic-indic digits
   179      *         european digits (if any) converted to arabic-indic digits
   295      */
   180      */
   296     public static String formatDigits(String str)
   181     public static String formatDigits(String str)
   297     {
   182     {
   298         return _formatDigits(str);
   183         if (ResourceLoader.getLocaleIdQt() == null)
       
   184         {
       
   185             return FormatterAvkon.formatDigits(str);
       
   186         }
       
   187         else
       
   188         {
       
   189             return FormatterQt.formatDigits(str);
       
   190         }
       
   191     }
       
   192 
       
   193     /*** ----------------------------- PROTECTED -------------------------- */
       
   194 
       
   195     /**
       
   196      * Default constructor.
       
   197      */
       
   198     protected Formatter()
       
   199     {
   299     }
   200     }
   300 
   201 
   301     /*** ----------------------------- PRIVATE ---------------------------- */
   202     /*** ----------------------------- PRIVATE ---------------------------- */
   302 
   203 
   303     /**
       
   304      * Replace first occurrence of the string pattern in the replaced field.
       
   305      *
       
   306      * @param pattern string to search for
       
   307      * @param replacement string to replace patterns
       
   308      * @return true if pattern was found and replaced, false if pattern was
       
   309      *         not found
       
   310      */
       
   311     private boolean replace(String pattern, String replacement)
       
   312     {
       
   313         int index = replaced.indexOf(pattern);
       
   314         if (index != -1)
       
   315         {
       
   316             if (replaced.indexOf(pattern + "[]") != -1)
       
   317             {
       
   318                 replaced =
       
   319                     replaced.substring(0, index) + replacement +
       
   320                     replaced.substring(index + pattern.length() + 2);
       
   321             }
       
   322             else if (replaced.indexOf(pattern + "[") != -1)
       
   323             {
       
   324                 return replaceWithMax(pattern, replacement, index);
       
   325             }
       
   326             else
       
   327             {
       
   328                 replaced =
       
   329                     replaced.substring(0, index) + replacement +
       
   330                     replaced.substring(index + pattern.length());
       
   331             }
       
   332             return true;
       
   333         }
       
   334         return false;
       
   335     }
       
   336 
       
   337     /**
       
   338      * Replace first occurrence of the string pattern in the replaced field.
       
   339      * Replace [N...N] defined amount of characters.
       
   340      *
       
   341      * @param pattern string to search for
       
   342      * @param replacement string to replace patterns
       
   343      * @param index of replacement tag.
       
   344      * @return true if pattern was found and replaced, false if pattern was
       
   345      *         not found
       
   346      */
       
   347     private boolean replaceWithMax(String pattern, String replacement, int maxIndex)
       
   348     {
       
   349         boolean result = false;
       
   350         int closingIndex = replaced.indexOf("]", maxIndex + pattern.length());
       
   351 
       
   352         // Check format [N...N] comply. If not skip.
       
   353         if (closingIndex > 0)
       
   354         {
       
   355             try
       
   356             {
       
   357                 int maxLen = Integer.parseInt(replaced.substring(
       
   358                                                   maxIndex + pattern.length() + 1, closingIndex));
       
   359 
       
   360                 if (maxLen > replacement.length())
       
   361                 {
       
   362                     maxLen = replacement.length();
       
   363                 }
       
   364 
       
   365                 replaced = replaced.substring(0, maxIndex) +
       
   366                            replacement.substring(0, maxLen) +
       
   367                            replaced.substring(closingIndex + 1);
       
   368                 result = true;
       
   369             }
       
   370             catch (NumberFormatException nfe)
       
   371             {
       
   372                 Logger.WLOG(Logger.EUtils, "Replace with max failed to invalid"
       
   373                             + " replacement amount");
       
   374             }
       
   375         }
       
   376         return result;
       
   377     }
       
   378 
       
   379     /**
       
   380      * Finds next %[N...N]n pattern from the replaced field.
       
   381      * Returns found pattern, or null if no pattern was found.
       
   382      */
       
   383     private String findMaxPattern()
       
   384     {
       
   385         String result = null;
       
   386         String startPattern = "%[";
       
   387         String endPattern = "]" + nextIndex;
       
   388         int startIndex = replaced.indexOf(startPattern);
       
   389         if (startIndex >= 0)
       
   390         {
       
   391             int endIndex = replaced.indexOf(endPattern, startIndex);
       
   392             if (endIndex >= 0)
       
   393             {
       
   394                 result = replaced.substring(
       
   395                     startIndex, endIndex + endPattern.length());
       
   396             }
       
   397         }
       
   398         return result;
       
   399     }
       
   400 
       
   401     /*** ----------------------------- NATIVE ----------------------------- */
   204     /*** ----------------------------- NATIVE ----------------------------- */
   402 
       
   403     /**
       
   404      * Format integer to current locale.
       
   405      *
       
   406      * @param number to be formatted.
       
   407      * @return number formatted as current locale String.
       
   408      */
       
   409     private native String _formatInteger(int number);
       
   410 
       
   411     /**
       
   412      * Format date to current locale.
       
   413      *
       
   414      * @param timeInMilliSecs to be formatted.
       
   415      * @param format Date format.
       
   416      * @return date formatted as current locale String.
       
   417      *
       
   418      */
       
   419     private native String _formatDate(long timeInMilliSecs);
       
   420 
       
   421     /**
       
   422      * Applies convertion from european digits into arabic-indic digits
       
   423      * based on existing language settings
       
   424      *
       
   425      * @param str String which might contain european digits
       
   426      * @return A string identical with the provided string but with the
       
   427      *         european digits (if any) converted to arabic-indic digits
       
   428      */
       
   429     private static native String _formatDigits(String str);
       
   430 }
   205 }