KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > ext > KeywordMatchGenerator


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.editor.ext;
21
22 import java.util.Arrays JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.StringTokenizer JavaDoc;
26 import java.util.NoSuchElementException JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.File JavaDoc;
29 import java.io.Reader JavaDoc;
30 import java.io.FileReader JavaDoc;
31
32 /**
33 * Generator of code used for matching the keywords or more generally some
34 * group of words.
35 *
36 * @author Miloslav Metelka
37 * @version 1.00
38 */

39
40 public class KeywordMatchGenerator {
41
42     private static final String JavaDoc USAGE
43     = "Usage: java org.netbeans.editor.ext.KeywordMatchGenerator [options]" // NOI18N
44
+ " keyword-file [match-function-name]\n\n" // NOI18N
45
+ "Options:\n" // NOI18N
46
+ " -i Ignore case in matching\n" // NOI18N
47
+ " -s Input is in 'input' String or StringBuffer instead of char buffer\n" // NOI18N
48
+ "\nGenerator of method that matches" // NOI18N
49
+ " the keywords provided in the file.\n" // NOI18N
50
+ "Keywords in the file must be separated by spaces or new-lines" // NOI18N
51
+ " and they don't need to be sorted.\n"; // NOI18N
52

53     private static final String JavaDoc UNKNOWN_OPTION = " is unknown option.\n"; // NOI18N
54

55     public static final String JavaDoc IGNORE_CASE = "-i"; // NOI18N
56

57     public static final String JavaDoc USE_STRING = "-s"; // NOI18N
58

59     private static final String JavaDoc DEFAULT_METHOD_NAME = "match"; // NOI18N
60

61     private static final String JavaDoc[] OPTION_LIST = { IGNORE_CASE, USE_STRING };
62
63     /** The list of keywords */
64     private String JavaDoc kwds[];
65
66     /** Maximum length of keyword */
67     private int maxKwdLen;
68
69     /** Options */
70     private HashMap JavaDoc options = new HashMap JavaDoc();
71
72     private HashMap JavaDoc kwdConstants = new HashMap JavaDoc();
73
74     /** Provide indentation (default 2 spaces) */
75     private String JavaDoc indent(int cnt) {
76         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
77
78         while(cnt-- > 0) {
79             sb.append(" "); // NOI18N
80
}
81         return sb.toString();
82     }
83
84     protected void initScan(String JavaDoc methodName) {
85
86         if (methodName == null) {
87             methodName = DEFAULT_METHOD_NAME;
88         }
89
90         // write keyword constants table
91
appendString("\n"); // NOI18N
92
for (int i = 0; i < kwds.length; i++) {
93             appendString(indent(1) + "public static final int " + kwdConstants.get(kwds[i]) // NOI18N
94
+ " = " + i + ";\n"); // NOI18N
95
}
96         appendString("\n"); // NOI18N
97

98         // write method header
99
appendString(indent(1) + "public static int "); // NOI18N
100
appendString(methodName);
101         if (options.get(USE_STRING) != null) {
102             appendString("(String buffer, int offset, int len) {\n"); // NOI18N
103
} else {
104             appendString("(char[] buffer, int offset, int len) {\n"); // NOI18N
105
}
106         appendString(indent(2) + "if (len > " + maxKwdLen + ")\n"); // NOI18N
107
appendString(indent(3) + "return -1;\n"); // NOI18N
108
}
109
110     public void scan() {
111         scan(0, kwds.length, 0, 2, 0);
112     }
113
114     protected void finishScan() {
115         appendString(indent(1) + "}\n\n"); // NOI18N
116
}
117
118     public void addOption(String JavaDoc option) {
119         options.put(option, option);
120     }
121
122     protected String JavaDoc getKwdConstantPrefix() {
123         return ""; // "KWD_"; // NOI18N
124
}
125
126     protected String JavaDoc getKwdConstant(String JavaDoc kwd) {
127         return (String JavaDoc)kwdConstants.get(kwd);
128     }
129
130     protected boolean upperCaseKeyConstants() {
131         return true;
132     }
133
134     /** Parse the keywords from a string */
135     private void parseKeywords(String JavaDoc s) {
136         ArrayList JavaDoc keyList = new ArrayList JavaDoc();
137         StringTokenizer JavaDoc strTok = new StringTokenizer JavaDoc(s);
138
139         try {
140             while(true) {
141                 String JavaDoc key = strTok.nextToken();
142                 int keyLen = key.length();
143                 maxKwdLen = Math.max(maxKwdLen, keyLen);
144                 keyList.add(key);
145                 kwdConstants.put(key, getKwdConstantPrefix()
146                                  + (upperCaseKeyConstants() ? key.toUpperCase() : key));
147             }
148         } catch(NoSuchElementException JavaDoc e) {
149             // no more elements
150
}
151
152         kwds = new String JavaDoc[keyList.size()];
153         keyList.toArray(kwds);
154         Arrays.sort(kwds);
155     }
156
157     protected String JavaDoc getCurrentChar() {
158         boolean useString = (options.get(USE_STRING) != null);
159         boolean ignoreCase = (options.get(IGNORE_CASE) != null);
160
161         if(useString) {
162             return ignoreCase ? "Character.toLowerCase(buffer.charAt(offset++))" // NOI18N
163
: "buffer.charAt(offset++)"; // NOI18N
164
} else {
165             return ignoreCase ? "Character.toLowerCase(buffer[offset++])" // NOI18N
166
: "buffer[offset++]"; // NOI18N
167
}
168     }
169
170     private void appendCheckedReturn(String JavaDoc kwd, int offset, int indent) {
171         appendString(indent(indent) + "return (len == " // NOI18N
172
+ kwd.length());
173
174         int kwdLenM1 = kwd.length() - 1;
175         for(int k = offset; k <= kwdLenM1; k++) {
176             appendString("\n" + indent(indent + 1) + "&& "); // NOI18N
177
appendString(getCurrentChar() + " == '" + kwd.charAt(k) + "'"); // NOI18N
178
}
179
180         appendString(")\n" + indent(indent + 2) + "? " + getKwdConstant(kwd) + " : -1;\n"); // NOI18N
181
}
182
183     protected void appendString(String JavaDoc s) {
184         System.out.print(s);
185     }
186
187     /** Scan the keywords and generate the output. This method is initially
188     * called with the full range of keywords and offset equal to zero.
189     * It recursively calls itself to scan the subgroups.
190     * @param indFrom index in kwds[] where the subgroup of keywords starts
191     * @pararm indTo index in kwds[] where the subgroup of keywords ends
192     * @param offset current horizontal offset. It's incremented as the subgroups
193     * are recognized. All the characters prior to offset index are the same
194     * in all keywords in the group.
195     */

196     private void scan(int indFrom, int indTo, int offset, int indent, int minKwdLen) {
197         // System.out.println(">>>DEBUG<<< indFrom=" + indFrom + ", indTo=" + indTo + ", offset=" + offset + ", indent=" + indent + ", minKwdLen="+ minKwdLen); // NOI18N
198
int maxLen = 0;
199         for (int i = indFrom; i < indTo; i++) {
200             maxLen = Math.max(maxLen, kwds[i].length());
201         }
202
203         int same;
204         int minLen;
205         do {
206             minLen = Integer.MAX_VALUE;
207             // Compute minimum and maximum keyword length in the current group
208
for (int i = indFrom; i < indTo; i++) {
209                 minLen = Math.min(minLen, kwds[i].length());
210             }
211
212             // System.out.println(">>>DEBUG<<< while(): minLen=" + minLen + ", minKwdLen=" + minKwdLen); // NOI18N
213
if (minLen > minKwdLen) {
214                 appendString(indent(indent) + "if (len <= " + (minLen - 1) + ")\n"); // NOI18N
215
appendString(indent(indent + 1) + "return -1;\n"); // NOI18N
216
}
217
218             // Compute how many chars from current offset on are the same
219
// in all keywords in the current group
220
same = 0;
221             boolean stop = false;
222             for (int i = offset; i < minLen; i++) {
223                 char c = kwds[indFrom].charAt(i);
224                 for (int j = indFrom + 1; j < indTo; j++) {
225                     if (kwds[j].charAt(i) != c) {
226                         stop = true;
227                         break;
228                     }
229                 }
230                 if (stop) {
231                     break;
232                 }
233                 same++;
234             }
235
236             // System.out.println(">>>DEBUG<<< minLen=" + minLen + ", maxLen=" + maxLen + ", same=" + same); // NOI18N
237

238             // Add check for all the same chars
239
if (same > 0) {
240                 appendString(indent(indent) + "if ("); // NOI18N
241
for (int i = 0; i < same; i++) {
242                     if (i > 0) {
243                         appendString(indent(indent + 1) + "|| "); // NOI18N
244
}
245                     appendString(getCurrentChar() + " != '" + kwds[indFrom].charAt(offset + i) + "'"); // NOI18N
246
if (i < same - 1) {
247                         appendString("\n"); // NOI18N
248
}
249                 }
250                 appendString(")\n" + indent(indent + 2) + "return -1;\n"); // NOI18N
251

252             }
253
254             // Increase the offset to the first 'non-same' char
255
offset += same;
256
257             // If there's a keyword with the length equal to the current offset
258
// it will be first in the (sorted) group and it will be matched now
259
if (offset == kwds[indFrom].length()) {
260                 appendString(indent(indent) + "if (len == " + offset + ")\n"); // NOI18N
261
appendString(indent(indent + 1) + "return " // NOI18N
262
+ getKwdConstant(kwds[indFrom]) + ";\n"); // NOI18N
263
indFrom++; // increase starting index as first keyword already matched
264
if (offset >= minLen) {
265                     minLen = offset + 1;
266                 }
267             }
268
269             minKwdLen = minLen; // minLen already tested, so assign new minimum
270

271         } while (same > 0 && indFrom < indTo);
272
273         // If there are other chars at the end of any keyword,
274
// add the switch statement
275
if (offset < maxLen) {
276             appendString(indent(indent) + "switch (" + getCurrentChar() + ") {\n"); // NOI18N
277

278             // Compute subgroups
279
int i = indFrom;
280             while(i < indTo) {
281                 // Add the case statement
282
char actChar = kwds[i].charAt(offset);
283                 appendString(indent(indent + 1) + "case '" + actChar + "':\n"); // NOI18N
284

285                 // Check whether the subgroup will have more than one keyword
286
int subGroupEndInd = i + 1;
287                 while(subGroupEndInd < indTo
288                         && kwds[subGroupEndInd].length() > offset
289                         && kwds[subGroupEndInd].charAt(offset) == actChar
290                      ) {
291                     subGroupEndInd++;
292                 }
293
294                 if(subGroupEndInd > i + 1) { // more than one keyword in subgroup
295
scan(i, subGroupEndInd, offset + 1, indent + 2, minLen);
296                 } else { // just one keyword in the subgroup
297
appendCheckedReturn(kwds[i], offset + 1, indent + 2);
298                 }
299
300                 // advance current index to the end of current subgroup
301
i = subGroupEndInd;
302             }
303
304             appendString(indent(indent + 1) + "default:\n"); // NOI18N
305
appendString(indent(indent + 2) + "return -1;\n"); // NOI18N
306
appendString(indent(indent) + "}\n"); // NOI18N
307
} else { // no add-on chars, keyword not found in this case
308
appendString(indent(indent) + "return -1;\n"); // NOI18N
309
}
310
311     }
312
313     /** Main method */
314     public static void main(String JavaDoc args[]) {
315         KeywordMatchGenerator km = new KeywordMatchGenerator();
316
317         // parse options
318
int argShift;
319         for (argShift = 0; argShift < args.length; argShift++) {
320             int j;
321             if (args[argShift].charAt(0) != '-') {
322                 break; // no more options
323
}
324             for (j = 0; j < OPTION_LIST.length; j++) {
325                 if (args[argShift].equals(OPTION_LIST[j])) {
326                     km.addOption(OPTION_LIST[j]);
327                     break;
328                 }
329             }
330             if (j == OPTION_LIST.length) {
331                 System.err.println("'" + args[argShift] + "'" + UNKNOWN_OPTION); // NOI18N
332
System.err.println(USAGE);
333                 return;
334             }
335         }
336
337         // check count of mandatory args
338
if (args.length - argShift < 1) {
339             System.err.println(USAGE);
340             return;
341         }
342
343         // read keyword file
344
String JavaDoc kwds = null;
345         try {
346             File JavaDoc f = new File JavaDoc(args[argShift]);
347             if (!f.exists()) {
348                 System.err.println("Non-existent file '" + args[argShift] + "'"); // NOI18N
349
return;
350             }
351             char arr[] = new char[(int)f.length()];
352             Reader JavaDoc isr = new FileReader JavaDoc(f);
353
354             int n = 0;
355             while (n < f.length()) {
356                 int count = isr.read(arr, n, (int)f.length() - n);
357                 if (count < 0)
358                     break;
359                 n += count;
360             }
361
362             kwds = new String JavaDoc(arr);
363         } catch(IOException JavaDoc e) {
364             // IO exception
365
System.err.println("Cannot read from keyword file '" + args[argShift] + "'"); // NOI18N
366
return;
367         }
368
369         // Check for optional method name
370
String JavaDoc methodName = null;
371         if (args.length - argShift >= 2) {
372             methodName = args[argShift + 1];
373         }
374
375         // generate
376
km.parseKeywords(kwds);
377         km.initScan(methodName);
378         km.scan();
379         km.finishScan();
380
381     }
382
383
384 }
385
Popular Tags