зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1352177 - 2. Introduce new config file format for SDK bindings; r=snorp
Instead of specifying a class name per line, the new format uses the .ini format, with each section name specifying the class, and each property name specifying a member of the class. WrapForJNI options can be specified with each class or member. Comments can be specified with ';' or '#'. For example, # Generate bindings for Bundle using default options: [android.os.Bundle] # Generate bindings for Bundle using class options: [android.os.Bundle = exceptionMode:nsresult] # Generate bindings for Bundle using method options: [android.os.Bundle] putInt = stubName:PutInteger # Generate bindings for Bundle using class options with method override: # (note that all options are overriden at the same time.) [android.os.Bundle = exceptionMode:nsresult] # putInt will have stubName "PutInteger", and exceptionMode of "abort" putInt = stubName:PutInteger # putChar will have stubName "PutCharacter", and exceptionMode of "nsresult" putChar = stubName:PutCharacter, exceptionMode:nsresult # Overloded methods can be specified using its signature [android.os.Bundle] # Skip the copy constructor <init>(Landroid/os/Bundle;)V = skip:true # Generic member types can be specified [android.view.KeyEvent = skip:true] # Skip everything except fields <field> = skip:false # Skip everything except putInt and putChar [android.os.Bundle = skip:true] putInt = skip:false putChar = # Avoid conflicts in native bindings [android.os.Bundle] # Bundle(PersistableBundle) native binding can conflict with Bundle(ClassLoader) <init>(Landroid/os/PersistableBundle;)V = stubName:NewFromPersistableBundle # Generate a getter instead of a literal for certain runtime constants [android.os.Build$VERSION = skip:true] SDK_INT = noLiteral:true
This commit is contained in:
Родитель
f915e1bd65
Коммит
6723035d5c
|
@ -417,9 +417,9 @@ public class CodeGenerator {
|
|||
final String uniqueName = info.wrapperName;
|
||||
final Class<?> type = field.getType();
|
||||
|
||||
// Handles a peculiar case when dealing with enum types. We don't care about this field.
|
||||
// It just gets in the way and stops our code from compiling.
|
||||
if (field.isSynthetic() || field.getName().equals("$VALUES")) {
|
||||
// Handle various cases where we don't care about the field.
|
||||
if (field.isSynthetic() || field.getName().equals("$VALUES") ||
|
||||
field.getName().equals("CREATOR")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -537,35 +537,6 @@ public class CodeGenerator {
|
|||
"\n");
|
||||
}
|
||||
|
||||
public void generateMembers(Member[] members) {
|
||||
for (Member m : members) {
|
||||
if (!Modifier.isPublic(m.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = Utils.getMemberName(m);
|
||||
name = name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
|
||||
// Default for SDK bindings.
|
||||
final AnnotationInfo info = new AnnotationInfo(name,
|
||||
AnnotationInfo.ExceptionMode.NSRESULT,
|
||||
AnnotationInfo.CallingThread.ANY,
|
||||
AnnotationInfo.DispatchTarget.CURRENT);
|
||||
final AnnotatableEntity entity = new AnnotatableEntity(m, info);
|
||||
|
||||
if (m instanceof Constructor) {
|
||||
generateConstructor(entity);
|
||||
} else if (m instanceof Method) {
|
||||
generateMethod(entity);
|
||||
} else if (m instanceof Field) {
|
||||
generateField(entity);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"expected member to be Constructor, Method, or Field");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void generateClasses(final ClassWithOptions[] classes) {
|
||||
if (classes.length == 0) {
|
||||
return;
|
||||
|
|
|
@ -4,6 +4,75 @@
|
|||
|
||||
package org.mozilla.gecko.annotationProcessors;
|
||||
|
||||
/**
|
||||
* Generate C++ bindings for SDK classes using a config file.
|
||||
*
|
||||
* java SDKProcessor <sdkjar> <configfile> <outdir> <fileprefix> <max-sdk-version>
|
||||
*
|
||||
* <sdkjar>: jar file containing the SDK classes (e.g. android.jar)
|
||||
* <configfile>: config file for generating bindings
|
||||
* <outdir>: output directory for generated binding files
|
||||
* <fileprefix>: prefix used for generated binding files
|
||||
* <max-sdk-version>: SDK version for generated class members (bindings will not be
|
||||
* generated for members with SDK versions higher than max-sdk-version)
|
||||
*
|
||||
* The config file is a text file following the .ini format:
|
||||
*
|
||||
* ; comment
|
||||
* [section1]
|
||||
* property = value
|
||||
*
|
||||
* # comment
|
||||
* [section2]
|
||||
* property = value
|
||||
*
|
||||
* Each section specifies a qualified SDK class. Each property specifies a
|
||||
* member of that class. The class and/or the property may specify options
|
||||
* found in the WrapForJNI annotation. For example,
|
||||
*
|
||||
* # Generate bindings for Bundle using default options:
|
||||
* [android.os.Bundle]
|
||||
*
|
||||
* # Generate bindings for Bundle using class options:
|
||||
* [android.os.Bundle = exceptionMode:nsresult]
|
||||
*
|
||||
* # Generate bindings for Bundle using method options:
|
||||
* [android.os.Bundle]
|
||||
* putInt = stubName:PutInteger
|
||||
*
|
||||
* # Generate bindings for Bundle using class options with method override:
|
||||
* # (note that all options are overriden at the same time.)
|
||||
* [android.os.Bundle = exceptionMode:nsresult]
|
||||
* # putInt will have stubName "PutInteger", and exceptionMode of "abort"
|
||||
* putInt = stubName:PutInteger
|
||||
* # putChar will have stubName "PutCharacter", and exceptionMode of "nsresult"
|
||||
* putChar = stubName:PutCharacter, exceptionMode:nsresult
|
||||
*
|
||||
* # Overloded methods can be specified using its signature
|
||||
* [android.os.Bundle]
|
||||
* # Skip the copy constructor
|
||||
* <init>(Landroid/os/Bundle;)V = skip:true
|
||||
*
|
||||
* # Generic member types can be specified
|
||||
* [android.view.KeyEvent = skip:true]
|
||||
* # Skip everything except fields
|
||||
* <field> = skip:false
|
||||
*
|
||||
* # Skip everything except putInt and putChar
|
||||
* [android.os.Bundle = skip:true]
|
||||
* putInt = skip:false
|
||||
* putChar =
|
||||
*
|
||||
* # Avoid conflicts in native bindings
|
||||
* [android.os.Bundle]
|
||||
* # Bundle(PersistableBundle) native binding can conflict with Bundle(ClassLoader)
|
||||
* <init>(Landroid/os/PersistableBundle;)V = stubName:NewFromPersistableBundle
|
||||
*
|
||||
* # Generate a getter instead of a literal for certain runtime constants
|
||||
* [android.os.Build$VERSION = skip:true]
|
||||
* SDK_INT = noLiteral:true
|
||||
*/
|
||||
|
||||
import com.android.tools.lint.checks.ApiLookup;
|
||||
import com.android.tools.lint.LintCliClient;
|
||||
|
||||
|
@ -13,17 +82,17 @@ import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLC
|
|||
import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.Utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Scanner;
|
||||
import java.util.Vector;
|
||||
import java.util.Locale;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
|
@ -45,17 +114,126 @@ public class SDKProcessor {
|
|||
private static ApiLookup sApiLookup;
|
||||
private static int sMaxSdkVersion;
|
||||
|
||||
private static class ParseException extends Exception {
|
||||
public ParseException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClassInfo {
|
||||
public final String name;
|
||||
|
||||
// Map constructor/field/method signature to a set of annotation values.
|
||||
private final HashMap<String, String> mAnnotations = new HashMap<>();
|
||||
// Default set of annotation values to use.
|
||||
private final String mDefaultAnnotation;
|
||||
|
||||
public ClassInfo(final String text) {
|
||||
final String[] mapping = text.split("=", 2);
|
||||
name = mapping[0].trim();
|
||||
mDefaultAnnotation = mapping.length > 1 ? mapping[1].trim() : null;
|
||||
}
|
||||
|
||||
public void addAnnotation(final String text) throws ParseException {
|
||||
final String[] mapping = text.split("=", 2);
|
||||
final String prop = mapping[0].trim();
|
||||
if (prop.isEmpty()) {
|
||||
throw new ParseException("Missing member name: " + text);
|
||||
}
|
||||
if (mapping.length < 2) {
|
||||
throw new ParseException("Missing equal sign: " + text);
|
||||
}
|
||||
if (mAnnotations.get(prop) != null) {
|
||||
throw new ParseException("Already has member: " + prop);
|
||||
}
|
||||
mAnnotations.put(prop, mapping[1].trim());
|
||||
}
|
||||
|
||||
public AnnotationInfo getAnnotationInfo(final Member member) throws ParseException {
|
||||
String stubName = Utils.getNativeName(member);
|
||||
AnnotationInfo.ExceptionMode mode = AnnotationInfo.ExceptionMode.ABORT;
|
||||
AnnotationInfo.CallingThread thread = AnnotationInfo.CallingThread.ANY;
|
||||
AnnotationInfo.DispatchTarget target = AnnotationInfo.DispatchTarget.CURRENT;
|
||||
boolean noLiteral = false;
|
||||
boolean isGeneric = false;
|
||||
|
||||
final String name = Utils.getMemberName(member);
|
||||
String annotation = mAnnotations.get(
|
||||
name + (member instanceof Field ? ":" : "") + Utils.getSignature(member));
|
||||
if (annotation == null) {
|
||||
// Match name without signature
|
||||
annotation = mAnnotations.get(name);
|
||||
}
|
||||
if (annotation == null) {
|
||||
// Match <constructor>, <field>, <method>
|
||||
annotation = mAnnotations.get("<" + member.getClass().getSimpleName()
|
||||
.toLowerCase(Locale.ROOT) + '>');
|
||||
isGeneric = true;
|
||||
}
|
||||
if (annotation == null) {
|
||||
// Fallback on class options, if any.
|
||||
annotation = mDefaultAnnotation;
|
||||
}
|
||||
if (annotation == null || annotation.isEmpty()) {
|
||||
return new AnnotationInfo(stubName, mode, thread, target, noLiteral);
|
||||
}
|
||||
|
||||
final String[] elements = annotation.split(",");
|
||||
for (final String element : elements) {
|
||||
final String[] pair = element.split(":", 2);
|
||||
if (pair.length < 2) {
|
||||
throw new ParseException("Missing option value: " + element);
|
||||
}
|
||||
final String pairName = pair[0].trim();
|
||||
final String pairValue = pair[1].trim();
|
||||
switch (pairName) {
|
||||
case "skip":
|
||||
if (Boolean.valueOf(pairValue)) {
|
||||
// Return null to signal skipping current method.
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
case "stubName":
|
||||
if (isGeneric) {
|
||||
// Prevent specifying stubName for class options.
|
||||
throw new ParseException("stubName doesn't make sense here: " +
|
||||
pairValue);
|
||||
}
|
||||
stubName = pairValue;
|
||||
break;
|
||||
case "exceptionMode":
|
||||
mode = Utils.getEnumValue(AnnotationInfo.ExceptionMode.class,
|
||||
pairValue);
|
||||
break;
|
||||
case "calledFrom":
|
||||
thread = Utils.getEnumValue(AnnotationInfo.CallingThread.class,
|
||||
pairValue);
|
||||
break;
|
||||
case "dispatchTo":
|
||||
target = Utils.getEnumValue(AnnotationInfo.DispatchTarget.class,
|
||||
pairValue);
|
||||
break;
|
||||
case "noLiteral":
|
||||
noLiteral = Boolean.valueOf(pairValue);
|
||||
break;
|
||||
default:
|
||||
throw new ParseException("Unknown option: " + pairName);
|
||||
}
|
||||
}
|
||||
return new AnnotationInfo(stubName, mode, thread, target, noLiteral);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// We expect a list of jars on the commandline. If missing, whinge about it.
|
||||
if (args.length < 5) {
|
||||
System.err.println("Usage: java SDKProcessor sdkjar classlistfile outdir fileprefix max-sdk-version");
|
||||
System.err.println("Usage: java SDKProcessor sdkjar configfile outdir fileprefix max-sdk-version");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
System.out.println("Processing platform bindings...");
|
||||
|
||||
String sdkJar = args[0];
|
||||
Vector classes = getClassList(args[1]);
|
||||
String outdir = args[2];
|
||||
String generatedFilePrefix = args[3];
|
||||
sMaxSdkVersion = Integer.parseInt(args[4]);
|
||||
|
@ -100,14 +278,26 @@ public class SDKProcessor {
|
|||
throw new RuntimeException(e.toString());
|
||||
}
|
||||
|
||||
for (Iterator<String> i = classes.iterator(); i.hasNext(); ) {
|
||||
String className = i.next();
|
||||
System.out.println("Looking up: " + className);
|
||||
|
||||
generateClass(Class.forName(className, true, loader),
|
||||
try {
|
||||
final ClassInfo[] classes = getClassList(args[1]);
|
||||
for (final ClassInfo cls : classes) {
|
||||
System.out.println("Looking up: " + cls.name);
|
||||
generateClass(Class.forName(cls.name, true, loader),
|
||||
cls,
|
||||
implementationFile,
|
||||
headerFile);
|
||||
}
|
||||
} catch (final IllegalStateException|IOException|ParseException e) {
|
||||
System.err.println("***");
|
||||
System.err.println("*** Error parsing config file: " + args[1]);
|
||||
System.err.println("*** " + e);
|
||||
System.err.println("***");
|
||||
if (e.getCause() != null) {
|
||||
e.getCause().printStackTrace(System.err);
|
||||
}
|
||||
System.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
implementationFile.append(
|
||||
"} /* sdk */\n" +
|
||||
|
@ -148,7 +338,12 @@ public class SDKProcessor {
|
|||
});
|
||||
|
||||
ArrayList<Member> list = new ArrayList<>();
|
||||
for (Member m : members) {
|
||||
for (final Member m : members) {
|
||||
if (m.getDeclaringClass() == Object.class) {
|
||||
// Skip methods from Object.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sometimes (e.g. Bundle) has methods that moved to/from a superclass in a later SDK
|
||||
// version, so we check for both classes and see if we can find a minimum SDK version.
|
||||
int version = getAPIVersion(cls, m);
|
||||
|
@ -157,8 +352,9 @@ public class SDKProcessor {
|
|||
version = version2;
|
||||
}
|
||||
if (version > sMaxSdkVersion) {
|
||||
System.out.println("Skipping " + m.getDeclaringClass().getName() + "." + m.getName() +
|
||||
", version " + version + " > " + sMaxSdkVersion);
|
||||
System.out.println("Skipping " + m.getDeclaringClass().getName() + "." +
|
||||
Utils.getMemberName(m) + ", version " + version + " > " +
|
||||
sMaxSdkVersion);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -169,8 +365,8 @@ public class SDKProcessor {
|
|||
if (m instanceof Field && !m.equals(cls.getField(m.getName()))) {
|
||||
// m is a field in a superclass that has been hidden by
|
||||
// a field with the same name in a subclass.
|
||||
System.out.println("Skipping " + m.getName() +
|
||||
" from " + m.getDeclaringClass());
|
||||
System.out.println("Skipping " + Utils.getMemberName(m) +
|
||||
" from " + m.getDeclaringClass().getName());
|
||||
continue;
|
||||
}
|
||||
} catch (final NoSuchFieldException e) {
|
||||
|
@ -182,37 +378,99 @@ public class SDKProcessor {
|
|||
return list.toArray(new Member[list.size()]);
|
||||
}
|
||||
|
||||
private static void generateMembers(CodeGenerator generator, ClassInfo clsInfo,
|
||||
Member[] members) throws ParseException {
|
||||
for (Member m : members) {
|
||||
if (!Modifier.isPublic(m.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Default for SDK bindings.
|
||||
final AnnotationInfo info = clsInfo.getAnnotationInfo(m);
|
||||
if (info == null) {
|
||||
// Skip this member.
|
||||
continue;
|
||||
}
|
||||
final AnnotatableEntity entity = new AnnotatableEntity(m, info);
|
||||
|
||||
if (m instanceof Constructor) {
|
||||
generator.generateConstructor(entity);
|
||||
} else if (m instanceof Method) {
|
||||
generator.generateMethod(entity);
|
||||
} else if (m instanceof Field) {
|
||||
generator.generateField(entity);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"expected member to be Constructor, Method, or Field");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateClass(Class<?> clazz,
|
||||
ClassInfo clsInfo,
|
||||
StringBuilder implementationFile,
|
||||
StringBuilder headerFile) {
|
||||
StringBuilder headerFile) throws ParseException {
|
||||
String generatedName = clazz.getSimpleName();
|
||||
|
||||
CodeGenerator generator = new CodeGenerator(new ClassWithOptions(clazz, generatedName));
|
||||
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getConstructors()));
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getMethods()));
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getFields()));
|
||||
generateMembers(generator, clsInfo,
|
||||
sortAndFilterMembers(clazz, clazz.getConstructors()));
|
||||
generateMembers(generator, clsInfo, sortAndFilterMembers(clazz, clazz.getMethods()));
|
||||
generateMembers(generator, clsInfo, sortAndFilterMembers(clazz, clazz.getFields()));
|
||||
|
||||
headerFile.append(generator.getHeaderFileContents());
|
||||
implementationFile.append(generator.getWrapperFileContents());
|
||||
}
|
||||
|
||||
private static Vector<String> getClassList(String path) {
|
||||
Scanner scanner = null;
|
||||
try {
|
||||
scanner = new Scanner(new FileInputStream(path));
|
||||
private static ClassInfo[] getClassList(BufferedReader reader)
|
||||
throws ParseException, IOException {
|
||||
final ArrayList<ClassInfo> classes = new ArrayList<>();
|
||||
ClassInfo currentClass = null;
|
||||
String line;
|
||||
|
||||
Vector lines = new Vector();
|
||||
while (scanner.hasNextLine()) {
|
||||
lines.add(scanner.nextLine());
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
return lines;
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.toString());
|
||||
return null;
|
||||
switch (line.charAt(0)) {
|
||||
case ';':
|
||||
case '#':
|
||||
// Comment
|
||||
continue;
|
||||
case '[':
|
||||
// New section
|
||||
if (line.charAt(line.length() - 1) != ']') {
|
||||
throw new ParseException("Missing trailing ']': " + line);
|
||||
}
|
||||
currentClass = new ClassInfo(line.substring(1, line.length() - 1));
|
||||
classes.add(currentClass);
|
||||
break;
|
||||
default:
|
||||
// New mapping
|
||||
if (currentClass == null) {
|
||||
throw new ParseException("Missing class: " + line);
|
||||
}
|
||||
currentClass.addAnnotation(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (classes.isEmpty()) {
|
||||
throw new ParseException("No class found in config file");
|
||||
}
|
||||
return classes.toArray(new ClassInfo[classes.size()]);
|
||||
}
|
||||
|
||||
private static ClassInfo[] getClassList(final String path)
|
||||
throws ParseException, IOException {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(path));
|
||||
return getClassList(reader);
|
||||
} finally {
|
||||
if (scanner != null) {
|
||||
scanner.close();
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,32 +121,6 @@ public class GeneratableElementIterator implements Iterator<AnnotatableEntity> {
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static <T extends Enum<T>> T getEnumValue(Class<T> type, String name)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
try {
|
||||
return Enum.valueOf(type, name.toUpperCase());
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
Object[] values = (Object[]) type.getDeclaredMethod("values").invoke(null);
|
||||
StringBuilder names = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (i != 0) {
|
||||
names.append(", ");
|
||||
}
|
||||
names.append(values[i].toString().toLowerCase());
|
||||
}
|
||||
|
||||
System.err.println("***");
|
||||
System.err.println("*** Invalid value \"" + name + "\" for " + type.getSimpleName());
|
||||
System.err.println("*** Specify one of " + names.toString());
|
||||
System.err.println("***");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(6);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationInfo buildAnnotationInfo(AnnotatedElement element, Annotation annotation) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
final String annotationTypeName = annotationType.getName();
|
||||
|
@ -175,19 +149,19 @@ public class GeneratableElementIterator implements Iterator<AnnotatableEntity> {
|
|||
|
||||
final Method exceptionModeMethod = annotationType.getDeclaredMethod("exceptionMode");
|
||||
exceptionModeMethod.setAccessible(true);
|
||||
exceptionMode = getEnumValue(
|
||||
exceptionMode = Utils.getEnumValue(
|
||||
AnnotationInfo.ExceptionMode.class,
|
||||
(String) exceptionModeMethod.invoke(annotation));
|
||||
|
||||
final Method calledFromMethod = annotationType.getDeclaredMethod("calledFrom");
|
||||
calledFromMethod.setAccessible(true);
|
||||
callingThread = getEnumValue(
|
||||
callingThread = Utils.getEnumValue(
|
||||
AnnotationInfo.CallingThread.class,
|
||||
(String) calledFromMethod.invoke(annotation));
|
||||
|
||||
final Method dispatchToMethod = annotationType.getDeclaredMethod("dispatchTo");
|
||||
dispatchToMethod.setAccessible(true);
|
||||
dispatchTarget = getEnumValue(
|
||||
dispatchTarget = Utils.getEnumValue(
|
||||
AnnotationInfo.DispatchTarget.class,
|
||||
(String) dispatchToMethod.invoke(annotation));
|
||||
|
||||
|
|
|
@ -9,11 +9,13 @@ import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
|
|||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A collection of utility methods used by CodeGenerator. Largely used for translating types.
|
||||
|
@ -219,7 +221,7 @@ public class Utils {
|
|||
*/
|
||||
public static String getNativeName(Member member) {
|
||||
final String name = getMemberName(member);
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
return name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,7 +232,7 @@ public class Utils {
|
|||
*/
|
||||
public static String getNativeName(Class<?> clz) {
|
||||
final String name = clz.getName();
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
return name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,4 +287,44 @@ public class Utils {
|
|||
public static boolean isFinal(final Member member) {
|
||||
return Modifier.isFinal(member.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an enum value with the given name.
|
||||
*
|
||||
* @param type Enum class type.
|
||||
* @param name Enum value name.
|
||||
* @return Enum value with the given name.
|
||||
*/
|
||||
public static <T extends Enum<T>> T getEnumValue(Class<T> type, String name) {
|
||||
try {
|
||||
return Enum.valueOf(type, name.toUpperCase(Locale.ROOT));
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
final Object[] values;
|
||||
try {
|
||||
values = (Object[]) type.getDeclaredMethod("values").invoke(null);
|
||||
} catch (final NoSuchMethodException |
|
||||
IllegalAccessException |
|
||||
InvocationTargetException exception) {
|
||||
throw new RuntimeException("Cannot access enum: " + type, exception);
|
||||
}
|
||||
|
||||
StringBuilder names = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (i != 0) {
|
||||
names.append(", ");
|
||||
}
|
||||
names.append(values[i].toString().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
System.err.println("***");
|
||||
System.err.println("*** Invalid value \"" + name + "\" for " + type.getSimpleName());
|
||||
System.err.println("*** Specify one of " + names.toString());
|
||||
System.err.println("***");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(1);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче