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