Skip to content

Commit

Permalink
Added new Helper VariantBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
hypfvieh committed Aug 16, 2024
1 parent 1a21b6e commit 4fdcb5c
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ The library will remain open source and MIT licensed and can still be used, fork
#### Changes

##### Changes in 5.1.1 (not released yet):
- Nothing yet
- Added new Helper class `VariantBuilder` to allow creating Variants which contain Maps or Collections without messing with the required DBus type arguments

##### Changes in 5.1.0 (2024-08-01):
- Use Junit BOM thanks to [spannm](https://github.com/spannm) ([PR#248](https://github.com/hypfvieh/dbus-java/issues/248))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ private static String[] recursiveGetDBusType(StringBuffer[] _out, Type _dataType
return sigs;
} else if (_dataType instanceof ParameterizedType p) {
if (p.getRawType().equals(Map.class)) {
_out[_level].append("a{");
_out[_level].append(ArgumentType.ARRAY_STRING)
.append(ArgumentType.DICT_ENTRY1_STRING);
Type[] t = p.getActualTypeArguments();
try {
String[] s = recursiveGetDBusType(_out, t[0], true, _level + 1);
Expand All @@ -257,7 +258,7 @@ private static String[] recursiveGetDBusType(StringBuffer[] _out, Type _dataType
LOGGER.debug("", _ex);
throw new DBusException("Map must have 2 parameters");
}
_out[_level].append('}');
_out[_level].append(ArgumentType.DICT_ENTRY2_STRING);
} else if (List.class.isAssignableFrom((Class<?>) p.getRawType())) {
for (Type t : p.getActualTypeArguments()) {
if (Type.class.equals(t)) {
Expand Down Expand Up @@ -323,7 +324,7 @@ private static String[] recursiveGetDBusType(StringBuffer[] _out, Type _dataType
}
}
}
_out[_level].append(')');
_out[_level].append(ArgumentType.STRUCT2_STRING);

} else if (Enum.class.isAssignableFrom(dataTypeClazz)) {
_out[_level].append((char) ArgumentType.STRING);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package org.freedesktop.dbus.types;

import org.freedesktop.dbus.Marshalling;
import org.freedesktop.dbus.messages.constants.ArgumentType;

import java.util.*;

/**
* Builder to create Variants for parameterized types like Collections or Maps more easily.
* <p>
* When working with Variants you can use the default constructor which is taking your
* object, but that will not work if you use Maps/Collections because the actual type(s) used inside
* of those objects are not known on runtime (due to Java type erasure).
* </p><p>
* In this case you can use this builder providing the Class type you want to have inside of your
* Variant (e.g. Map.class) and the generic class types used inside of the Map.
* </p>
* <p>
* <b>Example:</b>
* <pre>
* VariantBuilder.of(Map.class).withGenericTypes(String.class, Integer.class).create(myMap);
* </pre>
* </p>
*
* @since 5.1.1 - 2024-08-16
* @author hypfvieh
*/
public final class VariantBuilder {
private final Class<?> baseClass;

private final List<Class<?>> genericTypes = new ArrayList<>();

private VariantBuilder(Class<?> _baseClass) {
baseClass = _baseClass;
}

/**
* Create a new instance using the given class as starting point.
* <p>
* If you want to create Variant containing a Map or List, this would be Map/List.class.
* </p>
* @param _clz class to use, never null
* @return new instance
*/
public static VariantBuilder of(Class<?> _clz) {
return new VariantBuilder(Objects.requireNonNull(_clz, "Class required"));
}

/**
* Add one or more generic types.
* <p>
* Use this if you want to create a Variant containing a Map, Collection.
* You have to provide the data types used inside of your Map/Collection to this method.
* E.g. you have Map&gt;Integer,String&lt; than you have to provide Integer.class and String.class to this method.
* </p>
*
* @param _clz generic classes to add
* @return this
*/
public VariantBuilder withGenericTypes(Class<?>... _clz) {
if (_clz == null || _clz.length == 0) {
return this;
}
genericTypes.addAll(Arrays.asList(_clz));
return this;
}

/**
* Create the Variant instance using the provided data object.
*
* @param <X> Type inside of the Variant
* @param _data data to store in Variant
*
* @return Variant
*
* @throws IllegalArgumentException when provided data object is not compatible with class given in constuctor
* @throws NullPointerException when null is given
*/
public <X> Variant<X> create(X _data) {
Objects.requireNonNull(_data, "No data given");

if (!baseClass.isAssignableFrom(_data.getClass())) {
throw new IllegalArgumentException("Given data is not compatible with defined Variant base class");
}

StringBuilder sb = new StringBuilder();

boolean isMap = false;
if (Map.class.isAssignableFrom(baseClass)) {
sb.append(ArgumentType.ARRAY_STRING).append(ArgumentType.DICT_ENTRY1_STRING);
isMap = true;
} else {
sb.append(Marshalling.convertJavaClassesToSignature(baseClass));
}

genericTypes.stream()
.map(Marshalling::convertJavaClassesToSignature)
.forEach(sb::append);

if (isMap) {
sb.append(ArgumentType.DICT_ENTRY2_STRING);
}

return new Variant<>(_data, sb.toString());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.freedesktop.dbus.types;

import org.freedesktop.dbus.Struct;
import org.freedesktop.dbus.annotations.Position;
import org.freedesktop.dbus.test.AbstractBaseTest;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Map;

public class VariantBuilderTest extends AbstractBaseTest {

@Test
void testBuilder() {

assertEquals(new Variant<>(List.of("str"), "as"),
VariantBuilder.of(List.class)
.withGenericTypes(String.class)
.create(List.of("str")));

assertEquals(new Variant<>("foo"),
VariantBuilder.of(String.class)
.create("foo"));

assertEquals(new Variant<>(Map.of(1, "str"), "a{is}"),
VariantBuilder.of(Map.class)
.withGenericTypes(Integer.class, String.class)
.create(Map.of(1, "str")));

assertEquals(new Variant<>(List.of(new VbStruct("test", false)), "a(sb)"),
VariantBuilder.of(List.class)
.withGenericTypes(VbStruct.class)
.create(List.of(new VbStruct("test", false))));

assertThrows(NullPointerException.class, () -> VariantBuilder.of(String.class).create(null));
assertThrows(IllegalArgumentException.class, () -> VariantBuilder.of(String.class).create(1));
assertThrows(NullPointerException.class, () -> VariantBuilder.of(null));

}

public static class VbStruct extends Struct {
@Position(0)
private String val1;
@Position(1)
private boolean val2;

public VbStruct(String _val1, boolean _val2) {
super();
val1 = _val1;
val2 = _val2;
}

}
}

0 comments on commit 4fdcb5c

Please sign in to comment.