Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2008-2024 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright 2008-2024 Ping Identity Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2008-2024 Ping Identity Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
package com.unboundid.util;
import java.io.IOException;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.unboundid.util.UtilityMessages.*;
/**
* This class provides a method for generating a string value comprised of zero
* or more components. The components may be any combination of zero or more
* strings, sequential numeric ranges, and random numeric ranges. These
* components should be formatted as follows:
*
*
Strings are simply any kind of static text that will be used as-is
* without any modification, except that double opening or closing square
* brackets (i.e., "[[" or "]]") will be
* replaced with single opening or closing square brackets to distinguish
* them from the square brackets used in numeric ranges or URL
* references.
*
Sequential numeric ranges consist of an opening square bracket, a
* numeric value to be used as the lower bound for the range, a colon, a
* second numeric value to be used as the upper bound for the range, an
* optional 'x' character followed by a numeric value to be
* used as the increment, an optional '%' character followed
* by a format string as allowed by the {@link java.text.DecimalFormat}
* class to define how the resulting value should be formatted, and a
* closing square bracket to indicate the end of the range.
*
Random numeric ranges consist of an opening square bracket, a
* numeric value to be used as the lower bound for the range, a dash, a
* second numeric value to be used as the upper bound for the range, an
* optional '%' character followed by a format string as
* allowed by the {@link java.text.DecimalFormat} class to define how the
* resulting value should be formatted, and a closing square bracket to
* indicate the end of the range.
*
Randomly character ranges consist of an opening square bracket, the
* word "random", a colon, the number of random characters to generate,
* another colon, the set of characters to include, and a closing square
* bracket. For example, "[random:4:0123456789abcdef]" will generate a
* string of four randomly selected characters from the set of hexadecimal
* digits. The final colon and character set may be omitted to use the
* set of lowercase alphabetic characters.
*
Strings read from a file specified by a given URL. That file may be
* contained on the local filesystem (using a URL like
* "file:///tmp/mydata.txt") or read from a remote server via HTTP (using
* a URL like "http://server.example.com/mydata.txt"). In either case,
* the provided URL must not contain a closing square bracket character.
* If this option is used, then that file must contain one value per line,
* and its contents will be read into memory and values from the file will
* be selected in a random order and used in place of the bracketed
* URL. Alternately, a local file may be read in sequential order by
* using "sequentialfile:" or "streamfile:" instead of "file:"; the former
* will load the entire file into memory while the latter will only hold
* a small amount of data in memory at any time.
*
Timestamps in a specified format. A pattern of just "[timestamp]" will
* be replaced with the current time, with millisecond precision, in the
* generalized time format (for example, "20180102030405.678Z"). A value
* A value of "[timestamp:format=XXX]" will be replaced with the current
* time in the specified format, where the format value can be one of
* "milliseconds" for the number of milliseconds since the epoch (January
* 1, 1970 at midnight UTC), "seconds" for the number of seconds since the
* epoch, or any value supported by Java's {@code SimpleDateFormat} class.
* A pattern of "[timestamp:min=XXX:max=XXX]" will be replaced with a
* randomly selected timestamp in generalized time format between the
* given minimum and maximum timestamps (inclusive), which must be in
* generalized time format. A pattern of
* "[timestamp:min=XXX:max=XXX:format=XXX]" will be replaced with a
* randomly selected timestamp in the specified format between the given
* minimum and maximum timestamps (where the minimum and maximum
* timestamp values must be in the generalized time format).
*
Randomly generated UUIDs (universally unique identifiers) as described
* in RFC 4122. These
* UUIDs may be generated using a pattern string of "[uuid]".
*
Back-references that will be replaced with the same value as the
* bracketed token in the specified position in the string. For example,
* a component of "[ref:1]" will be replaced with the same value as used
* in the first bracketed component of the value pattern. Back-references
* must only reference components that have been previously defined in the
* value pattern, and not those which appear after the reference.
*
*
* It must be possible to represent all of the numeric values used in sequential
* or random numeric ranges as {@code long} values. In a sequential numeric
* range, if the first value is larger than the second value, then values will
* be chosen in descending rather than ascending order (and if an increment is
* given, then it should be positive). In addition, once the end of a
* sequential range has been reached, then the value will wrap around to the
* beginning of that range.
*
* Examples of value pattern components include:
*
*
Hello -- The static text "Hello".
*
[[Hello]] -- The static text "[Hello]" (note
* that the double square brackets were replaced with single square
* brackets).
*
[0:1000] -- A sequential numeric range that will iterate
* in ascending sequential order from 0 to 1000. The 1002nd value that is
* requested will cause the value to be wrapped around to 0 again.
*
[1000:0] -- A sequential numeric range that will iterate
* in descending sequential order from 1000 to 0. The 1002nd value that is
* requested will cause the value to be wrapped around to 1000 again.
*
[0:1000x5%0000] -- A sequential numeric range that will
* iterate in ascending sequential order from 0 to 1000 in increments of
* five with all values represented as four-digit numbers padded with
* leading zeroes. For example, the first four values generated by this
* component will be "0000", "0005", "0010", and "0015".
*
[0-1000] -- A random numeric range that will choose values
* at random between 0 and 1000, inclusive.
*
[0-1000%0000] -- A random numeric range that will choose
* values at random between 0 and 1000, inclusive, and values will be
* padded with leading zeroes as necessary so that they are represented
* using four digits.
*
[random:5] -- Will generate a string of five randomly
* selected lowercase letters to be used in place of the bracketed
* range.
*
[random:4:0123456789abcdef] -- Will generate a string of
* four randomly selected hexadecimal digits to be used in place of the
* bracketed range.
*
[random:5:abcdefghijklmnopqrstuvwxyz] -- Will generate a
* string of five randomly selected lowercase letters to be used in place
* of the bracketed range.
*
[file:///tmp/mydata.txt] -- A URL reference that will
* cause randomly-selected lines from the specified local file to be used
* in place of the bracketed range. To make it clear that the file
* contents are randomly accessed, you may use {@code randomfile} in place
* of {@code file}. The entire file will be read into memory, so this may
* not be a suitable option for very large files.
*
[sequentialfile:///tmp/mydata.txt] -- A URL reference that
* will cause lines from the specified local file, selected in sequential
* order, to be used in place of the bracketed range. The entire file
* will be read into memory, so this may not be a suitable option for very
* large files.
*
[streamfile:///tmp/mydata.txt] -- A URL reference that
* will cause lines from the specified local file, selected in sequential
* order, to be used in place of the bracketed range. A background thread
* will be used to read data from the file and place it into a queue so
* that it is available quickly, but only a small amount of data will be
* held in memory at any time, so this is a suitable option for very
* large files.
*
[timestamp] -- The current time in generalized time
* format, with millisecond precision.
*
[timestamp:format=milliseconds] -- The current time
* expressed as the number of milliseconds since January 1, 1970 at
* midnight UTC (that is, the output of
* {@code System.currentTimeMillis()}.
*
[timestamp:format=seconds] -- The current time expressed
* as the number of seconds since January 1, 1970 at midnight UTC.
*
[timestamp:format=yyyy-MM-dd'T'HH:mm:ss.SSSZ] -- The
* current time expressed in the specified format string.
*
[timestamp:min=20180101000000.000Z:max=20181231235959.999Z:
* format=yyyyMMddHHmmss] -- A randomly selected timestamp
* sometime in the year 2018 in the specified format.
*
[http://server.example.com/tmp/mydata.txt] -- A URL
* reference that will cause randomly-selected lines from the specified
* remote HTTP-accessible file to be used in place of the bracketed
* range.
*
[uuid] -- Will cause a randomly generated UUID to be used
* in place of the bracketed range.
*
*
* Examples of full value pattern strings include:
*
*
dc=example,dc=com -- A value pattern containing only
* static text and no numeric components.
*
[1000:9999] -- A value pattern containing only a numeric
* component that will choose numbers in sequential order from 1000 to
* 9999.
*
(uid=user.[1-1000000]) -- A value pattern that combines
* the static text "(uid=user." with a value chosen randomly
* between one and one million, and another static text string of
* ")".
*
uid=user.[1-1000000],ou=org[1-10],dc=example,dc=com -- A
* value pattern containing two numeric components interspersed between
* three static text components.
*
uid=user.[1-1000000],ou=org[ref:1],dc=example,dc=com -- A
* value pattern in which the organization number will be the same as the
* randomly-selected user number.
*
*/
@NotMutable()
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class ValuePattern
implements Serializable
{
/**
* The URL to the publicly-accessible javadoc for this class, which provides
* a detailed overview of the supported value pattern syntax.
*/
@NotNull public static final String PUBLIC_JAVADOC_URL =
"https://docs.ldap.com/ldap-sdk/docs/javadoc/index.html?" +
"com/unboundid/util/ValuePattern.html";
/**
* The serial version UID for this serializable class.
*/
private static final long serialVersionUID = 4502778464751705304L;
// Indicates whether the provided value pattern includes one or more
// back-references.
private final boolean hasBackReference;
// The string that was originally used to create this value pattern.
@NotNull private final String pattern;
// The thread-local array list that will be used to hold values for
// back-references.
@NotNull private final ThreadLocal> refLists;
// The thread-local string builder that will be used to build values.
@NotNull private final ThreadLocal buffers;
// The value pattern components that will be used to generate values.
@NotNull private final ValuePatternComponent[] components;
/**
* Creates a new value pattern from the provided string.
*
* @param s The string representation of the value pattern to create. It
* must not be {@code null}.
*
* @throws ParseException If the provided string cannot be parsed as a valid
* value pattern string.
*/
public ValuePattern(@NotNull final String s)
throws ParseException
{
this(s, null);
}
/**
* Creates a new value pattern from the provided string.
*
* @param s The string representation of the value pattern to create. It
* must not be {@code null}.
* @param r The seed to use for the random number generator. It may be
* {@code null} if no seed is required.
*
* @throws ParseException If the provided string cannot be parsed as a valid
* value pattern string.
*/
public ValuePattern(@NotNull final String s, @Nullable final Long r)
throws ParseException
{
Validator.ensureNotNull(s);
pattern = s;
refLists = new ThreadLocal<>();
buffers = new ThreadLocal<>();
final AtomicBoolean hasRef = new AtomicBoolean(false);
final Random random;
if (r == null)
{
random = new Random();
}
else
{
random = new Random(r);
}
final ArrayList l = new ArrayList<>(3);
parse(s, 0, l, random, hasRef);
hasBackReference = hasRef.get();
if (hasBackReference)
{
int availableReferences = 0;
for (final ValuePatternComponent c : l)
{
if (c instanceof BackReferenceValuePatternComponent)
{
final BackReferenceValuePatternComponent brvpc =
(BackReferenceValuePatternComponent) c;
if (brvpc.getIndex() > availableReferences)
{
throw new ParseException(
ERR_REF_VALUE_PATTERN_INVALID_INDEX.get(brvpc.getIndex()), 0);
}
}
if (c.supportsBackReference())
{
availableReferences++;
}
}
}
components = new ValuePatternComponent[l.size()];
l.toArray(components);
}
/**
* Recursively parses the provided string into a list of value pattern
* components.
*
* @param s The string representation of the value pattern to create. It
* may be a portion of the entire value pattern string.
* @param o The offset of the first character of the provided string in
* the full value pattern string.
* @param l The list into which the parsed components should be added.
* @param r The random number generator to use to seed random number
* generators used by components.
* @param ref A value that may be updated if the pattern contains any
* back-references.
*
* @throws ParseException If the provided string cannot be parsed as a valid
* value pattern string.
*/
private static void parse(@NotNull final String s, final int o,
@NotNull final ArrayList l,
@NotNull final Random r,
@NotNull final AtomicBoolean ref)
throws ParseException
{
// Find the first occurrence of "[[". Parse the portion of the string
// before it, into the list, then add a string value pattern containing "[",
// then parse the portion of the string after it.
// First, parse out any occurrences of "[[" and replace them with string
// value pattern components containing only "[".
int pos = s.indexOf("[[");
if (pos >= 0)
{
if (pos > 0)
{
parse(s.substring(0, pos), o, l, r, ref);
}
l.add(new StringValuePatternComponent("["));
if (pos < (s.length() - 2))
{
parse(s.substring(pos+2), (o+pos+2), l, r, ref);
}
return;
}
// Find the first occurrence of "]]". Parse the portion of the string
// before it, into the list, then add a string value pattern containing "]",
// then parse the portion of the string after it.
pos = s.indexOf("]]");
if (pos >= 0)
{
if (pos > 0)
{
parse(s.substring(0, pos), o, l, r, ref);
}
l.add(new StringValuePatternComponent("]"));
if (pos < (s.length() - 2))
{
parse(s.substring(pos+2), (o+pos+2), l, r, ref);
}
return;
}
// Find the first occurrence of "[" and the corresponding "]". The part
// before that will be a string. Then parse out the numeric or URL
// component, and parse the rest of the string after the "]".
pos = s.indexOf('[');
if (pos >= 0)
{
final int closePos = s.indexOf(']');
if (closePos < 0)
{
throw new ParseException(
ERR_VALUE_PATTERN_UNMATCHED_OPEN.get(o+pos), (o+pos));
}
else if (closePos < pos)
{
throw new ParseException(
ERR_VALUE_PATTERN_UNMATCHED_CLOSE.get(o+closePos), (o+closePos));
}
if (pos > 0)
{
l.add(new StringValuePatternComponent(s.substring(0, pos)));
}
final String bracketedToken = s.substring(pos+1, closePos);
if (bracketedToken.startsWith("random:"))
{
l.add(new RandomCharactersValuePatternComponent(bracketedToken,
r.nextLong()));
}
else if (bracketedToken.startsWith("file:"))
{
final String path = bracketedToken.substring(5);
try
{
l.add(new FileValuePatternComponent(path, r.nextLong(), false));
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get(
path, StaticUtils.getExceptionMessage(ioe)), o+pos);
}
}
else if (bracketedToken.startsWith("randomfile:"))
{
final String path = bracketedToken.substring(11);
try
{
l.add(new FileValuePatternComponent(path, r.nextLong(), false));
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get(
path, StaticUtils.getExceptionMessage(ioe)), o+pos);
}
}
else if (bracketedToken.startsWith("sequentialfile:"))
{
final String path = bracketedToken.substring(15);
try
{
l.add(new FileValuePatternComponent(path, r.nextLong(), true));
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
throw new ParseException(ERR_FILE_VALUE_PATTERN_NOT_USABLE.get(
path, StaticUtils.getExceptionMessage(ioe)), o+pos);
}
}
else if (bracketedToken.startsWith("streamfile:"))
{
final String path = bracketedToken.substring(11);
try
{
l.add(new StreamFileValuePatternComponent(path));
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
throw new ParseException(ERR_STREAM_FILE_VALUE_PATTERN_NOT_USABLE.get(
path, StaticUtils.getExceptionMessage(ioe)), o+pos);
}
}
else if (bracketedToken.startsWith("http://"))
{
try
{
l.add(new HTTPValuePatternComponent(bracketedToken, r.nextLong()));
}
catch (final IOException ioe)
{
Debug.debugException(ioe);
throw new ParseException(ERR_HTTP_VALUE_PATTERN_NOT_USABLE.get(
bracketedToken, StaticUtils.getExceptionMessage(ioe)), o+pos);
}
}
else if (bracketedToken.startsWith("timestamp"))
{
l.add(new TimestampValuePatternComponent(bracketedToken,
r.nextLong()));
}
else if (bracketedToken.equals("uuid"))
{
l.add(new UUIDValuePatternComponent());
}
else if (bracketedToken.startsWith("ref:"))
{
ref.set(true);
final String valueStr = bracketedToken.substring(4);
try
{
final int index = Integer.parseInt(valueStr);
if (index == 0)
{
throw new ParseException(ERR_REF_VALUE_PATTERN_ZERO_INDEX.get(),
(o+pos+4));
}
else if (index < 0)
{
throw new ParseException(
ERR_REF_VALUE_PATTERN_NOT_VALID.get(valueStr), (o+pos+4));
}
else
{
l.add(new BackReferenceValuePatternComponent(index));
}
}
catch (final NumberFormatException nfe)
{
Debug.debugException(nfe);
throw new ParseException(
ERR_REF_VALUE_PATTERN_NOT_VALID.get(valueStr), (o+pos+4));
}
}
else
{
l.add(parseNumericComponent(s.substring(pos+1, closePos), (o+pos+1),
r));
}
if (closePos < (s.length() - 1))
{
parse(s.substring(closePos+1), (o+closePos+1), l, r, ref);
}
return;
}
// If there are any occurrences of "]" without a corresponding open, then
// that's invalid.
pos = s.indexOf(']');
if (pos >= 0)
{
throw new ParseException(
ERR_VALUE_PATTERN_UNMATCHED_CLOSE.get(o+pos), (o+pos));
}
// There are no brackets, so it's just a static string.
l.add(new StringValuePatternComponent(s));
}
/**
* Parses the specified portion of the provided string as either a
* sequential or random numeric value pattern component.
*
* @param s The string to parse, not including the square brackets.
* @param o The offset in the overall value pattern string at which the
* provided substring begins.
* @param r The random number generator to use to seed random number
* generators used by components.
*
* @return The parsed numeric value pattern component.
*
* @throws ParseException If the specified substring cannot be parsed as a
*
*/
@NotNull()
private static ValuePatternComponent parseNumericComponent(
@NotNull final String s,final int o, @NotNull final Random r)
throws ParseException
{
boolean delimiterFound = false;
boolean sequential = false;
int pos = 0;
long lowerBound = 0L;
lowerBoundLoop:
for ( ; pos < s.length(); pos++)
{
switch (s.charAt(pos))
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// These are all acceptable.
break;
case '-':
if (pos == 0)
{
// This indicates that the value is negative.
break;
}
else
{
// This indicates the end of the lower bound.
delimiterFound = true;
sequential = false;
try
{
lowerBound = Long.parseLong(s.substring(0, pos));
}
catch (final NumberFormatException nfe)
{
Debug.debugException(nfe);
throw new ParseException(
ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
Long.MAX_VALUE),
(o-1));
}
pos++;
break lowerBoundLoop;
}
case ':':
delimiterFound = true;
sequential = true;
if (pos == 0)
{
throw new ParseException(
ERR_VALUE_PATTERN_EMPTY_LOWER_BOUND.get(o-1), (o-1));
}
else
{
try
{
lowerBound = Long.parseLong(s.substring(0, pos));
}
catch (final NumberFormatException nfe)
{
Debug.debugException(nfe);
throw new ParseException(
ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
Long.MAX_VALUE),
(o-1));
}
}
pos++;
break lowerBoundLoop;
default:
throw new ParseException(
ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), (o+pos)),
(o+pos));
}
}
if (! delimiterFound)
{
throw new ParseException(ERR_VALUE_PATTERN_NO_DELIMITER.get(o-1), (o-1));
}
boolean hasIncrement = false;
int startPos = pos;
long upperBound = lowerBound;
long increment = 1L;
String formatString = null;
delimiterFound = false;
upperBoundLoop:
for ( ; pos < s.length(); pos++)
{
switch (s.charAt(pos))
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// These are all acceptable.
break;
case '-':
if (pos == startPos)
{
// This indicates that the value is negative.
break;
}
else
{
throw new ParseException(
ERR_VALUE_PATTERN_INVALID_CHARACTER.get('-', (o+pos)),
(o+pos));
}
case 'x':
delimiterFound = true;
hasIncrement = true;
if (pos == startPos)
{
throw new ParseException(
ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1));
}
else
{
try
{
upperBound = Long.parseLong(s.substring(startPos, pos));
}
catch (final NumberFormatException nfe)
{
Debug.debugException(nfe);
throw new ParseException(
ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
Long.MAX_VALUE),
(o-1));
}
}
pos++;
break upperBoundLoop;
case '%':
delimiterFound = true;
hasIncrement = false;
if (pos == startPos)
{
throw new ParseException(
ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1));
}
else
{
try
{
upperBound = Long.parseLong(s.substring(startPos, pos));
}
catch (final NumberFormatException nfe)
{
Debug.debugException(nfe);
throw new ParseException(
ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
Long.MAX_VALUE),
(o-1));
}
}
pos++;
break upperBoundLoop;
default:
throw new ParseException(
ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos), (o+pos)),
(o+pos));
}
}
if (! delimiterFound)
{
if (pos == startPos)
{
throw new ParseException(
ERR_VALUE_PATTERN_EMPTY_UPPER_BOUND.get(o-1), (o-1));
}
try
{
upperBound = Long.parseLong(s.substring(startPos, pos));
}
catch (final NumberFormatException nfe)
{
Debug.debugException(nfe);
throw new ParseException(
ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
Long.MAX_VALUE),
(o-1));
}
if (sequential)
{
return new SequentialValuePatternComponent(lowerBound, upperBound, 1,
null);
}
else
{
return new RandomValuePatternComponent(lowerBound, upperBound,
r.nextLong(), null);
}
}
if (hasIncrement)
{
delimiterFound = false;
startPos = pos;
incrementLoop:
for ( ; pos < s.length(); pos++)
{
switch (s.charAt(pos))
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// These are all acceptable.
break;
case '-':
if (pos == startPos)
{
// This indicates that the value is negative.
break;
}
else
{
throw new ParseException(
ERR_VALUE_PATTERN_INVALID_CHARACTER.get('-', (o+pos)),
(o+pos));
}
case '%':
delimiterFound = true;
if (pos == startPos)
{
throw new ParseException(
ERR_VALUE_PATTERN_EMPTY_INCREMENT.get(o-1), (o-1));
}
else if (pos == (s.length() - 1))
{
throw new ParseException(
ERR_VALUE_PATTERN_EMPTY_FORMAT.get(o-1), (o-1));
}
else
{
try
{
increment = Long.parseLong(s.substring(startPos, pos));
}
catch (final NumberFormatException nfe)
{
Debug.debugException(nfe);
throw new ParseException(
ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
Long.MAX_VALUE),
(o-1));
}
formatString = s.substring(pos+1);
}
break incrementLoop;
default:
throw new ParseException(
ERR_VALUE_PATTERN_INVALID_CHARACTER.get(s.charAt(pos),
(o+pos)),
(o+pos));
}
}
if (! delimiterFound)
{
if (pos == startPos)
{
throw new ParseException(
ERR_VALUE_PATTERN_EMPTY_INCREMENT.get(o-1), (o-1));
}
try
{
increment = Long.parseLong(s.substring(startPos, pos));
}
catch (final NumberFormatException nfe)
{
Debug.debugException(nfe);
throw new ParseException(
ERR_VALUE_PATTERN_VALUE_NOT_LONG.get((o-1), Long.MIN_VALUE,
Long.MAX_VALUE),
(o-1));
}
}
}
else
{
formatString = s.substring(pos);
if (formatString.length() == 0)
{
throw new ParseException(
ERR_VALUE_PATTERN_EMPTY_FORMAT.get(o-1), (o-1));
}
}
if (sequential)
{
return new SequentialValuePatternComponent(lowerBound, upperBound,
increment, formatString);
}
else
{
return new RandomValuePatternComponent(lowerBound, upperBound,
r.nextLong(), formatString);
}
}
/**
* Retrieves the next value generated from the value pattern.
*
* @return The next value generated from the value pattern.
*/
@NotNull()
public String nextValue()
{
StringBuilder buffer = buffers.get();
if (buffer == null)
{
buffer = new StringBuilder();
buffers.set(buffer);
}
else
{
buffer.setLength(0);
}
ArrayList refList = refLists.get();
if (hasBackReference)
{
if (refList == null)
{
refList = new ArrayList<>(10);
refLists.set(refList);
}
else
{
refList.clear();
}
}
for (final ValuePatternComponent c : components)
{
if (hasBackReference)
{
if (c instanceof BackReferenceValuePatternComponent)
{
final BackReferenceValuePatternComponent brvpc =
(BackReferenceValuePatternComponent) c;
final String value = refList.get(brvpc.getIndex() - 1);
buffer.append(value);
refList.add(value);
}
else if (c.supportsBackReference())
{
final int startPos = buffer.length();
c.append(buffer);
refList.add(buffer.substring(startPos));
}
else
{
c.append(buffer);
}
}
else
{
c.append(buffer);
}
}
return buffer.toString();
}
/**
* Retrieves a string representation of this value pattern, which will be the
* original pattern string used to create it.
*
* @return A string representation of this value pattern.
*/
@Override()
@NotNull()
public String toString()
{
return pattern;
}
}