1/* 2 * Copyright 2003 The Apache Software Foundation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.mockito.cglib.util; 17 18import java.util.*; 19 20import org.mockito.asm.ClassVisitor; 21import org.mockito.asm.Label; 22import org.mockito.asm.Type; 23import org.mockito.cglib.core.*; 24 25/** 26 * This class implements a simple String->int mapping for a fixed set of keys. 27 */ 28abstract public class StringSwitcher { 29 private static final Type STRING_SWITCHER = 30 TypeUtils.parseType("org.mockito.cglib.util.StringSwitcher"); 31 private static final Signature INT_VALUE = 32 TypeUtils.parseSignature("int intValue(String)"); 33 private static final StringSwitcherKey KEY_FACTORY = 34 (StringSwitcherKey)KeyFactory.create(StringSwitcherKey.class); 35 36 interface StringSwitcherKey { 37 public Object newInstance(String[] strings, int[] ints, boolean fixedInput); 38 } 39 40 /** 41 * Helper method to create a StringSwitcher. 42 * For finer control over the generated instance, use a new instance of StringSwitcher.Generator 43 * instead of this static method. 44 * @param strings the array of String keys; must be the same length as the value array 45 * @param ints the array of integer results; must be the same length as the key array 46 * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as <code>-1</code>; if true, 47 * the result will be undefined, and the resulting code will be faster 48 */ 49 public static StringSwitcher create(String[] strings, int[] ints, boolean fixedInput) { 50 Generator gen = new Generator(); 51 gen.setStrings(strings); 52 gen.setInts(ints); 53 gen.setFixedInput(fixedInput); 54 return gen.create(); 55 } 56 57 protected StringSwitcher() { 58 } 59 60 /** 61 * Return the integer associated with the given key. 62 * @param s the key 63 * @return the associated integer value, or <code>-1</code> if the key is unknown (unless 64 * <code>fixedInput</code> was specified when this <code>StringSwitcher</code> was created, 65 * in which case the return value for an unknown key is undefined) 66 */ 67 abstract public int intValue(String s); 68 69 public static class Generator extends AbstractClassGenerator { 70 private static final Source SOURCE = new Source(StringSwitcher.class.getName()); 71 72 private String[] strings; 73 private int[] ints; 74 private boolean fixedInput; 75 76 public Generator() { 77 super(SOURCE); 78 } 79 80 /** 81 * Set the array of recognized Strings. 82 * @param strings the array of String keys; must be the same length as the value array 83 * @see #setInts 84 */ 85 public void setStrings(String[] strings) { 86 this.strings = strings; 87 } 88 89 /** 90 * Set the array of integer results. 91 * @param ints the array of integer results; must be the same length as the key array 92 * @see #setStrings 93 */ 94 public void setInts(int[] ints) { 95 this.ints = ints; 96 } 97 98 /** 99 * Configure how unknown String keys will be handled. 100 * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as <code>-1</code>; if true, 101 * the result will be undefined, and the resulting code will be faster 102 */ 103 public void setFixedInput(boolean fixedInput) { 104 this.fixedInput = fixedInput; 105 } 106 107 protected ClassLoader getDefaultClassLoader() { 108 return getClass().getClassLoader(); 109 } 110 111 /** 112 * Generate the <code>StringSwitcher</code>. 113 */ 114 public StringSwitcher create() { 115 setNamePrefix(StringSwitcher.class.getName()); 116 Object key = KEY_FACTORY.newInstance(strings, ints, fixedInput); 117 return (StringSwitcher)super.create(key); 118 } 119 120 public void generateClass(ClassVisitor v) throws Exception { 121 ClassEmitter ce = new ClassEmitter(v); 122 ce.begin_class(Constants.V1_2, 123 Constants.ACC_PUBLIC, 124 getClassName(), 125 STRING_SWITCHER, 126 null, 127 Constants.SOURCE_FILE); 128 EmitUtils.null_constructor(ce); 129 final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, INT_VALUE, null); 130 e.load_arg(0); 131 final List stringList = Arrays.asList(strings); 132 int style = fixedInput ? Constants.SWITCH_STYLE_HASHONLY : Constants.SWITCH_STYLE_HASH; 133 EmitUtils.string_switch(e, strings, style, new ObjectSwitchCallback() { 134 public void processCase(Object key, Label end) { 135 e.push(ints[stringList.indexOf(key)]); 136 e.return_value(); 137 } 138 public void processDefault() { 139 e.push(-1); 140 e.return_value(); 141 } 142 }); 143 e.end_method(); 144 ce.end_class(); 145 } 146 147 protected Object firstInstance(Class type) { 148 return (StringSwitcher)ReflectUtils.newInstance(type); 149 } 150 151 protected Object nextInstance(Object instance) { 152 return instance; 153 } 154 } 155} 156