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

de.japkit.el.juel.DynamicFunctionMapper Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
package de.japkit.el.juel;

import com.google.common.base.Objects;
import de.japkit.el.ElExtensions;
import de.japkit.el.juel.MethodCache;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import javax.el.FunctionMapper;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.ClassLoadingStrategy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.instrumentation.MethodDelegation;
import net.bytebuddy.modifier.MethodArguments;
import net.bytebuddy.modifier.Ownership;
import net.bytebuddy.modifier.Visibility;
import org.eclipse.xtend.lib.annotations.Data;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.lib.StringExtensions;
import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;

/**
 * A function mapper that allows to call functions on value stack.
 */
@Data
@SuppressWarnings("all")
public class DynamicFunctionMapper extends FunctionMapper {
  public static class Invoker {
    private final Object function;
    
    public Invoker(final Object function) {
      this.function = function;
    }
    
    public Object invoke(final Object... params) {
      return ElExtensions.invoke(this.function, null, params);
    }
  }
  
  private final Map contextMap;
  
  private final MethodCache methodCache;
  
  @Override
  public Method resolveFunction(final String prefix, final String localName) {
    Method _xblockexpression = null;
    {
      boolean _isNullOrEmpty = StringExtensions.isNullOrEmpty(prefix);
      boolean _not = (!_isNullOrEmpty);
      if (_not) {
        StringConcatenation _builder = new StringConcatenation();
        _builder.append("Prefix ");
        _builder.append(prefix, "");
        _builder.append(" not supported.");
        throw new UnsupportedOperationException(_builder.toString());
      }
      final Object function = this.contextMap.get(localName);
      boolean _equals = Objects.equal(function, null);
      if (_equals) {
        return null;
      }
      Method _elvis = null;
      Method _get = this.methodCache.get(function);
      if (_get != null) {
        _elvis = _get;
      } else {
        Method _xblockexpression_1 = null;
        {
          final DynamicFunctionMapper.Invoker invoker = new DynamicFunctionMapper.Invoker(function);
          final Method method = this.createStaticDelegateMethod(localName, invoker);
          final Method method1 = method;
          this.methodCache.put(function, method1);
          _xblockexpression_1 = method1;
        }
        _elvis = _xblockexpression_1;
      }
      _xblockexpression = _elvis;
    }
    return _xblockexpression;
  }
  
  private Method createStaticDelegateMethod(final String functionName, final DynamicFunctionMapper.Invoker invoker) {
    try {
      Method _xblockexpression = null;
      {
        final ArrayList> argTypes = new ArrayList>();
        argTypes.add(Object[].class);
        ByteBuddy _byteBuddy = new ByteBuddy();
        DynamicType.Builder _subclass = _byteBuddy.subclass(Object.class);
        Class _class = this.getClass();
        Package _package = _class.getPackage();
        String _name = _package.getName();
        String _plus = (_name + ".functioninvokers.");
        String _firstUpper = StringExtensions.toFirstUpper(functionName);
        String _plus_1 = (_plus + _firstUpper);
        DynamicType.Builder _name_1 = _subclass.name(_plus_1);
        DynamicType.Builder.ExceptionDeclarableMethodInterception _defineMethod = _name_1.defineMethod(
          "invoke", 
          Object.class, argTypes, 
          MethodArguments.VARARGS, 
          Ownership.STATIC, 
          Visibility.PUBLIC);
        MethodDelegation _to = MethodDelegation.to(invoker);
        DynamicType.Builder.MethodAnnotationTarget _intercept = _defineMethod.intercept(_to);
        DynamicType.Unloaded _make = _intercept.make();
        Class _class_1 = this.getClass();
        ClassLoader _classLoader = _class_1.getClassLoader();
        DynamicType.Loaded _load = _make.load(_classLoader, ClassLoadingStrategy.Default.WRAPPER);
        final Class invokerClass = _load.getLoaded();
        final Method method = invokerClass.getMethod("invoke", Object[].class);
        _xblockexpression = method;
      }
      return _xblockexpression;
    } catch (Throwable _e) {
      throw Exceptions.sneakyThrow(_e);
    }
  }
  
  public DynamicFunctionMapper(final Map contextMap, final MethodCache methodCache) {
    super();
    this.contextMap = contextMap;
    this.methodCache = methodCache;
  }
  
  @Override
  @Pure
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((this.contextMap== null) ? 0 : this.contextMap.hashCode());
    result = prime * result + ((this.methodCache== null) ? 0 : this.methodCache.hashCode());
    return result;
  }
  
  @Override
  @Pure
  public boolean equals(final Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    DynamicFunctionMapper other = (DynamicFunctionMapper) obj;
    if (this.contextMap == null) {
      if (other.contextMap != null)
        return false;
    } else if (!this.contextMap.equals(other.contextMap))
      return false;
    if (this.methodCache == null) {
      if (other.methodCache != null)
        return false;
    } else if (!this.methodCache.equals(other.methodCache))
      return false;
    return true;
  }
  
  @Override
  @Pure
  public String toString() {
    String result = new ToStringBuilder(this)
    	.addAllFields()
    	.toString();
    return result;
  }
  
  @Pure
  public Map getContextMap() {
    return this.contextMap;
  }
  
  @Pure
  public MethodCache getMethodCache() {
    return this.methodCache;
  }
}