3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 namespace System.Globalization {
10 using System.Diagnostics.Contracts;
12 ////////////////////////////////////////////////////////////////////////////
14 // Used in HebrewNumber.ParseByChar to maintain the context information (
15 // the state in the state machine and current Hebrew number values, etc.)
16 // when parsing Hebrew number character by character.
18 ////////////////////////////////////////////////////////////////////////////
20 internal struct HebrewNumberParsingContext {
21 // The current state of the state machine for parsing Hebrew numbers.
22 internal HebrewNumber.HS state;
23 // The current value of the Hebrew number.
24 // The final value is determined when state is FoundEndOfHebrewNumber.
27 public HebrewNumberParsingContext(int result) {
28 // Set the start state of the state machine for parsing Hebrew numbers.
29 state = HebrewNumber.HS.Start;
34 ////////////////////////////////////////////////////////////////////////////
36 // Please see ParseByChar() for comments about different states defined here.
38 ////////////////////////////////////////////////////////////////////////////
40 internal enum HebrewNumberParsingState {
43 FoundEndOfHebrewNumber,
47 ////////////////////////////////////////////////////////////////////////////
51 // Provides static methods for formatting integer values into
52 // Hebrew text and parsing Hebrew number text.
55 // Parse can only handles value 1 ~ 999.
56 // ToString() can only handles 1 ~ 999. If value is greater than 5000,
57 // 5000 will be subtracted from the value.
59 ////////////////////////////////////////////////////////////////////////////
61 internal class HebrewNumber {
63 // This class contains only static methods. Add a private ctor so that
64 // compiler won't generate a default one for us.
65 private HebrewNumber() {
68 ////////////////////////////////////////////////////////////////////////////
72 // Converts the given number to Hebrew letters according to the numeric
73 // value of each Hebrew letter. Basically, this converts the lunar year
74 // and the lunar month to letters.
76 // The character of a year is described by three letters of the Hebrew
77 // alphabet, the first and third giving, respectively, the days of the
78 // weeks on which the New Year occurs and Passover begins, while the
79 // second is the initial of the Hebrew word for defective, normal, or
82 // Defective Year : Both Heshvan and Kislev are defective (353 or 383 days)
83 // Normal Year : Heshvan is defective, Kislev is full (354 or 384 days)
84 // Complete Year : Both Heshvan and Kislev are full (355 or 385 days)
86 ////////////////////////////////////////////////////////////////////////////
88 internal static String ToString(int Number) {
90 char cUnits; // tens and units chars
91 int Hundreds, Tens; // hundreds and tens values
92 StringBuilder szHebrew = new StringBuilder();
96 // Adjust the number if greater than 5000.
102 Contract.Assert(Number > 0 && Number <= 999, "Number is out of range.");;
107 Hundreds = Number / 100;
110 Number -= Hundreds * 100;
115 // If the number is greater than 400, use the multiples of 400.
116 for (int i = 0; i < (Hundreds / 4) ; i++) {
117 szHebrew.Append('\x05ea');
120 int remains = Hundreds % 4;
122 szHebrew.Append((char)((int)'\x05e6' + remains));
137 cTens = '\x05d9'; // Hebrew Letter Yod
140 cTens = '\x05db'; // Hebrew Letter Kaf
143 cTens = '\x05dc'; // Hebrew Letter Lamed
146 cTens = '\x05de'; // Hebrew Letter Mem
149 cTens = '\x05e0'; // Hebrew Letter Nun
152 cTens = '\x05e1'; // Hebrew Letter Samekh
155 cTens = '\x05e2'; // Hebrew Letter Ayin
158 cTens = '\x05e4'; // Hebrew Letter Pe
161 cTens = '\x05e6'; // Hebrew Letter Tsadi
168 cUnits = (char)(Number > 0 ? ((int)'\x05d0' + Number - 1) : 0);
170 if ((cUnits == '\x05d4') && // Hebrew Letter He (5)
171 (cTens == '\x05d9')) { // Hebrew Letter Yod (10)
172 cUnits = '\x05d5'; // Hebrew Letter Vav (6)
173 cTens = '\x05d8'; // Hebrew Letter Tet (9)
176 if ((cUnits == '\x05d5') && // Hebrew Letter Vav (6)
177 (cTens == '\x05d9')) { // Hebrew Letter Yod (10)
178 cUnits = '\x05d6'; // Hebrew Letter Zayin (7)
179 cTens = '\x05d8'; // Hebrew Letter Tet (9)
183 // Copy the appropriate info to the given buffer.
186 if (cTens != '\x0') {
187 szHebrew.Append(cTens);
190 if (cUnits != '\x0') {
191 szHebrew.Append(cUnits);
194 if (szHebrew.Length > 1) {
195 szHebrew.Insert(szHebrew.Length - 1, '"');
197 szHebrew.Append('\'');
203 return (szHebrew.ToString());
206 ////////////////////////////////////////////////////////////////////////////
208 // Token used to tokenize a Hebrew word into tokens so that we can use in the
211 ////////////////////////////////////////////////////////////////////////////
218 Digit10 = 3, // 10 ~ 90
219 Digit1 = 4, // 1, 2, 3, 4, 5, 8,
227 ////////////////////////////////////////////////////////////////////////////
229 // This class is used to map a token into its Hebrew digit value.
231 ////////////////////////////////////////////////////////////////////////////
234 internal HebrewToken token;
236 internal HebrewValue(HebrewToken token, int value) {
243 // Map a Hebrew character from U+05D0 ~ U+05EA to its digit value.
244 // The value is -1 if the Hebrew character does not have a associated value.
246 static HebrewValue[] HebrewValues = {
247 new HebrewValue(HebrewToken.Digit1, 1) , // '\x05d0
248 new HebrewValue(HebrewToken.Digit1, 2) , // '\x05d1
249 new HebrewValue(HebrewToken.Digit1, 3) , // '\x05d2
250 new HebrewValue(HebrewToken.Digit1, 4) , // '\x05d3
251 new HebrewValue(HebrewToken.Digit1, 5) , // '\x05d4
252 new HebrewValue(HebrewToken.Digit6_7,6) , // '\x05d5
253 new HebrewValue(HebrewToken.Digit6_7,7) , // '\x05d6
254 new HebrewValue(HebrewToken.Digit1, 8) , // '\x05d7
255 new HebrewValue(HebrewToken.Digit9, 9) , // '\x05d8
256 new HebrewValue(HebrewToken.Digit10, 10) , // '\x05d9; // Hebrew Letter Yod
257 new HebrewValue(HebrewToken.Invalid, -1) , // '\x05da;
258 new HebrewValue(HebrewToken.Digit10, 20) , // '\x05db; // Hebrew Letter Kaf
259 new HebrewValue(HebrewToken.Digit10, 30) , // '\x05dc; // Hebrew Letter Lamed
260 new HebrewValue(HebrewToken.Invalid, -1) , // '\x05dd;
261 new HebrewValue(HebrewToken.Digit10, 40) , // '\x05de; // Hebrew Letter Mem
262 new HebrewValue(HebrewToken.Invalid, -1) , // '\x05df;
263 new HebrewValue(HebrewToken.Digit10, 50) , // '\x05e0; // Hebrew Letter Nun
264 new HebrewValue(HebrewToken.Digit10, 60) , // '\x05e1; // Hebrew Letter Samekh
265 new HebrewValue(HebrewToken.Digit10, 70) , // '\x05e2; // Hebrew Letter Ayin
266 new HebrewValue(HebrewToken.Invalid, -1) , // '\x05e3;
267 new HebrewValue(HebrewToken.Digit10, 80) , // '\x05e4; // Hebrew Letter Pe
268 new HebrewValue(HebrewToken.Invalid, -1) , // '\x05e5;
269 new HebrewValue(HebrewToken.Digit10, 90) , // '\x05e6; // Hebrew Letter Tsadi
270 new HebrewValue(HebrewToken.Digit100, 100) , // '\x05e7;
271 new HebrewValue(HebrewToken.Digit200_300, 200) , // '\x05e8;
272 new HebrewValue(HebrewToken.Digit200_300, 300) , // '\x05e9;
273 new HebrewValue(HebrewToken.Digit400, 400) , // '\x05ea;
276 const int minHebrewNumberCh = 0x05d0;
277 static char maxHebrewNumberCh = (char)(minHebrewNumberCh + HebrewValues.Length - 1);
279 ////////////////////////////////////////////////////////////////////////////
281 // Hebrew number parsing State
282 // The current state and the next token will lead to the next state in the state machine.
285 ////////////////////////////////////////////////////////////////////////////
288 _err = -1, // an error state
290 S400 = 1, // a Hebrew digit 400
291 S400_400 = 2, // Two Hebrew digit 400
292 S400_X00 = 3, // Two Hebrew digit 400 and followed by 100
293 S400_X0 = 4, // Hebrew digit 400 and followed by 10 ~ 90
294 X00_DQ = 5, // A hundred number and followed by a double quote.
296 X0_DQ = 7, // A two-digit number and followed by a double quote.
297 X = 8, // A single digit Hebrew number.
298 X0 = 9, // A two-digit Hebrew number
299 X00 = 10, // A three-digit Hebrew number
300 S400_DQ = 11, // A Hebrew digit 400 and followed by a double quote.
303 S9 = 14, // Hebrew digit 9
304 X00_S9 = 15, // A hundered number and followed by a digit 9
305 S9_DQ = 16, // Hebrew digit 9 and followed by a double quote
306 END = 100, // A terminial state is reached.
310 // The state machine for Hebrew number pasing.
312 readonly static HS[][] NumberPasingState = {
313 // 400 300/200 100 90~10 8~1 6, 7, 9, ' "
314 /* 0 */ new HS[] {HS.S400, HS.X00, HS.X00, HS.X0, HS.X, HS.X, HS.X, HS.S9, HS._err, HS._err},
315 /* 1: S400 */ new HS[] {HS.S400_400, HS.S400_X00, HS.S400_X00, HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS.END, HS.S400_DQ},
316 /* 2: S400_400 */ new HS[] {HS._err, HS._err, HS.S400_400_100,HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS._err, HS.S400_400_DQ},
317 /* 3: S400_X00 */ new HS[] {HS._err, HS._err, HS._err, HS.S400_X00_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS._err, HS.X00_DQ},
318 /* 4: S400_X0 */ new HS[] {HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.X0_DQ},
319 /* 5: X00_DQ */ new HS[] {HS._err, HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err},
320 /* 6: S400_X00_X0 */new HS[] {HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.X0_DQ},
321 /* 7: X0_DQ */ new HS[] {HS._err, HS._err, HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err},
322 /* 8: X */ new HS[] {HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS._err},
323 /* 9: X0 */ new HS[] {HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.X0_DQ},
324 /* 10: X00 */ new HS[] {HS._err, HS._err, HS._err, HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9, HS.END, HS.X00_DQ},
325 /* 11: S400_DQ */ new HS[] {HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err},
326 /* 12: S400_400_DQ*/new HS[] {HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err},
327 /* 13: S400_400_100*/new HS[]{HS._err, HS._err, HS._err, HS.S400_X00_X0, HS._err, HS._err, HS._err, HS.X00_S9, HS._err, HS.X00_DQ},
328 /* 14: S9 */ new HS[] {HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err,HS.END, HS.S9_DQ},
329 /* 15: X00_S9 */ new HS[] {HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.S9_DQ},
330 /* 16: S9_DQ */ new HS[] {HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.END, HS._err, HS._err, HS._err},
334 ////////////////////////////////////////////////////////////////////////
337 // Parse the Hebrew number by passing one character at a time.
338 // The state between characters are maintained at HebrewNumberPasingContext.
340 // Return a enum of HebrewNumberParsingState.
341 // NotHebrewDigit: The specified ch is not a valid Hebrew digit.
342 // InvalidHebrewNumber: After parsing the specified ch, it will lead into
343 // an invalid Hebrew number text.
344 // FoundEndOfHebrewNumber: A terminal state is reached. This means that
345 // we find a valid Hebrew number text after the specified ch is parsed.
346 // ContinueParsing: The specified ch is a valid Hebrew digit, and
347 // it will lead into a valid state in the state machine, we should
348 // continue to parse incoming characters.
350 ////////////////////////////////////////////////////////////////////////
352 internal static HebrewNumberParsingState ParseByChar(char ch, ref HebrewNumberParsingContext context) {
355 token = HebrewToken.SingleQuote;
356 } else if (ch == '\"') {
357 token = HebrewToken.DoubleQuote;
359 int index = (int)ch - minHebrewNumberCh;
360 if (index >= 0 && index < HebrewValues.Length ) {
361 token = HebrewValues[index].token;
362 if (token == HebrewToken.Invalid) {
363 return (HebrewNumberParsingState.NotHebrewDigit);
365 context.result += HebrewValues[index].value;
367 // Not in valid Hebrew digit range.
368 return (HebrewNumberParsingState.NotHebrewDigit);
371 context.state = NumberPasingState[(int)context.state][(int)token];
372 if (context.state == HS._err) {
373 // Invalid Hebrew state. This indicates an incorrect Hebrew number.
374 return (HebrewNumberParsingState.InvalidHebrewNumber);
376 if (context.state == HS.END) {
377 // Reach a terminal state.
378 return (HebrewNumberParsingState.FoundEndOfHebrewNumber);
380 // We should continue to parse.
381 return (HebrewNumberParsingState.ContinueParsing);
384 ////////////////////////////////////////////////////////////////////////
387 // Check if the ch is a valid Hebrew number digit.
388 // This function will return true if the specified char is a legal Hebrew
389 // digit character, single quote, or double quote.
391 // true if the specified character is a valid Hebrew number character.
393 ////////////////////////////////////////////////////////////////////////
395 internal static bool IsDigit(char ch) {
396 if (ch >= minHebrewNumberCh && ch <= maxHebrewNumberCh) {
397 return (HebrewValues[ch - minHebrewNumberCh].value >= 0);
399 return (ch == '\'' || ch == '\"');