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