1 /*
   2  * Copyright (c) 2012 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 org.openjdk.tests.separate;
  27 
  28 import java.util.*;
  29 import java.io.StringWriter;
  30 import java.io.PrintWriter;
  31 
  32 public class SourceModel {
  33 
  34     public static final String stdMethodName = "m";
  35 
  36     public static interface SourceProcessor {
  37         // Called with a generated source file
  38         void process(String name, String content);
  39     }
  40 
  41     public static abstract class Element {
  42 
  43         protected abstract void generate(PrintWriter pw);
  44 
  45         public String toString() {
  46             StringWriter sw = new StringWriter();
  47             PrintWriter pw = new PrintWriter(sw);
  48             generate(pw);
  49             return sw.toString();
  50         }
  51     };
  52 
  53     public static class AccessFlag extends Element {
  54         private String flag;
  55 
  56         public AccessFlag(String name) { flag = name; }
  57 
  58         protected void generate(PrintWriter pw) {
  59             pw.print(flag);
  60         }
  61 
  62         public String toString() { return flag; }
  63 
  64         public static final AccessFlag PUBLIC = new AccessFlag("public");
  65         public static final AccessFlag PRIVATE = new AccessFlag("private");
  66         public static final AccessFlag PROTECTED = new AccessFlag("protected");
  67         public static final AccessFlag STATIC = new AccessFlag("static");
  68         public static final AccessFlag FINAL = new AccessFlag("final");
  69         public static final AccessFlag SYNCHRONIZED = new AccessFlag("synchronized");
  70         public static final AccessFlag VOLATILE = new AccessFlag("volatile");
  71         public static final AccessFlag NATIVE = new AccessFlag("native");
  72         public static final AccessFlag ABSTRACT = new AccessFlag("abstract");
  73         public static final AccessFlag STRICTFP = new AccessFlag("strictfp");
  74         public static final AccessFlag DEFAULT = new AccessFlag("default");
  75     }
  76 
  77     public static class TypeParameter extends Element {
  78         private String parameter;
  79 
  80         public TypeParameter(String str) {
  81             this.parameter = str;
  82         }
  83 
  84         protected void generate(PrintWriter pw) {
  85             pw.print(parameter);
  86         }
  87     }
  88 
  89     public static class TypeArgument extends Element {
  90         private String argument;
  91 
  92         public TypeArgument(String str) {
  93             this.argument = str;
  94         }
  95 
  96         protected void generate(PrintWriter pw) {
  97             pw.print(argument);
  98         }
  99     }
 100 
 101     public static class MethodParameter extends Element {
 102         private String type;
 103         private String name;
 104 
 105         public MethodParameter(String type, String name) {
 106             this.type = type;
 107             this.name = name;
 108         }
 109 
 110         protected void generate(PrintWriter pw) {
 111             pw.printf("%s %s", this.type, this.name);
 112         }
 113 
 114         public String toString() { return type + " " + name; }
 115     }
 116 
 117     public static abstract class Type extends Element {
 118         private String name;
 119         private List<AccessFlag> accessFlags;
 120         private List<TypeParameter> parameters;
 121         private List<Extends> supertypes;
 122         private List<Method> methods;
 123 
 124         // methods from superclasses that are required for compilation
 125         // (and thus will be present in stubs)
 126         private Set<Method> methodDependencies;
 127         private List<Type> typeDependencies;
 128 
 129         protected Type(String name,
 130                 List<AccessFlag> flags, List<TypeParameter> params,
 131                 List<Extends> ifaces, List<Method> methods) {
 132             this.name = name;
 133             this.accessFlags = flags == null ? new ArrayList<>() : flags;
 134             this.parameters = params == null ? new ArrayList<>() : params;
 135             this.supertypes = ifaces == null ? new ArrayList<>() : ifaces;
 136             this.methods = methods == null ? new ArrayList<>() : methods;
 137             this.methodDependencies = new HashSet<>();
 138             this.typeDependencies = new ArrayList<>();
 139         }
 140 
 141         public String getName() { return this.name; }
 142         public List<AccessFlag> getAccessFlags() { return this.accessFlags; }
 143         public List<TypeParameter> getParameters() { return this.parameters; }
 144         public List<Extends> getSupertypes() { return this.supertypes; }
 145         public List<Method> getMethods() { return this.methods; }
 146         public Set<Method> methodDependencies() {
 147             return this.methodDependencies;
 148         }
 149 
 150         public Class getSuperclass() { return null; }
 151         protected abstract void setSuperClass(Extends supertype);
 152 
 153         public void addSuperType(Extends sup) {
 154             assert sup.getType() instanceof Interface : "Must be an interface";
 155             this.supertypes.add(sup);
 156         }
 157         public void addSuperType(Interface iface) {
 158             this.supertypes.add(new Extends(iface));
 159         }
 160 
 161         public void addMethod(Method m) {
 162             this.methods.add(m);
 163         }
 164 
 165         public void addAccessFlag(AccessFlag f) {
 166             this.accessFlags.add(f);
 167         }
 168 
 169         // Convenience method for creation.  Parameters are interpreted
 170         // according to their type.  Class (or Extends with a Class type) is
 171         // considered a superclass (only one allowed).  TypeParameters are
 172         // generic parameter names.  Interface (or Extends with an Interface
 173         // type) is an implemented supertype.  Methods are methods (duh!).
 174         protected void addComponent(Element p) {
 175             if (p instanceof Class) {
 176                 setSuperClass(new Extends((Class)p));
 177             } else if (p instanceof Extends) {
 178                 Extends ext = (Extends)p;
 179                 if (ext.supertype instanceof Class) {
 180                     setSuperClass(ext);
 181                 } else if (ext.supertype instanceof Interface) {
 182                     addSuperType(ext);
 183                 } else {
 184                     assert false : "What is this thing?";
 185                 }
 186             } else if (p instanceof Interface) {
 187                 addSuperType((Interface)p);
 188             } else if (p instanceof TypeParameter) {
 189                 this.parameters.add((TypeParameter)p);
 190             } else if (p instanceof Method) {
 191                 addMethod((Method)p);
 192             } else if (p instanceof AccessFlag) {
 193                 addAccessFlag((AccessFlag)p);
 194             } else {
 195                 assert false : "What is this thing?";
 196             }
 197         }
 198 
 199         // Find and return the first method that has name 'name'
 200         public Method findMethod(String name) {
 201             for (Method m : methods) {
 202                 if (m.name.equals(name)) {
 203                     return m;
 204                 }
 205             }
 206             return null;
 207         }
 208 
 209         public void addCompilationDependency(Type t) {
 210             typeDependencies.add(t);
 211         }
 212 
 213         public void addCompilationDependency(Method m) {
 214             methodDependencies.add(m);
 215         }
 216 
 217         // Convenience method for creating an Extends object using this
 218         // class and specified type arguments.
 219         public Extends with(String ... args) {
 220             return new Extends(this, args);
 221         }
 222 
 223         public abstract void generate(SourceProcessor sp);
 224         public abstract void generateAsDependency(
 225             SourceProcessor sp, Set<Method> neededMethods);
 226 
 227         protected void generateName(PrintWriter pw) {
 228             pw.print(this.name);
 229             StringJoiner joiner = new StringJoiner(",", "<", ">").setEmptyValue("");
 230             this.parameters.stream().map(Element::toString).forEach(joiner::add);
 231             pw.print(joiner.toString());
 232             pw.print(" ");
 233         }
 234 
 235         protected void generateBody(PrintWriter pw, String superSpec) {
 236             StringJoiner joiner = new StringJoiner(",", superSpec + " ", " ")
 237                     .setEmptyValue("");
 238             supertypes.stream().map(Element::toString).forEach(joiner::add);
 239             pw.print(joiner.toString());
 240             pw.println("{ ");
 241             joiner = new StringJoiner("\n    ", "\n    ", "\n").setEmptyValue("");
 242             methods.stream().map(Element::toString).forEach(joiner::add);
 243             pw.print(joiner.toString());
 244             pw.println("}");
 245         }
 246 
 247         protected void generateAccessFlags(PrintWriter pw) {
 248             StringJoiner joiner = new StringJoiner(" ", "", " ");
 249             accessFlags.stream().map(AccessFlag::toString).forEach(joiner::add);
 250             pw.print(joiner.toString());
 251         }
 252 
 253         protected void generateBodyAsDependency(
 254             PrintWriter pw, Set<Method> neededMethods) {
 255             pw.println(" {");
 256             for (Method m : this.methods) {
 257                 if (neededMethods.contains(m)) {
 258                     pw.print("    ");
 259                     m.generate(pw);
 260                     pw.println();
 261                 }
 262             }
 263             pw.println("}");
 264         }
 265 
 266         public Collection<Type> typeDependencies() {
 267             HashMap<String,Type> dependencies = new HashMap<>();
 268             Type superclass = getSuperclass();
 269             if (superclass != null) {
 270                 dependencies.put(superclass.getName(), superclass);
 271             }
 272             for (Extends e : getSupertypes())
 273                 dependencies.put(e.getType().getName(), e.getType());
 274             // Do these last so that they override
 275             for (Type t : this.typeDependencies)
 276                 dependencies.put(t.getName(), t);
 277             return dependencies.values();
 278         }
 279     }
 280 
 281     public static class Class extends Type {
 282         private Extends superClass;
 283 
 284         public Class(String name, List<AccessFlag> flags,
 285                 List<TypeParameter> params, Extends sprClass,
 286                 List<Extends> interfaces, List<Method> methods) {
 287             super(name, flags, params, interfaces, methods);
 288             this.superClass = sprClass;
 289             addAccessFlag(AccessFlag.PUBLIC); // should remove this
 290         }
 291 
 292         public Class(String name, Element ... components) {
 293             super(name, null, null, null, null);
 294             this.superClass = null;
 295 
 296             for (Element p : components) {
 297                 addComponent(p);
 298             }
 299             addAccessFlag(AccessFlag.PUBLIC); // should remove this
 300         }
 301 
 302         public boolean isAbstract() {
 303             for (AccessFlag flag : getAccessFlags()) {
 304                 if (flag == AccessFlag.ABSTRACT) {
 305                     return true;
 306                 }
 307             }
 308             return false;
 309         }
 310 
 311         @Override
 312         public void setSuperClass(Extends ext) {
 313             assert this.superClass == null : "Multiple superclasses defined";
 314             assert ext.getType() instanceof Class : "Must be a class";
 315             this.superClass = ext;
 316         }
 317 
 318         public void setSuperClass(Class c) {
 319             setSuperClass(new Extends(c));
 320         }
 321 
 322         @Override
 323         public Class getSuperclass() {
 324             return superClass == null ? null : (Class)superClass.supertype;
 325         }
 326 
 327         public void generate(SourceProcessor processor) {
 328             StringWriter sw = new StringWriter();
 329             PrintWriter pw = new PrintWriter(sw);
 330             generate(pw);
 331             processor.process(getName(), sw.toString());
 332         }
 333 
 334         public void generate(PrintWriter pw) {
 335             generateAccessFlags(pw);
 336             pw.print("class ");
 337             generateName(pw);
 338             if (superClass != null) {
 339                 pw.print("extends ");
 340                 superClass.generate(pw);
 341                 pw.print(" ");
 342             }
 343             generateBody(pw, "implements");
 344         }
 345 
 346         public void generateAsDependency(
 347                 SourceProcessor processor, Set<Method> neededMethods) {
 348             StringWriter sw = new StringWriter();
 349             PrintWriter pw = new PrintWriter(sw);
 350             generateAccessFlags(pw);
 351             pw.print("class ");
 352             generateName(pw);
 353             pw.print(" ");
 354             generateBodyAsDependency(pw, neededMethods);
 355 
 356             processor.process(getName(), sw.toString());
 357         }
 358     }
 359 
 360     public static class Interface extends Type {
 361 
 362         public Interface(String name,
 363                   List<AccessFlag> flags, List<TypeParameter> params,
 364                   List<Extends> interfaces, List<Method> methods) {
 365             super(name, flags, params, interfaces, methods);
 366         }
 367 
 368         public Interface(String name, Element ... components) {
 369             super(name, null, null, null, null);
 370             for (Element c : components) {
 371                 addComponent(c);
 372             }
 373         }
 374 
 375         protected void setSuperClass(Extends ext) {
 376             assert false : "Interfaces cannot have Class supertypes";
 377         }
 378 
 379         public void generate(SourceProcessor processor) {
 380             StringWriter sw = new StringWriter();
 381             PrintWriter pw = new PrintWriter(sw);
 382             generate(pw);
 383             processor.process(getName(), sw.toString());
 384         }
 385 
 386         public void generate(PrintWriter pw) {
 387             generateAccessFlags(pw);
 388             pw.print("interface ");
 389             generateName(pw);
 390             pw.print(" ");
 391             generateBody(pw, "extends");
 392         }
 393 
 394         public void generateAsDependency(
 395                 SourceProcessor processor, Set<Method> neededMethods) {
 396             StringWriter sw = new StringWriter();
 397             PrintWriter pw = new PrintWriter(sw);
 398 
 399             generateAccessFlags(pw);
 400             pw.print("interface ");
 401             generateName(pw);
 402             pw.print(" ");
 403             generateBodyAsDependency(pw, neededMethods);
 404 
 405             processor.process(getName(), sw.toString());
 406         }
 407     }
 408 
 409     /**
 410      * Represents a type extension that might contain type arguments
 411      */
 412     public static class Extends extends Element {
 413         private final Type supertype;
 414         private final List<TypeArgument> arguments;
 415 
 416         public Type getType() { return supertype; }
 417         public List<TypeArgument> getArguments() {
 418             return arguments;
 419         }
 420 
 421         public Extends(Type supertype, String ... args) {
 422             assert supertype != null : "Null supertype";
 423             this.supertype = supertype;
 424             this.arguments = new ArrayList<>();
 425             for (String arg : args) {
 426                 this.arguments.add(new TypeArgument(arg));
 427             }
 428         }
 429 
 430         public void generate(PrintWriter pw) {
 431             StringJoiner joiner = new StringJoiner(",", "<", ">").setEmptyValue("");
 432             getArguments().stream().map(Element::toString).forEach(joiner::add);
 433             pw.print(supertype.getName());
 434             pw.print(joiner.toString());
 435         }
 436     }
 437 
 438     public static abstract class Method extends Element {
 439         private String name;
 440         private String returnType;
 441         private List<AccessFlag> accessFlags;
 442         private List<MethodParameter> parameters;
 443         private boolean emitSuppressWarnings;
 444 
 445         protected Method(String ret, String name, Element ... params) {
 446             this.name = name;
 447             this.returnType = ret;
 448             this.accessFlags = new ArrayList<>();
 449             this.parameters = new ArrayList<>();
 450             this.emitSuppressWarnings = false;
 451 
 452             Arrays.asList(params).stream()
 453                 .filter(x -> x instanceof MethodParameter)
 454                 .map(x -> (MethodParameter)x)
 455                 .forEach(this.parameters::add);
 456             Arrays.asList(params).stream()
 457                 .filter(x -> x instanceof AccessFlag)
 458                 .map(x -> (AccessFlag)x)
 459                 .forEach(this.accessFlags::add);
 460             assert accessFlags.size() + parameters.size() == params.length :
 461                    "Non method parameters or access flags in constructor";
 462         }
 463 
 464         public String getName() { return this.name; }
 465         public String getReturnType() { return this.returnType; }
 466         public List<MethodParameter> getParameters() {
 467             return this.parameters;
 468         }
 469         public List<AccessFlag> getAccessFlags() {
 470             return this.accessFlags;
 471         }
 472         public Element[] getElements() {
 473             ArrayList<Element> elements = new ArrayList<>();
 474             getParameters().stream().forEach(elements::add);
 475             getAccessFlags().stream().forEach(elements::add);
 476             return elements.toArray(new Element[elements.size()]);
 477         }
 478 
 479         public void suppressWarnings() { this.emitSuppressWarnings = true; }
 480 
 481         public void generateWarningSuppression(PrintWriter pw) {
 482             if (this.emitSuppressWarnings) {
 483                 pw.printf("@SuppressWarnings(\"unchecked\")\n    ");
 484             }
 485         }
 486 
 487         protected void generateDecl(PrintWriter pw) {
 488             generateWarningSuppression(pw);
 489             StringJoiner joiner = new StringJoiner(" ", "", " ");
 490             accessFlags.stream().map(AccessFlag::toString).forEach(joiner::add);
 491             pw.print(joiner.toString());
 492             joiner = new StringJoiner(",");
 493             pw.printf("%s %s(", returnType, name);
 494             parameters.stream().map(MethodParameter::toString).forEach(joiner::add);
 495             pw.print(joiner.toString());
 496             pw.print(")");
 497         }
 498     }
 499 
 500     public static class AbstractMethod extends Method {
 501         public AbstractMethod(
 502                 String ret, String name, Element ... params) {
 503             super(ret, name, params);
 504             this.getAccessFlags().add(AccessFlag.ABSTRACT);
 505         }
 506 
 507         public void generate(PrintWriter pw) {
 508             generateDecl(pw);
 509             pw.print(";");
 510         }
 511 
 512         public static AbstractMethod std() {
 513             return new AbstractMethod(
 514                 "int", SourceModel.stdMethodName, AccessFlag.PUBLIC);
 515         }
 516     }
 517 
 518     public static class ConcreteMethod extends Method {
 519         protected String body;
 520 
 521         public ConcreteMethod(String ret, String name,
 522                 String body, Element ... params) {
 523             super(ret, name, params);
 524             this.body = body;
 525         }
 526 
 527         public void generate(PrintWriter pw) {
 528             generateDecl(pw);
 529             pw.printf(" { %s }", this.body);
 530         }
 531 
 532         public static ConcreteMethod std(String value) {
 533             return new ConcreteMethod(
 534                 "int", SourceModel.stdMethodName, "return " + value + ";",
 535                 AccessFlag.PUBLIC);
 536         }
 537     }
 538 
 539     // When the default method flag gets moved into the traditional
 540     // access flags location, we can remove this class completely and
 541     // use a ConcreteMethod with an AccessFlag("default") in the constructor
 542     public static class DefaultMethod extends Method {
 543         protected String body;
 544 
 545         public DefaultMethod(String ret, String name, String body,
 546                 Element ... params) {
 547             super(ret, name, params);
 548             this.body = body;
 549             this.getAccessFlags().add(AccessFlag.DEFAULT);
 550         }
 551 
 552         public void generate(PrintWriter pw) {
 553             generateDecl(pw);
 554             pw.printf(" { %s }", this.body);
 555         }
 556 
 557         public static DefaultMethod std(String value) {
 558             return new DefaultMethod(
 559                 "int", SourceModel.stdMethodName, "return " + value + ";");
 560         }
 561     }
 562 }