2
+ − 1
/*
+ − 2
* Copyright 1998-2001 Sun Microsystems, Inc. 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. Sun designates this
+ − 8
* particular file as subject to the "Classpath" exception as provided
+ − 9
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ − 22
* CA 95054 USA or visit www.sun.com if you need additional information or
+ − 23
* have any 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
}