You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Formatter.java 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. * Copyright (c) 2006-2017 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  5. * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
  6. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
  7. * permit persons to whom the Software is furnished to do so, subject to the following conditions:
  8. *
  9. * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
  10. * Software.
  11. *
  12. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  13. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
  14. * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  15. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  16. */
  17. package com.dmdirc.ui.messages;
  18. import com.dmdirc.Precondition;
  19. import com.dmdirc.config.provider.AggregateConfigProvider;
  20. import com.dmdirc.util.DateUtils;
  21. import java.util.HashMap;
  22. import java.util.IllegalFormatConversionException;
  23. import java.util.Map;
  24. import java.util.MissingFormatArgumentException;
  25. import java.util.UnknownFormatConversionException;
  26. /**
  27. * The Formatter provides a standard way to format messages for display.
  28. */
  29. public final class Formatter {
  30. /**
  31. * A cache of types needed by the various formatters.
  32. */
  33. private static final Map<String, Character[]> TYPE_CACHE = new HashMap<>();
  34. /**
  35. * Creates a new instance of Formatter.
  36. */
  37. private Formatter() {
  38. // Shouldn't be used
  39. }
  40. /**
  41. * Inserts the supplied arguments into a format string for the specified message type.
  42. *
  43. * @param messageType The message type that the arguments should be formatted as
  44. * @param config The config manager to use to format the message
  45. * @param arguments The arguments to this message type
  46. *
  47. * @return A formatted string
  48. */
  49. @Precondition("The specified message type is not null")
  50. public static String formatMessage(final AggregateConfigProvider config,
  51. final String messageType,
  52. final Object... arguments) {
  53. assert messageType != null;
  54. final String res = config.hasOptionString("formatter", messageType)
  55. ? config.getOption("formatter", messageType).replace("%-1$", "%"
  56. + arguments.length + '$') : null;
  57. if (res == null) {
  58. return "<No format string for message type " + messageType + '>';
  59. } else {
  60. try {
  61. final Object[] newArgs = castArguments(res, arguments);
  62. return String.format(res.replaceAll("(%[0-9]+\\$)u", "$1s"), newArgs);
  63. } catch (IllegalFormatConversionException ex) {
  64. return "<Invalid format string for message type " + messageType
  65. + "; Error: Illegal format conversion: " + ex.getMessage() + '>';
  66. } catch (UnknownFormatConversionException ex) {
  67. return "<Invalid format string for message type " + messageType
  68. + "; Error: Unknown format conversion: " + ex.getMessage() + '>';
  69. } catch (MissingFormatArgumentException ex) {
  70. return "<Invalid format string for message type " + messageType
  71. + "; Error: Missing format argument: " + ex.getMessage() + '>';
  72. } catch (NumberFormatException ex) {
  73. return "<Invalid format string for message type " + messageType
  74. + "; Error: Invalid number conversion: " + ex.getMessage() + '>';
  75. }
  76. }
  77. }
  78. /**
  79. * Casts the specified arguments to the relevant classes, based on the format type cache.
  80. *
  81. * @param format The format to be used
  82. * @param args The arguments to be casted
  83. *
  84. * @return A new set of arguments of appropriate types
  85. */
  86. @Precondition("The specified format is not null")
  87. private static Object[] castArguments(final String format, final Object[] args) {
  88. assert format != null;
  89. if (!TYPE_CACHE.containsKey(format)) {
  90. analyseFormat(format, args);
  91. }
  92. final Object[] res = new Object[args.length];
  93. int i = 0;
  94. for (Character chr : TYPE_CACHE.get(format)) {
  95. if (i >= args.length) {
  96. break;
  97. }
  98. switch (chr) {
  99. case 'b':
  100. case 'B':
  101. case 'h':
  102. case 'H':
  103. case 's':
  104. case 'S':
  105. // General (strings)
  106. res[i] = String.valueOf(args[i]);
  107. break;
  108. case 'c':
  109. case 'C':
  110. // Character
  111. res[i] = String.valueOf(args[i]).charAt(0);
  112. break;
  113. case 'd':
  114. case 'o':
  115. case 'x':
  116. case 'X':
  117. // Integers
  118. res[i] = Integer.valueOf((String) args[i]);
  119. break;
  120. case 'e':
  121. case 'E':
  122. case 'f':
  123. case 'g':
  124. case 'G':
  125. case 'a':
  126. case 'A':
  127. // Floating point
  128. res[i] = Float.valueOf((String) args[i]);
  129. break;
  130. case 't':
  131. case 'T':
  132. // Date
  133. if (args[i] instanceof String) {
  134. // Assume it's a timestamp(?)
  135. res[i] = 1000 * Long.valueOf((String) args[i]);
  136. } else {
  137. res[i] = args[i];
  138. }
  139. break;
  140. case 'u':
  141. // Duration hacks
  142. res[i] = DateUtils.formatDuration(Integer.valueOf(
  143. String.valueOf(args[i].toString())));
  144. break;
  145. default:
  146. res[i] = args[i];
  147. }
  148. i++;
  149. }
  150. return res;
  151. }
  152. /**
  153. * Analyses the specified format string and fills in the format type cache.
  154. *
  155. * @param format The format to analyse
  156. * @param args The raw arguments
  157. */
  158. private static void analyseFormat(final String format, final Object[] args) {
  159. final Character[] types = new Character[args.length];
  160. for (int i = 0; i < args.length; i++) {
  161. final int index = format.indexOf("%" + (i + 1) + '$');
  162. if (index > -1) {
  163. types[i] = format.charAt(index + 3);
  164. } else {
  165. types[i] = 's';
  166. }
  167. }
  168. TYPE_CACHE.put(format, types);
  169. }
  170. }