View Javadoc

1   /*
2    * Copyright 2011 Király Attila
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.github.cage.token;
17  
18  import java.util.Collections;
19  import java.util.HashMap;
20  import java.util.Map;
21  import java.util.Random;
22  
23  import com.github.cage.IGenerator;
24  import com.github.cage.IGeneratorFactory;
25  
26  /**
27   * Factory that can generate {@link Character} generating {@link IGenerator}s.
28   * 
29   * @author akiraly
30   */
31  public class RandomCharacterGeneratorFactory implements
32  		IGeneratorFactory<Character> {
33  	/**
34  	 * English lower cased vowel character array.
35  	 */
36  	public static final char[] ENGLISH_VOWELS = "aeiou".toCharArray();
37  
38  	/**
39  	 * English lower cased consonant character array.
40  	 */
41  	public static final char[] ENGLISH_CONSONANTS = "bcdfghjklmnpqrstxyvz"
42  			.toCharArray();
43  
44  	/**
45  	 * Arabic numeral character array.
46  	 */
47  	public static final char[] ARABIC_NUMERALS = "0123456789".toCharArray();
48  
49  	/**
50  	 * Default character set for the default case. It contains the English lower
51  	 * cased letters.
52  	 */
53  	public static final char[] DEFAULT_DEFAULT_CHARACTER_SET = (new String(
54  			ENGLISH_VOWELS) + new String(ENGLISH_CONSONANTS)).toCharArray();
55  
56  	/**
57  	 * Special character sets for the default case.
58  	 */
59  	public static final Map<Character, char[]> DEFAULT_SPECIAL_CHARACTER_SETS = Collections
60  			.unmodifiableMap(createDefaultSpecialCharacterSets());
61  
62  	private final char[] defaultCharacterSet;
63  
64  	private final Map<Character, char[]> specialCharacterSets;
65  
66  	private final Random rnd;
67  
68  	/**
69  	 * Generates characters based on the settings of the factory. This is
70  	 * <b>not</b> thread safe!
71  	 */
72  	public class RandomCharacterGenerator implements IGenerator<Character> {
73  		private char[] currentCharacterSet = defaultCharacterSet;
74  
75  		public Character next() {
76  			char next = currentCharacterSet[rnd
77  					.nextInt(currentCharacterSet.length)];
78  
79  			if (specialCharacterSets != null) {
80  				char[] nextCharacterSet = specialCharacterSets.get(next);
81  				if (nextCharacterSet != null) {
82  					if (nextCharacterSet.length < 1) {
83  						throw new IllegalStateException(
84  								"specialCharacterSets should not hold an empty char[] value");
85  					}
86  					currentCharacterSet = nextCharacterSet;
87  				} else {
88  					currentCharacterSet = defaultCharacterSet;
89  				}
90  			}
91  
92  			return next;
93  		}
94  	}
95  
96  	/**
97  	 * Constructor.
98  	 */
99  	public RandomCharacterGeneratorFactory() {
100 		this(null);
101 	}
102 
103 	/**
104 	 * Constructor.
105 	 * 
106 	 * @param rnd
107 	 *            used to generate random numbers. Can be null.
108 	 */
109 	public RandomCharacterGeneratorFactory(Random rnd) {
110 		this(null, DEFAULT_SPECIAL_CHARACTER_SETS, rnd);
111 	}
112 
113 	/**
114 	 * Constructor.
115 	 * 
116 	 * @param defaultCharacterSet
117 	 *            used generally for character choosing, can be null.
118 	 * @param specialCharacterSets
119 	 *            map contains special cases. A (k, v) pair in this map means
120 	 *            that character k can only be immediately followed by a
121 	 *            character in v array. No value should be empty or null. The
122 	 *            map parameter itself can be null.
123 	 * @param rnd
124 	 *            used to generate random numbers. Can be null.
125 	 */
126 	public RandomCharacterGeneratorFactory(char[] defaultCharacterSet,
127 			Map<Character, char[]> specialCharacterSets, Random rnd) {
128 		this.defaultCharacterSet = defaultCharacterSet != null
129 				&& defaultCharacterSet.length > 0 ? defaultCharacterSet
130 				: DEFAULT_DEFAULT_CHARACTER_SET;
131 		this.specialCharacterSets = specialCharacterSets != null
132 				&& !specialCharacterSets.isEmpty() ? specialCharacterSets
133 				: null;
134 		this.rnd = rnd != null ? rnd : new Random();
135 	}
136 
137 	/**
138 	 * Helper function to build {@link #DEFAULT_SPECIAL_CHARACTER_SETS}. It
139 	 * contains some rules to avoid confusing character pairs. Also makes sure
140 	 * that vowels and consonants follow each other in an alternating fashion.
141 	 * 
142 	 * @return populated map
143 	 */
144 	protected static Map<Character, char[]> createDefaultSpecialCharacterSets() {
145 		Map<Character, char[]> m = new HashMap<Character, char[]>();
146 
147 		char[] con = ENGLISH_CONSONANTS;
148 		String conS = new String(con);
149 		char[] vow = ENGLISH_VOWELS;
150 		String vowS = new String(vow);
151 
152 		m.put('a', con);
153 		m.put('b', vow);
154 		m.put('c', vowS.replaceAll("o", "").toCharArray());
155 		m.put('d', vowS.replaceAll("o", "").toCharArray());
156 		m.put('e', con);
157 		m.put('f', vow);
158 		m.put('g', vow);
159 		m.put('h', vow);
160 		m.put('i', conS.replaceAll("j|l", "").toCharArray());
161 		m.put('j', vowS.replaceAll("i", "").toCharArray());
162 		m.put('k', vow);
163 		m.put('l', vowS.replaceAll("i|o", "").toCharArray());
164 		m.put('m', vow);
165 		m.put('n', vowS.replaceAll("u", "").toCharArray());
166 		m.put('o', conS.replaceAll("b|l|p", "").toCharArray());
167 		m.put('p', vow);
168 		m.put('q', vowS.replaceAll("o", "").toCharArray());
169 		m.put('r', vowS.replaceAll("u", "").toCharArray());
170 		m.put('s', vow);
171 		m.put('t', vow);
172 		m.put('u', con);
173 		m.put('v', vow);
174 		m.put('w', vow);
175 		m.put('x', vow);
176 		m.put('y', vow);
177 		m.put('z', vow);
178 
179 		return m;
180 	}
181 
182 	public IGenerator<Character> next() {
183 		return new RandomCharacterGenerator();
184 	}
185 
186 	/**
187 	 * @return default character set for character generation, not null
188 	 */
189 	public char[] getDefaultCharacterSet() {
190 		return defaultCharacterSet;
191 	}
192 
193 	/**
194 	 * @return map holding the special rules for each character, can be null
195 	 */
196 	public Map<Character, char[]> getSpecialCharacterSets() {
197 		return specialCharacterSets;
198 	}
199 }