/** * Copyright (c) 2010 Darmstadt University of Technology. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marcel Bruch - initial API and implementation. */ package org.eclipse.recommenders.utils.names; import static org.eclipse.recommenders.utils.Checks.*; import static org.eclipse.recommenders.utils.Throws.*; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.MapMaker; public class VmTypeName implements ITypeName { // These private fields need to be intialized before the public ones below. private static Map<String /* vmTypeName */, VmTypeName> index = new MapMaker().weakValues().makeMap(); private static final Pattern GENERICS_PATTERN = Pattern.compile("<[^<>]*>"); public static final VmTypeName OBJECT = VmTypeName.get("Ljava/lang/Object"); public static final VmTypeName JAVA_LANG_NULL_POINTER_EXCEPTION = VmTypeName.get("Ljava/lang/NullPointerException"); public static final VmTypeName JAVA_LANG_STRING = VmTypeName.get("Ljava/lang/String"); public static final VmTypeName JAVA_LANG_EXCEPTION_IN_INITIALIZER_ERROR = VmTypeName .get("Ljava/lang/ExceptionInInitializerError"); public static final VmTypeName STRING = VmTypeName.get("Ljava/lang/String"); public static final VmTypeName NULL = get("Lnull"); public static final VmTypeName BYTE = get("B"); public static final VmTypeName BOOLEAN = get("Z"); public static final VmTypeName CHAR = get("C"); public static final VmTypeName DOUBLE = get("D"); public static final VmTypeName FLOAT = get("F"); public static final VmTypeName INT = get("I"); public static final VmTypeName LONG = get("J"); public static final VmTypeName SHORT = get("S"); public static final VmTypeName VOID = get("V"); public static synchronized VmTypeName get(String typeName) { typeName = removeGenerics(typeName); VmTypeName res = index.get(typeName); if (res == null) { res = new VmTypeName(typeName); index.put(typeName, res); } return res; } private static String removeGenerics(String typeName) { int oldLength; do { oldLength = typeName.length(); Matcher matcher = GENERICS_PATTERN.matcher(typeName); typeName = matcher.replaceAll(""); } while (typeName.length() < oldLength); return typeName; } private String identifier; /** * @see #get(String) */ @VisibleForTesting protected VmTypeName(final String vmTypeName) { ensureIsNotNull(vmTypeName); ensureIsFalse(vmTypeName.length() == 0, "empty size for type name not permitted"); loop: for (int i = 0; i < vmTypeName.length(); i++) { switch (vmTypeName.charAt(i)) { case '[': continue; case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'V': case 'Z': case 'L': break loop; default: throwUnreachable("Invalid type name: " + vmTypeName); } } int off = 0; while (off < vmTypeName.length()) { final char c = vmTypeName.charAt(off); // '-' as in package-info.class if (c == '-' || c == '[' || c == '/' || c == '<' || c == '>' || Character.isJavaIdentifierPart(c)) { off++; continue; } throwIllegalArgumentException("Cannot parse '%s' as vm type name.", vmTypeName); break; } identifier = vmTypeName; } @Override public ITypeName getArrayBaseType() { ensureIsTrue(isArrayType(), "only array-types have a base type!"); int start = 0; while (identifier.charAt(++start) == '[') { // start counter gets increased } return get(identifier.substring(start)); } @Override public int getArrayDimensions() { int count = 0; int start = 0; while (identifier.charAt(start++) == '[') { count++; } return count; } @Override public String getClassName() { final int indexOf = identifier.lastIndexOf('/'); if (indexOf < 0 && !isPrimitiveType()) { return identifier.substring(1); } return identifier.substring(indexOf + 1); } @Override public String getIdentifier() { return identifier; } @Override public IPackageName getPackage() { final int lastSlash = identifier.lastIndexOf('/'); if (lastSlash == -1 || identifier.charAt(0) == '[') { return VmPackageName.DEFAULT_PACKAGE; } return VmPackageName.get(identifier.substring(1, lastSlash)); } @Override public boolean isAnonymousType() { return identifier.matches(".*\\$\\d+"); } @Override public boolean isArrayType() { return identifier.charAt(0) == '['; } @Override public boolean isDeclaredType() { return identifier.charAt(0) == 'L'; } @Override public boolean isNestedType() { return identifier.contains("$"); } @Override public boolean isPrimitiveType() { return !(isArrayType() || isDeclaredType()); } @Override public boolean isVoid() { return this == VOID; } @Override public int compareTo(final ITypeName o) { return getIdentifier().compareTo(o.getIdentifier()); } @Override public String toString() { return getIdentifier(); } @Override public IMethodName getDeclaringMethod() { ensureIsTrue(isNestedType(), "only valid on nested types"); final int lastPathSegmentSeparator = identifier.lastIndexOf('/'); final String path = identifier.substring(0, lastPathSegmentSeparator); final int bracket = path.lastIndexOf('('); final int methodSeparator = path.lastIndexOf('/', bracket); final String newFQName = path.substring(0, methodSeparator) + "." + path.substring(methodSeparator + 1); return VmMethodName.get(newFQName); } @Override public ITypeName getDeclaringType() { ensureIsTrue(isNestedType(), "only valid on nested types"); final int lastIndexOf = identifier.lastIndexOf('$'); final String declaringTypeName = identifier.substring(0, lastIndexOf); return get(declaringTypeName); } }