View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.list;
18  
19  import java.util.Collection;
20  import java.util.List;
21  import java.util.ListIterator;
22  
23  import org.apache.commons.collections4.Transformer;
24  import org.apache.commons.collections4.collection.TransformedCollection;
25  import org.apache.commons.collections4.iterators.AbstractListIteratorDecorator;
26  
27  /**
28   * Decorates another {@code List} to transform objects that are added.
29   * <p>
30   * The add and set methods are affected by this class.
31   * Thus objects must be removed or searched for using their transformed form.
32   * For example, if the transformation converts Strings to Integers, you must
33   * use the Integer form to remove objects.
34   * </p>
35   * <p>
36   * This class is Serializable from Commons Collections 3.1.
37   * </p>
38   *
39   * @since 3.0
40   */
41  public class TransformedList<E> extends TransformedCollection<E> implements List<E> {
42  
43      /**
44       * Inner class Iterator for the TransformedList
45       */
46      protected class TransformedListIterator extends AbstractListIteratorDecorator<E> {
47  
48          /**
49           * Create a new transformed list iterator.
50           *
51           * @param iterator  the list iterator to decorate
52           */
53          protected TransformedListIterator(final ListIterator<E> iterator) {
54              super(iterator);
55          }
56  
57          @Override
58          public void add(E object) {
59              object = transform(object);
60              getListIterator().add(object);
61          }
62  
63          @Override
64          public void set(E object) {
65              object = transform(object);
66              getListIterator().set(object);
67          }
68      }
69  
70      /** Serialization version */
71      private static final long serialVersionUID = 1077193035000013141L;
72  
73      /**
74       * Factory method to create a transforming list that will transform
75       * existing contents of the specified list.
76       * <p>
77       * If there are any elements already in the list being decorated, they
78       * will be transformed by this method.
79       * Contrast this with {@link #transformingList(List, Transformer)}.
80       *
81       * @param <E> the type of the elements in the list
82       * @param list  the list to decorate, must not be null
83       * @param transformer  the transformer to use for conversion, must not be null
84       * @return a new transformed List
85       * @throws NullPointerException if list or transformer is null
86       * @since 4.0
87       */
88      public static <E> TransformedList<E> transformedList(final List<E> list,
89                                                           final Transformer<? super E, ? extends E> transformer) {
90          final TransformedList<E> decorated = new TransformedList<>(list, transformer);
91          if (!list.isEmpty()) {
92              @SuppressWarnings("unchecked") // list is of type E
93              final E[] values = (E[]) list.toArray(); // NOPMD - false positive for generics
94              list.clear();
95              for (final E value : values) {
96                  decorated.decorated().add(transformer.transform(value));
97              }
98          }
99          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 }