From fefe6239f7079f1a87bd0d4a1e953679bfff34ae Mon Sep 17 00:00:00 2001 From: Kustaa Nyholm Date: Mon, 31 Jan 2022 18:44:57 +0200 Subject: [PATCH] First attempt at Java side automatic downcast feature --- .../bytedeco/javacpp/annotation/Downcast.java | 13 +++ .../bytedeco/javacpp/annotation/Renamed.java | 18 ++++ .../org/bytedeco/javacpp/tools/Generator.java | 21 +++++ .../java/org/bytedeco/javacpp/tools/Info.java | 6 +- .../org/bytedeco/javacpp/tools/Parser.java | 92 ++++++++++++++++++- .../java/org/bytedeco/javacpp/tools/Type.java | 3 +- 6 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/bytedeco/javacpp/annotation/Downcast.java create mode 100644 src/main/java/org/bytedeco/javacpp/annotation/Renamed.java diff --git a/src/main/java/org/bytedeco/javacpp/annotation/Downcast.java b/src/main/java/org/bytedeco/javacpp/annotation/Downcast.java new file mode 100644 index 000000000..40d4f662f --- /dev/null +++ b/src/main/java/org/bytedeco/javacpp/annotation/Downcast.java @@ -0,0 +1,13 @@ +package org.bytedeco.javacpp.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented @Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE}) +public @interface Downcast { + String downcaster() default ""; +} diff --git a/src/main/java/org/bytedeco/javacpp/annotation/Renamed.java b/src/main/java/org/bytedeco/javacpp/annotation/Renamed.java new file mode 100644 index 000000000..2b9712c39 --- /dev/null +++ b/src/main/java/org/bytedeco/javacpp/annotation/Renamed.java @@ -0,0 +1,18 @@ +package org.bytedeco.javacpp.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.nio.Buffer; +import org.bytedeco.javacpp.Pointer; +import org.bytedeco.javacpp.tools.Generator; + +/** + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD}) +public @interface Renamed { +} diff --git a/src/main/java/org/bytedeco/javacpp/tools/Generator.java b/src/main/java/org/bytedeco/javacpp/tools/Generator.java index 653b7d796..7782eeb90 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Generator.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Generator.java @@ -89,6 +89,7 @@ import org.bytedeco.javacpp.annotation.Opaque; import org.bytedeco.javacpp.annotation.Platform; import org.bytedeco.javacpp.annotation.Raw; +import org.bytedeco.javacpp.annotation.Renamed; import org.bytedeco.javacpp.annotation.ValueGetter; import org.bytedeco.javacpp.annotation.ValueSetter; import org.bytedeco.javacpp.annotation.Virtual; @@ -3513,6 +3514,17 @@ static Method[] functionMethods(Class cls, boolean[] callbackAllocators) { functionMethods : null; } + String removeSuffix(String name, MethodInformation info) { + if (info.annotations != null) { + for (Annotation a : info.annotations) { + if (a instanceof Renamed) { + return name.substring(0, name.length() - Parser.RENAMED_SUFFIX.length()); + } + } + } + return name; + } + MethodInformation methodInformation(Method method) { MethodInformation info = new MethodInformation(); info.cls = method.getDeclaringClass(); @@ -3521,6 +3533,7 @@ MethodInformation methodInformation(Method method) { info.modifiers = method.getModifiers(); info.returnType = method.getReturnType(); info.name = method.getName(); + info.name = removeSuffix(info.name, info); Name name = method.getAnnotation(Name.class); info.memberName = name != null ? name.value() : new String[] { info.name }; Index index = method.getAnnotation(Index.class); @@ -3565,6 +3578,14 @@ MethodInformation methodInformation(Method method) { info2.parameterTypes = method2.getParameterTypes(); info2.annotations = method2.getAnnotations(); info2.parameterAnnotations = method2.getParameterAnnotations(); + + for (Annotation a:info2.annotations) { + if (a instanceof Renamed) { + info2.name = info2.name.substring(0, info2.name.length() - Parser.RENAMED_SUFFIX.length()); + break; + } + } + } int skipParameters = info.parameterTypes.length > 0 && info.parameterTypes[0] == Class.class ? 1 : 0; int skipParameters2 = info2.parameterTypes.length > 0 && info2.parameterTypes[0] == Class.class ? 1 : 0; diff --git a/src/main/java/org/bytedeco/javacpp/tools/Info.java b/src/main/java/org/bytedeco/javacpp/tools/Info.java index 82bf1f6f0..d99d89848 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Info.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Info.java @@ -64,7 +64,8 @@ public Info(Info i) { base = i.base; cppText = i.cppText; javaText = i.javaText; - } + downCaster = i.downCaster; + } /** A list of C++ identifiers, expressions, or header filenames to which this info is to be bound. * Usually set via the constructor parameter of {@link #Info(String...)}. */ @@ -120,7 +121,10 @@ public Info(Info i) { String cppText = null; /** Outputs the given code, instead of the result parsed from the declaration of C++ identifiers. */ String javaText = null; + /** Contains the name of the downcaster function */ + String downCaster = null; + public Info downCaster(String downCaster) { this.downCaster = downCaster; return this; } public Info cppNames(String... cppNames) { this.cppNames = cppNames; return this; } public Info javaNames(String... javaNames) { this.javaNames = javaNames; return this; } public Info annotations(String... annotations) { this.annotations = annotations; return this; } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index f41309a02..1580a7da3 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -66,6 +66,7 @@ * @author Samuel Audet */ public class Parser { + final static String RENAMED_SUFFIX = "__"; public Parser(Logger logger, Properties properties) { this(logger, properties, null, null); @@ -671,7 +672,7 @@ Type type(Context context, boolean definition) throws ParserException { } type.cppName += separator; Info info = infoMap.getFirst(t.cppName); - String s = info != null && info.cppTypes != null ? info.cppTypes[0] : t.cppName; + String s = info != null && info.cppTypes != null && info.cppTypes.length > 0 ? info.cppTypes[0] : t.cppName; if (t.constValue && !s.startsWith("const ")) { s = "const " + s; } @@ -988,6 +989,7 @@ Type type(Context context, boolean definition) throws ParserException { for (String s : info.annotations) { type.annotations += s + " "; } + type.downCaster = info.downCaster; } if (context.cppName != null && type.javaName.length() > 0) { String cppName = type.cppName; @@ -1398,6 +1400,7 @@ Declarator declarator(Context context, String defaultName, int infoNumber, boole for (String s : info2.annotations) { type.annotations += s + " "; } + type.downCaster = info2.downCaster; info = infoMap.getFirst(type.cppName, false); break; } @@ -2517,7 +2520,11 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti decl.text += "public " + context.shorten(context.javaName) + dcl.parameters.list + " { super((Pointer)null); allocate" + params.names + "; }\n" + type.annotations + "private native void allocate" + dcl.parameters.list + ";\n"; } else { - decl.text += modifiers + type.annotations + context.shorten(type.javaName) + " " + dcl.javaName + dcl.parameters.list + ";\n"; + String paramList = dcl.parameters.list.replaceAll("@Downcast", ""); + if (type.annotations.contains("@Downcast")) + decl.text += modifiers + type.annotations + "@Renamed " + context.shorten(type.javaName) + " " + dcl.javaName + RENAMED_SUFFIX + paramList + ";\n"; + else + decl.text += modifiers + type.annotations + context.shorten(type.javaName) + " " + dcl.javaName + paramList + ";\n"; } decl.signature = dcl.signature; @@ -2560,8 +2567,69 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti prevDcl.add(dcl); } declList.spacing = null; + Declaration dcli2 = null; + for (Declaration dcli : declList) { + if (dcli.function) { + if (dcli.text.contains("@Renamed")) { + + dcli2 = new Declaration(); + + //List infos = context.infoMap.get(dcli.declarator.type.cppName,false); + + dcli2.type = dcli.type; + dcli2.declarator = dcli.declarator; + dcli2.constMember = dcli.constMember; + dcli2.inaccessible = dcli.inaccessible; + dcli2.incomplete = dcli.incomplete; + dcli2.function = dcli.function; + dcli2.variable = dcli.variable; + dcli2.comment = dcli.comment; + dcli2.text = fixFun(dcli.text, info, dcli.declarator.type.downCaster); + dcli2.signature = dcli2.text; + } + } + + } + if (dcli2 != null) { + declList.add(dcli2); + } return true; } + + String fixFun(String text, Info info, String downCaster) { + if (text.endsWith("};")) + return text; + String decl = text; + decl = decl.replace(RENAMED_SUFFIX, ""); + decl = decl.replace("@Renamed", ""); + decl = decl.replace(" native ", " "); + decl = decl.replace(";", ""); + String name = decl.substring(0, decl.indexOf("(")).trim(); + name = name.substring(name.lastIndexOf(" ")); + String retype = decl.substring(0, decl.indexOf("(")).trim(); + retype = retype.substring(0, retype.lastIndexOf(" ")).trim(); + retype = retype.substring(retype.lastIndexOf(" ")).trim(); + + String params = decl.substring(decl.indexOf("(") + 1, decl.lastIndexOf(")")); + String[] alist = params.split(","); + String args = ""; + if (alist != null) { + for (String a : alist) { + String a1 = a.trim(); + String a2 = a1.substring(a1.lastIndexOf(" ") + 1); + if (args.length() > 0) + args += ", "; + args += a2; + } + } + //String n=f.substring(0,f.indexOf("("))-1).sub + + String x = "\n" + decl + "{" + "return " + "(" + retype + ")" + downCaster + "(" + name + RENAMED_SUFFIX + "(" + args + ")" + ")" + ";" + "};"; + // System.out.println("KUSTI>>> " + text); + // System.out.println(" >>> " + x); + return x; + } + boolean variable(Context context, DeclarationList declList) throws ParserException { int backIndex = tokens.index; @@ -4384,7 +4452,25 @@ public File[] parse(File outputDirectory, String[] classPath, Class cls) throws if (prevd != null) { out.append(prevd.text); } - out.append("\n}\n").close(); + + // Inject target level Java + for (Info i : infoMap.get("@" + target)) { + if (i.javaText != null) { + out.append("\n"); + out.append(i.javaText); + out.append("\n"); + } + } + + for (Info i : infoMap.get("@")) { + if (i.javaText != null) { + out.append("\n"); + out.append(i.javaText); + out.append("\n"); + } + } + + out.append("\n}\n").close(); } return outputFiles.toArray(new File[outputFiles.size()]); diff --git a/src/main/java/org/bytedeco/javacpp/tools/Type.java b/src/main/java/org/bytedeco/javacpp/tools/Type.java index 2eeeaab05..4737ae07f 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Type.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Type.java @@ -37,7 +37,8 @@ class Type { String annotations = "", cppName = "", javaName = "", javaNames[] = null; Type[] arguments = null; Attribute[] attributes = null; - + String downCaster = null; + @Override public boolean equals(Object obj) { if (obj == this) { return true;