2626
2727import static jenkins .model .lazy .AbstractLazyLoadRunMap .Direction .ASC ;
2828import static jenkins .model .lazy .AbstractLazyLoadRunMap .Direction .DESC ;
29+ import static jenkins .model .lazy .AbstractLazyLoadRunMap .Direction .EXACT ;
2930
3031import edu .umd .cs .findbugs .annotations .CheckForNull ;
3132import hudson .model .Job ;
3940import java .util .Collection ;
4041import java .util .Collections ;
4142import java .util .Comparator ;
42- import java .util .Iterator ;
4343import java .util .Map ;
44+ import java .util .NavigableMap ;
4445import java .util .NoSuchElementException ;
4546import java .util .Set ;
4647import java .util .SortedMap ;
9697public abstract class AbstractLazyLoadRunMap <R > extends AbstractMap <Integer , R > implements SortedMap <Integer , R > {
9798 private final CopyOnWriteMap .Tree <Integer , BuildReference <R >> core = new CopyOnWriteMap .Tree <>(
9899 Collections .reverseOrder ());
99- private final BuildReferenceMapAdapter <R > adapter = new BuildReferenceMapAdapter <>( core ,
100- this :: resolveBuildRef , this :: getNumberOf , this :: getBuildClass ) {
100+ private final BuildReferenceMapAdapter . Resolver <R > buildResolver = new BuildReferenceMapAdapterResolver ();
101+ private final BuildReferenceMapAdapter < R > adapter = new BuildReferenceMapAdapter <>( core , buildResolver ) {
101102 @ Override
102103 protected boolean removeValue (R value ) {
103104 return AbstractLazyLoadRunMap .this .removeValue (value );
@@ -260,7 +261,7 @@ public SortedMap<Integer, R> getLoadedBuilds() {
260261 res .put (entry .getKey (), buildRef );
261262 }
262263 }
263- return new BuildReferenceMapAdapter <>(res , this :: resolveBuildRef , this :: getNumberOf , this :: getBuildClass );
264+ return new BuildReferenceMapAdapter <>(res , buildResolver );
264265 }
265266
266267 /**
@@ -286,16 +287,12 @@ public SortedMap<Integer, R> tailMap(Integer fromKey) {
286287
287288 @ Override
288289 public Integer firstKey () {
289- R r = newestBuild ();
290- if (r == null ) throw new NoSuchElementException ();
291- return getNumberOf (r );
290+ return adapter .firstKey ();
292291 }
293292
294293 @ Override
295294 public Integer lastKey () {
296- R r = oldestBuild ();
297- if (r == null ) throw new NoSuchElementException ();
298- return getNumberOf (r );
295+ return adapter .lastKey ();
299296 }
300297
301298 public R newestBuild () {
@@ -339,48 +336,27 @@ public boolean runExists(int number) {
339336 * If DESC, finds the closest #M that satisfies M ≤ N.
340337 */
341338 public @ CheckForNull R search (final int n , final Direction d ) {
342- switch (d ) {
343- case EXACT :
344- return getByNumber (n );
345- case ASC :
346- for (int m : core .descendingMap ().keySet ()) {
347- if (m < n ) {
348- continue ;
349- }
350- R r = getByNumber (m );
351- if (r != null ) {
352- return r ;
353- }
354- }
355- return null ;
356- case DESC :
357- Iterator <Integer > iterator = core .keySet ().iterator ();
358- while (iterator .hasNext ()) {
359- int m = iterator .next ();
360- if (m > n ) {
361- continue ;
362- }
363- R r = getByNumber (m );
364- if (r != null ) {
365- return r ;
366- }
367- }
368- return null ;
369- default :
370- throw new AssertionError ();
339+ if (d == EXACT ) {
340+ return this .adapter .get (n );
371341 }
342+ // prepare sub map, where we need to find first resolvable entry
343+ NavigableMap <Integer , BuildReference <R >> subCore = (d == ASC )
344+ ? core .headMap (n , true ).descendingMap ()
345+ : core .tailMap (n , true );
346+ // wrap with BuildReferenceMapAdapter to skip unresolvable entries
347+ return new BuildReferenceMapAdapter <>(subCore , buildResolver ).values ().stream ().findFirst ().orElse (null );
372348 }
373349
374350 public R getById (String id ) {
375351 return getByNumber (Integer .parseInt (id ));
376352 }
377353
378354 /**
379- * Ensure load referent object if needed, cache it and return
380- * Save that object is unloadable in case of failure to avoid next load attempts
355+ * Ensure loading referent object if needed, cache it and return
356+ * Save that object as ' unloadable' in case of failure to avoid next load attempts
381357 *
382358 * @param ref reference object to be resolved
383- * @return R referent build object, or null if can't be resolved
359+ * @return R referent build object, or null if it can't be resolved
384360 */
385361 private R resolveBuildRef (BuildReference <R > ref ) {
386362 if (ref == null || ref .isUnloadable ()) {
@@ -551,5 +527,22 @@ public enum Direction {
551527 ASC , DESC , EXACT
552528 }
553529
530+ private class BuildReferenceMapAdapterResolver implements BuildReferenceMapAdapter .Resolver <R > {
531+ @ Override
532+ public R resolveBuildRef (BuildReference <R > buildRef ) {
533+ return AbstractLazyLoadRunMap .this .resolveBuildRef (buildRef );
534+ }
535+
536+ @ Override
537+ public Integer getNumberOf (R build ) {
538+ return AbstractLazyLoadRunMap .this .getNumberOf (build );
539+ }
540+
541+ @ Override
542+ public Class <R > getBuildClass () {
543+ return AbstractLazyLoadRunMap .this .getBuildClass ();
544+ }
545+ }
546+
554547 static final Logger LOGGER = Logger .getLogger (AbstractLazyLoadRunMap .class .getName ());
555548}
0 commit comments