Skip to content

Commit

Permalink
Add dynamic properties support for collections.
Browse files Browse the repository at this point in the history
  • Loading branch information
meistermeier committed Aug 24, 2023
1 parent c5fa066 commit 6049668
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Predicate;
Expand Down Expand Up @@ -123,6 +128,8 @@ private void addMapToProperties(Map<?, ?> fieldValue, Map<String, Object> graphP
@SuppressWarnings("unchecked") // It is checked by the condition above.
EnumStringConverter enumStringConverter = converterCache.computeIfAbsent((Class<? extends Enum<?>>) entryValue.getClass(), EnumStringConverter::new);
graphProperties.put(entryKey, enumStringConverter.toGraphProperty((Enum<?>) entryValue));
} else if (Set.class.isAssignableFrom(entryValue.getClass())) {
graphProperties.put(entryKey, new ArrayList<>((Set<?>) entryValue)); // todo maps more than required
} else {
throw new MappingException("Could not map key=" + entryPrefix + entry.getKey() + ", " +
"value=" + entryValue + " (type = " + entryValue.getClass() + ") " +
Expand Down Expand Up @@ -166,16 +173,24 @@ private void putToMap(Map<Object, Object> result, String propertyKey, Object val
o = new HashMap<>();
result.put(keyInstance, o);
}
putToMap(o, propertyKey.substring(delimiterIndex + delimiter.length()), value, nestedFieldType(fieldType));
putToMap(o, propertyKey.substring(delimiterIndex + delimiter.length()), value, nestedMapFieldType(fieldType));
} else {
Object keyInstance = keyInstanceFromString(propertyKey, getKeyType(fieldType));

Type valueType = nestedFieldType(fieldType);
Type valueType = nestedMapFieldType(fieldType);
if (valueType != null) {
if (value instanceof String && ClassUtils.isEnum(valueType)) {
@SuppressWarnings("unchecked")
EnumStringConverter enumStringConverter = converterCache.computeIfAbsent((Class<? extends Enum<?>>) valueType, EnumStringConverter::new);
value = enumStringConverter.toEntityAttribute((String) value);
} else if (valueType instanceof ParameterizedType) {
value = Utils.coerceTypes((Class<?>) nestedCollectionFieldType(valueType), value);
Class<?> rawCollectionType = (Class<?>) ((ParameterizedType) valueType).getRawType();
if (SortedSet.class.isAssignableFrom(rawCollectionType)) {
value = new TreeSet<>((Collection<?>) value);
} else if (Set.class.isAssignableFrom(rawCollectionType)) {
value = new HashSet<>((Collection<?>) value);
}
} else {
value = Utils.coerceTypes((Class<?>) valueType, value);
}
Expand All @@ -193,14 +208,22 @@ private Class<?> getKeyType(Type fieldType) {
}
}

private Type nestedFieldType(Type keyType) {
private Type nestedMapFieldType(Type keyType) {
if (keyType instanceof ParameterizedType) {
return ((ParameterizedType) keyType).getActualTypeArguments()[1];
} else {
return null;
}
}

private Type nestedCollectionFieldType(Type keyType) {
if (keyType instanceof ParameterizedType) {
return ((ParameterizedType) keyType).getActualTypeArguments()[0];
} else {
return null;
}
}

private String keyInstanceToString(Object propertyKey) {
if (propertyKey == null) {
throw new UnsupportedOperationException("Null is not a supported property key!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -44,6 +45,7 @@
@NodeEntity
public class User {


public enum EnumA {VALUE_AA}

public enum EnumB {
Expand Down Expand Up @@ -89,12 +91,19 @@ public String toString() {
@Properties(transformEnumKeysWith = LowerCasePropertiesFilter.class)
private Map<EnumA, Object> filteredProperties;

@Properties(prefix = "listProperties", allowCast = true)
private Map<String, List<String>> listProperties;

@Properties(prefix = "setProperties", allowCast = true)
private Map<String, Set<String>> setProperties;

@Convert(NoPrefixPropertiesConverter.class)
private Map<String, Object> manualProperties;

@Relationship(type = "VISITED")
private Set<Visit> visits;


public User() {
}

Expand Down Expand Up @@ -258,6 +267,22 @@ public void setEnumBValuesByEnum(
this.enumBValuesByEnum = enumBValuesByEnum;
}

public void setListProperties(Map<String, List<String>> listProperties) {
this.listProperties = listProperties;
}

public Map<String, List<String>> getListProperties() {
return listProperties;
}

public Map<String, Set<String>> getSetProperties() {
return setProperties;
}

public void setSetProperties(Map<String, Set<String>> setProperties) {
this.setProperties = setProperties;
}

public static class LowerCasePropertiesFilter implements BiFunction<Phase, String, String> {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Before;
import org.junit.BeforeClass;
Expand Down Expand Up @@ -452,4 +455,38 @@ public void manualConversionShouldSupportPropertiesWithouthPrefix() {
assertThat(user.getMyProperties())
.containsOnlyKeys("prop1", "prop2");
}

@Test //GH-955
public void shouldHandleListOfProperties() {
User user = new User();
Map<String, List<String>> properties = new HashMap<>();
properties.put("a", Arrays.asList("a", "b"));
properties.put("b", Arrays.asList("c", "d"));
user.setListProperties(properties);

session.save(user);
session.clear();

user = session.load(User.class, user.getId());
assertThat(user.getListProperties())
.containsOnly(new HashMap.SimpleEntry<>("a", Arrays.asList("a", "b")), new HashMap.SimpleEntry<>("b", Arrays.asList("c", "d")));
}

@Test //GH-955
public void shouldHandleSetOfProperties() {
User user = new User();
Map<String, Set<String>> properties = new HashMap<>();
Set<String> a = new HashSet<>(Arrays.asList("a", "b"));
Set<String> b = new HashSet<>(Arrays.asList("c", "d"));
properties.put("a", a);
properties.put("b", b);
user.setSetProperties(properties);

session.save(user);
session.clear();

user = session.load(User.class, user.getId());
assertThat(user.getSetProperties())
.containsOnly(new HashMap.SimpleEntry<>("a", new HashSet<>(Arrays.asList("a", "b"))), new HashMap.SimpleEntry<>("b", new HashSet<>(Arrays.asList("c", "d"))));
}
}

0 comments on commit 6049668

Please sign in to comment.