/* * Copyright (c) 2012, 2013, 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 org.openjdk.tests.separate; import java.util.*; import java.io.StringWriter; import java.io.PrintWriter; public class SourceModel { public static final String stdMethodName = "m"; public static interface SourceProcessor { // Called with a generated source file void process(String name, String content); } public static abstract class Element { protected abstract void generate(PrintWriter pw); public String toString() { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); generate(pw); return sw.toString(); } } public static class AccessFlag extends Element { private String flag; public AccessFlag(String name) { flag = name; } protected void generate(PrintWriter pw) { pw.print(flag); } public String toString() { return flag; } public static final AccessFlag PUBLIC = new AccessFlag("public"); public static final AccessFlag PRIVATE = new AccessFlag("private"); public static final AccessFlag PROTECTED = new AccessFlag("protected"); public static final AccessFlag STATIC = new AccessFlag("static"); public static final AccessFlag FINAL = new AccessFlag("final"); public static final AccessFlag SYNCHRONIZED = new AccessFlag("synchronized"); public static final AccessFlag VOLATILE = new AccessFlag("volatile"); public static final AccessFlag NATIVE = new AccessFlag("native"); public static final AccessFlag ABSTRACT = new AccessFlag("abstract"); public static final AccessFlag STRICTFP = new AccessFlag("strictfp"); public static final AccessFlag DEFAULT = new AccessFlag("default"); } public static class TypeParameter extends Element { private String parameter; public TypeParameter(String str) { this.parameter = str; } protected void generate(PrintWriter pw) { pw.print(parameter); } } public static class TypeArgument extends Element { private String argument; public TypeArgument(String str) { this.argument = str; } protected void generate(PrintWriter pw) { pw.print(argument); } } public static class MethodParameter extends Element { private String type; private String name; public MethodParameter(String type, String name) { this.type = type; this.name = name; } protected void generate(PrintWriter pw) { pw.printf("%s %s", this.type, this.name); } public String toString() { return type + " " + name; } } public static abstract class Type extends Element { private String name; private List<AccessFlag> accessFlags; private List<TypeParameter> parameters; private List<Extends> supertypes; private List<Method> methods; // methods from superclasses that are required for compilation // (and thus will be present in stubs) private Set<Method> methodDependencies; private List<Type> typeDependencies; private boolean fullCompilation; protected Type(String name, List<AccessFlag> flags, List<TypeParameter> params, List<Extends> ifaces, List<Method> methods) { this.name = name; this.accessFlags = flags == null ? new ArrayList<>() : flags; this.parameters = params == null ? new ArrayList<>() : params; this.supertypes = ifaces == null ? new ArrayList<>() : ifaces; this.methods = methods == null ? new ArrayList<>() : methods; this.methodDependencies = new HashSet<>(); this.typeDependencies = new ArrayList<>(); } public String getName() { return this.name; } public List<AccessFlag> getAccessFlags() { return this.accessFlags; } public List<TypeParameter> getParameters() { return this.parameters; } public List<Extends> getSupertypes() { return this.supertypes; } public List<Method> getMethods() { return this.methods; } public Set<Method> methodDependencies() { return this.methodDependencies; } public Class getSuperclass() { return null; } protected abstract void setSuperClass(Extends supertype); public void addSuperType(Extends sup) { assert sup.getType() instanceof Interface : "Must be an interface"; this.supertypes.add(sup); } public void addSuperType(Interface iface) { this.supertypes.add(new Extends(iface)); } public void addMethod(Method m) { this.methods.add(m); } public void addAccessFlag(AccessFlag f) { this.accessFlags.add(f); } // Convenience method for creation. Parameters are interpreted // according to their type. Class (or Extends with a Class type) is // considered a superclass (only one allowed). TypeParameters are // generic parameter names. Interface (or Extends with an Interface // type) is an implemented supertype. Methods are methods (duh!). protected void addComponent(Element p) { if (p instanceof Class) { setSuperClass(new Extends((Class)p)); } else if (p instanceof Extends) { Extends ext = (Extends)p; if (ext.supertype instanceof Class) { setSuperClass(ext); } else if (ext.supertype instanceof Interface) { addSuperType(ext); } else { assert false : "What is this thing?"; } } else if (p instanceof Interface) { addSuperType((Interface)p); } else if (p instanceof TypeParameter) { this.parameters.add((TypeParameter)p); } else if (p instanceof Method) { addMethod((Method)p); } else if (p instanceof AccessFlag) { addAccessFlag((AccessFlag)p); } else { assert false : "What is this thing?"; } } // Find and return the first method that has name 'name' public Method findMethod(String name) { for (Method m : methods) { if (m.name.equals(name)) { return m; } } return null; } public void addCompilationDependency(Type t) { typeDependencies.add(t); } public void addCompilationDependency(Method m) { methodDependencies.add(m); } public boolean isFullCompilation() { return fullCompilation; } public void setFullCompilation(boolean fullCompilation) { this.fullCompilation = fullCompilation; } // Convenience method for creating an Extends object using this // class and specified type arguments. public Extends with(String ... args) { return new Extends(this, args); } public abstract void generate(SourceProcessor sp); public abstract void generateAsDependency( SourceProcessor sp, Set<Method> neededMethods); protected void generateName(PrintWriter pw) { pw.print(this.name); toJoinedString(this.parameters, ",", "<", ">", ""); pw.print(toJoinedString(this.parameters, ",", "<", ">", "")); pw.print(" "); } protected void generateBody(PrintWriter pw, String superSpec) { pw.print(toJoinedString(this.supertypes, ",", superSpec + " ", " ", "")); pw.println("{ "); pw.print(toJoinedString(this.methods, "\n ", "\n ", "\n", "")); pw.println("}"); } protected void generateAccessFlags(PrintWriter pw) { pw.print(toJoinedString(this.accessFlags, " ", "", " ")); } protected void generateBodyAsDependency( PrintWriter pw, Set<Method> neededMethods) { pw.println(" {"); for (Method m : this.methods) { if (neededMethods.contains(m)) { pw.print(" "); m.generate(pw); pw.println(); } } pw.println("}"); } public Collection<Type> typeDependencies(boolean recursive) { HashMap<String,Type> dependencies = new HashMap<>(); Type superclass = getSuperclass(); if (superclass != null) { dependencies.put(superclass.getName(), superclass); if (recursive) { for (Type t : superclass.typeDependencies(true)) dependencies.put(t.getName(), t); } } for (Extends e : getSupertypes()) { dependencies.put(e.getType().getName(), e.getType()); if (recursive) { for (Type t : e.getType().typeDependencies(true)) dependencies.put(t.getName(), t); } } // Do these last so that they override for (Type t : this.typeDependencies) dependencies.put(t.getName(), t); return dependencies.values(); } } public static class Class extends Type { private Extends superClass; public Class(String name, List<AccessFlag> flags, List<TypeParameter> params, Extends sprClass, List<Extends> interfaces, List<Method> methods) { super(name, flags, params, interfaces, methods); this.superClass = sprClass; addAccessFlag(AccessFlag.PUBLIC); // should remove this } public Class(String name, Element ... components) { super(name, null, null, null, null); this.superClass = null; for (Element p : components) { addComponent(p); } addAccessFlag(AccessFlag.PUBLIC); // should remove this } public boolean isAbstract() { for (AccessFlag flag : getAccessFlags()) { if (flag == AccessFlag.ABSTRACT) { return true; } } return false; } @Override public void setSuperClass(Extends ext) { assert this.superClass == null : "Multiple superclasses defined"; assert ext.getType() instanceof Class : "Must be a class"; this.superClass = ext; } public void setSuperClass(Class c) { setSuperClass(new Extends(c)); } @Override public Class getSuperclass() { return superClass == null ? null : (Class)superClass.supertype; } public void generate(SourceProcessor processor) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); generate(pw); processor.process(getName(), sw.toString()); } public void generate(PrintWriter pw) { generateAccessFlags(pw); pw.print("class "); generateName(pw); if (superClass != null) { pw.print("extends "); superClass.generate(pw); pw.print(" "); } generateBody(pw, "implements"); } public void generateAsDependency( SourceProcessor processor, Set<Method> neededMethods) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); generateAccessFlags(pw); pw.print("class "); generateName(pw); pw.print(" "); generateBodyAsDependency(pw, neededMethods); processor.process(getName(), sw.toString()); } } public static class Interface extends Type { public Interface(String name, List<AccessFlag> flags, List<TypeParameter> params, List<Extends> interfaces, List<Method> methods) { super(name, flags, params, interfaces, methods); } public Interface(String name, Element ... components) { super(name, null, null, null, null); for (Element c : components) { addComponent(c); } } protected void setSuperClass(Extends ext) { assert false : "Interfaces cannot have Class supertypes"; } public void generate(SourceProcessor processor) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); generate(pw); processor.process(getName(), sw.toString()); } public void generate(PrintWriter pw) { generateAccessFlags(pw); pw.print("interface "); generateName(pw); pw.print(" "); generateBody(pw, "extends"); } public void generateAsDependency( SourceProcessor processor, Set<Method> neededMethods) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); generateAccessFlags(pw); pw.print("interface "); generateName(pw); pw.print(" "); generateBodyAsDependency(pw, neededMethods); processor.process(getName(), sw.toString()); } } /** * Represents a type extension that might contain type arguments */ public static class Extends extends Element { private final Type supertype; private final List<TypeArgument> arguments; public Type getType() { return supertype; } public List<TypeArgument> getArguments() { return arguments; } public Extends(Type supertype, String ... args) { assert supertype != null : "Null supertype"; this.supertype = supertype; this.arguments = new ArrayList<>(); for (String arg : args) { this.arguments.add(new TypeArgument(arg)); } } public void generate(PrintWriter pw) { pw.print(supertype.getName()); pw.print(toJoinedString(getArguments(), ",", "<", ">", "")); } } public static abstract class Method extends Element { private String name; private String returnType; private List<AccessFlag> accessFlags; private List<MethodParameter> parameters; private boolean emitSuppressWarnings; protected Method(String ret, String name, Element ... params) { this.name = name; this.returnType = ret; this.accessFlags = new ArrayList<>(); this.parameters = new ArrayList<>(); this.emitSuppressWarnings = false; for (Element e : params) { if (e instanceof MethodParameter) { this.parameters.add((MethodParameter) e); } else if (e instanceof AccessFlag) { this.accessFlags.add((AccessFlag) e); } } assert accessFlags.size() + parameters.size() == params.length : "Non method parameters or access flags in constructor"; } public String getName() { return this.name; } public String getReturnType() { return this.returnType; } public List<MethodParameter> getParameters() { return this.parameters; } public List<AccessFlag> getAccessFlags() { return this.accessFlags; } public Element[] getElements() { ArrayList<Element> elements = new ArrayList<>(); elements.addAll(getParameters()); elements.addAll(getAccessFlags()); return elements.toArray(new Element[0]); } public void suppressWarnings() { this.emitSuppressWarnings = true; } public void generateWarningSuppression(PrintWriter pw) { if (this.emitSuppressWarnings) { pw.printf("@SuppressWarnings(\"unchecked\")\n "); } } protected void generateDecl(PrintWriter pw) { generateWarningSuppression(pw); pw.print(toJoinedString(this.accessFlags, " ", "", " ")); pw.printf("%s %s(", returnType, name); pw.print(toJoinedString(parameters, ",")); pw.print(")"); } } public static class AbstractMethod extends Method { public AbstractMethod( String ret, String name, Element ... params) { super(ret, name, params); this.getAccessFlags().add(AccessFlag.ABSTRACT); } public void generate(PrintWriter pw) { generateDecl(pw); pw.print(";"); } public static AbstractMethod std() { return new AbstractMethod( "int", SourceModel.stdMethodName, AccessFlag.PUBLIC); } } public static class ConcreteMethod extends Method { protected String body; public ConcreteMethod(String ret, String name, String body, Element ... params) { super(ret, name, params); this.body = body; } public void generate(PrintWriter pw) { generateDecl(pw); pw.printf(" { %s }", this.body); } public static ConcreteMethod std(String value) { return new ConcreteMethod( "int", SourceModel.stdMethodName, "return " + value + ";", AccessFlag.PUBLIC); } } // When the default method flag gets moved into the traditional // access flags location, we can remove this class completely and // use a ConcreteMethod with an AccessFlag("default") in the constructor public static class DefaultMethod extends Method { protected String body; public DefaultMethod(String ret, String name, String body, Element ... params) { super(ret, name, params); this.body = body; this.getAccessFlags().add(AccessFlag.DEFAULT); } public void generate(PrintWriter pw) { generateDecl(pw); pw.printf(" { %s }", this.body); } public static DefaultMethod std(String value) { return new DefaultMethod( "int", SourceModel.stdMethodName, "return " + value + ";"); } } private static <T> String toJoinedString(List<T> list, String... p) { StringBuilder sb = new StringBuilder(); String sep = ""; String init = ""; String end = ""; String empty = null; switch (p.length) { case 4: empty = p[3]; /*fall-through*/ case 3: end = p[2]; /*fall-through*/ case 2: init = p[1]; /*fall-through*/ case 1: sep = p[0]; break; } if (empty != null && list.isEmpty()) { return empty; } else { sb.append(init); for (T x : list) { if (sb.length() != init.length()) { sb.append(sep); } sb.append(x.toString()); } sb.append(end); return sb.toString(); } } }