1 /*
   2  * Copyright (c) 2014, 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 
  24 package com.sun.tools.jextract;
  25 
  26 import java.foreign.layout.Function;
  27 import java.foreign.layout.Layout;
  28 import java.util.function.BiFunction;
  29 import javax.lang.model.SourceVersion;
  30 import jdk.internal.clang.Type;
  31 import com.sun.tools.jextract.tree.LayoutUtils;
  32 
  33 /**
  34  * General utility functions
  35  */
  36 public class Utils {
  37     public static void validSimpleIdentifier(String name) {
  38         int length = name.length();
  39         if (length == 0) {
  40             throw new IllegalArgumentException();
  41         }
  42 
  43         int ch = name.codePointAt(0);
  44         if (length == 1 && ch == '_') {
  45             throw new IllegalArgumentException("'_' is no longer valid identifier.");
  46         }
  47 
  48         if (!Character.isJavaIdentifierStart(ch)) {
  49             throw new IllegalArgumentException("Invalid start character for an identifier: " + ch);
  50         }
  51 
  52         for (int i = 1; i < length; i++) {
  53             ch = name.codePointAt(i);
  54             if (!Character.isJavaIdentifierPart(ch)) {
  55                 throw new IllegalArgumentException("Invalid character for an identifier: " + ch);
  56             }
  57         }
  58     }
  59 
  60     public static void validPackageName(String name) {
  61         if (name.isEmpty()) {
  62             throw new IllegalArgumentException();
  63         }
  64         int idx = name.lastIndexOf('.');
  65         if (idx == -1) {
  66            validSimpleIdentifier(name);
  67         } else {
  68             validSimpleIdentifier(name.substring(idx + 1));
  69             validPackageName(name.substring(0, idx));
  70         }
  71     }
  72 
  73     public static String toJavaIdentifier(String str) {
  74         final int size = str.length();
  75         StringBuilder sb = new StringBuilder(size);
  76         if (! Character.isJavaIdentifierStart(str.charAt(0))) {
  77             sb.append('_');
  78         }
  79         for (int i = 0; i < size; i++) {
  80             char ch = str.charAt(i);
  81             if (Character.isJavaIdentifierPart(ch)) {
  82                 sb.append(ch);
  83             } else {
  84                 sb.append('_');
  85             }
  86         }
  87         return sb.toString();
  88     }
  89 
  90     public static String toClassName(String cname) {
  91         StringBuilder sb = new StringBuilder(cname.length());
  92         cname = toJavaIdentifier(cname);
  93         sb.append(cname);
  94         if (SourceVersion.isKeyword(cname)) {
  95             sb.append("$");
  96         }
  97         return sb.toString();
  98     }
  99 
 100     public static String toInternalName(String pkg, String name, String... nested) {
 101         if ((pkg == null || pkg.isEmpty()) && nested == null) {
 102             return name;
 103         }
 104 
 105         StringBuilder sb = new StringBuilder();
 106         if (pkg != null && ! pkg.isEmpty()) {
 107             sb.append(pkg.replace('.', '/'));
 108             if (sb.charAt(sb.length() - 1) != '/') {
 109                 sb.append('/');
 110             }
 111         }
 112         sb.append(name);
 113         for (String n: nested) {
 114             sb.append('$');
 115             sb.append(n);
 116         }
 117         return sb.toString();
 118     }
 119 
 120     public static String getName(Type type) {
 121         return LayoutUtils.getName(type);
 122     }
 123 
 124     public static String ClassToDescriptor(Class<?> cls) {
 125         if (cls.isArray()) {
 126             return cls.getName();
 127         }
 128         if (cls.isPrimitive()) {
 129             switch (cls.getTypeName()) {
 130                 case "int":
 131                     return "I";
 132                 case "long":
 133                     return "J";
 134                 case "byte":
 135                     return "B";
 136                 case "char":
 137                     return "C";
 138                 case "float":
 139                     return "F";
 140                 case "double":
 141                     return "D";
 142                 case "short":
 143                     return "S";
 144                 case "boolean":
 145                     return "Z";
 146                 case "void":
 147                     return "V";
 148             }
 149         }
 150         // assuming reference
 151         return "L" + cls.getName() + ";";
 152     }
 153 
 154     public static String DescriptorToBinaryName(String descriptor) {
 155         final char[] ar = descriptor.trim().toCharArray();
 156         switch (ar[0]) {
 157             case '(':
 158                 throw new IllegalArgumentException("Method descriptor is not allowed");
 159             case 'B':
 160                 return "byte";
 161             case 'C':
 162                 return "char";
 163             case 'D':
 164                 return "double";
 165             case 'F':
 166                 return "float";
 167             case 'I':
 168                 return "int";
 169             case 'J':
 170                 return "long";
 171             case 'S':
 172                 return "short";
 173             case 'Z':
 174                 return "boolean";
 175         }
 176 
 177         StringBuilder sb = new StringBuilder();
 178         if (ar[0] == 'L') {
 179             for (int i = 1; i < ar.length && ar[i] != ';'; i++) {
 180                 if (ar[i] == '/') {
 181                     sb.append('.');
 182                 }
 183                 if (!Character.isJavaIdentifierPart(ar[i])) {
 184                     throw new IllegalArgumentException("Malformed descriptor");
 185                 }
 186                 sb.append(ar[i]);
 187             }
 188             return sb.toString();
 189         }
 190 
 191         if (ar[0] == '[') {
 192             int depth = 1;
 193             while (ar[depth] == '[') depth++;
 194             sb.append(DescriptorToBinaryName(descriptor.substring(depth)));
 195             for (int i = 0; i < depth; i++) {
 196                 sb.append("[]");
 197             }
 198             return sb.toString();
 199         }
 200 
 201         throw new IllegalArgumentException("Malformed descriptor");
 202     }
 203 
 204     public static Layout getLayout(Type type) {
 205         return LayoutUtils.getLayout(type);
 206     }
 207 
 208     public static Function getFunction(Type type) {
 209         return LayoutUtils.getFunction(type);
 210     }
 211 
 212     public static Class<?> unboxIfNeeded(Class<?> clazz) {
 213         if (clazz == Boolean.class) {
 214             return boolean.class;
 215         } else if (clazz == Void.class) {
 216             return void.class;
 217         } else if (clazz == Byte.class) {
 218             return byte.class;
 219         } else if (clazz == Character.class) {
 220             return char.class;
 221         } else if (clazz == Short.class) {
 222             return short.class;
 223         } else if (clazz == Integer.class) {
 224             return int.class;
 225         } else if (clazz == Long.class) {
 226             return long.class;
 227         } else if (clazz == Float.class) {
 228             return float.class;
 229         } else if (clazz == Double.class) {
 230             return double.class;
 231         } else {
 232             return clazz;
 233         }
 234     }
 235 }