001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.list;
018
019import java.util.Collection;
020import java.util.List;
021import java.util.ListIterator;
022
023import org.apache.commons.collections4.Transformer;
024import org.apache.commons.collections4.collection.TransformedCollection;
025import org.apache.commons.collections4.iterators.AbstractListIteratorDecorator;
026
027/**
028 * Decorates another <code>List</code> to transform objects that are added.
029 * <p>
030 * The add and set methods are affected by this class.
031 * Thus objects must be removed or searched for using their transformed form.
032 * For example, if the transformation converts Strings to Integers, you must
033 * use the Integer form to remove objects.
034 * <p>
035 * This class is Serializable from Commons Collections 3.1.
036 *
037 * @since 3.0
038 */
039public class TransformedList<E> extends TransformedCollection<E> implements List<E> {
040
041    /** Serialization version */
042    private static final long serialVersionUID = 1077193035000013141L;
043
044    /**
045     * Factory method to create a transforming list.
046     * <p>
047     * If there are any elements already in the list being decorated, they
048     * are NOT transformed.
049     * Contrast this with {@link #transformedList(List, Transformer)}.
050     *
051     * @param <E> the type of the elements in the list
052     * @param list  the list to decorate, must not be null
053     * @param transformer  the transformer to use for conversion, must not be null
054     * @return a new transformed list
055     * @throws NullPointerException if list or transformer is null
056     * @since 4.0
057     */
058    public static <E> TransformedList<E> transformingList(final List<E> list,
059                                                          final Transformer<? super E, ? extends E> transformer) {
060        return new TransformedList<>(list, transformer);
061    }
062
063    /**
064     * Factory method to create a transforming list that will transform
065     * existing contents of the specified list.
066     * <p>
067     * If there are any elements already in the list being decorated, they
068     * will be transformed by this method.
069     * Contrast this with {@link #transformingList(List, Transformer)}.
070     *
071     * @param <E> the type of the elements in the list
072     * @param list  the list to decorate, must not be null
073     * @param transformer  the transformer to use for conversion, must not be null
074     * @return a new transformed List
075     * @throws NullPointerException if list or transformer is null
076     * @since 4.0
077     */
078    public static <E> TransformedList<E> transformedList(final List<E> list,
079                                                         final Transformer<? super E, ? extends E> transformer) {
080        final TransformedList<E> decorated = new TransformedList<>(list, transformer);
081        if (list.size() > 0) {
082            @SuppressWarnings("unchecked") // list is of type E
083            final E[] values = (E[]) list.toArray(); // NOPMD - false positive for generics
084            list.clear();
085            for (final E value : values) {
086                decorated.decorated().add(transformer.transform(value));
087            }
088        }
089        return decorated;
090    }
091
092    //-----------------------------------------------------------------------
093    /**
094     * Constructor that wraps (not copies).
095     * <p>
096     * If there are any elements already in the list being decorated, they
097     * are NOT transformed.
098     *
099     * @param list  the list to decorate, must not be null
100     * @param transformer  the transformer to use for conversion, must not be null
101     * @throws NullPointerException if list or transformer is null
102     */
103    protected TransformedList(final List<E> list, final Transformer<? super E, ? extends E> transformer) {
104        super(list, transformer);
105    }
106
107    /**
108     * Gets the decorated list.
109     *
110     * @return the decorated list
111     */
112    protected List<E> getList() {
113        return (List<E>) decorated();
114    }
115
116    @Override
117    public boolean equals(final Object object) {
118        return object == this || decorated().equals(object);
119    }
120
121    @Override
122    public int hashCode() {
123        return decorated().hashCode();
124    }
125
126    //-----------------------------------------------------------------------
127
128    @Override
129    public E get(final int index) {
130        return getList().get(index);
131    }
132
133    @Override
134    public int indexOf(final Object object) {
135        return getList().indexOf(object);
136    }
137
138    @Override
139    public int lastIndexOf(final Object object) {
140        return getList().lastIndexOf(object);
141    }
142
143    @Override
144    public E remove(final int index) {
145        return getList().remove(index);
146    }
147
148    //-----------------------------------------------------------------------
149
150    @Override
151    public void add(final int index, E object) {
152        object = transform(object);
153        getList().add(index, object);
154    }
155
156    @Override
157    public boolean addAll(final int index, Collection<? extends E> coll) {
158        coll = transform(coll);
159        return getList().addAll(index, coll);
160    }
161
162    @Override
163    public ListIterator<E> listIterator() {
164        return listIterator(0);
165    }
166
167    @Override
168    public ListIterator<E> listIterator(final int i) {
169        return new TransformedListIterator(getList().listIterator(i));
170    }
171
172    @Override
173    public E set(final int index, E object) {
174        object = transform(object);
175        return getList().set(index, object);
176    }
177
178    @Override
179    public List<E> subList(final int fromIndex, final int toIndex) {
180        final List<E> sub = getList().subList(fromIndex, toIndex);
181        return new TransformedList<>(sub, transformer);
182    }
183
184    /**
185     * Inner class Iterator for the TransformedList
186     */
187    protected class TransformedListIterator extends AbstractListIteratorDecorator<E> {
188
189        /**
190         * Create a new transformed list iterator.
191         *
192         * @param iterator  the list iterator to decorate
193         */
194        protected TransformedListIterator(final ListIterator<E> iterator) {
195            super(iterator);
196        }
197
198        @Override
199        public void add(E object) {
200            object = transform(object);
201            getListIterator().add(object);
202        }
203
204        @Override
205        public void set(E object) {
206            object = transform(object);
207            getListIterator().set(object);
208        }
209    }
210
211}