001 /*
002 * Copyright 2008-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 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 */
021 package com.unboundid.util;
022
023
024
025 import java.io.IOException;
026 import java.io.Serializable;
027 import java.text.ParseException;
028 import java.util.ArrayList;
029 import java.util.Random;
030 import java.util.concurrent.atomic.AtomicBoolean;
031
032 import static com.unboundid.util.Debug.*;
033 import static com.unboundid.util.StaticUtils.*;
034 import 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)
148 public 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
498 lowerBoundLoop:
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
591 upperBoundLoop:
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
721 incrementLoop:
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 }