001/**
002 * Copyright 2005-2018 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.util;
017
018import java.io.Serializable;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024import java.util.ListIterator;
025
026import org.kuali.rice.krad.datadictionary.Copyable;
027import org.kuali.rice.krad.uif.component.DelayedCopy;
028
029/**
030 * List implementation for internal use by a lifecycle element.
031 *
032 * <p>Mutability of the list will follow the semantics for the lifecycle element.</p>
033 *
034 * @author Kuali Rice Team (rice.collab@kuali.org)
035 * @param <T> list item type
036 */
037public class LifecycleAwareList<T> implements List<T>, Copyable, Serializable {
038
039    private static final long serialVersionUID = -8971217230511446882L;
040
041    /**
042     * Delegating list iterator proxy.
043     *
044     * @author Kuali Rice Team (rice.collab@kuali.org)
045     */
046    private class ListIter implements ListIterator<T> {
047
048        private final ListIterator<T> delegate;
049
050        /**
051         * @see LifecycleAwareList#listIterator()
052         */
053        private ListIter() {
054            this.delegate = LifecycleAwareList.this.delegate.listIterator();
055        }
056
057        /**
058         * @see LifecycleAwareList#listIterator(int)
059         */
060        private ListIter(int index) {
061            this.delegate = LifecycleAwareList.this.delegate.listIterator(index);
062        }
063
064        @Override
065        public boolean hasNext() {
066            return this.delegate.hasNext();
067        }
068
069        @Override
070        public T next() {
071            return this.delegate.next();
072        }
073
074        @Override
075        public boolean hasPrevious() {
076            return this.delegate.hasPrevious();
077        }
078
079        @Override
080        public T previous() {
081            return this.delegate.previous();
082        }
083
084        @Override
085        public int nextIndex() {
086            return this.delegate.nextIndex();
087        }
088
089        @Override
090        public int previousIndex() {
091            return this.delegate.previousIndex();
092        }
093
094        @Override
095        public void remove() {
096            lifecycleElement.checkMutable(true);
097            this.delegate.remove();
098        }
099
100        @Override
101        public void set(T e) {
102            lifecycleElement.checkMutable(true);
103            this.delegate.set(e);
104        }
105
106        @Override
107        public void add(T e) {
108            lifecycleElement.checkMutable(true);
109            this.delegate.add(e);
110        }
111
112    }
113
114    /**
115     * Delegating iterator proxy.
116     *
117     * @author Kuali Rice Team (rice.collab@kuali.org)
118     */
119    private class Iter implements Iterator<T> {
120
121        private final Iterator<T> delegate;
122
123        /**
124         * @see LifecycleAwareList#iterator()
125         */
126        private Iter() {
127            this.delegate = LifecycleAwareList.this.delegate.iterator();
128        }
129
130        @Override
131        public boolean hasNext() {
132            return this.delegate.hasNext();
133        }
134
135        @Override
136        public T next() {
137            return this.delegate.next();
138        }
139
140        @Override
141        public void remove() {
142            lifecycleElement.checkMutable(true);
143            this.delegate.remove();
144        }
145    }
146
147    /**
148     * The component this list is related to.
149     */
150    private final LifecycleElement lifecycleElement;
151
152    /**
153     * Delegating list implementation.
154     */
155    @DelayedCopy(inherit = true)
156    private List<T> delegate;
157
158    /**
159     * Create a new list instance.
160     *
161     * @param lifecycleElement The lifecycle element to use for mutability checks.
162     */
163    public LifecycleAwareList(LifecycleElement lifecycleElement) {
164        this.lifecycleElement = lifecycleElement;
165        this.delegate = Collections.emptyList();
166    }
167
168    /**
169     * Create a new list instance, based on another list.
170     *
171     * @param lifecycleElement The lifecycle element to use for mutability checks.
172     * @param delegate The list to wrap.
173     */
174    public LifecycleAwareList(LifecycleElement lifecycleElement, List<T> delegate) {
175        this.lifecycleElement = lifecycleElement;
176        
177        List<T> wrapped = delegate;
178        while (wrapped instanceof LifecycleAwareList) {
179            wrapped = ((LifecycleAwareList<T>) wrapped).delegate;
180        }
181        
182        this.delegate = delegate;
183    }
184
185    /**
186     * Ensure that the delegate list can be modified.
187     */
188    private void ensureMutable() {
189        lifecycleElement.checkMutable(true);
190
191        if (delegate == Collections.EMPTY_LIST) {
192            delegate = new ArrayList<T>();
193        }
194    }
195
196    @Override
197    public int size() {
198        return this.delegate.size();
199    }
200
201    @Override
202    public boolean isEmpty() {
203        return this.delegate.isEmpty();
204    }
205
206    @Override
207    public boolean contains(Object o) {
208        return this.delegate.contains(o);
209    }
210
211    @Override
212    public Iterator<T> iterator() {
213        return new Iter();
214    }
215
216    @Override
217    public Object[] toArray() {
218        return this.delegate.toArray();
219    }
220
221    @Override
222    public <A> A[] toArray(A[] a) {
223        return this.delegate.toArray(a);
224    }
225
226    @Override
227    public boolean add(T e) {
228        ensureMutable();
229        return this.delegate.add(e);
230    }
231
232    @Override
233    public boolean remove(Object o) {
234        lifecycleElement.checkMutable(true);
235        return delegate != Collections.EMPTY_LIST && delegate.remove(o);
236    }
237
238    @Override
239    public boolean containsAll(Collection<?> c) {
240        return this.delegate.containsAll(c);
241    }
242
243    @Override
244    public boolean addAll(Collection<? extends T> c) {
245        ensureMutable();
246        return this.delegate.addAll(c);
247    }
248
249    @Override
250    public boolean addAll(int index, Collection<? extends T> c) {
251        ensureMutable();
252        return this.delegate.addAll(index, c);
253    }
254
255    @Override
256    public boolean removeAll(Collection<?> c) {
257        lifecycleElement.checkMutable(true);
258        return delegate != Collections.EMPTY_LIST && this.delegate.removeAll(c);
259    }
260
261    @Override
262    public boolean retainAll(Collection<?> c) {
263        lifecycleElement.checkMutable(true);
264        return delegate != Collections.EMPTY_LIST && this.delegate.retainAll(c);
265    }
266
267    @Override
268    public void clear() {
269        if (this.delegate != Collections.EMPTY_LIST) {
270            this.delegate.clear();
271        }
272    }
273
274    @Override
275    public boolean equals(Object o) {
276        return this.delegate.equals(o);
277    }
278
279    @Override
280    public int hashCode() {
281        return this.delegate.hashCode();
282    }
283
284    @Override
285    public T get(int index) {
286        return this.delegate.get(index);
287    }
288
289    @Override
290    public T set(int index, T element) {
291        lifecycleElement.checkMutable(true);
292        return this.delegate.set(index, element);
293    }
294
295    @Override
296    public void add(int index, T element) {
297        ensureMutable();
298        this.delegate.add(index, element);
299    }
300
301    @Override
302    public T remove(int index) {
303        lifecycleElement.checkMutable(true);
304        return this.delegate.remove(index);
305    }
306
307    @Override
308    public int indexOf(Object o) {
309        return this.delegate.indexOf(o);
310    }
311
312    @Override
313    public int lastIndexOf(Object o) {
314        return this.delegate.lastIndexOf(o);
315    }
316
317    @Override
318    public ListIterator<T> listIterator() {
319        ensureMutable();
320        return new ListIter();
321    }
322
323    @Override
324    public ListIterator<T> listIterator(int index) {
325        ensureMutable();
326        return new ListIter(index);
327    }
328
329    @Override
330    public List<T> subList(int fromIndex, int toIndex) {
331        return new LifecycleAwareList<T>(lifecycleElement, this.delegate.subList(fromIndex, toIndex));
332    }
333
334    /**
335     * @see java.lang.Object#clone()
336     */
337    @Override
338    public Object clone() throws CloneNotSupportedException {
339        return super.clone();
340    }
341
342}