Skip to content

Commit aee03c5

Browse files
committed
Use composite collections in attribute merging
This commit introduces composite collections (i.e. Collection, Set, Map) and uses these composites in request predicates, where before new collections were instantiated. Closes gh-32245
1 parent 89d746d commit aee03c5

File tree

9 files changed

+953
-53
lines changed

9 files changed

+953
-53
lines changed

Diff for: spring-core/src/main/java/org/springframework/util/CollectionUtils.java

+40
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import java.util.Properties;
3232
import java.util.Set;
3333
import java.util.SortedSet;
34+
import java.util.function.BiFunction;
35+
import java.util.function.Consumer;
3436

3537
import org.springframework.lang.Nullable;
3638

@@ -506,4 +508,42 @@ public static <K, V> MultiValueMap<K, V> unmodifiableMultiValueMap(
506508
return new UnmodifiableMultiValueMap<>(targetMap);
507509
}
508510

511+
/**
512+
* Return a (partially unmodifiable) map that combines the provided two
513+
* maps. Invoking {@link Map#put(Object, Object)} or {@link Map#putAll(Map)}
514+
* on the returned map results in an {@link UnsupportedOperationException}.
515+
* @param first the first map to compose
516+
* @param second the second map to compose
517+
* @return a new map that composes the given two maps
518+
* @since 6.2
519+
*/
520+
public static <K, V> Map<K, V> compositeMap(Map<K,V> first, Map<K,V> second) {
521+
return new CompositeMap<>(first, second);
522+
}
523+
524+
/**
525+
* Return a map that combines the provided maps. Invoking
526+
* {@link Map#put(Object, Object)} on the returned map will apply
527+
* {@code putFunction}, or will throw an
528+
* {@link UnsupportedOperationException} {@code putFunction} is
529+
* {@code null}. The same applies to {@link Map#putAll(Map)} and
530+
* {@code putAllFunction}.
531+
* @param first the first map to compose
532+
* @param second the second map to compose
533+
* @param putFunction applied when {@code Map::put} is invoked. If
534+
* {@code null}, {@code Map::put} throws an
535+
* {@code UnsupportedOperationException}.
536+
* @param putAllFunction applied when {@code Map::putAll} is invoked. If
537+
* {@code null}, {@code Map::putAll} throws an
538+
* {@code UnsupportedOperationException}.
539+
* @return a new map that composes the give maps
540+
* @since 6.2
541+
*/
542+
public static <K, V> Map<K, V> compositeMap(Map<K,V> first, Map<K,V> second,
543+
@Nullable BiFunction<K, V, V> putFunction,
544+
@Nullable Consumer<Map<K, V>> putAllFunction) {
545+
546+
return new CompositeMap<>(first, second, putFunction, putAllFunction);
547+
}
548+
509549
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.util;
18+
19+
import java.lang.reflect.Array;
20+
import java.util.Collection;
21+
import java.util.Iterator;
22+
23+
24+
/**
25+
* Composite collection that combines two other collections. This type is only
26+
* exposed through {@link CompositeMap#values()}.
27+
*
28+
* @author Arjen Poutsma
29+
* @since 6.2
30+
* @param <E> the type of elements maintained by this collection
31+
*/
32+
class CompositeCollection<E> implements Collection<E> {
33+
34+
private final Collection<E> first;
35+
36+
private final Collection<E> second;
37+
38+
39+
CompositeCollection(Collection<E> first, Collection<E> second) {
40+
Assert.notNull(first, "First must not be null");
41+
Assert.notNull(second, "Second must not be null");
42+
this.first = first;
43+
this.second = second;
44+
}
45+
46+
@Override
47+
public int size() {
48+
return this.first.size() + this.second.size();
49+
}
50+
51+
@Override
52+
public boolean isEmpty() {
53+
return this.first.isEmpty() && this.second.isEmpty();
54+
}
55+
56+
@Override
57+
public boolean contains(Object o) {
58+
if (this.first.contains(o)) {
59+
return true;
60+
}
61+
else {
62+
return this.second.contains(o);
63+
}
64+
}
65+
66+
@Override
67+
public Iterator<E> iterator() {
68+
CompositeIterator<E> iterator = new CompositeIterator<>();
69+
iterator.add(this.first.iterator());
70+
iterator.add(this.second.iterator());
71+
return iterator;
72+
}
73+
74+
@Override
75+
public Object[] toArray() {
76+
Object[] result = new Object[size()];
77+
Object[] firstArray = this.first.toArray();
78+
Object[] secondArray = this.second.toArray();
79+
System.arraycopy(firstArray, 0, result, 0, firstArray.length);
80+
System.arraycopy(secondArray, 0, result, firstArray.length, secondArray.length);
81+
return result;
82+
}
83+
84+
@Override
85+
@SuppressWarnings("unchecked")
86+
public <T> T[] toArray(T[] a) {
87+
int size = this.size();
88+
T[] result;
89+
if (a.length >= size) {
90+
result = a;
91+
}
92+
else {
93+
result = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
94+
}
95+
96+
int idx = 0;
97+
for (E e : this) {
98+
result[idx++] = (T) e;
99+
}
100+
if (result.length > size) {
101+
result[size] = null;
102+
}
103+
return result;
104+
}
105+
106+
@Override
107+
public boolean add(E e) {
108+
throw new UnsupportedOperationException();
109+
}
110+
111+
@Override
112+
public boolean remove(Object o) {
113+
boolean firstResult = this.first.remove(o);
114+
boolean secondResult = this.second.remove(o);
115+
return firstResult || secondResult;
116+
}
117+
118+
@Override
119+
public boolean containsAll(Collection<?> c) {
120+
for (Object o : c) {
121+
if (!contains(o)) {
122+
return false;
123+
}
124+
}
125+
return true;
126+
}
127+
128+
@Override
129+
public boolean addAll(Collection<? extends E> c) {
130+
boolean changed = false;
131+
for (E e : c) {
132+
if (add(e)) {
133+
changed = true;
134+
}
135+
}
136+
return changed;
137+
}
138+
139+
@Override
140+
public boolean removeAll(Collection<?> c) {
141+
if (c.isEmpty()) {
142+
return false;
143+
}
144+
boolean firstResult = this.first.removeAll(c);
145+
boolean secondResult = this.second.removeAll(c);
146+
147+
return firstResult || secondResult;
148+
}
149+
150+
@Override
151+
public boolean retainAll(Collection<?> c) {
152+
boolean firstResult = this.first.retainAll(c);
153+
boolean secondResult = this.second.retainAll(c);
154+
155+
return firstResult || secondResult;
156+
}
157+
158+
@Override
159+
public void clear() {
160+
this.first.clear();
161+
this.second.clear();
162+
}
163+
}

0 commit comments

Comments
 (0)