1 /*
  2  * Copyright (c) 2020, 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 package jdk.internal.jextract.impl;
 26 
 27 import javax.tools.JavaFileObject;
 28 import java.lang.constant.ClassDesc;
 29 import java.util.List;
 30 import java.util.function.Consumer;
 31 
 32 /**
 33  * Superclass for .java source generator classes.
 34  */
 35 abstract class ClassSourceBuilder extends JavaSourceBuilder {
 36 
 37     enum Kind {
 38         CLASS("class"),
 39         INTERFACE("interface");
 40 
 41         final String kindName;
 42 
 43         Kind(String kindName) {
 44             this.kindName = kindName;
 45         }
 46     }
 47 
 48     final Kind kind;
 49     final ClassDesc desc;
 50     protected final JavaSourceBuilder enclosing;
 51 
 52     // code buffer
 53     private StringBuilder sb = new StringBuilder();
 54     // current line alignment (number of 4-spaces)
 55     private int align;
 56 
 57     ClassSourceBuilder(JavaSourceBuilder enclosing, Kind kind, String name) {
 58         this.enclosing = enclosing;
 59         this.align = (enclosing instanceof ClassSourceBuilder) ?
 60             ((ClassSourceBuilder) enclosing).align : 0;
 61         this.kind = kind;
 62         this.desc = ClassDesc.of(enclosing.packageName(), enclosing.uniqueNestedClassName(name));
 63     }
 64 
 65     boolean isNested() {
 66         return enclosing instanceof ClassSourceBuilder;
 67     }
 68 
 69     String className() {
 70         return desc.displayName();
 71     }
 72 
 73     String fullName() {
 74         return isNested() ?
 75                 ((ClassSourceBuilder)enclosing).className() + "." + className() :
 76                 className();
 77     }
 78 
 79     @Override
 80     public final String packageName() {
 81         return desc.packageName();
 82     }
 83 
 84     String superClass() {
 85         return null;
 86     }
 87 
 88     String mods() {
 89         return (!isNested() || kind == Kind.INTERFACE) ?
 90                     "public " : "public static ";
 91     }
 92 
 93     void classBegin() {
 94         if (isNested()) {
 95             incrAlign();
 96         }
 97         emitPackagePrefix();
 98         emitImportSection();
 99 
100         indent();
101         append(mods());
102         append(kind.kindName + " " + className());
103         if (superClass() != null) {
104             append(" extends ");
105             append(superClass());
106         }
107         append(" {\n\n");
108     }
109 
110     JavaSourceBuilder classEnd() {
111         indent();
112         append("}\n\n");
113         if (isNested()) {
114             decrAlign();
115             ((ClassSourceBuilder)enclosing).append(build());
116             sb = null;
117         }
118         return enclosing;
119     }
120 
121     @Override
122     public List<JavaFileObject> toFiles() {
123         if (isNested()) {
124             throw new UnsupportedOperationException("Nested builder!");
125         }
126         String res = build();
127         sb = null;
128         return List.of(Utils.fileFromString(packageName(), className(), res));
129     }
130 
131     // Internal generation helpers (used by other builders)
132 
133     void append(String s) {
134         sb.append(s);
135     }
136 
137     void append(char c) {
138         sb.append(c);
139     }
140 
141     void append(boolean b) {
142         sb.append(b);
143     }
144 
145     void append(long l) {
146         sb.append(l);
147     }
148 
149     void indent() {
150         for (int i = 0; i < align; i++) {
151             append("    ");
152         }
153     }
154 
155     void incrAlign() {
156         align++;
157     }
158 
159     void decrAlign() {
160         align--;
161     }
162 
163     String build() {
164         String s = sb.toString();
165         return s;
166     }
167 
168     // is the name enclosed enclosed by a class of the same name?
169     boolean isEnclosedBySameName(String name) {
170         return className().equals(name) ||
171                 (isNested() && enclosing.isEnclosedBySameName(name));
172     }
173 
174     protected void emitPackagePrefix() {
175         if (!isNested()) {
176             assert packageName().indexOf('/') == -1 : "package name invalid: " + packageName();
177             append("// Generated by jextract\n\n");
178             if (!packageName().isEmpty()) {
179                 append("package ");
180                 append(packageName());
181                 append(";\n\n");
182             }
183         }
184     }
185 
186     protected void emitImportSection() {
187         if (!isNested()) {
188             append("import java.lang.invoke.MethodHandle;\n");
189             append("import java.lang.invoke.VarHandle;\n");
190             append("import java.nio.ByteOrder;\n");
191             append("import jdk.incubator.foreign.*;\n");
192             append("import static jdk.incubator.foreign.ValueLayout.*;\n");
193         }
194     }
195 
196     protected void emitGetter(String mods, Class<?> type, String name, String access, boolean nullCheck, String symbolName) {
197         incrAlign();
198         indent();
199         append(mods + " " + type.getSimpleName() + " " +name + "() {\n");
200         incrAlign();
201         indent();
202         append("return ");
203         if (nullCheck) {
204             append("RuntimeHelper.requireNonNull(");
205         }
206         append(access);
207         if (nullCheck) {
208             append(",\"");
209             append(symbolName);
210             append("\")");
211         }
212         append(";\n");
213         decrAlign();
214         indent();
215         append("}\n");
216         decrAlign();
217     }
218 
219     protected void emitGetter(String mods, Class<?> type, String name, String access) {
220         emitGetter(mods, type, name, access, false, null);
221     }
222 
223     ToplevelBuilder toplevel() {
224         JavaSourceBuilder encl = enclosing;
225         while (encl instanceof ClassSourceBuilder) {
226             encl = ((ClassSourceBuilder) encl).enclosing;
227         }
228         return (ToplevelBuilder)encl;
229     }
230 
231     @Override
232     protected void emitWithConstantClass(Consumer<ConstantBuilder> constantConsumer) {
233         enclosing.emitWithConstantClass(constantConsumer);
234     }
235 }