1 | /*
|
---|
2 | * Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
|
---|
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
---|
4 | *
|
---|
5 | * This code is free software; you can redistribute it and/or modify it
|
---|
6 | * under the terms of the GNU General Public License version 2 only, as
|
---|
7 | * published by the Free Software Foundation. Oracle designates this
|
---|
8 | * particular file as subject to the "Classpath" exception as provided
|
---|
9 | * by Oracle in the LICENSE file that accompanied this code.
|
---|
10 | *
|
---|
11 | * This code is distributed in the hope that it will be useful, but WITHOUT
|
---|
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
---|
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
---|
14 | * version 2 for more details (a copy is included in the LICENSE file that
|
---|
15 | * accompanied this code).
|
---|
16 | *
|
---|
17 | * You should have received a copy of the GNU General Public License version
|
---|
18 | * 2 along with this work; if not, write to the Free Software Foundation,
|
---|
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
---|
20 | *
|
---|
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
---|
22 | * or visit www.oracle.com if you need additional information or have any
|
---|
23 | * questions.
|
---|
24 | */
|
---|
25 |
|
---|
26 | import com.sun.javadoc.ClassDoc;
|
---|
27 | import com.sun.javadoc.MethodDoc;
|
---|
28 | import com.sun.javadoc.RootDoc;
|
---|
29 | import com.sun.javadoc.Tag;
|
---|
30 |
|
---|
31 | import java.beans.Introspector;
|
---|
32 |
|
---|
33 | import java.util.Enumeration;
|
---|
34 | import java.util.Hashtable;
|
---|
35 | import java.util.HashMap;
|
---|
36 | import java.util.StringTokenizer;
|
---|
37 |
|
---|
38 | /**
|
---|
39 | * Properties supported and tag syntax:
|
---|
40 | *
|
---|
41 | * @beaninfo
|
---|
42 | * bound: flag
|
---|
43 | * constrained: flag
|
---|
44 | * expert: flag
|
---|
45 | * hidden: flag
|
---|
46 | * preferred: flag
|
---|
47 | * description: string
|
---|
48 | * displayname: string
|
---|
49 | * propertyeditorclass: string (with dots: foo.bar.MyPropertyEditor
|
---|
50 | * customizerclass: string (w/dots: foo.bar.MyCustomizer)
|
---|
51 | * attribute: key1 value1
|
---|
52 | * attribute: key2 value2
|
---|
53 | *
|
---|
54 | * TODO: getValue and genDocletInfo needs some cleaning.
|
---|
55 | *
|
---|
56 | * @author Hans Muller
|
---|
57 | * @author Rich Schiavi
|
---|
58 | * @author Mark Davidson
|
---|
59 | */
|
---|
60 | public class GenDocletBeanInfo {
|
---|
61 |
|
---|
62 | static String[] ATTRIBUTE_NAMES = { "bound",
|
---|
63 | "constrained",
|
---|
64 | "expert",
|
---|
65 | "hidden",
|
---|
66 | "preferred",
|
---|
67 | "displayname",
|
---|
68 | "propertyeditorclass",
|
---|
69 | "customizerclass",
|
---|
70 | "displayname",
|
---|
71 | "description",
|
---|
72 | "enum",
|
---|
73 | "attribute" };
|
---|
74 | private static boolean DEBUG = false;
|
---|
75 |
|
---|
76 | private static String fileDir = "";
|
---|
77 | private static String templateDir = "";
|
---|
78 |
|
---|
79 | public static final String TRUE = "true";
|
---|
80 | public static final String FALSE = "false";
|
---|
81 |
|
---|
82 | /**
|
---|
83 | * Method called from the javadoc environment to determint the options length.
|
---|
84 | * Doclet options:
|
---|
85 | * -t template location
|
---|
86 | * -d outputdir
|
---|
87 | * -x true Enable debug output.
|
---|
88 | */
|
---|
89 | public static int optionLength(String option) {
|
---|
90 | // remind: this needs to be cleaned up
|
---|
91 | if (option.equals("-t"))
|
---|
92 | return 2;
|
---|
93 | if (option.equals("-d"))
|
---|
94 | return 2;
|
---|
95 | if (option.equals("-x"))
|
---|
96 | return 2;
|
---|
97 | return 0;
|
---|
98 | }
|
---|
99 |
|
---|
100 | /** @beaninfo
|
---|
101 | * bound:true
|
---|
102 | * constrained:false
|
---|
103 | * expert:true
|
---|
104 | * hidden:true
|
---|
105 | * preferred:false
|
---|
106 | * description: the description of this method can
|
---|
107 | * do all sorts of funky things. if it \n
|
---|
108 | * is indented like this, we have to remove
|
---|
109 | * all char spaces greater than 2 and also any hard-coded \n
|
---|
110 | * newline characters and all newlines
|
---|
111 | * displayname: theString
|
---|
112 | * propertyeditorclass: foo.bar.MyPropertyEditorClass
|
---|
113 | * customizerclass: foo.bar.MyCustomizerClass
|
---|
114 | * attribute:key1 value1
|
---|
115 | * attribute: key2 value2
|
---|
116 | *
|
---|
117 | */
|
---|
118 | public static boolean start(RootDoc doc) {
|
---|
119 | readOptions(doc.options());
|
---|
120 |
|
---|
121 | if (templateDir.length() == 0) {
|
---|
122 | System.err.println("-t option not specified");
|
---|
123 | return false;
|
---|
124 | }
|
---|
125 | if (fileDir.length() == 0) {
|
---|
126 | System.err.println("-d option not specified");
|
---|
127 | return false;
|
---|
128 | }
|
---|
129 |
|
---|
130 | GenSwingBeanInfo generator = new GenSwingBeanInfo(fileDir, templateDir, DEBUG);
|
---|
131 | Hashtable dochash = new Hashtable();
|
---|
132 | DocBeanInfo dbi;
|
---|
133 |
|
---|
134 | /* "javadoc Foo.java Bar.java" will return:
|
---|
135 | * "Foo Foo.I1 Foo.I2 Bar Bar.I1 Bar.I2"
|
---|
136 | * i.e., with all the innerclasses of classes specified in the command
|
---|
137 | * line. We don't want to generate BeanInfo for any of these inner
|
---|
138 | * classes, so we ignore these by remembering what the last outer
|
---|
139 | * class was. A hack, I admit, but makes the build faster.
|
---|
140 | */
|
---|
141 | String previousClass = null;
|
---|
142 |
|
---|
143 | ClassDoc[] classes = doc.classes();
|
---|
144 |
|
---|
145 | for (int cnt = 0; cnt < classes.length; cnt++) {
|
---|
146 | String className = classes[cnt].qualifiedName();
|
---|
147 | if (previousClass != null &&
|
---|
148 | className.startsWith(previousClass) &&
|
---|
149 | className.charAt(previousClass.length()) == '.') {
|
---|
150 | continue;
|
---|
151 | }
|
---|
152 | previousClass = className;
|
---|
153 |
|
---|
154 | // XXX - debug
|
---|
155 | System.out.println("\n>>> Generating beaninfo for " + className + "...");
|
---|
156 |
|
---|
157 | // Examine the javadoc tags and look for the the @beaninfo tag
|
---|
158 | // This first block looks at the javadoc for the class
|
---|
159 | Tag[] tags = classes[cnt].tags();
|
---|
160 | for (int i = 0; i < tags.length; i++) {
|
---|
161 | if (tags[i].kind().equalsIgnoreCase("@beaninfo")) {
|
---|
162 | if (DEBUG)
|
---|
163 | System.out.println("GenDocletBeanInfo: found @beaninfo tagged Class: " + tags[i].text());
|
---|
164 | dbi = genDocletInfo(tags[i].text(), classes[cnt].name());
|
---|
165 | dochash.put(dbi.name, dbi);
|
---|
166 | break;
|
---|
167 | }
|
---|
168 | }
|
---|
169 |
|
---|
170 | // This block looks at the javadoc for the class methods.
|
---|
171 | int startPos = -1;
|
---|
172 | MethodDoc[] methods = classes[cnt].methods();
|
---|
173 | for (int j = 0; j < methods.length; j++) {
|
---|
174 | // actually don't "introspect" - look for all
|
---|
175 | // methods with a @beaninfo tag
|
---|
176 | tags = methods[j].tags();
|
---|
177 | for (int x = 0; x < tags.length; x++){
|
---|
178 | if (tags[x].kind().equalsIgnoreCase("@beaninfo")){
|
---|
179 | if ((methods[j].name().startsWith("get")) ||
|
---|
180 | (methods[j].name().startsWith("set")))
|
---|
181 | startPos = 3;
|
---|
182 | else if (methods[j].name().startsWith("is"))
|
---|
183 | startPos = 2;
|
---|
184 | else
|
---|
185 | startPos = 0;
|
---|
186 | String propDesc =
|
---|
187 | Introspector.decapitalize((methods[j].name()).substring(startPos));
|
---|
188 | if (DEBUG)
|
---|
189 | System.out.println("GenDocletBeanInfo: found @beaninfo tagged Method: " + tags[x].text());
|
---|
190 | dbi = genDocletInfo(tags[x].text(), propDesc);
|
---|
191 | dochash.put(dbi.name, dbi);
|
---|
192 | break;
|
---|
193 | }
|
---|
194 | }
|
---|
195 | }
|
---|
196 | if (DEBUG) {
|
---|
197 | // dump our classes doc beaninfo
|
---|
198 | System.out.println(">>>>DocletBeanInfo for class: " + classes[cnt].name());
|
---|
199 | Enumeration e = dochash.elements();
|
---|
200 | while (e.hasMoreElements()) {
|
---|
201 | DocBeanInfo db = (DocBeanInfo)e.nextElement();
|
---|
202 | System.out.println(db.toString());
|
---|
203 | }
|
---|
204 | }
|
---|
205 |
|
---|
206 | // Use the generator to create the beaninfo code for the class.
|
---|
207 | generator.genBeanInfo(classes[cnt].containingPackage().name(),
|
---|
208 | classes[cnt].name(), dochash);
|
---|
209 | // reset the values!
|
---|
210 | dochash.clear();
|
---|
211 | } // end for loop
|
---|
212 | return true;
|
---|
213 | }
|
---|
214 |
|
---|
215 | /**
|
---|
216 | * Reads the command line options.
|
---|
217 | * Side Effect, sets class variables templateDir, fileDir and DEBUG
|
---|
218 | */
|
---|
219 | private static void readOptions(String[][] options) {
|
---|
220 | // Parse the command line args
|
---|
221 | for (int i = 0; i < options.length; i++){
|
---|
222 | if (options[i][0].equals("-t")) {
|
---|
223 | templateDir = options[i][1];
|
---|
224 | } else if (options[i][0].equals("-d")) {
|
---|
225 | fileDir = options[i][1];
|
---|
226 | } else if (options[i][0].equals("-x")){
|
---|
227 | if (options[i][1].equals("true"))
|
---|
228 | DEBUG=true;
|
---|
229 | else
|
---|
230 | DEBUG=false;
|
---|
231 | }
|
---|
232 | }
|
---|
233 | }
|
---|
234 |
|
---|
235 | /**
|
---|
236 | * Create a "BeanInfo" data structure from the tag. This is a data structure
|
---|
237 | * which contains all beaninfo data for a method or a class.
|
---|
238 | *
|
---|
239 | * @param text All the text after the @beaninfo tag.
|
---|
240 | * @param name Name of the property i.e., mnemonic for setMnemonic
|
---|
241 | */
|
---|
242 | private static DocBeanInfo genDocletInfo(String text, String name) {
|
---|
243 | int beanflags = 0;
|
---|
244 | String desc = "null";
|
---|
245 | String displayname = "null";
|
---|
246 | String propertyeditorclass = "null";
|
---|
247 | String customizerclass = "null";
|
---|
248 | String value = "null";
|
---|
249 | HashMap attribs = null;
|
---|
250 | HashMap enums = null;
|
---|
251 |
|
---|
252 | int index;
|
---|
253 |
|
---|
254 | for (int j = 0; j < ATTRIBUTE_NAMES.length; j++){
|
---|
255 | index = 0;
|
---|
256 | if ((index = text.indexOf(ATTRIBUTE_NAMES[j])) != -1){
|
---|
257 | value = getValue((text).substring(index),ATTRIBUTE_NAMES[j]);
|
---|
258 |
|
---|
259 | if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("attribute")) {
|
---|
260 | attribs = getAttributeMap(value, " ");
|
---|
261 | }
|
---|
262 | if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("enum")) {
|
---|
263 | enums = getAttributeMap(value, " \n");
|
---|
264 | }
|
---|
265 | else if (ATTRIBUTE_NAMES[j].equals("displayname")){
|
---|
266 | displayname = value;
|
---|
267 | }
|
---|
268 | else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("propertyeditorclass")) {
|
---|
269 | propertyeditorclass = value;
|
---|
270 | }
|
---|
271 | else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("customizerclass")){
|
---|
272 | customizerclass = value;
|
---|
273 | }
|
---|
274 | else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("bound"))
|
---|
275 | && (value.equalsIgnoreCase(TRUE)))
|
---|
276 | beanflags = beanflags | DocBeanInfo.BOUND;
|
---|
277 | else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("expert"))
|
---|
278 | && (value.equalsIgnoreCase(TRUE)))
|
---|
279 | beanflags = beanflags | DocBeanInfo.EXPERT;
|
---|
280 | else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("constrained"))
|
---|
281 | && (value.equalsIgnoreCase(TRUE)))
|
---|
282 | beanflags = beanflags | DocBeanInfo.CONSTRAINED;
|
---|
283 | else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("hidden"))
|
---|
284 | && (value.equalsIgnoreCase(TRUE)))
|
---|
285 | beanflags = beanflags | DocBeanInfo.HIDDEN;
|
---|
286 | else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("preferred"))
|
---|
287 | && (value.equalsIgnoreCase(TRUE)))
|
---|
288 | beanflags = beanflags | DocBeanInfo.PREFERRED;
|
---|
289 | else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("description")){
|
---|
290 | desc = value;
|
---|
291 | }
|
---|
292 | }
|
---|
293 | }
|
---|
294 | /** here we create our doclet-beaninfo data structure, which we read in
|
---|
295 | * later if it has anything worthwhile
|
---|
296 | */
|
---|
297 |
|
---|
298 | // Construct a new Descriptor class
|
---|
299 | return new DocBeanInfo(name, beanflags, desc,displayname,
|
---|
300 | propertyeditorclass, customizerclass,
|
---|
301 | attribs, enums);
|
---|
302 | }
|
---|
303 |
|
---|
304 | /**
|
---|
305 | * Parses the substring and returns the cleaned up value for the attribute.
|
---|
306 | * @param substring Full String of the attrib tag.
|
---|
307 | * i.e., "attribute: visualUpdate true" will return "visualUpdate true";
|
---|
308 | */
|
---|
309 | private static String getValue(String substring, String prop) {
|
---|
310 | StringTokenizer t;
|
---|
311 | String value = "null";
|
---|
312 |
|
---|
313 | try {
|
---|
314 | /** if the ATTRIBUTE_NAMES is NOT the description, then we
|
---|
315 | * parse until newline
|
---|
316 | * if it is the description we read until the next token
|
---|
317 | * and then look for a match in the last MAXMATCH index
|
---|
318 | * and truncate the description
|
---|
319 | * if it is the attribute we wead until no more
|
---|
320 | */
|
---|
321 | if (prop.equalsIgnoreCase("attribute")){
|
---|
322 | StringBuffer tmp = new StringBuffer();
|
---|
323 | try {
|
---|
324 | t = new StringTokenizer(substring, " :\n");
|
---|
325 | t.nextToken().trim();//the prop
|
---|
326 | // we want to return : key1 value1 key2 value2
|
---|
327 | while (t.hasMoreTokens()){
|
---|
328 | tmp.append(t.nextToken().trim()).append(" ");
|
---|
329 | tmp.append(t.nextToken().trim()).append(" ");
|
---|
330 | String test = t.nextToken().trim();
|
---|
331 | if (!(test.equalsIgnoreCase("attribute")))
|
---|
332 | break;
|
---|
333 | }
|
---|
334 | } catch (Exception e){
|
---|
335 | }
|
---|
336 | value = tmp.toString();
|
---|
337 | }
|
---|
338 | else if (prop.equalsIgnoreCase("enum")){
|
---|
339 | t = new StringTokenizer(substring, ":");
|
---|
340 | t.nextToken().trim(); // the prop we already know
|
---|
341 | StringBuffer tmp = new StringBuffer(t.nextToken().trim());
|
---|
342 | for (int i = 0; i < ATTRIBUTE_NAMES.length; i++){
|
---|
343 | if (tmp.toString().endsWith(ATTRIBUTE_NAMES[i])){
|
---|
344 | int len = ATTRIBUTE_NAMES[i].length();
|
---|
345 | // trim off that
|
---|
346 | tmp.setLength(tmp.length() - len);
|
---|
347 | break;
|
---|
348 | }
|
---|
349 | }
|
---|
350 | value = tmp.toString();
|
---|
351 | }
|
---|
352 | else if (prop.equalsIgnoreCase("description")){
|
---|
353 | t = new StringTokenizer(substring, ":");
|
---|
354 | t.nextToken().trim(); // the prop we already know
|
---|
355 | StringBuffer tmp = new StringBuffer(t.nextToken().trim());
|
---|
356 | for (int i = 0; i < ATTRIBUTE_NAMES.length; i++){
|
---|
357 | if (tmp.toString().endsWith(ATTRIBUTE_NAMES[i])){
|
---|
358 | int len = ATTRIBUTE_NAMES[i].length();
|
---|
359 | // trim off that
|
---|
360 | tmp.setLength(tmp.length() - len);
|
---|
361 | break;
|
---|
362 | }
|
---|
363 | }
|
---|
364 | value = hansalizeIt(tmp.toString());
|
---|
365 | }
|
---|
366 | else {
|
---|
367 | // Single value properties like bound: true
|
---|
368 | t = new StringTokenizer(substring, ":\n");
|
---|
369 | t.nextToken().trim(); // the prop we already know
|
---|
370 | value = t.nextToken().trim();
|
---|
371 | }
|
---|
372 |
|
---|
373 | // now we need to look for a match of any of the
|
---|
374 | // property
|
---|
375 |
|
---|
376 | return value;
|
---|
377 | }
|
---|
378 | catch (Exception e){
|
---|
379 | return "invalidValue";
|
---|
380 | }
|
---|
381 | }
|
---|
382 |
|
---|
383 | /**
|
---|
384 | * Creates a HashMap containing the key value pair for the parsed values
|
---|
385 | * of the "attributes" and "enum" tags.
|
---|
386 | * ie. For attribute value: visualUpdate true
|
---|
387 | * The HashMap will have key: visualUpdate, value: true
|
---|
388 | */
|
---|
389 | private static HashMap getAttributeMap(String str, String delim) {
|
---|
390 | StringTokenizer t = new StringTokenizer(str, delim);
|
---|
391 | HashMap map = null;
|
---|
392 | String key;
|
---|
393 | String value;
|
---|
394 |
|
---|
395 | int num = t.countTokens()/2;
|
---|
396 | if (num > 0) {
|
---|
397 | map = new HashMap();
|
---|
398 | for (int i = 0; i < num; i++) {
|
---|
399 | key = t.nextToken().trim();
|
---|
400 | value = t.nextToken().trim();
|
---|
401 | map.put(key, value);
|
---|
402 | }
|
---|
403 | }
|
---|
404 | return map;
|
---|
405 | }
|
---|
406 |
|
---|
407 | // looks for extra spaces, \n hard-coded and invisible,etc
|
---|
408 | private static String hansalizeIt(String from){
|
---|
409 | char [] chars = from.toCharArray();
|
---|
410 | int len = chars.length;
|
---|
411 | int toss = 0;
|
---|
412 |
|
---|
413 | // remove double spaces
|
---|
414 | for (int i = 0; i < len; i++){
|
---|
415 | if ((chars[i] == ' ')) {
|
---|
416 | if (i+1 < len) {
|
---|
417 | if ((chars[i+1] == ' ' ) || (chars[i+1] == '\n'))
|
---|
418 | {
|
---|
419 | --len;
|
---|
420 | System.arraycopy(chars,i+1,chars,i,len-i);
|
---|
421 | --i;
|
---|
422 | }
|
---|
423 | }
|
---|
424 | }
|
---|
425 |
|
---|
426 | if (chars[i] == '\n'){
|
---|
427 | chars[i] = ' ';
|
---|
428 | i -= 2;
|
---|
429 | }
|
---|
430 |
|
---|
431 | if (chars[i] == '\\') {
|
---|
432 | if (i+1 < len) {
|
---|
433 | if (chars[i+1] == 'n'){
|
---|
434 | chars[i+1] = ' ';
|
---|
435 | --len;
|
---|
436 | System.arraycopy(chars,i+1, chars,i, len-i);
|
---|
437 | --i;
|
---|
438 | }
|
---|
439 | }
|
---|
440 | }
|
---|
441 | }
|
---|
442 | return new String(chars,0,len);
|
---|
443 | }
|
---|
444 |
|
---|
445 | }
|
---|