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