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