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 }