001: /*
002: * Copyright 2003 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package net.sf.cglib.util;
017:
018: import java.util.*;
019: import net.sf.cglib.core.*;
020: import org.objectweb.asm.ClassVisitor;
021: import org.objectweb.asm.Label;
022: import org.objectweb.asm.Type;
023:
024: /**
025: * This class implements a simple String->int mapping for a fixed set of keys.
026: */
027: abstract public class StringSwitcher {
028: private static final Type STRING_SWITCHER = TypeUtils
029: .parseType("net.sf.cglib.util.StringSwitcher");
030: private static final Signature INT_VALUE = TypeUtils
031: .parseSignature("int intValue(String)");
032: private static final StringSwitcherKey KEY_FACTORY = (StringSwitcherKey) KeyFactory
033: .create(StringSwitcherKey.class);
034:
035: interface StringSwitcherKey {
036: public Object newInstance(String[] strings, int[] ints,
037: boolean fixedInput);
038: }
039:
040: /**
041: * Helper method to create a StringSwitcher.
042: * For finer control over the generated instance, use a new instance of StringSwitcher.Generator
043: * instead of this static method.
044: * @param strings the array of String keys; must be the same length as the value array
045: * @param ints the array of integer results; must be the same length as the key array
046: * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as <code>-1</code>; if true,
047: * the result will be undefined, and the resulting code will be faster
048: */
049: public static StringSwitcher create(String[] strings, int[] ints,
050: boolean fixedInput) {
051: Generator gen = new Generator();
052: gen.setStrings(strings);
053: gen.setInts(ints);
054: gen.setFixedInput(fixedInput);
055: return gen.create();
056: }
057:
058: protected StringSwitcher() {
059: }
060:
061: /**
062: * Return the integer associated with the given key.
063: * @param s the key
064: * @return the associated integer value, or <code>-1</code> if the key is unknown (unless
065: * <code>fixedInput</code> was specified when this <code>StringSwitcher</code> was created,
066: * in which case the return value for an unknown key is undefined)
067: */
068: abstract public int intValue(String s);
069:
070: public static class Generator extends AbstractClassGenerator {
071: private static final Source SOURCE = new Source(
072: StringSwitcher.class.getName());
073:
074: private String[] strings;
075: private int[] ints;
076: private boolean fixedInput;
077:
078: public Generator() {
079: super (SOURCE);
080: }
081:
082: /**
083: * Set the array of recognized Strings.
084: * @param strings the array of String keys; must be the same length as the value array
085: * @see #setInts
086: */
087: public void setStrings(String[] strings) {
088: this .strings = strings;
089: }
090:
091: /**
092: * Set the array of integer results.
093: * @param ints the array of integer results; must be the same length as the key array
094: * @see #setStrings
095: */
096: public void setInts(int[] ints) {
097: this .ints = ints;
098: }
099:
100: /**
101: * Configure how unknown String keys will be handled.
102: * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as <code>-1</code>; if true,
103: * the result will be undefined, and the resulting code will be faster
104: */
105: public void setFixedInput(boolean fixedInput) {
106: this .fixedInput = fixedInput;
107: }
108:
109: protected ClassLoader getDefaultClassLoader() {
110: return getClass().getClassLoader();
111: }
112:
113: /**
114: * Generate the <code>StringSwitcher</code>.
115: */
116: public StringSwitcher create() {
117: setNamePrefix(StringSwitcher.class.getName());
118: Object key = KEY_FACTORY.newInstance(strings, ints,
119: fixedInput);
120: return (StringSwitcher) super .create(key);
121: }
122:
123: public void generateClass(ClassVisitor v) throws Exception {
124: ClassEmitter ce = new ClassEmitter(v);
125: ce.begin_class(Constants.V1_2, Constants.ACC_PUBLIC,
126: getClassName(), STRING_SWITCHER, null,
127: Constants.SOURCE_FILE);
128: EmitUtils.null_constructor(ce);
129: final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
130: INT_VALUE, null);
131: e.load_arg(0);
132: final List stringList = Arrays.asList(strings);
133: int style = fixedInput ? Constants.SWITCH_STYLE_HASHONLY
134: : Constants.SWITCH_STYLE_HASH;
135: EmitUtils.string_switch(e, strings, style,
136: new ObjectSwitchCallback() {
137: public void processCase(Object key, Label end) {
138: e.push(ints[stringList.indexOf(key)]);
139: e.return_value();
140: }
141:
142: public void processDefault() {
143: e.push(-1);
144: e.return_value();
145: }
146: });
147: e.end_method();
148: ce.end_class();
149: }
150:
151: protected Object firstInstance(Class type) {
152: return (StringSwitcher) ReflectUtils.newInstance(type);
153: }
154:
155: protected Object nextInstance(Object instance) {
156: return instance;
157: }
158: }
159: }
|