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}