/******************************************************************************* * Copyright (c) 2002 - 2006 IBM Corporation. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.wala.types; import java.util.Map; import com.ibm.wala.classLoader.Language; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.strings.ImmutableByteArray; import com.ibm.wala.util.strings.StringStuff; import com.ibm.wala.util.strings.UTF8Convert; /** * A method descriptor; something like: (Ljava/langString;)Ljava/lang/Class; * * Descriptors are canonical */ public final class Descriptor { /** * A mapping from Key -> Descriptor */ private static final Map<Key, Descriptor> map = HashMapFactory.make(); /** * key holds the logical value of this descriptor */ private final Key key; /** * @param parameters the parameters for a descriptor * @param returnType the return type * @return the canonical representative for this descriptor value */ public static Descriptor findOrCreate(TypeName[] parameters, TypeName returnType) { if (returnType == null) { throw new IllegalArgumentException("null returnType"); } if (parameters != null && parameters.length == 0) { parameters = null; } Key k = new Key(returnType, parameters); Descriptor result = map.get(k); if (result == null) { result = new Descriptor(k); map.put(k, result); } return result; } /** * @param b a byte array holding the string representation of this descriptor * @return the canonical representative for this descriptor value */ public static Descriptor findOrCreate(Language l, ImmutableByteArray b) throws IllegalArgumentException { TypeName returnType = StringStuff.parseForReturnTypeName(l, b); TypeName[] parameters = StringStuff.parseForParameterNames(l, b); Key k = new Key(returnType, parameters); Descriptor result = map.get(k); if (result == null) { result = new Descriptor(k); map.put(k, result); } return result; } public static Descriptor findOrCreate(ImmutableByteArray b) throws IllegalArgumentException { return findOrCreate(Language.JAVA, b); } /** * @param s string representation of this descriptor * @return the canonical representative for this descriptor value */ public static Descriptor findOrCreateUTF8(String s) throws IllegalArgumentException { return findOrCreateUTF8(Language.JAVA, s); } /** * @param s string representation of this descriptor * @return the canonical representative for this descriptor value */ public static Descriptor findOrCreateUTF8(Language l, String s) throws IllegalArgumentException { byte[] b = UTF8Convert.toUTF8(s); return findOrCreate(l, new ImmutableByteArray(b)); } /** * @param key "value" of this descriptor */ private Descriptor(Key key) { this.key = key; } @Override public boolean equals(Object obj) { return this == obj; } @Override public int hashCode() { return key.hashCode(); } @Override public String toString() { return key.toString(); } /** * @return a unicode string representation of this descriptor */ public String toUnicodeString() { return key.toUnicodeString(); } /** * @return the name of the return type of this descriptor */ public TypeName getReturnType() { return key.returnType; } /** * @return the type names for the parameters in this descriptor */ public TypeName[] getParameters() { return key.parameters; } /** * @return number of parameters in this descriptor */ public int getNumberOfParameters() { return key.parameters == null ? 0 : key.parameters.length; } /** * value that defines a descriptor: used to canonicalize instances */ private static class Key { final private TypeName returnType; final private TypeName[] parameters; final private int hashCode; // cached for efficiency Key(TypeName returnType, TypeName[] parameters) { this.returnType = returnType; this.parameters = parameters; if (parameters != null) { assert parameters.length > 0; } hashCode = computeHashCode(); } @Override public int hashCode() { return hashCode; } public int computeHashCode() { int result = returnType.hashCode() * 5309; if (parameters != null) { for (int i = 0; i < parameters.length; i++) { result += parameters[i].hashCode() * (5323 ^ i); } } return result; } @Override public boolean equals(Object obj) { assert obj instanceof Key; Key other = (Key) obj; if (!returnType.equals(other.returnType)) { return false; } if (parameters == null) { return (other.parameters == null) ? true : false; } if (other.parameters == null) { return false; } if (parameters.length != other.parameters.length) { return false; } for (int i = 0; i < parameters.length; i++) { if (!parameters[i].equals(other.parameters[i])) { return false; } } return true; } @Override public String toString() { StringBuffer result = new StringBuffer(); result.append("("); if (parameters != null) { for (int i = 0; i < parameters.length; i++) { TypeName p = parameters[i]; result.append(p); appendSemicolonIfNeeded(result, p); } } result.append(")"); result.append(returnType); appendSemicolonIfNeeded(result, returnType); return result.toString(); } public String toUnicodeString() { StringBuffer result = new StringBuffer(); result.append("("); if (parameters != null) { for (int i = 0; i < parameters.length; i++) { TypeName p = parameters[i]; result.append(p.toUnicodeString()); appendSemicolonIfNeeded(result, p); } } result.append(")"); result.append(returnType); appendSemicolonIfNeeded(result, returnType); return result.toString(); } private void appendSemicolonIfNeeded(StringBuffer result, TypeName p) { if (p.isArrayType()) { TypeName e = p.getInnermostElementType(); String x = e.toUnicodeString(); if (x.startsWith("L") || x.startsWith("P")) { result.append(";"); } } else { String x = p.toUnicodeString(); if (x.startsWith("L") || x.startsWith("P")) { result.append(";"); } } } } }