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