001package org.javasimon.console.text; 002 003import java.text.DateFormat; 004import java.text.DecimalFormat; 005import java.text.DecimalFormatSymbols; 006import java.text.NumberFormat; 007import java.text.SimpleDateFormat; 008import java.util.Date; 009 010import org.javasimon.SimonState; 011import org.javasimon.console.SimonType; 012import org.javasimon.console.TimeFormatType; 013import org.javasimon.console.html.HtmlResourceType; 014import org.javasimon.utils.SimonUtils; 015 016/** 017 * Base helper class to format values of attributes of simons. 018 * This class is in charge of initializing the dictionnary contained 019 * in {@link CompositeStringifier}. 020 * This class can be overridden to export differently JSON, CSV formats 021 * 022 * @author gquintana 023 */ 024public class StringifierFactory { 025 026 /** Value used to identify Date (=timestamp) sub type. */ 027 public static final String DATE_SUBTYPE = "Date"; 028 029 /** Value used to identify Time (=duration) sub type. */ 030 public static final String TIME_SUBTYPE = "Time"; 031 032 /** 033 * Value used to identify None (=disabled=hidden) sub type 034 * 035 * @see NoneStringifier 036 */ 037 public static final String NONE_SUBTYPE = "None"; 038 039 /** Default number pattern of Integers and Longs. */ 040 public static final String INTEGER_NUMBER_PATTERN = "0"; 041 042 /** Default number pattern of Doubles and Floats. */ 043 public static final String READABLE_NUMBER_PATTERN = "0.000"; 044 045 /** Date+Time pattern used aimed human reading. */ 046 public static final String READABLE_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss"; 047 048 /** Date+Time pattern used aimed XML parsers (ISO). */ 049 public static final String ISO_DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; 050 051 /** Stringifier dictionary. */ 052 protected final CompositeStringifier compositeStringifier = new CompositeStringifier(); 053 054 /** 055 * Register a stringifier for null values. 056 * Method aimed at being overridden. Should call {@link #registerNullStringifier(java.lang.String) }. 057 */ 058 protected Stringifier registerNullStringifier() { 059 return registerNullStringifier(""); 060 } 061 062 /** Register a stringifier for null values. */ 063 protected final Stringifier registerNullStringifier(final String nullValue) { 064 Stringifier nullStringifier = new Stringifier() { 065 public String toString(Object object) { 066 return nullValue; 067 } 068 }; 069 compositeStringifier.setNullStringifier(nullStringifier); 070 return nullStringifier; 071 } 072 073 /** 074 * Register a stringifier for String values. 075 * Method aimed at being overridden. 076 */ 077 protected Stringifier<String> registerStringStringifier(Stringifier nullStringifier) { 078 Stringifier<String> stringStringifier = new BaseStringifier<String>(nullStringifier) { 079 @Override 080 protected String doToString(String s) { 081 return s; 082 } 083 }; 084 compositeStringifier.add(String.class, stringStringifier); 085 return stringStringifier; 086 } 087 088 /** Register a stringifier for Long and long values. */ 089 protected final Stringifier<Long> registerLongStringifier(String name, Stringifier<Long> longStringifier) { 090 compositeStringifier.add(Long.class, name, longStringifier); 091 compositeStringifier.add(Long.TYPE, name, longStringifier); 092 return longStringifier; 093 } 094 095 /** 096 * Register a stringifier for Double and double values. 097 */ 098 protected final Stringifier<Double> registerDoubleStringifier(String name, Stringifier<Double> doubleStringifier) { 099 compositeStringifier.add(Double.class, name, doubleStringifier); 100 compositeStringifier.add(Double.TYPE, name, doubleStringifier); 101 return doubleStringifier; 102 } 103 104 /** 105 * Register a stringifier for various types and subtypes 106 * Method aimed at being overridden. 107 */ 108 public void init(TimeFormatType timeFormat, String datePattern, String numberPattern) { 109 // Null 110 final Stringifier nullStringifier = registerNullStringifier(); 111 112 // Default 113 compositeStringifier.setDefaultStringifier(new BaseStringifier(nullStringifier)); 114 115 // String 116 final Stringifier<String> stringStringifier = registerStringStringifier(nullStringifier); 117 118 // Disabled String 119 compositeStringifier.add(String.class, NONE_SUBTYPE, NoneStringifier.getInstance()); 120 121 // Integer 122 final Stringifier<Integer> integerStringifier = new BaseStringifier<Integer>(nullStringifier) { 123 124 @Override 125 protected boolean isValid(Integer l) { 126 return super.isValid(l) && !(l == Integer.MIN_VALUE || l == Integer.MAX_VALUE); 127 } 128 }; 129 compositeStringifier.add(Integer.class, integerStringifier); 130 compositeStringifier.add(Integer.TYPE, integerStringifier); 131 132 // Long 133 final Stringifier<Long> longStringifier = new BaseStringifier<Long>(nullStringifier) { 134 @Override 135 protected boolean isValid(Long l) { 136 return super.isValid(l) && !(l == Long.MIN_VALUE || l == Long.MAX_VALUE); 137 } 138 }; 139 registerLongStringifier(null, longStringifier); 140 141 // Float 142 final Stringifier<Float> floatStringifier = new NumberStringifier<Float>(nullStringifier, numberPattern) { 143 @Override 144 protected boolean isValid(Float f) { 145 return super.isValid(f) && !( 146 f == Float.MIN_VALUE || f == Float.MAX_VALUE || f.isInfinite() || f.isNaN()); 147 } 148 }; 149 compositeStringifier.add(Float.class, floatStringifier); 150 compositeStringifier.add(Float.TYPE, floatStringifier); 151 152 // Double 153 final Stringifier<Double> doubleStringifier = new NumberStringifier<Double>(nullStringifier, numberPattern) { 154 @Override 155 protected boolean isValid(Double d) { 156 return super.isValid(d) && !( 157 d == Double.MIN_VALUE || d == Double.MAX_VALUE || d.isInfinite() || d.isNaN()); 158 } 159 }; 160 registerDoubleStringifier(null, doubleStringifier); 161 162 // Enum 163 final Stringifier<Enum> enumStringifier = new BaseStringifier<Enum>(nullStringifier) { 164 @Override 165 protected String doToString(Enum e) { 166 return stringStringifier.toString(e.name()); 167 } 168 }; 169 compositeStringifier.add(Enum.class, enumStringifier); 170 compositeStringifier.add(SimonType.class, enumStringifier); 171 compositeStringifier.add(SimonState.class, enumStringifier); 172 compositeStringifier.add(HtmlResourceType.class, enumStringifier); 173 174 // Date 175 final Stringifier<java.util.Date> dateStringifier = new DateStringifier(nullStringifier, stringStringifier, datePattern); 176 compositeStringifier.add(Date.class, dateStringifier); 177 final LongDateStringifier longDateStringifier = new LongDateStringifier(nullStringifier, dateStringifier); 178 registerLongStringifier(DATE_SUBTYPE, longDateStringifier); 179 180 // Time 181 final LongTimeStringifier longTimeStringifier = new LongTimeStringifier(nullStringifier, longStringifier, stringStringifier, timeFormat); 182 registerLongStringifier(TIME_SUBTYPE, longTimeStringifier); 183 final DoubleTimeStringifier doubleTimeStringifier = new DoubleTimeStringifier(nullStringifier, doubleStringifier, stringStringifier, timeFormat); 184 registerDoubleStringifier(TIME_SUBTYPE, doubleTimeStringifier); 185 186 // Boolean 187 final Stringifier<Boolean> booleanStringifier = new BaseStringifier<Boolean>(nullStringifier) { 188 @Override 189 protected String doToString(Boolean b) { 190 return stringStringifier.toString(b.toString()); 191 } 192 }; 193 compositeStringifier.add(Boolean.class, booleanStringifier); 194 compositeStringifier.add(Boolean.TYPE, booleanStringifier); 195 } 196 197 /** Stringifier implementation for {@link Date} type. */ 198 protected static class DateStringifier extends BaseStringifier<Date> { 199 private final DateFormat dateFormat; 200 private final Stringifier<String> stringStringifier; 201 202 public DateStringifier(Stringifier nullStringifier, Stringifier<String> stringStringifier, String datePattern) { 203 super(nullStringifier); 204 this.dateFormat = new SimpleDateFormat(datePattern); 205 this.stringStringifier = stringStringifier; 206 } 207 208 @Override 209 protected String doToString(Date d) { 210 return stringStringifier.toString(dateFormat.format(d)); 211 } 212 } 213 214 /** Stringifier implementation for {@link Number} type. */ 215 protected static class NumberStringifier<T extends Number> extends BaseStringifier<T> { 216 private final NumberFormat numberFormat; 217 218 public NumberStringifier(Stringifier nullStringifier, String numberPattern) { 219 super(nullStringifier); 220 DecimalFormat decimalFormat = new DecimalFormat(numberPattern); 221 DecimalFormatSymbols decimalFormatSymbols = decimalFormat.getDecimalFormatSymbols(); 222 decimalFormatSymbols.setDecimalSeparator('.'); 223 decimalFormat.setDecimalFormatSymbols(decimalFormatSymbols); 224 this.numberFormat = decimalFormat; 225 } 226 227 @Override 228 protected String doToString(T n) { 229 return numberFormat.format(n.doubleValue()); 230 } 231 } 232 233 /** Stringifier implementation for {@link Long} type representing a timestamp. */ 234 protected static class LongDateStringifier extends BaseStringifier<Long> { 235 236 private final Stringifier<Date> dateStringifier; 237 238 public LongDateStringifier(Stringifier nullStringifier, Stringifier<Date> dateStringifier) { 239 super(nullStringifier); 240 this.dateStringifier = dateStringifier; 241 } 242 243 @Override 244 protected boolean isValid(Long object) { 245 return super.isValid(object) && object > 0L && object < Long.MAX_VALUE; 246 } 247 248 @Override 249 protected String doToString(Long object) { 250 return dateStringifier.toString(new Date(object)); 251 } 252 } 253 254 /** Stringifier implementation for {@link Long} type representing a duration. */ 255 protected static class LongTimeStringifier extends BaseStringifier<Long> { 256 257 private final TimeFormatType timeFormatType; 258 private final Stringifier<Long> longStringifier; 259 private final Stringifier<String> stringStringifier; 260 261 public LongTimeStringifier(Stringifier nullStringifier, Stringifier<Long> longStringifier, Stringifier<String> stringStringifier, TimeFormatType timeFormatType) { 262 super(nullStringifier); 263 this.timeFormatType = timeFormatType; 264 this.longStringifier = longStringifier; 265 this.stringStringifier = stringStringifier; 266 } 267 268 @Override 269 protected boolean isValid(Long object) { 270 return super.isValid(object) && object >= 0L && object < Long.MAX_VALUE; 271 } 272 273 @Override 274 protected String doToString(Long l) { 275 if (timeFormatType == TimeFormatType.AUTO) { 276 return stringStringifier.toString(SimonUtils.presentNanoTime(l)); 277 } else { 278 long l2 = timeFormatType.convert(l); 279 return longStringifier.toString(l2); 280 } 281 } 282 } 283 284 /** Stringifier implementation for {@link Double} type representing a duration. */ 285 protected static class DoubleTimeStringifier extends BaseStringifier<Double> { 286 287 private final TimeFormatType timeFormatType; 288 private final Stringifier<Double> doubleStringifier; 289 private final Stringifier<String> stringStringifier; 290 291 public DoubleTimeStringifier(Stringifier nullStringifier, Stringifier<Double> doubleStringifier, Stringifier<String> stringStringifier, TimeFormatType timeFormatType) { 292 super(nullStringifier); 293 this.timeFormatType = timeFormatType; 294 this.doubleStringifier = doubleStringifier; 295 this.stringStringifier = stringStringifier; 296 } 297 298 @Override 299 protected boolean isValid(Double object) { 300 return super.isValid(object) && object >= 0D && object < Double.MAX_VALUE && object != Double.NaN; 301 } 302 303 @Override 304 protected String doToString(Double d) { 305 if (timeFormatType == TimeFormatType.AUTO) { 306 return stringStringifier.toString(SimonUtils.presentNanoTime(d)); 307 } else { 308 Double d2 = timeFormatType.convert(d); 309 return doubleStringifier.toString(d2); 310 } 311 } 312 } 313 314 /** Get the stringifier for null values. */ 315 @SuppressWarnings("unchecked") 316 public <T> Stringifier<T> getNullStringifier() { 317 return compositeStringifier.getNullStringifier(); 318 } 319 320 /** Get the stringifier for given type. */ 321 public <T> Stringifier<T> getStringifier(Class<? extends T> type) { 322 return compositeStringifier.getForType(type); 323 } 324 325 /** Get the stringifier for given type and sub type. */ 326 public <T> Stringifier<T> getStringifier(Class<? extends T> type, String subType) { 327 return compositeStringifier.getForType(type, subType); 328 } 329 330 /** Get the stringifier for given instance and use it to format value. */ 331 public <T> String toString(T value) { 332 return compositeStringifier.toString(value); 333 } 334 335 /** Get the stringifier for given instance and subtype and use it to format value. */ 336 public <T> String toString(T value, String subType) { 337 return compositeStringifier.toString(value, subType); 338 } 339}