-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
DefaultMapProperty.java
871 lines (739 loc) · 32.4 KB
/
DefaultMapProperty.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.api.internal.provider;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.gradle.api.Action;
import org.gradle.api.Transformer;
import org.gradle.api.internal.provider.MapCollectors.EntriesFromMap;
import org.gradle.api.internal.provider.MapCollectors.EntriesFromMapProvider;
import org.gradle.api.internal.provider.MapCollectors.EntryWithValueFromProvider;
import org.gradle.api.internal.provider.MapCollectors.SingleEntry;
import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Provider;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.gradle.internal.Cast.uncheckedCast;
import static org.gradle.internal.Cast.uncheckedNonnullCast;
/**
* The implementation for {@link MapProperty}.
* <p>
* Value suppliers for map properties are implementations of {@link MapSupplier}.
* </p>
* <p>
* Increments to map property values are implementations of {@link MapCollector}.
* </p>
*
* This class mimics much of the behavior {@link AbstractCollectionProperty} provides for regular collections
* but for maps. Read that class' documentation to better understand the roles of {@link MapSupplier} and {@link MapCollector}.
*
* @param <K> the type of entry key
* @param <V> the type of entry value
*/
public class DefaultMapProperty<K, V> extends AbstractProperty<Map<K, V>, MapSupplier<K, V>> implements MapProperty<K, V>, MapProviderInternal<K, V> {
private static final String NULL_KEY_FORBIDDEN_MESSAGE = String.format("Cannot add an entry with a null key to a property of type %s.", Map.class.getSimpleName());
private static final String NULL_VALUE_FORBIDDEN_MESSAGE = String.format("Cannot add an entry with a null value to a property of type %s.", Map.class.getSimpleName());
private final Class<K> keyType;
private final Class<V> valueType;
private final ValueCollector<K> keyCollector;
private final MapEntryCollector<K, V> entryCollector;
private MapSupplier<K, V> defaultValue = emptySupplier();
public DefaultMapProperty(PropertyHost propertyHost, Class<K> keyType, Class<V> valueType) {
super(propertyHost);
this.keyType = keyType;
this.valueType = valueType;
keyCollector = new ValidatingValueCollector<>(Set.class, keyType, ValueSanitizers.forType(keyType));
entryCollector = new ValidatingMapEntryCollector<>(keyType, valueType, ValueSanitizers.forType(keyType), ValueSanitizers.forType(valueType));
init(defaultValue, getDefaultConvention());
}
@Override
protected MapSupplier<K, V> getDefaultConvention() {
return noValueSupplier();
}
@Override
protected boolean isDefaultConvention() {
return isNoValueSupplier(getConventionSupplier());
}
private MapSupplier<K, V> emptySupplier() {
return new EmptySupplier();
}
private MapSupplier<K, V> noValueSupplier() {
return uncheckedCast(new NoValueSupplier(Value.missing()));
}
@Nullable
@Override
@SuppressWarnings("unchecked")
public Class<Map<K, V>> getType() {
return (Class) Map.class;
}
@Override
public Class<K> getKeyType() {
return keyType;
}
@Override
public Class<V> getValueType() {
return valueType;
}
@Override
public Class<?> publicType() {
return MapProperty.class;
}
@Override
public int getFactoryId() {
return ManagedFactories.MapPropertyManagedFactory.FACTORY_ID;
}
@Override
public Provider<V> getting(final K key) {
return new EntryProvider(key);
}
@Override
@SuppressWarnings("unchecked")
public MapProperty<K, V> empty() {
setSupplier(emptySupplier());
return this;
}
@Override
@SuppressWarnings("unchecked")
public void setFromAnyValue(@Nullable Object object) {
if (object == null || object instanceof Map<?, ?>) {
set((Map) object);
} else if (object instanceof Provider<?>) {
set((Provider) object);
} else {
throw new IllegalArgumentException(String.format(
"Cannot set the value of a property of type %s using an instance of type %s.", Map.class.getName(), object.getClass().getName()));
}
}
@Override
@SuppressWarnings("unchecked")
public void set(@Nullable Map<? extends K, ? extends V> entries) {
if (entries == null) {
doUnset(true);
} else {
setSupplier(new CollectingSupplier(new MapCollectors.EntriesFromMap<>(entries), false));
}
}
@Override
public void set(Provider<? extends Map<? extends K, ? extends V>> provider) {
setSupplier(new CollectingSupplier(new MapCollectors.EntriesFromMapProvider<>(checkMapProvider(provider)), false));
}
@Override
public MapProperty<K, V> value(@Nullable Map<? extends K, ? extends V> entries) {
set(entries);
return this;
}
@Override
public MapProperty<K, V> value(Provider<? extends Map<? extends K, ? extends V>> provider) {
set(provider);
return this;
}
@Override
public void put(K key, V value) {
getConfigurer().put(key, value);
}
@Override
public void put(K key, Provider<? extends V> providerOfValue) {
getConfigurer().put(key, providerOfValue);
}
@Override
public void putAll(Map<? extends K, ? extends V> entries) {
getConfigurer().putAll(entries);
}
@Override
public void putAll(Provider<? extends Map<? extends K, ? extends V>> provider) {
getConfigurer().putAll(provider);
}
@Override
public void insert(K key, Provider<? extends V> providerOfValue) {
withActualValue(it -> it.put(key, providerOfValue));
}
@Override
public void insert(K key, V value) {
withActualValue(it -> it.put(key, value));
}
@Override
public void insertAll(Provider<? extends Map<? extends K, ? extends V>> provider) {
withActualValue(it -> it.putAll(provider));
}
@Override
public void insertAll(Map<? extends K, ? extends V> entries) {
withActualValue(it -> it.putAll(entries));
}
private void addExplicitCollector(MapCollector<K, V> collector, boolean ignoreAbsent) {
assertCanMutate();
MapSupplier<K, V> explicitValue = getExplicitValue(defaultValue).absentIgnoringIfNeeded(ignoreAbsent);
setSupplier(explicitValue.plus(collector.absentIgnoringIfNeeded(ignoreAbsent)));
}
private Configurer getConfigurer() {
return getConfigurer(false);
}
private Configurer getConfigurer(boolean ignoreAbsent) {
return new Configurer(ignoreAbsent);
}
protected void withActualValue(Action<Configurer> action) {
setToConventionIfUnset();
action.execute(getConfigurer(true));
}
private boolean isNoValueSupplier(MapSupplier<K, V> valueSupplier) {
return valueSupplier instanceof DefaultMapProperty.NoValueSupplier;
}
private ProviderInternal<? extends Map<? extends K, ? extends V>> checkMapProvider(@Nullable Provider<? extends Map<? extends K, ? extends V>> provider) {
return checkMapProvider("value", provider);
}
@SuppressWarnings("unchecked")
private ProviderInternal<? extends Map<? extends K, ? extends V>> checkMapProvider(String valueKind, @Nullable Provider<? extends Map<? extends K, ? extends V>> provider) {
if (provider == null) {
throw new IllegalArgumentException(String.format("Cannot set the %s of a property using a null provider.", valueKind));
}
ProviderInternal<? extends Map<? extends K, ? extends V>> p = Providers.internal(provider);
if (p.getType() != null && !Map.class.isAssignableFrom(p.getType())) {
throw new IllegalArgumentException(String.format("Cannot set the %s of a property of type %s using a provider of type %s.",
valueKind,
Map.class.getName(), p.getType().getName()));
}
if (p instanceof MapProviderInternal) {
Class<? extends K> providerKeyType = ((MapProviderInternal<? extends K, ? extends V>) p).getKeyType();
Class<? extends V> providerValueType = ((MapProviderInternal<? extends K, ? extends V>) p).getValueType();
if (!keyType.isAssignableFrom(providerKeyType) || !valueType.isAssignableFrom(providerValueType)) {
throw new IllegalArgumentException(String.format("Cannot set the %s of a property of type %s with key type %s and value type %s " +
"using a provider with key type %s and value type %s.", valueKind, Map.class.getName(), keyType.getName(), valueType.getName(),
providerKeyType.getName(), providerValueType.getName()));
}
}
return p;
}
@Override
public MapProperty<K, V> convention(@Nullable Map<? extends K, ? extends V> value) {
if (value == null) {
setConvention(noValueSupplier());
} else {
setConvention(new CollectingSupplier(new EntriesFromMap<>(value), false));
}
return this;
}
@Override
public MapProperty<K, V> convention(Provider<? extends Map<? extends K, ? extends V>> valueProvider) {
setConvention(new CollectingSupplier(new EntriesFromMapProvider<>(Providers.internal(valueProvider)), false));
return this;
}
@Override
public MapProperty<K, V> unsetConvention() {
discardConvention();
return this;
}
@Override
public MapProperty<K, V> unset() {
doUnset(false);
return this;
}
private void doUnset(boolean changeDefault) {
super.unset();
if (changeDefault) {
defaultValue = noValueSupplier();
}
}
public void fromState(ExecutionTimeValue<? extends Map<? extends K, ? extends V>> value) {
if (value.isMissing()) {
setSupplier(noValueSupplier());
} else if (value.hasFixedValue()) {
setSupplier(new FixedSupplier<>(uncheckedNonnullCast(value.getFixedValue()), uncheckedCast(value.getSideEffect())));
} else {
CollectingProvider<K, V> asCollectingProvider = uncheckedNonnullCast(value.getChangingValue());
setSupplier(new CollectingSupplier(new EntriesFromMapProvider<>(asCollectingProvider)));
}
}
@Override
public Provider<Set<K>> keySet() {
return new KeySetProvider();
}
@Override
public void replace(Transformer<? extends @org.jetbrains.annotations.Nullable Provider<? extends Map<? extends K, ? extends V>>, ? super Provider<Map<K, V>>> transformation) {
Provider<? extends Map<? extends K, ? extends V>> newValue = transformation.transform(shallowCopy());
if (newValue != null) {
set(newValue);
} else {
set((Map<? extends K, ? extends V>) null);
}
}
@Override
protected String describeContents() {
return String.format("Map(%s->%s, %s)", keyType.getSimpleName(), valueType.getSimpleName(), describeValue());
}
@Override
protected Value<? extends Map<K, V>> calculateValueFrom(EvaluationContext.ScopeContext context, MapSupplier<K, V> value, ValueConsumer consumer) {
return value.calculateValue(consumer);
}
@Override
protected MapSupplier<K, V> finalValue(EvaluationContext.ScopeContext context, MapSupplier<K, V> value, ValueConsumer consumer) {
Value<? extends Map<K, V>> result = value.calculateValue(consumer);
if (!result.isMissing()) {
return new FixedSupplier<>(result.getWithoutSideEffect(), uncheckedCast(result.getSideEffect()));
} else if (result.getPathToOrigin().isEmpty()) {
return noValueSupplier();
} else {
return new NoValueSupplier(result);
}
}
@Override
protected ExecutionTimeValue<? extends Map<K, V>> calculateOwnExecutionTimeValue(EvaluationContext.ScopeContext context, MapSupplier<K, V> value) {
return value.calculateExecutionTimeValue();
}
private class EntryProvider extends AbstractMinimalProvider<V> {
private final K key;
public EntryProvider(K key) {
this.key = key;
}
@Nullable
@Override
public Class<V> getType() {
return valueType;
}
@Override
protected Value<? extends V> calculateOwnValue(ValueConsumer consumer) {
Value<? extends Map<K, V>> result = DefaultMapProperty.this.calculateOwnValue(consumer);
if (result.isMissing()) {
return result.asType();
}
Value<? extends V> resultValue = Value.ofNullable(result.getWithoutSideEffect().get(key));
return resultValue.withSideEffect(SideEffect.fixedFrom(result));
}
}
private class KeySetProvider extends AbstractMinimalProvider<Set<K>> {
@Nullable
@Override
@SuppressWarnings("unchecked")
public Class<Set<K>> getType() {
return (Class) Set.class;
}
@Override
protected Value<? extends Set<K>> calculateOwnValue(ValueConsumer consumer) {
try (EvaluationContext.ScopeContext context = DefaultMapProperty.this.openScope()) {
beforeRead(context, consumer);
return getSupplier(context).calculateKeys(consumer);
}
}
}
private class NoValueSupplier implements MapSupplier<K, V> {
private final Value<? extends Map<K, V>> value;
public NoValueSupplier(Value<? extends Map<K, V>> value) {
this.value = value.asType();
assert value.isMissing();
}
@Override
public MapSupplier<K, V> absentIgnoring() {
return emptySupplier();
}
@Override
public boolean calculatePresence(ValueConsumer consumer) {
return false;
}
@Override
public Value<? extends Map<K, V>> calculateValue(ValueConsumer consumer) {
return value;
}
@Override
public Value<? extends Set<K>> calculateKeys(ValueConsumer consumer) {
return value.asType();
}
@Override
public MapSupplier<K, V> plus(MapCollector<K, V> collector) {
// nothing + something = nothing
return this;
}
@Override
public ExecutionTimeValue<? extends Map<K, V>> calculateExecutionTimeValue() {
return ExecutionTimeValue.missing();
}
@Override
public ValueProducer getProducer() {
return ValueProducer.unknown();
}
@Override
public String toString() {
return value.toString();
}
}
private class EmptySupplier implements MapSupplier<K, V> {
@Override
public MapSupplier<K, V> absentIgnoring() {
return this;
}
@Override
public boolean calculatePresence(ValueConsumer consumer) {
return true;
}
@Override
public Value<? extends Map<K, V>> calculateValue(ValueConsumer consumer) {
return Value.of(ImmutableMap.of());
}
@Override
public Value<? extends Set<K>> calculateKeys(ValueConsumer consumer) {
return Value.of(ImmutableSet.of());
}
@Override
public MapSupplier<K, V> plus(MapCollector<K, V> collector) {
// empty + something = something
return new CollectingSupplier(collector);
}
@Override
public ExecutionTimeValue<? extends Map<K, V>> calculateExecutionTimeValue() {
return ExecutionTimeValue.fixedValue(ImmutableMap.of());
}
@Override
public ValueProducer getProducer() {
return ValueProducer.noProducer();
}
@Override
public String toString() {
return "{}";
}
}
private static class FixedSupplier<K, V> implements MapSupplier<K, V> {
private final Map<K, V> entries;
private final SideEffect<? super Map<K, V>> sideEffect;
public FixedSupplier(Map<K, V> entries, @Nullable SideEffect<? super Map<K, V>> sideEffect) {
this.entries = entries;
this.sideEffect = sideEffect;
}
@Override
public boolean calculatePresence(ValueConsumer consumer) {
return true;
}
@Override
public Value<? extends Map<K, V>> calculateValue(ValueConsumer consumer) {
return Value.of(entries).withSideEffect(sideEffect);
}
@Override
public Value<? extends Set<K>> calculateKeys(ValueConsumer consumer) {
return Value.of(entries.keySet());
}
@Override
public MapSupplier<K, V> plus(MapCollector<K, V> collector) {
throw new UnsupportedOperationException();
}
@Override
public MapSupplier<K, V> absentIgnoring() {
return this;
}
@Override
public ExecutionTimeValue<? extends Map<K, V>> calculateExecutionTimeValue() {
return ExecutionTimeValue.fixedValue(entries).withSideEffect(sideEffect);
}
@Override
public ValueProducer getProducer() {
return ValueProducer.unknown();
}
@Override
public String toString() {
return entries.toString();
}
}
private class CollectingSupplier implements MapSupplier<K, V> {
private final MapCollector<K, V> collector;
// TODO-RC: can we get rid of this? Can we only keep this in Collectors? Changing execution time value is the only case that needs this.
private final boolean ignoreAbsent;
public CollectingSupplier(MapCollector<K, V> collector, boolean ignoreAbsent) {
this.collector = collector;
this.ignoreAbsent = ignoreAbsent;
}
public CollectingSupplier(MapCollector<K, V> collector) {
this(collector, false);
}
@Override
public MapSupplier<K, V> absentIgnoring() {
return ignoreAbsent ? this : new CollectingSupplier(collector, true);
}
@Override
public boolean calculatePresence(ValueConsumer consumer) {
return collector.calculatePresence(consumer);
}
@Override
public Value<? extends Set<K>> calculateKeys(ValueConsumer consumer) {
// TODO - don't make a copy when the collector already produces an immutable collection
ImmutableSet.Builder<K> builder = ImmutableSet.builder();
Value<Void> result = collector.collectKeys(consumer, keyCollector, builder);
if (result.isMissing()) {
return result.asType();
}
return Value.of(ImmutableSet.copyOf(builder.build())).withSideEffect(SideEffect.fixedFrom(result));
}
@Override
public Value<? extends Map<K, V>> calculateValue(ValueConsumer consumer) {
// TODO - don't make a copy when the collector already produces an immutable collection
// Cannot use ImmutableMap.Builder here, as it does not allow multiple entries with the same key, however the contract
// for MapProperty allows a provider to override the entries of earlier providers and so there can be multiple entries
// with the same key
Map<K, V> entries = new LinkedHashMap<>();
Value<Void> result = collector.collectEntries(consumer, entryCollector, entries);
if (result.isMissing()) {
return result.asType();
}
return Value.of(ImmutableMap.copyOf(entries)).withSideEffect(SideEffect.fixedFrom(result));
}
@Override
public MapSupplier<K, V> plus(MapCollector<K, V> addedCollector) {
MapCollector<K, V> left = this.collector.absentIgnoringIfNeeded(ignoreAbsent);
MapCollector<K, V> right = addedCollector;
PlusCollector<K, V> newCollector = new PlusCollector<>(left, right);
return new CollectingSupplier(newCollector);
}
@Override
public ExecutionTimeValue<? extends Map<K, V>> calculateExecutionTimeValue() {
List<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> execTimeValues = collectExecutionTimeValues();
ExecutionTimeValue<Map<K, V>> fixedOrMissing = fixedOrMissingValueOf(execTimeValues);
return fixedOrMissing != null
? fixedOrMissing
: ExecutionTimeValue.changingValue(new CollectingProvider<>(execTimeValues));
}
/**
* Try to simplify the set of execution values to either a missing value or a fixed value.
*/
@Nullable
private ExecutionTimeValue<Map<K, V>> fixedOrMissingValueOf(List<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> values) {
boolean fixed = true;
boolean changingContent = false;
for (ExecutionTimeValue<? extends Map<? extends K, ? extends V>> value : values) {
if (value.isMissing()) {
return ExecutionTimeValue.missing();
}
if (value.isChangingValue()) {
fixed = false;
} else if (value.hasChangingContent()) {
changingContent = true;
}
}
if (fixed) {
SideEffectBuilder<? super Map<K, V>> sideEffectBuilder = SideEffect.builder();
ImmutableMap<K, V> entries = collectEntries(values, sideEffectBuilder);
return maybeChangingContent(ExecutionTimeValue.fixedValue(entries), changingContent)
.withSideEffect(sideEffectBuilder.build());
}
return null;
}
private List<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> collectExecutionTimeValues() {
List<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> values = new ArrayList<>();
collector.calculateExecutionTimeValue(values::add);
return values;
}
private ImmutableMap<K, V> collectEntries(List<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> values, SideEffectBuilder<? super Map<K, V>> sideEffectBuilder) {
Map<K, V> entries = new LinkedHashMap<>();
for (ExecutionTimeValue<? extends Map<? extends K, ? extends V>> value : values) {
entryCollector.addAll(value.getFixedValue().entrySet(), entries);
sideEffectBuilder.add(SideEffect.fixedFrom(value));
}
return ImmutableMap.copyOf(entries);
}
private ExecutionTimeValue<Map<K, V>> maybeChangingContent(ExecutionTimeValue<Map<K, V>> value, boolean changingContent) {
return changingContent ? value.withChangingContent() : value;
}
@Override
public ValueProducer getProducer() {
return collector.getProducer();
}
@Override
public String toString() {
return collector.toString();
}
}
private static class CollectingProvider<K, V> extends AbstractMinimalProvider<Map<K, V>> {
private final List<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> values;
public CollectingProvider(List<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> values) {
this.values = values;
}
@Nullable
@Override
public Class<Map<K, V>> getType() {
return uncheckedCast(Map.class);
}
@Override
public ExecutionTimeValue<? extends Map<K, V>> calculateExecutionTimeValue() {
return ExecutionTimeValue.changingValue(this);
}
@Override
protected Value<? extends Map<K, V>> calculateOwnValue(ValueConsumer consumer) {
Map<K, V> entries = new LinkedHashMap<>();
SideEffectBuilder<? super Map<K, V>> sideEffectBuilder = SideEffect.builder();
for (ExecutionTimeValue<? extends Map<? extends K, ? extends V>> executionTimeValue : values) {
Value<? extends Map<? extends K, ? extends V>> value = executionTimeValue.toProvider().calculateValue(consumer);
if (value.isMissing()) {
return Value.missing();
} else {
entries.putAll(value.getWithoutSideEffect());
sideEffectBuilder.add(SideEffect.fixedFrom(value));
}
}
return Value.of(ImmutableMap.copyOf(entries)).withSideEffect(sideEffectBuilder.build());
}
}
private class Configurer {
private final boolean ignoreAbsent;
public Configurer(boolean ignoreAbsent) {
this.ignoreAbsent = ignoreAbsent;
}
void addCollector(MapCollector<K, V> collector) {
addExplicitCollector(collector, ignoreAbsent);
}
public void put(K key, V value) {
Preconditions.checkNotNull(key, NULL_KEY_FORBIDDEN_MESSAGE);
Preconditions.checkNotNull(value, NULL_VALUE_FORBIDDEN_MESSAGE);
addCollector(new SingleEntry<>(key, value));
}
public void put(K key, Provider<? extends V> providerOfValue) {
Preconditions.checkNotNull(key, NULL_KEY_FORBIDDEN_MESSAGE);
Preconditions.checkNotNull(providerOfValue, NULL_VALUE_FORBIDDEN_MESSAGE);
ProviderInternal<? extends V> p = Providers.internal(providerOfValue);
if (p.getType() != null && !valueType.isAssignableFrom(p.getType())) {
throw new IllegalArgumentException(String.format("Cannot add an entry to a property of type %s with values of type %s using a provider of type %s.",
Map.class.getName(), valueType.getName(), p.getType().getName()));
}
addCollector(new EntryWithValueFromProvider<>(key, Providers.internal(providerOfValue)));
}
public void putAll(Map<? extends K, ? extends V> entries) {
addCollector(new EntriesFromMap<>(entries));
}
public void putAll(Provider<? extends Map<? extends K, ? extends V>> provider) {
addCollector(new EntriesFromMapProvider<>(checkMapProvider(provider)));
}
}
private static abstract class AbstractPlusCollector<K, V> implements MapCollector<K, V> {
protected final MapCollector<K, V> left;
protected final MapCollector<K, V> right;
private AbstractPlusCollector(MapCollector<K, V> left, MapCollector<K, V> right) {
this.left = left;
this.right = right;
}
@Override
public ValueProducer getProducer() {
return left.getProducer().plus(right.getProducer());
}
@Override
public String toString() {
return left + " + " + right;
}
}
private static class PlusCollector<K, V> extends AbstractPlusCollector<K, V> {
public PlusCollector(MapCollector<K, V> left, MapCollector<K, V> right) {
super(left, right);
}
@Override
public MapCollector<K, V> absentIgnoring() {
return new AbsentIgnoringPlusCollector<K, V>(left, right);
}
@Override
public boolean calculatePresence(ValueConsumer consumer) {
return left.calculatePresence(consumer) && right.calculatePresence(consumer);
}
@Override
public Value<Void> collectEntries(ValueConsumer consumer, MapEntryCollector<K, V> collector, Map<K, V> dest) {
Value<Void> leftValue = left.collectEntries(consumer, collector, dest);
if (leftValue.isMissing()) {
return leftValue;
}
Value<Void> rightValue = right.collectEntries(consumer, collector, dest);
if (rightValue.isMissing()) {
return rightValue;
}
return Value.present()
.withSideEffect(SideEffect.fixedFrom(leftValue))
.withSideEffect(SideEffect.fixedFrom(rightValue));
}
@Override
public Value<Void> collectKeys(ValueConsumer consumer, ValueCollector<K> collector, ImmutableCollection.Builder<K> dest) {
Value<Void> result = left.collectKeys(consumer, collector, dest);
if (result.isMissing()) {
return result;
}
return right.collectKeys(consumer, collector, dest);
}
@Override
public void calculateExecutionTimeValue(Action<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> visitor) {
left.calculateExecutionTimeValue(visitor);
right.calculateExecutionTimeValue(visitor);
}
}
/**
* A plus collector that either produces a composition of both of its left and right sides,
* or Value.present() with empty content (if left or right side are missing).
*/
private static class AbsentIgnoringPlusCollector<K, V> extends AbstractPlusCollector<K, V> {
private AbsentIgnoringPlusCollector(MapCollector<K, V> left, MapCollector<K, V> right) {
super(left, right);
}
@Override
public boolean calculatePresence(ValueConsumer consumer) {
return true;
}
@Override
public MapCollector<K, V> absentIgnoring() {
return this;
}
@Override
public Value<Void> collectEntries(ValueConsumer consumer, MapEntryCollector<K, V> collector, Map<K, V> dest) {
Map<K, V> candidates = new LinkedHashMap<>();
// we cannot use dest directly because we don't want to emit any entries if either left or right are missing
Value<Void> leftValue = left.collectEntries(consumer, collector, candidates);
if (leftValue.isMissing()) {
return Value.present();
}
Value<Void> rightValue = right.collectEntries(consumer, collector, candidates);
if (rightValue.isMissing()) {
return Value.present();
}
dest.putAll(candidates);
return Value.present()
.withSideEffect(SideEffect.fixedFrom(leftValue))
.withSideEffect(SideEffect.fixedFrom(rightValue));
}
@Override
public Value<Void> collectKeys(ValueConsumer consumer, ValueCollector<K> collector, ImmutableCollection.Builder<K> dest) {
ImmutableSet.Builder<K> candidateKeys = ImmutableSet.builder();
Value<Void> leftResult = left.collectKeys(consumer, collector, candidateKeys);
if (leftResult.isMissing()) {
return Value.present();
}
Value<Void> rightResult = right.collectKeys(consumer, collector, candidateKeys);
if (rightResult.isMissing()) {
return Value.present();
}
dest.addAll(candidateKeys.build());
return rightResult;
}
@Override
public void calculateExecutionTimeValue(Action<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> visitor) {
boolean[] anyMissing = {false};
ImmutableList.Builder<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> toVisit = ImmutableList.builder();
Action<ExecutionTimeValue<? extends Map<? extends K, ? extends V>>> safeVisitor = value -> {
if (value.isMissing()) {
anyMissing[0] = true;
} else {
toVisit.add(value);
}
};
left.calculateExecutionTimeValue(safeVisitor);
right.calculateExecutionTimeValue(safeVisitor);
if (!anyMissing[0]) {
toVisit.build().forEach(it -> visitor.execute(it));
}
}
}
}