Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Map type to mapper field #539

Merged
merged 41 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
07c6262
test: create Form entity to generic map scenario
otaviojava Aug 7, 2024
a2b6980
style: remove extra line
otaviojava Aug 7, 2024
9080d95
test: create scenario with generic type
otaviojava Aug 7, 2024
4b9de84
test: create test scenario to generic types
otaviojava Aug 7, 2024
b06e84b
refactoring: update to CollectionParameter instead of Generic
otaviojava Aug 7, 2024
c899928
refactoring: update and rename to CollectionField
otaviojava Aug 7, 2024
bc42ca7
feat: generate field and paramter to map
otaviojava Aug 7, 2024
d2344b1
feat: update header to form sample
otaviojava Aug 7, 2024
f946e58
feat: create generic type
otaviojava Aug 7, 2024
a5847d6
feat: create scenarios to cover with maptypereference
otaviojava Aug 7, 2024
88a7a5b
feat: implement map reader
otaviojava Aug 7, 2024
9ad7806
style: update checkstyle at communication-core
otaviojava Aug 7, 2024
0864848
feat: update mapfield implementation
otaviojava Aug 8, 2024
a34cf06
feat: include mapping at the builder
otaviojava Aug 8, 2024
00085f8
feat: include map at the converter
otaviojava Aug 8, 2024
f52a6b0
test: create test scenario to map
otaviojava Aug 8, 2024
f5ac08b
style: update imports
otaviojava Aug 8, 2024
24cc05c
feat: create map parameter
otaviojava Aug 8, 2024
14706fd
test: create initial structure to map parameter
otaviojava Aug 8, 2024
0d57d79
feat: include annotation to jnosql
otaviojava Aug 8, 2024
c9871d5
test: create entity to form
otaviojava Aug 8, 2024
705b6d7
test: create initial structure to map
otaviojava Aug 8, 2024
fa32514
feat: define mapper at the builder
otaviojava Aug 8, 2024
5fac7c7
test: create map test scenarios
otaviojava Aug 8, 2024
2988a41
style: fix imports at the map field metadata
otaviojava Aug 8, 2024
988b1fd
test: fix test
otaviojava Aug 8, 2024
fc6fff0
test: fix test at parameter metadatabuilder
otaviojava Aug 8, 2024
4a3f553
style: update header to map type reference
otaviojava Aug 8, 2024
3385555
refactoring: create feed to decrease the recursive logic
otaviojava Aug 8, 2024
545b1d5
feat: include field converter type
otaviojava Aug 8, 2024
8c2b991
feat: include semantic at field converter
otaviojava Aug 8, 2024
90d40c2
feat: enhance update MapFeild nad MapParameter
otaviojava Aug 8, 2024
7542364
style: remove imports
otaviojava Aug 8, 2024
98d9a5c
feat: update method at MapParameter metadta
otaviojava Aug 8, 2024
441b6f5
feat: update reflection implementation with value
otaviojava Aug 8, 2024
717fac8
test: create scenario to parameter with map and generics
otaviojava Aug 8, 2024
309725d
feat: include condition with Map
otaviojava Aug 8, 2024
64870f6
test: create engine and at converter
otaviojava Aug 8, 2024
d0ad67f
test: create test scenario to converter
otaviojava Aug 8, 2024
797e793
docs: enhance documentation to changelog
otaviojava Aug 8, 2024
7343c3d
style: include license
otaviojava Aug 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version

- Fix the `Orderby` annotation in the Repository
- Make the JDQL return the correct type when the select is by field
- Invalid deserialization of maps with generic values
- Make sure at the serialization to the field, the API does not return any communication layer, but standard Java types

=== Removed

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 2024 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
Expand Down Expand Up @@ -88,22 +88,46 @@ private <K, V> Map<K, V> getMap(Class<K> keyClass, Class<V> valueClass, Object v
return map;
}
}
if (Entry.class.isInstance(value)) {
Map<K, V> map = new HashMap<>();
convertEntryToMap(value, map);
return map;
}
throw new UnsupportedOperationException("There is not supported convert" + value + " a not Map type.");
}

