Skip to content

Commit d6f5d21

Browse files
author
Dmytro Ukhlov
committed
JENKINS-75986: Refactor BuildReferenceMapAdapter to improve code re-usage
- Reuse the standard collection base classes (AbstractMap, AbstractSet, AbstractCollection) to avoid redundant code. - Treat BuildReferenceMapAdapter strictly as a read-only view (except for the case where iterator remove() delegation is required for core), and remove unnecessary mutation methods. - Move the Spliterator customizations from AbstractLazyLoadRunMap into BuildReferenceMapAdapter so that entrySet(), values(), and keySet() implementations can be reused. - Align BuildReferenceMapAdapter with the weak consistency semantics of RunMap (e.g., size() may be approximate, but isEmpty() should remain reliable).
1 parent 0f1904b commit d6f5d21

File tree

5 files changed

+203
-533
lines changed

5 files changed

+203
-533
lines changed

core/src/main/java/hudson/model/RunMap.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ protected String getIdOf(R r) {
201201

202202
/**
203203
* Add a <em>new</em> build to the map.
204-
* Do not use when loading existing builds (use {@link #put(Integer, Object)}).
204+
* Do not use when loading existing builds (use {@link #putAll(Map)}).
205205
*/
206206
@Override
207207
public R put(R r) {

core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java

Lines changed: 32 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@
3535
import hudson.util.CopyOnWriteMap;
3636
import java.io.File;
3737
import java.io.IOException;
38-
import java.util.AbstractCollection;
3938
import java.util.AbstractMap;
40-
import java.util.AbstractSet;
4139
import java.util.Collection;
4240
import java.util.Collections;
4341
import java.util.Comparator;
@@ -46,10 +44,7 @@
4644
import java.util.NoSuchElementException;
4745
import java.util.Set;
4846
import java.util.SortedMap;
49-
import java.util.Spliterator;
50-
import java.util.Spliterators;
5147
import java.util.TreeMap;
52-
import java.util.function.IntConsumer;
5348
import java.util.function.IntPredicate;
5449
import java.util.logging.Level;
5550
import java.util.logging.Logger;
@@ -101,146 +96,28 @@
10196
public abstract class AbstractLazyLoadRunMap<R> extends AbstractMap<Integer, R> implements SortedMap<Integer, R> {
10297
private final CopyOnWriteMap.Tree<Integer, BuildReference<R>> core = new CopyOnWriteMap.Tree<>(
10398
Collections.reverseOrder());
104-
105-
private LazyLoadRunMapEntrySet<R> entrySet = new LazyLoadRunMapEntrySet<>(this);
106-
107-
private transient volatile Set<Integer> keySet;
108-
private transient volatile Collection<R> values;
99+
private final BuildReferenceMapAdapter<R> adapter = new BuildReferenceMapAdapter<>(core, this::resolveBuildRef,
100+
this::getNumberOf) {
101+
@Override
102+
protected boolean removeValue(R value) {
103+
return AbstractLazyLoadRunMap.this.removeValue(value);
104+
}
105+
};
109106

110107
@Override
111108
public Set<Integer> keySet() {
112-
Set<Integer> ks = keySet;
113-
if (ks == null) {
114-
ks = new AbstractSet<>() {
115-
@Override
116-
public Iterator<Integer> iterator() {
117-
return new Iterator() {
118-
private final Iterator<Entry<Integer, R>> it = entrySet().iterator();
119-
120-
@Override
121-
public boolean hasNext() {
122-
return it.hasNext();
123-
}
124-
125-
@Override
126-
public Integer next() {
127-
return it.next().getKey();
128-
}
129-
130-
@Override
131-
public void remove() {
132-
it.remove();
133-
}
134-
};
135-
}
136-
137-
@Override
138-
public Spliterator<Integer> spliterator() {
139-
return new Spliterators.AbstractIntSpliterator(
140-
Long.MAX_VALUE,
141-
Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SORTED) {
142-
private final Iterator<Integer> it = iterator();
143-
144-
@Override
145-
public boolean tryAdvance(IntConsumer action) {
146-
if (action == null) {
147-
throw new NullPointerException();
148-
}
149-
if (it.hasNext()) {
150-
action.accept(it.next());
151-
return true;
152-
}
153-
return false;
154-
}
155-
156-
@Override
157-
public Comparator<Integer> getComparator() {
158-
return Collections.reverseOrder();
159-
}
160-
};
161-
}
162-
163-
@Override
164-
public int size() {
165-
return AbstractLazyLoadRunMap.this.size();
166-
}
167-
168-
@Override
169-
public boolean isEmpty() {
170-
return AbstractLazyLoadRunMap.this.isEmpty();
171-
}
172-
173-
@Override
174-
public void clear() {
175-
AbstractLazyLoadRunMap.this.clear();
176-
}
177-
178-
@Override
179-
public boolean contains(Object k) {
180-
return AbstractLazyLoadRunMap.this.containsKey(k);
181-
}
182-
};
183-
keySet = ks;
184-
}
185-
return ks;
109+
return adapter.keySet();
186110
}
187111

188112
@Override
189113
public Collection<R> values() {
190-
Collection<R> vals = values;
191-
if (vals == null) {
192-
vals = new AbstractCollection<>() {
193-
@Override
194-
public Iterator<R> iterator() {
195-
return new Iterator<>() {
196-
private final Iterator<Entry<Integer, R>> it = entrySet().iterator();
197-
198-
@Override
199-
public boolean hasNext() {
200-
return it.hasNext();
201-
}
202-
203-
@Override
204-
public R next() {
205-
return it.next().getValue();
206-
}
207-
208-
@Override
209-
public void remove() {
210-
it.remove();
211-
}
212-
};
213-
}
214-
215-
@Override
216-
public Spliterator<R> spliterator() {
217-
return Spliterators.spliteratorUnknownSize(
218-
iterator(), Spliterator.DISTINCT | Spliterator.ORDERED);
219-
}
220-
221-
@Override
222-
public int size() {
223-
return AbstractLazyLoadRunMap.this.size();
224-
}
225-
226-
@Override
227-
public boolean isEmpty() {
228-
return AbstractLazyLoadRunMap.this.isEmpty();
229-
}
230-
231-
@Override
232-
public void clear() {
233-
AbstractLazyLoadRunMap.this.clear();
234-
}
114+
return adapter.values();
115+
}
235116

236-
@Override
237-
public boolean contains(Object v) {
238-
return AbstractLazyLoadRunMap.this.containsValue(v);
239-
}
240-
};
241-
values = vals;
242-
}
243-
return vals;
117+
@Override
118+
public Set<Entry<Integer, R>> entrySet() {
119+
assert baseDirInitialized();
120+
return adapter.entrySet();
244121
}
245122

246123
/**
@@ -354,18 +231,22 @@ public final void recognizeNumber(int buildNumber) {
354231

355232
@Override
356233
public Comparator<? super Integer> comparator() {
357-
return Collections.reverseOrder();
234+
return core.comparator();
358235
}
359236

360237
@Override
361238
public boolean isEmpty() {
362-
return search(Integer.MAX_VALUE, DESC) == null;
239+
return adapter.isEmpty();
363240
}
364241

365242
@Override
366-
public Set<Entry<Integer, R>> entrySet() {
367-
assert baseDirInitialized();
368-
return entrySet;
243+
public boolean containsKey(Object value) {
244+
return adapter.containsKey(value);
245+
}
246+
247+
@Override
248+
public boolean containsValue(Object value) {
249+
return adapter.containsValue(value);
369250
}
370251

371252
/**
@@ -379,7 +260,7 @@ public SortedMap<Integer, R> getLoadedBuilds() {
379260
res.put(entry.getKey(), buildRef);
380261
}
381262
}
382-
return Collections.unmodifiableSortedMap(new BuildReferenceMapAdapter<>(this, res));
263+
return Collections.unmodifiableSortedMap(new BuildReferenceMapAdapter<>(res, this::resolveBuildRef, this::getNumberOf));
383264
}
384265

385266
/**
@@ -390,33 +271,17 @@ public SortedMap<Integer, R> getLoadedBuilds() {
390271
*/
391272
@Override
392273
public SortedMap<Integer, R> subMap(Integer fromKey, Integer toKey) {
393-
// TODO: if this method can produce a lazy map, that'd be wonderful
394-
// because due to the lack of floor/ceil/higher/lower kind of methods
395-
// to look up keys in SortedMap, various places of Jenkins rely on
396-
// subMap+firstKey/lastKey combo.
397-
398-
R start = search(fromKey, DESC);
399-
if (start == null) return EMPTY_SORTED_MAP;
400-
401-
R end = search(toKey, ASC);
402-
if (end == null) return EMPTY_SORTED_MAP;
403-
404-
for (R i = start; i != end; ) {
405-
i = search(getNumberOf(i) - 1, DESC);
406-
assert i != null;
407-
}
408-
409-
return Collections.unmodifiableSortedMap(new BuildReferenceMapAdapter<>(this, core.subMap(fromKey, toKey)));
274+
return adapter.subMap(fromKey, toKey);
410275
}
411276

412277
@Override
413278
public SortedMap<Integer, R> headMap(Integer toKey) {
414-
return subMap(Integer.MAX_VALUE, toKey);
279+
return adapter.headMap(toKey);
415280
}
416281

417282
@Override
418283
public SortedMap<Integer, R> tailMap(Integer fromKey) {
419-
return subMap(fromKey, Integer.MIN_VALUE);
284+
return adapter.tailMap(fromKey);
420285
}
421286

422287
@Override
@@ -443,11 +308,7 @@ public R oldestBuild() {
443308

444309
@Override
445310
public R get(Object key) {
446-
if (key instanceof Integer) {
447-
int n = (Integer) key;
448-
return get(n);
449-
}
450-
return super.get(key);
311+
return adapter.get(key);
451312
}
452313

453314
public R get(int n) {
@@ -552,7 +413,7 @@ private R resolveBuildRef(BuildReference<R> ref) {
552413
}
553414

554415
public R getByNumber(int n) {
555-
return resolveBuildRef(core.get(n));
416+
return adapter.get(n);
556417
}
557418

558419
/**
@@ -597,14 +458,9 @@ public synchronized void putAll(Map<? extends Integer, ? extends R> newData) {
597458
core.putAll(newWrapperData);
598459
}
599460

600-
/**
601-
* Return underlining {@link BuildReference} core map.
602-
*
603-
* @return
604-
* full build reference map.
605-
*/
606-
/*package*/ SortedMap<Integer, BuildReference<R>> all() {
607-
return core;
461+
@Override
462+
public R remove(Object key) {
463+
return adapter.remove(key);
608464
}
609465

610466
/**
@@ -652,7 +508,6 @@ protected BuildReference<R> createReference(R r) {
652508
return new BuildReference<>(getIdOf(r), r);
653509
}
654510

655-
656511
/**
657512
* Parses {@code R} instance from data in the specified directory.
658513
*
@@ -694,7 +549,5 @@ public enum Direction {
694549
ASC, DESC, EXACT
695550
}
696551

697-
private static final SortedMap EMPTY_SORTED_MAP = Collections.unmodifiableSortedMap(new TreeMap());
698-
699552
static final Logger LOGGER = Logger.getLogger(AbstractLazyLoadRunMap.class.getName());
700553
}

0 commit comments

Comments
 (0)