View Javadoc

1   /*
2    * ====================
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4    * 
5    * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.     
6    * 
7    * The contents of this file are subject to the terms of the Common Development 
8    * and Distribution License("CDDL") (the "License").  You may not use this file 
9    * except in compliance with the License.
10   * 
11   * You can obtain a copy of the License at 
12   * http://IdentityConnectors.dev.java.net/legal/license.txt
13   * See the License for the specific language governing permissions and limitations 
14   * under the License. 
15   * 
16   * When distributing the Covered Code, include this CDDL Header Notice in each file
17   * and include the License file at identityconnectors/legal/license.txt.
18   * If applicable, add the following below this CDDL Header, with the fields 
19   * enclosed by brackets [] replaced by your own identifying information: 
20   * "Portions Copyrighted [year] [name of copyright owner]"
21   * ====================
22   */
23  package org.identityconnectors.contract.data;
24  
25  import java.lang.reflect.Constructor;
26  import java.util.HashMap;
27  import java.util.Map;
28  import java.util.Random;
29  import java.util.Set;
30  import java.util.TreeSet;
31  
32  /**
33   * <p>
34   * Random generator uses a <strong>pattern</strong> to generate a random
35   * sequence based on given pattern.
36   * </p>
37   * <p>
38   * the supported characters are (can appear in pattern string):
39   * </p>
40   * <ul>
41   * <li># - numeric</li>
42   * <li>a - lowercase letter</li>
43   * <li>A - uppercase letter</li>
44   * <li>? - lowercase and uppercase letter</li>
45   * <li>. - any character</li>
46   * </ul>
47   * <p>
48   * Any other character inside the pattern is directly printed to the output.
49   * </p>
50   * <p>
51   * Backslash is used to escape any character. For instance pattern "###\\.##"
52   * prints a floating point random number
53   * </p>
54   * 
55   * <p>
56   * org.identityconnectors.contract.data.macro.RandomMacro -- written by Dan
57   * Vernon, the original source of random generating functionality.
58   * </p>
59   * <p>
60   * Note: This is just a helper class. Users will directly use methods get() and
61   * random() of class {@link org.identityconnectors.contract.data.groovy.Lazy}.
62   * </p>
63   * 
64   * @author David Adam, Zdenek Louzensky
65   * 
66   */
67  public class RandomGenerator {
68  
69      private static Random rnd;
70      static {
71          rnd = new Random(System.currentTimeMillis());
72      }
73  
74      /**
75       * generate a random string based on given pattern
76       * @param pattern see the definition in class header
77       */
78      public static String generate(String pattern) {
79  
80          return createRandomString(pattern, getDefaultCharacterSetMap());
81      }
82  
83      /**
84       * <p>
85       * generates a random string based on given pattern, finally tries to call
86       * constructor on the given class accepting a string argument.
87       * </p>
88       * 
89       * @param pattern
90       *            see the definition in class header
91       * @return object initialized with random string
92       */
93      public static Object generate(String pattern, Class<?> clazz) {
94          String generatedStr = createRandomString(pattern,
95                  getDefaultCharacterSetMap());
96  
97          // resolve Character and ByteArray types:
98          if (clazz.isInstance(new byte[0])) {
99              return generatedStr.getBytes();
100         }
101         if (Character.class.isAssignableFrom(clazz)) {
102             char[] arr = generatedStr.toCharArray();
103             return arr[0];
104         }
105 
106         // Among others, this handles the conversion to a GuardedString,
107         // whose constructor takes a char[].
108         try {
109             Constructor<?> constructor = clazz.getConstructor(char[].class);
110             return constructor.newInstance(generatedStr.toCharArray());
111         } catch (Exception e) {
112             // OK
113         }
114 
115         try {
116             Constructor<?> constructor = clazz.getConstructor(String.class);
117             return constructor.newInstance(generatedStr);
118         } catch (Exception e) {
119             // ok
120         }
121         return null;
122     }
123 
124     /**
125      * Creates characters map
126      * 
127      * @return
128      */
129     private static Map<Character, Set<Character>> getDefaultCharacterSetMap() {
130         Map<Character, Set<Character>> characterSetMap = new HashMap<Character, Set<Character>>();
131 
132         // these are the Sets used by the map. For any character
133         // in the macro, a get is done on the map with that character
134         // as the key, and the Set returned represents the list of
135         // characters to pick from randomly
136         Set<Character> alpha_lower = new TreeSet<Character>();
137         Set<Character> alpha_upper = new TreeSet<Character>();
138         Set<Character> alpha_mixed = new TreeSet<Character>();
139         Set<Character> alpha_numeric = new TreeSet<Character>();
140         Set<Character> numeric = new TreeSet<Character>();
141 
142         // all lower case letters
143         addRange(alpha_lower, 'a', 'z');
144         // all upper case letters
145         addRange(alpha_upper, 'A', 'Z');
146         // all numbers
147         addRange(numeric, '0', '9');
148         // all lower case, upper case, and numbers
149         alpha_numeric.addAll(alpha_lower);
150         alpha_numeric.addAll(alpha_upper);
151         alpha_numeric.addAll(numeric);
152         // all lower case and upper case
153         alpha_mixed.addAll(alpha_lower);
154         alpha_mixed.addAll(alpha_upper);
155 
156         // setup the mappings
157         characterSetMap.put(new Character('#'), numeric);
158         characterSetMap.put(new Character('a'), alpha_lower);
159         characterSetMap.put(new Character('A'), alpha_upper);
160         characterSetMap.put(new Character('?'), alpha_mixed);
161         characterSetMap.put(new Character('.'), alpha_numeric);
162 
163         return characterSetMap;
164     }
165 
166     /**
167      * Fills the Set with Characters
168      * 
169      * @param s
170      *            {@link Set} to be filled
171      * @param min
172      * @param max
173      */
174     private static void addRange(Set<Character> s, char min, char max) {
175         if (max < min) {
176             char temp = min;
177             min = max;
178             max = temp;
179         }
180 
181         for (char i = min; i <= max; i++) {
182             s.add(new Character(i));
183         }
184     }
185 
186     /**
187      * Gets random character from the set
188      * 
189      * @param rnd
190      * @param validChars
191      * @return
192      */
193     private static char randomCharacter(Random rnd, Set<Character> validChars) {
194         int patternRange = validChars.size();
195         int next = 0;
196         synchronized (RandomGenerator.class) {
197             next = rnd.nextInt(patternRange);
198         }
199         Character[] charArray = validChars.toArray(new Character[0]);
200         Character charValue = charArray[next];
201         return charValue.charValue();
202     }
203 
204     /**
205      * Generates random character
206      * 
207      * @param pattern
208      * @param characterSetMap
209      * @return
210      */
211     private static String createRandomString(String pattern,
212             Map<Character, Set<Character>> characterSetMap) {
213         StringBuffer replacement = new StringBuffer();
214         for (int i = 0; i < pattern.length(); i++) {
215             Set<Character> characterSet = characterSetMap.get(new Character(
216                     pattern.charAt(i)));
217             if (pattern.charAt(i) == '\\') {
218                 // do escape, and print the next character
219                 i++;
220                 if (i < pattern.length()) {
221                     replacement.append(pattern.charAt(i));
222                 }
223                 continue;
224             }
225             if (characterSet == null) {
226                 replacement.append(pattern.charAt(i));
227             } else {
228                 replacement.append(randomCharacter(rnd, characterSet));
229             }
230         }
231         return replacement.toString();
232     }
233 }