001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.visitor.*;
007import serp.util.*;
008
009/**
010 * The <code>lookupswitch</code> instruction.
011 *
012 * @author Abe White
013 */
014public class LookupSwitchInstruction extends JumpInstruction {
015    // case info
016    private List _matches = new LinkedList();
017    private List _cases = new LinkedList();
018
019    LookupSwitchInstruction(Code owner) {
020        super(owner, Constants.LOOKUPSWITCH);
021    }
022
023    int getLength() {
024        // don't call super.getLength(), cause JumpInstruction will return
025        // value assuming this is an 'if' or 'goto' instruction
026        int length = 1;
027
028        // make the first byte of the 'default' a multiple of 4 from the
029        // start of the method
030        int byteIndex = getByteIndex() + 1;
031        for (; (byteIndex % 4) != 0; byteIndex++, length++);
032
033        // default, npairs
034        length += 8;
035
036        // pairs
037        length += (8 * _matches.size());
038        return length;
039    }
040
041    public int getStackChange() {
042        return -1;
043    }
044
045    /**
046     * Synonymous with {@link #getTarget}.
047     */
048    public Instruction getDefaultTarget() {
049        return getTarget();
050    }
051
052    /**
053     * Synonymous with {@link #setTarget}.
054     */
055    public LookupSwitchInstruction setDefaultTarget(Instruction ins) {
056        return (LookupSwitchInstruction) setTarget(ins);
057    }
058
059    /**
060     * Synonymous with {@link #getOffset}.
061     */
062    public int getDefaultOffset() {
063        return getOffset();
064    }
065
066    /**
067     * Synonymous with {@link #setOffset}.
068     */
069    public LookupSwitchInstruction setDefaultOffset(int offset) {
070        setOffset(offset);
071        return this;
072    }
073
074    /**
075     * Set the match-jumppt pairs for this switch.
076     *
077     * @return this instruction, for method chaining
078     */
079    public LookupSwitchInstruction setCases(int[] matches, 
080        Instruction[] targets) {
081        _matches.clear();
082        _cases.clear();
083        for (int i = 0; i < matches.length; i++)
084            _matches.add(Numbers.valueOf(matches[i]));
085        for (int i = 0; i < targets.length; i++) {
086            InstructionPtrStrategy next = new InstructionPtrStrategy(this);
087            next.setTargetInstruction(targets[i]);
088            _cases.add(next);
089        }
090        invalidateByteIndexes();
091        return this;
092    }
093
094    public int[] getOffsets() {
095        int bi = getByteIndex();
096        int[] offsets = new int[_cases.size()];
097        for (int i = 0; i < offsets.length; i++)
098            offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
099                - bi;
100        return offsets;
101    }
102
103    /**
104     * Return the values of the case statements for this switch.
105     */
106    public int[] getMatches() {
107        int[] matches = new int[_matches.size()];
108        Iterator itr = _matches.iterator();
109        for (int i = 0; i < matches.length; i++)
110            matches[i] = ((Integer) itr.next()).intValue();
111        return matches;
112    }
113
114    /**
115     * Return the targets of the case statements for this switch.
116     */
117    public Instruction[] getTargets() {
118        Instruction[] result = new Instruction[_cases.size()];
119        for (int i = 0; i < result.length; i++)
120            result[i] = ((InstructionPtrStrategy) _cases.get(i)).
121                getTargetInstruction();
122        return result;
123    }
124
125    /**
126     * Add a case to this switch.
127     *
128     * @return this instruction, for method chaining
129     */
130    public LookupSwitchInstruction addCase(int match, Instruction target) {
131        _matches.add(Numbers.valueOf(match));
132        _cases.add(new InstructionPtrStrategy(this, target));
133        invalidateByteIndexes();
134        return this;
135    }
136
137    private Instruction findJumpPoint(int jumpByteIndex, List inss) {
138        Instruction ins;
139        for (Iterator itr = inss.iterator(); itr.hasNext();) {
140            ins = (Instruction) itr.next();
141            if (ins.getByteIndex() == jumpByteIndex)
142                return ins;
143        }
144        return null;
145    }
146
147    public void updateTargets() {
148        super.updateTargets();
149        for (Iterator itr = _cases.iterator(); itr.hasNext();)
150            ((InstructionPtrStrategy) itr.next()).updateTargets();
151    }
152
153    public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
154        super.replaceTarget(oldTarget, newTarget);
155        for (Iterator itr = _cases.iterator(); itr.hasNext();)
156            ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget,
157                newTarget);
158    }
159
160    public void acceptVisit(BCVisitor visit) {
161        visit.enterLookupSwitchInstruction(this);
162        visit.exitLookupSwitchInstruction(this);
163    }
164
165    void read(Instruction orig) {
166        super.read(orig);
167
168        LookupSwitchInstruction ins = (LookupSwitchInstruction) orig;
169        _matches = new LinkedList(ins._matches);
170        _cases.clear();
171        for (Iterator itr = ins._cases.iterator(); itr.hasNext();) {
172            InstructionPtrStrategy origPtr = (InstructionPtrStrategy)itr.next();
173            InstructionPtrStrategy newPtr = new InstructionPtrStrategy(this);
174            newPtr.setByteIndex(origPtr.getByteIndex());
175            _cases.add(newPtr);
176        }
177        invalidateByteIndexes();
178    }
179
180    void read(DataInput in) throws IOException {
181        // don't call super
182        int bi = getByteIndex();
183        for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++)
184            in.readByte();
185
186        setOffset(in.readInt());
187        _matches.clear();
188        _cases.clear();
189        for (int i = 0, pairCount = in.readInt(); i < pairCount; i++) {
190            _matches.add(Numbers.valueOf(in.readInt()));
191            InstructionPtrStrategy next = new InstructionPtrStrategy(this);
192            next.setByteIndex(bi + in.readInt());
193            _cases.add(next);
194        }
195    }
196
197    void write(DataOutput out) throws IOException {
198        // don't call super
199        int bi = getByteIndex();
200        for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++)
201            out.writeByte(0);
202
203        out.writeInt(getOffset());
204        out.writeInt(_matches.size());
205        for (int i = 0; i < _matches.size(); i++) {
206            out.writeInt(((Integer) _matches.get(i)).intValue());
207            out.writeInt(((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
208                - bi);
209        }
210    }
211}