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