001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.tools.makeldif; 018 019import static org.opends.messages.ToolMessages.*; 020import static org.opends.server.util.StaticUtils.*; 021 022import java.text.DecimalFormat; 023import java.util.List; 024import java.util.Random; 025 026import org.forgerock.i18n.LocalizableMessage; 027import org.opends.server.types.InitializationException; 028 029/** 030 * This class defines a tag that may be used to generate random values. It has 031 * a number of subtypes based on the type of information that should be 032 * generated, including: 033 * <UL> 034 * <LI>alpha:length</LI> 035 * <LI>alpha:minlength:maxlength</LI> 036 * <LI>numeric:length</LI> 037 * <LI>numeric:minvalue:maxvalue</LI> 038 * <LI>numeric:minvalue:maxvalue:format</LI> 039 * <LI>alphanumeric:length</LI> 040 * <LI>alphanumeric:minlength:maxlength</LI> 041 * <LI>chars:characters:length</LI> 042 * <LI>chars:characters:minlength:maxlength</LI> 043 * <LI>hex:length</LI> 044 * <LI>hex:minlength:maxlength</LI> 045 * <LI>base64:length</LI> 046 * <LI>base64:minlength:maxlength</LI> 047 * <LI>month</LI> 048 * <LI>month:maxlength</LI> 049 * <LI>telephone</LI> 050 * </UL> 051 */ 052public class RandomTag 053 extends Tag 054{ 055 /** 056 * The value that indicates that the value is to be generated from a fixed 057 * number of characters from a given character set. 058 */ 059 private static final int RANDOM_TYPE_CHARS_FIXED = 1; 060 /** 061 * The value that indicates that the value is to be generated from a variable 062 * number of characters from a given character set. 063 */ 064 private static final int RANDOM_TYPE_CHARS_VARIABLE = 2; 065 /** The value that indicates that the value should be a random number. */ 066 private static final int RANDOM_TYPE_NUMERIC = 3; 067 /** The value that indicates that the value should be a random month. */ 068 private static final int RANDOM_TYPE_MONTH = 4; 069 /** The value that indicates that the value should be a telephone number. */ 070 private static final int RANDOM_TYPE_TELEPHONE = 5; 071 072 /** The character set that will be used for alphabetic characters. */ 073 private static final char[] ALPHA_CHARS = 074 "abcdefghijklmnopqrstuvwxyz".toCharArray(); 075 /** The character set that will be used for numeric characters. */ 076 private static final char[] NUMERIC_CHARS = "01234567890".toCharArray(); 077 /** The character set that will be used for alphanumeric characters. */ 078 private static final char[] ALPHANUMERIC_CHARS = 079 "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray(); 080 /** The character set that will be used for hexadecimal characters. */ 081 private static final char[] HEX_CHARS = "01234567890abcdef".toCharArray(); 082 /** The character set that will be used for base64 characters. */ 083 private static final char[] BASE64_CHARS = 084 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + 085 "01234567890+/").toCharArray(); 086 087 /** The set of month names that will be used. */ 088 private static final String[] MONTHS = 089 { 090 "January", 091 "February", 092 "March", 093 "April", 094 "May", 095 "June", 096 "July", 097 "August", 098 "September", 099 "October", 100 "November", 101 "December" 102 }; 103 104 105 106 /** The character set that should be used to generate the values. */ 107 private char[] characterSet; 108 109 /** The decimal format used to format numeric values. */ 110 private DecimalFormat decimalFormat; 111 112 /** The number of characters between the minimum and maximum length (inclusive). */ 113 private int lengthRange; 114 /** The maximum number of characters to include in the value. */ 115 private int maxLength; 116 /** The minimum number of characters to include in the value. */ 117 private int minLength; 118 /** The type of random value that should be generated. */ 119 private int randomType; 120 121 /** The maximum numeric value that should be generated. */ 122 private long maxValue; 123 /** The minimum numeric value that should be generated. */ 124 private long minValue; 125 /** The number of values between the minimum and maximum value (inclusive). */ 126 private long valueRange; 127 128 /** The random number generator for this tag. */ 129 private Random random; 130 131 132 133 /** Creates a new instance of this random tag. */ 134 public RandomTag() 135 { 136 characterSet = null; 137 decimalFormat = null; 138 lengthRange = 1; 139 maxLength = 0; 140 minLength = 0; 141 randomType = 0; 142 maxValue = 0L; 143 minValue = 0L; 144 valueRange = 1L; 145 } 146 147 148 149 /** 150 * Retrieves the name for this tag. 151 * 152 * @return The name for this tag. 153 */ 154 @Override 155 public String getName() 156 { 157 return "Random"; 158 } 159 160 161 162 /** 163 * Indicates whether this tag is allowed for use in the extra lines for 164 * branches. 165 * 166 * @return <CODE>true</CODE> if this tag may be used in branch definitions, 167 * or <CODE>false</CODE> if not. 168 */ 169 @Override 170 public boolean allowedInBranch() 171 { 172 return true; 173 } 174 175 176 177 /** 178 * Performs any initialization for this tag that may be needed while parsing 179 * a branch definition. 180 * 181 * @param templateFile The template file in which this tag is used. 182 * @param branch The branch in which this tag is used. 183 * @param arguments The set of arguments provided for this tag. 184 * @param lineNumber The line number on which this tag appears in the 185 * template file. 186 * @param warnings A list into which any appropriate warning messages 187 * may be placed. 188 * 189 * @throws InitializationException If a problem occurs while initializing 190 * this tag. 191 */ 192 @Override 193 public void initializeForBranch(TemplateFile templateFile, Branch branch, 194 String[] arguments, int lineNumber, 195 List<LocalizableMessage> warnings) 196 throws InitializationException 197 { 198 initializeInternal(templateFile, arguments, lineNumber, warnings); 199 } 200 201 202 203 /** 204 * Performs any initialization for this tag that may be needed while parsing 205 * a template definition. 206 * 207 * @param templateFile The template file in which this tag is used. 208 * @param template The template in which this tag is used. 209 * @param arguments The set of arguments provided for this tag. 210 * @param lineNumber The line number on which this tag appears in the 211 * template file. 212 * @param warnings A list into which any appropriate warning messages 213 * may be placed. 214 * 215 * @throws InitializationException If a problem occurs while initializing 216 * this tag. 217 */ 218 @Override 219 public void initializeForTemplate(TemplateFile templateFile, 220 Template template, String[] arguments, 221 int lineNumber, List<LocalizableMessage> warnings) 222 throws InitializationException 223 { 224 initializeInternal(templateFile, arguments, lineNumber, warnings); 225 } 226 227 228 229 /** 230 * Performs any initialization for this tag that may be needed while parsing 231 * either a branch or template definition. 232 * 233 * @param templateFile The template file in which this tag is used. 234 * @param arguments The set of arguments provided for this tag. 235 * @param lineNumber The line number on which this tag appears in the 236 * template file. 237 * @param warnings A list into which any appropriate warning messages 238 * may be placed. 239 * 240 * @throws InitializationException If a problem occurs while initializing 241 * this tag. 242 */ 243 private void initializeInternal(TemplateFile templateFile, String[] arguments, 244 int lineNumber, List<LocalizableMessage> warnings) 245 throws InitializationException 246 { 247 random = templateFile.getRandom(); 248 249 // There must be at least one argument, to specify the type of random value 250 // to generate. 251 if (arguments == null || arguments.length == 0) 252 { 253 LocalizableMessage message = 254 ERR_MAKELDIF_TAG_NO_RANDOM_TYPE_ARGUMENT.get(lineNumber); 255 throw new InitializationException(message); 256 } 257 258 int numArgs = arguments.length; 259 String randomTypeString = toLowerCase(arguments[0]); 260 261 if (randomTypeString.equals("alpha")) 262 { 263 characterSet = ALPHA_CHARS; 264 decodeLength(arguments, 1, lineNumber, warnings); 265 } 266 else if (randomTypeString.equals("numeric")) 267 { 268 if (numArgs == 2) 269 { 270 randomType = RANDOM_TYPE_CHARS_FIXED; 271 characterSet = NUMERIC_CHARS; 272 273 try 274 { 275 minLength = Integer.parseInt(arguments[1]); 276 277 if (minLength < 0) 278 { 279 LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 280 minLength, 0, getName(), lineNumber); 281 throw new InitializationException(message); 282 } 283 else if (minLength == 0) 284 { 285 LocalizableMessage message = WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get( 286 lineNumber); 287 warnings.add(message); 288 } 289 } 290 catch (NumberFormatException nfe) 291 { 292 LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 293 arguments[1], getName(), lineNumber); 294 throw new InitializationException(message, nfe); 295 } 296 } 297 else if (numArgs == 3 || numArgs == 4) 298 { 299 randomType = RANDOM_TYPE_NUMERIC; 300 301 if (numArgs == 4) 302 { 303 try 304 { 305 decimalFormat = new DecimalFormat(arguments[3]); 306 } 307 catch (Exception e) 308 { 309 LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_FORMAT_STRING.get( 310 arguments[3], getName(), lineNumber); 311 throw new InitializationException(message, e); 312 } 313 } 314 else 315 { 316 decimalFormat = null; 317 } 318 319 try 320 { 321 minValue = Long.parseLong(arguments[1]); 322 } 323 catch (NumberFormatException nfe) 324 { 325 LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 326 arguments[1], getName(), lineNumber); 327 throw new InitializationException(message, nfe); 328 } 329 330 try 331 { 332 maxValue = Long.parseLong(arguments[2]); 333 if (maxValue < minValue) 334 { 335 LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 336 maxValue, minValue, getName(), lineNumber); 337 throw new InitializationException(message); 338 } 339 340 valueRange = maxValue - minValue + 1; 341 } 342 catch (NumberFormatException nfe) 343 { 344 LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 345 arguments[2], getName(), lineNumber); 346 throw new InitializationException(message, nfe); 347 } 348 } 349 else 350 { 351 LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get( 352 getName(), lineNumber, 2, 4, numArgs); 353 throw new InitializationException(message); 354 } 355 } 356 else if (randomTypeString.equals("alphanumeric")) 357 { 358 characterSet = ALPHANUMERIC_CHARS; 359 decodeLength(arguments, 1, lineNumber, warnings); 360 } 361 else if (randomTypeString.equals("chars")) 362 { 363 if (numArgs < 3 || numArgs > 4) 364 { 365 LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get( 366 getName(), lineNumber, 3, 4, numArgs); 367 throw new InitializationException(message); 368 } 369 370 characterSet = arguments[1].toCharArray(); 371 decodeLength(arguments, 2, lineNumber, warnings); 372 } 373 else if (randomTypeString.equals("hex")) 374 { 375 characterSet = HEX_CHARS; 376 decodeLength(arguments, 1, lineNumber, warnings); 377 } 378 else if (randomTypeString.equals("base64")) 379 { 380 characterSet = BASE64_CHARS; 381 decodeLength(arguments, 1, lineNumber, warnings); 382 } 383 else if (randomTypeString.equals("month")) 384 { 385 randomType = RANDOM_TYPE_MONTH; 386 387 if (numArgs == 1) 388 { 389 maxLength = 0; 390 } 391 else if (numArgs == 2) 392 { 393 try 394 { 395 maxLength = Integer.parseInt(arguments[1]); 396 if (maxLength <= 0) 397 { 398 LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 399 maxLength, 1, getName(), lineNumber); 400 throw new InitializationException(message); 401 } 402 } 403 catch (NumberFormatException nfe) 404 { 405 LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 406 arguments[1], getName(), lineNumber); 407 throw new InitializationException(message, nfe); 408 } 409 } 410 else 411 { 412 LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get( 413 getName(), lineNumber, 1, 2, numArgs); 414 throw new InitializationException(message); 415 } 416 } 417 else if (randomTypeString.equals("telephone")) 418 { 419 randomType = RANDOM_TYPE_TELEPHONE; 420 } 421 else 422 { 423 LocalizableMessage message = ERR_MAKELDIF_TAG_UNKNOWN_RANDOM_TYPE.get( 424 lineNumber, randomTypeString); 425 throw new InitializationException(message); 426 } 427 } 428 429 430 431 /** 432 * Decodes the information in the provided argument list as either a single 433 * integer specifying the number of characters, or two integers specifying the 434 * minimum and maximum number of characters. 435 * 436 * @param arguments The set of arguments to be processed. 437 * @param startPos The position at which the first legth value should 438 * appear in the argument list. 439 * @param lineNumber The line number on which the tag appears in the 440 * template file. 441 * @param warnings A list into which any appropriate warning messages may 442 * be placed. 443 */ 444 private void decodeLength(String[] arguments, int startPos, int lineNumber, 445 List<LocalizableMessage> warnings) 446 throws InitializationException 447 { 448 int numArgs = arguments.length - startPos + 1; 449 450 if (numArgs == 2) 451 { 452 // There is a fixed number of characters in the value. 453 randomType = RANDOM_TYPE_CHARS_FIXED; 454 455 try 456 { 457 minLength = Integer.parseInt(arguments[startPos]); 458 459 if (minLength < 0) 460 { 461 LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 462 minLength, 0, getName(), lineNumber); 463 throw new InitializationException(message); 464 } 465 else if (minLength == 0) 466 { 467 LocalizableMessage message = WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get( 468 lineNumber); 469 warnings.add(message); 470 } 471 } 472 catch (NumberFormatException nfe) 473 { 474 LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 475 arguments[startPos], getName(), lineNumber); 476 throw new InitializationException(message, nfe); 477 } 478 } 479 else if (numArgs == 3) 480 { 481 // There are minimum and maximum lengths. 482 randomType = RANDOM_TYPE_CHARS_VARIABLE; 483 484 try 485 { 486 minLength = Integer.parseInt(arguments[startPos]); 487 488 if (minLength < 0) 489 { 490 LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 491 minLength, 0, getName(), lineNumber); 492 throw new InitializationException(message); 493 } 494 } 495 catch (NumberFormatException nfe) 496 { 497 LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 498 arguments[startPos], getName(), lineNumber); 499 throw new InitializationException(message, nfe); 500 } 501 502 try 503 { 504 maxLength = Integer.parseInt(arguments[startPos+1]); 505 lengthRange = maxLength - minLength + 1; 506 507 if (maxLength < minLength) 508 { 509 LocalizableMessage message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 510 maxLength, minLength, getName(), lineNumber); 511 throw new InitializationException(message); 512 } 513 else if (maxLength == 0) 514 { 515 LocalizableMessage message = 516 WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get(lineNumber); 517 warnings.add(message); 518 } 519 } 520 catch (NumberFormatException nfe) 521 { 522 LocalizableMessage message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 523 arguments[startPos+1], getName(), lineNumber); 524 throw new InitializationException(message, nfe); 525 } 526 } 527 else 528 { 529 LocalizableMessage message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get( 530 getName(), lineNumber, startPos+1, startPos+2, numArgs); 531 throw new InitializationException(message); 532 } 533 } 534 535 536 537 /** 538 * Generates the content for this tag by appending it to the provided tag. 539 * 540 * @param templateEntry The entry for which this tag is being generated. 541 * @param templateValue The template value to which the generated content 542 * should be appended. 543 * 544 * @return The result of generating content for this tag. 545 */ 546 @Override 547 public TagResult generateValue(TemplateEntry templateEntry, 548 TemplateValue templateValue) 549 { 550 StringBuilder buffer = templateValue.getValue(); 551 552 switch (randomType) 553 { 554 case RANDOM_TYPE_CHARS_FIXED: 555 for (int i=0; i < minLength; i++) 556 { 557 buffer.append(characterSet[random.nextInt(characterSet.length)]); 558 } 559 break; 560 561 case RANDOM_TYPE_CHARS_VARIABLE: 562 int numChars = random.nextInt(lengthRange) + minLength; 563 for (int i=0; i < numChars; i++) 564 { 565 buffer.append(characterSet[random.nextInt(characterSet.length)]); 566 } 567 break; 568 569 case RANDOM_TYPE_NUMERIC: 570 long randomValue = 571 ((random.nextLong() & 0x7FFFFFFFFFFFFFFFL) % valueRange) + minValue; 572 if (decimalFormat == null) 573 { 574 buffer.append(randomValue); 575 } 576 else 577 { 578 buffer.append(decimalFormat.format(randomValue)); 579 } 580 break; 581 582 case RANDOM_TYPE_MONTH: 583 String month = MONTHS[random.nextInt(MONTHS.length)]; 584 if (maxLength == 0 || month.length() <= maxLength) 585 { 586 buffer.append(month); 587 } 588 else 589 { 590 buffer.append(month, 0, maxLength); 591 } 592 break; 593 594 case RANDOM_TYPE_TELEPHONE: 595 buffer.append("+1 "); 596 for (int i=0; i < 3; i++) 597 { 598 buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]); 599 } 600 buffer.append(' '); 601 for (int i=0; i < 3; i++) 602 { 603 buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]); 604 } 605 buffer.append(' '); 606 for (int i=0; i < 4; i++) 607 { 608 buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]); 609 } 610 break; 611 } 612 613 return TagResult.SUCCESS_RESULT; 614 } 615} 616