All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.taketoday.bytecode.util.StringSwitcher Maven / Gradle / Ivy

/*
 * Original Author -> Harry Yang ([email protected]) https://taketoday.cn
 * Copyright © TODAY & 2017 - 2022 All Rights Reserved.
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see [http://www.gnu.org/licenses/]
 */
package cn.taketoday.bytecode.util;

import java.util.Arrays;
import java.util.List;

import cn.taketoday.bytecode.ClassVisitor;
import cn.taketoday.bytecode.Label;
import cn.taketoday.bytecode.Opcodes;
import cn.taketoday.bytecode.Type;
import cn.taketoday.bytecode.commons.MethodSignature;
import cn.taketoday.bytecode.core.AbstractClassGenerator;
import cn.taketoday.bytecode.core.ClassEmitter;
import cn.taketoday.bytecode.core.CodeEmitter;
import cn.taketoday.bytecode.core.EmitUtils;
import cn.taketoday.bytecode.core.ObjectSwitchCallback;
import cn.taketoday.util.ReflectionUtils;

import static cn.taketoday.lang.Constant.SOURCE_FILE;

/**
 * This class implements a simple String->int mapping for a fixed set of keys.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public abstract class StringSwitcher {

  private static final Type STRING_SWITCHER = Type.fromClass(StringSwitcher.class);
  private static final MethodSignature INT_VALUE = MethodSignature.from("int intValue(String)");

  record StringSwitcherKey(List strings, int[] ints, boolean fixedInput) {

  }

  /**
   * Helper method to create a StringSwitcher. For finer control over the
   * generated instance, use a new instance of StringSwitcher.Generator instead of
   * this static method.
   *
   * @param strings the array of String keys; must be the same length as the value
   * array
   * @param ints the array of integer results; must be the same length as the key
   * array
   * @param fixedInput if false, an unknown key will be returned from {@link #intValue}
   * as -1; if true, the result will be undefined, and the
   * resulting code will be faster
   */
  public static StringSwitcher create(String[] strings, int[] ints, boolean fixedInput) {
    Generator gen = new Generator();
    gen.setStrings(strings);
    gen.setInts(ints);
    gen.setFixedInput(fixedInput);
    return gen.create();
  }

  protected StringSwitcher() { }

  /**
   * Return the integer associated with the given key.
   *
   * @param s the key
   * @return the associated integer value, or -1 if the key is
   * unknown (unless fixedInput was specified when this
   * StringSwitcher was created, in which case the return
   * value for an unknown key is undefined)
   */
  abstract public int intValue(String s);

  public static class Generator extends AbstractClassGenerator {

    private int[] ints;
    private String[] strings;
    private boolean fixedInput;

    public Generator() {
      super(StringSwitcher.class);
    }

    /**
     * Set the array of recognized Strings.
     *
     * @param strings the array of String keys; must be the same length as the value
     * array
     * @see #setInts
     */
    public void setStrings(String[] strings) {
      this.strings = strings;
    }

    /**
     * Set the array of integer results.
     *
     * @param ints the array of integer results; must be the same length as the key
     * array
     * @see #setStrings
     */
    public void setInts(int[] ints) {
      this.ints = ints;
    }

    /**
     * Configure how unknown String keys will be handled.
     *
     * @param fixedInput if false, an unknown key will be returned from {@link #intValue}
     * as -1; if true, the result will be undefined, and the
     * resulting code will be faster
     */
    public void setFixedInput(boolean fixedInput) {
      this.fixedInput = fixedInput;
    }

    @Override
    protected ClassLoader getDefaultClassLoader() {
      return getClass().getClassLoader();
    }

    /**
     * Generate the StringSwitcher.
     */
    public StringSwitcher create() {
      setNamePrefix(StringSwitcher.class.getName());
      Object key = new StringSwitcherKey(Arrays.asList(strings), ints, fixedInput);
      return (StringSwitcher) super.create(key);
    }

    @Override
    public void generateClass(ClassVisitor v) throws Exception {
      final ClassEmitter ce = new ClassEmitter(v);
      ce.beginClass(Opcodes.JAVA_VERSION, Opcodes.ACC_PUBLIC, getClassName(), STRING_SWITCHER, null, SOURCE_FILE);
      EmitUtils.nullConstructor(ce);
      final CodeEmitter e = ce.beginMethod(Opcodes.ACC_PUBLIC, INT_VALUE);
      e.loadArg(0);
      final List stringList = Arrays.asList(strings);
      int style = fixedInput ? Opcodes.SWITCH_STYLE_HASHONLY : Opcodes.SWITCH_STYLE_HASH;
      EmitUtils.stringSwitch(e, strings, style, new ObjectSwitchCallback() {

        @Override
        public void processCase(Object key, Label end) {
          e.push(ints[stringList.indexOf(key)]);
          e.returnValue();
        }

        @Override
        public void processDefault() {
          e.push(-1);
          e.returnValue();
        }
      });
      e.end_method();
      ce.endClass();
    }

    @Override
    protected Object firstInstance(Class type) {
      return ReflectionUtils.newInstance(type);
    }

    @Override
    protected Object nextInstance(Object instance) {
      return instance;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy