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