Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/make/src/classes/build/tools/swingbeaninfo/GenDocletBeanInfo.java
32287 views
/*1* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package build.tools.swingbeaninfo;2627import com.sun.javadoc.ClassDoc;28import com.sun.javadoc.MethodDoc;29import com.sun.javadoc.RootDoc;30import com.sun.javadoc.Tag;3132import java.beans.Introspector;3334import java.util.Enumeration;35import java.util.Hashtable;36import java.util.HashMap;37import java.util.StringTokenizer;3839/**40* Properties supported and tag syntax:41*42* @beaninfo43* bound: flag44* constrained: flag45* expert: flag46* hidden: flag47* preferred: flag48* description: string49* displayname: string50* propertyeditorclass: string (with dots: foo.bar.MyPropertyEditor51* customizerclass: string (w/dots: foo.bar.MyCustomizer)52* attribute: key1 value153* attribute: key2 value254*55* TODO: getValue and genDocletInfo needs some cleaning.56*57* @author Hans Muller58* @author Rich Schiavi59* @author Mark Davidson60*/61public class GenDocletBeanInfo {6263static String[] ATTRIBUTE_NAMES = { "bound",64"constrained",65"expert",66"hidden",67"preferred",68"displayname",69"propertyeditorclass",70"customizerclass",71"displayname",72"description",73"enum",74"attribute" };75private static boolean DEBUG = false;7677private static String fileDir = "";78private static String templateDir = "";7980public static final String TRUE = "true";81public static final String FALSE = "false";8283/**84* Method called from the javadoc environment to determint the options length.85* Doclet options:86* -t template location87* -d outputdir88* -x true Enable debug output.89*/90public static int optionLength(String option) {91// remind: this needs to be cleaned up92if (option.equals("-t"))93return 2;94if (option.equals("-d"))95return 2;96if (option.equals("-x"))97return 2;98return 0;99}100101/** @beaninfo102* bound:true103* constrained:false104* expert:true105* hidden:true106* preferred:false107* description: the description of this method can108* do all sorts of funky things. if it \n109* is indented like this, we have to remove110* all char spaces greater than 2 and also any hard-coded \n111* newline characters and all newlines112* displayname: theString113* propertyeditorclass: foo.bar.MyPropertyEditorClass114* customizerclass: foo.bar.MyCustomizerClass115* attribute:key1 value1116* attribute: key2 value2117*118*/119public static boolean start(RootDoc doc) {120readOptions(doc.options());121122if (templateDir.length() == 0) {123System.err.println("-t option not specified");124return false;125}126if (fileDir.length() == 0) {127System.err.println("-d option not specified");128return false;129}130131GenSwingBeanInfo generator = new GenSwingBeanInfo(fileDir, templateDir, DEBUG);132Hashtable dochash = new Hashtable();133DocBeanInfo dbi;134135/* "javadoc Foo.java Bar.java" will return:136* "Foo Foo.I1 Foo.I2 Bar Bar.I1 Bar.I2"137* i.e., with all the innerclasses of classes specified in the command138* line. We don't want to generate BeanInfo for any of these inner139* classes, so we ignore these by remembering what the last outer140* class was. A hack, I admit, but makes the build faster.141*/142String previousClass = null;143144ClassDoc[] classes = doc.classes();145146for (int cnt = 0; cnt < classes.length; cnt++) {147String className = classes[cnt].qualifiedName();148if (previousClass != null &&149className.startsWith(previousClass) &&150className.charAt(previousClass.length()) == '.') {151continue;152}153previousClass = className;154155// XXX - debug156System.out.println("\n>>> Generating beaninfo for " + className + "...");157158// Examine the javadoc tags and look for the the @beaninfo tag159// This first block looks at the javadoc for the class160Tag[] tags = classes[cnt].tags();161for (int i = 0; i < tags.length; i++) {162if (tags[i].kind().equalsIgnoreCase("@beaninfo")) {163if (DEBUG)164System.out.println("GenDocletBeanInfo: found @beaninfo tagged Class: " + tags[i].text());165dbi = genDocletInfo(tags[i].text(), classes[cnt].name());166dochash.put(dbi.name, dbi);167break;168}169}170171// This block looks at the javadoc for the class methods.172int startPos = -1;173MethodDoc[] methods = classes[cnt].methods();174for (int j = 0; j < methods.length; j++) {175// actually don't "introspect" - look for all176// methods with a @beaninfo tag177tags = methods[j].tags();178for (int x = 0; x < tags.length; x++){179if (tags[x].kind().equalsIgnoreCase("@beaninfo")){180if ((methods[j].name().startsWith("get")) ||181(methods[j].name().startsWith("set")))182startPos = 3;183else if (methods[j].name().startsWith("is"))184startPos = 2;185else186startPos = 0;187String propDesc =188Introspector.decapitalize((methods[j].name()).substring(startPos));189if (DEBUG)190System.out.println("GenDocletBeanInfo: found @beaninfo tagged Method: " + tags[x].text());191dbi = genDocletInfo(tags[x].text(), propDesc);192dochash.put(dbi.name, dbi);193break;194}195}196}197if (DEBUG) {198// dump our classes doc beaninfo199System.out.println(">>>>DocletBeanInfo for class: " + classes[cnt].name());200Enumeration e = dochash.elements();201while (e.hasMoreElements()) {202DocBeanInfo db = (DocBeanInfo)e.nextElement();203System.out.println(db.toString());204}205}206207// Use the generator to create the beaninfo code for the class.208generator.genBeanInfo(classes[cnt].containingPackage().name(),209classes[cnt].name(), dochash);210// reset the values!211dochash.clear();212} // end for loop213return true;214}215216/**217* Reads the command line options.218* Side Effect, sets class variables templateDir, fileDir and DEBUG219*/220private static void readOptions(String[][] options) {221// Parse the command line args222for (int i = 0; i < options.length; i++){223if (options[i][0].equals("-t")) {224templateDir = options[i][1];225} else if (options[i][0].equals("-d")) {226fileDir = options[i][1];227} else if (options[i][0].equals("-x")){228if (options[i][1].equals("true"))229DEBUG=true;230else231DEBUG=false;232}233}234}235236/**237* Create a "BeanInfo" data structure from the tag. This is a data structure238* which contains all beaninfo data for a method or a class.239*240* @param text All the text after the @beaninfo tag.241* @param name Name of the property i.e., mnemonic for setMnemonic242*/243private static DocBeanInfo genDocletInfo(String text, String name) {244int beanflags = 0;245String desc = "null";246String displayname = "null";247String propertyeditorclass = "null";248String customizerclass = "null";249String value = "null";250HashMap attribs = null;251HashMap enums = null;252253int index;254255for (int j = 0; j < ATTRIBUTE_NAMES.length; j++){256index = 0;257if ((index = text.indexOf(ATTRIBUTE_NAMES[j])) != -1){258value = getValue((text).substring(index),ATTRIBUTE_NAMES[j]);259260if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("attribute")) {261attribs = getAttributeMap(value, " ");262}263if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("enum")) {264enums = getAttributeMap(value, " \n");265}266else if (ATTRIBUTE_NAMES[j].equals("displayname")){267displayname = value;268}269else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("propertyeditorclass")) {270propertyeditorclass = value;271}272else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("customizerclass")){273customizerclass = value;274}275else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("bound"))276&& (value.equalsIgnoreCase(TRUE)))277beanflags = beanflags | DocBeanInfo.BOUND;278else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("expert"))279&& (value.equalsIgnoreCase(TRUE)))280beanflags = beanflags | DocBeanInfo.EXPERT;281else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("constrained"))282&& (value.equalsIgnoreCase(TRUE)))283beanflags = beanflags | DocBeanInfo.CONSTRAINED;284else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("hidden"))285&& (value.equalsIgnoreCase(TRUE)))286beanflags = beanflags | DocBeanInfo.HIDDEN;287else if ((ATTRIBUTE_NAMES[j].equalsIgnoreCase("preferred"))288&& (value.equalsIgnoreCase(TRUE)))289beanflags = beanflags | DocBeanInfo.PREFERRED;290else if (ATTRIBUTE_NAMES[j].equalsIgnoreCase("description")){291desc = value;292}293}294}295/** here we create our doclet-beaninfo data structure, which we read in296* later if it has anything worthwhile297*/298299// Construct a new Descriptor class300return new DocBeanInfo(name, beanflags, desc,displayname,301propertyeditorclass, customizerclass,302attribs, enums);303}304305/**306* Parses the substring and returns the cleaned up value for the attribute.307* @param substring Full String of the attrib tag.308* i.e., "attribute: visualUpdate true" will return "visualUpdate true";309*/310private static String getValue(String substring, String prop) {311StringTokenizer t;312String value = "null";313314try {315/** if the ATTRIBUTE_NAMES is NOT the description, then we316* parse until newline317* if it is the description we read until the next token318* and then look for a match in the last MAXMATCH index319* and truncate the description320* if it is the attribute we wead until no more321*/322if (prop.equalsIgnoreCase("attribute")){323StringBuffer tmp = new StringBuffer();324try {325t = new StringTokenizer(substring, " :\n");326t.nextToken().trim();//the prop327// we want to return : key1 value1 key2 value2328while (t.hasMoreTokens()){329tmp.append(t.nextToken().trim()).append(" ");330tmp.append(t.nextToken().trim()).append(" ");331String test = t.nextToken().trim();332if (!(test.equalsIgnoreCase("attribute")))333break;334}335} catch (Exception e){336}337value = tmp.toString();338}339else if (prop.equalsIgnoreCase("enum")){340t = new StringTokenizer(substring, ":");341t.nextToken().trim(); // the prop we already know342StringBuffer tmp = new StringBuffer(t.nextToken().trim());343for (int i = 0; i < ATTRIBUTE_NAMES.length; i++){344if (tmp.toString().endsWith(ATTRIBUTE_NAMES[i])){345int len = ATTRIBUTE_NAMES[i].length();346// trim off that347tmp.setLength(tmp.length() - len);348break;349}350}351value = tmp.toString();352}353else if (prop.equalsIgnoreCase("description")){354t = new StringTokenizer(substring, ":");355t.nextToken().trim(); // the prop we already know356StringBuffer tmp = new StringBuffer(t.nextToken().trim());357for (int i = 0; i < ATTRIBUTE_NAMES.length; i++){358if (tmp.toString().endsWith(ATTRIBUTE_NAMES[i])){359int len = ATTRIBUTE_NAMES[i].length();360// trim off that361tmp.setLength(tmp.length() - len);362break;363}364}365value = hansalizeIt(tmp.toString());366}367else {368// Single value properties like bound: true369t = new StringTokenizer(substring, ":\n");370t.nextToken().trim(); // the prop we already know371value = t.nextToken().trim();372}373374// now we need to look for a match of any of the375// property376377return value;378}379catch (Exception e){380return "invalidValue";381}382}383384/**385* Creates a HashMap containing the key value pair for the parsed values386* of the "attributes" and "enum" tags.387* ie. For attribute value: visualUpdate true388* The HashMap will have key: visualUpdate, value: true389*/390private static HashMap getAttributeMap(String str, String delim) {391StringTokenizer t = new StringTokenizer(str, delim);392HashMap map = null;393String key;394String value;395396int num = t.countTokens()/2;397if (num > 0) {398map = new HashMap();399for (int i = 0; i < num; i++) {400key = t.nextToken().trim();401value = t.nextToken().trim();402map.put(key, value);403}404}405return map;406}407408// looks for extra spaces, \n hard-coded and invisible,etc409private static String hansalizeIt(String from){410char [] chars = from.toCharArray();411int len = chars.length;412int toss = 0;413414// remove double spaces415for (int i = 0; i < len; i++){416if ((chars[i] == ' ')) {417if (i+1 < len) {418if ((chars[i+1] == ' ' ) || (chars[i+1] == '\n'))419{420--len;421System.arraycopy(chars,i+1,chars,i,len-i);422--i;423}424}425}426427if (chars[i] == '\n'){428chars[i] = ' ';429i -= 2;430}431432if (chars[i] == '\\') {433if (i+1 < len) {434if (chars[i+1] == 'n'){435chars[i+1] = ' ';436--len;437System.arraycopy(chars,i+1, chars,i, len-i);438--i;439}440}441}442}443return new String(chars,0,len);444}445446}447448449