blob: 7eb7954999c0d9f2d04e1dbe5c1db60c6c16feb5 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* 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 com.google.gwt.dev.util;
import com.google.gwt.core.ext.typeinfo.JniConstants;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A parsed Java reference from within a JSNI method.
*/
public class JsniRef {
/**
* Special field name for referring to a class literal.
*/
public static final String CLASS = "class";
/**
* Special method name for a class constructor.
*/
public static final String NEW = "new";
/**
* A parameter list indicating a match to any overload.
*/
public static final String WILDCARD_PARAM_LIST = "*";
/**
* A regex pattern for a Java reference in JSNI code. Its groups are:
* <ol>
* <li>the class name
* <li>the field or method name
* <li>the method parameter types, including the surrounding parentheses
* <li>the method parameter types, excluding the parentheses
* </ol>
*/
private static Pattern JsniRefPattern = Pattern.compile("@?([^:]+)::([^(]+)(\\((.*)\\))?");
/**
* Parse a Java reference from JSNI code. This parser is forgiving; it does
* not always detect invalid references. If the refString is improperly
* formatted, returns null.
*/
public static JsniRef parse(String refString) {
Matcher matcher = JsniRefPattern.matcher(refString);
if (!matcher.matches()) {
return null;
}
String className = matcher.group(1);
String memberName = matcher.group(2);
String paramTypesString = null;
String[] paramTypes = null;
if (matcher.group(3) != null) {
paramTypesString = matcher.group(4);
if (!paramTypesString.equals(WILDCARD_PARAM_LIST)) {
paramTypes = computeParamTypes(paramTypesString);
if (paramTypes == null) {
return null;
}
}
}
return new JsniRef(className, memberName, paramTypesString, paramTypes);
}
private static String[] computeParamTypes(String paramTypesString) {
ArrayList<String> types = new ArrayList<String>();
StringBuilder nextType = new StringBuilder();
boolean inRef = false;
for (char c : paramTypesString.toCharArray()) {
nextType.append(c);
if (inRef) {
if (c == JniConstants.DESC_REF_END) {
types.add(nextType.toString());
nextType.setLength(0);
inRef = false;
}
} else {
switch (c) {
case JniConstants.DESC_BOOLEAN:
case JniConstants.DESC_BYTE:
case JniConstants.DESC_CHAR:
case JniConstants.DESC_DOUBLE:
case JniConstants.DESC_FLOAT:
case JniConstants.DESC_INT:
case JniConstants.DESC_LONG:
case JniConstants.DESC_SHORT:
case JniConstants.DESC_VOID:
types.add(nextType.toString());
nextType.setLength(0);
break;
case JniConstants.DESC_ARRAY:
// Nothing special to do.
break;
case JniConstants.DESC_REF:
inRef = true;
break;
default:
// Bad input.
return null;
}
}
}
return types.toArray(Empty.STRINGS);
}
private final String className;
private final String memberName;
private final String[] paramTypes;
private final String paramTypesString;
protected JsniRef(String className, String memberName,
String paramTypesString, String[] paramTypes) {
this.className = className;
this.memberName = memberName;
this.paramTypesString = paramTypesString;
this.paramTypes = paramTypes;
}
public String className() {
return className;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof JsniRef) && toString().equals(obj.toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
public boolean isField() {
return paramTypesString == null;
}
public boolean isMethod() {
return paramTypesString != null;
}
/**
* Whether this method reference matches all overloads of the specified class
* and method name. Only valid for method references.
*/
public boolean matchesAnyOverload() {
return paramTypesString.equals(WILDCARD_PARAM_LIST);
}
public String memberName() {
return memberName;
}
public String memberSignature() {
String ret = memberName;
if (isMethod()) {
ret += "(" + paramTypesString + ")";
}
return ret;
}
/**
* Return the list of parameter types for the method referred to by this
* reference. Only valid for method references where
* {@link #matchesAnyOverload()} is false.
*/
public String[] paramTypes() {
assert !matchesAnyOverload();
return paramTypes;
}
public String paramTypesString() {
return paramTypesString;
}
@Override
public String toString() {
return "@" + className + "::" + memberSignature();
}
}