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 }