001package Torello.CSS; 002 003import Torello.Java.Additional.ByRef; 004 005import java.util.Vector; 006import java.util.stream.IntStream; 007import java.util.function.Consumer; 008import java.math.BigDecimal; 009 010/** 011 * CSS-Tokenizer {@code Number}-Literal & {@code Number}-Token Class. 012 */ 013@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="CSS_TOK") 014public class Num extends CSSToken 015 implements CharSequence, java.io.Serializable, Comparable<CharSequence> 016{ 017 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 018 protected static final long serialVersionUID = 1; 019 020 021 // ******************************************************************************************** 022 // ******************************************************************************************** 023 // Public & Final Fields 024 // ******************************************************************************************** 025 // ******************************************************************************************** 026 027 028 /** 029 * Though Java's {@code BigDecimal} may be an "overly ambitious" means of representing 030 * CSS-Extracted Number-Literals, for now, this is how it is going to work. After the 031 * {@code 'javadoc'} stuff is done, maybe I'll change it to {@code java.lang.Number} or 032 * something else. 033 * 034 * <BR /><BR />Yes, a {@code "2em"} or {@code "10px"} would be saved as a BigDecimal 035 * {@code 2} and {@code 10}. The upside is that Java {@code double} and {@code integer} 036 * primitives are easily extracted using {@code java.math.BigDecimal}'s exported methods. 037 */ 038 public final BigDecimal number; 039 040 /** 041 * The parser will return true if the parsed Number-Literal had neither a "Decimal Part", 042 * nor an "Exponent Part". If either of these were present, then this {@code boolean} will 043 * contain {@code FALSE}. 044 * 045 * <BR /><BR />Note that even though something like {@code 5e2} - <I>which is actually just the 046 * integer {@code 500}</I> - were parsed, this {@code boolean} would still evaluate to 047 * {@code FALSE}. 048 */ 049 public final boolean integerOrNumber; 050 051 /** 052 * This shall contain one of three values: {@code '+', '-'} or ASCII {@code 0}. If the parsed 053 * Number-Literal began with a sign-character, then the appropriate sign-character will be 054 * stored. If the Number-Literal had no sign-character, then a {@code 0} is stored. 055 */ 056 public final char signChar; 057 058 059 // ******************************************************************************************** 060 // ******************************************************************************************** 061 // Private Constructor, API "is" and "if" Methods 062 // ******************************************************************************************** 063 // ******************************************************************************************** 064 065 066 private Num( 067 final int[] css, 068 final int sPos, 069 final int ePos, 070 final BigDecimal number, 071 final boolean integerOrNumber, 072 final char signChar 073 ) 074 { 075 super(css, sPos, ePos); 076 077 this.number = number; 078 this.integerOrNumber = integerOrNumber; 079 this.signChar = signChar; 080 } 081 082 Num( 083 final int[] css, 084 final int sPos, 085 final int ePos, 086 final Num n 087 ) 088 { this(css, sPos, ePos, n.number, n.integerOrNumber, n.signChar); } 089 090 @Override 091 public final boolean isNum() { return true; } 092 093 @Override 094 public final Num ifNum() { return this; } 095 096 097 // ******************************************************************************************** 098 // ******************************************************************************************** 099 // User's Constructor: a static "build" method 100 // ******************************************************************************************** 101 // ******************************************************************************************** 102 103 104 /** 105 * <EMBED CLASS=defs DATA-TOK=Num DATA-P=numStr> 106 * <EMBED CLASS='external-html' DATA-FILE-ID=BUILD_DESC> 107 * @param numStr <EMBED CLASS='external-html' DATA-FILE-ID=BUILD_PARAM> 108 * @return <EMBED CLASS='external-html' DATA-FILE-ID=BUILD_RET> 109 * @throws TokenizeException <EMBED CLASS='external-html' DATA-FILE-ID=BUILD_TOK_EX> 110 */ 111 @SuppressWarnings("unchecked") 112 public static Num build(final String numStr) 113 { return (Num) CSSToken.build(numStr, INPUT_CHECKER, Num::consume); } 114 115 private static final CSSToken.InputChecker INPUT_CHECKER = (int[] css) -> 116 { 117 if (css.length < 1) throw new TokenizeException(Num.class); 118 119 if (! Num.is(css, 0)) throw new TokenizeException 120 ("String-text beginning does not constitute a valid CSS Number-Token"); 121 }; 122 123 124 // ******************************************************************************************** 125 // ******************************************************************************************** 126 // Tokenizer's "is" Method(s) 127 // ******************************************************************************************** 128 // ******************************************************************************************** 129 130 131 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 132 // Copied from: 133 // https://drafts.csswg.org/css-syntax-3/#check-if-three-code-points-would-start-a-number 134 // March 2024 135 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 136 // 137 // 4.3.10. Check if three code points would start a number 138 // 139 // This section describes how to check if three code points would start a number. The algorithm 140 // described here can be called explicitly with three code points, or can be called with the 141 // input stream itself. In the latter case, the three code points in question are the current 142 // input code point and the next two input code points, in that order. 143 // 144 // NOTE: This algorithm will not consume any additional code points. 145 // 146 // Look at the first code point: 147 // 148 // ** U+002B PLUS SIGN (+) 149 // ** U+002D HYPHEN-MINUS (-) 150 // ==> 1) If the second code point is a digit, return true. 151 // 2) Otherwise, if the second code point is a U+002E FULL STOP (.) and the third code 152 // point is a digit, return true. 153 // 3) Otherwise, return false. 154 // 155 // ** U+002E FULL STOP (.) 156 // ==> If the second code point is a digit, return true. Otherwise, return false. 157 // 158 // ** digit 159 // ==> Return true. 160 // 161 // ** anything else 162 // ==> Return false. 163 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 164 165 /** 166 * Checks whether or not the next token to consume is a number token, or number-subclass token. 167 * 168 * <EMBED CLASS=defs DATA-TOK=Number-Literal 169 * DATA-URL=check-if-three-code-points-would-start-a-number DATA-OP=Check> 170 * <EMBED CLASS=external-html DATA-FILE-ID=COPIED_CSS_WG> 171 * <EMBED CLASS=external-html DATA-FILE-ID=CHECK_NUMBER_3CP> 172 * @param css CSS-{@code String} as an array of code-points. 173 * @param sPos The array-index where the tokenizer is to consume its next token 174 * @return {@code TRUE} if and only if the next token in the array is a number 175 */ 176 public static boolean is(int[] css, final int sPos) 177 { 178 final int c1 = ((sPos+0) < css.length) ? css[sPos+0] : 0; 179 final int c2 = ((sPos+1) < css.length) ? css[sPos+1] : 0; 180 final int c3 = ((sPos+2) < css.length) ? css[sPos+2] : 0; 181 182 // U+002B PLUS SIGN (+) **OR** U+002D HYPHEN-MINUS (-) 183 if ((c1 == '+') || (c1 == '-')) 184 { 185 // 1) If the second code point is a digit, return true. 186 if ((c2 >= '0') && (c2 <= '9')) return true; 187 188 // 2) Otherwise, if the second code point is a U+002E FULL STOP (.) and the third code 189 // point is a digit, return true. 190 191 if ((c2 == '.') && (c3 >= '0') && (c3 <= '9')) return true; 192 193 // 3) Otherwise, return false. 194 return false; 195 } 196 197 // U+002E FULL STOP (.) 198 // If the second code point is a digit, return true. Otherwise, return false. 199 200 if (c1 == '.') return ((c2 >= '0') && (c2 <= '9')); 201 202 // digit ==> Return true. 203 if ((c1 >= '0') && (c1 <= '9')) return true; 204 205 // anything else ==> Return false. 206 return false; 207 } 208 209 210 // ******************************************************************************************** 211 // ******************************************************************************************** 212 // CONSUME 213 // ******************************************************************************************** 214 // ******************************************************************************************** 215 216 217 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 218 // Copied from: 219 // https://drafts.csswg.org/css-syntax-3/#consume-a-numeric-token 220 // March 27, 2024 221 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 222 // 223 // 4.3.3. Consume a numeric token 224 // 225 // This section describes how to consume a numeric token from a stream of code points. It 226 // returns either a <number-token>, <percentage-token>, or <dimension-token>. 227 // 228 // Consume a number and let number be the result. 229 // 230 // If the next 3 input code points would start an ident sequence, then: 231 // 232 // Create a <dimension-token> with the same value, type flag, and sign character as number, and 233 // a unit set initially to the empty string. 234 // 235 // Consume an ident sequence. Set the <dimension-token>’s unit to the returned value. 236 // 237 // Return the <dimension-token>. 238 // 239 // Otherwise, if the next input code point is U+0025 PERCENTAGE SIGN (%), consume it. 240 // Create a <percentage-token> with the same value and sign character as number, and return it. 241 // 242 // Otherwise, create a <number-token> with the same value, type flag, and sign character as 243 // number, and return it. 244 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 245 246 /** 247 * This is a tokenizer method which <B>"consumes"</B> the next {@code Number}-Token (or 248 * Number-Token Subclass) from the input Code-Point Array. 249 * 250 * <EMBED CLASS=defs DATA-TOK=Numeric-Token DATA-URL=consume-a-numeric-token 251 * DATA-OP=Consume> 252 * <EMBED CLASS=external-html DATA-FILE-ID=COPIED_CSS_WG> 253 * <EMBED CLASS=external-html DATA-FILE-ID=NUMERIC_TOKEN> 254 */ 255 protected static void consume( // When invoked from 'CSSTokenizer' 256 final int[] css, // C, int[] css 257 final ByRef<Integer> POS, // P, array-pos loop-variable 258 final Consumer<CSSToken> returnParsedToken, // T, Vector<CSSToken>.add 259 final Consumer<TokenizeError> errorEncountered // E, Vector<TokenizeError>.add 260 ) 261 { 262 ByRef<Num> num = new ByRef<>(); 263 264 final int numEndPos = consumeNumber(css, POS.f, num); 265 266 // If the next 3 input code points would start an ident sequence, then: 267 if (Identifier.startsIdentSequence(css, numEndPos)) 268 Dimension.consume(css, POS, returnParsedToken, numEndPos, num.f); 269 270 // Otherwise, if the next input code point is U+0025 PERCENTAGE SIGN (%), consume it. 271 // Create a <percentage-token> with the same value and sign character as number, and return 272 // it. 273 274 else if ((numEndPos < css.length) && (css[numEndPos] == '%')) 275 { 276 returnParsedToken.accept(new Percentage(css, POS.f, numEndPos + 1, num.f)); 277 POS.f = numEndPos + 1; 278 } 279 280 // Otherwise, create a <number-token> with the same value, type flag, and sign character as 281 // number, and return it. 282 283 else 284 { 285 returnParsedToken.accept(num.f); 286 POS.f = numEndPos; 287 } 288 } 289 290 291 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 292 // Copied from: 293 // https://drafts.csswg.org/css-syntax-3/#consume-a-number 294 // March 27, 2024 295 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 296 // 297 // 4.3.13. Consume a number 298 // 299 // This section describes how to consume a number from a stream of code points. It returns a 300 // numeric value, a string type which is either "integer" or "number", and an optional sign 301 // character which is either "+", "-", or missing. 302 // 303 // NOTE: This algorithm does not do the verification of the first few code points that are 304 // necessary to ensure a number can be obtained from the stream. Ensure that the stream starts 305 // with a number before calling this algorithm. 306 // 307 // Execute the following steps in order: 308 // 309 // 1) Let type be the string "integer". Let number part and exponent part be the empty string. 310 // 311 // 2) If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), consume 312 // it. Append it to number part and set sign character to it. 313 // 314 // 3) While the next input code point is a digit, consume it and append it to number part. 315 // 316 // 4) If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then: 317 // 1) Consume the next input code point and append it to number part. 318 // 2) While the next input code point is a digit, consume it and append it to number part. 319 // 3) Set type to "number". 320 // 321 // 5) If the next 2 or 3 input code points are: 322 // * U+0045 LATIN CAPITAL LETTER E (E) 323 // * or U+0065 LATIN SMALL LETTER E (e). 324 // 325 // optionally followed by: 326 // * U+002D HYPHEN-MINUS (-) 327 // * or U+002B PLUS SIGN (+), 328 // 329 // followed by a digit, then: 330 // 331 // 1) Consume the next input code point. 332 // 2) If the next input code point is "+" or "-", consume it and append to exponent part 333 // 3) While the next input code point is a digit, consume it and append it to exponent part. 334 // 4) Set type to "number". 335 // 336 // 6) Let value be the result of interpreting number part as a base-10 number. 337 // 338 // If exponent part is non-empty, interpret it as a base-10 integer, then raise 10 to the 339 // power of the result, multiply it by value, and set value to that result. 340 // 341 // 7) Return value, type, and sign character. 342 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 343 // 344 // I had to create this helper/data record ever since removing a portion of the "consumeNumber" 345 // method to a second helper-method called "finishConsumeNumber". Then / afterwards, in order 346 // to pass all of the data needed to do the "Finish Consuming a Number" code, I just went 347 // ahead and built this cute little "CNumRecord" - IN ORDER TO EASILY COPY THE RELEVANT 348 // VARIABLES TO THE "finishConsumeNumber" method. 349 // 350 // This "CNumRecord" really wouldn't even hurt a fly. Leave it alone, it only exists to split 351 // up "consumeNumber" into two methods, while still having access to all the variables that are 352 // fields inside this class. 353 // 354 // The reason that "consumeNumber", sort-of, "spilled over" into a second-method is because the 355 // part that was put into the second-method is actually invoked from three different places 356 // within the first... You get that, right? 357 358 private static class CNumRecord 359 { 360 final int sPos; 361 final int[] css; 362 final char signChar; 363 final ByRef<Num> outNum; 364 365 final IntStream.Builder numberPart = IntStream.builder(); 366 final IntStream.Builder expPart = IntStream.builder(); 367 368 private CNumRecord( 369 final int[] css, 370 final int sPos, 371 final ByRef<Num> outNum, 372 final char signChar 373 ) 374 { 375 this.css = css; 376 this.sPos = sPos; 377 this.signChar = signChar; 378 this.outNum = outNum; 379 } 380 } 381 382 /** 383 * This is a tokenizer method which <B>"consumes"</B> the next {@code Number}-Literal from the 384 * input Code-Point Array. 385 * 386 * <EMBED CLASS=defs DATA-TOK=Number-Literal DATA-URL=consume-a-number DATA-OP=Consume> 387 * <EMBED CLASS=external-html DATA-FILE-ID=COPIED_CSS_WG> 388 * <EMBED CLASS=external-html DATA-FILE-ID=NUMBER> 389 * <EMBED CLASS=external-html DATA-FILE-ID=NUMBER_TOK_SVG> 390 */ 391 protected static int consumeNumber( 392 final int[] css, 393 final int sPos, 394 final ByRef<Num> outNum 395 ) 396 { 397 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 398 // 1) INITIALIZATION: 399 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 400 // 401 // a. Let type be the string "integer". 402 // b. Let number part and exponent part be the empty string. 403 404 boolean integerOrNumber = true; 405 int pos = sPos; 406 int c = css[pos++]; 407 408 409 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 410 // 2) SIGN: If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-) 411 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 412 // 413 // Consume it. Append it to number part and set sign character to it. 414 // 415 // Note that the 'Initializations' part (Step 1) spills over into step 2. The actual 416 // little configuration-record that I had to eventually write isn't instantiated until 417 // right here, in this if-branch. 418 // 419 // Remember that 'CNumRecord' sort of just stands for "Consume-Number-Record". 420 421 final CNumRecord r; 422 423 if ((c == '+') || (c == '-')) 424 { 425 // Instantiates a "Consume-Number-Record" with the Sign-Character set to +/- 426 r = new CNumRecord(css, sPos, outNum, (char) c); 427 428 r.numberPart.accept(c); 429 430 c = css[pos++]; // No need to check for IOOB, this method is package-private, and 431 // it is only called if this contains a valid-number 432 // There is no valid number that is only a '+' or '-' 433 } 434 435 // This initializes a "Consume-Number-Record" with an empty Sign-Character 436 else r = new CNumRecord(css, sPos, outNum, (char) 0); 437 438 439 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 440 // 3) NUMBER: While the next input-cp is a digit, consume it and append it to number part 441 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 442 443 while ((c >= '0') && (c <= '9')) 444 { 445 r.numberPart.accept(c); 446 447 if (pos < css.length) c = css[pos++]; 448 else return finishConsumeNumber(r, pos, true); 449 } 450 451 452 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 453 // 4) DECIMAL: If the next 2 input-cp's are U+002E FULL STOP (.) followed by a digit: 454 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 455 // 456 // 1) Consume the next input code point and append it to number part. 457 // 2) While the next input code point is a digit, consume it and append it to number part. 458 // 3) Set type to "number". 459 460 if (c == '.') 461 { 462 integerOrNumber = false; 463 464 if ((pos < css.length) && Character.isDigit(css[pos])) 465 { 466 r.numberPart.accept('.'); // The '.' (dot / full-stop) 467 c = css[pos++]; 468 469 // This 'while-loop' was EXACTLY BLOCK-COPIED from the one directly above 470 while ((c >= '0') && (c <= '9')) 471 { 472 r.numberPart.accept(c); 473 474 if (pos < css.length) c = css[pos++]; 475 else return finishConsumeNumber(r, pos, false); 476 } 477 } 478 479 // The following 'else' branch is for cases such as "10. Chapter 5" (Where the '.' is 480 // not part of the number) 481 // 482 // Note that since 'pos' is currently pointing at the character after the '.' (or it is 483 // pointing at css.length), '1' MUST BE SUBTRACTED FROM 'pos' 484 485 else return finishConsumeNumber(r, pos - 1, true); 486 } 487 488 489 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 490 // 5) EXPONENT: If the next 2 or 3 input code points are: (This part sucks and is ugly) 491 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 492 // 493 // * U+0045 LATIN CAPITAL LETTER E (E) 494 // * or U+0065 LATIN SMALL LETTER E (e). 495 // 496 // optionally followed by: 497 // * U+002D HYPHEN-MINUS (-) 498 // * or U+002B PLUS SIGN (+), 499 // 500 // followed by a digit, then: 501 // 502 // 1) Consume the next input code point. 503 // 2) If the next input code point is "+" or "-", consume it and append to exponent part 504 // 3) While the next input code point is a digit, consume it and append it to exponent part. 505 // 4) Set type to "number". 506 507 if ((c != 'e') && (c != 'E')) 508 509 // For this case, we have to "back the pointer up" by 1 place - a.k.a. pass "pos - 1" 510 // This is because 'pos' is currently pointing to TWO CHARACTERS AFTER THE LAST NUMBER 511 512 return finishConsumeNumber(r, pos - 1, integerOrNumber); 513 514 // NOTE: From this point foward, 'c' IS GUARANTEED TO HOLD AN 'e' OR AN 'E'. 515 // This is very-likely (but not guaranteed) to be an "Exponent-Part" 516 // 517 // If the 'e' or 'E' happend to be an extranneous letter (for instance as in 5em), where 518 // the 'e' was the first letter of the dimension-identifier "em", then that situation is 519 // handled at the very end (on the last line) of this method. In that particular situation 520 // THIS WOULD NOT BE AN EXPONENT 521 // 522 // The particular 'if-branch' (which is directly below) handles the case where there is an 523 // 'e' or 'E', followed by AT LEAST ONE DIGIT. If that has happend, then THIS IS 524 // GUARANTEED TO BE AN EXPONENT. 525 526 /* 527 Don't delete this. If there is any confusion, this is the only way to have even a 528 prayer of understanding what all of these cute-little "++" even are. 529 530 System.out.println( 531 "c=" + ((char) c) + ", " + 532 "pos=" + pos + ", " + 533 "css[" + (pos) + "]=" + ((char) css[pos]) + ", " + 534 (((pos+1) < css.length) ? ("css[" + (pos) + "]=" + ((char) css[pos])) : "") 535 ); 536 */ 537 538 if ( (pos < css.length) 539 && Character.isDigit(css[pos]) 540 ) 541 { 542 r.expPart.accept(c); // The letter 'e' or 'E' 543 r.expPart.accept(css[pos++]); // The first Digit of the Exponent 544 545 // This type of coding is so different than anything I have done. You just don't ever 546 // mess with this type of stuff on a regular-basis. It's exactly the type of code that 547 // you would think is really easy and common. Unfortunately, it is extremely uncommon 548 // because once a parser has been written, you never have to go back and write one 549 // again. 550 // 551 // TESTING HELPS A LOT. Everything is an "off-by-one error" in this Parser. There are 552 // SO MANY MICRO-VARIANTS TO WORRY ABOUT! What if the CSS-String ends at the end of 553 // this number? Obviously that won't happen much, but if it does, the whole program 554 // crashes. What if a User is actually intending to use Scientific-Notation in his 555 // css? Obviously that's even less common, but if you don't get it right, the whole 556 // program crashes. 557 // 558 // What if they are using Scientific-Notation with a Percentage? 559 // As in: "+1e-3%" Sound ridiculous? CSS accepts that, so this code has to handle it 560 561 while (pos < css.length) 562 { 563 if (Character.isDigit(c = css[pos++])) r.expPart.accept(c); 564 else { pos--; break; } 565 } 566 567 return finishConsumeNumber(r, pos, false); 568 } 569 570 // This 'if-branch' handles the case where there was an 'e' or 'E', followed by a '+' or 571 // '-' sign, followed by any digit. THIS IS ALSO A CASE WHERE THERE **IS** AN EXPONENT 572 573 if ( ((pos + 1) < css.length) 574 && ((css[pos] == '+') || (css[pos] == '-')) 575 && Character.isDigit(css[pos + 1]) 576 ) 577 { 578 r.expPart.accept(c); // The letter 'e' or 'E' 579 r.expPart.accept(css[pos++]); // The Sign Character for the Exponent 580 r.expPart.accept(css[pos++]); // The first Digit of the Exponent 581 582 // This loop was block copied from the one directly above. See that loop for a little 583 // background information. 584 585 while (pos < css.length) 586 { 587 if (Character.isDigit(c = css[pos++])) r.expPart.accept(c); 588 else { pos--; break; } 589 } 590 591 return finishConsumeNumber(r, pos, false); 592 } 593 594 // This line of code will **ONLY** be reached if there was an 'e' or 'E' character that 595 // wasn't actually followed by a number. Again the most common example whereby this line 596 // would be reached are things such as "5em", where the 'e' turns out not to be an 597 // exponent-character, but rather the first letter in the "Dimension-String" "em" 598 // (which, actually stands for "emphemeral unit", and means the current font-size) 599 600 return finishConsumeNumber(r, pos - 1, integerOrNumber); 601 } 602 603 // Helper method for the above method 604 private static int finishConsumeNumber( 605 final CNumRecord r, 606 final int pos, 607 final boolean integerOrNumber 608 ) 609 { 610 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 611 // 6) Let value be the result of interpreting number part as a base-10 number. 612 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 613 // 614 // If exponent part is non-empty, interpret it as a base-10 integer, then raise 10 to the 615 // power of the result, multiply it by value, and set value to that result. 616 617 final int[] numArr = r.numberPart.build().toArray(); 618 final int[] expArr = r.expPart.build().toArray(); 619 620 final String numStr = new String(numArr, 0, numArr.length); 621 final String expStr = new String(expArr, 0, expArr.length); 622 623 final String bdStr = numStr + expStr; 624 625 /* 626 It is true that a "CSS Parser" or a "CSS Tokenizer" isn't that difficult to write. The 627 catch is that this type of code is stuff that you just never write very often. As such, 628 observing this and debugging this isn't that fun. Please don't delete this comment. It is 629 the only way to figure out the tiny-mistakes that can be made in "Num.consume" 630 */ 631 632 /* 633 System.out.println( 634 "\tr.sPos: " + r.sPos + '\n' + 635 "\tpos: " + pos + '\n' + 636 "\tbdStr: " + bdStr + '\n' + 637 "\tnumStr: " + numStr + '\n' + 638 "\texpStr: " + expStr + '\n' + 639 "\tintegerOrNumber: " + integerOrNumber 640 ); 641 */ 642 643 final BigDecimal bd = new BigDecimal(bdStr); 644 645 646 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 647 // 7) Return value, type, and sign character. 648 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 649 650 r.outNum.f = new Num(r.css, r.sPos, pos, bd, integerOrNumber, r.signChar); 651 return pos; 652 } 653}