OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of intl; | 5 part of intl; |
6 | 6 |
7 /** | 7 /** |
8 * This is a private class internal to DateFormat which is used for formatting | 8 * This is a private class internal to DateFormat which is used for formatting |
9 * particular fields in a template. e.g. if the format is hh:mm:ss then the | 9 * particular fields in a template. e.g. if the format is hh:mm:ss then the |
10 * fields would be "hh", ":", "mm", ":", and "ss". Each type of field knows | 10 * fields would be "hh", ":", "mm", ":", and "ss". Each type of field knows |
(...skipping 21 matching lines...) Expand all Loading... |
32 /** Format date according to our specification and return the result. */ | 32 /** Format date according to our specification and return the result. */ |
33 String format(DateTime date) { | 33 String format(DateTime date) { |
34 // Default implementation in the superclass, works for both types of | 34 // Default implementation in the superclass, works for both types of |
35 // literal patterns, and is overridden by _DateFormatPatternField. | 35 // literal patterns, and is overridden by _DateFormatPatternField. |
36 return pattern; | 36 return pattern; |
37 } | 37 } |
38 | 38 |
39 /** Abstract method for subclasses to implementing parsing for their format.*/ | 39 /** Abstract method for subclasses to implementing parsing for their format.*/ |
40 void parse(_Stream input, _DateBuilder dateFields); | 40 void parse(_Stream input, _DateBuilder dateFields); |
41 | 41 |
| 42 /** |
| 43 * Abstract method for subclasses to implementing 'loose' parsing for |
| 44 * their format, accepting input case-insensitively, and allowing some |
| 45 * delimiters to be skipped. |
| 46 */ |
| 47 void parseLoose(_Stream input, _DateBuilder dateFields); |
| 48 |
42 /** Parse a literal field. We just look for the exact input. */ | 49 /** Parse a literal field. We just look for the exact input. */ |
43 void parseLiteral(_Stream input) { | 50 void parseLiteral(_Stream input) { |
44 var found = input.read(width); | 51 var found = input.read(width); |
45 if (found != pattern) { | 52 if (found != pattern) { |
46 throwFormatException(input); | 53 throwFormatException(input); |
47 } | 54 } |
48 } | 55 } |
49 | 56 |
| 57 /** |
| 58 * Parse a literal field. We accept either an exact match, or an arbitrary |
| 59 * amount of whitespace. |
| 60 */ |
| 61 void parseLiteralLoose(_Stream input) { |
| 62 var found = input.peek(width); |
| 63 if (found == pattern) { |
| 64 input.read(width); |
| 65 } |
| 66 while (!input.atEnd() && input.peek().trim().isEmpty) { |
| 67 input.read(); |
| 68 } |
| 69 } |
| 70 |
50 /** Throw a format exception with an error message indicating the position.*/ | 71 /** Throw a format exception with an error message indicating the position.*/ |
51 void throwFormatException(_Stream stream) { | 72 void throwFormatException(_Stream stream) { |
52 throw new FormatException("Trying to read $this from ${stream.contents} " | 73 throw new FormatException("Trying to read $this from ${stream.contents} " |
53 "at position ${stream.index}"); | 74 "at position ${stream.index}"); |
54 } | 75 } |
55 } | 76 } |
56 | 77 |
57 /** | 78 /** |
58 * Represents a literal field - a sequence of characters that doesn't | 79 * Represents a literal field - a sequence of characters that doesn't |
59 * change according to the date's data. As such, the implementation | 80 * change according to the date's data. As such, the implementation |
60 * is extremely simple. | 81 * is extremely simple. |
61 */ | 82 */ |
62 class _DateFormatLiteralField extends _DateFormatField { | 83 class _DateFormatLiteralField extends _DateFormatField { |
63 | 84 |
64 _DateFormatLiteralField(pattern, parent): super(pattern, parent); | 85 _DateFormatLiteralField(pattern, parent): super(pattern, parent); |
65 | 86 |
66 parse(_Stream input, _DateBuilder dateFields) { | 87 parse(_Stream input, _DateBuilder dateFields) { |
67 return parseLiteral(input); | 88 parseLiteral(input); |
68 } | 89 } |
| 90 |
| 91 parseLoose(_Stream input, _DateBuilder dateFields) => |
| 92 parseLiteralLoose(input); |
69 } | 93 } |
70 | 94 |
71 /** | 95 /** |
72 * Represents a literal field with quoted characters in it. This is | 96 * Represents a literal field with quoted characters in it. This is |
73 * only slightly more complex than a _DateFormatLiteralField. | 97 * only slightly more complex than a _DateFormatLiteralField. |
74 */ | 98 */ |
75 class _DateFormatQuotedField extends _DateFormatField { | 99 class _DateFormatQuotedField extends _DateFormatField { |
76 | 100 |
77 String _fullPattern; | 101 String _fullPattern; |
78 | 102 |
79 String fullPattern() => _fullPattern; | 103 String fullPattern() => _fullPattern; |
80 | 104 |
81 _DateFormatQuotedField(pattern, parent): super(pattern, parent) { | 105 _DateFormatQuotedField(pattern, parent): super(pattern, parent) { |
82 _fullPattern = pattern; | 106 _fullPattern = pattern; |
83 patchQuotes(); | 107 patchQuotes(); |
84 } | 108 } |
85 | 109 |
86 parse(_Stream input, _DateBuilder dateFields) { | 110 parse(_Stream input, _DateBuilder dateFields) { |
87 return parseLiteral(input); | 111 parseLiteral(input); |
88 } | 112 } |
89 | 113 |
| 114 parseLoose(_Stream input, _DateBuilder dateFields) => |
| 115 parseLiteralLoose(input); |
| 116 |
90 void patchQuotes() { | 117 void patchQuotes() { |
91 if (pattern == "''") { | 118 if (pattern == "''") { |
92 pattern = "'"; | 119 pattern = "'"; |
93 } else { | 120 } else { |
94 pattern = pattern.substring(1, pattern.length - 1); | 121 pattern = pattern.substring(1, pattern.length - 1); |
95 var twoEscapedQuotes = new RegExp(r"''"); | 122 var twoEscapedQuotes = new RegExp(r"''"); |
96 pattern = pattern.replaceAll(twoEscapedQuotes, "'"); | 123 pattern = pattern.replaceAll(twoEscapedQuotes, "'"); |
97 } | 124 } |
98 } | 125 } |
99 } | 126 } |
100 | 127 |
| 128 /** |
| 129 * A field that parses "loosely", meaning that we'll accept input that is |
| 130 * missing delimiters, has upper/lower case mixed up, and might not strictly |
| 131 * conform to the pattern, e.g. the pattern calls for Sep we might accept |
| 132 * sep, september, sEPTember. Doesn't affect numeric fields. |
| 133 */ |
| 134 class _LoosePatternField extends _DateFormatPatternField { |
| 135 _LoosePatternField(String pattern, parent) : super(pattern, parent); |
| 136 |
| 137 /** |
| 138 * Parse from a list of possibilities, but case-insensitively. |
| 139 * Assumes that input is lower case. |
| 140 */ |
| 141 int parseEnumeratedString(_Stream input, List possibilities) { |
| 142 var lowercasePossibilities = possibilities |
| 143 .map((x) => x.toLowerCase()) |
| 144 .toList(); |
| 145 try { |
| 146 return super.parseEnumeratedString(input, lowercasePossibilities); |
| 147 } on FormatException { |
| 148 return -1; |
| 149 } |
| 150 } |
| 151 |
| 152 /** |
| 153 * Parse a month name, case-insensitively, and set it in [dateFields]. |
| 154 * Assumes that [input] is lower case. |
| 155 */ |
| 156 void parseMonth(input, dateFields) { |
| 157 if (width <= 2) { |
| 158 handleNumericField(input, dateFields.setMonth); |
| 159 return; |
| 160 } |
| 161 var possibilities = |
| 162 [symbols.MONTHS, symbols.SHORTMONTHS]; |
| 163 for (var monthNames in possibilities) { |
| 164 var month = parseEnumeratedString(input, monthNames); |
| 165 if (month != -1) { |
| 166 dateFields.month = month + 1; |
| 167 return; |
| 168 } |
| 169 } |
| 170 } |
| 171 |
| 172 /** |
| 173 * Parse a standalone day name, case-insensitively. |
| 174 * Assumes that input is lower case. Doesn't do anything |
| 175 */ |
| 176 void parseStandaloneDay(input) { |
| 177 // This is ignored, but we still have to skip over it the correct amount. |
| 178 if (width <= 2) { |
| 179 handleNumericField(input, (x) => x); |
| 180 return; |
| 181 } |
| 182 var possibilities = |
| 183 [symbols.STANDALONEWEEKDAYS, symbols.STANDALONESHORTWEEKDAYS]; |
| 184 for (var dayNames in possibilities) { |
| 185 var day = parseEnumeratedString(input, dayNames); |
| 186 if (day != -1) { |
| 187 return; |
| 188 } |
| 189 } |
| 190 } |
| 191 |
| 192 /** |
| 193 * Parse a standalone month name, case-insensitively. |
| 194 * Assumes that input is lower case. Doesn't do anything |
| 195 */ |
| 196 void parseStandaloneMonth(input, dateFields) { |
| 197 if (width <= 2) { |
| 198 handleNumericField(input, (x) => x); |
| 199 return; |
| 200 } |
| 201 var possibilities = |
| 202 [symbols.STANDALONEMONTHS, symbols.STANDALONESHORTMONTHS]; |
| 203 for (var monthNames in possibilities) { |
| 204 var month = parseEnumeratedString(input, monthNames); |
| 205 if (month != -1) { |
| 206 dateFields.month = month + 1; |
| 207 return; |
| 208 } |
| 209 } |
| 210 } |
| 211 |
| 212 /** |
| 213 * Parse a day of the week name, case-insensitively. |
| 214 * Assumes that input is lower case. Doesn't do anything |
| 215 */ |
| 216 void parseDayOfWeek(_Stream input) { |
| 217 // This is IGNORED, but we still have to skip over it the correct amount. |
| 218 if (width <= 2) { |
| 219 handleNumericField(input, (x) => x); |
| 220 return; |
| 221 } |
| 222 var possibilities = [symbols.WEEKDAYS, symbols.SHORTWEEKDAYS]; |
| 223 for (var dayNames in possibilities) { |
| 224 var day = parseEnumeratedString(input, dayNames); |
| 225 if (day != -1) { |
| 226 return; |
| 227 } |
| 228 } |
| 229 } |
| 230 } |
| 231 |
101 /* | 232 /* |
102 * Represents a field in the pattern that formats some aspect of the | 233 * Represents a field in the pattern that formats some aspect of the |
103 * date. Consists primarily of a switch on the particular pattern characters | 234 * date. Consists primarily of a switch on the particular pattern characters |
104 * to determine what to do. | 235 * to determine what to do. |
105 */ | 236 */ |
106 class _DateFormatPatternField extends _DateFormatField { | 237 class _DateFormatPatternField extends _DateFormatField { |
107 | 238 |
108 _DateFormatPatternField(pattern, parent): super(pattern, parent); | 239 _DateFormatPatternField(pattern, parent): super(pattern, parent); |
109 | 240 |
110 /** Format date according to our specification and return the result. */ | 241 /** Format date according to our specification and return the result. */ |
111 String format(DateTime date) { | 242 String format(DateTime date) { |
112 return formatField(date); | 243 return formatField(date); |
113 } | 244 } |
114 | 245 |
115 /** | 246 /** |
116 * Parse the date according to our specification and put the result | 247 * Parse the date according to our specification and put the result |
117 * into the correct place in dateFields. | 248 * into the correct place in dateFields. |
118 */ | 249 */ |
119 void parse(_Stream input, _DateBuilder dateFields) { | 250 void parse(_Stream input, _DateBuilder dateFields) { |
120 parseField(input, dateFields); | 251 parseField(input, dateFields); |
121 } | 252 } |
122 | 253 |
| 254 |
| 255 /** |
| 256 * Parse the date according to our specification and put the result |
| 257 * into the correct place in dateFields. Allow looser parsing, accepting |
| 258 * case-insensitive input and skipped delimiters. |
| 259 */ |
| 260 void parseLoose(_Stream input, _DateBuilder dateFields) { |
| 261 new _LoosePatternField(pattern, parent).parse(input, dateFields); |
| 262 } |
| 263 |
123 /** | 264 /** |
124 * Parse a field representing part of a date pattern. Note that we do not | 265 * Parse a field representing part of a date pattern. Note that we do not |
125 * return a value, but rather build up the result in [builder]. | 266 * return a value, but rather build up the result in [builder]. |
126 */ | 267 */ |
127 void parseField(_Stream input, _DateBuilder builder) { | 268 void parseField(_Stream input, _DateBuilder builder) { |
128 try { | 269 try { |
129 switch(pattern[0]) { | 270 switch(pattern[0]) { |
130 case 'a': parseAmPm(input, builder); break; | 271 case 'a': parseAmPm(input, builder); break; |
131 case 'c': parseStandaloneDay(input); break; | 272 case 'c': parseStandaloneDay(input); break; |
132 case 'd': handleNumericField(input, builder.setDay); break; // day | 273 case 'd': handleNumericField(input, builder.setDay); break; // day |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
426 var basicString = toBePrinted.toString(); | 567 var basicString = toBePrinted.toString(); |
427 if (basicString.length >= width) return basicString; | 568 if (basicString.length >= width) return basicString; |
428 var buffer = new StringBuffer(); | 569 var buffer = new StringBuffer(); |
429 for (var i = 0; i < width - basicString.length; i++) { | 570 for (var i = 0; i < width - basicString.length; i++) { |
430 buffer.write('0'); | 571 buffer.write('0'); |
431 } | 572 } |
432 buffer.write(basicString); | 573 buffer.write(basicString); |
433 return buffer.toString(); | 574 return buffer.toString(); |
434 } | 575 } |
435 } | 576 } |
OLD | NEW |