/* * 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; } }