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.cf.code;
18
19import com.android.dx.util.IntList;
20import com.android.dx.util.MutabilityControl;
21
22/**
23 * List of (value, target) mappings representing the choices of a
24 * {@code tableswitch} or {@code lookupswitch} instruction. It
25 * also holds the default target for the switch.
26 */
27public final class SwitchList extends MutabilityControl {
28    /** {@code non-null;} list of test values */
29    private final IntList values;
30
31    /**
32     * {@code non-null;} list of targets corresponding to the test values; there
33     * is always one extra element in the target list, to hold the
34     * default target
35     */
36    private final IntList targets;
37
38    /** ultimate size of the list */
39    private int size;
40
41    /**
42     * Constructs an instance.
43     *
44     * @param size {@code >= 0;} the number of elements to be in the table
45     */
46    public SwitchList(int size) {
47        super(true);
48        this.values = new IntList(size);
49        this.targets = new IntList(size + 1);
50        this.size = size;
51    }
52
53    /** {@inheritDoc} */
54    @Override
55    public void setImmutable() {
56        values.setImmutable();
57        targets.setImmutable();
58        super.setImmutable();
59    }
60
61    /**
62     * Gets the size of the list.
63     *
64     * @return {@code >= 0;} the list size
65     */
66    public int size() {
67        return size;
68    }
69
70    /**
71     * Gets the indicated test value.
72     *
73     * @param n {@code >= 0;}, < size(); which index
74     * @return the test value
75     */
76    public int getValue(int n) {
77        return values.get(n);
78    }
79
80    /**
81     * Gets the indicated target. Asking for the target at {@code size()}
82     * returns the default target.
83     *
84     * @param n {@code >= 0, <= size();} which index
85     * @return {@code >= 0;} the target
86     */
87    public int getTarget(int n) {
88        return targets.get(n);
89    }
90
91    /**
92     * Gets the default target. This is just a shorthand for
93     * {@code getTarget(size())}.
94     *
95     * @return {@code >= 0;} the default target
96     */
97    public int getDefaultTarget() {
98        return targets.get(size);
99    }
100
101    /**
102     * Gets the list of all targets. This includes one extra element at the
103     * end of the list, which holds the default target.
104     *
105     * @return {@code non-null;} the target list
106     */
107    public IntList getTargets() {
108        return targets;
109    }
110
111    /**
112     * Gets the list of all case values.
113     *
114     * @return {@code non-null;} the case value list
115     */
116    public IntList getValues() {
117        return values;
118    }
119
120    /**
121     * Sets the default target. It is only valid to call this method
122     * when all the non-default elements have been set.
123     *
124     * @param target {@code >= 0;} the absolute (not relative) default target
125     * address
126     */
127    public void setDefaultTarget(int target) {
128        throwIfImmutable();
129
130        if (target < 0) {
131            throw new IllegalArgumentException("target < 0");
132        }
133
134        if (targets.size() != size) {
135            throw new RuntimeException("non-default elements not all set");
136        }
137
138        targets.add(target);
139    }
140
141    /**
142     * Adds the given item.
143     *
144     * @param value the test value
145     * @param target {@code >= 0;} the absolute (not relative) target address
146     */
147    public void add(int value, int target) {
148        throwIfImmutable();
149
150        if (target < 0) {
151            throw new IllegalArgumentException("target < 0");
152        }
153
154        values.add(value);
155        targets.add(target);
156    }
157
158    /**
159     * Shrinks this instance if possible, removing test elements that
160     * refer to the default target. This is only valid after the instance
161     * is fully populated, including the default target (naturally).
162     */
163    public void removeSuperfluousDefaults() {
164        throwIfImmutable();
165
166        int sz = size;
167
168        if (sz != (targets.size() - 1)) {
169            throw new IllegalArgumentException("incomplete instance");
170        }
171
172        int defaultTarget = targets.get(sz);
173        int at = 0;
174
175        for (int i = 0; i < sz; i++) {
176            int target = targets.get(i);
177            if (target != defaultTarget) {
178                if (i != at) {
179                    targets.set(at, target);
180                    values.set(at, values.get(i));
181                }
182                at++;
183            }
184        }
185
186        if (at != sz) {
187            values.shrink(at);
188            targets.set(at, defaultTarget);
189            targets.shrink(at + 1);
190            size = at;
191        }
192    }
193}
194