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 }