private <K, V> void convertEntryToMap(Object value, Map<K, V> map) {
Entry entry = Entry.class.cast(value);
Entry entry = (Entry) value;
Object entryValue = entry.value().get();
if (entryValue instanceof Entry) {
Map<String, Object> subMap = new HashMap<>();
Entry subEntry = Entry.class.cast(entryValue);
convertEntryToMap(subEntry, subMap);
map.put((K) entry.name(), (V) subMap);
if (entryValue instanceof Entry subEntry) {
feedEntryValue(map, subEntry, entry);
} else if(entryValue instanceof Iterable<?> iterable) {
feedIterable(map, iterable, entry);
} else {
map.put((K) entry.name(), (V) entryValue);
}
}

private <K, V> void feedIterable(Map<K, V> map, Iterable<?> iterable, Entry entry) {
List<Object> collection = new ArrayList<>();
iterable.forEach(collection::add);
if (collection.isEmpty()) {
map.put((K) entry.name(), (V) Collections.emptyList());
} else if (collection.stream().allMatch(Entry.class::isInstance)) {
Map<K, V> subMap = new HashMap<>();
collection.forEach(e -> convertEntryToMap(e, subMap));
map.put((K) entry.name(), (V) subMap);
} else {
map.put((K) entry.name(), (V) collection);
}
}

private <K, V> void feedEntryValue(Map<K, V> map, Entry subEntry, Entry entry) {
Map<String, Object> subMap = new HashMap<>();
convertEntryToMap(subEntry, subMap);
map.put((K) entry.name(), (V) subMap);
}

private <K, V> Map<K, V> convertToMap(Class<K> keyClass, Class<V> valueClass, Object value) {
Map mapValue = Map.class.cast(value);
return (Map<K, V>) mapValue.keySet().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,67 @@ void shouldConvertSubEntryToMap() {
});
}

@SuppressWarnings("unchecked")
@Test
void shouldConvertUsingObject() {
Entry subEntry = new EntryTest("key", Value.of("value"));
Entry entry = new EntryTest("key", Value.of(subEntry));

Map<String, Object> map = referenceReader.convert(new TypeReference<>() {
}, Collections.singletonList(entry));

Map<String, String> subMap = (Map<String, String>) map.get("key");

assertSoftly(softly -> {
softly.assertThat(map).as("Map is correctly converted").hasSize(1).contains(entry("key", Map.of("key", "value")));
softly.assertThat(subMap).as("SubMap is correctly converted").hasSize(1).contains(entry("key", "value"));
});
}

@Test
void shouldConvertEntry() {
Entry entry = new EntryTest("key", Value.of("value"));

Map<String, String> map = referenceReader.convert(new TypeReference<>() {
}, entry);

assertSoftly(softly -> softly.assertThat(map).as("Map is correctly converted").hasSize(1).contains(entry("key", "value")));
}

@Test
void shouldConvertEntryWithSubEntry() {
Entry subEntry = new EntryTest("key", Value.of("value"));
Entry entry = new EntryTest("key", Value.of(subEntry));

Map<String, Object> map = referenceReader.convert(new TypeReference<>() {
}, Collections.singletonList(entry));

Map<String, String> subMap = (Map<String, String>) map.get("key");

assertSoftly(softly -> {
softly.assertThat(map).as("Map is correctly converted").hasSize(1).contains(entry("key", Map.of("key", "value")));
softly.assertThat(subMap).as("SubMap is correctly converted").hasSize(1).contains(entry("key", "value"));
});
}

@Test
void shouldConvertEntryWithSubEntryAsList() {
Entry subEntry = new EntryTest("key3", Value.of("value"));
Entry subEntry2 = new EntryTest("key2", Value.of("value2"));
Entry entry = new EntryTest("key", Value.of(List.of(subEntry, subEntry2)));

Map<String, Object> map = referenceReader.convert(new TypeReference<>() {
}, Collections.singletonList(entry));

Map<String, Object> subMap = (Map<String, Object>) map.get("key");

assertSoftly(softly -> {
softly.assertThat(map).as("Map is correctly converted").hasSize(1).containsKey("key");
softly.assertThat(subMap).as("SubMap is correctly converted").hasSize(2).contains(entry("key2", "value2"),
entry("key3", "value"));
});
}

