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}