0N/A/*
2362N/A * Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Aimport com.sun.javadoc.ClassDoc;
0N/Aimport com.sun.javadoc.MethodDoc;
0N/Aimport com.sun.javadoc.RootDoc;
0N/Aimport com.sun.javadoc.Tag;
0N/A
0N/Aimport java.beans.Introspector;
0N/A
0N/Aimport java.util.Enumeration;
0N/Aimport java.util.Hashtable;
0N/Aimport java.util.HashMap;
0N/Aimport java.util.StringTokenizer;
0N/A
0N/A/**
0N/A * Properties supported and tag syntax:
0N/A *
0N/A * @beaninfo
0N/A * bound: flag
0N/A * constrained: flag
0N/A * expert: flag
0N/A * hidden: flag
0N/A * preferred: flag
0N/A * description: string
0N/A * displayname: string
0N/A * propertyeditorclass: string (with dots: foo.bar.MyPropertyEditor
0N/A * customizerclass: string (w/dots: foo.bar.MyCustomizer)
0N/A * attribute: key1 value1
0N/A * attribute: key2 value2
0N/A *
0N/A * TODO: getValue and genDocletInfo needs some cleaning.
0N/A *
0N/A * @author Hans Muller
0N/A * @author Rich Schiavi
0N/A * @author Mark Davidson
0N/A */
0N/Apublic class GenDocletBeanInfo {
0N/A
0N/A static String[] ATTRIBUTE_NAMES = { "bound",
0N/A "constrained",
0N/A "expert",
0N/A "hidden",
0N/A "preferred",
0N/A "displayname",
0N/A "propertyeditorclass",
0N/A "customizerclass",
0N/A "displayname",
0N/A "description",
0N/A "enum",
0N/A "attribute" };
0N/A private static boolean DEBUG = false;
0N/A
0N/A private static String fileDir = "";
0N/A private static String templateDir = "";
0N/A
0N/A public static final String TRUE = "true";
0N/A public static final String FALSE = "false";
0N/A
0N/A /**
0N/A * Method called from the javadoc environment to determint the options length.
0N/A * Doclet options:
0N/A * -t template location
0N/A * -d outputdir
0N/A * -x true Enable debug output.
0N/A */
0N/A public static int optionLength(String option) {
0N/A // remind: this needs to be cleaned up
0N/A if (option.equals("-t"))
0N/A return 2;
0N/A if (option.equals("-d"))
0N/A return 2;
0N/A if (option.equals("-x"))
0N/A return 2;
0N/A return 0;
0N/A }
0N/A
0N/A /** @beaninfo
0N/A * bound:true
0N/A * constrained:false
0N/A * expert:true
0N/A * hidden:true
0N/A * preferred:false
0N/A * description: the description of this method can
0N/A * do all sorts of funky things. if it \n
0N/A * is indented like this, we have to remove
0N/A * all char spaces greater than 2 and also any hard-coded \n
0N/A * newline characters and all newlines
0N/A * displayname: theString
0N/A * propertyeditorclass: foo.bar.MyPropertyEditorClass
0N/A * customizerclass: foo.bar.MyCustomizerClass
0N/A * attribute:key1 value1
0N/A * attribute: key2 value2
0N/A *
0N/A */
0N/A public static boolean start(RootDoc doc) {
0N/A readOptions(doc.options());
0N/A
0N/A if (templateDir.length() == 0) {
0N/A System.err.println("-t option not specified");
0N/A return false;
0N/A }
0N/A if (fileDir.length() == 0) {
0N/A System.err.println("-d option not specified");
0N/A return false;
0N/A }
0N/A
0N/A GenSwingBeanInfo generator = new GenSwingBeanInfo(fileDir, templateDir, DEBUG);
0N/A Hashtable dochash = new Hashtable();
0N/A DocBeanInfo dbi;
0N/A
0N/A /* "javadoc Foo.java Bar.java" will return:
0N/A * "Foo Foo.I1 Foo.I2 Bar Bar.I1 Bar.I2"
0N/A * i.e., with all the innerclasses of classes specified in the command
0N/A * line. We don't want to generate BeanInfo for any of these inner
0N/A * classes, so we ignore these by remembering what the last outer
0N/A * class was. A hack, I admit, but makes the build faster.
0N/A */
0N/A String previousClass = null;
0N/A
0N/A ClassDoc[] classes = doc.classes();
0N/A
0N/A for (int cnt = 0; cnt < classes.length; cnt++) {
0N/A String className = classes[cnt].qualifiedName();
0N/A if (previousClass != null &&
0N/A className.startsWith(previousClass) &&
0N/A className.charAt(previousClass.length()) == '.') {
0N/A continue;
0N/A }
0N/A previousClass = className;
0N/A
0N/A // XXX - debug
0N/A System.out.println("\n>>> Generating beaninfo for " + className + "...");
0N/A
0N/A // Examine the javadoc tags and look for the the @beaninfo tag
0N/A // This first block looks at the javadoc for the class
0N/A Tag[] tags = classes[cnt].tags();
0N/A for (int i = 0; i < tags.length; i++) {
0N/A if (tags[i].kind().equalsIgnoreCase("@beaninfo")) {
0N/A if (DEBUG)
0N/A System.out.println("GenDocletBeanInfo: found @beaninfo tagged Class: " + tags[i].text());
0N/A dbi = genDocletInfo(tags[i].text(), classes[cnt].name());
0N/A dochash.put(dbi.name, dbi);
0N/A break;
0N/A }
0N/A }
0N/A
0N/A // This block looks at the javadoc for the class methods.
0N/A int startPos = -1;
0N/A MethodDoc[] methods = classes[cnt].methods();
0N/A for (int j = 0; j < methods.length; j++) {
0N/A // actually don't "introspect" - look for all
0N/A // methods with a @beaninfo tag
0N/A tags = methods[j].tags();
0N/A for (int x = 0; x < tags.length; x++){
0N/A if (tags[x].kind().equalsIgnoreCase("@beaninfo")){
0N/A if ((methods[j].name().startsWith("get")) ||
0N/A (methods[j].name().startsWith("set")))
0N/A startPos = 3;
0N/A else if (methods[j].name().startsWith("is"))
0N/A startPos = 2;
0N/A else
0N/A startPos = 0;
0N/A String propDesc =
0N/A Introspector.decapitalize((methods[j].name()).substring(startPos));
0N/A if (DEBUG)
0N/A System.out.println("GenDocletBeanInfo: found @beaninfo tagged Method: " + tags[x].text());
0N/A dbi = genDocletInfo(tags[x].text(), propDesc);
0N/A dochash.put(dbi.name, dbi);
0N/A break;
0N/A }
0N/A }
0N/A }
0N/A if (DEBUG) {
0N/A // dump our classes doc beaninfo
0N/A System.out.println(">>>>DocletBeanInfo for class: " + classes[cnt].name());
0N/A Enumeration e = dochash.elements();
0N/A while (e.hasMoreElements()) {
0N/A DocBeanInfo db = (DocBeanInfo)e.nextElement();
0N/A System.out.println(db.toString());
0N/A }
0N/A }
0N/A
0N/A // Use the generator to create the beaninfo code for the class.
0N/A generator.genBeanInfo(classes[cnt].containingPackage().name(),
0N/A classes[cnt].name(), dochash);
0N/A // reset the values!
0N/A dochash.clear();
0N/A } // end for loop
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Reads the command line options.
0N/A * Side Effect, sets class variables templateDir, fileDir and DEBUG
0N/A */
0N/A private static void readOptions(String[][] options) {
0N/A // Parse the command line args
0N/A for (int i = 0; i < options.length; i++){
0N/A if (options[i][0].equals("-t")) {
0N/A templateDir = options[i][1];
0N/A } else if (options[i][0].equals("-d")) {
0N/A fileDir = options[i][1];
0N/A } else if (options[i][0].equals("-x")){
0N/A if (options[i][1].equals("true"))
0N/A DEBUG=true;
0N/A else
0N/A DEBUG=false;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Create a "BeanInfo" data structure from the tag. This is a data structure
0N/A * which contains all beaninfo data for a method or a class.
0N/A *
0N/A * @param text All the text after the @beaninfo tag.
0N/A * @param name Name of the property i.e., mnemonic for setMnemonic
0N/A */
0N/A private static DocBeanInfo genDocletInfo(String text, String name) {
0N/A int beanflags = 0;
0N/A String desc = "null";
0N/A String displayname = "null";
0N/A String propertyeditorclass = "null";
0N/A String customizerclass = "null";
0N/A String value = "null";
0N/A HashMap attribs = null;
0N/A HashMap enums = null;
0N/A
0N/A int index;
0N/A
0N/A for (int j = 0; j < ATTRIBUTE_NAMES.length; j++){
0N/A index = 0;
0N/A if ((index = text.indexOf(ATTRIBUTE_NAMES[j])) != -1){
0N/A value = getValue((text).substring(index),ATTRIBUTE_NAMES[j]);
0N/A
0N/A if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("attribute")) {
0N/A attribs = getAttributeMap(value, " ");
0N/A }
0N/A if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("enum")) {
0N/A enums = getAttributeMap(value, " \n");
0N/A }
0N/A else if (ATTRIBUTE_NAMES[j].equals("displayname")){
0N/A displayname = value;
0N/A }
0N/A else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("propertyeditorclass")) {
0N/A propertyeditorclass = value;
0N/A }
0N/A else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("customizerclass")){
0N/A customizerclass = value;
0N/A }
0N/A else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("bound"))
0N/A && (value.equalsIgnoreCase(TRUE)))
0N/A beanflags = beanflags | DocBeanInfo.BOUND;
0N/A else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("expert"))
0N/A && (value.equalsIgnoreCase(TRUE)))
0N/A beanflags = beanflags | DocBeanInfo.EXPERT;
0N/A else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("constrained"))
0N/A && (value.equalsIgnoreCase(TRUE)))
0N/A beanflags = beanflags | DocBeanInfo.CONSTRAINED;
0N/A else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("hidden"))
0N/A && (value.equalsIgnoreCase(TRUE)))
0N/A beanflags = beanflags | DocBeanInfo.HIDDEN;
0N/A else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("preferred"))
0N/A && (value.equalsIgnoreCase(TRUE)))
0N/A beanflags = beanflags | DocBeanInfo.PREFERRED;
0N/A else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("description")){
0N/A desc = value;
0N/A }
0N/A }
0N/A }
0N/A /** here we create our doclet-beaninfo data structure, which we read in
0N/A * later if it has anything worthwhile
0N/A */
0N/A
0N/A // Construct a new Descriptor class
0N/A return new DocBeanInfo(name, beanflags, desc,displayname,
0N/A propertyeditorclass, customizerclass,
0N/A attribs, enums);
0N/A }
0N/A
0N/A /**
0N/A * Parses the substring and returns the cleaned up value for the attribute.
0N/A * @param substring Full String of the attrib tag.
0N/A * i.e., "attribute: visualUpdate true" will return "visualUpdate true";
0N/A */
0N/A private static String getValue(String substring, String prop) {
0N/A StringTokenizer t;
0N/A String value = "null";
0N/A
0N/A try {
0N/A /** if the ATTRIBUTE_NAMES is NOT the description, then we
0N/A * parse until newline
0N/A * if it is the description we read until the next token
0N/A * and then look for a match in the last MAXMATCH index
0N/A * and truncate the description
0N/A * if it is the attribute we wead until no more
0N/A */
0N/A if (prop.equalsIgnoreCase("attribute")){
0N/A StringBuffer tmp = new StringBuffer();
0N/A try {
0N/A t = new StringTokenizer(substring, " :\n");
0N/A t.nextToken().trim();//the prop
0N/A // we want to return : key1 value1 key2 value2
0N/A while (t.hasMoreTokens()){
0N/A tmp.append(t.nextToken().trim()).append(" ");
0N/A tmp.append(t.nextToken().trim()).append(" ");
0N/A String test = t.nextToken().trim();
0N/A if (!(test.equalsIgnoreCase("attribute")))
0N/A break;
0N/A }
0N/A } catch (Exception e){
0N/A }
0N/A value = tmp.toString();
0N/A }
0N/A else if (prop.equalsIgnoreCase("enum")){
0N/A t = new StringTokenizer(substring, ":");
0N/A t.nextToken().trim(); // the prop we already know
0N/A StringBuffer tmp = new StringBuffer(t.nextToken().trim());
0N/A for (int i = 0; i < ATTRIBUTE_NAMES.length; i++){
0N/A if (tmp.toString().endsWith(ATTRIBUTE_NAMES[i])){
0N/A int len = ATTRIBUTE_NAMES[i].length();
0N/A // trim off that
0N/A tmp.setLength(tmp.length() - len);
0N/A break;
0N/A }
0N/A }
0N/A value = tmp.toString();
0N/A }
0N/A else if (prop.equalsIgnoreCase("description")){
0N/A t = new StringTokenizer(substring, ":");
0N/A t.nextToken().trim(); // the prop we already know
0N/A StringBuffer tmp = new StringBuffer(t.nextToken().trim());
0N/A for (int i = 0; i < ATTRIBUTE_NAMES.length; i++){
0N/A if (tmp.toString().endsWith(ATTRIBUTE_NAMES[i])){
0N/A int len = ATTRIBUTE_NAMES[i].length();
0N/A // trim off that
0N/A tmp.setLength(tmp.length() - len);
0N/A break;
0N/A }
0N/A }
0N/A value = hansalizeIt(tmp.toString());
0N/A }
0N/A else {
0N/A // Single value properties like bound: true
0N/A t = new StringTokenizer(substring, ":\n");
0N/A t.nextToken().trim(); // the prop we already know
0N/A value = t.nextToken().trim();
0N/A }
0N/A
0N/A // now we need to look for a match of any of the
0N/A // property
0N/A
0N/A return value;
0N/A }
0N/A catch (Exception e){
0N/A return "invalidValue";
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Creates a HashMap containing the key value pair for the parsed values
0N/A * of the "attributes" and "enum" tags.
0N/A * ie. For attribute value: visualUpdate true
0N/A * The HashMap will have key: visualUpdate, value: true
0N/A */
0N/A private static HashMap getAttributeMap(String str, String delim) {
0N/A StringTokenizer t = new StringTokenizer(str, delim);
0N/A HashMap map = null;
0N/A String key;
0N/A String value;
0N/A
0N/A int num = t.countTokens()/2;
0N/A if (num > 0) {
0N/A map = new HashMap();
0N/A for (int i = 0; i < num; i++) {
0N/A key = t.nextToken().trim();
0N/A value = t.nextToken().trim();
0N/A map.put(key, value);
0N/A }
0N/A }
0N/A return map;
0N/A }
0N/A
0N/A // looks for extra spaces, \n hard-coded and invisible,etc
0N/A private static String hansalizeIt(String from){
0N/A char [] chars = from.toCharArray();
0N/A int len = chars.length;
0N/A int toss = 0;
0N/A
0N/A // remove double spaces
0N/A for (int i = 0; i < len; i++){
0N/A if ((chars[i] == ' ')) {
0N/A if (i+1 < len) {
0N/A if ((chars[i+1] == ' ' ) || (chars[i+1] == '\n'))
0N/A {
0N/A --len;
0N/A System.arraycopy(chars,i+1,chars,i,len-i);
0N/A --i;
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (chars[i] == '\n'){
0N/A chars[i] = ' ';
0N/A i -= 2;
0N/A }
0N/A
0N/A if (chars[i] == '\\') {
0N/A if (i+1 < len) {
0N/A if (chars[i+1] == 'n'){
0N/A chars[i+1] = ' ';
0N/A --len;
0N/A System.arraycopy(chars,i+1, chars,i, len-i);
0N/A --i;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return new String(chars,0,len);
0N/A }
0N/A
0N/A}