001/* 002 * Copyright 2008-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2014 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.</LI> 116 * <LI><CODE>[http://server.example.com/tmp/mydata.txt]</CODE> -- A URL 117 * reference that will cause randomly-selected lines from the specified 118 * remote HTTP-accessible file to be used in place of the bracketed 119 * range.</LI> 120 * </UL> 121 * <BR> 122 * Examples of full value pattern strings include: 123 * <UL> 124 * <LI><CODE>dc=example,dc=com</CODE> -- A value pattern containing only 125 * static text and no numeric components.</LI> 126 * <LI><CODE>[1000:9999]</CODE> -- A value pattern containing only a numeric 127 * component that will choose numbers in sequential order from 1000 to 128 * 9999.</LI> 129 * <LI><CODE>(uid=user.[1-1000000])</CODE> -- A value pattern that combines 130 * the static text "<CODE>(uid=user.</CODE>" with a value chosen randomly 131 * between one and one million, and another static text string of 132 * "<CODE>)</CODE>".</LI> 133 * <LI><CODE>uid=user.[1-1000000],ou=org[1-10],dc=example,dc=com</CODE> -- A 134 * value pattern containing two numeric components interspersed between 135 * three static text components.</LI> 136 * <LI><CODE>uid=user.[1-1000000],ou=org[ref:1],dc=example,dc=com</CODE> -- A 137 * value pattern in which the organization number will be the same as the 138 * randomly-selected user number.</LI> 139 * </UL> 140 */ 141@NotMutable() 142@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 143public final class ValuePattern 144 implements Serializable 145{ 146 /** 147 * The serial version UID for this serializable class. 148 */ 149 private static final long serialVersionUID = 4502778464751705304L; 150 151 152 153 // Indicates whether the provided value pattern includes one or more 154 // back-references. 155 private final boolean hasBackReference; 156 157 // The string that was originally used to create this value pattern. 158 private final String pattern; 159 160 // The thread-local array list that will be used to hold values for 161 // back-references. 162 private final ThreadLocal<ArrayList<String>> refLists; 163 164 // The thread-local string builder that will be used to build values. 165 private final ThreadLocal<StringBuilder> buffers; 166 167 // The value pattern components that will be used to generate values. 168 private final ValuePatternComponent[] components; 169 170 171 172 /** 173 * Creates a new value pattern from the provided string. 174 * 175 * @param s The string representation of the value pattern to create. It 176 * must not be {@code null}. 177 * 178 * @throws ParseException If the provided string cannot be parsed as a valid 179 * value pattern string. 180 */ 181 public ValuePattern(final String s) 182 throws ParseException 183 { 184 this(s, null); 185 } 186 187 188 189 /** 190 * Creates a new value pattern from the provided string. 191 * 192 * @param s The string representation of the value pattern to create. It 193 * must not be {@code null}. 194 * @param r The seed to use for the random number generator. It may be 195 * {@code null} if no seed is required. 196 * 197 * @throws ParseException If the provided string cannot be parsed as a valid 198 * value pattern string. 199 */ 200 public ValuePattern(final String s, final Long r) 201 throws ParseException 202 { 203 Validator.ensureNotNull(s); 204 205 pattern = s; 206 refLists = new ThreadLocal<ArrayList<String>>(); 207 buffers = new ThreadLocal<StringBuilder>(); 208 209 final AtomicBoolean hasRef = new AtomicBoolean(false); 210 211 final Random random; 212 if (r == null) 213 { 214 random = new Random(); 215 } 216 else 217 { 218 random = new Random(r); 219 } 220 221 final ArrayList<ValuePatternComponent> l = 222 new ArrayList<ValuePatternComponent>(3); 223 parse(s, 0, l, random, hasRef); 224 225 hasBackReference = hasRef.get(); 226 if (hasBackReference) 227 { 228 int availableReferences = 0; 229 for (final ValuePatternComponent c : l) 230 { 231 if (c instanceof BackReferenceValuePatternComponent) 232 { 233 final BackReferenceValuePatternComponent brvpc = 234 (BackReferenceValuePatternComponent) c; 235 if (brvpc.getIndex() > availableReferences) 236 { 237 throw new ParseException( 238 ERR_REF_VALUE_PATTERN_INVALID_INDEX.get(brvpc.getIndex()), 0); 239 } 240 } 241 242 if (c.supportsBackReference()) 243 { 244 availableReferences++; 245 } 246 } 247 } 248 249 components = new ValuePatternComponent[l.size()]; 250 l.toArray(components); 251 } 252 253 254 255 /** 256 * Recursively parses the provided string into a list of value pattern 257 * components. 258 * 259 * @param s The string representation of the value pattern to create. It 260 * may be a portion of the entire value pattern string. 261 * @param o The offset of the first character of the provided string in 262 * the full value pattern string. 263 * @param l The list into which the parsed components should be added. 264 * @param r The random number generator to use to seed random number 265 * generators used by components. 266 * @param ref A value that may be updated if the pattern contains any 267 * back-references. 268 * 269 * @throws ParseException If the provided string cannot be parsed as a valid 270 * value pattern string. 271 */ 272 private static void parse(final String s, final int o, 273 final ArrayList<ValuePatternComponent> l, 274 final Random r, final AtomicBoolean ref) 275 throws ParseException 276 { 277 // Find the first occurrence of "[[". Parse the portion of the string 278 // before it, into the list, then add a string value pattern containing "[", 279 // then parse the portion of the string after it. 280 // First, parse out any occurrences of "[[" and replace them with string 281 // value pattern components containing only "[". 282 int pos = s.indexOf("[["); 283 if (pos >= 0) 284 { 285 if (pos > 0) 286 { 287 parse(s.substring(0, pos), o, l, r, ref); 288 } 289 290 l.add(new StringValuePatternComponent("[")); 291 292 if (pos < (s.length() - 2)) 293 { 294 parse(s.substring(pos+2), (o+pos+2), l, r, ref); 295 } 296 return; 297 } 298 299 // Find the first occurrence of "]]". Parse the portion of the string 300 // before it, into the list, then add a string value pattern containing "]", 301 // then parse the portion of the string after it. 302 pos = s.indexOf("]]"); 303 if (pos >= 0) 304 { 305 if (pos > 0) 306 { 307 parse(s.substring(0, pos), o, l, r, ref); 308 } 309 310 l.add(new StringValuePatternComponent("]")); 311 312 if (pos < (s.length() - 2)) 313 { 314 parse(s.substring(pos+2), (o+pos+2), l, r, ref); 315 } 316 return; 317 } 318 319 // Find the first occurrence of "[" and the corresponding "]". The part 320 // before that will be a string. Then parse out the numeric or URL 321 // component, and parse the rest of the string after the "]". 322 pos = s.indexOf('['); 323 if (pos >= 0) 324 { 325 final int closePos = s.indexOf(']'); 326 if (closePos < 0) 327 { 328 throw new ParseException( 329 ERR_VALUE_PATTERN_UNMATCHED_OPEN.get(o+pos), (o+pos)); 330 } 331 else if (closePos < pos) 332 { 333 throw new ParseException( 334 ERR_VALUE_PATTERN_UNMATCHED_CLOSE.get(o+closePos), (o+closePos)); 335 } 336 337 if (pos > 0) 338 { 339 l.add(new StringValuePatternComponent(s.substring(0, pos))); 340 } 341 342 final String bracketedToken = s.substring(pos+1, closePos); 343 if (bracketedToken.startsWith("file:")) 344 { 345 final String path = bracketedToken.substring(5); 346 try 347 { 348 l.add(new FileValuePatternComponent(path, r.nextLong())); 349 } 350 catch (IOException ioe) 351 { 352 debugException(ioe); 353 throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get( 354 path, getExceptionMessage(ioe)), o+pos); 355 } 356 } 357 else if (bracketedToken.startsWith("http://")) 358 { 359 try 360 { 361 l.add(new HTTPValuePatternComponent(bracketedToken, r.nextLong())); 362 } 363 catch (IOException ioe) 364 { 365 debugException(ioe); 366 throw new ParseException(ERR_HTTP_VALUE_PATTERN_NOT_USABLE.get( 367 bracketedToken, getExceptionMessage(ioe)), o+pos); 368 } 369 } 370 else if (bracketedToken.startsWith("ref:")) 371 { 372 ref.set(true); 373 374 final String valueStr = bracketedToken.substring(4); 375 try 376 { 377 final int index = Integer.parseInt(valueStr); 378 if (index == 0) 379 { 380 throw new ParseException(ERR_REF_VALUE_PATTERN_ZERO_INDEX.get(), 381 (o+pos+4)); 382 } 383 else if (index < 0) 384 { 385 throw new ParseException( 386 ERR_REF_VALUE_PATTERN_NOT_VALID.get(valueStr), (o+pos+4)); 387 } 388 else 389 { 390 l.add(new BackReferenceValuePatternComponent(index)); 391 } 392 } 393 catch (final NumberFormatException nfe) 394 { 395 debugException(nfe); 396 throw new ParseException( 397 ERR_REF_VALUE_PATTERN_NOT_VALID.get(valueStr), (o+pos+4)); 398 } 399 } 400 else 401 { 402 l.add(parseNumericComponent(s.substring(pos+1, closePos), (o+pos+1), 403 r)); 404 } 405 406 if (closePos < (s.length() - 1)) 407 { 408 parse(s.substring(closePos+1), (o+closePos+1), l, r, ref); 409 } 410 411 return; 412 } 413 414 415 // If there are any occurrences of "]" without a corresponding open, then 416 // that's invalid. 417 pos = s.indexOf(']'); 418 if (pos >= 0) 419 { 420 throw new ParseException( 421 ERR_VALUE_PATTERN_UNMATCHED_CLOSE.get(o+pos), (o+pos)); 422 } 423 424 // There are no brackets, so it's just a static string. 425 l.add(new StringValuePatternComponent(s)); 426 } 427 428 429 430 /** 431 * Parses the specified portion of the provided string as either a 432 * sequential or random numeric value pattern component. 433 * 434 * @param s The string to parse, not including the square brackets. 435 * @param o The offset in the overall value pattern string at which the 436 * provided substring begins. 437 * @param r The random number generator to use to seed random number 438 * generators used by components. 439 * 440 * @return The parsed numeric value pattern component. 441 * 442 * @throws ParseException If the specified substring cannot be parsed as a 443 * 444 */ 445 private static ValuePatternComponent parseNumericComponent(final String s, 446 final int o, 447 final Random r) 448 throws ParseException 449 { 450 boolean delimiterFound = false; 451 boolean sequential = false; 452 int pos = 0; 453 long lowerBound = 0L; 454 455lowerBoundLoop: 456 for ( ; pos < s.length(); pos++) 457 { 458 switch (s.charAt(pos)) 459 { 460 case '0': 461 case '1': 462 case '2': 463 case '3': 464 case '4': 465 case '5': 466 case '6': 467 case '7': 468 case '8': 469 case '9': 470 // These are all acceptable. 471 break; 472 473 case '-': 474 if (pos == 0) 475 { 476 // This indicates that the value is negative. 477 break; 478 } 479 else 480 { 481 // This indicates the end of the lower bound. 482 delimiterFound = true; 483 sequential = false; 484 485 try 486 { 487 lowerBound = Long.parseLong(s.substring(0, pos)); 488 } 489 catch (NumberFormatException nfe) 490 { 491 Debug.debugException(nfe); 492 throw new ParseException( 493 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 494 Long.MAX_VALUE), 495 (o-1)); 496 } 497 pos++; 498 break lowerBoundLoop; 499 } 500 501 case ':': 502 delimiterFound = true; 503 sequential = true; 504 505 if (pos == 0) 506 { 507 throw new ParseException( 508 ERR_VALUE_PATTERN_EMPTY_LOWER_BOUND.get(o-1), (o-1)); 509 } 510 else 511 { 512 try 513 { 514 lowerBound = Long.parseLong(s.substring(0, pos)); 515 } 516 catch (NumberFormatException nfe) 517 { 518 Debug.debugException(nfe); 519 throw new ParseException( 520 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 521 Long.MAX_VALUE), 522 (o-1)); 523 } 524 } 525 pos++; 526 break lowerBoundLoop; 527 528 default: 529 throw new ParseException( 530 ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), (o+pos)), 531 (o+pos)); 532 } 533 } 534 535 if (! delimiterFound) 536 { 537 throw new ParseException(ERR_VALUE_PATTERN_NO_DELIMITER.get(o-1), (o-1)); 538 } 539 540 boolean hasIncrement = false; 541 int startPos = pos; 542 long upperBound = lowerBound; 543 long increment = 1L; 544 String formatString = null; 545 546 delimiterFound = false; 547 548upperBoundLoop: 549 for ( ; pos < s.length(); pos++) 550 { 551 switch (s.charAt(pos)) 552 { 553 case '0': 554 case '1': 555 case '2': 556 case '3': 557 case '4': 558 case '5': 559 case '6': 560 case '7': 561 case '8': 562 case '9': 563 // These are all acceptable. 564 break; 565 566 case '-': 567 if (pos == startPos) 568 { 569 // This indicates that the value is negative. 570 break; 571 } 572 else 573 { 574 throw new ParseException( 575 ERR_VALUE_PATTERN_INVALID_CHARACTER.get('-', (o+pos)), 576 (o+pos)); 577 } 578 579 case 'x': 580 delimiterFound = true; 581 hasIncrement = true; 582 583 if (pos == startPos) 584 { 585 throw new ParseException( 586 ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1)); 587 } 588 else 589 { 590 try 591 { 592 upperBound = Long.parseLong(s.substring(startPos, pos)); 593 } 594 catch (NumberFormatException nfe) 595 { 596 Debug.debugException(nfe); 597 throw new ParseException( 598 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 599 Long.MAX_VALUE), 600 (o-1)); 601 } 602 } 603 pos++; 604 break upperBoundLoop; 605 606 case '%': 607 delimiterFound = true; 608 hasIncrement = false; 609 610 if (pos == startPos) 611 { 612 throw new ParseException( 613 ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1)); 614 } 615 else 616 { 617 try 618 { 619 upperBound = Long.parseLong(s.substring(startPos, pos)); 620 } 621 catch (NumberFormatException nfe) 622 { 623 Debug.debugException(nfe); 624 throw new ParseException( 625 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 626 Long.MAX_VALUE), 627 (o-1)); 628 } 629 } 630 pos++; 631 break upperBoundLoop; 632 633 default: 634 throw new ParseException( 635 ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), (o+pos)), 636 (o+pos)); 637 } 638 } 639 640 if (! delimiterFound) 641 { 642 if (pos == startPos) 643 { 644 throw new ParseException( 645 ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1)); 646 } 647 648 try 649 { 650 upperBound = Long.parseLong(s.substring(startPos, pos)); 651 } 652 catch (NumberFormatException nfe) 653 { 654 Debug.debugException(nfe); 655 throw new ParseException( 656 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 657 Long.MAX_VALUE), 658 (o-1)); 659 } 660 661 if (sequential) 662 { 663 return new SequentialValuePatternComponent(lowerBound, upperBound, 1, 664 null); 665 } 666 else 667 { 668 return new RandomValuePatternComponent(lowerBound, upperBound, 669 r.nextLong(), null); 670 } 671 } 672 673 if (hasIncrement) 674 { 675 delimiterFound = false; 676 startPos = pos; 677 678incrementLoop: 679 for ( ; pos < s.length(); pos++) 680 { 681 switch (s.charAt(pos)) 682 { 683 case '0': 684 case '1': 685 case '2': 686 case '3': 687 case '4': 688 case '5': 689 case '6': 690 case '7': 691 case '8': 692 case '9': 693 // These are all acceptable. 694 break; 695 696 case '-': 697 if (pos == startPos) 698 { 699 // This indicates that the value is negative. 700 break; 701 } 702 else 703 { 704 throw new ParseException( 705 ERR_VALUE_PATTERN_INVALID_CHARACTER.get('-', (o+pos)), 706 (o+pos)); 707 } 708 709 case '%': 710 delimiterFound = true; 711 if (pos == startPos) 712 { 713 throw new ParseException( 714 ERR_VALUE_PATTERN_EMPTY_INCREMENT.get(o-1), (o-1)); 715 } 716 else if (pos == (s.length() - 1)) 717 { 718 throw new ParseException( 719 ERR_VALUE_PATTERN_EMPTY_FORMAT.get(o-1), (o-1)); 720 } 721 else 722 { 723 try 724 { 725 increment = Long.parseLong(s.substring(startPos, pos)); 726 } 727 catch (NumberFormatException nfe) 728 { 729 Debug.debugException(nfe); 730 throw new ParseException( 731 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 732 Long.MAX_VALUE), 733 (o-1)); 734 } 735 736 formatString = s.substring(pos+1); 737 } 738 break incrementLoop; 739 740 default: 741 throw new ParseException( 742 ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), 743 (o+pos)), 744 (o+pos)); 745 } 746 } 747 748 if (! delimiterFound) 749 { 750 if (pos == startPos) 751 { 752 throw new ParseException( 753 ERR_VALUE_PATTERN_EMPTY_INCREMENT.get(o-1), (o-1)); 754 } 755 756 try 757 { 758 increment = Long.parseLong(s.substring(startPos, pos)); 759 } 760 catch (NumberFormatException nfe) 761 { 762 Debug.debugException(nfe); 763 throw new ParseException( 764 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE, 765 Long.MAX_VALUE), 766 (o-1)); 767 } 768 } 769 } 770 else 771 { 772 formatString = s.substring(pos); 773 if (formatString.length() == 0) 774 { 775 throw new ParseException( 776 ERR_VALUE_PATTERN_EMPTY_FORMAT.get(o-1), (o-1)); 777 } 778 } 779 780 if (sequential) 781 { 782 return new SequentialValuePatternComponent(lowerBound, upperBound, 783 increment, formatString); 784 } 785 else 786 { 787 return new RandomValuePatternComponent(lowerBound, upperBound, 788 r.nextLong(), formatString); 789 } 790 } 791 792 793 794 /** 795 * Retrieves the next value generated from the value pattern. 796 * 797 * @return The next value generated from the value pattern. 798 */ 799 public String nextValue() 800 { 801 StringBuilder buffer = buffers.get(); 802 if (buffer == null) 803 { 804 buffer = new StringBuilder(); 805 buffers.set(buffer); 806 } 807 else 808 { 809 buffer.setLength(0); 810 } 811 812 ArrayList<String> refList = refLists.get(); 813 if (hasBackReference) 814 { 815 if (refList == null) 816 { 817 refList = new ArrayList<String>(10); 818 refLists.set(refList); 819 } 820 else 821 { 822 refList.clear(); 823 } 824 } 825 826 for (final ValuePatternComponent c : components) 827 { 828 if (hasBackReference) 829 { 830 if (c instanceof BackReferenceValuePatternComponent) 831 { 832 final BackReferenceValuePatternComponent brvpc = 833 (BackReferenceValuePatternComponent) c; 834 final String value = refList.get(brvpc.getIndex() - 1); 835 buffer.append(value); 836 refList.add(value); 837 } 838 else if (c.supportsBackReference()) 839 { 840 final int startPos = buffer.length(); 841 c.append(buffer); 842 refList.add(buffer.substring(startPos)); 843 } 844 else 845 { 846 c.append(buffer); 847 } 848 } 849 else 850 { 851 c.append(buffer); 852 } 853 } 854 855 return buffer.toString(); 856 } 857 858 859 860 /** 861 * Retrieves a string representation of this value pattern, which will be the 862 * original pattern string used to create it. 863 * 864 * @return A string representation of this value pattern. 865 */ 866 @Override() 867 public String toString() 868 { 869 return pattern; 870 } 871}