View Javadoc
1   /*
2    * Copyright (C) 2012 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.collect;
16  
17  import static com.google.common.base.Preconditions.checkElementIndex;
18  
19  import com.google.common.annotations.GwtCompatible;
20  import com.google.common.math.IntMath;
21  
22  import java.util.AbstractList;
23  import java.util.List;
24  import java.util.ListIterator;
25  import java.util.RandomAccess;
26  
27  import javax.annotation.Nullable;
28  
29  /**
30   * Implementation of {@link Lists#cartesianProduct(List)}.
31   * 
32   * @author Louis Wasserman
33   */
34  @GwtCompatible
35  final class CartesianList<E> extends AbstractList<List<E>> implements RandomAccess {
36  
37    private transient final ImmutableList<List<E>> axes;
38    private transient final int[] axesSizeProduct;
39    
40    static <E> List<List<E>> create(List<? extends List<? extends E>> lists) {
41      ImmutableList.Builder<List<E>> axesBuilder =
42          new ImmutableList.Builder<List<E>>(lists.size());
43      for (List<? extends E> list : lists) {
44        List<E> copy = ImmutableList.copyOf(list);
45        if (copy.isEmpty()) {
46          return ImmutableList.of();
47        }
48        axesBuilder.add(copy);
49      }
50      return new CartesianList<E>(axesBuilder.build());
51    }
52  
53    CartesianList(ImmutableList<List<E>> axes) {
54      this.axes = axes;
55      int[] axesSizeProduct = new int[axes.size() + 1];
56      axesSizeProduct[axes.size()] = 1;
57      try {
58        for (int i = axes.size() - 1; i >= 0; i--) {
59          axesSizeProduct[i] =
60              IntMath.checkedMultiply(axesSizeProduct[i + 1], axes.get(i).size());
61        }
62      } catch (ArithmeticException e) {
63        throw new IllegalArgumentException(
64            "Cartesian product too large; must have size at most Integer.MAX_VALUE");
65      }
66      this.axesSizeProduct = axesSizeProduct;
67    }
68  
69    private int getAxisIndexForProductIndex(int index, int axis) {
70      return (index / axesSizeProduct[axis + 1]) % axes.get(axis).size();
71    }
72  
73    @Override
74    public ImmutableList<E> get(final int index) {
75      checkElementIndex(index, size());
76      return new ImmutableList<E>() {
77  
78        @Override
79        public int size() {
80          return axes.size();
81        }
82  
83        @Override
84        public E get(int axis) {
85          checkElementIndex(axis, size());
86          int axisIndex = getAxisIndexForProductIndex(index, axis);
87          return axes.get(axis).get(axisIndex);
88        }
89  
90        @Override
91        boolean isPartialView() {
92          return true;
93        }
94      };
95    }
96  
97    @Override
98    public int size() {
99      return axesSizeProduct[0];
100   }
101 
102   @Override
103   public boolean contains(@Nullable Object o) {
104     if (!(o instanceof List)) {
105       return false;
106     }
107     List<?> list = (List<?>) o;
108     if (list.size() != axes.size()) {
109       return false;
110     }
111     ListIterator<?> itr = list.listIterator();
112     while (itr.hasNext()) {
113       int index = itr.nextIndex();
114       if (!axes.get(index).contains(itr.next())) {
115         return false;
116       }
117     }
118     return true;
119   }
120 }