FixedSizeList.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
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
17package com.android.dx.util;
18
19import java.util.Arrays;
20
21/**
22 * Simple (mostly) fixed-size list of objects, which may be made immutable.
23 */
24public 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     */
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
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
64    public int hashCode() {
65        return Arrays.hashCode(arr);
66    }
67
68    /** {@inheritDoc} */
69    @Override
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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