diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/Util.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/Util.java
index 7081bb276..a32478119 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/Util.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/Util.java
@@ -163,18 +163,33 @@ public static String upperCaseFirstChar(String _str) {
* @return camel case string or input if nothing todo. Returns null if input was null.
*/
public static String snakeToCamelCase(String _input) {
+ return toCamelCase("_", _input);
+ }
+
+ /**
+ * Converts a kabab-case-string to camel case string.
+ *
+ * Eg. this-is-kebab-case → thisIsSnakeCase
+ * @param _input string
+ * @return camel case string or input if nothing todo. Returns null if input was null.
+ */
+ public static String kebabToCamelCase(String _input) {
+ return toCamelCase("-", _input);
+ }
+
+ private static String toCamelCase(String _delimiter, String _input) {
if (isBlank(_input)) {
return _input;
}
- Pattern compile = Pattern.compile("_[a-zA-Z]");
+ Pattern compile = Pattern.compile(Pattern.quote(_delimiter) + "[a-zA-Z0-9]");
Matcher matcher = compile.matcher(_input);
String result = _input;
while (matcher.find()) {
String match = matcher.group();
- String replacement = match.replace("_", "");
+ String replacement = match.replace(_delimiter, "");
replacement = replacement.toUpperCase();
result = result.replaceFirst(match, replacement);
diff --git a/dbus-java-tests/src/test/java/org/freedesktop/dbus/utils/UtilTest.java b/dbus-java-tests/src/test/java/org/freedesktop/dbus/utils/UtilTest.java
index 5b3bb88db..f40d634d0 100644
--- a/dbus-java-tests/src/test/java/org/freedesktop/dbus/utils/UtilTest.java
+++ b/dbus-java-tests/src/test/java/org/freedesktop/dbus/utils/UtilTest.java
@@ -21,4 +21,15 @@ void testRequireMinimum(String _name, int _minVal, int _testVal, boolean _throws
assertEquals(_testVal, result);
}
}
+
+ @ParameterizedTest(name = "{index}: {0}")
+ @CsvSource({
+ "No Snake,This is no snake,This is no snake",
+ "Snake,This_is_a_snake,ThisIsASnake",
+ "Partial Snake,This_is_partial snake,ThisIsPartial snake",
+ "Snake with numbers,This_is_0_8_15_snake,ThisIs0815Snake",
+ })
+ void testSnakeToCamelCase(String _name, String _input, String _expected) {
+ assertEquals(_expected, Util.snakeToCamelCase(_input));
+ }
}
diff --git a/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/ClassBuilderInfo.java b/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/ClassBuilderInfo.java
index e0720fe70..2b4bd48a8 100644
--- a/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/ClassBuilderInfo.java
+++ b/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/ClassBuilderInfo.java
@@ -1,9 +1,16 @@
package org.freedesktop.dbus.utils.generator;
+import org.freedesktop.dbus.annotations.DBusBoundProperty;
import org.freedesktop.dbus.annotations.DBusInterfaceName;
+import org.freedesktop.dbus.annotations.DBusMemberName;
+import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.utils.Util;
import org.freedesktop.dbus.utils.bin.IdentifierMangler;
+import org.freedesktop.dbus.utils.generator.ClassBuilderInfo.AnnotationInfo.AnnotArgs;
+import org.slf4j.LoggerFactory;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
import java.io.File;
import java.lang.annotation.Annotation;
import java.util.*;
@@ -19,7 +26,9 @@
*/
public class ClassBuilderInfo {
- private static final String DEFAULT_INDENT = " ";
+ private static final Set RESERVED_METHOD_NAMES = getReservedMethods(Message.class);
+
+ private static final String DEFAULT_INDENT = " ";
/** Imported files for this class. */
private final Set imports = new TreeSet<>();
/** Annotations of this class. */
@@ -159,17 +168,19 @@ public String createClassFileContent() {
* @return
*/
private List createClassFileContent(boolean _staticClass, Set _otherImports) {
- List content = new ArrayList<>();
final String classIndent = _staticClass ? DEFAULT_INDENT : "";
final String memberIndent = _staticClass ? DEFAULT_INDENT.repeat(2) : DEFAULT_INDENT;
Set allImports = new TreeSet<>();
allImports.addAll(getImports());
+
if (_otherImports != null) {
allImports.addAll(_otherImports);
}
+ List content = new ArrayList<>();
+
if (!_staticClass) {
content.add("package " + getPackageName() + ";");
content.add("");
@@ -188,6 +199,8 @@ private List createClassFileContent(boolean _staticClass, Set _o
for (AnnotationInfo annotation : annotations) {
allImports.add(annotation.getAnnotationClass().getName());
+ allImports.addAll(annotation.getAdditionalImports().stream().map(Class::getName).toList());
+
String annotationCode = classIndent + "@" + annotation.getAnnotationClass().getSimpleName();
if (annotation.getAnnotationParams() != null) {
annotationCode += "(" + annotation.getAnnotationParams() + ")";
@@ -222,7 +235,11 @@ private List createClassFileContent(boolean _staticClass, Set _o
// add member fields
for (MemberOrArgument member : members) {
if (!member.getAnnotations().isEmpty()) {
- content.addAll(member.getAnnotations().stream().map(l -> memberIndent + l).toList());
+ member.getAnnotations().stream().forEach(l -> {
+ content.add(memberIndent + l.getAnnotationString());
+ allImports.addAll(l.getAdditionalImports().stream().map(Class::getName).toList());
+ allImports.add(l.getAnnotationClass().getName());
+ });
}
content.add(memberIndent + "private " + member.asOneLineString(allImports, "", false) + ";");
}
@@ -265,6 +282,9 @@ private List createClassFileContent(boolean _staticClass, Set _o
.filter(l -> l.contains(".")) // no dots in name means this is only a class name so we are in same package and don't need to import
.map(l -> "import " + l + ";")
.toList());
+ } else {
+ // add the collected additional imports to the provided set when creating inner class
+ _otherImports.addAll(allImports);
}
return content;
@@ -367,6 +387,28 @@ static Set getImportsForType(String _type) {
return imports;
}
+ static Set getReservedMethods(Class> _class) {
+ try {
+ return Arrays.stream(Introspector.getBeanInfo(_class).getMethodDescriptors())
+ .map(e -> e.getMethod().getName())
+ .collect(Collectors.toSet());
+ } catch (IntrospectionException _ex) {
+ LoggerFactory.getLogger(ClassBuilderInfo.class).error("Could not extract method names from {}", _class, _ex);
+ return Set.of();
+ }
+ }
+
+ /**
+ * Check if the provided method name classes with any method name found in {@link #RESERVED_METHOD_NAMES}.
+ * Will check the given method name with usual Java method prefixes (get/is/set) as well.
+ * @param _methodName method name
+ * @return true if reserved
+ */
+ static boolean isReservedMethodName(String _methodName) {
+ return RESERVED_METHOD_NAMES.contains(_methodName) || RESERVED_METHOD_NAMES.contains("set" + _methodName)
+ || RESERVED_METHOD_NAMES.contains("get" + _methodName) || RESERVED_METHOD_NAMES.contains("is" + _methodName);
+ }
+
/**
* Contains information about annotation to place on classes, members or methods.
*
@@ -374,23 +416,124 @@ static Set getImportsForType(String _type) {
* @since v3.2.1 - 2019-11-13
*/
public static class AnnotationInfo {
+
/** Annotation class. */
private final Class extends Annotation> annotationClass;
- /** Annotation params (e.g. value = "foo", key = "bar"). */
- private final String annotationParams;
+ /** Map of parameters for the annotation (should be ordered). */
+ private final Map annotationParams = new LinkedHashMap<>();
- public AnnotationInfo(Class extends Annotation> _annotationClass, String _annotationParams) {
+ private final Set> additionalImports = new LinkedHashSet<>();
+
+ public AnnotationInfo(Class extends Annotation> _annotationClass, AnnotArgs _annotationParams) {
annotationClass = _annotationClass;
- annotationParams = _annotationParams;
+ if (_annotationParams != null) {
+ _annotationParams.args.forEach(e -> {
+ annotationParams.put(e.key(), e.value());
+ if (e.value() != null && !e.value().getClass().getPackage().getName().startsWith("java.lang")) {
+ additionalImports.add(e.value().getClass());
+ }
+ });
+ }
}
public Class extends Annotation> getAnnotationClass() {
return annotationClass;
}
- public String getAnnotationParams() {
+ public Map getAnnotationParams() {
return annotationParams;
}
+
+ public Set> getAdditionalImports() {
+ return additionalImports;
+ }
+
+ public String getAnnotationString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("@").append(getAnnotationClass().getSimpleName());
+
+ if (!getAnnotationParams().isEmpty()) {
+ sb.append("(");
+
+ if (getAnnotationParams().size() == 1 && "value".equals(getAnnotationParams().keySet().iterator().next())) {
+ sb.append(handleArg(getAnnotationParams().values().iterator().next()));
+ } else {
+ getAnnotationParams().forEach((k, v) -> {
+ sb.append(k).append(" = ");
+ sb.append(handleArg(v));
+ });
+ }
+
+ sb.append(")");
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return "AnnotationInfo [annotationClass=" + annotationClass + ", annotationParams=" + annotationParams + ", additionalImports=" + additionalImports + "]";
+ }
+
+ private String handleArg(Object _value) {
+ if (_value instanceof String s) {
+ return "\"" + s + "\"";
+ } else {
+ return String.valueOf(_value);
+ }
+ }
+
+ public static final class AnnotArgs {
+ private final Set args = new LinkedHashSet<>();
+
+ private AnnotArgs() {
+
+ }
+
+ public AnnotArgs add(String _key, Object _val) {
+ Objects.requireNonNull(_key);
+ Objects.requireNonNull(_val);
+
+ args.add(new AnnotArg(_key, _val));
+ return this;
+ }
+
+ /**
+ * Shortcut for {@code #add("value", _val)}.
+ * @param _val value to set for "value" option
+ * @return this
+ */
+ public AnnotArgs add(Object _val) {
+ return this.add("value", _val);
+ }
+
+ public static AnnotArgs create() {
+ return new AnnotArgs();
+ }
+
+ record AnnotArg(String key, Object value) {
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key);
+ }
+
+ @Override
+ public boolean equals(Object _obj) {
+ if (this == _obj) {
+ return true;
+ }
+ if (_obj == null) {
+ return false;
+ }
+ if (getClass() != _obj.getClass()) {
+ return false;
+ }
+ AnnotArg other = (AnnotArg) _obj;
+ return Objects.equals(key, other.key);
+ }
+ }
+ }
}
/**
@@ -402,24 +545,31 @@ public String getAnnotationParams() {
public static class ClassMethod {
private static final String METHOD_TEMPL = """
- %s%s %s(%s);
+ %s%s %s%s(%s);
""";
/** Name of this method. */
private final String name;
/** Return value of the method. */
private final String returnType;
+ /** Prefix for method name (e.g. get or is), {@code null} if not needed. */
+ private final String methodPrefix;
/** True if method should be final, false otherwise. */
private final boolean finalMethod;
/** Arguments for this method, key is argument name, value is argument type. */
- private final List arguments = new ArrayList<>();
+ private final List arguments = new ArrayList<>();
/** List of annotations for this method. */
- private final List annotations = new ArrayList<>();
+ private final List annotations = new ArrayList<>();
public ClassMethod(String _name, String _returnType, boolean _finalMethod) {
+ this(_name, _returnType, null, _finalMethod);
+ }
+
+ public ClassMethod(String _name, String _returnType, String _methodPrefix, boolean _finalMethod) {
name = _name;
returnType = _returnType;
finalMethod = _finalMethod;
+ methodPrefix = _methodPrefix;
}
public String getName() {
@@ -430,6 +580,10 @@ public String getReturnType() {
return returnType;
}
+ public String getMethodPrefix() {
+ return methodPrefix;
+ }
+
public boolean isFinalMethod() {
return finalMethod;
}
@@ -438,27 +592,53 @@ public List getArguments() {
return arguments;
}
- public List getAnnotations() {
+ public List getAnnotations() {
return annotations;
}
public List generateCode(boolean _isInterface, String _argumentPrefix, String _indent, Set _allImports) {
List result = new ArrayList<>();
- if (!getAnnotations().isEmpty()) {
- result.addAll(getAnnotations().stream().map(a -> _indent + a).toList());
+
+ String methodName = Util.kebabToCamelCase(Util.snakeToCamelCase(getName()));
+
+ List currentAnnotations = new ArrayList<>(getAnnotations());
+
+ // java method name differs from bus method name -> add annotation to mitigate
+ if (!getName().equals(methodName)) {
+ if (currentAnnotations.stream().anyMatch(e -> e.getAnnotationClass() == DBusBoundProperty.class)) {
+ currentAnnotations.stream().filter(e -> e.getAnnotationClass() == DBusBoundProperty.class)
+ .forEach(e -> e.getAnnotationParams().put("name", getName()));
+ } else {
+ currentAnnotations.add(new AnnotationInfo(DBusMemberName.class, AnnotArgs.create().add(getName())));
+ }
+ }
+
+ if (!currentAnnotations.isEmpty()) {
+ result.addAll(currentAnnotations.stream().map(a -> _indent + a.getAnnotationString()).toList());
}
String publicModifier = !_isInterface ? "public " : "";
String mthReturnType = getReturnType() == null ? "void"
: TypeConverter.getProperJavaClass(getReturnType(), _allImports);
String args = "";
+
if (!getArguments().isEmpty()) {
args += getArguments().stream()
.map(e -> e.asOneLineString(_allImports, _argumentPrefix, true))
.collect(Collectors.joining(", "));
}
- METHOD_TEMPL.formatted(publicModifier, mthReturnType, getName(), args)
+ String prefix = "";
+ if (!Util.isBlank(getMethodPrefix())) {
+ methodName = Util.upperCaseFirstChar(methodName);
+ prefix = getMethodPrefix();
+ }
+
+ if (isReservedMethodName(methodName) || isReservedMethodName(prefix + methodName)) {
+ methodName += methodName + "FromBus";
+ }
+
+ METHOD_TEMPL.formatted(publicModifier, mthReturnType, prefix, methodName, args)
.lines().map(l -> _indent + l).forEach(result::add);
return result;
@@ -474,6 +654,8 @@ public List generateCode(boolean _isInterface, String _argumentPrefix, S
*/
public static class MemberOrArgument {
+ private static final String GETTER_SETTER_ANNOTATION = "@%s(\"%s\")";
+
private static final String GETTER_TEMPL = """
public %s get%s() {
%sreturn %s;
@@ -495,7 +677,7 @@ public static class MemberOrArgument {
/** List of classes/types or placeholders put into diamond operators to use as generics. */
private final List generics = new ArrayList<>();
/** List of annotations for this member. */
- private final List annotations = new ArrayList<>();
+ private final List annotations = new ArrayList<>();
public MemberOrArgument(String _name, String _type, boolean _finalMember) {
// repair reserved words by adding 'Param' as appendix, and when start with _ too
@@ -508,7 +690,7 @@ public MemberOrArgument(String _name, String _type) {
this(_name, _type, false);
}
- public List getAnnotations() {
+ public List getAnnotations() {
return annotations;
}
@@ -553,7 +735,7 @@ public String asOneLineString(Set _allImports, String _prefix, boolean _
}
if (_includeAnnotations && !getAnnotations().isEmpty()) {
- sb.append(String.join(" ", getAnnotations()))
+ sb.append(String.join(" ", getAnnotations().stream().map(e -> e.getAnnotationString()).toList()))
.append(" ");
}
@@ -574,14 +756,33 @@ public List generateCode(String _indent, String _prefix, Set _al
memberType += "<" + getGenerics().stream().map(c -> TypeConverter.convertJavaType(c, false)).collect(Collectors.joining(", ")) + ">";
}
- String getterSetterName = Util.snakeToCamelCase(Util.upperCaseFirstChar(getName()));
+ String getterSetterName = Util.kebabToCamelCase(Util.snakeToCamelCase(Util.upperCaseFirstChar(getName())));
+
+ String getterAnnotation = "";
+
+ if (isReservedMethodName(getterSetterName)) {
+ getterAnnotation = GETTER_SETTER_ANNOTATION.formatted(DBusMemberName.class.getSimpleName(), getterSetterName);
+
+ if (isReservedMethodName(getterSetterName)) {
+ _allImports.add(DBusMemberName.class.getName());
+ }
+
+ getterSetterName += "FromBus";
+ }
+
if (!isFinalArg()) {
+ if (!Util.isBlank(getterAnnotation)) {
+ result.add(_indent + getterAnnotation);
+ }
SETTER_TEMPL.formatted(getterSetterName, memberType,
maybePrefix("arg", _prefix), DEFAULT_INDENT, getName(), maybePrefix("arg", _prefix))
.lines().map(l -> _indent + l).forEach(result::add);
}
addEmptyLineIfNeeded(result);
+ if (!Util.isBlank(getterAnnotation)) {
+ result.add(_indent + getterAnnotation);
+ }
GETTER_TEMPL.formatted(memberType, getterSetterName, DEFAULT_INDENT, getName())
.lines().map(l -> _indent + l).forEach(result::add);
@@ -657,13 +858,14 @@ public List generatedCode(String _indent, String _className, String _arg
if (!getSuperArguments().isEmpty()) {
assignments = " ".repeat(_indent.length() / 2) + "super(" + getSuperArguments().stream()
.map(e -> maybePrefix(e.getName(), _argumentPrefix))
- .collect(Collectors.joining(", ")) + ");";
+ .collect(Collectors.joining(", ")) + ");" + System.lineSeparator();
}
if (!getArguments().isEmpty()) {
List assigns = new ArrayList<>();
+ String innerIndent = " ".repeat(_indent.length() / 2);
for (MemberOrArgument e : getArguments()) {
- assigns.add(_indent + "this." + e.getName() + " = " + maybePrefix(e.getName(), _argumentPrefix) + ";");
+ assigns.add(innerIndent + "this." + e.getName() + " = " + maybePrefix(e.getName(), _argumentPrefix) + ";");
}
assignments += String.join(System.lineSeparator(), assigns);
}
diff --git a/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/InterfaceCodeGenerator.java b/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/InterfaceCodeGenerator.java
index 25ea2a3c5..8d300bd40 100644
--- a/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/InterfaceCodeGenerator.java
+++ b/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/InterfaceCodeGenerator.java
@@ -19,6 +19,7 @@
import org.freedesktop.dbus.utils.Util;
import org.freedesktop.dbus.utils.XmlUtil;
import org.freedesktop.dbus.utils.generator.ClassBuilderInfo.*;
+import org.freedesktop.dbus.utils.generator.ClassBuilderInfo.AnnotationInfo.AnnotArgs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
@@ -47,6 +48,8 @@
*/
public class InterfaceCodeGenerator {
+ private static final String STRUCT_CLASS_SUFFIX = "Struct";
+
private final DocumentBuilderFactory docFac = DocumentBuilderFactory.newInstance();
private final Logger logger = LoggerFactory.getLogger(getClass());
@@ -186,7 +189,8 @@ private Map extractAll(Element _ife) throws IOException, DBusExcep
interfaceClass.setClassName(className);
if (forcePackageName != null) {
interfaceClass.getAnnotations().add(new AnnotationInfo(DBusInterfaceName.class,
- "\"" + originalPackageName + "." + className + "\""));
+ AnnotArgs.create().add(originalPackageName + "." + className)
+ ));
}
interfaceClass.setExtendClass(DBusInterface.class.getName());
@@ -197,7 +201,7 @@ private Map extractAll(Element _ife) throws IOException, DBusExcep
switch (element.getTagName().toLowerCase()) {
case "method" -> additionalClasses.addAll(extractMethods(element, interfaceClass));
case "property" -> additionalClasses.addAll(extractProperties(element, interfaceClass));
- case "signal" -> extractSignals(element, interfaceClass);
+ case "signal" -> additionalClasses.addAll(extractSignals(element, interfaceClass));
}
}
@@ -214,11 +218,12 @@ private Map extractAll(Element _ife) throws IOException, DBusExcep
*
* @param _signalElement signal xml element
* @param _clzBldr {@link ClassBuilderInfo} object
+ * @return list containing additionally created class or empty list
*
* @throws IOException on IO Error
* @throws DBusException on DBus Error
*/
- private void extractSignals(Element _signalElement, ClassBuilderInfo _clzBldr) throws IOException, DBusException {
+ private List extractSignals(Element _signalElement, ClassBuilderInfo _clzBldr) throws IOException, DBusException {
String className = _signalElement.getAttribute("name");
if (className.contains(".")) {
@@ -234,6 +239,7 @@ private void extractSignals(Element _signalElement, ClassBuilderInfo _clzBldr) t
_clzBldr.getInnerClasses().add(innerClass);
List argsList = new ArrayList<>();
+ List additionalClasses = new ArrayList<>();
if (!_signalElement.hasChildNodes()) { // signal without any input/output?!
logger.info("Signal without any input/output arguments. Creating empty signal class: {}", innerClass.getFqcn());
@@ -244,8 +250,11 @@ private void extractSignals(Element _signalElement, ClassBuilderInfo _clzBldr) t
int unknownArgCnt = 0;
for (Element argElm : signalArgs) {
- String argType = TypeConverter.getJavaTypeFromDBusType(argElm.getAttribute("type"), _clzBldr.getImports());
+ // _clzBldr, additionalClasses, methodElementName, argElm, argName
String argName = Util.snakeToCamelCase(argElm.getAttribute("name"));
+
+ String argType = extractOrCreateArgType(_clzBldr, additionalClasses, className, argElm.getAttribute("type"), argName);
+ TypeConverter.getJavaTypeFromDBusType(argElm.getAttribute("type"), _clzBldr.getImports());
if (Util.isBlank(argName)) {
argName = "arg" + unknownArgCnt;
unknownArgCnt++;
@@ -269,6 +278,8 @@ private void extractSignals(Element _signalElement, ClassBuilderInfo _clzBldr) t
classConstructor.getSuperArguments().addAll(argsList);
innerClass.getConstructors().add(classConstructor);
+
+ return additionalClasses;
}
/**
@@ -300,13 +311,7 @@ private List extractMethods(Element _methodElement, ClassBuild
String argType;
String argName = argElm.getAttribute("name");
- if (argElm.getAttribute("type").contains("(")) { // this argument requires some sort of struct
- String structPart = argElm.getAttribute("type").replaceAll("(\\(.+\\))", "$1");
- String paramName = Util.defaultString(Util.upperCaseFirstChar(Util.snakeToCamelCase(argName)), "");
- argType = buildStructClass(structPart, methodElementName + paramName + "Struct", _clzBldr, additionalClasses);
- } else {
- argType = TypeConverter.getJavaTypeFromDBusType(argElm.getAttribute("type"), _clzBldr.getImports());
- }
+ argType = extractOrCreateArgType(_clzBldr, additionalClasses, methodElementName, argElm.getAttribute("type"), argName);
if (Util.isBlank(argName)) {
argName = "arg" + unknownArgNameCnt;
@@ -331,7 +336,7 @@ private List extractMethods(Element _methodElement, ClassBuild
List genericTypes = new ArrayList<>();
if (avoidUsingTuple) {
- resultType = buildStructClass(dbusSignatures, methodElementName + "Struct", _clzBldr, additionalClasses);
+ resultType = buildStructClass(dbusSignatures, methodElementName + STRUCT_CLASS_SUFFIX, _clzBldr, additionalClasses);
} else {
resultType = createTuple(outputArgs, methodElementName + "Tuple", _clzBldr, additionalClasses, genericTypes);
}
@@ -370,6 +375,24 @@ private List extractMethods(Element _methodElement, ClassBuild
}
+ private String extractOrCreateArgType(ClassBuilderInfo _clzBldr, List _additionalClasses,
+ String _methodElementName, String _argTypeStringFromElement, String _argName) throws DBusException {
+ String argType;
+ if (_argTypeStringFromElement.contains("(")) { // this argument requires some sort of struct
+ String structPart = _argTypeStringFromElement.replaceAll("(\\(.+\\))", "$1");
+ String paramName = Util.defaultString(Util.upperCaseFirstChar(Util.snakeToCamelCase(_argName)), "");
+ String parentType = buildStructClass(structPart, _methodElementName + paramName + STRUCT_CLASS_SUFFIX, _clzBldr, _additionalClasses);
+ if (parentType != null) {
+ argType = parentType;
+ } else {
+ argType = null;
+ }
+ } else {
+ argType = TypeConverter.getJavaTypeFromDBusType(_argTypeStringFromElement, _clzBldr.getImports());
+ }
+ return argType;
+ }
+
/**
* Extract <property> elements properties.
*
@@ -410,7 +433,7 @@ private List extractProperties(Element _propertyElement, Class
} else if (attrType.contains("(")) {
// contains structure
String structPart = attrType.replaceAll("(\\(.+\\))", "$1");
- type = buildStructClass(structPart, "Property" + attrName + "Struct", _clzBldr, additionalClasses);
+ type = buildStructClass(structPart, "Property" + attrName + STRUCT_CLASS_SUFFIX, _clzBldr, additionalClasses);
isStruct = true;
} else {
type = TypeConverter.getJavaTypeFromDBusType(attrType, _clzBldr.getImports());
@@ -444,34 +467,41 @@ private List extractProperties(Element _propertyElement, Class
String rtnType = origType != null ? origType : clzzName;
- ClassMethod classMethod = new ClassMethod(
- ("boolean".equalsIgnoreCase(clzzName) ? "is" : "get") + attrName, rtnType, false);
+ String methodPrefix = "boolean".equalsIgnoreCase(clzzName) ? "is" : "get";
+ ClassMethod classMethod = new ClassMethod(attrName, rtnType, methodPrefix, false);
_clzBldr.getMethods().add(classMethod);
+ AnnotArgs annotArgs = AnnotArgs.create();
+
if (propertyTypeRef != null) {
- classMethod.getAnnotations().add("@" + DBusBoundProperty.class.getSimpleName() + "(type = " + propertyTypeRef.getClassName() + ".class)");
+ annotArgs.add("type", propertyTypeRef.getClassName() + ".class");
} else if (isStruct) {
- classMethod.getAnnotations().add("@" + DBusBoundProperty.class.getSimpleName() + "(type = " + clzzName + ".class)");
- } else {
- classMethod.getAnnotations().add("@" + DBusBoundProperty.class.getSimpleName());
+ annotArgs.add("type", clzzName + ".class");
}
+
+ classMethod.getAnnotations().add(new AnnotationInfo(DBusBoundProperty.class, annotArgs));
+
_clzBldr.getImports().add(DBusBoundProperty.class.getName());
}
if (DBusProperty.Access.WRITE.getAccessName().equals(attrAccess)
|| DBusProperty.Access.READ_WRITE.getAccessName().equals(attrAccess)) {
- ClassMethod classMethod = new ClassMethod("set" + attrName, "void", false);
+
+ ClassMethod classMethod = new ClassMethod(attrName, "void", "set", false);
classMethod.getArguments().add(new MemberOrArgument(attrName.substring(0, 1).toLowerCase() + attrName.substring(1), clzzName));
_clzBldr.getMethods().add(classMethod);
- classMethod.getAnnotations().add("@" + DBusBoundProperty.class.getSimpleName());
+
+ classMethod.getAnnotations().add(new AnnotationInfo(DBusBoundProperty.class, null));
+
_clzBldr.getImports().add(DBusBoundProperty.class.getName());
}
} else {
- String annotationParams = "name = \"" + attrName + "\", "
- + "type = " + clzzName + ".class, "
- + "access = " + DBusProperty.Access.class.getSimpleName() + "." + access;
+ AnnotArgs annotArgs = AnnotArgs.create()
+ .add("name", attrName)
+ .add("type", clzzName)
+ .add("access", DBusProperty.Access.class.getSimpleName() + "." + access);
- AnnotationInfo annotationInfo = new AnnotationInfo(DBusProperty.class, annotationParams);
+ AnnotationInfo annotationInfo = new AnnotationInfo(DBusProperty.class, annotArgs);
_clzBldr.getAnnotations().add(annotationInfo);
}
@@ -511,7 +541,7 @@ private String createTuple(List _outputArgs, String _className
String genericName = findNextGenericName(genericTypes.keySet());
genericTypes.put(genericName, entry.getType());
- entry.getAnnotations().add("@Position(" + position++ + ")");
+ entry.getAnnotations().add(new AnnotationInfo(Position.class, AnnotArgs.create().add(position++)));
entry.setType(genericName);
cnstrctArgs.add(new MemberOrArgument(entry.getName(), genericName));
}
@@ -587,7 +617,7 @@ private String buildStructClass(List _dbusTypeStr, String _structName,
String structClassName;
if (data.dbusSig().contains("(")) {
- String subStructFqcn = structFqcn + Util.upperCaseFirstChar(Objects.toString(data.name(), "")) + "Struct";
+ String subStructFqcn = structFqcn + Util.upperCaseFirstChar(Objects.toString(data.name(), "")) + STRUCT_CLASS_SUFFIX;
structClassName = new StructTreeBuilder(argumentPrefix, generatedStructClassNames)
.buildStructClasses(data.dbusSig(), subStructFqcn, _packageName, _structClasses);
} else {
@@ -597,9 +627,8 @@ private String buildStructClass(List _dbusTypeStr, String _structName,
}
MemberOrArgument argument = new MemberOrArgument(data.name(), structClassName, true);
- argument.getAnnotations().add("@Position(" + i + ")");
+ argument.getAnnotations().add(new AnnotationInfo(Position.class, AnnotArgs.create().add(i)));
root.getMembers().add(argument);
- root.getImports().add(Position.class.getName());
classConstructor.getArguments().add(new MemberOrArgument(data.name(), structClassName));
}
diff --git a/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/StructTreeBuilder.java b/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/StructTreeBuilder.java
index e7927f776..a506694a8 100644
--- a/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/StructTreeBuilder.java
+++ b/dbus-java-utils/src/main/java/org/freedesktop/dbus/utils/generator/StructTreeBuilder.java
@@ -5,6 +5,8 @@
import org.freedesktop.dbus.annotations.Position;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.utils.Util;
+import org.freedesktop.dbus.utils.generator.ClassBuilderInfo.AnnotationInfo;
+import org.freedesktop.dbus.utils.generator.ClassBuilderInfo.AnnotationInfo.AnnotArgs;
import org.freedesktop.dbus.utils.generator.ClassBuilderInfo.ClassConstructor;
import org.freedesktop.dbus.utils.generator.ClassBuilderInfo.ClassType;
import org.freedesktop.dbus.utils.generator.ClassBuilderInfo.MemberOrArgument;
@@ -137,7 +139,7 @@ private ClassBuilderInfo createNested(List _list, String _structFqcn
for (StructTree inTree : _list) {
MemberOrArgument member = new MemberOrArgument("member" + position, inTree.getDataType().getName(), true);
- member.getAnnotations().add("@Position(" + position + ")");
+ member.getAnnotations().add(new AnnotationInfo(Position.class, AnnotArgs.create().add(position)));
String constructorArg = "member" + position;
@@ -181,8 +183,6 @@ private ClassBuilderInfo createNested(List _list, String _structFqcn
retval = null;
}
- root.getImports().add(Position.class.getName()); // add position annotation as include
-
root.getImports().add(inTree.getDataType().getName());
root.getMembers().add(member);
}
diff --git a/dbus-java-utils/src/test/java/org/freedesktop/dbus/utils/generator/InterfaceCodeGeneratorTest.java b/dbus-java-utils/src/test/java/org/freedesktop/dbus/utils/generator/InterfaceCodeGeneratorTest.java
index be7e130c0..5d592fb87 100644
--- a/dbus-java-utils/src/test/java/org/freedesktop/dbus/utils/generator/InterfaceCodeGeneratorTest.java
+++ b/dbus-java-utils/src/test/java/org/freedesktop/dbus/utils/generator/InterfaceCodeGeneratorTest.java
@@ -17,10 +17,14 @@
class InterfaceCodeGeneratorTest {
static InterfaceCodeGenerator loadDBusXmlFile(File _inputFile, String _objectPath, String _busName) {
+ return loadDBusXmlFile(false, _inputFile, _objectPath, _busName);
+ }
+
+ static InterfaceCodeGenerator loadDBusXmlFile(boolean _createPropertyMethods, File _inputFile, String _objectPath, String _busName) {
if (!Util.isBlank(_busName)) {
String introspectionData = Util.readFileToString(_inputFile);
- return new InterfaceCodeGenerator(false, introspectionData, _objectPath, _busName, null, false, null, false);
+ return new InterfaceCodeGenerator(false, introspectionData, _objectPath, _busName, null, _createPropertyMethods, null, false);
} else {
fail("No valid busName given");
}
@@ -66,6 +70,66 @@ void testCreateNetworkManagerWirelessInterface() throws Exception {
assertFalse(clzContent.contains("this._interfaceName"));
}
+ @Test
+ void testHandleKebabCase() throws Exception {
+ InterfaceCodeGenerator ci2 = loadDBusXmlFile(true,
+ new File("src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.PowerProfileMonitor.xml"),
+ "/", "org.freedesktop.portal.PowerProfileMonitor");
+ Map analyze = ci2.analyze(true);
+
+ assertEquals(1, analyze.size());
+
+ String clzContent = analyze.get(analyze.keySet().iterator().next());
+
+ assertTrue(clzContent.contains("@DBusBoundProperty(name = \"power-saver-enabled\")"));
+ assertTrue(clzContent.contains("boolean isPowerSaverEnabled();"));
+ }
+
+ @Test
+ void testHandleReservedMethodNames() throws Exception {
+ InterfaceCodeGenerator ci2 = loadDBusXmlFile(true,
+ new File("src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.Clipboard.xml"),
+ "/", "org.freedesktop.portal.Clipboard");
+ Map analyze = ci2.analyze(true);
+
+ assertEquals(1, analyze.size());
+
+ String clzContent = analyze.get(analyze.keySet().iterator().next());
+
+ assertTrue(clzContent.contains("@DBusMemberName(\"Serial\")"));
+ assertTrue(clzContent.contains("public UInt32 getSerialFromBus()"));
+ }
+
+ @Test
+ void testHandleStructSignals() throws Exception {
+ InterfaceCodeGenerator ci2 = loadDBusXmlFile(true,
+ new File("src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.GlobalShortcuts.xml"),
+ "/", "org.freedesktop.portal.GlobalShortcuts");
+ Map analyze = ci2.analyze(true);
+
+ assertEquals(3, analyze.size());
+
+ String primaryFile = analyze.entrySet().stream()
+ .filter(e -> e.getKey().getName().equals("GlobalShortcuts.java"))
+ .findFirst()
+ .orElseThrow()
+ .getValue();
+
+ assertTrue(primaryFile.contains("public ShortcutsChanged(String path, DBusPath sessionHandle, List shortcuts) throws DBusException {"));
+ assertTrue(primaryFile.contains("private final List shortcuts;"));
+ assertTrue(primaryFile.contains("public List getShortcuts() {"));
+
+ String secondaryFile = analyze.entrySet().stream()
+ .filter(e -> e.getKey().getName().equals("ShortcutsChangedShortcutsStruct.java"))
+ .findFirst()
+ .orElseThrow()
+ .getValue();
+
+ assertTrue(secondaryFile.contains("private final String member0;"));
+ assertTrue(secondaryFile.contains("private final Map> member1;"));
+
+ }
+
@Test
void testCreateSampleStructArgs() throws Exception {
InterfaceCodeGenerator ci2 = loadDBusXmlFile(
diff --git a/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.Clipboard.xml b/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.Clipboard.xml
new file mode 100644
index 000000000..bff08821f
--- /dev/null
+++ b/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.Clipboard.xml
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.GlobalShortcuts.xml b/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.GlobalShortcuts.xml
new file mode 100644
index 000000000..26b2fb753
--- /dev/null
+++ b/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.GlobalShortcuts.xml
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.PowerProfileMonitor.xml b/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.PowerProfileMonitor.xml
new file mode 100644
index 000000000..ecd748ed9
--- /dev/null
+++ b/dbus-java-utils/src/test/resources/CreateInterface/xdg-desktop/org.freedesktop.portal.PowerProfileMonitor.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+