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}