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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.tools.makeldif; 028 import org.opends.messages.Message; 029 030 031 032 import java.text.DecimalFormat; 033 import java.util.List; 034 import java.util.Random; 035 036 import org.opends.server.types.InitializationException; 037 038 import static org.opends.messages.ToolMessages.*; 039 040 import 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 */ 067 public 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 // The number of characters between the minimum and maximum length 174 // (inclusive). 175 private int lengthRange; 176 177 // The maximum number of characters to include in the value. 178 private int maxLength; 179 180 // The minimum number of characters to include in the value. 181 private int minLength; 182 183 // The type of random value that should be generated. 184 private int randomType; 185 186 // The maximum numeric value that should be generated. 187 private long maxValue; 188 189 // The minimum numeric value that should be generated. 190 private long minValue; 191 192 // The number of values between the minimum and maximum value (inclusive). 193 private long valueRange; 194 195 // The random number generator for this tag. 196 private Random random; 197 198 199 200 /** 201 * Creates a new instance of this random tag. 202 */ 203 public RandomTag() 204 { 205 characterSet = null; 206 decimalFormat = null; 207 lengthRange = 1; 208 maxLength = 0; 209 minLength = 0; 210 randomType = 0; 211 maxValue = 0L; 212 minValue = 0L; 213 valueRange = 1L; 214 } 215 216 217 218 /** 219 * Retrieves the name for this tag. 220 * 221 * @return The name for this tag. 222 */ 223 public String getName() 224 { 225 return "Random"; 226 } 227 228 229 230 /** 231 * Indicates whether this tag is allowed for use in the extra lines for 232 * branches. 233 * 234 * @return <CODE>true</CODE> if this tag may be used in branch definitions, 235 * or <CODE>false</CODE> if not. 236 */ 237 public boolean allowedInBranch() 238 { 239 return true; 240 } 241 242 243 244 /** 245 * Performs any initialization for this tag that may be needed while parsing 246 * a branch definition. 247 * 248 * @param templateFile The template file in which this tag is used. 249 * @param branch The branch in which this tag is used. 250 * @param arguments The set of arguments provided for this tag. 251 * @param lineNumber The line number on which this tag appears in the 252 * template file. 253 * @param warnings A list into which any appropriate warning messages 254 * may be placed. 255 * 256 * @throws InitializationException If a problem occurs while initializing 257 * this tag. 258 */ 259 public void initializeForBranch(TemplateFile templateFile, Branch branch, 260 String[] arguments, int lineNumber, 261 List<Message> warnings) 262 throws InitializationException 263 { 264 initializeInternal(templateFile, arguments, lineNumber, warnings); 265 } 266 267 268 269 /** 270 * Performs any initialization for this tag that may be needed while parsing 271 * a template definition. 272 * 273 * @param templateFile The template file in which this tag is used. 274 * @param template The template in which this tag is used. 275 * @param arguments The set of arguments provided for this tag. 276 * @param lineNumber The line number on which this tag appears in the 277 * template file. 278 * @param warnings A list into which any appropriate warning messages 279 * may be placed. 280 * 281 * @throws InitializationException If a problem occurs while initializing 282 * this tag. 283 */ 284 public void initializeForTemplate(TemplateFile templateFile, 285 Template template, String[] arguments, 286 int lineNumber, List<Message> warnings) 287 throws InitializationException 288 { 289 initializeInternal(templateFile, arguments, lineNumber, warnings); 290 } 291 292 293 294 /** 295 * Performs any initialization for this tag that may be needed while parsing 296 * either a branch or template definition. 297 * 298 * @param templateFile The template file in which this tag is used. 299 * @param arguments The set of arguments provided for this tag. 300 * @param lineNumber The line number on which this tag appears in the 301 * template file. 302 * @param warnings A list into which any appropriate warning messages 303 * may be placed. 304 * 305 * @throws InitializationException If a problem occurs while initializing 306 * this tag. 307 */ 308 private void initializeInternal(TemplateFile templateFile, String[] arguments, 309 int lineNumber, List<Message> warnings) 310 throws InitializationException 311 { 312 random = templateFile.getRandom(); 313 314 // There must be at least one argument, to specify the type of random value 315 // to generate. 316 if ((arguments == null) || (arguments.length == 0)) 317 { 318 Message message = 319 ERR_MAKELDIF_TAG_NO_RANDOM_TYPE_ARGUMENT.get(lineNumber); 320 throw new InitializationException(message); 321 } 322 323 int numArgs = arguments.length; 324 String randomTypeString = toLowerCase(arguments[0]); 325 326 if (randomTypeString.equals("alpha")) 327 { 328 characterSet = ALPHA_CHARS; 329 decodeLength(arguments, 1, lineNumber, warnings); 330 } 331 else if (randomTypeString.equals("numeric")) 332 { 333 if (numArgs == 2) 334 { 335 randomType = RANDOM_TYPE_CHARS_FIXED; 336 characterSet = NUMERIC_CHARS; 337 338 try 339 { 340 minLength = Integer.parseInt(arguments[1]); 341 342 if (minLength < 0) 343 { 344 Message message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 345 minLength, 0, getName(), lineNumber); 346 throw new InitializationException(message); 347 } 348 else if (minLength == 0) 349 { 350 Message message = WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get( 351 lineNumber); 352 warnings.add(message); 353 } 354 } 355 catch (NumberFormatException nfe) 356 { 357 Message message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 358 arguments[1], getName(), lineNumber); 359 throw new InitializationException(message, nfe); 360 } 361 } 362 else if ((numArgs == 3) || (numArgs == 4)) 363 { 364 randomType = RANDOM_TYPE_NUMERIC; 365 366 if (numArgs == 4) 367 { 368 try 369 { 370 decimalFormat = new DecimalFormat(arguments[3]); 371 } 372 catch (Exception e) 373 { 374 Message message = ERR_MAKELDIF_TAG_INVALID_FORMAT_STRING.get( 375 arguments[3], getName(), lineNumber); 376 throw new InitializationException(message, e); 377 } 378 } 379 else 380 { 381 decimalFormat = null; 382 } 383 384 try 385 { 386 minValue = Long.parseLong(arguments[1]); 387 } 388 catch (NumberFormatException nfe) 389 { 390 Message message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 391 arguments[1], getName(), lineNumber); 392 throw new InitializationException(message, nfe); 393 } 394 395 try 396 { 397 maxValue = Long.parseLong(arguments[2]); 398 if (maxValue < minValue) 399 { 400 Message message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 401 maxValue, minValue, getName(), lineNumber); 402 throw new InitializationException(message); 403 } 404 405 valueRange = maxValue - minValue + 1; 406 } 407 catch (NumberFormatException nfe) 408 { 409 Message message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 410 arguments[2], getName(), lineNumber); 411 throw new InitializationException(message, nfe); 412 } 413 } 414 else 415 { 416 Message message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get( 417 getName(), lineNumber, 2, 4, numArgs); 418 throw new InitializationException(message); 419 } 420 } 421 else if (randomTypeString.equals("alphanumeric")) 422 { 423 characterSet = ALPHANUMERIC_CHARS; 424 decodeLength(arguments, 1, lineNumber, warnings); 425 } 426 else if (randomTypeString.equals("chars")) 427 { 428 if ((numArgs < 3) || (numArgs > 4)) 429 { 430 Message message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get( 431 getName(), lineNumber, 3, 4, numArgs); 432 throw new InitializationException(message); 433 } 434 435 characterSet = arguments[1].toCharArray(); 436 decodeLength(arguments, 2, lineNumber, warnings); 437 } 438 else if (randomTypeString.equals("hex")) 439 { 440 characterSet = HEX_CHARS; 441 decodeLength(arguments, 1, lineNumber, warnings); 442 } 443 else if (randomTypeString.equals("base64")) 444 { 445 characterSet = BASE64_CHARS; 446 decodeLength(arguments, 1, lineNumber, warnings); 447 } 448 else if (randomTypeString.equals("month")) 449 { 450 randomType = RANDOM_TYPE_MONTH; 451 452 if (numArgs == 1) 453 { 454 maxLength = 0; 455 } 456 else if (numArgs == 2) 457 { 458 try 459 { 460 maxLength = Integer.parseInt(arguments[1]); 461 if (maxLength <= 0) 462 { 463 Message message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 464 maxLength, 1, getName(), lineNumber); 465 throw new InitializationException(message); 466 } 467 } 468 catch (NumberFormatException nfe) 469 { 470 Message message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 471 arguments[1], getName(), lineNumber); 472 throw new InitializationException(message, nfe); 473 } 474 } 475 else 476 { 477 Message message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get( 478 getName(), lineNumber, 1, 2, numArgs); 479 throw new InitializationException(message); 480 } 481 } 482 else if (randomTypeString.equals("telephone")) 483 { 484 randomType = RANDOM_TYPE_TELEPHONE; 485 } 486 else 487 { 488 Message message = ERR_MAKELDIF_TAG_UNKNOWN_RANDOM_TYPE.get( 489 lineNumber, randomTypeString); 490 throw new InitializationException(message); 491 } 492 } 493 494 495 496 /** 497 * Decodes the information in the provided argument list as either a single 498 * integer specifying the number of characters, or two integers specifying the 499 * minimum and maximum number of characters. 500 * 501 * @param arguments The set of arguments to be processed. 502 * @param startPos The position at which the first legth value should 503 * appear in the argument list. 504 * @param lineNumber The line number on which the tag appears in the 505 * template file. 506 * @param warnings A list into which any appropriate warning messages may 507 * be placed. 508 */ 509 private void decodeLength(String[] arguments, int startPos, int lineNumber, 510 List<Message> warnings) 511 throws InitializationException 512 { 513 int numArgs = arguments.length - startPos + 1; 514 515 if (numArgs == 2) 516 { 517 // There is a fixed number of characters in the value. 518 randomType = RANDOM_TYPE_CHARS_FIXED; 519 520 try 521 { 522 minLength = Integer.parseInt(arguments[startPos]); 523 524 if (minLength < 0) 525 { 526 Message message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 527 minLength, 0, getName(), lineNumber); 528 throw new InitializationException(message); 529 } 530 else if (minLength == 0) 531 { 532 Message message = WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get( 533 lineNumber); 534 warnings.add(message); 535 } 536 } 537 catch (NumberFormatException nfe) 538 { 539 Message message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 540 arguments[startPos], getName(), lineNumber); 541 throw new InitializationException(message, nfe); 542 } 543 } 544 else if (numArgs == 3) 545 { 546 // There are minimum and maximum lengths. 547 randomType = RANDOM_TYPE_CHARS_VARIABLE; 548 549 try 550 { 551 minLength = Integer.parseInt(arguments[startPos]); 552 553 if (minLength < 0) 554 { 555 Message message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 556 minLength, 0, getName(), lineNumber); 557 throw new InitializationException(message); 558 } 559 } 560 catch (NumberFormatException nfe) 561 { 562 Message message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 563 arguments[startPos], getName(), lineNumber); 564 throw new InitializationException(message, nfe); 565 } 566 567 try 568 { 569 maxLength = Integer.parseInt(arguments[startPos+1]); 570 lengthRange = maxLength - minLength + 1; 571 572 if (maxLength < minLength) 573 { 574 Message message = ERR_MAKELDIF_TAG_INTEGER_BELOW_LOWER_BOUND.get( 575 maxLength, minLength, getName(), lineNumber); 576 throw new InitializationException(message); 577 } 578 else if (maxLength == 0) 579 { 580 Message message = 581 WARN_MAKELDIF_TAG_WARNING_EMPTY_VALUE.get(lineNumber); 582 warnings.add(message); 583 } 584 } 585 catch (NumberFormatException nfe) 586 { 587 Message message = ERR_MAKELDIF_TAG_CANNOT_PARSE_AS_INTEGER.get( 588 arguments[startPos+1], getName(), lineNumber); 589 throw new InitializationException(message, nfe); 590 } 591 } 592 else 593 { 594 Message message = ERR_MAKELDIF_TAG_INVALID_ARGUMENT_RANGE_COUNT.get( 595 getName(), lineNumber, startPos+1, startPos+2, numArgs); 596 throw new InitializationException(message); 597 } 598 } 599 600 601 602 /** 603 * Generates the content for this tag by appending it to the provided tag. 604 * 605 * @param templateEntry The entry for which this tag is being generated. 606 * @param templateValue The template value to which the generated content 607 * should be appended. 608 * 609 * @return The result of generating content for this tag. 610 */ 611 public TagResult generateValue(TemplateEntry templateEntry, 612 TemplateValue templateValue) 613 { 614 StringBuilder buffer = templateValue.getValue(); 615 616 switch (randomType) 617 { 618 case RANDOM_TYPE_CHARS_FIXED: 619 for (int i=0; i < minLength; i++) 620 { 621 buffer.append(characterSet[random.nextInt(characterSet.length)]); 622 } 623 break; 624 625 case RANDOM_TYPE_CHARS_VARIABLE: 626 int numChars = random.nextInt(lengthRange) + minLength; 627 for (int i=0; i < numChars; i++) 628 { 629 buffer.append(characterSet[random.nextInt(characterSet.length)]); 630 } 631 break; 632 633 case RANDOM_TYPE_NUMERIC: 634 long randomValue = 635 ((random.nextLong() & 0x7FFFFFFFFFFFFFFFL) % valueRange) + minValue; 636 if (decimalFormat == null) 637 { 638 buffer.append(randomValue); 639 } 640 else 641 { 642 buffer.append(decimalFormat.format(randomValue)); 643 } 644 break; 645 646 case RANDOM_TYPE_MONTH: 647 String month = MONTHS[random.nextInt(MONTHS.length)]; 648 if ((maxLength == 0) || (month.length() <= maxLength)) 649 { 650 buffer.append(month); 651 } 652 else 653 { 654 buffer.append(month.substring(0, maxLength)); 655 } 656 break; 657 658 case RANDOM_TYPE_TELEPHONE: 659 buffer.append("+1 "); 660 for (int i=0; i < 3; i++) 661 { 662 buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]); 663 } 664 buffer.append(' '); 665 for (int i=0; i < 3; i++) 666 { 667 buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]); 668 } 669 buffer.append(' '); 670 for (int i=0; i < 4; i++) 671 { 672 buffer.append(NUMERIC_CHARS[random.nextInt(NUMERIC_CHARS.length)]); 673 } 674 break; 675 } 676 677 return TagResult.SUCCESS_RESULT; 678 } 679 } 680