jdk/make/tools/swing-beans/GenDocletBeanInfo.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     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 }