001/* 002 * Copyright 2008-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.util; 022 023 024 025import java.io.IOException; 026import java.io.Serializable; 027import java.text.ParseException; 028import java.util.ArrayList; 029import java.util.Random; 030import java.util.concurrent.atomic.AtomicBoolean; 031 032import static com.unboundid.util.Debug.*; 033import static com.unboundid.util.StaticUtils.*; 034import static com.unboundid.util.UtilityMessages.*; 035 036 037 038/** 039 * This class provides a method for generating a string value comprised of zero 040 * or more components. The components may be any combination of zero or more 041 * strings, sequential numeric ranges, and random numeric ranges. These 042 * components should be formatted as follows: 043 * <UL> 044 * <LI>Strings are simply any kind of static text that will be used as-is 045 * without any modification, except that double opening or closing square 046 * brackets (i.e., "<CODE>[[</CODE>" or "<CODE>]]</CODE>") will be 047 * replaced with single opening or closing square brackets to distinguish 048 * them from the square brackets used in numeric ranges or URL 049 * references.</LI> 050 * <LI>Sequential numeric ranges consist of an opening square bracket, a 051 * numeric value to be used as the lower bound for the range, a colon, a 052 * second numeric value to be used as the upper bound for the range, an 053 * optional '<CODE>x</CODE>' character followed by a numeric value to be 054 * used as the increment, an optional '<CODE>%</CODE>' character followed 055 * by a format string as allowed by the {@link java.text.DecimalFormat} 056 * class to define how the resulting value should be formatted, and a 057 * closing square bracket to indicate the end of the range.</LI> 058 * <LI>Random numeric ranges consist of an opening square bracket, a 059 * numeric value to be used as the lower bound for the range, a dash, a 060 * second numeric value to be used as the upper bound for the range, an 061 * optional '<CODE>%</CODE>' character followed by a format string as 062 * allowed by the {@link java.text.DecimalFormat} class to define how the 063 * resulting value should be formatted, and a closing square bracket to 064 * indicate the end of the range.</LI> 065 * <LI>Strings read from a file specified by a given URL. That file may be 066 * contained on the local filesystem (using a URL like 067 * "file:///tmp/mydata.txt") or read from a remote server via HTTP (using 068 * a URL like "http://server.example.com/mydata.txt"). In either case, 069 * the provided URL must not contain a closing square bracket character. 070 * If this option is used, then that file must contain one value per line, 071 * and its contents will be read into memory and values from the file will 072 * be selected in a random order and used in place of the bracketed 073 * URL.</LI> 074 * <LI>Back-references that will be replaced with the same value as the 075 * bracketed token in the specified position in the string. For example, 076 * a component of "[ref:1]" will be replaced with the same value as used 077 * in the first bracketed component of the value pattern. Back-references 078 * must only reference components that have been previously defined in the 079 * value pattern, and not those which appear after the reference.</LI> 080 * </UL> 081 * <BR> 082 * It must be possible to represent all of the numeric values used in sequential 083 * or random numeric ranges as {@code long} values. In a sequential numeric 084 * range, if the first value is larger than the second value, then values will 085 * be chosen in descending rather than ascending order (and if an increment is 086 * given, then it should be positive). In addition, once the end of a 087 * sequential range has been reached, then the value will wrap around to the 088 * beginning of that range. 089 * <BR> 090 * Examples of value pattern components include: 091 * <UL> 092 * <LI><CODE>Hello</CODE> -- The static text "<CODE>Hello</CODE>".</LI> 093 * <LI><CODE>[[Hello]]</CODE> -- The static text "<CODE>[Hello]</CODE>" (note 094 * that the double square brackets were replaced with single square 095 * brackets).</LI> 096 * <LI><CODE>[0:1000]</CODE> -- A sequential numeric range that will iterate 097 * in ascending sequential order from 0 to 1000. The 1002nd value that is 098 * requested will cause the value to be wrapped around to 0 again.</LI> 099 * <LI><CODE>[1000:0]</CODE> -- A sequential numeric range that will iterate 100 * in descending sequential order from 1000 to 0. The 1002nd value that is 101 * requested will cause the value to be wrapped around to 1000 again.</LI> 102 * <LI><CODE>[0:1000x5%0000]</CODE> -- A sequential numeric range that will 103 * iterate in ascending sequential order from 0 to 1000 in increments of 104 * five with all values represented as four-digit numbers padded with 105 * leading zeroes. For example, the first four values generated by this 106 * component will be "0000", "0005", "0010", and "0015".</LI> 107 * <LI><CODE>[0-1000]</CODE> -- A random numeric range that will choose values 108 * at random between 0 and 1000, inclusive.</LI> 109 * <LI><CODE>[0-1000%0000]</CODE> -- A random numeric range that will choose 110 * values at random between 0 and 1000, inclusive, and values will be 111 * padded with leading zeroes as necessary so that they are represented 112 * using four digits.</LI> 113 * <LI><CODE>[file:///tmp/mydata.txt]</CODE> -- A URL reference that will 114 * cause randomly-selected lines from the specified local file to be used 115 * in place of the bracketed range. To make it clear that the file 116 * contents are randomly accessed, you may use {@code randomfile} in place 117 * of {@code file}.</LI> 118 * <LI><CODE>[sequentialfile:///tmp/mydata.txt]</CODE> -- A URL reference that 119 * will cause lines from the specified local file, selected in sequential 120 * order, to be used in place of the bracketed range.</LI> 121 * <LI><CODE>[http://server.example.com/tmp/mydata.txt]</CODE> -- A URL 122 * reference that will cause randomly-selected lines from the specified 123 * remote HTTP-accessible file to be used in place of the bracketed 124 * range.</LI> 125 * </UL> 126 * <BR> 127 * Examples of full value pattern strings include: 128 * <UL> 129 * <LI><CODE>dc=example,dc=com</CODE> -- A value pattern containing only 130 * static text and no numeric components.</LI> 131 * <LI><CODE>[1000:9999]</CODE> -- A value pattern containing only a numeric 132 * component that will choose numbers in sequential order from 1000 to 133 * 9999.</LI> 134 * <LI><CODE>(uid=user.[1-1000000])</CODE> -- A value pattern that combines 135 * the static text "<CODE>(uid=user.</CODE>" with a value chosen randomly 136 * between one and one million, and another static text string of 137 * "<CODE>)</CODE>".</LI> 138 * <LI><CODE>uid=user.[1-1000000],ou=org[1-10],dc=example,dc=com</CODE> -- A 139 * value pattern containing two numeric components interspersed between 140 * three static text components.</LI> 141 * <LI><CODE>uid=user.[1-1000000],ou=org[ref:1],dc=example,dc=com</CODE> -- A 142 * value pattern in which the organization number will be the same as the 143 * randomly-selected user number.</LI> 144 * </UL> 145 */ 146@NotMutable() 147@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 148public final class ValuePattern 149 implements Serializable 150{ 151 /** 152 * The URL to the publicly-accessible javadoc for this class, which provides 153 * a detailed overview of the supported value pattern syntax. 154 */ 155 public static final String PUBLIC_JAVADOC_URL = 156 "https://docs.ldap.com/ldap-sdk/docs/javadoc/index.html?" + 157 "com/unboundid/util/ValuePattern.html"; 158 159 160 161 /** 162 * The serial version UID for this serializable class. 163 */ 164 private static final long serialVersionUID = 4502778464751705304L; 165 166 167 168 // Indicates whether the provided value pattern includes one or more 169 // back-references. 170 private final boolean hasBackReference; 171 172 // The string that was originally used to create this value pattern. 173 private final String pattern; 174 175 // The thread-local array list that will be used to hold values for 176 // back-references. 177 private final ThreadLocal<ArrayList<String>> refLists; 178 179 // The thread-local string builder that will be used to build values. 180 private final ThreadLocal<StringBuilder> buffers; 181 182 // The value pattern components that will be used to generate values. 183 private final ValuePatternComponent[] components; 184 185 186 187 /** 188 * Creates a new value pattern from the provided string. 189 * 190 * @param s The string representation of the value pattern to create. It 191 * must not be {@code null}. 192 * 193 * @throws ParseException If the provided string cannot be parsed as a valid 194 * value pattern string. 195 */ 196 public ValuePattern(final String s) 197 throws ParseException 198 { 199 this(s, null); 200 } 201 202 203 204 /** 205 * Creates a new value pattern from the provided string. 206 * 207 * @param s The string representation of the value pattern to create. It 208 * must not be {@code null}. 209 * @param r The seed to use for the random number generator. It may be 210 * {@code null} if no seed is required. 211 * 212 * @throws ParseException If the provided string cannot be parsed as a valid 213 * value pattern string. 214 */ 215 public ValuePattern(final String s, final Long r) 216 throws ParseException 217 { 218 Validator.ensureNotNull(s); 219 220 pattern = s; 221 refLists = new ThreadLocal<ArrayList<String>>(); 222 buffers = new ThreadLocal<StringBuilder>(); 223 224 final AtomicBoolean hasRef = new AtomicBoolean(false); 225 226 final Random random; 227 if (r == null) 228 { 229 random = new Random(); 230 } 231 else 232 { 233 random = new Random(r); 234 } 235 236 final ArrayList<ValuePatternComponent> l = 237 new ArrayList<ValuePatternComponent>(3); 238 parse(s, 0, l, random, hasRef); 239 240 hasBackReference = hasRef.get(); 241 if (hasBackReference) 242 { 243 int availableReferences = 0; 244 for (final ValuePatternComponent c : l) 245 { 246 if (c instanceof BackReferenceValuePatternComponent) 247 { 248 final BackReferenceValuePatternComponent brvpc = 249 (BackReferenceValuePatternComponent) c; 250 if (brvpc.getIndex() > availableReferences) 251 { 252 throw new ParseException( 253 ERR_REF_VALUE_PATTERN_INVALID_INDEX.get(brvpc.getIndex()), 0); 254 } 255 } 256 257 if (c.supportsBackReference()) 258 { 259 availableReferences++; 260 } 261 } 262 } 263 264 components = new ValuePatternComponent[l.size()]; 265 l.toArray(components); 266 } 267 268 269 270 /** 271 * Recursively parses the provided string into a list of value pattern 272 * components. 273 * 274 * @param s The string representation of the value pattern to create. It 275 * may be a portion of the entire value pattern string. 276 * @param o The offset of the first character of the provided string in 277 * the full value pattern string. 278 * @param l The list into which the parsed components should be added. 279 * @param r The random number generator to use to seed random number 280 * generators used by components. 281 * @param ref A value that may be updated if the pattern contains any 282 * back-references. 283 * 284 * @throws ParseException If the provided string cannot be parsed as a valid 285 * value pattern string. 286 */ 287 private static void parse(final String s, final int o, 288 final ArrayList<ValuePatternComponent> l, 289 final Random r, final AtomicBoolean ref) 290 throws ParseException 291 { 292 // Find the first occurrence of "[[". Parse the portion of the string 293 // before it, into the list, then add a string value pattern containing "[", 294 // then parse the portion of the string after it. 295 // First, parse out any occurrences of "[[" and replace them with string 296 // value pattern components containing only "[". 297 int pos = s.indexOf("[["); 298 if (pos >= 0) 299 { 300 if (pos > 0) 301 { 302 parse(s.substring(0, pos), o, l, r, ref); 303 } 304 305 l.add(new StringValuePatternComponent("[")); 306 307 if (pos < (s.length() - 2)) 308 { 309 parse(s.substring(pos+2), (o+pos+2), l, r, ref); 310 } 311 return; 312 } 313 314 // Find the first occurrence of "]]". Parse the portion of the string 315 // before it, into the list, then add a string value pattern containing "]", 316 // then parse the portion of the string after it. 317 pos = s.indexOf("]]"); 318 if (pos >= 0) 319 { 320 if (pos > 0) 321 { 322 parse(s.substring(0, pos), o, l, r, ref); 323 } 324 325 l.add(new StringValuePatternComponent("]")); 326 327 if (pos < (s.length() - 2)) 328 { 329 parse(s.substring(pos+2), (o+pos+2), l, r, ref); 330 } 331 return; 332 } 333 334 // Find the first occurrence of "[" and the corresponding "]". The part 335 // before that will be a string. Then parse out the numeric or URL 336 // component, and parse the rest of the string after the "]". 337 pos = s.indexOf('['); 338 if (pos >= 0) 339 { 340 final int closePos = s.indexOf(']'); 341 if (closePos < 0) 342 { 343 throw new ParseException( 344 ERR_VALUE_PATTERN_UNMATCHED_OPEN.get(o+pos), (o+pos)); 345 } 346 else if (closePos < pos) 347 { 348 throw new ParseException( 349 ERR_VALUE_PATTERN_UNMATCHED_CLOSE.get(o+closePos), (o+closePos)); 350 } 351 352 if (pos > 0) 353 { 354 l.add(new StringValuePatternComponent(s.substring(0, pos))); 355 } 356 357 final String bracketedToken = s.substring(pos+1, closePos); 358 if (bracketedToken.startsWith("file:")) 359 { 360 final String path = bracketedToken.substring(5); 361 try 362 { 363 l.add(new FileValuePatternComponent(path, r.nextLong(), false)); 364 } 365 catch (IOException ioe) 366 { 367 debugException(ioe); 368 throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get( 369 path, getExceptionMessage(ioe)), o+pos); 370 } 371 } 372 else if (bracketedToken.startsWith("randomfile:")) 373 { 374 final String path = bracketedToken.substring(11); 375 try 376 { 377 l.add(new FileValuePatternComponent(path, r.nextLong(), false)); 378 } 379 catch (IOException ioe) 380 { 381 debugException(ioe); 382 throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get( 383 path, getExceptionMessage(ioe)), o+pos); 384 } 385 } 386 else if (bracketedToken.startsWith("sequentialfile:")) 387 { 388 final String path = bracketedToken.substring(15); 389 try 390 { 391 l.add(new FileValuePatternComponent(path, r.nextLong(), true)); 392 } 393 catch (IOException ioe) 394 { 395 debugException(ioe); 396 throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get( 397 path, getExceptionMessage(ioe)), o+pos); 398 } 399 } 400 else if (bracketedToken.startsWith("http://")) 401 { 402 try 403 { 404 l.add(new HTTPValuePatternComponent(bracketedToken, r.nextLong())); 405 } 406 catch (IOException ioe) 407 { 408 debugException(ioe); 409 throw new ParseException(ERR_HTTP_VALUE_PATTERN_NOT_USABLE.get( 410 bracketedToken, getExceptionMessage(ioe)), o+pos); 411 } 412 } 413 else if (bracketedToken.startsWith("ref:")) 414 { 415 ref.set(true); 416 417 final String valueStr = bracketedToken.substring(4); 418 try 419 { 420 final int index = Integer.parseInt(valueStr); 421 if (index == 0) 422 { 423 throw new ParseException(ERR_REF_VALUE_PATTERN_ZERO_INDEX.get(), 424 (o+pos+4)); 425 } 426 else if (index < 0) 427 { 428 throw new ParseException( 429 ERR_REF_VALUE_PATTERN_NOT_VALID.get(valueStr), (o+pos+4)); 430 } 431 else 432 { 433 l.add(new BackReferenceValuePatternComponent(index)); 434 } 435 } 436 catch (final NumberFormatException nfe) 437 { 438 debugException(nfe); 439 throw new ParseException( 440 ERR_REF_VALUE_PATTERN_NOT_VALID.get(valueStr), (o+pos+4)); 441 } 442 } 443 else 444 { 445 l.add(parseNumericComponent(s.substring(pos+1, closePos), (o+pos+1), 446 r)); 447 } 448 449 if (closePos < (s.length() - 1)) 450 { 451 parse(s.substring(closePos+1), (o+closePos+1), l, r, ref); 452 } 453 454 return; 455 } 456 457 458 // If there are any occurrences of "]" without a corresponding open, then 459 // that's invalid. 460 pos = s.indexOf(']'); 461 if (pos >= 0) 462 { 463 throw new ParseException( 464 ERR_VALUE_PATTERN_UNMATCHED_CLOSE.get(o+pos), (o+pos)); 465 } 466 467 // There are no brackets, so it's just a static string. 468 l.add(new StringValuePatternComponent(s)); 469 } 470 471 472 473 /** 474 * Parses the specified portion of the provided string as either a 475 * sequential or random numeric value pattern component. 476 * 477 * @param s The string to parse, not including the square brackets. 478 * @param o The offset in the overall value pattern string at which the 479 * provided substring begins. 480 * @param r The random number generator to use to seed random number 481 * generators used by components. 482 * 483 * @return The parsed numeric value pattern component. 484 * 485 * @throws ParseException If the specified substring cannot be parsed as a 486 * 487 */ 488 private static ValuePatternComponent parseNumericComponent(final String s, 489 final int o, 490 final Random r) 491 throws ParseException 492 { 493 boolean delimiterFound = false; 494 boolean sequential = false; 495 int pos = 0; 496 long lowerBound = 0L; 497 498lowerBoundLoop: 499 for ( ; pos < s.length(); pos++) 500 { 501 switch (s.charAt(pos)) 502 { 503 case '0': 504 case '1': 505 case '2': 506 case '3': 507 case '4': 508 case '5': 509 case '6': 510 case '7': 511 case '8': 512 case '9': 513 // These are all acceptable. 514 break; 515 516 case '-': 517 if (pos == 0) 518 { 519 // This indicates that the value is negative. 520 break; 521 } 522 else 523 { 524 // This indicates the end of the lower bound. 525 delimiterFound = true; 526 sequential = false; 527 528 try 529 { 530 lowerBound = Long.parseLong(s.substring(0, pos)); 531 } 532 catch (NumberFormatException nfe) 533 { 534 Debug.debugException(nfe); 535 throw new ParseException( 536 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 537 Long.MAX_VALUE), 538 (o-1)); 539 } 540 pos++; 541 break lowerBoundLoop; 542 } 543 544 case ':': 545 delimiterFound = true; 546 sequential = true; 547 548 if (pos == 0) 549 { 550 throw new ParseException( 551 ERR_VALUE_PATTERN_EMPTY_LOWER_BOUND.get(o-1), (o-1)); 552 } 553 else 554 { 555 try 556 { 557 lowerBound = Long.parseLong(s.substring(0, pos)); 558 } 559 catch (NumberFormatException nfe) 560 { 561 Debug.debugException(nfe); 562 throw new ParseException( 563 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 564 Long.MAX_VALUE), 565 (o-1)); 566 } 567 } 568 pos++; 569 break lowerBoundLoop; 570 571 default: 572 throw new ParseException( 573 ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), (o+pos)), 574 (o+pos)); 575 } 576 } 577 578 if (! delimiterFound) 579 { 580 throw new ParseException(ERR_VALUE_PATTERN_NO_DELIMITER.get(o-1), (o-1)); 581 } 582 583 boolean hasIncrement = false; 584 int startPos = pos; 585 long upperBound = lowerBound; 586 long increment = 1L; 587 String formatString = null; 588 589 delimiterFound = false; 590 591upperBoundLoop: 592 for ( ; pos < s.length(); pos++) 593 { 594 switch (s.charAt(pos)) 595 { 596 case '0': 597 case '1': 598 case '2': 599 case '3': 600 case '4': 601 case '5': 602 case '6': 603 case '7': 604 case '8': 605 case '9': 606 // These are all acceptable. 607 break; 608 609 case '-': 610 if (pos == startPos) 611 { 612 // This indicates that the value is negative. 613 break; 614 } 615 else 616 { 617 throw new ParseException( 618 ERR_VALUE_PATTERN_INVALID_CHARACTER.get('-', (o+pos)), 619 (o+pos)); 620 } 621 622 case 'x': 623 delimiterFound = true; 624 hasIncrement = true; 625 626 if (pos == startPos) 627 { 628 throw new ParseException( 629 ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1)); 630 } 631 else 632 { 633 try 634 { 635 upperBound = Long.parseLong(s.substring(startPos, pos)); 636 } 637 catch (NumberFormatException nfe) 638 { 639 Debug.debugException(nfe); 640 throw new ParseException( 641 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 642 Long.MAX_VALUE), 643 (o-1)); 644 } 645 } 646 pos++; 647 break upperBoundLoop; 648 649 case '%': 650 delimiterFound = true; 651 hasIncrement = false; 652 653 if (pos == startPos) 654 { 655 throw new ParseException( 656 ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1)); 657 } 658 else 659 { 660 try 661 { 662 upperBound = Long.parseLong(s.substring(startPos, pos)); 663 } 664 catch (NumberFormatException nfe) 665 { 666 Debug.debugException(nfe); 667 throw new ParseException( 668 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 669 Long.MAX_VALUE), 670 (o-1)); 671 } 672 } 673 pos++; 674 break upperBoundLoop; 675 676 default: 677 throw new ParseException( 678 ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), (o+pos)), 679 (o+pos)); 680 } 681 } 682 683 if (! delimiterFound) 684 { 685 if (pos == startPos) 686 { 687 throw new ParseException( 688 ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1)); 689 } 690 691 try 692 { 693 upperBound = Long.parseLong(s.substring(startPos, pos)); 694 } 695 catch (NumberFormatException nfe) 696 { 697 Debug.debugException(nfe); 698 throw new ParseException( 699 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 700 Long.MAX_VALUE), 701 (o-1)); 702 } 703 704 if (sequential) 705 { 706 return new SequentialValuePatternComponent(lowerBound, upperBound, 1, 707 null); 708 } 709 else 710 { 711 return new RandomValuePatternComponent(lowerBound, upperBound, 712 r.nextLong(), null); 713 } 714 } 715 716 if (hasIncrement) 717 { 718 delimiterFound = false; 719 startPos = pos; 720 721incrementLoop: 722 for ( ; pos < s.length(); pos++) 723 { 724 switch (s.charAt(pos)) 725 { 726 case '0': 727 case '1': 728 case '2': 729 case '3': 730 case '4': 731 case '5': 732 case '6': 733 case '7': 734 case '8': 735 case '9': 736 // These are all acceptable. 737 break; 738 739 case '-': 740 if (pos == startPos) 741 { 742 // This indicates that the value is negative. 743 break; 744 } 745 else 746 { 747 throw new ParseException( 748 ERR_VALUE_PATTERN_INVALID_CHARACTER.get('-', (o+pos)), 749 (o+pos)); 750 } 751 752 case '%': 753 delimiterFound = true; 754 if (pos == startPos) 755 { 756 throw new ParseException( 757 ERR_VALUE_PATTERN_EMPTY_INCREMENT.get(o-1), (o-1)); 758 } 759 else if (pos == (s.length() - 1)) 760 { 761 throw new ParseException( 762 ERR_VALUE_PATTERN_EMPTY_FORMAT.get(o-1), (o-1)); 763 } 764 else 765 { 766 try 767 { 768 increment = Long.parseLong(s.substring(startPos, pos)); 769 } 770 catch (NumberFormatException nfe) 771 { 772 Debug.debugException(nfe); 773 throw new ParseException( 774 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 775 Long.MAX_VALUE), 776 (o-1)); 777 } 778 779 formatString = s.substring(pos+1); 780 } 781 break incrementLoop; 782 783 default: 784 throw new ParseException( 785 ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), 786 (o+pos)), 787 (o+pos)); 788 } 789 } 790 791 if (! delimiterFound) 792 { 793 if (pos == startPos) 794 { 795 throw new ParseException( 796 ERR_VALUE_PATTERN_EMPTY_INCREMENT.get(o-1), (o-1)); 797 } 798 799 try 800 { 801 increment = Long.parseLong(s.substring(startPos, pos)); 802 } 803 catch (NumberFormatException nfe) 804 { 805 Debug.debugException(nfe); 806 throw new ParseException( 807 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 808 Long.MAX_VALUE), 809 (o-1)); 810 } 811 } 812 } 813 else 814 { 815 formatString = s.substring(pos); 816 if (formatString.length() == 0) 817 { 818 throw new ParseException( 819 ERR_VALUE_PATTERN_EMPTY_FORMAT.get(o-1), (o-1)); 820 } 821 } 822 823 if (sequential) 824 { 825 return new SequentialValuePatternComponent(lowerBound, upperBound, 826 increment, formatString); 827 } 828 else 829 { 830 return new RandomValuePatternComponent(lowerBound, upperBound, 831 r.nextLong(), formatString); 832 } 833 } 834 835 836 837 /** 838 * Retrieves the next value generated from the value pattern. 839 * 840 * @return The next value generated from the value pattern. 841 */ 842 public String nextValue() 843 { 844 StringBuilder buffer = buffers.get(); 845 if (buffer == null) 846 { 847 buffer = new StringBuilder(); 848 buffers.set(buffer); 849 } 850 else 851 { 852 buffer.setLength(0); 853 } 854 855 ArrayList<String> refList = refLists.get(); 856 if (hasBackReference) 857 { 858 if (refList == null) 859 { 860 refList = new ArrayList<String>(10); 861 refLists.set(refList); 862 } 863 else 864 { 865 refList.clear(); 866 } 867 } 868 869 for (final ValuePatternComponent c : components) 870 { 871 if (hasBackReference) 872 { 873 if (c instanceof BackReferenceValuePatternComponent) 874 { 875 final BackReferenceValuePatternComponent brvpc = 876 (BackReferenceValuePatternComponent) c; 877 final String value = refList.get(brvpc.getIndex() - 1); 878 buffer.append(value); 879 refList.add(value); 880 } 881 else if (c.supportsBackReference()) 882 { 883 final int startPos = buffer.length(); 884 c.append(buffer); 885 refList.add(buffer.substring(startPos)); 886 } 887 else 888 { 889 c.append(buffer); 890 } 891 } 892 else 893 { 894 c.append(buffer); 895 } 896 } 897 898 return buffer.toString(); 899 } 900 901 902 903 /** 904 * Retrieves a string representation of this value pattern, which will be the 905 * original pattern string used to create it. 906 * 907 * @return A string representation of this value pattern. 908 */ 909 @Override() 910 public String toString() 911 { 912 return pattern; 913 } 914}