1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dx.util;
18 
19 import java.util.Arrays;
20 
21 /**
22  * Simple (mostly) fixed-size list of objects, which may be made immutable.
23  */
24 public class FixedSizeList
25         extends MutabilityControl implements ToHuman {
26     /** {@code non-null;} array of elements */
27     private Object[] arr;
28 
29     /**
30      * Constructs an instance. All indices initially contain {@code null}.
31      *
32      * @param size the size of the list
33      */
FixedSizeList(int size)34     public FixedSizeList(int size) {
35         super(size != 0);
36 
37         try {
38             arr = new Object[size];
39         } catch (NegativeArraySizeException ex) {
40             // Translate the exception.
41             throw new IllegalArgumentException("size < 0");
42         }
43     }
44 
45     /** {@inheritDoc} */
46     @Override
equals(Object other)47     public boolean equals(Object other) {
48         if (this == other) {
49             // Easy out.
50             return true;
51         }
52 
53         if ((other == null) || (getClass() != other.getClass())) {
54             // Another easy out.
55             return false;
56         }
57 
58         FixedSizeList list = (FixedSizeList) other;
59         return Arrays.equals(arr, list.arr);
60     }
61 
62     /** {@inheritDoc} */
63     @Override
hashCode()64     public int hashCode() {
65         return Arrays.hashCode(arr);
66     }
67 
68     /** {@inheritDoc} */
69     @Override
toString()70     public String toString() {
71         String name = getClass().getName();
72 
73         return toString0(name.substring(name.lastIndexOf('.') + 1) + '{',
74                          ", ",
75                          "}",
76                          false);
77     }
78 
79     /**
80      * {@inheritDoc}
81      *
82      * This method will only work if every element of the list
83      * implements {@link ToHuman}.
84      */
toHuman()85     public String toHuman() {
86         String name = getClass().getName();
87 
88         return toString0(name.substring(name.lastIndexOf('.') + 1) + '{',
89                          ", ",
90                          "}",
91                          true);
92     }
93 
94     /**
95      * Gets a customized string form for this instance.
96      *
97      * @param prefix {@code null-ok;} prefix for the start of the result
98      * @param separator {@code null-ok;} separator to insert between each item
99      * @param suffix {@code null-ok;} suffix for the end of the result
100      * @return {@code non-null;} the custom string
101      */
toString(String prefix, String separator, String suffix)102     public String toString(String prefix, String separator, String suffix) {
103         return toString0(prefix, separator, suffix, false);
104     }
105 
106     /**
107      * Gets a customized human string for this instance. This method will
108      * only work if every element of the list implements {@link
109      * ToHuman}.
110      *
111      * @param prefix {@code null-ok;} prefix for the start of the result
112      * @param separator {@code null-ok;} separator to insert between each item
113      * @param suffix {@code null-ok;} suffix for the end of the result
114      * @return {@code non-null;} the custom string
115      */
toHuman(String prefix, String separator, String suffix)116     public String toHuman(String prefix, String separator, String suffix) {
117         return toString0(prefix, separator, suffix, true);
118     }
119 
120     /**
121      * Gets the number of elements in this list.
122      */
size()123     public final int size() {
124         return arr.length;
125     }
126 
127     /**
128      * Shrinks this instance to fit, by removing any unset
129      * ({@code null}) elements, leaving the remaining elements in
130      * their original order.
131      */
shrinkToFit()132     public void shrinkToFit() {
133         int sz = arr.length;
134         int newSz = 0;
135 
136         for (int i = 0; i < sz; i++) {
137             if (arr[i] != null) {
138                 newSz++;
139             }
140         }
141 
142         if (sz == newSz) {
143             return;
144         }
145 
146         throwIfImmutable();
147 
148         Object[] newa = new Object[newSz];
149         int at = 0;
150 
151         for (int i = 0; i < sz; i++) {
152             Object one = arr[i];
153             if (one != null) {
154                 newa[at] = one;
155                 at++;
156             }
157         }
158 
159         arr = newa;
160         if (newSz == 0) {
161             setImmutable();
162         }
163     }
164 
165     /**
166      * Gets the indicated element. It is an error to call this with the
167      * index for an element which was never set; if you do that, this
168      * will throw {@code NullPointerException}. This method is
169      * protected so that subclasses may offer a safe type-checked
170      * public interface to their clients.
171      *
172      * @param n {@code >= 0, < size();} which element
173      * @return {@code non-null;} the indicated element
174      */
get0(int n)175     protected final Object get0(int n) {
176         try {
177             Object result = arr[n];
178 
179             if (result == null) {
180                 throw new NullPointerException("unset: " + n);
181             }
182 
183             return result;
184         } catch (ArrayIndexOutOfBoundsException ex) {
185             // Translate the exception.
186             return throwIndex(n);
187         }
188     }
189 
190     /**
191      * Gets the indicated element, allowing {@code null}s to be
192      * returned. This method is protected so that subclasses may
193      * (optionally) offer a safe type-checked public interface to
194      * their clients.
195      *
196      * @param n {@code >= 0, < size();} which element
197      * @return {@code null-ok;} the indicated element
198      */
getOrNull0(int n)199     protected final Object getOrNull0(int n) {
200         return arr[n];
201     }
202 
203     /**
204      * Sets the element at the given index, but without doing any type
205      * checks on the element. This method is protected so that
206      * subclasses may offer a safe type-checked public interface to
207      * their clients.
208      *
209      * @param n {@code >= 0, < size();} which element
210      * @param obj {@code null-ok;} the value to store
211      */
set0(int n, Object obj)212     protected final void set0(int n, Object obj) {
213         throwIfImmutable();
214 
215         try {
216             arr[n] = obj;
217         } catch (ArrayIndexOutOfBoundsException ex) {
218             // Translate the exception.
219             throwIndex(n);
220         }
221     }
222 
223     /**
224      * Throws the appropriate exception for the given index value.
225      *
226      * @param n the index value
227      * @return never
228      * @throws IndexOutOfBoundsException always thrown
229      */
throwIndex(int n)230     private Object throwIndex(int n) {
231         if (n < 0) {
232             throw new IndexOutOfBoundsException("n < 0");
233         }
234 
235         throw new IndexOutOfBoundsException("n >= size()");
236     }
237 
238     /**
239      * Helper for {@link #toString} and {@link #toHuman}, which both of
240      * those call to pretty much do everything.
241      *
242      * @param prefix {@code null-ok;} prefix for the start of the result
243      * @param separator {@code null-ok;} separator to insert between each item
244      * @param suffix {@code null-ok;} suffix for the end of the result
245      * @param human whether the output is to be human
246      * @return {@code non-null;} the custom string
247      */
toString0(String prefix, String separator, String suffix, boolean human)248     private String toString0(String prefix, String separator, String suffix,
249                              boolean human) {
250         int len = arr.length;
251         StringBuffer sb = new StringBuffer(len * 10 + 10);
252 
253         if (prefix != null) {
254             sb.append(prefix);
255         }
256 
257         for (int i = 0; i < len; i++) {
258             if ((i != 0) && (separator != null)) {
259                 sb.append(separator);
260             }
261 
262             if (human) {
263                 sb.append(((ToHuman) arr[i]).toHuman());
264             } else {
265                 sb.append(arr[i]);
266             }
267         }
268 
269         if (suffix != null) {
270             sb.append(suffix);
271         }
272 
273         return sb.toString();
274     }
275 
276 }
277