source: osm/applications/editors/josm/plugins/trustosm/src/tools/NameGenerator.java@ 24389

Last change on this file since 24389 was 24389, checked in by retsam, 14 years ago

josm plugin trustosm sourcecode and files

File size: 13.7 KB
Line 
1package tools;
2
3import java.io.BufferedReader;
4import java.io.FileReader;
5import java.io.IOException;
6import java.util.ArrayList;
7
8/**
9 * This class is released under GNU general public license
10 *
11 * Description: This class generates random names from syllables, and provides programmer a
12 * simple way to set a group of rules for generator to avoid unpronounceable and bizarre names.
13 *
14 * SYLLABLE FILE REQUIREMENTS/FORMAT:
15 * 1) all syllables are separated by line break.
16 * 2) Syllable should not contain or start with whitespace, as this character is ignored and only first part of the syllable is read.
17 * 3) + and - characters are used to set rules, and using them in other way, may result in unpredictable results.
18 * 4) Empty lines are ignored.
19 *
20 * SYLLABLE CLASSIFICATION:
21 * Name is usually composed from 3 different class of syllables, which include prefix, middle part and suffix.
22 * To declare syllable as a prefix in the file, insert "-" as a first character of the line.
23 * To declare syllable as a suffix in the file, insert "+" as a first character of the line.
24 * everything else is read as a middle part.
25 *
26 * NUMBER OF SYLLABLES:
27 * Names may have any positive number of syllables. In case of 2 syllables, name will be composed from prefix and suffix.
28 * In case of 1 syllable, name will be chosen from amongst the prefixes.
29 * In case of 3 and more syllables, name will begin with prefix, is filled with middle parts and ended with suffix.
30 *
31 * ASSIGNING RULES:
32 * I included a way to set 4 kind of rules for every syllable. To add rules to the syllables, write them right after the
33 * syllable and SEPARATE WITH WHITESPACE. (example: "aad +v -c"). The order of rules is not important.
34 *
35 * RULES:
36 * 1) +v means that next syllable must definitely start with a vocal.
37 * 2) +c means that next syllable must definitely start with a consonant.
38 * 3) -v means that this syllable can only be added to another syllable, that ends with a vocal.
39 * 4) -c means that this syllable can only be added to another syllable, that ends with a consonant.
40 * So, our example: "aad +v -c" means that "aad" can only be after consonant and next syllable must start with vocal.
41 * Beware of creating logical mistakes, like providing only syllables ending with consonants, but expecting only vocals, which will be detected
42 * and RuntimeException will be thrown.
43 *
44 * TO START:
45 * Create a new NameGenerator object, provide the syllable file, and create names using compose() method.
46 *
47 * @author Joonas Vali, August 2009.
48 *
49 */
50public class NameGenerator {
51 ArrayList<String> pre = new ArrayList<String>();
52 ArrayList<String> mid = new ArrayList<String>();
53 ArrayList<String> sur = new ArrayList<String>();
54
55 final private static char[] vocals = {'a', 'e', 'i', 'o', 'u', 'ä', 'ö', 'õ', 'ü', 'y'};
56 final private static char[] consonants = {'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y'};
57
58 private String fileName;
59
60 /**
61 * Create new random name generator object. refresh() is automatically called.
62 * @param fileName insert file name, where syllables are located
63 * @throws IOException
64 */
65 public NameGenerator(String fileName) throws IOException{
66 this.fileName = fileName;
67 refresh();
68 }
69
70 /**
71 * Change the file. refresh() is automatically called during the process.
72 * @param fileName insert the file name, where syllables are located.
73 * @throws IOException
74 */
75 public void changeFile(String fileName) throws IOException{
76 if(fileName == null) throw new IOException("File name cannot be null");
77 this.fileName = fileName;
78 refresh();
79 }
80
81 /**
82 * Refresh names from file. No need to call that method, if you are not changing the file during the operation of program, as this method
83 * is called every time file name is changed or new NameGenerator object created.
84 * @throws IOException
85 */
86 public void refresh() throws IOException{
87
88 FileReader input = null;
89 BufferedReader bufRead;
90 String line;
91
92 input = new FileReader(fileName);
93
94 bufRead = new BufferedReader(input);
95 line="";
96
97 while(line != null){
98 line = bufRead.readLine();
99 if(line != null && !line.equals("")){
100 if(line.charAt(0) == '-'){
101 pre.add(line.substring(1).toLowerCase());
102 }
103 else if(line.charAt(0) == '+'){
104 sur.add(line.substring(1).toLowerCase());
105 }
106 else{
107 mid.add(line.toLowerCase());
108 }
109 }
110 }
111 bufRead.close();
112 }
113
114 private String upper(String s){
115 return s.substring(0,1).toUpperCase().concat(s.substring(1));
116 }
117
118 private boolean containsConsFirst(ArrayList<String> array){
119 for(String s: array){
120 if(consonantFirst(s)) return true;
121 }
122 return false;
123 }
124
125 private boolean containsVocFirst(ArrayList<String> array){
126 for(String s: array){
127 if(vocalFirst(s)) return true;
128 }
129 return false;
130 }
131
132 private boolean allowCons(ArrayList<String> array){
133 for(String s: array){
134 if(hatesPreviousVocals(s) || hatesPreviousConsonants(s) == false) return true;
135 }
136 return false;
137 }
138
139 private boolean allowVocs(ArrayList<String> array){
140 for(String s: array){
141 if(hatesPreviousConsonants(s) || hatesPreviousVocals(s) == false) return true;
142 }
143 return false;
144 }
145
146 private boolean expectsVocal(String s){
147 if(s.substring(1).contains("+v")) return true;
148 else return false;
149 }
150 private boolean expectsConsonant(String s){
151 if(s.substring(1).contains("+c")) return true;
152 else return false;
153 }
154 private boolean hatesPreviousVocals(String s){
155 if(s.substring(1).contains("-c")) return true;
156 else return false;
157 }
158 private boolean hatesPreviousConsonants(String s){
159 if(s.substring(1).contains("-v")) return true;
160 else return false;
161 }
162
163 private String pureSyl(String s){
164 s = s.trim();
165 if(s.charAt(0) == '+' || s.charAt(0) == '-') s = s.substring(1);
166 return s.split(" ")[0];
167 }
168
169 private boolean vocalFirst(String s){
170 return (String.copyValueOf(vocals).contains(String.valueOf(s.charAt(0)).toLowerCase()));
171 }
172
173 private boolean consonantFirst(String s){
174 return (String.copyValueOf(consonants).contains(String.valueOf(s.charAt(0)).toLowerCase()));
175 }
176
177 private boolean vocalLast(String s){
178 return (String.copyValueOf(vocals).contains(String.valueOf(s.charAt(s.length()-1)).toLowerCase()));
179 }
180
181 private boolean consonantLast(String s){
182 return (String.copyValueOf(consonants).contains(String.valueOf(s.charAt(s.length()-1)).toLowerCase()));
183 }
184
185
186 /**
187 * Compose a new name.
188 * @param syls The number of syllables used in name.
189 * @return Returns composed name as a String
190 * @throws RuntimeException when logical mistakes are detected inside chosen file, and program is unable to complete the name.
191 */
192 public String compose(int syls){
193 if(syls > 2 && mid.size() == 0) throw new RuntimeException("You are trying to create a name with more than 3 parts, which requires middle parts, " +
194 "which you have none in the file "+fileName+". You should add some. Every word, which doesn't have + or - for a prefix is counted as a middle part.");
195 if(pre.size() == 0) throw new RuntimeException("You have no prefixes to start creating a name. add some and use \"-\" prefix, to identify it as a prefix for a name. (example: -asd)");
196 if(sur.size() == 0) throw new RuntimeException("You have no suffixes to end a name. add some and use \"+\" prefix, to identify it as a suffix for a name. (example: +asd)");
197 if(syls < 1) throw new RuntimeException("compose(int syls) can't have less than 1 syllable");
198 int expecting = 0; // 1 for vocal, 2 for consonant
199 int last = 0; // 1 for vocal, 2 for consonant
200 String name;
201 int a = (int)(Math.random() * pre.size());
202
203 if(vocalLast(pureSyl(pre.get(a)))) last = 1;
204 else last = 2;
205
206 if(syls > 2){
207 if(expectsVocal(pre.get(a))){
208 expecting = 1;
209 if(containsVocFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with vocal, " +
210 "but there is none. You should add one, or remove requirement for one.. ");
211 }
212 if(expectsConsonant(pre.get(a))){
213 expecting = 2;
214 if(containsConsFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with consonant, " +
215 "but there is none. You should add one, or remove requirement for one.. ");
216 }
217 }
218 else{
219 if(expectsVocal(pre.get(a))){
220 expecting = 1;
221 if(containsVocFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with vocal, " +
222 "but there is none. You should add one, or remove requirement for one.. ");
223 }
224 if(expectsConsonant(pre.get(a))){
225 expecting = 2;
226 if(containsConsFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with consonant, " +
227 "but there is none. You should add one, or remove requirement for one.. ");
228 }
229 }
230 if(vocalLast(pureSyl(pre.get(a))) && allowVocs(mid) == false) throw new RuntimeException("Expecting \"middle\" part that allows last character of prefix to be a vocal, " +
231 "but there is none. You should add one, or remove requirements that cannot be fulfilled.. the prefix used, was : \""+pre.get(a)+"\", which" +
232 "means there should be a part available, that has \"-v\" requirement or no requirements for previous syllables at all.");
233
234 if(consonantLast(pureSyl(pre.get(a))) && allowCons(mid) == false) throw new RuntimeException("Expecting \"middle\" part that allows last character of prefix to be a consonant, " +
235 "but there is none. You should add one, or remove requirements that cannot be fulfilled.. the prefix used, was : \""+pre.get(a)+"\", which" +
236 "means there should be a part available, that has \"-c\" requirement or no requirements for previous syllables at all.");
237
238 int b[] = new int[syls];
239 for(int i = 0; i<b.length-2; i++){
240
241 do{
242 b[i] = (int)(Math.random() * mid.size());
243 //System.out.println("exp " +expecting+" vocalF:"+vocalFirst(mid.get(b[i]))+" syl: "+mid.get(b[i]));
244 }
245 while(expecting == 1 && vocalFirst(pureSyl(mid.get(b[i]))) == false || expecting == 2 && consonantFirst(pureSyl(mid.get(b[i]))) == false
246 || last == 1 && hatesPreviousVocals(mid.get(b[i])) || last == 2 && hatesPreviousConsonants(mid.get(b[i])));
247
248 expecting = 0;
249 if(expectsVocal(mid.get(b[i]))){
250 expecting = 1;
251 if(i < b.length-3 && containsVocFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with vocal, " +
252 "but there is none. You should add one, or remove requirement for one.. ");
253 if(i == b.length-3 && containsVocFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with vocal, " +
254 "but there is none. You should add one, or remove requirement for one.. ");
255 }
256 if(expectsConsonant(mid.get(b[i]))){
257 expecting = 2;
258 if(i < b.length-3 && containsConsFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with consonant, " +
259 "but there is none. You should add one, or remove requirement for one.. ");
260 if(i == b.length-3 && containsConsFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with consonant, " +
261 "but there is none. You should add one, or remove requirement for one.. ");
262 }
263 if(vocalLast(pureSyl(mid.get(b[i]))) && allowVocs(mid) == false && syls > 3) throw new RuntimeException("Expecting \"middle\" part that allows last character of last syllable to be a vocal, " +
264 "but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
265 "means there should be a part available, that has \"-v\" requirement or no requirements for previous syllables at all.");
266
267 if(consonantLast(pureSyl(mid.get(b[i]))) && allowCons(mid) == false && syls > 3) throw new RuntimeException("Expecting \"middle\" part that allows last character of last syllable to be a consonant, " +
268 "but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
269 "means there should be a part available, that has \"-c\" requirement or no requirements for previous syllables at all.");
270 if(i == b.length-3){
271 if(vocalLast(pureSyl(mid.get(b[i]))) && allowVocs(sur) == false) throw new RuntimeException("Expecting \"suffix\" part that allows last character of last syllable to be a vocal, " +
272 "but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
273 "means there should be a suffix available, that has \"-v\" requirement or no requirements for previous syllables at all.");
274
275 if(consonantLast(pureSyl(mid.get(b[i]))) && allowCons(sur) == false) throw new RuntimeException("Expecting \"suffix\" part that allows last character of last syllable to be a consonant, " +
276 "but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
277 "means there should be a suffix available, that has \"-c\" requirement or no requirements for previous syllables at all.");
278 }
279 if(vocalLast(pureSyl(mid.get(b[i])))) last = 1;
280 else last = 2;
281 }
282
283 int c;
284 do{
285 c = (int)(Math.random() * sur.size());
286 }
287 while(expecting == 1 && vocalFirst(pureSyl(sur.get(c))) == false || expecting == 2 && consonantFirst(pureSyl(sur.get(c))) == false
288 || last == 1 && hatesPreviousVocals(sur.get(c)) || last == 2 && hatesPreviousConsonants(sur.get(c)));
289
290 name = upper(pureSyl(pre.get(a).toLowerCase()));
291 for(int i = 0; i<b.length-2; i++){
292 name = name.concat(pureSyl(mid.get(b[i]).toLowerCase()));
293 }
294 if(syls > 1)
295 name = name.concat(pureSyl(sur.get(c).toLowerCase()));
296 return name;
297 }
298}
Note: See TracBrowser for help on using the repository browser.