static Stream<Arguments> compatibleTypeReferences() {
return Stream.of(
arguments(new TypeReference<Map<String, String>>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
*
* @see ParameterMetaData
*/
public interface GenericFieldMetadata extends FieldMetadata {
public interface CollectionFieldMetadata extends FieldMetadata {
/**
* Returns true if it is the element has either Entity or Embeddable annotations
* @return true if the element has Entity or Embeddable annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
import java.util.Collection;

/**
* The GenericParameterMetaData interface extends the {@link ParameterMetaData} interface and provides
* additional information about a parameter with a generic type.
* The CollectionParameterMetaData interface extends the {@link ParameterMetaData} interface and provides
* additional information about a parameter with a generic type for collections.
*
* <p>This interface is used to represent parameters of generic types, where the type may be a collection
* or array containing elements of a specific type.</p>
*
* @see ParameterMetaData
*/
public interface GenericParameterMetaData extends ParameterMetaData {
public interface CollectionParameterMetaData extends ParameterMetaData {

/**
* Returns the {@link Class} representing the type of elements in the collection or array.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Otavio Santana
*/
package org.eclipse.jnosql.mapping.metadata;

/**
* The MapFieldMetadata interface extends the {@link FieldMetadata} interface and provides
* additional information about a parameter with a map type.
*
* <p>This interface is used to represent parameters of map types, where the type may be a map
* containing keys and values of specific types.</p>
*
* @see ParameterMetaData
*/
public interface MapFieldMetadata extends FieldMetadata {
/**
* Returns true if either the key or value has Entity or Embeddable annotations
* @return true if the key or value has Entity or Embeddable annotations
*/
boolean isEmbeddable();

/**
* Returns the {@link Class} representing the type of keys in the map.
*
* @return the key type of the map parameter
*/
Class<?> keyType();

/**
* Returns the {@link Class} representing the type of values in the map.
*
* @return the value type of the map parameter
*/
Class<?> valueType();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Otavio Santana
*/
package org.eclipse.jnosql.mapping.metadata;


import org.eclipse.jnosql.communication.Value;

/**
* The MapParameterMetaData interface extends the {@link ParameterMetaData} interface and provides
* additional information about a parameter with a generic type for maps.
*
* <p>This interface is used to represent parameters of generic types, where the type may be a map
* containing keys and values of specific types.</p>
*
* @see ParameterMetaData
*/
public interface MapParameterMetaData extends ParameterMetaData {

/**
* Returns the {@link Class} representing the type of keys in the map.
*
* @return the key type of the map parameter
*/
Class<?> keyType();

/**
* Returns the {@link Class} representing the type of values in the map.
*
* @return the value type of the map parameter
*/
Class<?> valueType();

/**
* Returns the object from the field type.
*
* @param value the value {@link Value}
* @return the instance from the field type
*/
Object value(Value value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import jakarta.nosql.AttributeConverter;
import jakarta.nosql.Embeddable;
import org.eclipse.jnosql.mapping.metadata.CollectionSupplier;
import org.eclipse.jnosql.mapping.metadata.GenericFieldMetadata;
import org.eclipse.jnosql.mapping.metadata.CollectionFieldMetadata;
import org.eclipse.jnosql.mapping.metadata.MappingType;

import java.lang.reflect.Field;
Expand All @@ -30,13 +30,13 @@
import java.util.Objects;
import java.util.ServiceLoader;

final class DefaultGenericFieldMetadata extends AbstractFieldMetadata implements GenericFieldMetadata {
final class DefaultCollectionFieldMetadata extends AbstractFieldMetadata implements CollectionFieldMetadata {

private final TypeSupplier<?> typeSupplier;

DefaultGenericFieldMetadata(MappingType type, Field field, String name, TypeSupplier<?> typeSupplier,
Class<? extends AttributeConverter<?, ?>> converter,
FieldReader reader, FieldWriter writer, String udt) {
DefaultCollectionFieldMetadata(MappingType type, Field field, String name, TypeSupplier<?> typeSupplier,
Class<? extends AttributeConverter<?, ?>> converter,
FieldReader reader, FieldWriter writer, String udt) {
super(type, field, name, converter, reader, writer, udt);
this.typeSupplier = typeSupplier;
}
Expand All @@ -63,7 +63,7 @@ public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) {
return false;
}
DefaultGenericFieldMetadata that = (DefaultGenericFieldMetadata) o;
DefaultCollectionFieldMetadata that = (DefaultCollectionFieldMetadata) o;
return mappingType == that.mappingType &&
Objects.equals(field, that.field) &&
Objects.equals(typeSupplier, that.typeSupplier) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,31 @@
import org.eclipse.jnosql.communication.TypeSupplier;
import jakarta.nosql.AttributeConverter;
import org.eclipse.jnosql.mapping.metadata.CollectionSupplier;
import org.eclipse.jnosql.mapping.metadata.GenericParameterMetaData;
import org.eclipse.jnosql.mapping.metadata.CollectionParameterMetaData;
import org.eclipse.jnosql.mapping.metadata.MappingType;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.ServiceLoader;

class DefaultGenericParameterMetaData extends DefaultParameterMetaData implements GenericParameterMetaData {
class DefaultCollectionParameterMetaData extends DefaultParameterMetaData implements CollectionParameterMetaData {


private final TypeSupplier<?> typeSupplier;

DefaultGenericParameterMetaData(String name, Class<?> type, boolean id,
Class<? extends AttributeConverter<?, ?>> converter,
MappingType mappingType, TypeSupplier<?> typeSupplier) {
DefaultCollectionParameterMetaData(String name, Class<?> type, boolean id,
Class<? extends AttributeConverter<?, ?>> converter,
MappingType mappingType, TypeSupplier<?> typeSupplier) {
super(name, type, id, converter, mappingType);
this.typeSupplier = typeSupplier;
}

@Override
public Class<?> elementType() {
return (Class<?>) ((ParameterizedType) typeSupplier.get()).getActualTypeArguments()[0];
}

@Override
public Collection<?> collectionInstance() {
Class<?> type = type();
final CollectionSupplier supplier = ServiceLoader.load(CollectionSupplier.class)
Expand Down
Loading