-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Expected behavior
The mapstruct should not produce a "Unmapped target properties" for "put" and "putAll" methods.
Actual behavior
The mapstruct supports org.immutables objects already. In case an object has got a java.util.Map attributes like:
public abstract Map<String, String> getAttributes();
the immutables generates the following methods in the Builder class for this attribute:
public final Builder putAttributes(String key, String value)
public final Builder putAttributes(Map.Entry<String, ? extends String> entry)
public final Builder attributes(Map<String, ? extends String> entries)
public final Builder putAllAttributes(Map<String, ? extends String> entries)
The mapstruct produces a Warning for "put" and "putAll" methods:
WARNING ItemMapper.java:24 Unmapped target properties: "putAttributes putAllAttributes".
as it believes they are fluentSetter.
As a workaround, the warnings can be suppressed by:
@Mapping(target = "putAttributes", ignore = true)
Steps to reproduce the problem
To reproduce the issue, I have add Map<String, String> getAttributes() to the org.mapstruct.ap.test.bugs._1801.domain.Item:
Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java (revision fd27380185fe83feb6d26308c7d358025783ff8f)
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ImmutableItemDTO.java (date 1669213502716)
@@ -7,7 +7,10 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Collections;
+import java.util.LinkedHashMap;
/**
* Immutable implementation of {@link ItemDTO}.
@@ -19,9 +22,11 @@
*/
public final class ImmutableItemDTO extends ItemDTO {
private final String id;
+ private final Map<String, String> attributes;
- private ImmutableItemDTO(String id) {
+ private ImmutableItemDTO(String id, Map<String, String> attributes) {
this.id = id;
+ this.attributes = attributes;
}
/**
@@ -32,84 +37,103 @@
return id;
}
+ /**
+ * @return The value of the {@code attributes} attribute
+ */
+ @Override
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
/**
* Copy the current immutable object by setting a value for the {@link ItemDTO#getId() id} attribute.
- * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
- *
+ * An equals check used to prevent copying of the same value by returning {@code this}.
* @param value A new value for id
- *
* @return A modified copy of the {@code this} object
*/
- public ImmutableItemDTO withId(String value) {
- if ( Objects.equals( this.id, value ) ) {
- return this;
- }
- return new ImmutableItemDTO( value );
+ public final ImmutableItemDTO withId(String value) {
+ String newValue = Objects.requireNonNull(value, "id");
+ if (this.id.equals(newValue)) return this;
+ return new ImmutableItemDTO(newValue, this.attributes);
+ }
+
+ /**
+ * Copy the current immutable object by replacing the {@link ItemDTO#getAttributes() attributes} map with the specified map.
+ * Nulls are not permitted as keys or values.
+ * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
+ * @param entries The entries to be added to the attributes map
+ * @return A modified copy of {@code this} object
+ */
+ public final ImmutableItemDTO withAttributes(Map<String, ? extends String> entries) {
+ if (this.attributes == entries) return this;
+ Map<String, String> newValue = createUnmodifiableMap(true, false, entries);
+ return new ImmutableItemDTO(this.id, newValue);
}
/**
* This instance is equal to all instances of {@code ImmutableItemDTO} that have equal attribute values.
- *
* @return {@code true} if {@code this} is equal to {@code another} instance
*/
@Override
public boolean equals(Object another) {
- if ( this == another ) {
- return true;
- }
+ if (this == another) return true;
return another instanceof ImmutableItemDTO
- && equalTo( (ImmutableItemDTO) another );
+ && equalTo((ImmutableItemDTO) another);
}
private boolean equalTo(ImmutableItemDTO another) {
- return id.equals( another.id );
+ return id.equals(another.id)
+ && attributes.equals(another.attributes);
}
/**
- * Computes a hash code from attributes: {@code id}.
- *
+ * Computes a hash code from attributes: {@code id}, {@code attributes}.
* @return hashCode value
*/
@Override
public int hashCode() {
int h = 5381;
- h += ( h << 5 ) + id.hashCode();
+ h += (h << 5) + id.hashCode();
+ h += (h << 5) + attributes.hashCode();
return h;
}
/**
- * Prints the immutable value {@code ItemDTO} with attribute values.
- *
+ * Prints the immutable value {@code Item} with attribute values.
* @return A string representation of the value
*/
@Override
public String toString() {
- return "ItemDTO{"
- + "id=" + id
- + "}";
+ return "Item{"
+ + "id=" + id
+ + ", attributes=" + attributes
+ + "}";
}
/**
* Creates an immutable copy of a {@link ItemDTO} value.
* Uses accessors to get values to initialize the new immutable instance.
* If an instance is already immutable, it is returned as is.
- *
* @param instance The instance to copy
- *
- * @return A copied immutable ItemDTO instance
+ * @return A copied immutable Item instance
*/
public static ImmutableItemDTO copyOf(ItemDTO instance) {
- if ( instance instanceof ImmutableItemDTO ) {
+ if (instance instanceof ImmutableItemDTO) {
return (ImmutableItemDTO) instance;
}
return ImmutableItemDTO.builder()
- .from( instance )
- .build();
+ .from(instance)
+ .build();
}
/**
* Creates a builder for {@link ImmutableItemDTO ImmutableItemDTO}.
- *
+ * <pre>
+ * ImmutableItemDTO.builder()
+ * .id(String) // required {@link ItemDTO#getId() id}
+ * .putAttributes|putAllAttributes(String => String) // {@link ItemDTO#getAttributes() attributes} mappings
+ * .build();
+ * </pre>
* @return A new ImmutableItemDTO builder
*/
public static ImmutableItemDTO.Builder builder() {
@@ -123,62 +147,149 @@
* <p><em>{@code Builder} is not thread-safe and generally should not be stored in a field or collection,
* but instead used immediately to create instances.</em>
*/
- public static final class Builder {
+ public static class Builder {
private static final long INIT_BIT_ID = 0x1L;
private long initBits = 0x1L;
private String id;
+ private Map<String, String> attributes = new LinkedHashMap<String, String>();
- private Builder() {
+ public Builder() {
}
/**
- * Fill a builder with attribute values from the provided {@code ItemDTO} instance.
+ * Fill a builder with attribute values from the provided {@code Item} instance.
* Regular attribute values will be replaced with those from the given instance.
* Absent optional values will not replace present values.
- *
+ * Collection elements and entries will be added, not replaced.
* @param instance The instance from which to copy values
- *
* @return {@code this} builder for use in a chained invocation
*/
- public Builder from(ItemDTO instance) {
- id( instance.getId() );
+ public final ImmutableItemDTO.Builder from(ItemDTO instance) {
+ Objects.requireNonNull(instance, "instance");
+ id(instance.getId());
+ putAllAttributes(instance.getAttributes());
return this;
}
/**
* Initializes the value for the {@link ItemDTO#getId() id} attribute.
- *
* @param id The value for id
- *
* @return {@code this} builder for use in a chained invocation
*/
- public Builder id(String id) {
- this.id = id;
+ public final ImmutableItemDTO.Builder id(String id) {
+ this.id = Objects.requireNonNull(id, "id");
initBits &= ~INIT_BIT_ID;
return this;
}
+ /**
+ * Put one entry to the {@link ItemDTO#getAttributes() attributes} map.
+ * @param key The key in the attributes map
+ * @param value The associated value in the attributes map
+ * @return {@code this} builder for use in a chained invocation
+ */
+ public final ImmutableItemDTO.Builder putAttributes(String key, String value) {
+ this.attributes.put(
+ Objects.requireNonNull(key, "attributes key"),
+ Objects.requireNonNull(value, "attributes value"));
+ return this;
+ }
+
+ /**
+ * Put one entry to the {@link ItemDTO#getAttributes() attributes} map. Nulls are not permitted
+ * @param entry The key and value entry
+ * @return {@code this} builder for use in a chained invocation
+ */
+ public final ImmutableItemDTO.Builder putAttributes(Map.Entry<String, ? extends String> entry) {
+ String k = entry.getKey();
+ String v = entry.getValue();
+ this.attributes.put(
+ Objects.requireNonNull(k, "attributes key"),
+ Objects.requireNonNull(v, "attributes value"));
+ return this;
+ }
+
+ /**
+ * Sets or replaces all mappings from the specified map as entries for the {@link ItemDTO#getAttributes() attributes} map. Nulls are not permitted
+ * @param entries The entries that will be added to the attributes map
+ * @return {@code this} builder for use in a chained invocation
+ */
+ public final ImmutableItemDTO.Builder attributes(Map<String, ? extends String> entries) {
+ this.attributes.clear();
+ return putAllAttributes(entries);
+ }
+
+ /**
+ * Put all mappings from the specified map as entries to {@link ItemDTO#getAttributes() attributes} map. Nulls are not permitted
+ * @param entries The entries that will be added to the attributes map
+ * @return {@code this} builder for use in a chained invocation
+ */
+ public final ImmutableItemDTO.Builder putAllAttributes(Map<String, ? extends String> entries) {
+ for (Map.Entry<String, ? extends String> e : entries.entrySet()) {
+ String k = e.getKey();
+ String v = e.getValue();
+ this.attributes.put(
+ Objects.requireNonNull(k, "attributes key"),
+ Objects.requireNonNull(v, "attributes value"));
+ }
+ return this;
+ }
+
/**
* Builds a new {@link ImmutableItemDTO ImmutableItemDTO}.
- *
- * @return An immutable instance of ItemDTO
- *
+ * @return An immutable instance of Item
* @throws java.lang.IllegalStateException if any required attributes are missing
*/
public ImmutableItemDTO build() {
- if ( initBits != 0 ) {
- throw new IllegalStateException( formatRequiredAttributesMessage() );
+ if (initBits != 0) {
+ throw new IllegalStateException(formatRequiredAttributesMessage());
}
- return new ImmutableItemDTO( id );
+ return new ImmutableItemDTO(id, createUnmodifiableMap(false, false, attributes));
}
private String formatRequiredAttributesMessage() {
List<String> attributes = new ArrayList<>();
- if ( ( initBits & INIT_BIT_ID ) != 0 ) {
- attributes.add( "id" );
- }
- return "Cannot build ItemDTO, some of required attributes are not set " + attributes;
+ if ((initBits & INIT_BIT_ID) != 0) attributes.add("id");
+ return "Cannot build Item, some of required attributes are not set " + attributes;
+ }
+ }
+
+ private static <K, V> Map<K, V> createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map<? extends K, ? extends V> map) {
+ switch (map.size()) {
+ case 0: return Collections.emptyMap();
+ case 1: {
+ Map.Entry<? extends K, ? extends V> e = map.entrySet().iterator().next();
+ K k = e.getKey();
+ V v = e.getValue();
+ if (checkNulls) {
+ Objects.requireNonNull(k, "key");
+ Objects.requireNonNull(v, "value");
+ }
+ if (skipNulls && (k == null || v == null)) {
+ return Collections.emptyMap();
+ }
+ return Collections.singletonMap(k, v);
+ }
+ default: {
+ Map<K, V> linkedMap = new LinkedHashMap<>(map.size());
+ if (skipNulls || checkNulls) {
+ for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
+ K k = e.getKey();
+ V v = e.getValue();
+ if (skipNulls) {
+ if (k == null || v == null) continue;
+ } else if (checkNulls) {
+ Objects.requireNonNull(k, "key");
+ Objects.requireNonNull(v, "value");
+ }
+ linkedMap.put(k, v);
+ }
+ } else {
+ linkedMap.putAll(map);
+ }
+ return Collections.unmodifiableMap(linkedMap);
+ }
}
}
}
Index: processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java
--- a/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java (revision fd27380185fe83feb6d26308c7d358025783ff8f)
+++ b/processor/src/main/java/org/mapstruct/ap/spi/ImmutablesAccessorNamingStrategy.java (date 1669216681078)
@@ -21,7 +21,19 @@
@Override
protected boolean isFluentSetter(ExecutableElement method) {
- return super.isFluentSetter( method ) && !method.getSimpleName().toString().equals( "from" );
+ return super.isFluentSetter( method ) && !method.getSimpleName().toString().equals( "from" )
+ // TODO uncomment the fix
+ // && !isPutterWithUpperCase4thCharacter( method )
+ ;
+ }
+
+ private boolean isPutterWithUpperCase4thCharacter(ExecutableElement method) {
+ return isPutterMethod( method ) && Character.isUpperCase( method.getSimpleName().toString().charAt( 3 ) );
+ }
+
+ public boolean isPutterMethod(ExecutableElement method) {
+ String methodName = method.getSimpleName().toString();
+ return methodName.startsWith( "put" ) && methodName.length() > 3;
}
}
Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java (revision fd27380185fe83feb6d26308c7d358025783ff8f)
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/dto/ItemDTO.java (date 1669212960416)
@@ -5,9 +5,13 @@
*/
package org.mapstruct.ap.test.bugs._1801.dto;
+import java.util.Map;
+
/**
* @author Zhizhi Deng
*/
public abstract class ItemDTO {
public abstract String getId();
+
+ public abstract Map<String, String> getAttributes();
}
Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java (revision fd27380185fe83feb6d26308c7d358025783ff8f)
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/Item.java (date 1669212834384)
@@ -5,11 +5,15 @@
*/
package org.mapstruct.ap.test.bugs._1801.domain;
+import java.util.Map;
+
/**
* @author Zhizhi Deng
*/
public abstract class Item {
public abstract String getId();
+ public abstract Map<String, String> getAttributes();
+
public static class Builder extends ImmutableItem.Builder { }
}
Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java (revision fd27380185fe83feb6d26308c7d358025783ff8f)
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/ItemMapper.java (date 1669213623229)
@@ -11,6 +11,8 @@
import org.mapstruct.ap.test.bugs._1801.dto.ItemDTO;
import org.mapstruct.factory.Mappers;
+import java.util.Map;
+
/**
* @author Zhizhi Deng
*/
@@ -20,4 +22,6 @@
public static final ItemMapper INSTANCE = Mappers.getMapper( ItemMapper.class );
public abstract Item map(ItemDTO itemDTO);
+
+ public Map<String, String> map(Map<String, String> from) { return from; }
}
Index: processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java
--- a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java (revision fd27380185fe83feb6d26308c7d358025783ff8f)
+++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1801/domain/ImmutableItem.java (date 1669212872768)
@@ -5,8 +5,12 @@
*/
package org.mapstruct.ap.test.bugs._1801.domain;
-import java.util.ArrayList;
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.Objects;
import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
/**
* Immutable implementation of {@link Item}.
@@ -19,9 +23,11 @@
@SuppressWarnings({"all"})
public final class ImmutableItem extends Item {
private final String id;
+ private final Map<String, String> attributes;
- private ImmutableItem(String id) {
+ private ImmutableItem(String id, Map<String, String> attributes) {
this.id = id;
+ this.attributes = attributes;
}
/**
@@ -32,15 +38,37 @@
return id;
}
+ /**
+ * @return The value of the {@code attributes} attribute
+ */
+ @Override
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
/**
* Copy the current immutable object by setting a value for the {@link Item#getId() id} attribute.
- * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
+ * An equals check used to prevent copying of the same value by returning {@code this}.
* @param value A new value for id
* @return A modified copy of the {@code this} object
*/
public final ImmutableItem withId(String value) {
- if (this.id == value) return this;
- return new ImmutableItem(value);
+ String newValue = Objects.requireNonNull(value, "id");
+ if (this.id.equals(newValue)) return this;
+ return new ImmutableItem(newValue, this.attributes);
+ }
+
+ /**
+ * Copy the current immutable object by replacing the {@link Item#getAttributes() attributes} map with the specified map.
+ * Nulls are not permitted as keys or values.
+ * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
+ * @param entries The entries to be added to the attributes map
+ * @return A modified copy of {@code this} object
+ */
+ public final ImmutableItem withAttributes(Map<String, ? extends String> entries) {
+ if (this.attributes == entries) return this;
+ Map<String, String> newValue = createUnmodifiableMap(true, false, entries);
+ return new ImmutableItem(this.id, newValue);
}
/**
@@ -51,21 +79,23 @@
public boolean equals(Object another) {
if (this == another) return true;
return another instanceof ImmutableItem
- && equalTo((ImmutableItem) another);
+ && equalTo((ImmutableItem) another);
}
private boolean equalTo(ImmutableItem another) {
- return id.equals(another.id);
+ return id.equals(another.id)
+ && attributes.equals(another.attributes);
}
/**
- * Computes a hash code from attributes: {@code id}.
+ * Computes a hash code from attributes: {@code id}, {@code attributes}.
* @return hashCode value
*/
@Override
public int hashCode() {
int h = 5381;
h += (h << 5) + id.hashCode();
+ h += (h << 5) + attributes.hashCode();
return h;
}
@@ -76,8 +106,9 @@
@Override
public String toString() {
return "Item{"
- + "id=" + id
- + "}";
+ + "id=" + id
+ + ", attributes=" + attributes
+ + "}";
}
/**
@@ -91,13 +122,29 @@
if (instance instanceof ImmutableItem) {
return (ImmutableItem) instance;
}
- return new Builder()
- .from(instance)
- .build();
+ return ImmutableItem.builder()
+ .from(instance)
+ .build();
}
+ /**
+ * Creates a builder for {@link ImmutableItem ImmutableItem}.
+ * <pre>
+ * ImmutableItem.builder()
+ * .id(String) // required {@link Item#getId() id}
+ * .putAttributes|putAllAttributes(String => String) // {@link Item#getAttributes() attributes} mappings
+ * .build();
+ * </pre>
+ * @return A new ImmutableItem builder
+ */
+ public static ImmutableItem.Builder builder() {
+ return new ImmutableItem.Builder();
+ }
+
/**
* Builds instances of type {@link ImmutableItem ImmutableItem}.
+ * Initialize attributes and then invoke the {@link #build()} method to create an
+ * immutable instance.
* <p><em>{@code Builder} is not thread-safe and generally should not be stored in a field or collection,
* but instead used immediately to create instances.</em>
*/
@@ -106,33 +153,90 @@
private long initBits = 0x1L;
private String id;
+ private Map<String, String> attributes = new LinkedHashMap<String, String>();
- Builder() {
+ public Builder() {
}
/**
* Fill a builder with attribute values from the provided {@code Item} instance.
* Regular attribute values will be replaced with those from the given instance.
* Absent optional values will not replace present values.
+ * Collection elements and entries will be added, not replaced.
* @param instance The instance from which to copy values
* @return {@code this} builder for use in a chained invocation
*/
public final Builder from(Item instance) {
+ Objects.requireNonNull(instance, "instance");
id(instance.getId());
+ putAllAttributes(instance.getAttributes());
return this;
}
/**
* Initializes the value for the {@link Item#getId() id} attribute.
- * @param id The value for id
+ * @param id The value for id
* @return {@code this} builder for use in a chained invocation
*/
public final Builder id(String id) {
- this.id = id;
+ this.id = Objects.requireNonNull(id, "id");
initBits &= ~INIT_BIT_ID;
return this;
}
+ /**
+ * Put one entry to the {@link Item#getAttributes() attributes} map.
+ * @param key The key in the attributes map
+ * @param value The associated value in the attributes map
+ * @return {@code this} builder for use in a chained invocation
+ */
+ public final Builder putAttributes(String key, String value) {
+ this.attributes.put(
+ Objects.requireNonNull(key, "attributes key"),
+ Objects.requireNonNull(value, "attributes value"));
+ return this;
+ }
+
+ /**
+ * Put one entry to the {@link Item#getAttributes() attributes} map. Nulls are not permitted
+ * @param entry The key and value entry
+ * @return {@code this} builder for use in a chained invocation
+ */
+ public final Builder putAttributes(Map.Entry<String, ? extends String> entry) {
+ String k = entry.getKey();
+ String v = entry.getValue();
+ this.attributes.put(
+ Objects.requireNonNull(k, "attributes key"),
+ Objects.requireNonNull(v, "attributes value"));
+ return this;
+ }
+
+ /**
+ * Sets or replaces all mappings from the specified map as entries for the {@link Item#getAttributes() attributes} map. Nulls are not permitted
+ * @param entries The entries that will be added to the attributes map
+ * @return {@code this} builder for use in a chained invocation
+ */
+ public final Builder attributes(Map<String, ? extends String> entries) {
+ this.attributes.clear();
+ return putAllAttributes(entries);
+ }
+
+ /**
+ * Put all mappings from the specified map as entries to {@link Item#getAttributes() attributes} map. Nulls are not permitted
+ * @param entries The entries that will be added to the attributes map
+ * @return {@code this} builder for use in a chained invocation
+ */
+ public final Builder putAllAttributes(Map<String, ? extends String> entries) {
+ for (Map.Entry<String, ? extends String> e : entries.entrySet()) {
+ String k = e.getKey();
+ String v = e.getValue();
+ this.attributes.put(
+ Objects.requireNonNull(k, "attributes key"),
+ Objects.requireNonNull(v, "attributes value"));
+ }
+ return this;
+ }
+
/**
* Builds a new {@link ImmutableItem ImmutableItem}.
* @return An immutable instance of Item
@@ -142,13 +246,51 @@
if (initBits != 0) {
throw new IllegalStateException(formatRequiredAttributesMessage());
}
- return new ImmutableItem(id);
+ return new ImmutableItem(id, createUnmodifiableMap(false, false, attributes));
}
private String formatRequiredAttributesMessage() {
- List<String> attributes = new ArrayList<String>();
+ List<String> attributes = new ArrayList<>();
if ((initBits & INIT_BIT_ID) != 0) attributes.add("id");
return "Cannot build Item, some of required attributes are not set " + attributes;
}
}
-}
+
+ private static <K, V> Map<K, V> createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map<? extends K, ? extends V> map) {
+ switch (map.size()) {
+ case 0: return Collections.emptyMap();
+ case 1: {
+ Map.Entry<? extends K, ? extends V> e = map.entrySet().iterator().next();
+ K k = e.getKey();
+ V v = e.getValue();
+ if (checkNulls) {
+ Objects.requireNonNull(k, "key");
+ Objects.requireNonNull(v, "value");
+ }
+ if (skipNulls && (k == null || v == null)) {
+ return Collections.emptyMap();
+ }
+ return Collections.singletonMap(k, v);
+ }
+ default: {
+ Map<K, V> linkedMap = new LinkedHashMap<>(map.size());
+ if (skipNulls || checkNulls) {
+ for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
+ K k = e.getKey();
+ V v = e.getValue();
+ if (skipNulls) {
+ if (k == null || v == null) continue;
+ } else if (checkNulls) {
+ Objects.requireNonNull(k, "key");
+ Objects.requireNonNull(v, "value");
+ }
+ linkedMap.put(k, v);
+ }
+ } else {
+ linkedMap.putAll(map);
+ }
+ return Collections.unmodifiableMap(linkedMap);
+ }
+ }
+ }
+}
\ No newline at end of file
MapStruct Version
Mapstruct 1.5.3