/*
* Copyright 2013 eXo Platform SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package juzu.impl.common;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
/**
* Uniquely identifies a method.
*
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public final class MethodHandle implements Iterable<String> {
public static MethodHandle parse(String s) throws NullPointerException, IllegalArgumentException {
int method = s.indexOf('#');
if (method == -1) {
throw new IllegalArgumentException("Invalid method handle " + s);
}
int leftParenthesis = s.indexOf('(', method + 1);
if (leftParenthesis == -1 || leftParenthesis > s.length() - 2) {
throw new IllegalArgumentException("Invalid method handle " + s);
}
if (s.charAt(s.length() - 1) != ')') {
throw new IllegalArgumentException("Invalid method handle " + s);
}
//
String type = s.substring(0, method);
String name = s.substring(method + 1, leftParenthesis);
//
if (s.length() - leftParenthesis == 2) {
return new MethodHandle(type, name);
} else {
String[] list = EMPTY_STRINGS;
for (String parameter : Spliterator.split(s, leftParenthesis + 1, s.length() - 1, ',', new ArrayList<String>())) {
if (parameter.length() == 0) {
throw new IllegalArgumentException();
}
list = Tools.appendTo(list, parameter);
}
return new MethodHandle(type, name, list);
}
}
/** . */
private static final String[] EMPTY_STRINGS = new String[0];
/** . */
private final String type;
/** . */
private final String name;
/** . */
private final String[] parameters;
/** . */
private String toString;
public MethodHandle(Method method) throws NullPointerException {
if (method == null) {
throw new NullPointerException("No null method accepted");
}
Type[] parameterTypes = method.getGenericParameterTypes();
String[] parameters = new String[parameterTypes.length];
StringBuilder sb = new StringBuilder();
for (int i = 0;i < parameters.length;i++) {
format(parameterTypes[i], sb);
parameters[i] = sb.toString();
sb.setLength(0);
}
//
this.type = method.getDeclaringClass().getName();
this.name = method.getName();
this.parameters = parameters.clone();
this.toString = null;
}
private void format(Type type, StringBuilder sb) {
if (type instanceof Class) {
Class classType = (Class)type;
if (classType.isArray()) {
format(classType.getComponentType(), sb);
sb.append("[]");
} else {
sb.append(classType.getName());
}
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType)type;
format(parameterizedType.getRawType(), sb);
sb.append('<');
Type[] typeArguments = parameterizedType.getActualTypeArguments();
for (int i = 0;i < typeArguments.length;i++) {
if (i > 0) {
sb.append(',');
}
format(typeArguments[i], sb);
}
sb.append('>');
} else if (type instanceof GenericArrayType) {
GenericArrayType arrayType = (GenericArrayType)type;
format(arrayType.getGenericComponentType(), sb);
sb.append("[]");
} else {
throw new UnsupportedOperationException("todo " + type);
}
}
public MethodHandle(String type, String name) {
this.type = type;
this.name = name;
this.parameters = EMPTY_STRINGS;
}
public MethodHandle(String type, String name, String... parameters) {
this.type = type;
this.name = name;
this.parameters = parameters.length == 0 ? parameters : parameters.clone();
}
public String getType() {
return type;
}
public String getName() {
return name;
}
public int getParameterSize() {
return parameters.length;
}
public String getParameterAt(int index) throws IndexOutOfBoundsException {
if (index < 0 || index > parameters.length) {
throw new IndexOutOfBoundsException("Bad index " + index);
}
return parameters[index];
}
public Iterator<String> iterator() {
return Tools.iterator(parameters);
}
@Override
public int hashCode() {
return type.hashCode() ^ name.hashCode() ^ Arrays.hashCode(parameters);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof MethodHandle) {
MethodHandle that = (MethodHandle)obj;
return type.equals(that.type) && name.equals(that.name) && Arrays.equals(parameters, that.parameters);
}
return false;
}
@Override
public String toString() {
if (toString == null) {
toString = Tools.join(new StringBuilder().append(type).append('#').append(name).append('('), ',', parameters).append(')').toString();
}
return toString;
}
}