/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2014-2015 ForgeRock AS
*/
package org.opends.server.tools.makeldif;
import org.forgerock.i18n.LocalizableMessage;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Random;
import org.opends.server.types.InitializationException;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.util.StaticUtils.*;
/**
* This class defines a tag that may be used to generate random values. It has
* a number of subtypes based on the type of information that should be
* generated, including:
*
* - alpha:length
* - alpha:minlength:maxlength
* - numeric:length
* - numeric:minvalue:maxvalue
* - numeric:minvalue:maxvalue:format
* - alphanumeric:length
* - alphanumeric:minlength:maxlength
* - chars:characters:length
* - chars:characters:minlength:maxlength
* - hex:length
* - hex:minlength:maxlength
* - base64:length
* - base64:minlength:maxlength
* - month
* - month:maxlength
* - telephone
*
*/
public class RandomTag
extends Tag
{
/**
* The value that indicates that the value is to be generated from a fixed
* number of characters from a given character set.
*/
public static final int RANDOM_TYPE_CHARS_FIXED = 1;
/**
* The value that indicates that the value is to be generated from a variable
* number of characters from a given character set.
*/
public static final int RANDOM_TYPE_CHARS_VARIABLE = 2;
/**
* The value that indicates that the value should be a random number.
*/
public static final int RANDOM_TYPE_NUMERIC = 3;
/**
* The value that indicates that the value should be a random month.
*/
public static final int RANDOM_TYPE_MONTH = 4;
/**
* The value that indicates that the value should be a telephone number.
*/
public static final int RANDOM_TYPE_TELEPHONE = 5;
/**
* The character set that will be used for alphabetic characters.
*/
public static final char[] ALPHA_CHARS =
"abcdefghijklmnopqrstuvwxyz".toCharArray();
/**
* The character set that will be used for numeric characters.
*/
public static final char[] NUMERIC_CHARS = "01234567890".toCharArray();
/**
* The character set that will be used for alphanumeric characters.
*/
public static final char[] ALPHANUMERIC_CHARS =
"abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
/**
* The character set that will be used for hexadecimal characters.
*/
public static final char[] HEX_CHARS = "01234567890abcdef".toCharArray();
/**
* The character set that will be used for base64 characters.
*/
public static final char[] BASE64_CHARS =
("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
"01234567890+/").toCharArray();
/**
* The set of month names that will be used.
*/
public static final String[] MONTHS =
{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
/** The character set that should be used to generate the values. */
private char[] characterSet;
/** The decimal format used to format numeric values. */
private DecimalFormat decimalFormat;
/**
* The number of characters between the minimum and maximum length
* (inclusive).
*/
private int lengthRange;
/** The maximum number of characters to include in the value. */
private int maxLength;
/** The minimum number of characters to include in the value. */
private int minLength;
/** The type of random value that should be generated. */
private int randomType;
/** The maximum numeric value that should be generated. */
private long maxValue;
/** The minimum numeric value that should be generated. */
private long minValue;
/** The number of values between the minimum and maximum value (inclusive). */
private long valueRange;
/** The random number generator for this tag. */
private Random random;
/**
* Creates a new instance of this random tag.
*/
public RandomTag()
{
characterSet = null;
decimalFormat = null;
lengthRange = 1;
maxLength = 0;
minLength = 0;
randomType = 0;
maxValue = 0L;
minValue = 0L;
valueRange = 1L;
}
/**
* Retrieves the name for this tag.
*
* @return The name for this tag.
*/
public String getName()
{
return "Random";
}
/**
* Indicates whether this tag is allowed for use in the extra lines for
* branches.
*
* @return true
if this tag may be used in branch definitions,
* or false
if not.
*/
public boolean allowedInBranch()
{
return true;
}
/**
* Performs any initialization for this tag that may be needed while parsing
* a branch definition.
*
* @param templateFile The template file in which this tag is used.
* @param branch The branch in which this tag is used.
* @param arguments The set of arguments provided for this tag.
* @param lineNumber The line number on which this tag appears in the
* template file.
* @param warnings A list into which any appropriate warning messages
* may be placed.
*
* @throws InitializationException If a problem occurs while initializing
* this tag.
*/
public void initializeForBranch(TemplateFile templateFile, Branch branch,
String[] arguments, int lineNumber,
List warnings)
throws InitializationException
{
initializeInternal(templateFile, arguments, lineNumber, warnings);
}
/**
* Performs any initialization for this tag that may be needed while parsing
* a template definition.
*
* @param templateFile The template file in which this tag is used.
* @param template The template in which this tag is used.
* @param arguments The set of arguments provided for this tag.
* @param lineNumber The line number on which this tag appears in the
* template file.
* @param warnings A list into which any appropriate warning messages
* may be placed.
*
* @throws InitializationException If a problem occurs while initializing
* this tag.
*/
public void initializeForTemplate(TemplateFile templateFile,
Template template, String[] arguments,
int lineNumber, List warnings)
throws InitializationException
{
initializeInternal(templateFile, arguments, lineNumber, warnings);
}
/**
* Performs any initialization for this tag that may be needed while parsing
* either a branch or template definition.
*
* @param templateFile The template file in which this tag is used.
* @param arguments The set of arguments provided for this tag.
* @param lineNumber The line number on which this tag appears in the
* template file.
* @param warnings A list into which any appropriate warning messages
* may be placed.
*
* @throws InitializationException If a problem occurs while initializing
* this tag.
*/
private void initializeInternal(TemplateFile templateFile, String[] arguments,
int lineNumber, List warnings)
throws InitializationException
{
random = templateFile.getRandom();
// There must be at least one argument, to specify the type of random value
// to generate.
if (arguments == null || arguments.length == 0)
{
LocalizableMessage message =
ERR_MAKELDIF_TAG_NO_RANDOM_TYPE_ARGUMENT.get(lineNumber);
throw new InitializationException(message);
}
int numArgs = arguments.length;
String randomTypeString = toLowerCase(arguments[0]);
if (randomTypeString.equals("alpha"))
{
characterSet = ALPHA_CHARS;
decodeLength(arguments, 1, lineNumber, warnings);
}
else if (randomTypeString.equals("numeric"))
{
if (numArgs == 2)
{
randomType = RANDOM_TYPE_CHARS_FIXED;
characterSet = NUMERIC_CHARS;
try
{
minLength = Integer.parseInt(arguments[1]);
if (minLength < 0)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get(
minLength, 0, getName(), lineNumber);
throw new InitializationException(message);
}
else if (minLength == 0)
{
LocalizableMessage message = WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get(
lineNumber);
warnings.add(message);
}
}
catch (NumberFormatException nfe)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get(
arguments[1], getName(), lineNumber);
throw new InitializationException(message, nfe);
}
}
else if (numArgs == 3 || numArgs == 4)
{
randomType = RANDOM_TYPE_NUMERIC;
if (numArgs == 4)
{
try
{
decimalFormat = new DecimalFormat(arguments[3]);
}
catch (Exception e)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_FORMAT_STRING.get(
arguments[3], getName(), lineNumber);
throw new InitializationException(message, e);
}
}
else
{
decimalFormat = null;
}
try
{
minValue = Long.parseLong(arguments[1]);
}
catch (NumberFormatException nfe)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get(
arguments[1], getName(), lineNumber);
throw new InitializationException(message, nfe);
}
try
{
maxValue = Long.parseLong(arguments[2]);
if (maxValue < minValue)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get(
maxValue, minValue, getName(), lineNumber);
throw new InitializationException(message);
}
valueRange = maxValue - minValue + 1;
}
catch (NumberFormatException nfe)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get(
arguments[2], getName(), lineNumber);
throw new InitializationException(message, nfe);
}
}
else
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get(
getName(), lineNumber, 2, 4, numArgs);
throw new InitializationException(message);
}
}
else if (randomTypeString.equals("alphanumeric"))
{
characterSet = ALPHANUMERIC_CHARS;
decodeLength(arguments, 1, lineNumber, warnings);
}
else if (randomTypeString.equals("chars"))
{
if (numArgs < 3 || numArgs > 4)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get(
getName(), lineNumber, 3, 4, numArgs);
throw new InitializationException(message);
}
characterSet = arguments[1].toCharArray();
decodeLength(arguments, 2, lineNumber, warnings);
}
else if (randomTypeString.equals("hex"))
{
characterSet = HEX_CHARS;
decodeLength(arguments, 1, lineNumber, warnings);
}
else if (randomTypeString.equals("base64"))
{
characterSet = BASE64_CHARS;
decodeLength(arguments, 1, lineNumber, warnings);
}
else if (randomTypeString.equals("month"))
{
randomType = RANDOM_TYPE_MONTH;
if (numArgs == 1)
{
maxLength = 0;
}
else if (numArgs == 2)
{
try
{
maxLength = Integer.parseInt(arguments[1]);
if (maxLength <= 0)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get(
maxLength, 1, getName(), lineNumber);
throw new InitializationException(message);
}
}
catch (NumberFormatException nfe)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get(
arguments[1], getName(), lineNumber);
throw new InitializationException(message, nfe);
}
}
else
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get(
getName(), lineNumber, 1, 2, numArgs);
throw new InitializationException(message);
}
}
else if (randomTypeString.equals("telephone"))
{
randomType = RANDOM_TYPE_TELEPHONE;
}
else
{
LocalizableMessage message = ERR_MAKELDIF_TAG_UNKNOWN_RANDOM_TYPE.get(
lineNumber, randomTypeString);
throw new InitializationException(message);
}
}
/**
* Decodes the information in the provided argument list as either a single
* integer specifying the number of characters, or two integers specifying the
* minimum and maximum number of characters.
*
* @param arguments The set of arguments to be processed.
* @param startPos The position at which the first legth value should
* appear in the argument list.
* @param lineNumber The line number on which the tag appears in the
* template file.
* @param warnings A list into which any appropriate warning messages may
* be placed.
*/
private void decodeLength(String[] arguments, int startPos, int lineNumber,
List warnings)
throws InitializationException
{
int numArgs = arguments.length - startPos + 1;
if (numArgs == 2)
{
// There is a fixed number of characters in the value.
randomType = RANDOM_TYPE_CHARS_FIXED;
try
{
minLength = Integer.parseInt(arguments[startPos]);
if (minLength < 0)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get(
minLength, 0, getName(), lineNumber);
throw new InitializationException(message);
}
else if (minLength == 0)
{
LocalizableMessage message = WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get(
lineNumber);
warnings.add(message);
}
}
catch (NumberFormatException nfe)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get(
arguments[startPos], getName(), lineNumber);
throw new InitializationException(message, nfe);
}
}
else if (numArgs == 3)
{
// There are minimum and maximum lengths.
randomType = RANDOM_TYPE_CHARS_VARIABLE;
try
{
minLength = Integer.parseInt(arguments[startPos]);
if (minLength < 0)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get(
minLength, 0, getName(), lineNumber);
throw new InitializationException(message);
}
}
catch (NumberFormatException nfe)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get(
arguments[startPos], getName(), lineNumber);
throw new InitializationException(message, nfe);
}
try
{
maxLength = Integer.parseInt(arguments[startPos+1]);
lengthRange = maxLength - minLength + 1;
if (maxLength < minLength)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get(
maxLength, minLength, getName(), lineNumber);
throw new InitializationException(message);
}
else if (maxLength == 0)
{
LocalizableMessage message =
WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get(lineNumber);
warnings.add(message);
}
}
catch (NumberFormatException nfe)
{
LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get(
arguments[startPos+1], getName(), lineNumber);
throw new InitializationException(message, nfe);
}
}
else
{
LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get(
getName(), lineNumber, startPos+1, startPos+2, numArgs);
throw new InitializationException(message);
}
}
/**
* Generates the content for this tag by appending it to the provided tag.
*
* @param templateEntry The entry for which this tag is being generated.
* @param templateValue The template value to which the generated content
* should be appended.
*
* @return The result of generating content for this tag.
*/
public TagResult generateValue(TemplateEntry templateEntry,
TemplateValue templateValue)
{
StringBuilder buffer = templateValue.getValue();
switch (randomType)
{
case RANDOM_TYPE_CHARS_FIXED:
for (int i=0; i < minLength; i++)
{
buffer.append(characterSet[random.nextInt(characterSet.length)]);
}
break;
case RANDOM_TYPE_CHARS_VARIABLE:
int numChars = random.nextInt(lengthRange) + minLength;
for (int i=0; i < numChars; i++)
{
buffer.append(characterSet[random.nextInt(characterSet.length)]);
}
break;
case RANDOM_TYPE_NUMERIC:
long randomValue =
((random.nextLong() & 0x7FFFFFFFFFFFFFFFL) % valueRange) + minValue;
if (decimalFormat == null)
{
buffer.append(randomValue);
}
else
{
buffer.append(decimalFormat.format(randomValue));
}
break;
case RANDOM_TYPE_MONTH:
String month = MONTHS[random.nextInt(MONTHS.length)];
if (maxLength == 0 || month.length() <= maxLength)
{
buffer.append(month);
}
else
{
buffer.append(month, 0, maxLength);
}
break;
case RANDOM_TYPE_TELEPHONE:
buffer.append("+1 ");
for (int i=0; i < 3; i++)
{
buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]);
}
buffer.append(' ');
for (int i=0; i < 3; i++)
{
buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]);
}
buffer.append(' ');
for (int i=0; i < 4; i++)
{
buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]);
}
break;
}
return TagResult.SUCCESS_RESULT;
}
}