/** * 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.ensureIsNotNull; import static org.eclipse.recommenders.utils.Throws.throwUnreachable; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang3.StringUtils; import com.google.common.annotations.Beta; /** * * Contains utility methods for parsing and converting plain vm strings (or their corresponding {@link IName}s * respectively) to source strings. * */ public final class Names { private Names() { // Not meant to be instantiated } public static enum PrimitiveType { BOOLEAN('Z', "boolean"), VOID('V', "void"), CHAR('C', "char"), BYTE('B', "byte"), SHORT('S', "short"), INT('I', "int"), FLOAT('F', "float"), LONG('J', "long"), DOUBLE('D', "double"); public static PrimitiveType fromSrc(final String src) { ensureIsNotNull(src, "src"); // for (final PrimitiveType t : values()) { if (t.src.equals(src)) { return t; } } return null; } private final String src; private final char vm; private PrimitiveType(final char vm, final String src) { this.vm = vm; this.src = src; } public String src() { return src; } public char vm() { return vm; } } public static final int STYLE_SHORT = 0; public static final int STYLE_LONG = 1; public static final String DOUBLE = "double"; public static final String LONG = "long"; public static final String FLOAT = "float"; public static final String INT = "int"; public static final String SHORT = "short"; public static final String BYTE = "byte"; public static final String CHAR = "char"; public static final String BOOLEAN = "boolean"; public static final String VOID = "void"; /** * Given "X.method(LType1;[II)V" * <ol> * <li>return[0] = method name, i.e., "X.method" * <li>return [1..n-2] = parameter types, i.e., ["Type1", "int[]","int"] * <li>return[n-1] = return type, i.e., "void" * </ol> */ public static String[] parseMethodSignature1(final String methodSignature) { ensureIsNotNull(methodSignature, "s"); // // this is not high performance code but it might be ok for now. // final int lastDot = s.lastIndexOf('.'); final int openingBracket = methodSignature.indexOf('('); final ArrayList<String> res = new ArrayList<String>(); // res.add(getType(s.substring(0, lastDot).toCharArray(), 0)); // res.add(s.substring(lastDot + 1, openingBracket)); // method name res.add(methodSignature.substring(0, openingBracket)); // final char[] desc = methodSignature.substring(openingBracket).toCharArray(); int off = 1; while (true) { final char car = desc[off]; if (car == ')') { break; } switch (car) { case 'V': // not possible - right ? ;) case 'Z': case 'C': case 'B': case 'S': case 'I': case 'F': case 'J': case 'D': res.add(internal_vm2srcTypeName(desc, off++)); continue; case 'L': res.add(internal_vm2srcTypeName(desc, off)); off = internal_findEndOfObjectType(desc, off); continue; case '[': res.add(internal_vm2srcTypeName(desc, off)); while (desc[off] == '[') { off++; } if (desc[off] == 'L') { off = internal_findEndOfObjectType(desc, off); } else { off++; } } } res.add(internal_vm2srcTypeName(desc, off + 1)); return res.toArray(new String[res.size()]); } private static int internal_findEndOfObjectType(final char[] desc, final int off) { ensureIsNotNull(desc, "desc"); // for (int index = off; index < desc.length; index++) { if (desc[index] == ';') { return index + 1; } } return desc.length; } private static String internal_vm2srcTypeName(final char[] buf, final int off) { ensureIsNotNull(buf, "buf"); // int len; switch (buf[off]) { case 'V': return VOID; case 'Z': return BOOLEAN; case 'C': return CHAR; case 'B': return BYTE; case 'S': return SHORT; case 'I': return INT; case 'F': return FLOAT; case 'J': return LONG; case 'D': return DOUBLE; case '[': final StringBuilder sb = new StringBuilder(); sb.append("[]"); len = 1; while (buf[off + len] == '[') { sb.append("[]"); ++len; } sb.insert(0, internal_vm2srcTypeName(buf, off + len)); return sb.toString(); case 'L': len = 1; while (off + len < buf.length && buf[off + len] != ';') { ++len; } return new String(buf, off + 1, len - 1).replaceAll("/", ".").replaceAll("\\$", "."); default: throw throwUnreachable("couldn't handle '%s'", buf); } } /** * * @return new String[] { declaringType, methodNameAndDesciptor }; */ public static String[] parseMethodSignature2(final String vmMethodSignature) { final int indexOfDot = vmMethodSignature.indexOf('.'); final String declaringType = vmMethodSignature.substring(0, indexOfDot); final String methodNameAndDesciptor = vmMethodSignature.substring(indexOfDot + 1); return new String[] { declaringType, methodNameAndDesciptor }; } /** * @return new String[] { type, methodName, methodDescriptor } */ public static String[] parseMethodSignature3(final String vmMethodSignature) { final int lastDot = vmMethodSignature.lastIndexOf('.'); final int firstBracket = vmMethodSignature.lastIndexOf('('); final String type = vmMethodSignature.substring(0, lastDot); final String methodName = vmMethodSignature.substring(lastDot + 1, firstBracket); final String methodDescriptor = vmMethodSignature.substring(firstBracket); return new String[] { type, methodName, methodDescriptor }; } public static String src2vmMethod(final String srcDeclaringType, final String methodName, final String[] srcParameterTypes, final String srcReturnType) { final String vmDeclaringTypeName = src2vmType(srcDeclaringType); final String vmMethodName = src2vmMethod(methodName, srcParameterTypes, srcReturnType); return vmDeclaringTypeName + "." + vmMethodName; } public static String src2vmMethod(final String methodName, final String[] srcParameterTypes, final String srcReturnType) { // TODO this code is incomplete and does not work well with arrays! ensureIsNotNull(methodName, "methodName"); ensureIsNotNull(srcParameterTypes, "srcParameterTypes"); ensureIsNotNull(srcReturnType, "srcReturnType"); // final StringBuilder sb = new StringBuilder(); sb.append(methodName).append('('); for (final String srcType : srcParameterTypes) { final String vmType = src2vmType(srcType); sb.append(vmType); if (vmType.startsWith("L")) { sb.append(';'); } } String vmReturnType = src2vmType(srcReturnType); if (vmReturnType.startsWith("L")) { vmReturnType += ";"; } sb.append(')').append(vmReturnType); return sb.toString(); } public static String src2vmType(String type) { ensureIsNotNull(type, "type"); // int dimensions = 0; if (type.endsWith("]")) { dimensions = StringUtils.countMatches(type, "[]"); type = StringUtils.substringBefore(type, "[") + ";"; } final PrimitiveType p = PrimitiveType.fromSrc(StringUtils.substringBefore(type, ";")); if (p != null) { return StringUtils.repeat("[", dimensions) + p.vm(); } return StringUtils.repeat("[", dimensions) + "L" + type.replaceAll("\\.", "/"); } public static List<String> src2vmType(final String[] srcTypes) { ensureIsNotNull(srcTypes, "srcTypes"); // final LinkedList<String> res = new LinkedList<String>(); for (final String srcName : srcTypes) { res.add(src2vmType(srcName)); } return res; } public static String vm2srcPackage(final IPackageName pkg) { ensureIsNotNull(pkg, "pkg"); return pkg.getIdentifier().replace('/', '.'); } public static String vm2srcQualifiedMethod(final IMethodName method) { final StringBuilder sb = new StringBuilder(); final ITypeName declaringType = method.getDeclaringType(); sb.append(vm2srcQualifiedType(declaringType)); sb.append('.'); sb.append(method.getName()); sb.append('('); for (final ITypeName param : method.getParameterTypes()) { sb.append(vm2srcSimpleTypeName(param)).append(", "); } if (method.hasParameters()) { sb.setLength(sb.length() - 2); } sb.append(')'); return sb.toString(); } public static String vm2srcQualifiedType(final ITypeName type) { if (type.isPrimitiveType()) { return Names.vm2srcSimpleTypeName(type); } if (type.isArrayType()) { return vm2srcQualifiedType(type.getArrayBaseType()) + StringUtils.repeat("[]", type.getArrayDimensions()); } String s = type.getIdentifier(); s = s.replace('/', '.'); return s.substring(1); } /** * @return the <b><method name>(<parameter simple types...>)</b> - no return value. */ public static String vm2srcSimpleMethod(final IMethodName name) { ensureIsNotNull(name, "name"); final StringBuilder sb = new StringBuilder(); if (name.getName().equals("<subtype-init>")) { sb.append("ConstructorCallFromSubtype"); } else { sb.append(name.isInit() ? "new " + vm2srcSimpleTypeName(name.getDeclaringType()) : name.getName()); } // sb.append('('); for (final ITypeName param : name.getParameterTypes()) { sb.append(vm2srcSimpleTypeName(param)).append(", "); } if (name.getParameterTypes().length > 0) { sb.delete(sb.length() - 2, sb.length()); } // // sb.append(')'); return sb.toString(); } public static String vm2srcSimpleTypeName(final String vmTypeName) { ensureIsNotNull(vmTypeName, "vmTypeName"); // final String type = internal_vm2srcTypeName(vmTypeName.toCharArray(), 0); final int lastDot = type.lastIndexOf('.'); if (lastDot == -1) { return type; } else { return type.substring(lastDot + 1); } } public static String vm2srcSimpleTypeName(final ITypeName type) { if (type.isArrayType()) { final int arrayDimensions = type.getArrayDimensions(); final ITypeName arrayBaseType = type.getArrayBaseType(); final StringBuilder sb = new StringBuilder(); final String simpleBaseType = vm2srcSimpleTypeName(arrayBaseType); sb.append(simpleBaseType); for (int i = arrayDimensions; i-- > 0;) { sb.append("[]"); } return sb.toString(); } if (type.isPrimitiveType()) { if (type == VmTypeName.BOOLEAN) { return BOOLEAN; } else if (type == VmTypeName.BYTE) { return BYTE; } else if (type == VmTypeName.CHAR) { return CHAR; } else if (type == VmTypeName.DOUBLE) { return DOUBLE; } else if (type == VmTypeName.FLOAT) { return FLOAT; } else if (type == VmTypeName.INT) { return INT; } else if (type == VmTypeName.LONG) { return LONG; } else if (type == VmTypeName.VOID) { return VOID; } else if (type == VmTypeName.SHORT) { return SHORT; } } return type.getClassName(); } /** * Converts a VM type descriptor to its Java source name: * <ul> * <li>Ljava/lang/String --> java.lang.String * <li>I --> int * </ul> */ public static String vm2srcTypeName(final String vmTypeDescriptor) { ensureIsNotNull(vmTypeDescriptor, "vmTypeName"); // return internal_vm2srcTypeName(vmTypeDescriptor.toCharArray(), 0); } @Beta public static IAnnotation vmType2vmAnnotation(final ITypeName annotationType) { return VmAnnotation.get(annotationType); } public static ITypeName java2vmType(final Class<?> clazz) { final String vmName = src2vmType(clazz.getName()); return VmTypeName.get(vmName); } /** * Takes a (dot-based) type descriptor as used in JDT completion proposals and returns a standardized VM type * descriptor. * * @see #src2vmType(String) */ public static String jdt2vmType(String jdtTypeDescriptor) { ensureIsNotNull(jdtTypeDescriptor, "jdtTypeDescriptor"); String tmp = jdtTypeDescriptor; if (tmp.endsWith(";")) { tmp = StringUtils.removeStart(tmp, "L"); tmp = StringUtils.removeEnd(tmp, ";"); } return src2vmType(tmp); } }