File Source: Formatter.java

     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  package com.dmdirc.ui.messages;
    24  
    25  import com.dmdirc.Precondition;
    26  import com.dmdirc.config.ConfigManager;
    27  
    28  import java.util.HashMap;
    29  import java.util.IllegalFormatConversionException;
    30  import java.util.Map;
    31  import java.util.MissingFormatArgumentException;
    32  import java.util.UnknownFormatConversionException;
    33  
    34  /**
    35   * The Formatter provides a standard way to format messages for display.
    36   */
         /* 
    P/P   *  Method: com.dmdirc.ui.messages.Formatter__static_init
          * 
          *  Postconditions:
          *    typeCache == &new HashMap(Formatter__static_init#1)
          *    new HashMap(Formatter__static_init#1) num objects == 1
          */
    37  public final class Formatter {
    38         
    39      /**
    40       * A cache of types needed by the various formatters.
    41       */
    42      private static final Map<String, Character[]> typeCache
    43              = new HashMap<String, Character[]>();
    44     
    45      /**
    46       * Creates a new instance of Formatter.
    47       */
             /* 
    P/P       *  Method: void com.dmdirc.ui.messages.Formatter()
              */
    48      private Formatter() {
    49          // Shouldn't be used
    50      }
    51      
    52      /**
    53       * Inserts the supplied arguments into a format string for the specified
    54       * message type.
    55       * 
    56       * @param messageType The message type that the arguments should be formatted as
    57       * @param config The config manager to use to format the message
    58       * @param arguments The arguments to this message type
    59       * @return A formatted string
    60       */
    61      @Precondition("The specified message type is not null")
    62      public static String formatMessage(final ConfigManager config, final String messageType,
    63              final Object... arguments) {
                 /* 
    P/P           *  Method: String formatMessage(ConfigManager, String, Object[])
                  * 
                  *  Preconditions:
                  *    config != null
                  *    messageType != null
                  *    (soft) arguments != null
                  *    (soft) arguments.length <= 232-1
                  *    (soft) arguments[...] != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.ConfigManager:getOption(...)@66 != null
                  * 
                  *  Postconditions:
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@66: {1}, {0}
                  */
    64          assert(messageType != null);
    65                  
    66          final String res = config.hasOptionString("formatter", messageType) ?
    67              config.getOption("formatter", messageType).replace("%-1$", "%"
    68              + arguments.length + "$"): null;
    69          
    70          if (res == null) {
    71              return "<No format string for message type " + messageType + ">";
    72          } else {
    73              try {
    74                  final Object[] newArgs = castArguments(res, arguments);
    75                  return String.format(res.replaceAll("(%[0-9]+\\$)u", "$1s"), newArgs);
    76              } catch (IllegalFormatConversionException ex) {
    77                  return "<Invalid format string for message type " + messageType
    78                          + "; Error: Illegal format conversion: " + ex.getMessage() + ">";
    79              } catch (UnknownFormatConversionException ex) {
    80                  return "<Invalid format string for message type " + messageType
    81                          + "; Error: Unknown format conversion: " + ex.getMessage() + ">";
    82              } catch (MissingFormatArgumentException ex) {
    83                  return "<Invalid format string for message type " + messageType
    84                          + "; Error: Missing format argument: " + ex.getMessage() + ">";
    85              }
    86          }
    87      }
    88      
    89      /**
    90       * Casts the specified arguments to the relevant classes, based on the
    91       * format type cache.
    92       *
    93       * @param format The format to be used
    94       * @param args The arguments to be casted
    95       * @return A new set of arguments of appropriate types
    96       */
    97      @Precondition("The specified format is not null")
    98      private static Object[] castArguments(final String format, final Object[] args) {
                 /* 
    P/P           *  Method: Object[] castArguments(String, Object[])
                  * 
                  *  Preconditions:
                  *    args != null
                  *    format != null
                  *    (soft) args.length <= 232-1
                  *    (soft) args[...] != null
                  * 
                  *  Presumptions:
                  *    arr$.length@108 <= 232-1
                  *    arr$.length@108 - args.length in {-232+1..0}
                  *    arr$[i$]@108 != null
                  *    java.lang.Integer:valueOf(...)@141 != null
                  *    java.lang.Long:longValue(...)@134 in {-9_223_372_036_854_775..18_446_744_073_709_551}
                  *    ...
                  * 
                  *  Postconditions:
                  *    init'ed(java.lang.String:valueOf(...)._tainted)
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    return_value == &amp;new Object[](castArguments#1)
                  *    new Object[](castArguments#1) num objects == 1
                  *    return_value.length == args.length
                  *    return_value.length <= 232-1
                  *    possibly_updated(return_value[...])
                  * 
                  *  Test Vectors:
                  *    java.lang.Character:charValue(...)@113: {65, 69, 71, 97, 101..103}, {66, 72, 83, 98, 104, 115}, {67, 99}, {84, 116}, {88, 100, 111, 120}, {117}, {0..64, 68, 70, 73..82, 85..87, 89..96, 105..110, 112..114, 118,119, 121..216-1}
                  *    java.lang.String:instanceof(...)@132: {0}, {1}
                  *    java.util.Map:containsKey(...)@101: {1}, {0}
                  */
    99          assert(format != null);
   100          
   101          if (!typeCache.containsKey(format)) {
   102              analyseFormat(format, args);
   103          }
   104          
   105          final Object[] res = new Object[args.length];
   106          
   107          int i = 0;
   108          for (Character chr : typeCache.get(format)) {
   109              if (i >= args.length) {
   110                  break;
   111              }
   112              
   113              switch (chr) {
   114              case 'b': case 'B': case 'h': case 'H': case 's': case 'S':
   115                  // General (strings)
   116                  res[i] = String.valueOf(args[i]);
   117                  break;
   118              case 'c': case 'C':
   119                  // Character
   120                  res[i] = String.valueOf(args[i]).charAt(0);
   121                  break;
   122              case 'd': case 'o': case 'x': case 'X':
   123                  // Integers
   124                  res[i] = Integer.valueOf((String) args[i]);
   125                  break;
   126              case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': case 'A':
   127                  // Floating point
   128                  res[i] = Float.valueOf((String) args[i]);
   129                  break;
   130              case 't': case 'T':
   131                  // Date
   132                  if (args[i] instanceof String) {
   133                      // Assume it's a timestamp(?)
   134                      res[i] = Long.valueOf(1000 * Long.valueOf((String) args[i]));
   135                  } else {
   136                      res[i] = args[i];
   137                  }
   138                  break;
   139              case 'u':
   140                  // Duration hacks
   141                  res[i] = formatDuration(Integer.valueOf(String.valueOf(args[i].toString())));
   142                  break;
   143              default:
   144                  res[i] = args[i];
   145              }
   146              
   147              i++;
   148          }
   149          
   150          return res;
   151      }
   152      
   153      /**
   154       * Tests for and adds one component of the duration format.
   155       * 
   156       * @param builder The string builder to append text to
   157       * @param current The number of seconds in the duration
   158       * @param duration The number of seconds in this component
   159       * @param name The name of this component
   160       * @return The number of seconds used by this component
   161       */
   162      private static int doDuration(final StringBuilder builder, final int current,
   163              final int duration, final String name) {
                 /* 
    P/P           *  Method: int doDuration(StringBuilder, int, int, String)
                  * 
                  *  Preconditions:
                  *    (soft) builder != null
                  *    (soft) current/duration in {-231..232-1}
                  *    (soft) duration != 0
                  *    (soft) duration*(current/duration) in {-231..232-1}
                  * 
                  *  Postconditions:
                  *    init'ed(builder._tainted)
                  *    return_value == One-of{0, duration*(current/duration)}
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    duration - current: {1..6_442_450_943}, {-6_442_450_943..0}
                  *    java.lang.StringBuilder:length(...)@170: {-231..0}, {1..232-1}
                  */
   164          int res = 0;
   165          
   166          if (current >= duration) {
   167              final int units = current / duration;
   168              res = units * duration;
   169              
   170              if (builder.length() > 0) {
   171                  builder.append(", ");
   172              }
   173              
   174              builder.append(units);
   175              builder.append(' ');
   176              builder.append(name + (units != 1 ? 's' : ""));
   177          }
   178          
   179          return res;
   180      }
   181      
   182      /**
   183       * Formats the specified number of seconds as a string containing the
   184       * number of days, hours, minutes and seconds.
   185       * 
   186       * @param duration The duration in seconds to be formatted
   187       * @return A textual version of the duration
   188       */
   189      public static String formatDuration(final int duration) {
                 /* 
    P/P           *  Method: String formatDuration(int)
                  * 
                  *  Postconditions:
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    return_value == &amp;java.lang.StringBuilder:toString(...)
                  */
   190          final StringBuilder buff = new StringBuilder();
   191          
   192          int seconds = duration;
   193          
   194          seconds -= doDuration(buff, seconds, 60*60*24, "day");
   195          seconds -= doDuration(buff, seconds, 60*60, "hour");
   196          seconds -= doDuration(buff, seconds, 60, "minute");
   197          seconds -= doDuration(buff, seconds, 1, "second");
   198          
   199          return buff.toString();
   200      }
   201      
   202      /**
   203       * Analyses the specified format string and fills in the format type cache.
   204       *
   205       * @param format The format to analyse
   206       * @param args The raw arguments
   207       */
   208      private static void analyseFormat(final String format, final Object[] args) {
                 /* 
    P/P           *  Method: void analyseFormat(String, Object[])
                  * 
                  *  Preconditions:
                  *    args != null
                  *    args.length <= 232-1
                  *    (soft) format != null
                  * 
                  *  Presumptions:
                  *    java.lang.String:indexOf(...)@212 <= 232-4
                  * 
                  *  Test Vectors:
                  *    java.lang.String:indexOf(...)@212: {-231..-1}, {0..232-4}
                  */
   209          final Character[] types = new Character[args.length];
   210          
   211          for (int i = 0; i < args.length; i++) {
   212              final int index = format.indexOf("%" + (i + 1) + "$");
   213              
   214              if (index > -1) {
   215                  types[i] = format.charAt(index + 3);
   216              } else {
   217                  types[i] = 's';
   218              }
   219          }
   220          
   221          typeCache.put(format, types);
   222      }
   223  
   224  }








SofCheck Inspector Build Version : 2.17854
Formatter.java 2009-Jun-25 01:54:24
Formatter.class 2009-Sep-02 17:04:17