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

Last change on this file since 32533 was 32533, checked in by donvip, 9 years ago

checkstyle

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