001// Copyright 2011 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007// http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.internal.plastic;
016
017import org.apache.tapestry5.internal.plastic.asm.Label;
018import org.apache.tapestry5.internal.plastic.asm.Opcodes;
019import org.apache.tapestry5.plastic.InstructionBuilder;
020import org.apache.tapestry5.plastic.InstructionBuilderCallback;
021import org.apache.tapestry5.plastic.SwitchBlock;
022import org.apache.tapestry5.plastic.SwitchCallback;
023
024public class SwitchBlockImpl extends Lockable implements SwitchBlock, Opcodes
025{
026    private final InstructionBuilder builder;
027
028    private final InstructionBuilderState state;
029
030    private final int min, max;
031
032    private final Label defaultLabel, endSwitchLabel;
033
034    private final Label[] caseLabels;
035
036    private boolean defaultAdded = false;
037
038    SwitchBlockImpl(InstructionBuilder builder, InstructionBuilderState state, int min, int max)
039    {
040        assert min <= max;
041
042        this.builder = builder;
043        this.state = state;
044        this.min = min;
045        this.max = max;
046
047        defaultLabel = new Label();
048        endSwitchLabel = new Label();
049
050        caseLabels = new Label[max - min + 1];
051
052        for (int i = min; i <= max; i++)
053        {
054            caseLabels[i - min] = new Label();
055        }
056
057        state.visitor.visitTableSwitchInsn(min, max, defaultLabel, caseLabels);
058    }
059
060    void doCallback(SwitchCallback callback)
061    {
062        check();
063
064        callback.doSwitch(this);
065
066        if (!defaultAdded)
067        {
068            addDefault(new InstructionBuilderCallback()
069            {
070                @Override
071                public void doBuild(InstructionBuilder builder)
072                {
073                    builder.throwException(IllegalArgumentException.class,
074                            "Switch value not matched in case statement.");
075                }
076            });
077        }
078
079        state.visitor.visitLabel(endSwitchLabel);
080
081        lock();
082    }
083
084    @Override
085    public void addCase(int caseValue, boolean jumpToEnd, InstructionBuilderCallback callback)
086    {
087        assert caseValue >= min;
088        assert caseValue <= max;
089
090        if (defaultAdded)
091            throw new IllegalStateException("The default block must come last.");
092
093        // TODO: Check that no case value is repeated
094
095        state.visitor.visitLabel(caseLabels[caseValue - min]);
096
097        callback.doBuild(builder);
098
099        if (jumpToEnd)
100            state.visitor.visitJumpInsn(GOTO, endSwitchLabel);
101    }
102
103    @Override
104    public void addDefault(InstructionBuilderCallback callback)
105    {
106        if (defaultAdded)
107            throw new IllegalStateException("A SwitchBlock may only have one default block.");
108
109        state.visitor.visitLabel(defaultLabel);
110
111        callback.doBuild(builder);
112
113        defaultAdded = true;
114    }
115}