Package org.renjin.invoke.reflection

Source Code of org.renjin.invoke.reflection.ClassBindingImpl

package org.renjin.invoke.reflection;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.renjin.eval.Context;
import org.renjin.eval.EvalException;
import org.renjin.invoke.ClassBinding;
import org.renjin.sexp.SEXP;
import org.renjin.sexp.Symbol;


import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

public class ClassBindingImpl implements ClassBinding {
 
  private static final IdentityHashMap<Class, ClassBindingImpl> TABLE = Maps.newIdentityHashMap();
 
  public static ClassBindingImpl get(Class clazz) {
    synchronized (TABLE) {
      ClassBindingImpl binding = TABLE.get(clazz);
      if(binding == null) {
        binding = new ClassBindingImpl(clazz);
        TABLE.put(clazz, binding);
      }
      return binding;
    }
  }
 
  private Class clazz;
 
  private ConstructorBinding constructorBinding;
  private IdentityHashMap<Symbol, MemberBinding> members = Maps.newIdentityHashMap();
  private IdentityHashMap<Symbol, SEXP> staticMembers = Maps.newIdentityHashMap();
 
  private ClassBindingImpl(Class clazz) {
    this.clazz = clazz;
           
    Map<Symbol, Method> getters = Maps.newHashMap();
    Multimap<Symbol, Method> setters = ArrayListMultimap.create();
    Multimap<Symbol, Method> methods = ArrayListMultimap.create();
    Multimap<Symbol, Method> staticMethods = ArrayListMultimap.create();
   
   
    for(Method method : clazz.getMethods()) {
      if((method.getModifiers() & Modifier.PUBLIC) != 0 &&
          method.getDeclaringClass() != Object.class) {
      
        if((method.getModifiers() & Modifier.STATIC) != 0 ) {
          staticMethods.put(Symbol.get(method.getName()), method);
        } else {
          String propertyName;
          if((propertyName=isGetter(method)) != null) {
            getters.put(Symbol.get(propertyName), method);
          } else if((propertyName=isSetter(method)) != null) {
            setters.put(Symbol.get(propertyName), method);
          } else {
            methods.put(Symbol.get(method.getName()), method);
          }
        }
      }
    }
   
    // any setters without matching getters will be treated as methods
    for(Symbol name : Lists.newArrayList(setters.keySet())) {
      if(!getters.containsKey(name)) {
        for(Method setter : setters.removeAll(name)) {
          methods.put(Symbol.get(setter.getName()), setter);
        }
      }
    }
   
    for(Symbol name : Sets.union(methods.keySet(), Sets.union(getters.keySet(), setters.keySet()))) {
      if(methods.containsKey(name)) {
        members.put(name, new MethodBinding(name, methods.get(name)));
     
        // TODO: add hidden getters / setters as methods
      } else {
        members.put(name, new PropertyBinding(name, getters.get(name), setters.get(name)));
      }
    }
   
    for(Symbol name : staticMethods.keySet()) {
      this.staticMembers.put(name, new MethodFunction(null,
          new FunctionBinding(staticMethods.get(name))));
    }
   
    this.constructorBinding = new ConstructorBinding(clazz.getConstructors());
    if(!this.constructorBinding.isEmpty()) {
      this.staticMembers.put(Symbol.get("new"), new ConstructorFunction(constructorBinding));
    }
  }

  private String isGetter(Method method) {
    // check signature
    if(method.getParameterTypes().length != 0) {
      return null;
    }
   
    String name = method.getName();
    if(name.startsWith("get") &&
       name.length() > "get".length()) {
     
      return name.substring(3,4).toLowerCase() + name.substring(4);
    }
   
    if(name.startsWith("is") &&
       name.length() > "is".length()) {
     
      return name.substring(2,3).toLowerCase() + name.substring(3);
    }
   
    return null;
  }
 
  private String isSetter(Method method) {
    if(method.getParameterTypes().length != 1) {
      return null;
    }
   
    String name = method.getName();
    if(name.startsWith("set") &&
        name.length() > "set".length()) {
      return name.substring(3,4).toLowerCase() + name.substring(4);
    }
   
    return null;
  }

  public Set<Symbol> getMembers() {
    return members.keySet();
  }

  @Override
  public MemberBinding getMemberBinding(Symbol name) {
    MemberBinding memberBinding = members.get(name);
    if(memberBinding == null) {
      throw new EvalException("Instance of class %s has no member named '%s'",
          getBoundClass().getName(), name.getPrintName());
    }
    return memberBinding;
  }
 
  public Set<Symbol> getStaticMembers() {
    return staticMembers.keySet();
  }
 
  public SEXP getStaticMember(Symbol name) {
    return staticMembers.get(name);
  }
 
  public SEXP getStaticMember(String name) {
    return getStaticMember(Symbol.get(name));
  }

  public Object newInstance(Context context, List<SEXP> constructorArgs) {
    return constructorBinding.newInstance(context, constructorArgs);
  }

  public Class getBoundClass() {
    return clazz;
  }

  public ConstructorBinding getConstructorBinding() {
    return constructorBinding;
  }
}
TOP

Related Classes of org.renjin.invoke.reflection.ClassBindingImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.