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}