001package jmri.util;
002import java.util.ArrayList;
003import java.util.Collection;
004
005import javax.annotation.Nonnull;
006
007/**
008 * An ArrayList that SpotBugs understands will never contain null elements.
009 *
010 * @see java.util.ArrayList
011 * @see java.util.List
012 * @author Bob Jacobsen, Copyright (C) 2017
013 * @param <E> E
014 */
015public class NonNullArrayList<E> extends ArrayList<E> {
016
017    @Override
018    public boolean add(E e) {
019        if (e == null) throw new IllegalArgumentException("NonNullArrayList.addAll cannot add null item");
020        return super.add(e);
021    }
022
023    @Override
024    public void add(int i, E e) {
025        if (e == null) throw new IllegalArgumentException("NonNullArrayList.addAll cannot add null item");
026        super.add(i, e);
027    }
028
029    @Override
030    public boolean addAll(Collection<? extends E> c) { // ideally, would be "extends @Nonnull e", but that's not this annotation
031        for (E e : c ) {
032            if (e == null) throw new IllegalArgumentException("NonNullArrayList.addAll cannot accept collection containing null");
033        }
034        return super.addAll(c);
035    }
036
037    @Override
038    public boolean addAll(int i, Collection<? extends E> c) { // ideally, would be "extends @Nonnull e", but that's not this annotation
039        for (E e : c ) {
040            if (e == null) throw new IllegalArgumentException("NonNullArrayList.addAll cannot accept collection containing null");
041        }
042        return super.addAll(i, c);
043    }
044
045    @Override
046    @Nonnull
047    public E get(int i) { return super.get(i); }
048
049    @Override
050    @Nonnull
051    public E remove(int i) { return super.remove(i); }
052
053    @Override
054    @Nonnull
055    public E set(int i, E e) {
056        if (e == null) throw new IllegalArgumentException("NonNullArrayList.addAll cannot set item null");
057        return super.set(i, e);
058    }
059
060    // test routines for SpotBugs checking - protected so you don't see them
061    // These should be clean
062    protected NonNullArrayList<Integer> testAddAndReturn() {
063        NonNullArrayList<Integer> t = new NonNullArrayList<>();
064        t.add(100);
065        // t.add(null); // SpotBugs will tag this
066        return t;
067    }
068
069    protected boolean testLoop(String c) {
070        NonNullArrayList<Integer> t = new NonNullArrayList<>();
071        t.add(100);
072        for (Integer s : t) {
073            if (s.toString().equals(c)) return true; // SpotBugs should not require null check
074        }
075        return false;
076    }
077
078    protected boolean asArgumentCheck(NonNullArrayList<Integer> t) {
079        if (t.get(0).toString().equals("100")) return true; // dereference of element of unknown Collection
080        for (Integer s : t) {
081            if (s.toString().equals("123")) return true; // dereference of element of unknown Collection
082        }
083        return false;
084    }
085
086
087}