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 }