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