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    
015    package org.apache.tapestry5.internal.plastic;
016    
017    import org.apache.tapestry5.internal.plastic.asm.Label;
018    import org.apache.tapestry5.internal.plastic.asm.Opcodes;
019    import org.apache.tapestry5.plastic.InstructionBuilder;
020    import org.apache.tapestry5.plastic.InstructionBuilderCallback;
021    import org.apache.tapestry5.plastic.SwitchBlock;
022    import org.apache.tapestry5.plastic.SwitchCallback;
023    
024    public 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                    public void doBuild(InstructionBuilder builder)
071                    {
072                        builder.throwException(IllegalArgumentException.class,
073                                "Switch value not matched in case statement.");
074                    }
075                });
076            }
077    
078            state.visitor.visitLabel(endSwitchLabel);
079    
080            lock();
081        }
082    
083        public void addCase(int caseValue, boolean jumpToEnd, InstructionBuilderCallback callback)
084        {
085            assert caseValue >= min;
086            assert caseValue <= max;
087    
088            if (defaultAdded)
089                throw new IllegalStateException("The default block must come last.");
090    
091            // TODO: Check that no case value is repeated
092    
093            state.visitor.visitLabel(caseLabels[caseValue - min]);
094    
095            callback.doBuild(builder);
096    
097            if (jumpToEnd)
098                state.visitor.visitJumpInsn(GOTO, endSwitchLabel);
099        }
100    
101        public void addDefault(InstructionBuilderCallback callback)
102        {
103            if (defaultAdded)
104                throw new IllegalStateException("A SwitchBlock may only have one default block.");
105    
106            state.visitor.visitLabel(defaultLabel);
107    
108            callback.doBuild(builder);
109    
110            defaultAdded = true;
111        }
112    }