/* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.jextract; import java.util.Map; import java.util.Set; import com.sun.tools.jextract.tree.FunctionTree; /** * A helper class to generate header interface class in source form. * After aggregating various constituents of a .java source, build * method is called to get overall generated source string. */ class JavaSourceBuilder { // buffer protected StringBuffer sb; // current line alignment (number of 4-spaces) protected int align; JavaSourceBuilder(int align) { this.align = align; this.sb = new StringBuffer(); } JavaSourceBuilder() { this(0); } protected int align() { return align; } protected void addPackagePrefix(String pkgName) { sb.append("// Generated by jextract\n\n"); if (!pkgName.isEmpty()) { sb.append("package "); sb.append(pkgName); sb.append(";\n\n"); } addImportSection(); } protected void addImportSection() { sb.append("import java.foreign.*;\n"); sb.append("import java.foreign.annotations.*;\n"); sb.append("import java.foreign.memory.*;\n"); sb.append("import java.lang.annotation.*;\n\n"); } protected void interfaceBegin(String name, boolean isStatic) { interfaceBegin(name, isStatic, false); } protected void interfaceBegin(String name, boolean isStatic, boolean isAnnotation) { check(); indent(); sb.append("public "); if (isStatic) { sb.append("static "); } if (isAnnotation) { sb.append('@'); } sb.append("interface "); sb.append(name); sb.append(" {\n\n"); } protected void interfaceEnd() { check(); indent(); sb.append("}\n\n"); } protected void addAnnotation(String annoName, Map fields) { addAnnotation(true, annoName, fields); } protected void addAnnotation(boolean incrAlign, String annoName, Map fields) { check(); if (incrAlign) { incrAlign(); } indent(); sb.append('@'); sb.append(annoName); if (fields.isEmpty()) { sb.append('\n'); return; } boolean singleProperty = (fields.size() == 1); if (singleProperty) { sb.append('('); } else { sb.append("(\n"); incrAlign(); } Set keys = fields.keySet(); int size = keys.size(); for (String key : keys) { if (!singleProperty) { indent(); } if (!singleProperty || !key.equals("value")) { sb.append(key); sb.append('='); } Object value = fields.get(key); if (value instanceof String) { sb.append('"'); sb.append(value.toString()); sb.append('"'); } else if (value instanceof Enum) { sb.append(value.getClass().getSimpleName()); sb.append('.'); sb.append(((Enum)value).name()); } else if (value instanceof Long) { sb.append(value); sb.append("L"); } else if (value instanceof String[]) { sb.append("{ "); String[] strs = (String[])value; for (int i = 0; i < strs.length; i++) { sb.append('"'); sb.append(strs[i]); sb.append('"'); if (i != strs.length - 1) { sb.append(", "); } } sb.append(" }"); } else if (value instanceof JType.ClassType[]) { sb.append("{ "); JType.ClassType[] types = (JType.ClassType[])value; for (int i = 0; i < types.length; i++) { sb.append(types[i].externalName); sb.append(".class"); if (i != types.length - 1) { sb.append(", "); } } sb.append(" }"); } else { sb.append(value); } if (!singleProperty && size > 1) { sb.append(",\n"); } size--; } if (!singleProperty) { decrAlign(); sb.append("\n"); indent(); } sb.append(")\n"); if (incrAlign) { decrAlign(); } } protected void addGetter(String name, JType jt) { check(); incrAlign(); indent(); sb.append("public "); sb.append(jt.getSourceSignature(false)); sb.append(' '); sb.append(name); sb.append("();\n\n"); decrAlign(); } protected void addSetter(String name, JType jt) { check(); incrAlign(); indent(); sb.append("public "); sb.append("void "); sb.append(name); sb.append('('); sb.append(jt.getSourceSignature(true)); sb.append(" value"); sb.append(");\n\n"); decrAlign(); } protected void fillArgTypes(JType.Function fn, String[] argTypes) { for (int i = 0; i < fn.args.length; i++) { argTypes[i] = fn.args[i].getSourceSignature(true); } if (fn.isVarArgs) { argTypes[argTypes.length - 1] = "Object..."; } } protected void fillArgNames(JType.Function fn, FunctionTree funcTree, String[] argNames) { for (int i = 0; i < fn.args.length; i++) { String name = funcTree != null? funcTree.paramName(i) : null; argNames[i] = (name == null || name.isEmpty())? ("$arg" + i) : name; } if (fn.isVarArgs) { argNames[argNames.length - 1] = "$args"; } } private void addMethod(String name, JType.Function fn, FunctionTree funcTree) { final int numArgs = fn.isVarArgs? fn.args.length + 1 : fn.args.length; final String[] argTypes = new String[numArgs]; final String[] argNames = new String[numArgs]; fillArgTypes(fn, argTypes); fillArgNames(fn, funcTree, argNames); check(); incrAlign(); indent(); sb.append("public "); sb.append(fn.returnType.getSourceSignature(false)); sb.append(' '); sb.append(name); sb.append('('); for (int i = 0; i < numArgs; i++) { sb.append(argTypes[i]); sb.append(' '); sb.append(argNames[i]); if (i != argTypes.length - 1) { sb.append(", "); } } sb.append(");\n\n"); decrAlign(); } protected void addMethod(String name, JType.Function fn) { addMethod(name, fn, null); } protected void addMethod(FunctionTree funcTree, JType.Function fn) { addMethod(funcTree.name(), fn, funcTree); } protected void addNestedType(JavaSourceBuilder jsb) { check(); sb.append(jsb.build()); } protected String build() { check(); String res = sb.toString(); this.sb = null; return res.toString(); } protected void indent() { for (int i = 0; i < align; i++) { sb.append(" "); } } protected void incrAlign() { align++; } protected void decrAlign() { align--; } protected void check() { if (sb == null) { throw new IllegalStateException("source already built"); } } }