|
39 | 39 | import com.google.protobuf.ByteString; |
40 | 40 | import com.google.protobuf.Message; |
41 | 41 | import com.google.protobuf.Timestamp; |
| 42 | +import io.spine.annotation.SPI; |
42 | 43 | import io.spine.core.Version; |
43 | 44 | import io.spine.server.entity.storage.AbstractColumnMapping; |
| 45 | +import io.spine.server.entity.storage.ColumnMapping; |
44 | 46 | import io.spine.server.entity.storage.ColumnTypeMapping; |
45 | 47 | import io.spine.string.Stringifiers; |
46 | 48 |
|
| 49 | +import java.util.HashMap; |
| 50 | +import java.util.Map; |
| 51 | + |
47 | 52 | import static com.google.cloud.Timestamp.ofTimeSecondsAndNanos; |
48 | 53 |
|
49 | 54 | /** |
|
52 | 57 | * <p>All column values are stored as Datastore {@link Value}-s. |
53 | 58 | * |
54 | 59 | * <p>Users of the storage can extend this class to specify their own column mapping for the |
55 | | - * selected types. |
| 60 | + * selected types. See {@link DsColumnMapping#customMapping() DsColumnMapping.customMapping()}. |
| 61 | + * |
| 62 | + * @see DatastoreStorageFactory.Builder#setColumnMapping(ColumnMapping) |
56 | 63 | */ |
57 | 64 | public class DsColumnMapping extends AbstractColumnMapping<Value<?>> { |
58 | 65 |
|
| 66 | + private static final Map<Class<?>, ColumnTypeMapping<?, ? extends Value<?>>> defaults |
| 67 | + = ImmutableMap.of(Timestamp.class, ofTimestamp(), |
| 68 | + Version.class, ofVersion()); |
| 69 | + |
| 70 | + /** |
| 71 | + * {@inheritDoc} |
| 72 | + * |
| 73 | + * <p>Merges the default column mapping rules with those provided by SPI users. |
| 74 | + * In case there are duplicate mappings for some column type, the value provided |
| 75 | + * by SPI users is used. |
| 76 | + * |
| 77 | + * @apiNote This method is made {@code final}, as it is designed |
| 78 | + * to use {@code ImmutableMap.Builder}, which does not allow to override values. |
| 79 | + * Therefore, it is not possible for SPI users to provide their own mapping rules |
| 80 | + * for types such as {@code Timestamp}, for which this class already has |
| 81 | + * a default mapping. SPI users should override |
| 82 | + * {@link #customMapping() DsColumnMapping.customMapping()} instead. |
| 83 | + */ |
59 | 84 | @Override |
60 | | - protected void |
| 85 | + protected final void |
61 | 86 | setupCustomMapping( |
62 | 87 | ImmutableMap.Builder<Class<?>, ColumnTypeMapping<?, ? extends Value<?>>> builder) { |
63 | | - builder.put(Timestamp.class, ofTimestamp()); |
64 | | - builder.put(Version.class, ofVersion()); |
| 88 | + Map<Class<?>, ColumnTypeMapping<?, ? extends Value<?>>> merged = new HashMap<>(); |
| 89 | + ImmutableMap<Class<?>, ColumnTypeMapping<?, ? extends Value<?>>> custom = customMapping(); |
| 90 | + merged.putAll(defaults); |
| 91 | + merged.putAll(custom); |
| 92 | + builder.putAll(merged); |
| 93 | + } |
| 94 | + |
| 95 | + /** |
| 96 | + * Returns the custom column mapping rules. |
| 97 | + * |
| 98 | + * <p>This method is designed for SPI users in order to be able to re-define |
| 99 | + * and-or append their custom mapping. As by default, {@code DsColumnMapping} |
| 100 | + * provides rules for {@link Timestamp} and {@link Version}, SPI users may |
| 101 | + * choose to either override these defaults by returning their own mapping for these types, |
| 102 | + * or supply even more mapping rules. |
| 103 | + * |
| 104 | + * <p>By default, this method returns an empty map. |
| 105 | + * |
| 106 | + * @return custom column mappings, per Java class of column |
| 107 | + */ |
| 108 | + @SPI |
| 109 | + protected ImmutableMap<Class<?>, ColumnTypeMapping<?, ? extends Value<?>>> customMapping() { |
| 110 | + return ImmutableMap.of(); |
65 | 111 | } |
66 | 112 |
|
67 | 113 | @Override |
@@ -120,16 +166,26 @@ protected ColumnTypeMapping<Message, StringValue> ofMessage() { |
120 | 166 | return o -> NullValue.of(); |
121 | 167 | } |
122 | 168 |
|
123 | | - @SuppressWarnings({"ProtoTimestampGetSecondsGetNano", "UnnecessaryLambda"}) |
124 | | - // This behavior is intended. |
125 | | - private static ColumnTypeMapping<Timestamp, TimestampValue> ofTimestamp() { |
| 169 | + /** |
| 170 | + * Returns the default mapping from {@link Timestamp} to {@link TimestampValue}. |
| 171 | + */ |
| 172 | + @SuppressWarnings({ |
| 173 | + "ProtoTimestampGetSecondsGetNano" /* In order to create exact value. */, |
| 174 | + "UnnecessaryLambda" /* For brevity.*/, |
| 175 | + "WeakerAccess" /* To allow access for SPI users. */}) |
| 176 | + protected static ColumnTypeMapping<Timestamp, TimestampValue> ofTimestamp() { |
126 | 177 | return timestamp -> TimestampValue.of( |
127 | 178 | ofTimeSecondsAndNanos(timestamp.getSeconds(), timestamp.getNanos()) |
128 | 179 | ); |
129 | 180 | } |
130 | 181 |
|
131 | | - @SuppressWarnings("UnnecessaryLambda") |
132 | | - private static ColumnTypeMapping<Version, LongValue> ofVersion() { |
| 182 | + /** |
| 183 | + * Returns the default mapping from {@link Version} to {@link LongValue}. |
| 184 | + */ |
| 185 | + @SuppressWarnings({ |
| 186 | + "UnnecessaryLambda" /* For brevity.*/, |
| 187 | + "WeakerAccess" /* To allow access for SPI users. */}) |
| 188 | + protected static ColumnTypeMapping<Version, LongValue> ofVersion() { |
133 | 189 | return version -> LongValue.of(version.getNumber()); |
134 | 190 | } |
135 | 191 | } |
0 commit comments