1 /* 2 * Copyright (c) 2019, 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 package com.sun.tools.jextract; 24 25 import java.util.Map; 26 import java.util.Set; 27 import com.sun.tools.jextract.tree.FunctionTree; 28 29 /** 30 * A helper class to generate header interface class in source form. 31 * After aggregating various constituents of a .java source, build 32 * method is called to get overall generated source string. 33 */ 34 class JavaSourceBuilder { 35 // buffer 36 protected StringBuffer sb; 37 // current line alignment (number of 4-spaces) 38 protected int align; 39 40 JavaSourceBuilder(int align) { 41 this.align = align; 42 this.sb = new StringBuffer(); 43 } 44 45 JavaSourceBuilder() { 46 this(0); 47 } 48 49 protected int align() { 50 return align; 51 } 52 53 protected void addPackagePrefix(String pkgName) { 54 assert pkgName.indexOf('/') == -1 : "package name invalid: " + pkgName; 55 sb.append("// Generated by jextract\n\n"); 56 if (!pkgName.isEmpty()) { 57 sb.append("package "); 58 sb.append(pkgName); 59 sb.append(";\n\n"); 60 } 61 addImportSection(); 62 } 63 64 protected void addImportSection() { 65 sb.append("import java.foreign.*;\n"); 66 sb.append("import java.foreign.annotations.*;\n"); 67 sb.append("import java.foreign.memory.*;\n"); 68 sb.append("import java.lang.annotation.*;\n\n"); 69 } 70 71 protected void interfaceBegin(String name, boolean isStatic) { 72 interfaceBegin(name, isStatic, null); 73 } 74 75 protected void interfaceBegin(String name, boolean isStatic, String superName) { 76 interfaceBegin(name, isStatic, false, superName); 77 } 78 79 protected void interfaceBegin(String name, boolean isStatic, boolean isAnnotation) { 80 interfaceBegin(name, isStatic, isAnnotation, null); 81 } 82 83 protected void interfaceBegin(String name, boolean isStatic, 84 boolean isAnnotation, String superName) { 85 check(); 86 indent(); 87 sb.append("public "); 88 if (isStatic) { 89 sb.append("static "); 90 } 91 if (isAnnotation) { 92 sb.append('@'); 93 } 94 sb.append("interface "); 95 sb.append(name); 96 if (superName != null) { 97 sb.append(" extends "); 98 sb.append(superName); 99 } 100 sb.append(" {\n\n"); 101 } 102 103 protected void interfaceEnd() { 104 check(); 105 indent(); 106 sb.append("}\n\n"); 107 } 108 109 protected void addAnnotation(String annoName, Map<String, Object> fields) { 110 addAnnotation(true, annoName, fields); 111 } 112 113 protected void addAnnotation(boolean incrAlign, String annoName, Map<String, Object> fields) { 114 check(); 115 if (incrAlign) { 116 incrAlign(); 117 } 118 119 indent(); 120 sb.append('@'); 121 sb.append(annoName); 122 if (fields.isEmpty()) { 123 sb.append('\n'); 124 return; 125 } 126 boolean singleProperty = (fields.size() == 1); 127 128 if (singleProperty) { 129 sb.append('('); 130 } else { 131 sb.append("(\n"); 132 incrAlign(); 133 } 134 Set<String> keys = fields.keySet(); 135 int size = keys.size(); 136 for (String key : keys) { 137 if (!singleProperty) { 138 indent(); 139 } 140 141 if (!singleProperty || !key.equals("value")) { 142 sb.append(key); 143 sb.append('='); 144 } 145 146 Object value = fields.get(key); 147 if (value instanceof String) { 148 sb.append('"'); 149 sb.append(value.toString()); 150 sb.append('"'); 151 } else if (value instanceof Enum) { 152 sb.append(value.getClass().getSimpleName()); 153 sb.append('.'); 154 sb.append(((Enum)value).name()); 155 } else if (value instanceof Long) { 156 sb.append(value); 157 sb.append("L"); 158 } else if (value instanceof String[]) { 159 sb.append("{ "); 160 String[] strs = (String[])value; 161 for (int i = 0; i < strs.length; i++) { 162 sb.append('"'); 163 sb.append(strs[i]); 164 sb.append('"'); 165 if (i != strs.length - 1) { 166 sb.append(", "); 167 } 168 } 169 sb.append(" }"); 170 } else if (value instanceof JType.ClassType[]) { 171 sb.append("{ "); 172 JType.ClassType[] types = (JType.ClassType[])value; 173 for (int i = 0; i < types.length; i++) { 174 sb.append(types[i].externalName); 175 sb.append(".class"); 176 if (i != types.length - 1) { 177 sb.append(", "); 178 } 179 } 180 sb.append(" }"); 181 } else { 182 sb.append(value); 183 } 184 if (!singleProperty && size > 1) { 185 sb.append(",\n"); 186 } 187 size--; 188 } 189 190 if (!singleProperty) { 191 decrAlign(); 192 sb.append("\n"); 193 indent(); 194 } 195 sb.append(")\n"); 196 197 if (incrAlign) { 198 decrAlign(); 199 } 200 } 201 202 protected void addGetter(String name, JType jt) { 203 check(); 204 incrAlign(); 205 indent(); 206 sb.append("public "); 207 sb.append(jt.getSourceSignature(false)); 208 sb.append(' '); 209 sb.append(name); 210 sb.append("();\n\n"); 211 decrAlign(); 212 } 213 214 protected void addSetter(String name, JType jt) { 215 check(); 216 incrAlign(); 217 indent(); 218 sb.append("public "); 219 sb.append("void "); 220 sb.append(name); 221 sb.append('('); 222 sb.append(jt.getSourceSignature(true)); 223 sb.append(" value"); 224 sb.append(");\n\n"); 225 decrAlign(); 226 } 227 228 protected void fillArgTypes(JType.Function fn, String[] argTypes) { 229 for (int i = 0; i < fn.args.length; i++) { 230 argTypes[i] = fn.args[i].getSourceSignature(true); 231 } 232 if (fn.isVarArgs) { 233 argTypes[argTypes.length - 1] = "Object..."; 234 } 235 } 236 237 protected void fillArgNames(JType.Function fn, FunctionTree funcTree, String[] argNames) { 238 for (int i = 0; i < fn.args.length; i++) { 239 String name = funcTree != null? funcTree.paramName(i) : null; 240 argNames[i] = (name == null || name.isEmpty())? ("$arg" + i) : name; 241 } 242 if (fn.isVarArgs) { 243 argNames[argNames.length - 1] = "$args"; 244 } 245 } 246 247 private void addMethod(String name, JType.Function fn, FunctionTree funcTree) { 248 final int numArgs = fn.isVarArgs? fn.args.length + 1 : fn.args.length; 249 final String[] argTypes = new String[numArgs]; 250 final String[] argNames = new String[numArgs]; 251 fillArgTypes(fn, argTypes); 252 fillArgNames(fn, funcTree, argNames); 253 254 check(); 255 incrAlign(); 256 indent(); 257 sb.append("public "); 258 sb.append(fn.returnType.getSourceSignature(false)); 259 sb.append(' '); 260 sb.append(name); 261 sb.append('('); 262 for (int i = 0; i < numArgs; i++) { 263 sb.append(argTypes[i]); 264 sb.append(' '); 265 sb.append(argNames[i]); 266 if (i != argTypes.length - 1) { 267 sb.append(", "); 268 } 269 } 270 sb.append(");\n\n"); 271 decrAlign(); 272 } 273 274 protected void addMethod(String name, JType.Function fn) { 275 addMethod(name, fn, null); 276 } 277 278 protected void addMethod(FunctionTree funcTree, JType.Function fn) { 279 addMethod(funcTree.name(), fn, funcTree); 280 } 281 282 protected void addNestedType(JavaSourceBuilder jsb) { 283 check(); 284 sb.append(jsb.build()); 285 } 286 287 protected String build() { 288 check(); 289 String res = sb.toString(); 290 this.sb = null; 291 return res.toString(); 292 } 293 294 protected void indent() { 295 for (int i = 0; i < align; i++) { 296 sb.append(" "); 297 } 298 } 299 300 protected void incrAlign() { 301 align++; 302 } 303 304 protected void decrAlign() { 305 align--; 306 } 307 308 protected void check() { 309 if (sb == null) { 310 throw new IllegalStateException("source already built"); 311 } 312 } 313 }