blob: 66868cbb7a4602541393b9338df2ed7dd49d213a [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 com.google.gwt.thirdparty.guava.common.base.Strings;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import java.util.List;
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);
int arrayDimensions = matcher.group(2).length() / 2;
String memberName = matcher.group(3);
String paramTypesString = null;
String[] paramTypes = null;
if (matcher.group(4) != null) {
paramTypesString = matcher.group(5);
if (!paramTypesString.equals(WILDCARD_PARAM_LIST)) {
paramTypes = computeParamTypes(paramTypesString);
if (paramTypes == null) {
return null;
}
}
}
return new JsniRef(className, arrayDimensions, memberName, paramTypesString, paramTypes);
}
private static String[] computeParamTypes(String paramTypesString) {
List<String> types = Lists.newArrayList();
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(StringInterner.get().intern(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(StringInterner.get().intern(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 String resolvedClassName;
private String resolvedNemberSignature;
private final String memberName;
private final String[] paramTypes;
private final String paramTypesString;
private final int arrayDimensions;
protected JsniRef(String className, int arrayDimensions, String memberName,
String paramTypesString, String[] paramTypes) {
this.className = className;
this.memberName = memberName;
this.arrayDimensions = arrayDimensions;
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;
}
public void setResolvedClassName(String resolvedClassName) {
this.resolvedClassName = StringInterner.get().intern(resolvedClassName);
}
public void setResolvedMemberWithSignature(String resolvedMemberSignature) {
this.resolvedNemberSignature = StringInterner.get().intern(resolvedMemberSignature);
}
public String getResolvedClassName() {
return resolvedClassName;
}
public String getFullResolvedClassName() {
return resolvedClassName == null ? null :
resolvedClassName + Strings.repeat("[]", arrayDimensions);
}
public String getResolvedReference() {
String fullResolvedClassName = getFullResolvedClassName();
return fullResolvedClassName == null || resolvedClassName == null ? null :
"@" + fullResolvedClassName + "::" + resolvedNemberSignature;
}
public String getResolvedMemberSignature() {
return resolvedNemberSignature;
}
public String fullClassName() {
return className + Strings.repeat("[]", arrayDimensions);
}
@Override
public String toString() {
return "@" + fullClassName() + "::" + memberSignature();
}
public boolean isArray() {
return arrayDimensions > 0;
}
public int getDimensions() {
return arrayDimensions;
}
}