package org.jruby.javasupport.binding;
import org.jruby.RubyModule;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.javasupport.JavaUtil;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.jruby.util.StringSupport.startsWith;
public abstract class MethodInstaller extends NamedInstaller {
final ArrayList<Method> methods = new ArrayList<>(4);
private List<String> aliases;
private boolean localMethod;
public MethodInstaller(String name, int type) { super(name, type); }
final void addMethod(final Method method, final Class<?> clazz) {
this.methods.add(method);
localMethod |=
clazz == method.getDeclaringClass() ||
method.getDeclaringClass().isInterface();
}
final void addAlias(final String alias) {
Collection<String> aliases = this.aliases;
if (aliases == null) {
aliases = this.aliases = new ArrayList<>(4);
}
if ( ! aliases.contains(alias) ) aliases.add(alias);
}
void assignAliases(final Map<String, AssignedName> assignedNames) {
final String name = this.name;
String rubyCasedName = JavaUtil.getRubyCasedName(name);
addUnassignedAlias(rubyCasedName, assignedNames, Priority.ALIAS);
String javaPropertyName = JavaUtil.getJavaPropertyName(name);
final List<Method> methods = this.methods;
for ( int i = 0; i < methods.size(); i++ ) {
final Method method = methods.get(i);
Class<?>[] argTypes = method.getParameterTypes();
Class<?> resultType = method.getReturnType();
int argCount = argTypes.length;
if (name.equals("apply")) {
addUnassignedAlias("[]", assignedNames, Priority.ALIAS);
} else if (argCount == 2 && name.equals("update")) {
addUnassignedAlias("[]=", assignedNames, Priority.ALIAS);
} else if (startsWith(name, '$')) {
addUnassignedAlias(MethodGatherer.fixScalaNames(name), assignedNames, Priority.ALIAS);
}
String rubyPropertyName = null;
if (javaPropertyName != null) {
if (rubyCasedName.startsWith("get_")) {
rubyPropertyName = rubyCasedName.substring(4);
if (argCount == 0) {
addUnassignedAlias(javaPropertyName, assignedNames, Priority.GET_ALIAS);
addUnassignedAlias(rubyPropertyName, assignedNames, Priority.GET_ALIAS);
}
} else if (rubyCasedName.startsWith("set_")) {
rubyPropertyName = rubyCasedName.substring(4);
if (argCount == 1 && resultType == void.class) {
addUnassignedAlias(javaPropertyName + '=', assignedNames, Priority.ALIAS);
addUnassignedAlias(rubyPropertyName + '=', assignedNames, Priority.ALIAS);
}
} else if (rubyCasedName.startsWith("is_")) {
rubyPropertyName = rubyCasedName.substring(3);
if (resultType == boolean.class) {
addUnassignedAlias(javaPropertyName, assignedNames, Priority.IS_ALIAS);
addUnassignedAlias(rubyPropertyName, assignedNames, Priority.IS_ALIAS);
}
}
}
if (resultType == boolean.class) {
addUnassignedAlias(rubyCasedName + '?', assignedNames, Priority.ALIAS);
if (rubyPropertyName != null) {
addUnassignedAlias(rubyPropertyName + '?', assignedNames, Priority.ALIAS);
}
}
}
}
boolean addUnassignedAlias(final String name,
final Map<String, AssignedName> assignedNames,
final Priority aliasType) {
AssignedName assignedName = assignedNames.get(name);
if (aliasType.moreImportantThan(assignedName)) {
addAlias(name);
assignedNames.put(name, new AssignedName(name, aliasType));
return true;
}
if (aliasType.asImportantAs(assignedName)) {
addAlias(name);
return true;
}
return false;
}
final void removeAlias(final String alias) {
Collection<String> aliases = this.aliases;
if (aliases == null) return;
aliases.remove(alias);
}
final void defineMethods(RubyModule target, DynamicMethod invoker) {
defineMethods(target, invoker, true);
}
protected final void defineMethods(RubyModule target, DynamicMethod invoker, boolean checkDups) {
String oldName = this.name;
target.addMethod(oldName, invoker);
List<String> aliases = this.aliases;
if ( aliases != null && isPublic() ) {
for (int i = 0; i < aliases.size(); i++) {
String name = aliases.get(i);
if (checkDups && oldName.equals(name)) continue;
target.addMethod(name, invoker);
}
}
}
@Override
boolean hasLocalMethod () { return localMethod; }
void setLocalMethod(boolean flag) { localMethod = flag; }
}