--- old/test/tools/javadoc/sampleapi/lib/sampleapi/generator/PackageGenerator.java Fri Jan 22 12:30:39 2016 +++ /dev/null Fri Jan 22 12:30:39 2016 @@ -1,832 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sampleapi.generator; - -import java.io.File; -import java.io.FileWriter; -import java.io.InputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Hashtable; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.ParserConfigurationException; -import org.xml.sax.SAXException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Text; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.sun.source.util.JavacTask; -import com.sun.tools.javac.api.JavacTool; -import com.sun.tools.javac.api.JavacTaskImpl; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Name; -import com.sun.tools.javac.util.Names; -import com.sun.tools.javac.util.List; -import com.sun.tools.javac.util.ListBuffer; -import com.sun.tools.javac.tree.TreeMaker; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.*; -import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.TypeTag; -import com.sun.tools.javac.code.TypeMetadata; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.code.Symtab; - -import sampleapi.util.*; -import sampleapi.SampleApi.Fault; - -public class PackageGenerator { - - String packageName; - String packageDirName; - - ArrayList topLevels; - Hashtable nameIndex; - Hashtable idBases; - Hashtable idAnnos; - - TreeMaker make; - Names names; - Symtab syms; - DocumentBuilderFactory factory; - Documentifier documentifier; - boolean fx = false; - - public PackageGenerator() { - JavacTool jt = JavacTool.create(); - JavacTask task = jt.getTask(null, null, null, null, null, null); - Context ctx = ((JavacTaskImpl)task).getContext(); - - make = TreeMaker.instance(ctx); - names = Names.instance(ctx); - syms = Symtab.instance(ctx); - factory = DocumentBuilderFactory.newInstance(); - - documentifier = Documentifier.instance(ctx); - } - - String dataSetName; - - public void processDataSet(String dsName) throws Fault { - dataSetName = dsName; - topLevels = new ArrayList<>(); - nameIndex = new Hashtable<>(); - idBases = new Hashtable<>(); - idAnnos = new Hashtable<>(); - - String dsPath = "res/xml/" + dsName + ".xml"; - - try { - InputStream is = getClass().getResourceAsStream("/" + dsPath); - if (is == null) - is = new FileInputStream(dsPath); - - DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.parse(is); - - Element rootElement = document.getDocumentElement(); - packageName = rootElement.getAttribute("package"); - fx = "fx".equals(rootElement.getAttribute("style")); - packageDirName = packageName.replace('.', '/'); - - // process nodes (toplevels) - NodeList nodeList = rootElement.getChildNodes(); - for (int i = 0; i < nodeList.getLength(); i++) { - Node node = nodeList.item(i); - - if (!(node instanceof Element)) - continue; - processTopLevel((Element)node); - } - } catch (ParserConfigurationException | SAXException | IOException e) { - throw new Fault("Error parsing dataset " + dsName); - } - - fx = false; - } - - public void generate(File outDir) throws Fault { - if (dataSetName == null) - throw new Fault("No Data Set processed"); - - try { - File pkgDir = new File(outDir, packageDirName); - pkgDir.mkdirs(); - - for (JCCompilationUnit decl : topLevels) { - JCClassDecl classDecl = (JCClassDecl)decl.getTypeDecls().get(0); - File outFile = new File(pkgDir, - classDecl.getSimpleName().toString() + ".java"); - FileWriter writer = new FileWriter(outFile); - writer.write(decl.toString()); - writer.flush(); - writer.close(); - } - - // package-info - File outFile = new File(pkgDir, "package-info.java"); - FileWriter writer = new FileWriter(outFile); - writer.write("/**\n"); - writer.write(documentifier.getDocGenerator().getPackageComment()); - writer.write("*/\n"); - writer.write("package " + packageName + ";"); - writer.flush(); - writer.close(); - } catch (IOException e) { - throw new Fault("Error writing output"); - } - } - - // levels' processing methods - - void processTopLevel(Element tlTag) { - String kind = tlTag.getTagName(); - - if (kind.equals("annodecl")) { - // decls stored separately, does not affect bases - String declId = tlTag.getAttribute("id"); - if (!declId.startsWith("@")) - declId = "@" + declId; - idAnnos.put(declId, processAnnoDecl(tlTag)); - return; - } - - ListBuffer[] bases = processBases(tlTag, null); - - for (JCTree base : bases[0]) { // [0] - bases namely - JCPackageDecl pkg = make.PackageDecl( - List.nil(), - make.QualIdent( - new Symbol.PackageSymbol( - names.fromString(packageName), - null))); - ListBuffer topLevelParts = new ListBuffer<>(); - topLevelParts.append(pkg); - topLevelParts.appendList(bases[1]); // [1] imports - topLevelParts.append(base); - - JCCompilationUnit topLevel = make.TopLevel(topLevelParts.toList()); - documentifier.documentify(topLevel, fx); - topLevels.add(topLevel); - } - } - - ListBuffer[] processBases(Element baseTag, Hashtable scope) { - String kind = baseTag.getTagName(); - String baseName = baseTag.getAttribute("basename"); - String typeParam = baseTag.getAttribute("tparam"); - String baseId = baseTag.getAttribute("id"); - - long kindFlag = 0; - switch (kind) { - case "class": - // no flags for class - break; - case "interface": - kindFlag |= Flags.INTERFACE; - break; - case "enum": - kindFlag |= Flags.ENUM; - break; - case "annotation": - kindFlag |= Flags.ANNOTATION | Flags.INTERFACE; - break; - } - - // go through other nodes; add modifiers to multiplier - NodeList nodes = baseTag.getChildNodes(); - ListBuffer bases = new ListBuffer<>(); - ListBuffer members = new ListBuffer<>(); - ListBuffer imports = new ListBuffer<>(); - JCExpression extType = null; - ListBuffer implTypes = new ListBuffer<>(); - SimpleMultiplier multiply = new SimpleMultiplier(); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - - if (!(node instanceof Element)) - continue; - - switch (((Element)node).getTagName()) { - case "modifier": - multiply.addAxis(((Element)node).getTextContent()); - break; - case "anno": - multiply.addAxis(((Element)node).getTextContent()); - break; - case "member": - // process members here - members.appendList(processMembers((Element)node, baseName, kind)); - break; - case "extend": - String classId = ((Element)node).getAttribute("id"); // this pkg - String classRef = ((Element)node).getAttribute("ref"); // external - if (classId.length() !=0 && - idBases.containsKey(classId)) { - // if have base, take methods from base members - JCClassDecl baseDecl = idBases.get(classId); - extType = make.Type( - getTypeByName( - baseDecl.getSimpleName().toString())); - members.appendList(processMethods(baseDecl.getMembers(), false)); - } else if (classRef.length() !=0) { - extType = make.Type(getTypeByName(classRef)); - } - break; - case "implement": - String interfaceId = ((Element)node).getAttribute("id"); - String interfaceRef = ((Element)node).getAttribute("ref"); - if (interfaceId.length() != 0 && - idBases.containsKey(interfaceId)) { - JCClassDecl baseDecl = idBases.get(interfaceId); - implTypes.add( - make.Type( - getTypeByName( - baseDecl.getSimpleName().toString()))); - members.appendList(processMethods(baseDecl.getMembers(), true)); - } else if (interfaceRef.length() != 0) { - implTypes.add(make.Type(getTypeByName(interfaceRef))); - } - break; - case "import": - imports.append( - make.Import( - make.Ident(names.fromString(((Element)node).getTextContent())), - false)); - } - } - - // process modifiers through multiplier - multiply.initIterator(); - while (multiply.hasNext()) { - ArrayList tuple = multiply.getNext(); - - long declFlags = kindFlag; - ListBuffer annos = new ListBuffer<>(); - for (String modifier : tuple) { - if (modifier.startsWith("@") && idAnnos.containsKey(modifier)) - annos.add(idAnnos.get(modifier)); // it's anno - else - declFlags |= getFlagByName(modifier); // it's modifier - } - - String declName = (scope == null) - ? getUniqName(baseName) - : baseName + getUniqIndex(scope, baseName); - JCClassDecl baseDecl = make.ClassDef( - make.Modifiers(declFlags, annos.toList()), - names.fromString(declName), - processTypeParams(typeParam), // type params - extType, // ext - implTypes.toList(), // impl - members.toList()); // members - - // fix constructors names - fixConstructorNames(baseDecl); - - bases.append(baseDecl); - - // for non-empty ids store first base occurence from multiplied sequence - if (baseId.length() != 0) { - idBases.put(baseId, baseDecl); - baseId = ""; - } - } - - return new ListBuffer[] { bases, imports }; - } - - List processTypeParams(String typeParams) { - - if (typeParams == null || typeParams.length() == 0) - return List.nil(); // empty - - String[] typeVarsArr = typeParams.split(","); - ListBuffer typeParamsDecls = new ListBuffer<>(); - - for (String typeVar : typeVarsArr) { - typeParamsDecls.add( - make.TypeParameter(names.fromString(typeVar), - List.nil())); - } - - return typeParamsDecls.toList(); - } - - ListBuffer processMembers(Element memberTag, String name, String kind) { - ListBuffer members = new ListBuffer<>(); - NodeList nodes = memberTag.getChildNodes(); - Hashtable scope = new Hashtable<>(); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - - if (!(node instanceof Element)) - continue; - - switch (((Element)node).getTagName()) { - case "field": - members.appendList(processFields((Element)node, scope)); - break; - case "serialfield": - members.append(processSerialFields((Element)node)); - break; - case "constant": - members.appendList(processConstants((Element)node, scope)); - break; - case "constructor": - members.appendList(processMethods((Element)node, scope, true, true)); - break; - case "method": - boolean needBody = kind.equals("class") || kind.equals("enum"); - members.appendList(processMethods((Element)node, scope, needBody, false)); - break; - case "class": - case "interface": - case "enum": - case "annotation": - members.appendList(processBases((Element)node, scope)[0]); - break; - } - } - - return members; - } - - ListBuffer processFields(Element fieldsNode, Hashtable scope) { - String kind = fieldsNode.getTagName(); - String baseName = fieldsNode.getAttribute("basename"); - - ListBuffer fields = new ListBuffer<>(); - NodeList nodes = fieldsNode.getChildNodes(); - SimpleMultiplier multiply = new SimpleMultiplier(); // for modifiers - String[] types = new String[] {}; - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - - if (!(node instanceof Element)) - continue; - - // parse type and modifiers - switch (((Element)node).getTagName()) { - case "modifier": - multiply.addAxis(((Element)node).getTextContent()); - break; - case "anno": - multiply.addAxis(((Element)node).getTextContent()); - case "type": - types = ((Element)node).getTextContent().split("\\|"); - break; - } - } - - // process through modifiers and types - multiply.initIterator(); - while (multiply.hasNext()) { - ArrayList tuple = multiply.getNext(); - - long declFlags = 0; - ListBuffer annos = new ListBuffer<>(); - for (String modifier : tuple) { - if (modifier.startsWith("@") && idAnnos.containsKey(modifier)) - annos.add(idAnnos.get(modifier)); // it's anno - else - declFlags |= getFlagByName(modifier); // it's modifier - } - - - for (String type : types) { - String declName = baseName + getUniqIndex(scope, baseName); - - Type initType = getTypeByName(type); - JCExpression initExpr = null; - if ((declFlags & Flags.STATIC) != 0) // static to be initialized - initExpr = make.Literal(initType.isPrimitive() ? - initType.getTag() : - TypeTag.BOT, - "String".equals(type) - ? new String("blah-blah-blah") - : new Integer(0)); - - JCVariableDecl fieldDecl = make.VarDef( - make.Modifiers(declFlags, annos.toList()), - names.fromString(declName), - make.Type(getTypeByName(type)), - initExpr); - - fields.append(fieldDecl); - } - } - - return fields; - } - - JCTree processSerialFields(Element sfNode) { - String baseName = sfNode.getAttribute("basename"); - String[] fieldTypes = sfNode.getTextContent().split(","); - - ListBuffer serialFields = new ListBuffer<>(); - Hashtable scope = new Hashtable<>(); - - for (String fType : fieldTypes) { - String fieldName = baseName + getUniqIndex(scope, baseName); - serialFields.add( - make.NewClass( - null, - null, - make.Type(getTypeByName("ObjectStreamField")), - List.from( - new JCTree.JCExpression[] { - make.Literal(fieldName), - make.Ident(names.fromString(fType + ".class")) - }), - null)); - } - - JCTree sfDecl = make.VarDef( - make.Modifiers( - Flags.PRIVATE | Flags.STATIC | Flags.FINAL), - names.fromString("serialPersistentFields"), - make.TypeArray( - make.Type(getTypeByName("ObjectStreamField"))), - make.NewArray( - null, - List.nil(), - serialFields.toList())); - - return sfDecl; - } - - ListBuffer processConstants(Element constNode, Hashtable scope) { - String baseName = constNode.getAttribute("basename"); - int count = 1; - try { - count = Integer.parseInt(constNode.getAttribute("count")); - } catch (Exception e) {} // nothing to do, will use count = 1 - - long declFlags = Flags.PUBLIC | Flags.STATIC | Flags.FINAL | Flags.ENUM; - ListBuffer fields = new ListBuffer<>(); - - for (int i = 0; i < count; i++) { - String declName = baseName + - ((count == 1) ? "" : getUniqIndex(scope, baseName)); - - JCVariableDecl constDecl = make.VarDef( - make.Modifiers(declFlags), - names.fromString(declName), - null, // no need for type in enum decl - null); // no init - - fields.append(constDecl); - } - return fields; - } - - ListBuffer processMethods(Element methodsNode, Hashtable scope, boolean needBody, boolean isConstructor) { - String kind = methodsNode.getTagName(); - String baseName = methodsNode.getAttribute("basename"); - String name = methodsNode.getAttribute("name"); - String methodTypeParam = methodsNode.getAttribute("tparam"); - - ListBuffer methods = new ListBuffer<>(); - NodeList nodes = methodsNode.getChildNodes(); - SimpleMultiplier multiply = new SimpleMultiplier(); // for modifiers - String[] types = new String[0]; - String[] params = new String[] { "none" }; // default - no params - ListBuffer throwTypes = new ListBuffer<>(); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - - if (!(node instanceof Element)) - continue; - - // parse type and modifiers - switch (((Element)node).getTagName()) { - case "modifier": - multiply.addAxis(((Element)node).getTextContent()); - break; - case "anno": - multiply.addAxis(((Element)node).getTextContent()); - break; - case "type": - types = ((Element)node).getTextContent().split("\\|"); - break; - case "param": - params = ((Element)node).getTextContent().split("\\|"); - break; - case "throw": - throwTypes.add( - getTypeByName(((Element)node).getTextContent())); - break; - - } - } - - // constructor? - if (isConstructor) { - baseName = "constructor"; - types = new String[] { "" }; - } - - // direct name not indexed - boolean isDirectName = false; - if (name.length() > 0) { - baseName = name; - isDirectName = true; - } - - // process through modifiers and types - multiply.initIterator(); - while (multiply.hasNext()) { - ArrayList tuple = multiply.getNext(); - - long declFlags = 0; - ListBuffer annos = new ListBuffer<>(); - for (String modifier : tuple) { - if (modifier.startsWith("@") && idAnnos.containsKey(modifier)) - annos.add(idAnnos.get(modifier)); // it's anno - else - declFlags |= getFlagByName(modifier); // it's modifier - } - - for (String type : types) { - String declName = baseName - + ((isConstructor || isDirectName) - ? "" : getUniqIndex(scope, baseName)); - - JCBlock body = null; - if (needBody && (declFlags & Flags.ABSTRACT) == 0) { // create body - List bodyStatements = List.nil(); - if (!type.equals("") && !type.equals("void")) { // create return statement - Type retType = getTypeByName(type); - bodyStatements = List.of( - make.Return( - make.Literal( - retType.isPrimitive() ? - retType.getTag() : - TypeTag.BOT, - new Integer(0)))); - } - body = make.Block(0, bodyStatements); - } - - // same method by different params (if they exist) - for (String param : params) { - - JCMethodDecl methodDecl = - make.MethodDef( - make.Modifiers(declFlags, annos.toList()), - names.fromString(declName), - isConstructor ? null : make.Type(getTypeByName(type)), - processTypeParams(methodTypeParam), // type params - null, // no receiver - processParams(param), // formal params - make.Types(throwTypes.toList()), // throws - body, - null); // no default value YET - - methods.append(methodDecl); - } - } - } - - return methods; - } - - JCAnnotation processAnnoDecl(Element annoDeclNode) { - String annoId = annoDeclNode.getAttribute("id"); - - ListBuffer args = new ListBuffer<>(); - String className = ""; - - NodeList nodes = annoDeclNode.getChildNodes(); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - - if (!(node instanceof Element)) - continue; - - switch (((Element)node).getTagName()) { - case "class": - className = ((Element)node).getTextContent(); - break; - case "arg": - String argName = ((Element)node).getAttribute("name"); - String argValue = ((Element)node).getAttribute("value"); - - JCExpression arg; - if (argName.length() == 0) - arg = make.Ident(names.fromString(argValue)); - else - arg = make.Assign( - make.Ident(names.fromString(argName)), - make.Ident(names.fromString(argValue))); - - args.add(arg); - break; - } - } - - return make.Annotation( - make.Ident(names.fromString(className)), - args.toList()); - } - - ListBuffer processMethods(List tree, boolean needBody) { - // for "extends" clause; returns methods only - ListBuffer methods = new ListBuffer<>(); - for (JCTree memberDecl : tree) { - if (memberDecl instanceof JCMethodDecl) { - JCMethodDecl methodDecl = (JCMethodDecl)memberDecl; - JCTree retTypeTree = methodDecl.getReturnType(); - - // skip constructors - if (retTypeTree == null) - continue; - - if (needBody) { - // here we need to 'implement' interface declared methods - Type retType = retTypeTree.type; - - List bodyStatements = List.nil(); - if (retType.getTag() != TypeTag.VOID) - bodyStatements = List.of( - make.Return( - make.Literal( - retType.isPrimitive() ? - retType.getTag() : - TypeTag.BOT, - new Integer(0)))); - - JCBlock body = make.Block(0, bodyStatements); - - methodDecl = make.MethodDef( - methodDecl.getModifiers(), - methodDecl.getName(), - (JCExpression)methodDecl.getReturnType(), - methodDecl.getTypeParameters(), - methodDecl.getReceiverParameter(), - methodDecl.getParameters(), - methodDecl.getThrows(), - body, - (JCExpression)methodDecl.getDefaultValue()); - } - - methods.add(methodDecl); - } - } - return methods; - } - - void fixConstructorNames(JCClassDecl baseDecl) { - ListBuffer newMembers = new ListBuffer<>(); - List members = baseDecl.getMembers(); - Name name = baseDecl.getSimpleName(); - - for (JCTree memberDecl : members) { - JCTree newDecl = memberDecl; - - if (memberDecl instanceof JCMethodDecl) { - JCMethodDecl methodDecl = (JCMethodDecl)memberDecl; - JCTree retTypeTree = methodDecl.getReturnType(); - - if (retTypeTree == null) - newDecl = make.MethodDef( - methodDecl.getModifiers(), - name, - (JCExpression)methodDecl.getReturnType(), - methodDecl.getTypeParameters(), - methodDecl.getReceiverParameter(), - methodDecl.getParameters(), - methodDecl.getThrows(), - methodDecl.getBody(), - (JCExpression)methodDecl.getDefaultValue()); - } - - newMembers.add(newDecl); - } - - baseDecl.defs = newMembers.toList(); - } - - List processParams(String paramTypes) { - - if ("none".equals(paramTypes)) - return List.nil(); // empty - - String[] typesArr = paramTypes.split(",(?!(\\w+,)*\\w+>)"); - ListBuffer paramsDecls = new ListBuffer<>(); - - int i = 0; - for (String typeName : typesArr) { - String paramName = "param" - + (typesArr.length == 1 ? "" : String.valueOf(i)); - paramsDecls.add( - make.VarDef(make.Modifiers(0), - names.fromString(paramName), - make.Type(getTypeByName(typeName)), - null)); - i++; - } - - return paramsDecls.toList(); - } - - // - // util methods - // - - String getUniqName(String name) { - if (!nameIndex.containsKey(name)) - nameIndex.put(name, new Integer(0)); - Integer index = nameIndex.get(name); - String uniqName = name + index; - nameIndex.put(name, index + 1); - return uniqName; - } - - int getUniqIndex(Hashtable scope, String name) { - if (!scope.containsKey(name)) - scope.put(name, new Integer(0)); - Integer index = scope.get(name); - scope.put(name, index + 1); - return index; - } - - long getFlagByName(String modifierName) { - switch (modifierName) { - case "public": - return Flags.PUBLIC; - case "private": - return Flags.PRIVATE; - case "protected": - return Flags.PROTECTED; - case "static": - return Flags.STATIC; - case "final": - return Flags.FINAL; - case "abstract": - return Flags.ABSTRACT; - case "strictfp": - return Flags.STRICTFP; - default: - return 0; - } - } - - Type getTypeByName(String typeName) { - //check for primitive types - switch (typeName) { - case "void": - return syms.voidType; - case "boolean": - return syms.booleanType; - case "byte": - return syms.byteType; - case "char": - return syms.charType; - case "double": - return syms.doubleType; - case "float": - return syms.floatType; - case "int": - return syms.intType; - case "long": - return syms.longType; - default: - return getTypeByName(typeName, List.nil()); - } - } - - Type getTypeByName(String typeName, List tparams) { - return new Type.ClassType( - Type.noType, - tparams, - new Symbol.ClassSymbol(0, names.fromString(typeName), null)); - } -} --- /dev/null Fri Jan 22 12:30:39 2016 +++ new/test/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/PackageGenerator.java Fri Jan 22 12:30:38 2016 @@ -0,0 +1,832 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sampleapi.generator; + +import java.io.File; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Hashtable; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; +import org.xml.sax.SAXException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Text; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.api.JavacTool; +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Name; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.TypeTag; +import com.sun.tools.javac.code.TypeMetadata; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symtab; + +import sampleapi.util.*; +import sampleapi.SampleApi.Fault; + +public class PackageGenerator { + + String packageName; + String packageDirName; + + ArrayList topLevels; + Hashtable nameIndex; + Hashtable idBases; + Hashtable idAnnos; + + TreeMaker make; + Names names; + Symtab syms; + DocumentBuilderFactory factory; + Documentifier documentifier; + boolean fx = false; + + public PackageGenerator() { + JavacTool jt = JavacTool.create(); + JavacTask task = jt.getTask(null, null, null, null, null, null); + Context ctx = ((JavacTaskImpl)task).getContext(); + + make = TreeMaker.instance(ctx); + names = Names.instance(ctx); + syms = Symtab.instance(ctx); + factory = DocumentBuilderFactory.newInstance(); + + documentifier = Documentifier.instance(ctx); + } + + String dataSetName; + + public void processDataSet(String dsName) throws Fault { + dataSetName = dsName; + topLevels = new ArrayList<>(); + nameIndex = new Hashtable<>(); + idBases = new Hashtable<>(); + idAnnos = new Hashtable<>(); + + String dsPath = "res/xml/" + dsName + ".xml"; + + try { + InputStream is = getClass().getResourceAsStream("/" + dsPath); + if (is == null) + is = new FileInputStream(dsPath); + + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(is); + + Element rootElement = document.getDocumentElement(); + packageName = rootElement.getAttribute("package"); + fx = "fx".equals(rootElement.getAttribute("style")); + packageDirName = packageName.replace('.', '/'); + + // process nodes (toplevels) + NodeList nodeList = rootElement.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + + if (!(node instanceof Element)) + continue; + processTopLevel((Element)node); + } + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new Fault("Error parsing dataset " + dsName); + } + + fx = false; + } + + public void generate(File outDir) throws Fault { + if (dataSetName == null) + throw new Fault("No Data Set processed"); + + try { + File pkgDir = new File(outDir, packageDirName); + pkgDir.mkdirs(); + + for (JCCompilationUnit decl : topLevels) { + JCClassDecl classDecl = (JCClassDecl)decl.getTypeDecls().get(0); + File outFile = new File(pkgDir, + classDecl.getSimpleName().toString() + ".java"); + FileWriter writer = new FileWriter(outFile); + writer.write(decl.toString()); + writer.flush(); + writer.close(); + } + + // package-info + File outFile = new File(pkgDir, "package-info.java"); + FileWriter writer = new FileWriter(outFile); + writer.write("/**\n"); + writer.write(documentifier.getDocGenerator().getPackageComment()); + writer.write("*/\n"); + writer.write("package " + packageName + ";"); + writer.flush(); + writer.close(); + } catch (IOException e) { + throw new Fault("Error writing output"); + } + } + + // levels' processing methods + + void processTopLevel(Element tlTag) { + String kind = tlTag.getTagName(); + + if (kind.equals("annodecl")) { + // decls stored separately, does not affect bases + String declId = tlTag.getAttribute("id"); + if (!declId.startsWith("@")) + declId = "@" + declId; + idAnnos.put(declId, processAnnoDecl(tlTag)); + return; + } + + ListBuffer[] bases = processBases(tlTag, null); + + for (JCTree base : bases[0]) { // [0] - bases namely + JCPackageDecl pkg = make.PackageDecl( + List.nil(), + make.QualIdent( + new Symbol.PackageSymbol( + names.fromString(packageName), + null))); + ListBuffer topLevelParts = new ListBuffer<>(); + topLevelParts.append(pkg); + topLevelParts.appendList(bases[1]); // [1] imports + topLevelParts.append(base); + + JCCompilationUnit topLevel = make.TopLevel(topLevelParts.toList()); + documentifier.documentify(topLevel, fx); + topLevels.add(topLevel); + } + } + + ListBuffer[] processBases(Element baseTag, Hashtable scope) { + String kind = baseTag.getTagName(); + String baseName = baseTag.getAttribute("basename"); + String typeParam = baseTag.getAttribute("tparam"); + String baseId = baseTag.getAttribute("id"); + + long kindFlag = 0; + switch (kind) { + case "class": + // no flags for class + break; + case "interface": + kindFlag |= Flags.INTERFACE; + break; + case "enum": + kindFlag |= Flags.ENUM; + break; + case "annotation": + kindFlag |= Flags.ANNOTATION | Flags.INTERFACE; + break; + } + + // go through other nodes; add modifiers to multiplier + NodeList nodes = baseTag.getChildNodes(); + ListBuffer bases = new ListBuffer<>(); + ListBuffer members = new ListBuffer<>(); + ListBuffer imports = new ListBuffer<>(); + JCExpression extType = null; + ListBuffer implTypes = new ListBuffer<>(); + SimpleMultiplier multiply = new SimpleMultiplier(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + + if (!(node instanceof Element)) + continue; + + switch (((Element)node).getTagName()) { + case "modifier": + multiply.addAxis(((Element)node).getTextContent()); + break; + case "anno": + multiply.addAxis(((Element)node).getTextContent()); + break; + case "member": + // process members here + members.appendList(processMembers((Element)node, baseName, kind)); + break; + case "extend": + String classId = ((Element)node).getAttribute("id"); // this pkg + String classRef = ((Element)node).getAttribute("ref"); // external + if (classId.length() !=0 && + idBases.containsKey(classId)) { + // if have base, take methods from base members + JCClassDecl baseDecl = idBases.get(classId); + extType = make.Type( + getTypeByName( + baseDecl.getSimpleName().toString())); + members.appendList(processMethods(baseDecl.getMembers(), false)); + } else if (classRef.length() !=0) { + extType = make.Type(getTypeByName(classRef)); + } + break; + case "implement": + String interfaceId = ((Element)node).getAttribute("id"); + String interfaceRef = ((Element)node).getAttribute("ref"); + if (interfaceId.length() != 0 && + idBases.containsKey(interfaceId)) { + JCClassDecl baseDecl = idBases.get(interfaceId); + implTypes.add( + make.Type( + getTypeByName( + baseDecl.getSimpleName().toString()))); + members.appendList(processMethods(baseDecl.getMembers(), true)); + } else if (interfaceRef.length() != 0) { + implTypes.add(make.Type(getTypeByName(interfaceRef))); + } + break; + case "import": + imports.append( + make.Import( + make.Ident(names.fromString(((Element)node).getTextContent())), + false)); + } + } + + // process modifiers through multiplier + multiply.initIterator(); + while (multiply.hasNext()) { + ArrayList tuple = multiply.getNext(); + + long declFlags = kindFlag; + ListBuffer annos = new ListBuffer<>(); + for (String modifier : tuple) { + if (modifier.startsWith("@") && idAnnos.containsKey(modifier)) + annos.add(idAnnos.get(modifier)); // it's anno + else + declFlags |= getFlagByName(modifier); // it's modifier + } + + String declName = (scope == null) + ? getUniqName(baseName) + : baseName + getUniqIndex(scope, baseName); + JCClassDecl baseDecl = make.ClassDef( + make.Modifiers(declFlags, annos.toList()), + names.fromString(declName), + processTypeParams(typeParam), // type params + extType, // ext + implTypes.toList(), // impl + members.toList()); // members + + // fix constructors names + fixConstructorNames(baseDecl); + + bases.append(baseDecl); + + // for non-empty ids store first base occurence from multiplied sequence + if (baseId.length() != 0) { + idBases.put(baseId, baseDecl); + baseId = ""; + } + } + + return new ListBuffer[] { bases, imports }; + } + + List processTypeParams(String typeParams) { + + if (typeParams == null || typeParams.length() == 0) + return List.nil(); // empty + + String[] typeVarsArr = typeParams.split(","); + ListBuffer typeParamsDecls = new ListBuffer<>(); + + for (String typeVar : typeVarsArr) { + typeParamsDecls.add( + make.TypeParameter(names.fromString(typeVar), + List.nil())); + } + + return typeParamsDecls.toList(); + } + + ListBuffer processMembers(Element memberTag, String name, String kind) { + ListBuffer members = new ListBuffer<>(); + NodeList nodes = memberTag.getChildNodes(); + Hashtable scope = new Hashtable<>(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + + if (!(node instanceof Element)) + continue; + + switch (((Element)node).getTagName()) { + case "field": + members.appendList(processFields((Element)node, scope)); + break; + case "serialfield": + members.append(processSerialFields((Element)node)); + break; + case "constant": + members.appendList(processConstants((Element)node, scope)); + break; + case "constructor": + members.appendList(processMethods((Element)node, scope, true, true)); + break; + case "method": + boolean needBody = kind.equals("class") || kind.equals("enum"); + members.appendList(processMethods((Element)node, scope, needBody, false)); + break; + case "class": + case "interface": + case "enum": + case "annotation": + members.appendList(processBases((Element)node, scope)[0]); + break; + } + } + + return members; + } + + ListBuffer processFields(Element fieldsNode, Hashtable scope) { + String kind = fieldsNode.getTagName(); + String baseName = fieldsNode.getAttribute("basename"); + + ListBuffer fields = new ListBuffer<>(); + NodeList nodes = fieldsNode.getChildNodes(); + SimpleMultiplier multiply = new SimpleMultiplier(); // for modifiers + String[] types = new String[] {}; + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + + if (!(node instanceof Element)) + continue; + + // parse type and modifiers + switch (((Element)node).getTagName()) { + case "modifier": + multiply.addAxis(((Element)node).getTextContent()); + break; + case "anno": + multiply.addAxis(((Element)node).getTextContent()); + case "type": + types = ((Element)node).getTextContent().split("\\|"); + break; + } + } + + // process through modifiers and types + multiply.initIterator(); + while (multiply.hasNext()) { + ArrayList tuple = multiply.getNext(); + + long declFlags = 0; + ListBuffer annos = new ListBuffer<>(); + for (String modifier : tuple) { + if (modifier.startsWith("@") && idAnnos.containsKey(modifier)) + annos.add(idAnnos.get(modifier)); // it's anno + else + declFlags |= getFlagByName(modifier); // it's modifier + } + + + for (String type : types) { + String declName = baseName + getUniqIndex(scope, baseName); + + Type initType = getTypeByName(type); + JCExpression initExpr = null; + if ((declFlags & Flags.STATIC) != 0) // static to be initialized + initExpr = make.Literal(initType.isPrimitive() ? + initType.getTag() : + TypeTag.BOT, + "String".equals(type) + ? new String("blah-blah-blah") + : new Integer(0)); + + JCVariableDecl fieldDecl = make.VarDef( + make.Modifiers(declFlags, annos.toList()), + names.fromString(declName), + make.Type(getTypeByName(type)), + initExpr); + + fields.append(fieldDecl); + } + } + + return fields; + } + + JCTree processSerialFields(Element sfNode) { + String baseName = sfNode.getAttribute("basename"); + String[] fieldTypes = sfNode.getTextContent().split(","); + + ListBuffer serialFields = new ListBuffer<>(); + Hashtable scope = new Hashtable<>(); + + for (String fType : fieldTypes) { + String fieldName = baseName + getUniqIndex(scope, baseName); + serialFields.add( + make.NewClass( + null, + null, + make.Type(getTypeByName("ObjectStreamField")), + List.from( + new JCTree.JCExpression[] { + make.Literal(fieldName), + make.Ident(names.fromString(fType + ".class")) + }), + null)); + } + + JCTree sfDecl = make.VarDef( + make.Modifiers( + Flags.PRIVATE | Flags.STATIC | Flags.FINAL), + names.fromString("serialPersistentFields"), + make.TypeArray( + make.Type(getTypeByName("ObjectStreamField"))), + make.NewArray( + null, + List.nil(), + serialFields.toList())); + + return sfDecl; + } + + ListBuffer processConstants(Element constNode, Hashtable scope) { + String baseName = constNode.getAttribute("basename"); + int count = 1; + try { + count = Integer.parseInt(constNode.getAttribute("count")); + } catch (Exception e) {} // nothing to do, will use count = 1 + + long declFlags = Flags.PUBLIC | Flags.STATIC | Flags.FINAL | Flags.ENUM; + ListBuffer fields = new ListBuffer<>(); + + for (int i = 0; i < count; i++) { + String declName = baseName + + ((count == 1) ? "" : getUniqIndex(scope, baseName)); + + JCVariableDecl constDecl = make.VarDef( + make.Modifiers(declFlags), + names.fromString(declName), + null, // no need for type in enum decl + null); // no init + + fields.append(constDecl); + } + return fields; + } + + ListBuffer processMethods(Element methodsNode, Hashtable scope, boolean needBody, boolean isConstructor) { + String kind = methodsNode.getTagName(); + String baseName = methodsNode.getAttribute("basename"); + String name = methodsNode.getAttribute("name"); + String methodTypeParam = methodsNode.getAttribute("tparam"); + + ListBuffer methods = new ListBuffer<>(); + NodeList nodes = methodsNode.getChildNodes(); + SimpleMultiplier multiply = new SimpleMultiplier(); // for modifiers + String[] types = new String[0]; + String[] params = new String[] { "none" }; // default - no params + ListBuffer throwTypes = new ListBuffer<>(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + + if (!(node instanceof Element)) + continue; + + // parse type and modifiers + switch (((Element)node).getTagName()) { + case "modifier": + multiply.addAxis(((Element)node).getTextContent()); + break; + case "anno": + multiply.addAxis(((Element)node).getTextContent()); + break; + case "type": + types = ((Element)node).getTextContent().split("\\|"); + break; + case "param": + params = ((Element)node).getTextContent().split("\\|"); + break; + case "throw": + throwTypes.add( + getTypeByName(((Element)node).getTextContent())); + break; + + } + } + + // constructor? + if (isConstructor) { + baseName = "constructor"; + types = new String[] { "" }; + } + + // direct name not indexed + boolean isDirectName = false; + if (name.length() > 0) { + baseName = name; + isDirectName = true; + } + + // process through modifiers and types + multiply.initIterator(); + while (multiply.hasNext()) { + ArrayList tuple = multiply.getNext(); + + long declFlags = 0; + ListBuffer annos = new ListBuffer<>(); + for (String modifier : tuple) { + if (modifier.startsWith("@") && idAnnos.containsKey(modifier)) + annos.add(idAnnos.get(modifier)); // it's anno + else + declFlags |= getFlagByName(modifier); // it's modifier + } + + for (String type : types) { + String declName = baseName + + ((isConstructor || isDirectName) + ? "" : getUniqIndex(scope, baseName)); + + JCBlock body = null; + if (needBody && (declFlags & Flags.ABSTRACT) == 0) { // create body + List bodyStatements = List.nil(); + if (!type.equals("") && !type.equals("void")) { // create return statement + Type retType = getTypeByName(type); + bodyStatements = List.of( + make.Return( + make.Literal( + retType.isPrimitive() ? + retType.getTag() : + TypeTag.BOT, + new Integer(0)))); + } + body = make.Block(0, bodyStatements); + } + + // same method by different params (if they exist) + for (String param : params) { + + JCMethodDecl methodDecl = + make.MethodDef( + make.Modifiers(declFlags, annos.toList()), + names.fromString(declName), + isConstructor ? null : make.Type(getTypeByName(type)), + processTypeParams(methodTypeParam), // type params + null, // no receiver + processParams(param), // formal params + make.Types(throwTypes.toList()), // throws + body, + null); // no default value YET + + methods.append(methodDecl); + } + } + } + + return methods; + } + + JCAnnotation processAnnoDecl(Element annoDeclNode) { + String annoId = annoDeclNode.getAttribute("id"); + + ListBuffer args = new ListBuffer<>(); + String className = ""; + + NodeList nodes = annoDeclNode.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + + if (!(node instanceof Element)) + continue; + + switch (((Element)node).getTagName()) { + case "class": + className = ((Element)node).getTextContent(); + break; + case "arg": + String argName = ((Element)node).getAttribute("name"); + String argValue = ((Element)node).getAttribute("value"); + + JCExpression arg; + if (argName.length() == 0) + arg = make.Ident(names.fromString(argValue)); + else + arg = make.Assign( + make.Ident(names.fromString(argName)), + make.Ident(names.fromString(argValue))); + + args.add(arg); + break; + } + } + + return make.Annotation( + make.Ident(names.fromString(className)), + args.toList()); + } + + ListBuffer processMethods(List tree, boolean needBody) { + // for "extends" clause; returns methods only + ListBuffer methods = new ListBuffer<>(); + for (JCTree memberDecl : tree) { + if (memberDecl instanceof JCMethodDecl) { + JCMethodDecl methodDecl = (JCMethodDecl)memberDecl; + JCTree retTypeTree = methodDecl.getReturnType(); + + // skip constructors + if (retTypeTree == null) + continue; + + if (needBody) { + // here we need to 'implement' interface declared methods + Type retType = retTypeTree.type; + + List bodyStatements = List.nil(); + if (retType.getTag() != TypeTag.VOID) + bodyStatements = List.of( + make.Return( + make.Literal( + retType.isPrimitive() ? + retType.getTag() : + TypeTag.BOT, + new Integer(0)))); + + JCBlock body = make.Block(0, bodyStatements); + + methodDecl = make.MethodDef( + methodDecl.getModifiers(), + methodDecl.getName(), + (JCExpression)methodDecl.getReturnType(), + methodDecl.getTypeParameters(), + methodDecl.getReceiverParameter(), + methodDecl.getParameters(), + methodDecl.getThrows(), + body, + (JCExpression)methodDecl.getDefaultValue()); + } + + methods.add(methodDecl); + } + } + return methods; + } + + void fixConstructorNames(JCClassDecl baseDecl) { + ListBuffer newMembers = new ListBuffer<>(); + List members = baseDecl.getMembers(); + Name name = baseDecl.getSimpleName(); + + for (JCTree memberDecl : members) { + JCTree newDecl = memberDecl; + + if (memberDecl instanceof JCMethodDecl) { + JCMethodDecl methodDecl = (JCMethodDecl)memberDecl; + JCTree retTypeTree = methodDecl.getReturnType(); + + if (retTypeTree == null) + newDecl = make.MethodDef( + methodDecl.getModifiers(), + name, + (JCExpression)methodDecl.getReturnType(), + methodDecl.getTypeParameters(), + methodDecl.getReceiverParameter(), + methodDecl.getParameters(), + methodDecl.getThrows(), + methodDecl.getBody(), + (JCExpression)methodDecl.getDefaultValue()); + } + + newMembers.add(newDecl); + } + + baseDecl.defs = newMembers.toList(); + } + + List processParams(String paramTypes) { + + if ("none".equals(paramTypes)) + return List.nil(); // empty + + String[] typesArr = paramTypes.split(",(?!(\\w+,)*\\w+>)"); + ListBuffer paramsDecls = new ListBuffer<>(); + + int i = 0; + for (String typeName : typesArr) { + String paramName = "param" + + (typesArr.length == 1 ? "" : String.valueOf(i)); + paramsDecls.add( + make.VarDef(make.Modifiers(0), + names.fromString(paramName), + make.Type(getTypeByName(typeName)), + null)); + i++; + } + + return paramsDecls.toList(); + } + + // + // util methods + // + + String getUniqName(String name) { + if (!nameIndex.containsKey(name)) + nameIndex.put(name, new Integer(0)); + Integer index = nameIndex.get(name); + String uniqName = name + index; + nameIndex.put(name, index + 1); + return uniqName; + } + + int getUniqIndex(Hashtable scope, String name) { + if (!scope.containsKey(name)) + scope.put(name, new Integer(0)); + Integer index = scope.get(name); + scope.put(name, index + 1); + return index; + } + + long getFlagByName(String modifierName) { + switch (modifierName) { + case "public": + return Flags.PUBLIC; + case "private": + return Flags.PRIVATE; + case "protected": + return Flags.PROTECTED; + case "static": + return Flags.STATIC; + case "final": + return Flags.FINAL; + case "abstract": + return Flags.ABSTRACT; + case "strictfp": + return Flags.STRICTFP; + default: + return 0; + } + } + + Type getTypeByName(String typeName) { + //check for primitive types + switch (typeName) { + case "void": + return syms.voidType; + case "boolean": + return syms.booleanType; + case "byte": + return syms.byteType; + case "char": + return syms.charType; + case "double": + return syms.doubleType; + case "float": + return syms.floatType; + case "int": + return syms.intType; + case "long": + return syms.longType; + default: + return getTypeByName(typeName, List.nil()); + } + } + + Type getTypeByName(String typeName, List tparams) { + return new Type.ClassType( + Type.noType, + tparams, + new Symbol.ClassSymbol(0, names.fromString(typeName), null)); + } +}