/* * Copyright 2013 Guidewire Software, Inc. */ package gw.xml.simple; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Collection; import java.util.ListIterator; class XmlChildList<T extends SimpleXmlNode> implements List<T> { private List<T> _delegate = new ArrayList<T>(); private SimpleXmlNode _owner; public XmlChildList(SimpleXmlNode owner) { _owner = owner; } @Override public int size() { return _delegate.size(); } @Override public boolean isEmpty() { return _delegate.isEmpty(); } @Override public boolean contains(Object o) { return _delegate.contains(o); } @Override public Iterator<T> iterator() { return new WrappingIterator(_delegate.iterator()); } @Override public Object[] toArray() { return _delegate.toArray(); } @Override public <T> T[] toArray(T[] a) { return _delegate.toArray(a); } @Override public boolean add(T t) { validateArgumentForAdd(t, "XmlChildList.add()"); t.setParent(_owner); return _delegate.add(t); } @Override public boolean remove(Object o) { boolean result = _delegate.remove(o); if (result) { ((SimpleXmlNode) o).setParent(null); } return result; } @Override public boolean containsAll(Collection<?> c) { return _delegate.containsAll(c); } @Override public boolean addAll(Collection<? extends T> c) { validateArgumentsForAdd(c, "addAll(Collection)"); setParents(c); return _delegate.addAll(c); } @Override public boolean addAll(int index, Collection<? extends T> c) { validateArgumentsForAdd(c, "addAll(int, Collection)"); setParents(c); return _delegate.addAll(index, c); } @Override public boolean removeAll(Collection<?> c) { boolean modified = false; for (int i = 0; i < _delegate.size(); i++) { if (c.contains(_delegate.get(i))) { remove(i); i--; modified = true; } } return modified; } @Override public boolean retainAll(Collection<?> c) { boolean modified = false; for (int i = 0; i < _delegate.size(); i++) { if (!c.contains(_delegate.get(i))) { remove(i); i--; modified = true; } } return modified; } @Override public void clear() { for (SimpleXmlNode node : _delegate) { node.setParent(null); } _delegate.clear(); } @Override public T get(int index) { return _delegate.get(index); } @Override public T set(int index, T element) { // We allow set() to be called with an element already in the list because otherwise sorting the list // of children would result in exceptions. validateArgumentForSet(element, "XmlChildList.set(int, T)"); if (element.getParent() == null) { element.setParent(_owner); } T original = _delegate.set(index, element); // This isn't efficient, and if that's an issue in practice it could be replaced with // reference counting within SimpleXmlNode instead. Since set(int, T) calls can result // in an element being added to the list multiple times, we can't just assume that // the call will result in the replaced element being removed from the list entirely, // so we have to check if (!contains(original)) { original.setParent(null); } return original; } @Override public void add(int index, T element) { validateArgumentForAdd(element, "XmlChildList.add(int, T)"); element.setParent(_owner); _delegate.add(index, element); } @Override public T remove(int index) { T result = _delegate.remove(index); result.setParent(null); return result; } @Override public int indexOf(Object o) { return _delegate.indexOf(o); } @Override public int lastIndexOf(Object o) { return _delegate.lastIndexOf(o); } @Override public ListIterator<T> listIterator() { return new WrappingListIterator(_delegate.listIterator()); } @Override public ListIterator<T> listIterator(int index) { return new WrappingListIterator(_delegate.listIterator(index)); } @Override public List<T> subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException("XmlChildList.subList(int, int) is not currently supported"); } // ---------------------------- Private helper methods private void setParents(Collection<? extends T> c) { for (T arg : c) { arg.setParent(_owner); } } private void validateArgumentsForAdd(Collection<? extends T> c, String functionName) { for (T arg : c) { validateArgumentForAdd(arg, functionName); } } private void validateArgumentForAdd(T arg, String functionName) { if (arg == null) { throw new IllegalArgumentException(functionName + " cannot be called with a null argument"); } else if (arg.getParent() != null) { throw new IllegalArgumentException(functionName + " cannot be called with an argument that already has a non-null parent"); } } private void validateArgumentForSet(T element, String functionName) { if (element == null) { throw new IllegalArgumentException(functionName + " cannot be called with a null argument"); } else if (element.getParent() != null && element.getParent() != _owner) { throw new IllegalArgumentException(functionName + " cannot be called with an element that has a non-null parent unless that parent is already set to the owner of this list"); } } private class WrappingIterator implements Iterator<T> { private Iterator<T> _wrapped; private T _lastRet; private WrappingIterator(Iterator<T> wrapped) { _wrapped = wrapped; } @Override public boolean hasNext() { return _wrapped.hasNext(); } @Override public T next() { _lastRet = _wrapped.next(); return _lastRet; } @Override public void remove() { _wrapped.remove(); _lastRet.setParent(null); } } private class WrappingListIterator implements ListIterator<T> { private ListIterator<T> _wrapped; private T _lastRet; private WrappingListIterator(ListIterator<T> wrapped) { _wrapped = wrapped; } @Override public boolean hasNext() { return _wrapped.hasNext(); } @Override public T next() { _lastRet = _wrapped.next(); return _lastRet; } @Override public boolean hasPrevious() { return _wrapped.hasPrevious(); } @Override public T previous() { _lastRet = _wrapped.previous(); return _lastRet; } @Override public int nextIndex() { return _wrapped.nextIndex(); } @Override public int previousIndex() { return _wrapped.previousIndex(); } @Override public void remove() { _wrapped.remove(); _lastRet.setParent(null); } @Override public void set(T t) { validateArgumentForSet(t, "WrappingListIterator.set(T)"); _wrapped.set(t); if (t.getParent() == null) { t.setParent(_owner); } if (!contains(_lastRet)) { _lastRet.setParent(null); } } @Override public void add(T t) { validateArgumentForAdd(t, "WrappingListIterator.add(T)"); _wrapped.add(t); t.setParent(_owner); } } }