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 }