    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 * @param <E> the type of the elements in the list.
    040 * @since 3.0
    041 */
    042public class TransformedList<E> extends TransformedCollection<E> implements List<E> {
    043
    044    /**
    045     * Inner class Iterator for the TransformedList
    046     */
    047    protected class TransformedListIterator extends AbstractListIteratorDecorator<E> {
    048
    049        /**
    050         * Create a new transformed list iterator.
    051         *
    052         * @param iterator  the list iterator to decorate
    053         */
    054        protected TransformedListIterator(final ListIterator<E> iterator) {
    055            super(iterator);
    056        }
    057
    058        @Override
    059        public void add(E object) {
    060            object = transform(object);
    061            getListIterator().add(object);
    062        }
    063
    064        @Override
    065        public void set(final E object) {
    066            getListIterator().set(transform(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.apply(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}