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