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