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