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