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
24package separate;
25
26import java.util.*;
27import java.io.StringWriter;
28import java.io.PrintWriter;
29
30public class SourceModel {
31
32    public static final String stdMethodName = "m";
33
34    public static interface SourceProcessor {
35        // Called with a generated source file
36        void process(String name, String content);
37    }
38
39    public static abstract class Element {
40
41        protected abstract void generate(PrintWriter pw);
42
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
54        public AccessFlag(String name) { flag = name; }
55
56        protected void generate(PrintWriter pw) {
57            pw.print(flag);
58        }
59
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
78        public TypeParameter(String str) {
79            this.parameter = str;
80        }
81
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
90        public TypeArgument(String str) {
91            this.argument = str;
92        }
93
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
103        public MethodParameter(String type, String name) {
104            this.type = type;
105            this.name = name;
106        }
107
108        protected void generate(PrintWriter pw) {
109            pw.printf("%s %s", this.type, this.name);
110        }
111
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
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
139        public String getName() { return this.name; }
140        public List<AccessFlag> getAccessFlags() { return this.accessFlags; }
141        public List<TypeParameter> getParameters() { return this.parameters; }
142        public List<Extends> getSupertypes() { return this.supertypes; }
143        public List<Method> getMethods() { return this.methods; }
144        public Set<Method> methodDependencies() {
145            return this.methodDependencies;
146        }
147
148        public Class getSuperclass() { return null; }
149        protected abstract void setSuperClass(Extends supertype);
150
151        public void addSuperType(Extends sup) {
152            assert sup.getType() instanceof Interface : "Must be an interface";
153            this.supertypes.add(sup);
154        }
155        public void addSuperType(Interface iface) {
156            this.supertypes.add(new Extends(iface));
157        }
158
159        public void addMethod(Method m) {
160            this.methods.add(m);
161        }
162
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!).
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'
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
207        public void addCompilationDependency(Type t) {
208            typeDependencies.add(t);
209        }
210
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.
217        public Extends with(String ... args) {
218            return new Extends(this, args);
219        }
220
221        public abstract void generate(SourceProcessor sp);
222        public abstract void generateAsDependency(
223            SourceProcessor sp, Set<Method> neededMethods);
224
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
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
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
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
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
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
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
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
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
316        public void setSuperClass(Class c) {
317            setSuperClass(new Extends(c));
318        }
319
320        @Override
321        public Class getSuperclass() {
322            return superClass == null ? null : (Class)superClass.supertype;
323        }
324
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
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
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
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
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
373        protected void setSuperClass(Extends ext) {
374            assert false : "Interfaces cannot have Class supertypes";
375        }
376
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
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
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
414        public Type getType() { return supertype; }
415        public List<TypeArgument> getArguments() {
416            return arguments;
417        }
418
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
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
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
462        public String getName() { return this.name; }
463        public String getReturnType() { return this.returnType; }
464        public List<MethodParameter> getParameters() {
465            return this.parameters;
466        }
467        public List<AccessFlag> getAccessFlags() {
468            return this.accessFlags;
469        }
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
477        public void suppressWarnings() { this.emitSuppressWarnings = true; }
478
479        public void generateWarningSuppression(PrintWriter pw) {
480            if (this.emitSuppressWarnings) {
481                pw.printf("@SuppressWarnings(\"unchecked\")\n    ");
482            }
483        }
484
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 {
499        public AbstractMethod(
500                String ret, String name, Element ... params) {
501            super(ret, name, params);
502            this.getAccessFlags().add(AccessFlag.ABSTRACT);
503        }
504
505        public void generate(PrintWriter pw) {
506            generateDecl(pw);
507            pw.print(";");
508        }
509
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
519        public ConcreteMethod(String ret, String name,
520                String body, Element ... params) {
521            super(ret, name, params);
522            this.body = body;
523        }
524
525        public void generate(PrintWriter pw) {
526            generateDecl(pw);
527            pw.printf(" { %s }", this.body);
528        }
529
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
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
550        public void generate(PrintWriter pw) {
551            generateDecl(pw);
552            pw.printf(" { %s }", this.body);
553        }
554
555        public static DefaultMethod std(String value) {
556            return new DefaultMethod(
557                "int", SourceModel.stdMethodName, "return " + value + ";");
558        }
559    }
560}
561