package jenkins.model.lazy; import groovy.util.MapEntry; import hudson.util.AdaptedIterator; import hudson.util.Iterators; import javax.annotation.Nullable; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; /** * Take {@code SortedMap<Integer,BuildReference<R>>} and make it look like {@code SortedMap<Integer,R>}. * * When {@link BuildReference} lost the build object, we'll use {@link AbstractLazyLoadRunMap#getById(String)} * to obtain one. * * @author Kohsuke Kawaguchi */ class BuildReferenceMapAdapter<R> implements SortedMap<Integer,R> { private final AbstractLazyLoadRunMap<R> loader; private final SortedMap<Integer,BuildReference<R>> core; BuildReferenceMapAdapter(AbstractLazyLoadRunMap<R> loader, SortedMap<Integer, BuildReference<R>> core) { this.loader = loader; this.core = core; } private R unwrap(@Nullable BuildReference<R> ref) { if (ref==null) return null; R v = ref.get(); if (v==null) v = loader.getById(ref.id); return v; } private BuildReference<R> wrap(@Nullable R value) { if (value==null) return null; return loader.createReference(value); } public Comparator<? super Integer> comparator() { return core.comparator(); } public SortedMap<Integer, R> subMap(Integer fromKey, Integer toKey) { return new BuildReferenceMapAdapter<R>(loader,core.subMap(fromKey, toKey)); } public SortedMap<Integer, R> headMap(Integer toKey) { return new BuildReferenceMapAdapter<R>(loader,core.headMap(toKey)); } public SortedMap<Integer, R> tailMap(Integer fromKey) { return new BuildReferenceMapAdapter<R>(loader,core.tailMap(fromKey)); } public Integer firstKey() { return core.firstKey(); } public Integer lastKey() { return core.lastKey(); } public Set<Integer> keySet() { return core.keySet(); } public Collection<R> values() { return new CollectionAdapter(core.values()); } public Set<Entry<Integer,R>> entrySet() { return new SetAdapter(core.entrySet()); } public int size() { return core.size(); } public boolean isEmpty() { return core.isEmpty(); } public boolean containsKey(Object key) { return core.containsKey(key); } public boolean containsValue(Object value) { return core.containsValue(value); // TODO should this be core.containsValue(wrap(value))? } public R get(Object key) { return unwrap(core.get(key)); } public R put(Integer key, R value) { return unwrap(core.put(key, wrap(value))); } public R remove(Object key) { return unwrap(core.remove(key)); } public void putAll(Map<? extends Integer, ? extends R> m) { for (Entry<? extends Integer, ? extends R> e : m.entrySet()) put(e.getKey(), e.getValue()); } public void clear() { core.clear(); } @Override public boolean equals(Object o) { return core.equals(o); // TODO this is wrong } @Override public int hashCode() { return core.hashCode(); } @Override public String toString() { return new LinkedHashMap<Integer,R>(this).toString(); } private class CollectionAdapter implements Collection<R> { private final Collection<BuildReference<R>> core; private CollectionAdapter(Collection<BuildReference<R>> core) { this.core = core; } public int size() { return core.size(); } public boolean isEmpty() { return core.isEmpty(); } public boolean contains(Object o) { // TODO: to properly pass this onto core, we need to wrap o into BuildReference but also needs to figure out ID. throw new UnsupportedOperationException(); } public Iterator<R> iterator() { // silently drop null, as if we didn't have them in this collection in the first place // this shouldn't be indistinguishable from concurrent modifications to the collection return Iterators.removeNull(new AdaptedIterator<BuildReference<R>,R>(core.iterator()) { protected R adapt(BuildReference<R> ref) { return unwrap(ref); } }); } public Object[] toArray() { List<Object> list = new ArrayList<Object>(); for (R r : this) list.add(r); return list.toArray(); } public <T> T[] toArray(T[] a) { int size = size(); T[] r = a; if (r.length>size) r = (T[]) Array.newInstance(a.getClass().getComponentType(), size); Iterator<R> itr = iterator(); int i=0; while (itr.hasNext()) { r[i++] = (T)itr.next(); } return r; } public boolean add(R value) { return core.add(wrap(value)); } public boolean remove(Object o) { // return core.remove(o); // TODO: to properly pass this onto core, we need to wrap o into BuildReference but also needs to figure out ID. throw new UnsupportedOperationException(); } public boolean containsAll(Collection<?> c) { for (Object o : c) { if (!contains(o)) return false; } return true; } public boolean addAll(Collection<? extends R> c) { boolean b=false; for (R r : c) { b |= add(r); } return b; } public boolean removeAll(Collection<?> c) { boolean b=false; for (Object o : c) { b|=remove(o); } return b; } public boolean retainAll(Collection<?> c) { // TODO: to properly pass this onto core, we need to wrap o into BuildReference but also needs to figure out ID. throw new UnsupportedOperationException(); } public void clear() { core.clear(); } @Override public boolean equals(Object o) { return core.equals(o); } @Override public int hashCode() { return core.hashCode(); } } private class SetAdapter implements Set<Entry<Integer, R>> { private final Set<Entry<Integer, BuildReference<R>>> core; private SetAdapter(Set<Entry<Integer, BuildReference<R>>> core) { this.core = core; } public int size() { return core.size(); } public boolean isEmpty() { return core.isEmpty(); } public boolean contains(Object o) { // TODO: to properly pass this onto core, we need to wrap o into BuildReference but also needs to figure out ID. throw new UnsupportedOperationException(); } public Iterator<Entry<Integer, R>> iterator() { return Iterators.removeNull(new AdaptedIterator<Entry<Integer,BuildReference<R>>,Entry<Integer, R>>(core.iterator()) { protected Entry<Integer, R> adapt(Entry<Integer, BuildReference<R>> e) { return _unwrap(e); } }); } public Object[] toArray() { List<Object> list = new ArrayList<Object>(); for (Entry<Integer, R> r : this) list.add(r); return list.toArray(); } public <T> T[] toArray(T[] a) { int size = size(); T[] r = a; if (r.length>size) r = (T[]) Array.newInstance(a.getClass().getComponentType(), size); Iterator<Entry<Integer, R>> itr = iterator(); int i=0; while (itr.hasNext()) { r[i++] = (T)itr.next(); } return r; } public boolean add(Entry<Integer, R> value) { return core.add(_wrap(value)); } public boolean remove(Object o) { // return core.remove(o); // TODO: to properly pass this onto core, we need to wrap o into BuildReference but also needs to figure out ID. throw new UnsupportedOperationException(); } public boolean containsAll(Collection<?> c) { for (Object o : c) { if (!contains(o)) return false; } return true; } public boolean addAll(Collection<? extends Entry<Integer,R>> c) { boolean b=false; for (Entry<Integer,R> r : c) { b |= add(r); } return b; } public boolean removeAll(Collection<?> c) { boolean b=false; for (Object o : c) { b|=remove(o); } return b; } public boolean retainAll(Collection<?> c) { // TODO: to properly pass this onto core, we need to wrap o into BuildReference but also needs to figure out ID. throw new UnsupportedOperationException(); } public void clear() { core.clear(); } @Override public boolean equals(Object o) { return core.equals(o); } @Override public int hashCode() { return core.hashCode(); } private Entry<Integer,BuildReference<R>> _wrap(Entry<Integer,R> e) { return new MapEntry(e.getKey(),wrap(e.getValue())); } private Entry<Integer, R> _unwrap(Entry<Integer, BuildReference<R>> e) { R v = unwrap(e.getValue()); if (v==null) return null; return new MapEntry(e.getKey(), v); } } }