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 * <p>
036 * This class is Serializable from Commons Collections 3.1.
037 * </p>
038 *
039 * @since 3.0
040 */
041public class TransformedList<E> extends TransformedCollection<E> implements List<E> {
042
043    /** Serialization version */
044    private static final long serialVersionUID = 1077193035000013141L;
045
046    /**
047     * Factory method to create a transforming list.
048     * <p>
049     * If there are any elements already in the list being decorated, they
050     * are NOT transformed.
051     * Contrast this with {@link #transformedList(List, Transformer)}.
052     *
053     * @param <E> the type of the elements in the list
054     * @param list  the list to decorate, must not be null
055     * @param transformer  the transformer to use for conversion, must not be null
056     * @return a new transformed list
057     * @throws NullPointerException if list or transformer is null
058     * @since 4.0
059     */
060    public static <E> TransformedList<E> transformingList(final List<E> list,
061                                                          final Transformer<? super E, ? extends E> transformer) {
062        return new TransformedList<>(list, transformer);
063    }
064
065    /**
066     * Factory method to create a transforming list that will transform
067     * existing contents of the specified list.
068     * <p>
069     * If there are any elements already in the list being decorated, they
070     * will be transformed by this method.
071     * Contrast this with {@link #transformingList(List, Transformer)}.
072     *
073     * @param <E> the type of the elements in the list
074     * @param list  the list to decorate, must not be null
075     * @param transformer  the transformer to use for conversion, must not be null
076     * @return a new transformed List
077     * @throws NullPointerException if list or transformer is null
078     * @since 4.0
079     */
080    public static <E> TransformedList<E> transformedList(final List<E> list,
081                                                         final Transformer<? super E, ? extends E> transformer) {
082        final TransformedList<E> decorated = new TransformedList<>(list, transformer);
083        if (list.size() > 0) {
084            @SuppressWarnings("unchecked") // list is of type E
085            final E[] values = (E[]) list.toArray(); // NOPMD - false positive for generics
086            list.clear();
087            for (final E value : values) {
088                decorated.decorated().add(transformer.transform(value));
089            }
090        }
091        return decorated;
092    }
093
094    //-----------------------------------------------------------------------
095    /**
096     * Constructor that wraps (not copies).
097     * <p>
098     * If there are any elements already in the list being decorated, they
099     * are NOT transformed.
100     *
101     * @param list  the list to decorate, must not be null
102     * @param transformer  the transformer to use for conversion, must not be null
103     * @throws NullPointerException if list or transformer is null
104     */
105    protected TransformedList(final List<E> list, final Transformer<? super E, ? extends E> transformer) {
106        super(list, transformer);
107    }
108
109    /**
110     * Gets the decorated list.
111     *
112     * @return the decorated list
113     */
114    protected List<E> getList() {
115        return (List<E>) decorated();
116    }
117
118    @Override
119    public boolean equals(final Object object) {
120        return object == this || decorated().equals(object);
121    }
122
123    @Override
124    public int hashCode() {
125        return decorated().hashCode();
126    }
127
128    //-----------------------------------------------------------------------
129
130    @Override
131    public E get(final int index) {
132        return getList().get(index);
133    }
134
135    @Override
136    public int indexOf(final Object object) {
137        return getList().indexOf(object);
138    }
139
140    @Override
141    public int lastIndexOf(final Object object) {
142        return getList().lastIndexOf(object);
143    }
144
145    @Override
146    public E remove(final int index) {
147        return getList().remove(index);
148    }
149
150    //-----------------------------------------------------------------------
151
152    @Override
153    public void add(final int index, E object) {
154        object = transform(object);
155        getList().add(index, object);
156    }
157
158    @Override
159    public boolean addAll(final int index, Collection<? extends E> coll) {
160        coll = transform(coll);
161        return getList().addAll(index, coll);
162    }
163
164    @Override
165    public ListIterator<E> listIterator() {
166        return listIterator(0);
167    }
168
169    @Override
170    public ListIterator<E> listIterator(final int i) {
171        return new TransformedListIterator(getList().listIterator(i));
172    }
173
174    @Override
175    public E set(final int index, E object) {
176        object = transform(object);
177        return getList().set(index, object);
178    }
179
180    @Override
181    public List<E> subList(final int fromIndex, final int toIndex) {
182        final List<E> sub = getList().subList(fromIndex, toIndex);
183        return new TransformedList<>(sub, transformer);
184    }
185
186    /**
187     * Inner class Iterator for the TransformedList
188     */
189    protected class TransformedListIterator extends AbstractListIteratorDecorator<E> {
190
191        /**
192         * Create a new transformed list iterator.
193         *
194         * @param iterator  the list iterator to decorate
195         */
196        protected TransformedListIterator(final ListIterator<E> iterator) {
197            super(iterator);
198        }
199
200        @Override
201        public void add(E object) {
202            object = transform(object);
203            getListIterator().add(object);
204        }
205
206        @Override
207        public void set(E object) {
208            object = transform(object);
209            getListIterator().set(object);
210        }
211    }
212
213}