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     */
021    package com.unboundid.util;
022    
023    
024    
025    import java.io.IOException;
026    import java.io.Serializable;
027    import java.text.ParseException;
028    import java.util.ArrayList;
029    import java.util.Random;
030    import java.util.concurrent.atomic.AtomicBoolean;
031    
032    import static com.unboundid.util.Debug.*;
033    import static com.unboundid.util.StaticUtils.*;
034    import static com.unboundid.util.UtilityMessages.*;
035    
036    
037    
038    /**
039     * This class provides a method for generating a string value comprised of zero
040     * or more components.  The components may be any combination of zero or more
041     * strings, sequential numeric ranges, and random numeric ranges.  These
042     * components should be formatted as follows:
043     * <UL>
044     *   <LI>Strings are simply any kind of static text that will be used as-is
045     *       without any modification, except that double opening or closing square
046     *       brackets (i.e., "<CODE>[[</CODE>" or "<CODE>]]</CODE>") will be
047     *       replaced with single opening or closing square brackets to distinguish
048     *       them from the square brackets used in numeric ranges or URL
049     *       references.</LI>
050     *   <LI>Sequential numeric ranges consist of an opening square bracket, a
051     *       numeric value to be used as the lower bound for the range, a colon, a
052     *       second numeric value to be used as the upper bound for the range, an
053     *       optional '<CODE>x</CODE>' character followed by a numeric value to be
054     *       used as the increment, an optional '<CODE>%</CODE>' character followed
055     *       by a format string as allowed by the {@link java.text.DecimalFormat}
056     *       class to define how the resulting value should be formatted, and a
057     *       closing square bracket to indicate the end of the range.</LI>
058     *   <LI>Random numeric ranges consist of an opening square bracket, a
059     *       numeric value to be used as the lower bound for the range, a dash, a
060     *       second numeric value to be used as the upper bound for the range, an
061     *       optional '<CODE>%</CODE>' character followed by a format string as
062     *       allowed by the {@link java.text.DecimalFormat} class to define how the
063     *       resulting value should be formatted, and a closing square bracket to
064     *       indicate the end of the range.</LI>
065     *   <LI>Strings read from a file specified by a given URL.  That file may be
066     *       contained on the local filesystem (using a URL like
067     *       "file:///tmp/mydata.txt") or read from a remote server via HTTP (using
068     *       a URL like "http://server.example.com/mydata.txt").  In either case,
069     *       the provided URL must not contain a closing square bracket character.
070     *       If this option is used, then that file must contain one value per line,
071     *       and its contents will be read into memory and values from the file will
072     *       be selected in a random order and used in place of the bracketed
073     *       URL.</LI>
074     *   <LI>Back-references that will be replaced with the same value as the
075     *       bracketed token in the specified position in the string.  For example,
076     *       a component of "[ref:1]" will be replaced with the same value as used
077     *       in the first bracketed component of the value pattern.  Back-references
078     *       must only reference components that have been previously defined in the
079     *       value pattern, and not those which appear after the reference.</LI>
080     * </UL>
081     * <BR>
082     * It must be possible to represent all of the numeric values used in sequential
083     * or random numeric ranges as {@code long} values.  In a sequential numeric
084     * range, if the first value is larger than the second value, then values will
085     * be chosen in descending rather than ascending order (and if an increment is
086     * given, then it should be positive).  In addition, once the end of a
087     * sequential range has been reached, then the value will wrap around to the
088     * beginning of that range.
089     * <BR>
090     * Examples of value pattern components include:
091     * <UL>
092     *   <LI><CODE>Hello</CODE> -- The static text "<CODE>Hello</CODE>".</LI>
093     *   <LI><CODE>[[Hello]]</CODE> -- The static text "<CODE>[Hello]</CODE>" (note
094     *       that the double square brackets were replaced with single square
095     *       brackets).</LI>
096     *   <LI><CODE>[0:1000]</CODE> -- A sequential numeric range that will iterate
097     *      in ascending sequential order from 0 to 1000.  The 1002nd value that is
098     *      requested will cause the value to be wrapped around to 0 again.</LI>
099     *   <LI><CODE>[1000:0]</CODE> -- A sequential numeric range that will iterate
100     *      in descending sequential order from 1000 to 0.  The 1002nd value that is
101     *      requested will cause the value to be wrapped around to 1000 again.</LI>
102     *   <LI><CODE>[0:1000x5%0000]</CODE> -- A sequential numeric range that will
103     *      iterate in ascending sequential order from 0 to 1000 in increments of
104     *      five with all values represented as four-digit numbers padded with
105     *      leading zeroes.  For example, the first four values generated by this
106     *      component will be "0000", "0005", "0010", and "0015".</LI>
107     *   <LI><CODE>[0-1000]</CODE> -- A random numeric range that will choose values
108     *       at random between 0 and 1000, inclusive.</LI>
109     *   <LI><CODE>[0-1000%0000]</CODE> -- A random numeric range that will choose
110     *       values at random between 0 and 1000, inclusive, and values will be
111     *       padded with leading zeroes as necessary so that they are represented
112     *       using four digits.</LI>
113     *   <LI><CODE>[file:///tmp/mydata.txt]</CODE> -- A URL reference that will
114     *       cause randomly-selected lines from the specified local file to be used
115     *       in place of the bracketed range.  To make it clear that the file
116     *       contents are randomly accessed, you may use {@code randomfile} in place
117     *       of {@code file}.</LI>
118     *   <LI><CODE>[sequentialfile:///tmp/mydata.txt]</CODE> -- A URL reference that
119     *       will cause lines from the specified local file, selected in sequential
120     *       order, to be used in place of the bracketed range.</LI>
121     *   <LI><CODE>[http://server.example.com/tmp/mydata.txt]</CODE> -- A URL
122     *       reference that will cause randomly-selected lines from the specified
123     *       remote HTTP-accessible file to be used in place of the bracketed
124     *       range.</LI>
125     * </UL>
126     * <BR>
127     * Examples of full value pattern strings include:
128     * <UL>
129     *   <LI><CODE>dc=example,dc=com</CODE> -- A value pattern containing only
130     *       static text and no numeric components.</LI>
131     *   <LI><CODE>[1000:9999]</CODE> -- A value pattern containing only a numeric
132     *       component that will choose numbers in sequential order from 1000 to
133     *       9999.</LI>
134     *   <LI><CODE>(uid=user.[1-1000000])</CODE> -- A value pattern that combines
135     *       the static text "<CODE>(uid=user.</CODE>" with a value chosen randomly
136     *       between one and one million, and another static text string of
137     *       "<CODE>)</CODE>".</LI>
138     *   <LI><CODE>uid=user.[1-1000000],ou=org[1-10],dc=example,dc=com</CODE> -- A
139     *       value pattern containing two numeric components interspersed between
140     *       three static text components.</LI>
141     *   <LI><CODE>uid=user.[1-1000000],ou=org[ref:1],dc=example,dc=com</CODE> -- A
142     *       value pattern in which the organization number will be the same as the
143     *       randomly-selected user number.</LI>
144     * </UL>
145     */
146    @NotMutable()
147    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
148    public final class ValuePattern
149           implements Serializable
150    {
151      /**
152       * The 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    
488    lowerBoundLoop:
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    
581    upperBoundLoop:
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    
711    incrementLoop:
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    }