001    /*
002     * Copyright 2008-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.util;
022    
023    
024    
025    import java.io.IOException;
026    import java.io.Serializable;
027    import java.text.ParseException;
028    import java.util.ArrayList;
029    import java.util.Random;
030    import java.util.concurrent.atomic.AtomicBoolean;
031    
032    import static com.unboundid.util.Debug.*;
033    import static com.unboundid.util.StaticUtils.*;
034    import static com.unboundid.util.UtilityMessages.*;
035    
036    
037    
038    /**
039     * This class provides a method for generating a string value comprised of zero
040     * or more components.  The components may be any combination of zero or more
041     * strings, sequential numeric ranges, and random numeric ranges.  These
042     * components should be formatted as follows:
043     * <UL>
044     *   <LI>Strings are simply any kind of static text that will be used as-is
045     *       without any modification, except that double opening or closing square
046     *       brackets (i.e., "<CODE>[[</CODE>" or "<CODE>]]</CODE>") will be
047     *       replaced with single opening or closing square brackets to distinguish
048     *       them from the square brackets used in numeric ranges or URL
049     *       references.</LI>
050     *   <LI>Sequential numeric ranges consist of an opening square bracket, a
051     *       numeric value to be used as the lower bound for the range, a colon, a
052     *       second numeric value to be used as the upper bound for the range, an
053     *       optional '<CODE>x</CODE>' character followed by a numeric value to be
054     *       used as the increment, an optional '<CODE>%</CODE>' character followed
055     *       by a format string as allowed by the {@link java.text.DecimalFormat}
056     *       class to define how the resulting value should be formatted, and a
057     *       closing square bracket to indicate the end of the range.</LI>
058     *   <LI>Random numeric ranges consist of an opening square bracket, a
059     *       numeric value to be used as the lower bound for the range, a dash, a
060     *       second numeric value to be used as the upper bound for the range, an
061     *       optional '<CODE>%</CODE>' character followed by a format string as
062     *       allowed by the {@link java.text.DecimalFormat} class to define how the
063     *       resulting value should be formatted, and a closing square bracket to
064     *       indicate the end of the range.</LI>
065     *   <LI>Strings read from a file specified by a given URL.  That file may be
066     *       contained on the local filesystem (using a URL like
067     *       "file:///tmp/mydata.txt") or read from a remote server via HTTP (using
068     *       a URL like "http://server.example.com/mydata.txt").  In either case,
069     *       the provided URL must not contain a closing square bracket character.
070     *       If this option is used, then that file must contain one value per line,
071     *       and its contents will be read into memory and values from the file will
072     *       be selected in a random order and used in place of the bracketed
073     *       URL.</LI>
074     *   <LI>Back-references that will be replaced with the same value as the
075     *       bracketed token in the specified position in the string.  For example,
076     *       a component of "[ref:1]" will be replaced with the same value as used
077     *       in the first bracketed component of the value pattern.  Back-references
078     *       must only reference components that have been previously defined in the
079     *       value pattern, and not those which appear after the reference.</LI>
080     * </UL>
081     * <BR>
082     * It must be possible to represent all of the numeric values used in sequential
083     * or random numeric ranges as {@code long} values.  In a sequential numeric
084     * range, if the first value is larger than the second value, then values will
085     * be chosen in descending rather than ascending order (and if an increment is
086     * given, then it should be positive).  In addition, once the end of a
087     * sequential range has been reached, then the value will wrap around to the
088     * beginning of that range.
089     * <BR>
090     * Examples of value pattern components include:
091     * <UL>
092     *   <LI><CODE>Hello</CODE> -- The static text "<CODE>Hello</CODE>".</LI>
093     *   <LI><CODE>[[Hello]]</CODE> -- The static text "<CODE>[Hello]</CODE>" (note
094     *       that the double square brackets were replaced with single square
095     *       brackets).</LI>
096     *   <LI><CODE>[0:1000]</CODE> -- A sequential numeric range that will iterate
097     *      in ascending sequential order from 0 to 1000.  The 1002nd value that is
098     *      requested will cause the value to be wrapped around to 0 again.</LI>
099     *   <LI><CODE>[1000:0]</CODE> -- A sequential numeric range that will iterate
100     *      in descending sequential order from 1000 to 0.  The 1002nd value that is
101     *      requested will cause the value to be wrapped around to 1000 again.</LI>
102     *   <LI><CODE>[0:1000x5%0000]</CODE> -- A sequential numeric range that will
103     *      iterate in ascending sequential order from 0 to 1000 in increments of
104     *      five with all values represented as four-digit numbers padded with
105     *      leading zeroes.  For example, the first four values generated by this
106     *      component will be "0000", "0005", "0010", and "0015".</LI>
107     *   <LI><CODE>[0-1000]</CODE> -- A random numeric range that will choose values
108     *       at random between 0 and 1000, inclusive.</LI>
109     *   <LI><CODE>[0-1000%0000]</CODE> -- A random numeric range that will choose
110     *       values at random between 0 and 1000, inclusive, and values will be
111     *       padded with leading zeroes as necessary so that they are represented
112     *       using four digits.</LI>
113     *   <LI><CODE>[file:///tmp/mydata.txt]</CODE> -- A URL reference that will
114     *       cause randomly-selected lines from the specified local file to be used
115     *       in place of the bracketed range.  To make it clear that the file
116     *       contents are randomly accessed, you may use {@code randomfile} in place
117     *       of {@code file}.</LI>
118     *   <LI><CODE>[sequentialfile:///tmp/mydata.txt]</CODE> -- A URL reference that
119     *       will cause lines from the specified local file, selected in sequential
120     *       order, to be used in place of the bracketed range.</LI>
121     *   <LI><CODE>[http://server.example.com/tmp/mydata.txt]</CODE> -- A URL
122     *       reference that will cause randomly-selected lines from the specified
123     *       remote HTTP-accessible file to be used in place of the bracketed
124     *       range.</LI>
125     * </UL>
126     * <BR>
127     * Examples of full value pattern strings include:
128     * <UL>
129     *   <LI><CODE>dc=example,dc=com</CODE> -- A value pattern containing only
130     *       static text and no numeric components.</LI>
131     *   <LI><CODE>[1000:9999]</CODE> -- A value pattern containing only a numeric
132     *       component that will choose numbers in sequential order from 1000 to
133     *       9999.</LI>
134     *   <LI><CODE>(uid=user.[1-1000000])</CODE> -- A value pattern that combines
135     *       the static text "<CODE>(uid=user.</CODE>" with a value chosen randomly
136     *       between one and one million, and another static text string of
137     *       "<CODE>)</CODE>".</LI>
138     *   <LI><CODE>uid=user.[1-1000000],ou=org[1-10],dc=example,dc=com</CODE> -- A
139     *       value pattern containing two numeric components interspersed between
140     *       three static text components.</LI>
141     *   <LI><CODE>uid=user.[1-1000000],ou=org[ref:1],dc=example,dc=com</CODE> -- A
142     *       value pattern in which the organization number will be the same as the
143     *       randomly-selected user number.</LI>
144     * </UL>
145     */
146    @NotMutable()
147    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
148    public final class ValuePattern
149           implements Serializable
150    {
151      /**
152       * The URL to the publicly-accessible javadoc for this class, which provides
153       * a detailed overview of the supported value pattern syntax.
154       */
155      public static final String PUBLIC_JAVADOC_URL =
156           "https://docs.ldap.com/ldap-sdk/docs/javadoc/index.html?" +
157                "com/unboundid/util/ValuePattern.html";
158    
159    
160    
161      /**
162       * The serial version UID for this serializable class.
163       */
164      private static final long serialVersionUID = 4502778464751705304L;
165    
166    
167    
168      // Indicates whether the provided value pattern includes one or more
169      // back-references.
170      private final boolean hasBackReference;
171    
172      // The string that was originally used to create this value pattern.
173      private final String pattern;
174    
175      // The thread-local array list that will be used to hold values for
176      // back-references.
177      private final ThreadLocal<ArrayList<String>> refLists;
178    
179      // The thread-local string builder that will be used to build values.
180      private final ThreadLocal<StringBuilder> buffers;
181    
182      // The value pattern components that will be used to generate values.
183      private final ValuePatternComponent[] components;
184    
185    
186    
187      /**
188       * Creates a new value pattern from the provided string.
189       *
190       * @param  s  The string representation of the value pattern to create.  It
191       *            must not be {@code null}.
192       *
193       * @throws  ParseException  If the provided string cannot be parsed as a valid
194       *                          value pattern string.
195       */
196      public ValuePattern(final String s)
197             throws ParseException
198      {
199        this(s, null);
200      }
201    
202    
203    
204      /**
205       * Creates a new value pattern from the provided string.
206       *
207       * @param  s  The string representation of the value pattern to create.  It
208       *            must not be {@code null}.
209       * @param  r  The seed to use for the random number generator.  It may be
210       *            {@code null} if no seed is required.
211       *
212       * @throws  ParseException  If the provided string cannot be parsed as a valid
213       *                          value pattern string.
214       */
215      public ValuePattern(final String s, final Long r)
216             throws ParseException
217      {
218        Validator.ensureNotNull(s);
219    
220        pattern  = s;
221        refLists = new ThreadLocal<ArrayList<String>>();
222        buffers  = new ThreadLocal<StringBuilder>();
223    
224        final AtomicBoolean hasRef = new AtomicBoolean(false);
225    
226        final Random random;
227        if (r == null)
228        {
229          random = new Random();
230        }
231        else
232        {
233          random = new Random(r);
234        }
235    
236        final ArrayList<ValuePatternComponent> l =
237             new ArrayList<ValuePatternComponent>(3);
238        parse(s, 0, l, random, hasRef);
239    
240        hasBackReference = hasRef.get();
241        if (hasBackReference)
242        {
243          int availableReferences = 0;
244          for (final ValuePatternComponent c : l)
245          {
246            if (c instanceof BackReferenceValuePatternComponent)
247            {
248              final BackReferenceValuePatternComponent brvpc =
249                   (BackReferenceValuePatternComponent) c;
250              if (brvpc.getIndex() > availableReferences)
251              {
252                throw new ParseException(
253                     ERR_REF_VALUE_PATTERN_INVALID_INDEX.get(brvpc.getIndex()), 0);
254              }
255            }
256    
257            if (c.supportsBackReference())
258            {
259              availableReferences++;
260            }
261          }
262        }
263    
264        components = new ValuePatternComponent[l.size()];
265        l.toArray(components);
266      }
267    
268    
269    
270      /**
271       * Recursively parses the provided string into a list of value pattern
272       * components.
273       *
274       * @param  s    The string representation of the value pattern to create.  It
275       *              may be a portion of the entire value pattern string.
276       * @param  o    The offset of the first character of the provided string in
277       *              the full value pattern string.
278       * @param  l    The list into which the parsed components should be added.
279       * @param  r    The random number generator to use to seed random number
280       *              generators used by components.
281       * @param  ref  A value that may be updated if the pattern contains any
282       *              back-references.
283       *
284       * @throws  ParseException  If the provided string cannot be parsed as a valid
285       *                          value pattern string.
286       */
287      private static void parse(final String s, final int o,
288                                final ArrayList<ValuePatternComponent> l,
289                                final Random r, final AtomicBoolean ref)
290              throws ParseException
291      {
292        // Find the first occurrence of "[[".  Parse the portion of the string
293        // before it, into the list, then add a string value pattern containing "[",
294        // then parse the portion of the string after it.
295        // First, parse out any occurrences of "[[" and replace them with string
296        // value pattern components containing only "[".
297        int pos = s.indexOf("[[");
298        if (pos >= 0)
299        {
300          if (pos > 0)
301          {
302            parse(s.substring(0, pos), o, l, r, ref);
303          }
304    
305          l.add(new StringValuePatternComponent("["));
306    
307          if (pos < (s.length() - 2))
308          {
309            parse(s.substring(pos+2), (o+pos+2), l, r, ref);
310          }
311          return;
312        }
313    
314        // Find the first occurrence of "]]".  Parse the portion of the string
315        // before it, into the list, then add a string value pattern containing "]",
316        // then parse the portion of the string after it.
317        pos = s.indexOf("]]");
318        if (pos >= 0)
319        {
320          if (pos > 0)
321          {
322            parse(s.substring(0, pos), o, l, r, ref);
323          }
324    
325          l.add(new StringValuePatternComponent("]"));
326    
327          if (pos < (s.length() - 2))
328          {
329            parse(s.substring(pos+2), (o+pos+2), l, r, ref);
330          }
331          return;
332        }
333    
334        // Find the first occurrence of "[" and the corresponding "]".  The part
335        // before that will be a string.  Then parse out the numeric or URL
336        // component, and parse the rest of the string after the "]".
337        pos = s.indexOf('[');
338        if (pos >= 0)
339        {
340          final int closePos = s.indexOf(']');
341          if (closePos < 0)
342          {
343            throw new ParseException(
344                 ERR_VALUE_PATTERN_UNMATCHED_OPEN.get(o+pos), (o+pos));
345          }
346          else if (closePos < pos)
347          {
348            throw new ParseException(
349                 ERR_VALUE_PATTERN_UNMATCHED_CLOSE.get(o+closePos), (o+closePos));
350          }
351    
352          if (pos > 0)
353          {
354            l.add(new StringValuePatternComponent(s.substring(0, pos)));
355          }
356    
357          final String bracketedToken = s.substring(pos+1, closePos);
358          if (bracketedToken.startsWith("file:"))
359          {
360            final String path = bracketedToken.substring(5);
361            try
362            {
363              l.add(new FileValuePatternComponent(path, r.nextLong(), false));
364            }
365            catch (IOException ioe)
366            {
367              debugException(ioe);
368              throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get(
369                   path, getExceptionMessage(ioe)), o+pos);
370            }
371          }
372          else if (bracketedToken.startsWith("randomfile:"))
373          {
374            final String path = bracketedToken.substring(11);
375            try
376            {
377              l.add(new FileValuePatternComponent(path, r.nextLong(), false));
378            }
379            catch (IOException ioe)
380            {
381              debugException(ioe);
382              throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get(
383                   path, getExceptionMessage(ioe)), o+pos);
384            }
385          }
386          else if (bracketedToken.startsWith("sequentialfile:"))
387          {
388            final String path = bracketedToken.substring(15);
389            try
390            {
391              l.add(new FileValuePatternComponent(path, r.nextLong(), true));
392            }
393            catch (IOException ioe)
394            {
395              debugException(ioe);
396              throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get(
397                   path, getExceptionMessage(ioe)), o+pos);
398            }
399          }
400          else if (bracketedToken.startsWith("http://"))
401          {
402            try
403            {
404              l.add(new HTTPValuePatternComponent(bracketedToken, r.nextLong()));
405            }
406            catch (IOException ioe)
407            {
408              debugException(ioe);
409              throw new ParseException(ERR_HTTP_VALUE_PATTERN_NOT_USABLE.get(
410                   bracketedToken, getExceptionMessage(ioe)), o+pos);
411            }
412          }
413          else if (bracketedToken.startsWith("ref:"))
414          {
415            ref.set(true);
416    
417            final String valueStr = bracketedToken.substring(4);
418            try
419            {
420              final int index = Integer.parseInt(valueStr);
421              if (index == 0)
422              {
423                throw new ParseException(ERR_REF_VALUE_PATTERN_ZERO_INDEX.get(),
424                     (o+pos+4));
425              }
426              else if (index < 0)
427              {
428                throw new ParseException(
429                     ERR_REF_VALUE_PATTERN_NOT_VALID.get(valueStr), (o+pos+4));
430              }
431              else
432              {
433                l.add(new BackReferenceValuePatternComponent(index));
434              }
435            }
436            catch (final NumberFormatException nfe)
437            {
438              debugException(nfe);
439              throw new ParseException(
440                   ERR_REF_VALUE_PATTERN_NOT_VALID.get(valueStr),  (o+pos+4));
441            }
442          }
443          else
444          {
445            l.add(parseNumericComponent(s.substring(pos+1, closePos), (o+pos+1),
446                                        r));
447          }
448    
449          if (closePos < (s.length() - 1))
450          {
451            parse(s.substring(closePos+1), (o+closePos+1), l, r, ref);
452          }
453    
454          return;
455        }
456    
457    
458        // If there are any occurrences of "]" without a corresponding open, then
459        // that's invalid.
460        pos = s.indexOf(']');
461        if (pos >= 0)
462        {
463          throw new ParseException(
464               ERR_VALUE_PATTERN_UNMATCHED_CLOSE.get(o+pos), (o+pos));
465        }
466    
467        // There are no brackets, so it's just a static string.
468        l.add(new StringValuePatternComponent(s));
469      }
470    
471    
472    
473      /**
474       * Parses the specified portion of the provided string as either a
475       * sequential or random numeric value pattern component.
476       *
477       * @param  s  The string to parse, not including the square brackets.
478       * @param  o  The offset in the overall value pattern string at which the
479       *            provided substring begins.
480       * @param  r  The random number generator to use to seed random number
481       *            generators used by components.
482       *
483       * @return  The parsed numeric value pattern component.
484       *
485       * @throws  ParseException  If the specified substring cannot be parsed as a
486       *
487       */
488      private static ValuePatternComponent parseNumericComponent(final String s,
489                                                                 final int o,
490                                                                 final Random r)
491              throws ParseException
492      {
493        boolean delimiterFound = false;
494        boolean sequential     = false;
495        int     pos            = 0;
496        long   lowerBound      = 0L;
497    
498    lowerBoundLoop:
499        for ( ; pos < s.length(); pos++)
500        {
501          switch (s.charAt(pos))
502          {
503            case '0':
504            case '1':
505            case '2':
506            case '3':
507            case '4':
508            case '5':
509            case '6':
510            case '7':
511            case '8':
512            case '9':
513              // These are all acceptable.
514              break;
515    
516            case '-':
517              if (pos == 0)
518              {
519                // This indicates that the value is negative.
520                break;
521              }
522              else
523              {
524                // This indicates the end of the lower bound.
525                delimiterFound = true;
526                sequential     = false;
527    
528                try
529                {
530                  lowerBound = Long.parseLong(s.substring(0, pos));
531                }
532                catch (NumberFormatException nfe)
533                {
534                  Debug.debugException(nfe);
535                  throw new ParseException(
536                       ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
537                                                            Long.MAX_VALUE),
538                       (o-1));
539                }
540                pos++;
541                break lowerBoundLoop;
542              }
543    
544            case ':':
545              delimiterFound = true;
546              sequential     = true;
547    
548              if (pos == 0)
549              {
550                throw new ParseException(
551                     ERR_VALUE_PATTERN_EMPTY_LOWER_BOUND.get(o-1), (o-1));
552              }
553              else
554              {
555                try
556                {
557                  lowerBound = Long.parseLong(s.substring(0, pos));
558                }
559                catch (NumberFormatException nfe)
560                {
561                  Debug.debugException(nfe);
562                  throw new ParseException(
563                       ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
564                                                            Long.MAX_VALUE),
565                       (o-1));
566                }
567              }
568              pos++;
569              break lowerBoundLoop;
570    
571            default:
572              throw new ParseException(
573                   ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), (o+pos)),
574                   (o+pos));
575          }
576        }
577    
578        if (! delimiterFound)
579        {
580          throw new ParseException(ERR_VALUE_PATTERN_NO_DELIMITER.get(o-1), (o-1));
581        }
582    
583        boolean hasIncrement = false;
584        int     startPos     = pos;
585        long    upperBound   = lowerBound;
586        long    increment    = 1L;
587        String  formatString = null;
588    
589        delimiterFound = false;
590    
591    upperBoundLoop:
592        for ( ; pos < s.length(); pos++)
593        {
594          switch (s.charAt(pos))
595          {
596            case '0':
597            case '1':
598            case '2':
599            case '3':
600            case '4':
601            case '5':
602            case '6':
603            case '7':
604            case '8':
605            case '9':
606              // These are all acceptable.
607              break;
608    
609            case '-':
610              if (pos == startPos)
611              {
612                // This indicates that the value is negative.
613                break;
614              }
615              else
616              {
617                throw new ParseException(
618                     ERR_VALUE_PATTERN_INVALID_CHARACTER.get('-', (o+pos)),
619                     (o+pos));
620              }
621    
622            case 'x':
623              delimiterFound = true;
624              hasIncrement   = true;
625    
626              if (pos == startPos)
627              {
628                throw new ParseException(
629                     ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1));
630              }
631              else
632              {
633                try
634                {
635                  upperBound = Long.parseLong(s.substring(startPos, pos));
636                }
637                catch (NumberFormatException nfe)
638                {
639                  Debug.debugException(nfe);
640                  throw new ParseException(
641                       ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
642                                                            Long.MAX_VALUE),
643                       (o-1));
644                }
645              }
646              pos++;
647              break upperBoundLoop;
648    
649            case '%':
650              delimiterFound = true;
651              hasIncrement   = false;
652    
653              if (pos == startPos)
654              {
655                throw new ParseException(
656                     ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1));
657              }
658              else
659              {
660                try
661                {
662                  upperBound = Long.parseLong(s.substring(startPos, pos));
663                }
664                catch (NumberFormatException nfe)
665                {
666                  Debug.debugException(nfe);
667                  throw new ParseException(
668                       ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
669                                                            Long.MAX_VALUE),
670                       (o-1));
671                }
672              }
673              pos++;
674              break upperBoundLoop;
675    
676            default:
677              throw new ParseException(
678                   ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), (o+pos)),
679                   (o+pos));
680          }
681        }
682    
683        if (! delimiterFound)
684        {
685          if (pos == startPos)
686          {
687            throw new ParseException(
688                 ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1));
689          }
690    
691          try
692          {
693            upperBound = Long.parseLong(s.substring(startPos, pos));
694          }
695          catch (NumberFormatException nfe)
696          {
697            Debug.debugException(nfe);
698            throw new ParseException(
699                 ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
700                                                      Long.MAX_VALUE),
701                 (o-1));
702          }
703    
704          if (sequential)
705          {
706            return new SequentialValuePatternComponent(lowerBound, upperBound, 1,
707                                                       null);
708          }
709          else
710          {
711            return new RandomValuePatternComponent(lowerBound, upperBound,
712                                                   r.nextLong(), null);
713          }
714        }
715    
716        if (hasIncrement)
717        {
718          delimiterFound = false;
719          startPos       = pos;
720    
721    incrementLoop:
722          for ( ; pos < s.length(); pos++)
723          {
724            switch (s.charAt(pos))
725            {
726              case '0':
727              case '1':
728              case '2':
729              case '3':
730              case '4':
731              case '5':
732              case '6':
733              case '7':
734              case '8':
735              case '9':
736                // These are all acceptable.
737                break;
738    
739              case '-':
740                if (pos == startPos)
741                {
742                  // This indicates that the value is negative.
743                  break;
744                }
745                else
746                {
747                  throw new ParseException(
748                       ERR_VALUE_PATTERN_INVALID_CHARACTER.get('-', (o+pos)),
749                       (o+pos));
750                }
751    
752              case '%':
753                delimiterFound = true;
754                if (pos == startPos)
755                {
756                  throw new ParseException(
757                       ERR_VALUE_PATTERN_EMPTY_INCREMENT.get(o-1), (o-1));
758                }
759                else if (pos == (s.length() - 1))
760                {
761                  throw new ParseException(
762                       ERR_VALUE_PATTERN_EMPTY_FORMAT.get(o-1), (o-1));
763                }
764                else
765                {
766                  try
767                  {
768                    increment = Long.parseLong(s.substring(startPos, pos));
769                  }
770                  catch (NumberFormatException nfe)
771                  {
772                    Debug.debugException(nfe);
773                    throw new ParseException(
774                         ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
775                                                              Long.MAX_VALUE),
776                         (o-1));
777                  }
778    
779                  formatString = s.substring(pos+1);
780                }
781                break incrementLoop;
782    
783              default:
784                throw new ParseException(
785                     ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos),
786                                                             (o+pos)),
787                     (o+pos));
788            }
789          }
790    
791          if (! delimiterFound)
792          {
793            if (pos == startPos)
794            {
795              throw new ParseException(
796                   ERR_VALUE_PATTERN_EMPTY_INCREMENT.get(o-1), (o-1));
797            }
798    
799            try
800            {
801              increment = Long.parseLong(s.substring(startPos, pos));
802            }
803            catch (NumberFormatException nfe)
804            {
805              Debug.debugException(nfe);
806              throw new ParseException(
807                   ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
808                                                        Long.MAX_VALUE),
809                   (o-1));
810            }
811          }
812        }
813        else
814        {
815          formatString = s.substring(pos);
816          if (formatString.length() == 0)
817          {
818            throw new ParseException(
819                 ERR_VALUE_PATTERN_EMPTY_FORMAT.get(o-1), (o-1));
820          }
821        }
822    
823        if (sequential)
824        {
825          return new SequentialValuePatternComponent(lowerBound, upperBound,
826                                                     increment, formatString);
827        }
828        else
829        {
830          return new RandomValuePatternComponent(lowerBound, upperBound,
831                                                 r.nextLong(), formatString);
832        }
833      }
834    
835    
836    
837      /**
838       * Retrieves the next value generated from the value pattern.
839       *
840       * @return  The next value generated from the value pattern.
841       */
842      public String nextValue()
843      {
844        StringBuilder buffer = buffers.get();
845        if (buffer == null)
846        {
847          buffer = new StringBuilder();
848          buffers.set(buffer);
849        }
850        else
851        {
852          buffer.setLength(0);
853        }
854    
855        ArrayList<String> refList = refLists.get();
856        if (hasBackReference)
857        {
858          if (refList == null)
859          {
860            refList = new ArrayList<String>(10);
861            refLists.set(refList);
862          }
863          else
864          {
865            refList.clear();
866          }
867        }
868    
869        for (final ValuePatternComponent c : components)
870        {
871          if (hasBackReference)
872          {
873            if (c instanceof BackReferenceValuePatternComponent)
874            {
875              final BackReferenceValuePatternComponent brvpc =
876                   (BackReferenceValuePatternComponent) c;
877              final String value = refList.get(brvpc.getIndex() - 1);
878              buffer.append(value);
879              refList.add(value);
880            }
881            else if (c.supportsBackReference())
882            {
883              final int startPos = buffer.length();
884              c.append(buffer);
885              refList.add(buffer.substring(startPos));
886            }
887            else
888            {
889              c.append(buffer);
890            }
891          }
892          else
893          {
894            c.append(buffer);
895          }
896        }
897    
898        return buffer.toString();
899      }
900    
901    
902    
903      /**
904       * Retrieves a string representation of this value pattern, which will be the
905       * original pattern string used to create it.
906       *
907       * @return  A string representation of this value pattern.
908       */
909      @Override()
910      public String toString()
911      {
912        return pattern;
913      }
914    }