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