From f89f1c370617105f940a4e7a0eabbbcb491939dd Mon Sep 17 00:00:00 2001 From: Samuel Audet Date: Wed, 24 Oct 2018 22:00:59 +0900 Subject: [PATCH] * Add `@Properties(global=...)` value to allow `Parser` to target Java packages (pull #252) --- CHANGELOG.md | 1 + README.md | 2 +- .../org/bytedeco/javacpp/ClassProperties.java | 11 +++- .../java/org/bytedeco/javacpp/Loader.java | 2 +- .../javacpp/annotation/Properties.java | 8 ++- .../org/bytedeco/javacpp/tools/Builder.java | 15 +++-- .../bytedeco/javacpp/tools/Declaration.java | 4 +- .../org/bytedeco/javacpp/tools/Parser.java | 58 ++++++++++++++++--- 8 files changed, 81 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5917dc3e..2c24afec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ + * Add `@Properties(global=...)` value to allow `Parser` to target Java packages ([pull #252](https://github.com/bytedeco/javacpp/pull/252)) * Fix `Generator` output for `@Const` parameters of function pointers ### October 15, 2018 version 1.4.3 diff --git a/README.md b/README.md index 503f72dc4..e3bc55e6c 100644 --- a/README.md +++ b/README.md @@ -432,6 +432,6 @@ And instead of `Loader.load()`, the library should be loaded with `System.load(" ---- -Project lead: Samuel Audet [samuel.audet `at` gmail.com](mailto:samuel.audet at gmail.com) +Project lead: Samuel Audet [samuel.audet `at` gmail.com](mailto:samuel.audet at gmail.com) Developer site: https://github.com/bytedeco/javacpp Discussion group: http://groups.google.com/group/javacpp-project diff --git a/src/main/java/org/bytedeco/javacpp/ClassProperties.java b/src/main/java/org/bytedeco/javacpp/ClassProperties.java index 24b43ef72..2913ce6a1 100644 --- a/src/main/java/org/bytedeco/javacpp/ClassProperties.java +++ b/src/main/java/org/bytedeco/javacpp/ClassProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2017 Samuel Audet + * Copyright (C) 2011-2018 Samuel Audet * * Licensed either under the Apache License, Version 2.0, or (at your option) * under the terms of the GNU General Public License as published by @@ -167,6 +167,15 @@ public void load(Class cls, boolean inherit) { if (target.length() > 0) { addAll("target", target); } + String global = classProperties.global(); + if (global.length() == 0) { + global = target; + } else if (target.length() > 0 && !global.startsWith(target)) { + global = target + "." + global; + } + if (global.length() > 0) { + addAll("global", global); + } String helper = classProperties.helper(); if (helper.length() > 0) { addAll("helper", helper); diff --git a/src/main/java/org/bytedeco/javacpp/Loader.java b/src/main/java/org/bytedeco/javacpp/Loader.java index 2dc42012c..d07ff9b6f 100644 --- a/src/main/java/org/bytedeco/javacpp/Loader.java +++ b/src/main/java/org/bytedeco/javacpp/Loader.java @@ -933,7 +933,7 @@ public static String load(Class cls, Properties properties, boolean pathsFirst) ClassProperties p = loadProperties(cls, properties, true); // Force initialization of all the target classes in case they need it - List targets = p.get("target"); + List targets = p.get("global"); if (targets.isEmpty()) { if (p.getInheritedClasses() != null) { for (Class c : p.getInheritedClasses()) { diff --git a/src/main/java/org/bytedeco/javacpp/annotation/Properties.java b/src/main/java/org/bytedeco/javacpp/annotation/Properties.java index 056c931ea..c912f2f2f 100644 --- a/src/main/java/org/bytedeco/javacpp/annotation/Properties.java +++ b/src/main/java/org/bytedeco/javacpp/annotation/Properties.java @@ -40,9 +40,13 @@ String[] names() default {}; /** A list of properties for different platforms. */ Platform[] value() default {}; - /** The target Java source code file of the {@link Parser}. */ + /** The target Java source code file of the {@link Parser}, unless {@link #global()} is set, + in which case this specifies the target Java package. */ String target() default ""; - /** An optional helper class the {@link Parser} should use as base of the target. + /** The name of a class where to output any global declarations that are not in classes. + If left empty, considers the {@link #target()} as a class where to put everything. */ + String global() default ""; + /** An optional helper class the {@link Parser} should use as base for the global class. Defaults to the class where this annotation was found. */ String helper() default ""; } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Builder.java b/src/main/java/org/bytedeco/javacpp/tools/Builder.java index ff955276e..323a1cc20 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Builder.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Builder.java @@ -936,11 +936,18 @@ public File[] build() throws IOException, InterruptedException, ParserException } catch (ClassCastException | InstantiationException | IllegalAccessException e) { // fail silently as if the interface wasn't implemented } - String target = p.getProperty("target"); + String target = p.getProperty("global"); if (target != null && !c.getName().equals(target)) { - File f = parse(classScanner.getClassLoader().getPaths(), c); - if (f != null) { - outputFiles.add(f); + boolean found = false; + for (Class c2 : classScanner.getClasses()) { + // do not try to regenerate classes that are already loaded + found |= c2.getName().equals(target); + } + if (!found) { + File f = parse(classScanner.getClassLoader().getPaths(), c); + if (f != null) { + outputFiles.add(f); + } } continue; } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Declaration.java b/src/main/java/org/bytedeco/javacpp/tools/Declaration.java index 6ca688a30..1e9c601d2 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Declaration.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Declaration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Samuel Audet + * Copyright (C) 2014-2018 Samuel Audet * * Licensed either under the Apache License, Version 2.0, or (at your option) * under the terms of the GNU General Public License as published by @@ -30,6 +30,6 @@ class Declaration { Type type = null; Declarator declarator = null; boolean abstractMember = false, constMember = false, inaccessible = false, - incomplete = false, function = false, variable = false; + incomplete = false, function = false, variable = false, comment = false; String signature = "", text = ""; } diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index f9d4c748d..78f4c925f 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -26,6 +26,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.Writer; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -225,6 +226,7 @@ void containers(Context context, DeclarationList declList) throws ParserExceptio for (int i = 0; i < dim - 1; i++) { arrayBrackets += "[]"; } + decl.type = new Type(containerType.javaName); decl.text += (dim == 0 ? "\n@NoOffset " : "\n") + "@Name(\"" + containerType.cppName + "\") public static class " + containerType.javaName + " extends Pointer {\n" + " static { Loader.load(); }\n" @@ -1352,6 +1354,7 @@ Declarator declarator(Context context, String defaultName, int infoNumber, boole } functionType = functionType.substring(functionType.lastIndexOf(' ') + 1); // get rid of pointer annotations if (!functionType.equals("Pointer")) { + definition.type = new Type(functionType); definition.text += (tokens.get().match(Token.CONST, Token.__CONST, Token.CONSTEXPR) ? "@Const " : "") + "public static class " + functionType + " extends FunctionPointer {\n" + " static { Loader.load(); }\n" + @@ -2541,6 +2544,7 @@ boolean typedef(Context context, DeclarationList declList) throws ParserExceptio } else if (context.namespace != null && context.javaName == null) { decl.text += "@Namespace(\"" + context.namespace + "\") "; } + decl.type = new Type(dcl.javaName); decl.text += "@Opaque public static class " + dcl.javaName + " extends Pointer {\n" + " /** Empty constructor. Calls {@code super((Pointer)null)}. */\n" + " public " + dcl.javaName + "() { super((Pointer)null); }\n" + @@ -2820,6 +2824,7 @@ boolean group(Context context, DeclarationList declList) throws ParserException } else if (context.namespace != null && context.javaName == null) { decl.text += "@Namespace(\"" + context.namespace + "\") "; } + decl.type = new Type(name); decl.text += "@Opaque public static class " + name + " extends " + base.javaName + " {\n" + " /** Empty constructor. Calls {@code super((Pointer)null)}. */\n" + " public " + name + "() { super((Pointer)null); }\n" + @@ -3365,6 +3370,7 @@ void declarations(Context context, DeclarationList declList) throws ParserExcept if (comment != null && comment.length() > 0) { decl.inaccessible = ctx.inaccessible; decl.text = comment; + decl.comment = true; declList.add(decl); } int startIndex = tokens.index; @@ -3433,6 +3439,7 @@ void declarations(Context context, DeclarationList declList) throws ParserExcept Declaration decl = new Declaration(); if (comment != null && comment.length() > 0) { decl.text = comment; + decl.comment = true; declList.add(decl); } } @@ -3521,9 +3528,12 @@ public File parse(File outputDirectory, String[] classPath, Class cls) throws IO allIncludes.addAll(allProperties.get("platform.include")); allIncludes.addAll(allProperties.get("platform.cinclude")); List allTargets = allProperties.get("target"); + List allGlobals = allProperties.get("global"); List clsTargets = clsProperties.get("target"); + List clsGlobals = clsProperties.get("global"); List clsHelpers = clsProperties.get("helper"); String target = clsTargets.get(0); // there can only be one + String global = clsGlobals.get(0); List allInherited = allProperties.getInheritedClasses(); infoMap = new InfoMap(); @@ -3555,9 +3565,11 @@ public File parse(File outputDirectory, String[] classPath, Class cls) throws IO version = "unknown"; } String text = "// Targeted by JavaCPP version " + version + ": DO NOT EDIT THIS FILE\n\n"; - int n = target.lastIndexOf('.'); + String targetPackage = ""; + int n = global.lastIndexOf('.'); if (n >= 0) { - text += "package " + target.substring(0, n) + ";\n\n"; + targetPackage = global.substring(0, n); + text += "package " + targetPackage + ";\n\n"; } List infoList = leafInfoMap.get(null); for (Info info : infoList) { @@ -3568,19 +3580,24 @@ public File parse(File outputDirectory, String[] classPath, Class cls) throws IO text += "import java.nio.*;\n" + "import org.bytedeco.javacpp.*;\n" + "import org.bytedeco.javacpp.annotation.*;\n\n"; - for (String s : allTargets) { - if (!target.equals(s)) { - text += "import static " + s + ".*;\n"; + for (int i = 0; i < allTargets.size(); i++) { + if (!target.equals(allTargets.get(i))) { + if (allTargets.get(i).equals(allGlobals.get(i))) { + text += "import static " + allTargets.get(i) + ".*;\n"; + } else { + text += "import " + allTargets.get(i) + ".*;\n" + + "import static " + allGlobals.get(i) + ".*;\n"; + } } } if (allTargets.size() > 1) { text += "\n"; } - text += "public class " + target.substring(n + 1) + " extends " + String globalText = text + "public class " + global.substring(n + 1) + " extends " + (clsHelpers.size() > 0 && clsIncludes.size() > 0 ? clsHelpers.get(0) : cls.getCanonicalName()) + " {\n" + " static { Loader.load(); }\n"; - String targetPath = target.replace('.', File.separatorChar); + String targetPath = global.replace('.', File.separatorChar); File targetFile = new File(outputDirectory, targetPath + ".java"); logger.info("Targeting " + targetFile); Context context = new Context(); @@ -3636,14 +3653,37 @@ public File parse(File outputDirectory, String[] classPath, Class cls) throws IO } try (Writer out = encoding != null ? new EncodingFileWriter(targetFile, encoding, lineSeparator) : new EncodingFileWriter(targetFile, lineSeparator)) { - out.append(text); + out.append(globalText); for (Info info : infoList) { if (info.javaText != null && !info.javaText.startsWith("import")) { out.append(info.javaText + "\n"); } } + Declaration prevd = null; for (Declaration d : declList) { - out.append(d.text); + if (!target.equals(global) && d.type != null && d.type.javaName != null && d.type.javaName.length() > 0) { + // if the user gave us a class name for "global", we're targeting a package, so output global classes into their own files + File javaFile = new File(targetDir, d.type.javaName + ".java"); + if (prevd != null && !prevd.comment) { + out.append(prevd.text); + } + out.append("\n// Targeting " + d.type.javaName + ".java" + "\n\n"); + logger.info("Targeting " + javaFile); + String javaText = text + "import static " + global + ".*;\n" + + "@Platform(library = \"" + clsProperties.getProperty("platform.library") + "\")\n" + + (prevd != null && prevd.comment ? prevd.text : "") + + d.text.replace("public static class " + d.type.javaName + " ", "public class " + d.type.javaName + " "); + Files.write(javaFile.toPath(), encoding != null ? javaText.getBytes(encoding) : javaText.getBytes()); + prevd = null; + } else { + if (prevd != null) { + out.append(prevd.text); + } + prevd = d; + } + } + if (prevd != null) { + out.append(prevd.text); } out.append("\n}\n").close(); }