1 module hunt.time.format.TextPrinterParser;
2 
3 import hunt.time.chrono.Chronology;
4 import hunt.time.chrono.Era;
5 import hunt.time.chrono.IsoChronology;
6 
7 import hunt.time.format.DateTimeParseContext;
8 import hunt.time.format.DateTimePrinterParser;
9 import hunt.time.format.DateTimePrintContext;
10 import hunt.time.format.DateTimeTextProvider;
11 import hunt.time.format.NumberPrinterParser;
12 import hunt.time.format.SignStyle;
13 import hunt.time.format.TextStyle;
14 
15 import hunt.time.temporal.ChronoField;
16 import hunt.time.temporal.TemporalField;
17 import hunt.time.temporal.TemporalQueries;
18 import hunt.time.util.QueryHelper;
19 import hunt.util.StringBuilder;
20 
21 import hunt.collection.List;
22 import hunt.collection.Map;
23 import hunt.Exceptions;
24 import hunt.Long;
25 import hunt.util.Common;
26 
27 import std.conv;
28 
29 import hunt.logging.ConsoleLogger;
30 
31 //-----------------------------------------------------------------------
32 /**
33 * Prints or parses field text.
34 */
35 static final class TextPrinterParser : DateTimePrinterParser {
36     private TemporalField field;
37     private TextStyle textStyle;
38     private DateTimeTextProvider provider;
39     /**
40     * The cached number printer parser.
41     * Immutable and volatile, so no synchronization needed.
42     */
43     private NumberPrinterParser _numberPrinterParser;
44 
45     /**
46     * Constructor.
47     *
48     * @param field  the field to output, not null
49     * @param textStyle  the text style, not null
50     * @param provider  the text provider, not null
51     */
52     this(TemporalField field, TextStyle textStyle, DateTimeTextProvider provider) {
53         // validated by caller
54         this.field = field;
55         this.textStyle = textStyle;
56         this.provider = provider;
57     }
58 
59     override public bool format(DateTimePrintContext context, StringBuilder buf) {
60         Long value = context.getValue(field);
61         version(HUNT_DEBUG) tracef("value is null: %s", value is null);
62         if (value is null) {
63             return false;
64         }
65         string text;
66         Chronology chrono = QueryHelper.query!Chronology(context.getTemporal(),
67                 TemporalQueries.chronology());
68         if (chrono is null || chrono == IsoChronology.INSTANCE) {
69             text = provider.getText(field, value.longValue(), textStyle, context.getLocale());
70         } else {
71             text = provider.getText(chrono, field, value.longValue(),
72                     textStyle, context.getLocale());
73         }
74         if (text is null) {
75             return numberPrinterParser().format(context, buf);
76         }
77         trace("xxxxxx=>", text);
78         buf.append(text);
79         return true;
80     }
81 
82     override public int parse(DateTimeParseContext context, string parseText, int position) {
83         int length = cast(int)(parseText.length);
84         if (position < 0 || position > length) {
85             throw new IndexOutOfBoundsException();
86         }
87         TextStyle style = (context.isStrict() ? textStyle : null);
88         Chronology chrono = context.getEffectiveChronology();
89         Iterable!(MapEntry!(string, Long)) it;
90         if (chrono is null || chrono == IsoChronology.INSTANCE) {
91             it = provider.getTextIterator(field, style, context.getLocale());
92         } else {
93             it = provider.getTextIterator(chrono, field, style, context.getLocale());
94         }
95         if (it !is null) {
96             foreach (MapEntry!(string, Long) entry; it) {
97                 string itText = entry.getKey();
98                 if (context.subSequenceEquals(itText, 0, parseText, position,
99                         cast(int)(itText.length))) {
100                     return context.setParsedField(field, entry.getValue()
101                             .longValue(), position, position + cast(int)(itText.length));
102                 }
103             }
104             if (field == ChronoField.ERA && !context.isStrict()) {
105                 // parse the possible era name from era.toString()
106                 List!(Era) eras = chrono.eras();
107                 foreach (Era era; eras) {
108                     string name = era.toString();
109                     if (context.subSequenceEquals(name, 0, parseText, position,
110                             cast(int)(name.length))) {
111                         return context.setParsedField(field, era.getValue(),
112                                 position, position + cast(int)(name.length));
113                     }
114                 }
115             }
116             if (context.isStrict()) {
117                 return ~position;
118             }
119         }
120         return numberPrinterParser().parse(context, parseText, position);
121     }
122 
123     /**
124     * Create and cache a number printer parser.
125     * @return the number printer parser for this field, not null
126     */
127     private NumberPrinterParser numberPrinterParser() {
128         if (_numberPrinterParser is null) {
129             _numberPrinterParser = new NumberPrinterParser(field, 1, 19, SignStyle.NORMAL);
130         }
131         return _numberPrinterParser;
132     }
133 
134     override public string toString() {
135         if (textStyle == TextStyle.FULL) {
136             return "Text(" ~ typeid(field).name ~ ")";
137         }
138         return "Text(" ~ typeid(field).name ~ "," ~ textStyle.ordinal().to!string ~ ")";
139     }
140 }