From 5d526c2acd23e04836ab96b119704291bca07cef Mon Sep 17 00:00:00 2001 From: Alain Magloire Date: Thu, 15 Jul 2004 18:27:57 +0000 Subject: [PATCH] 2004-07-15 Chris Wiebe Initial draft for the type hierarchy view. * browser/* --- core/org.eclipse.cdt.core/ChangeLog | 5 + .../cdt/core/browser/AllTypesCache.java | 58 +- .../eclipse/cdt/core/browser/ITypeInfo.java | 54 +- .../eclipse/cdt/core/browser/Signature.java | 1997 +++++++++++++++++ .../eclipse/cdt/core/browser/TypeInfo.java | 106 +- .../eclipse/cdt/core/browser/TypeUtil.java | 282 +++ .../typehierarchy/ChangeCollector.java | 451 ++++ .../browser/typehierarchy/ITypeHierarchy.java | 171 ++ .../ITypeHierarchyChangedListener.java | 29 + .../browser/typehierarchy/TypeHierarchy.java | 591 +++++ .../typehierarchy/TypeHierarchyBuilder.java | 129 ++ .../typehierarchy/TypeHierarchyMessages.java | 50 + .../TypeHierarchyMessages.properties | 17 + .../core/browser/cache/ITypeCache.java | 46 + .../core/browser/cache/IndexerTypesJob.java | 42 + .../core/browser/cache/SubTypeLocatorJob.java | 67 + .../core/browser/cache/TypeCache.java | 160 ++ .../core/browser/cache/TypeCacheManager.java | 55 +- .../cache/TypeCacheMessages.properties | 2 + .../core/browser/cache/TypeParser.java | 366 ++- .../cdt/internal/core/model/CElement.java | 2 +- 21 files changed, 4537 insertions(+), 143 deletions(-) create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/Signature.java create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeUtil.java create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ChangeCollector.java create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ITypeHierarchy.java create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ITypeHierarchyChangedListener.java create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchy.java create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyBuilder.java create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyMessages.java create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyMessages.properties create mode 100644 core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/SubTypeLocatorJob.java diff --git a/core/org.eclipse.cdt.core/ChangeLog b/core/org.eclipse.cdt.core/ChangeLog index 2026c6e5b89..6eb42969d5c 100644 --- a/core/org.eclipse.cdt.core/ChangeLog +++ b/core/org.eclipse.cdt.core/ChangeLog @@ -1,3 +1,8 @@ +2004-07-15 Chris Wiebe + + Initial draft for the type hierarchy view. + * browser/* + 2004-07-06 Bogdan Gheorghe Handled the case of CContainer in both updateIndexAddResource and updateIndexRemoveResource. diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/AllTypesCache.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/AllTypesCache.java index 2ba97fc17ff..f0dbf6ee405 100644 --- a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/AllTypesCache.java +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/AllTypesCache.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.Collection; import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy; +import org.eclipse.cdt.core.browser.typehierarchy.TypeHierarchyBuilder; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ElementChangedEvent; @@ -48,7 +50,7 @@ public class AllTypesCache { private static final int INITIAL_DELAY = 5000; private static IWorkingCopyProvider fgWorkingCopyProvider; - private static TypeCacheManager fgTypeCacheManager; + private static TypeHierarchyBuilder fgTypeHierarchyBuilder; private static IElementChangedListener fgElementChangedListener; private static IPropertyChangeListener fgPropertyChangeListener; private static boolean fgEnableIndexing = true; @@ -63,7 +65,8 @@ public class AllTypesCache { */ public static void initialize(IWorkingCopyProvider workingCopyProvider) { fgWorkingCopyProvider = workingCopyProvider; - fgTypeCacheManager = new TypeCacheManager(fgWorkingCopyProvider); + TypeCacheManager.getInstance().setWorkingCopyProvider(fgWorkingCopyProvider); + fgTypeHierarchyBuilder = new TypeHierarchyBuilder(); // load prefs Preferences prefs = CCorePlugin.getDefault().getPluginPreferences(); @@ -77,13 +80,13 @@ public class AllTypesCache { } // start jobs in background after INITIAL_DELAY - fgTypeCacheManager.reconcile(fgEnableIndexing, Job.BUILD, INITIAL_DELAY); + TypeCacheManager.getInstance().reconcile(fgEnableIndexing, Job.BUILD, INITIAL_DELAY); // add delta listener fgElementChangedListener = new IElementChangedListener() { public void elementChanged(ElementChangedEvent event) { - fgTypeCacheManager.processDelta(event.getDelta()); - fgTypeCacheManager.reconcile(fgEnableIndexing, Job.BUILD, 0); + TypeCacheManager.getInstance().processDelta(event.getDelta()); + TypeCacheManager.getInstance().reconcile(fgEnableIndexing, Job.BUILD, 0); } }; CoreModel.getDefault().addElementChangedListener(fgElementChangedListener); @@ -96,9 +99,9 @@ public class AllTypesCache { String value = (String) event.getNewValue(); fgEnableIndexing = Boolean.valueOf(value).booleanValue(); if (!fgEnableIndexing) { - fgTypeCacheManager.cancelJobs(); + TypeCacheManager.getInstance().cancelJobs(); } else { - fgTypeCacheManager.reconcile(fgEnableIndexing, Job.BUILD, 0); + TypeCacheManager.getInstance().reconcile(fgEnableIndexing, Job.BUILD, 0); } } } @@ -119,8 +122,8 @@ public class AllTypesCache { CCorePlugin.getDefault().getPluginPreferences().removePropertyChangeListener(fgPropertyChangeListener); // terminate all running jobs - if (fgTypeCacheManager != null) { - fgTypeCacheManager.cancelJobs(); + if (TypeCacheManager.getInstance() != null) { + TypeCacheManager.getInstance().cancelJobs(); } } @@ -139,7 +142,7 @@ public class AllTypesCache { public boolean shouldContinue() { return true; } }; for (int i = 0; i < projects.length; ++i) { - fgTypeCacheManager.getCache(projects[i]).accept(visitor); + TypeCacheManager.getInstance().getCache(projects[i]).accept(visitor); } return (ITypeInfo[]) fAllTypes.toArray(new ITypeInfo[fAllTypes.size()]); } @@ -167,7 +170,7 @@ public class AllTypesCache { public boolean shouldContinue() { return true; } }; for (int i = 0; i < projects.length; ++i) { - fgTypeCacheManager.getCache(projects[i]).accept(visitor); + TypeCacheManager.getInstance().getCache(projects[i]).accept(visitor); } return (ITypeInfo[]) fTypesFound.toArray(new ITypeInfo[fTypesFound.size()]); } @@ -198,7 +201,7 @@ public class AllTypesCache { public boolean shouldContinue() { return true; } }; for (int i = 0; i < projects.length; ++i) { - fgTypeCacheManager.getCache(projects[i]).accept(visitor); + TypeCacheManager.getInstance().getCache(projects[i]).accept(visitor); } return (ITypeInfo[]) fTypesFound.toArray(new ITypeInfo[fTypesFound.size()]); } @@ -224,7 +227,7 @@ public class AllTypesCache { public boolean shouldContinue() { return true; } }; for (int i = 0; i < projects.length; ++i) { - ITypeCache cache = fgTypeCacheManager.getCache(projects[i]); + ITypeCache cache = TypeCacheManager.getInstance().getCache(projects[i]); cache.accept(visitor); if (includeGlobalNamespace) { fTypesFound.add(cache.getGlobalNamespace()); @@ -243,7 +246,7 @@ public class AllTypesCache { for (int i = 0; i < projects.length; ++i) { IProject project = projects[i]; if (project.exists() && project.isOpen()) { - if (!fgTypeCacheManager.getCache(project).isUpToDate()) + if (!TypeCacheManager.getInstance().getCache(project).isUpToDate()) return false; } } @@ -277,7 +280,7 @@ public class AllTypesCache { for (int i = 0; i < projects.length; ++i) { IProject project = projects[i]; // wait for any running jobs to finish - fgTypeCacheManager.getCache(project).reconcileAndWait(true, Job.SHORT, monitor); + TypeCacheManager.getInstance().getCache(project).reconcileAndWait(true, Job.SHORT, monitor); } monitor.done(); } @@ -293,17 +296,36 @@ public class AllTypesCache { if (location == null) { // cancel background jobs IProject project = info.getEnclosingProject(); - fgTypeCacheManager.getCache(project).cancelJobs(); + TypeCacheManager.getInstance().getCache(project).cancelJobs(); // start the search job - fgTypeCacheManager.getCache(project).locateTypeAndWait(info, Job.SHORT, monitor); + TypeCacheManager.getInstance().getCache(project).locateTypeAndWait(info, Job.SHORT, monitor); // get the newly parsed location location = info.getResolvedReference(); // resume background jobs - fgTypeCacheManager.reconcile(fgEnableIndexing, Job.BUILD, 0); + TypeCacheManager.getInstance().reconcile(fgEnableIndexing, Job.BUILD, 0); } return location; } + + public static ITypeInfo getTypeForElement(ICElement elem) { + return TypeUtil.getTypeForElement(elem); + } + + /** + * Creates and returns a type hierarchy for this type containing + * this type and all of its supertypes and subtypes in the workspace. + * + * @param info the given type + * @param monitor the given progress monitor + * @return a type hierarchy for the given type + */ + public static ITypeHierarchy createTypeHierarchy(ICElement type, IProgressMonitor monitor) throws CModelException { + ITypeInfo info = TypeUtil.getTypeForElement(type); + if (info != null) + return fgTypeHierarchyBuilder.createTypeHierarchy(info, fgEnableIndexing, monitor); + return null; + } } diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeInfo.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeInfo.java index 64e4159d68e..9c1eb6aad5a 100644 --- a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeInfo.java +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeInfo.java @@ -11,6 +11,7 @@ package org.eclipse.cdt.core.browser; import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; import org.eclipse.cdt.internal.core.browser.cache.ITypeCache; import org.eclipse.core.resources.IProject; @@ -145,11 +146,6 @@ public interface ITypeInfo extends Comparable { */ public ITypeReference getResolvedReference(); - /** - * Returns the corresponding CElement or null if not found. - */ - public ICElement getCElement(); - /** * Returns true if the type can be substituted. */ @@ -157,4 +153,52 @@ public interface ITypeInfo extends Comparable { public ITypeCache getCache(); public void setCache(ITypeCache typeCache); + + /** + * Returns true if other types extend this type. + */ + public boolean hasSubTypes(); + + /** Gets all types which extend this type. + * @return array of types, or null if none found. + */ + public ITypeInfo[] getSubTypes(); + + /** + * Returns true if this type has base classes. + */ + public boolean hasSuperTypes(); + + /** Gets the base classes. + * @return array of types, or null if none found. + */ + public ITypeInfo[] getSuperTypes(); + + /** + * Gets the base class access visibility (PRIVATE, PROTECTED, PUBLIC) + */ + public ASTAccessVisibility getSuperTypeAccess(ITypeInfo subType); + + /** + * Adds a derived class reference, i.e. this type is used as + * a base class at the given location. + */ + public void addDerivedReference(ITypeReference location); + + /** Gets the originating locations where this type was + * used as a base class. + * @return all known source references, or an empty + * array if none found. + */ + public ITypeReference[] getDerivedReferences(); + + /** + * Returns true if the type is a class or struct. + */ + public boolean isClass(); + + /** + * Returns true if type is referenced in the given scope. + */ + public boolean isReferenced(ITypeSearchScope scope); } diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/Signature.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/Signature.java new file mode 100644 index 00000000000..316d3934892 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/Signature.java @@ -0,0 +1,1997 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * IBM Corporation - added J2SE 1.5 support + *******************************************************************************/ +package org.eclipse.cdt.core.browser; + +import org.eclipse.cdt.internal.core.CharOperation; + +//TODO move this class to CoreModel? + +/** + * Provides methods for encoding and decoding type and method signature strings. + *

+ * Signatures obtained from parsing source (".java") files differ subtly from + * ones obtained from pre-compiled binary (".class") files in class names are + * usually left unresolved in the former. For example, the normal resolved form + * of the type "String" embeds the class's package name ("Ljava.lang.String;" + * or "Ljava/lang/String;"), whereas the unresolved form contains only what is + * written "QString;". + *

+ *

+ * Generic types introduce to the Java language in J2SE 1.5 add three new + * facets to signatures: type variables, parameterized types with type arguments, + * and formal type parameters. Rich signatures containing these facets + * only occur when dealing with code that makes overt use of the new language + * features. All other code, and certainly all Java code written or compiled + * with J2SE 1.4 or earlier, involved only simple signatures. + *

+ *

+ * The syntax for a type signature is: + *

+ * TypeSignature ::=
+ *     "B"  // byte
+ *   | "C"  // char
+ *   | "D"  // double
+ *   | "F"  // float
+ *   | "I"  // int
+ *   | "J"  // long
+ *   | "S"  // short
+ *   | "V"  // void
+ *   | "Z"  // boolean
+ *   | "T" + Identifier + ";" // type variable
+ *   | "[" + TypeSignature  // array X[]
+ *   | ResolvedClassTypeSignature
+ *   | UnresolvedClassTypeSignature
+ * 
+ * ResolvedClassTypeSignature ::= // resolved named type (in compiled code)
+ *     "L" + Identifier + OptionalTypeArguments
+ *           ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
+ * 
+ * UnresolvedClassTypeSignature ::= // unresolved named type (in source code)
+ *     "Q" + Identifier + OptionalTypeArguments
+ *           ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
+ * 
+ * OptionalTypeArguments ::=
+ *     "<" + TypeArgument+ + ">" 
+ *   |
+ * 
+ * TypeArgument ::=
+ *   | TypeSignature
+ *   | "*" // wildcard ?
+ *   | "+" TypeSignature // wildcard ? extends X
+ *   | "-" TypeSignature // wildcard ? super X
+ * 
+ *

+ *

+ * Examples: + *

+ *

+ *

+ * The syntax for a method signature is: + *

+ * MethodSignature ::= "(" + ParamTypeSignature* + ")" + ReturnTypeSignature
+ * ParamTypeSignature ::= TypeSignature
+ * ReturnTypeSignature ::= TypeSignature
+ * 
+ *

+ * Examples: + *

+ *

+ *

+ * The syntax for a formal type parameter signature is: + *

+ * FormalTypeParameterSignature ::=
+ *     TypeVariableName + OptionalClassBound + InterfaceBound*
+ * TypeVariableName ::= Identifier
+ * OptionalClassBound ::=
+ *     ":"
+ *   | ":" + TypeSignature
+ * InterfaceBound ::= 
+ *     ":" + TypeSignature
+ * 
+ *

+ * Examples: + *

+ *

+ *

+ * This class provides static methods and constants only; it is not intended to be + * instantiated or subclassed by clients. + *

+ */ +public final class Signature { + + /** + * Character constant indicating the primitive type boolean in a signature. + * Value is 'Z'. + */ + public static final char C_BOOLEAN = 'Z'; + + /** + * Character constant indicating the primitive type byte in a signature. + * Value is 'B'. + */ + public static final char C_BYTE = 'B'; + + /** + * Character constant indicating the primitive type char in a signature. + * Value is 'C'. + */ + public static final char C_CHAR = 'C'; + + /** + * Character constant indicating the primitive type double in a signature. + * Value is 'D'. + */ + public static final char C_DOUBLE = 'D'; + + /** + * Character constant indicating the primitive type float in a signature. + * Value is 'F'. + */ + public static final char C_FLOAT = 'F'; + + /** + * Character constant indicating the primitive type int in a signature. + * Value is 'I'. + */ + public static final char C_INT = 'I'; + + /** + * Character constant indicating the semicolon in a signature. + * Value is ';'. + */ + public static final char C_SEMICOLON = ';'; + + /** + * Character constant indicating the colon in a signature. + * Value is ':'. + * @since 3.0 + */ + public static final char C_COLON = ':'; + + /** + * Character constant indicating the primitive type long in a signature. + * Value is 'J'. + */ + public static final char C_LONG = 'J'; + + /** + * Character constant indicating the primitive type short in a signature. + * Value is 'S'. + */ + public static final char C_SHORT = 'S'; + + /** + * Character constant indicating result type void in a signature. + * Value is 'V'. + */ + public static final char C_VOID = 'V'; + + /** + * Character constant indicating result const in a signature. + * Value is 'K'. + */ + public static final char C_CONST = 'K'; + + /** + * Character constant indicating the start of a resolved type variable in a + * signature. Value is 'T'. + * @since 3.0 + */ + public static final char C_TYPE_VARIABLE = 'T'; + + /** + * Character constant indicating a wildcard type argument + * in a signature. + * Value is '*'. + * @since 3.0 + */ + public static final char C_STAR = '*'; + + /** + * Character constant indicating the dot in a signature. + * Value is '.'. + */ + public static final char C_DOT = '.'; + + /** + * Character constant indicating the dollar in a signature. + * Value is '$'. + */ + public static final char C_DOLLAR = '$'; + + /** + * Character constant indicating an array type in a signature. + * Value is '['. + */ + public static final char C_ARRAY = '['; + + /** + * Character constant indicating the start of a resolved, named type in a + * signature. Value is 'L'. + */ + public static final char C_RESOLVED = 'L'; + + /** + * Character constant indicating the start of an unresolved, named type in a + * signature. Value is 'Q'. + */ + public static final char C_UNRESOLVED = 'Q'; + + /** + * Character constant indicating the end of a named type in a signature. + * Value is ';'. + */ + public static final char C_NAME_END = ';'; + + /** + * Character constant indicating the start of a parameter type list in a + * signature. Value is '('. + */ + public static final char C_PARAM_START = '('; + + /** + * Character constant indicating the end of a parameter type list in a + * signature. Value is ')'. + */ + public static final char C_PARAM_END = ')'; + + /** + * Character constant indicating the start of a formal type parameter + * (or type argument) list in a signature. Value is '<'. + * @since 3.0 + */ + public static final char C_GENERIC_START = '<'; + + /** + * Character constant indicating the end of a generic type list in a + * signature. Value is '%gt;'. + * @since 3.0 + */ + public static final char C_GENERIC_END = '>'; + + /** + * String constant for the signature of the primitive type boolean. + * Value is "Z". + */ + public static final String SIG_BOOLEAN = "Z"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type byte. + * Value is "B". + */ + public static final String SIG_BYTE = "B"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type char. + * Value is "C". + */ + public static final String SIG_CHAR = "C"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type double. + * Value is "D". + */ + public static final String SIG_DOUBLE = "D"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type float. + * Value is "F". + */ + public static final String SIG_FLOAT = "F"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type int. + * Value is "I". + */ + public static final String SIG_INT = "I"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type long. + * Value is "J". + */ + public static final String SIG_LONG = "J"; //$NON-NLS-1$ + + /** + * String constant for the signature of the primitive type short. + * Value is "S". + */ + public static final String SIG_SHORT = "S"; //$NON-NLS-1$ + + /** String constant for the signature of result type void. + * Value is "V". + */ + public static final String SIG_VOID = "V"; //$NON-NLS-1$ + + + /** + * Kind constant for a class type signature. + * @see #getTypeSignatureKind(String) + * @since 3.0 + */ + public static int CLASS_TYPE_SIGNATURE = 1; + + /** + * Kind constant for a base (primitive or void) type signature. + * @see #getTypeSignatureKind(String) + * @since 3.0 + */ + public static int BASE_TYPE_SIGNATURE = 2; + + /** + * Kind constant for a type variable signature. + * @see #getTypeSignatureKind(String) + * @since 3.0 + */ + public static int TYPE_VARIABLE_SIGNATURE = 3; + + /** + * Kind constant for an array type signature. + * @see #getTypeSignatureKind(String) + * @since 3.0 + */ + public static int ARRAY_TYPE_SIGNATURE = 4; + + private static final char[] BOOLEAN = {'b', 'o', 'o', 'l', 'e', 'a', 'n'}; + private static final char[] BYTE = {'b', 'y', 't', 'e'}; + private static final char[] CHAR = {'c', 'h', 'a', 'r'}; + private static final char[] DOUBLE = {'d', 'o', 'u', 'b', 'l', 'e'}; + private static final char[] FLOAT = {'f', 'l', 'o', 'a', 't'}; + private static final char[] INT = {'i', 'n', 't'}; + private static final char[] LONG = {'l', 'o', 'n', 'g'}; + private static final char[] SHORT = {'s', 'h', 'o', 'r', 't'}; + private static final char[] VOID = {'v', 'o', 'i', 'd'}; + private static final char[] CONST = {'c', 'o', 'n', 's', 't'}; + + private static final String EMPTY = new String(CharOperation.NO_CHAR); + +private Signature() { + // Not instantiable +} + +private static boolean checkPrimitiveType(char[] primitiveTypeName, char[] typeName) { + return CharOperation.fragmentEquals(primitiveTypeName, typeName, 0, true) && + (typeName.length == primitiveTypeName.length + || Character.isWhitespace(typeName[primitiveTypeName.length]) + || typeName[primitiveTypeName.length] == C_ARRAY + || typeName[primitiveTypeName.length] == C_DOT); +} + +/** + * Creates a new type signature with the given amount of array nesting added + * to the given type signature. + * + * @param typeSignature the type signature + * @param arrayCount the desired number of levels of array nesting + * @return the encoded array type signature + * + * @since 2.0 + */ +public static char[] createArraySignature(char[] typeSignature, int arrayCount) { + if (arrayCount == 0) return typeSignature; + int sigLength = typeSignature.length; + char[] result = new char[arrayCount + sigLength]; + for (int i = 0; i < arrayCount; i++) { + result[i] = C_ARRAY; + } + System.arraycopy(typeSignature, 0, result, arrayCount, sigLength); + return result; +} +/** + * Creates a new type signature with the given amount of array nesting added + * to the given type signature. + * + * @param typeSignature the type signature + * @param arrayCount the desired number of levels of array nesting + * @return the encoded array type signature + */ +public static String createArraySignature(String typeSignature, int arrayCount) { + return new String(createArraySignature(typeSignature.toCharArray(), arrayCount)); +} + +/** + * Creates a method signature from the given parameter and return type + * signatures. The encoded method signature is dot-based. + * + * @param parameterTypes the list of parameter type signatures + * @param returnType the return type signature + * @return the encoded method signature + * + * @since 2.0 + */ +public static char[] createMethodSignature(char[][] parameterTypes, char[] returnType) { + int parameterTypesLength = parameterTypes.length; + int parameterLength = 0; + for (int i = 0; i < parameterTypesLength; i++) { + parameterLength += parameterTypes[i].length; + + } + int returnTypeLength = returnType.length; + char[] result = new char[1 + parameterLength + 1 + returnTypeLength]; + result[0] = C_PARAM_START; + int index = 1; + for (int i = 0; i < parameterTypesLength; i++) { + char[] parameterType = parameterTypes[i]; + int length = parameterType.length; + System.arraycopy(parameterType, 0, result, index, length); + index += length; + } + result[index] = C_PARAM_END; + System.arraycopy(returnType, 0, result, index+1, returnTypeLength); + return result; +} + +/** + * Creates a method signature from the given parameter and return type + * signatures. The encoded method signature is dot-based. This method + * is equivalent to + * createMethodSignature(parameterTypes, returnType). + * + * @param parameterTypes the list of parameter type signatures + * @param returnType the return type signature + * @return the encoded method signature + * @see Signature#createMethodSignature(char[][], char[]) + */ +public static String createMethodSignature(String[] parameterTypes, String returnType) { + int parameterTypesLenth = parameterTypes.length; + char[][] parameters = new char[parameterTypesLenth][]; + for (int i = 0; i < parameterTypesLenth; i++) { + parameters[i] = parameterTypes[i].toCharArray(); + } + return new String(createMethodSignature(parameters, returnType.toCharArray())); +} + +/** + * Creates a new type signature from the given type name encoded as a character + * array. The type name may contain primitive types or array types. However, + * parameterized types are not supported. + * This method is equivalent to + * createTypeSignature(new String(typeName),isResolved), although + * more efficient for callers with character arrays rather than strings. If the + * type name is qualified, then it is expected to be dot-based. + * + * @param typeName the possibly qualified type name + * @param isResolved true if the type name is to be considered + * resolved (for example, a type name from a binary class file), and + * false if the type name is to be considered unresolved + * (for example, a type name found in source code) + * @return the encoded type signature + * @see #createTypeSignature(java.lang.String,boolean) + */ +public static String createTypeSignature(char[] typeName, boolean isResolved) { + return new String(createCharArrayTypeSignature(typeName, isResolved)); +} +/** + * Creates a new type signature from the given type name encoded as a character + * array. The type name may contain primitive types or array types. However, + * parameterized types are not supported. + * This method is equivalent to + * createTypeSignature(new String(typeName),isResolved).toCharArray(), + * although more efficient for callers with character arrays rather than strings. + * If the type name is qualified, then it is expected to be dot-based. + * + * @param typeName the possibly qualified type name + * @param isResolved true if the type name is to be considered + * resolved (for example, a type name from a binary class file), and + * false if the type name is to be considered unresolved + * (for example, a type name found in source code) + * @return the encoded type signature + * @see #createTypeSignature(java.lang.String,boolean) + * + * @since 2.0 + */ +public static char[] createCharArrayTypeSignature(char[] typeName, boolean isResolved) { + if (typeName == null) throw new IllegalArgumentException("null"); //$NON-NLS-1$ + int length = typeName.length; + if (length == 0) throw new IllegalArgumentException(new String(typeName)); + + int arrayCount = CharOperation.occurencesOf('[', typeName); + char[] sig; + + switch (typeName[0]) { + // primitive type? + case 'b' : + if (checkPrimitiveType(BOOLEAN, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_BOOLEAN; + break; + } else if (checkPrimitiveType(BYTE, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_BYTE; + break; + } + case 'c': + if (checkPrimitiveType(CHAR, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_CHAR; + break; + } + case 'd': + if (checkPrimitiveType(DOUBLE, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_DOUBLE; + break; + } + case 'f': + if (checkPrimitiveType(FLOAT, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_FLOAT; + break; + } + case 'i': + if (checkPrimitiveType(INT, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_INT; + break; + } + case 'l': + if (checkPrimitiveType(LONG, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_LONG; + break; + } + case 's': + if (checkPrimitiveType(SHORT, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_SHORT; + break; + } + case 'v': + if (checkPrimitiveType(VOID, typeName)) { + sig = new char[arrayCount+1]; + sig[arrayCount] = C_VOID; + break; + } + default: + // non primitive type + int sigLength = arrayCount + 1 + length + 1; // for example '[[[Ljava.lang.String;' + sig = new char[sigLength]; + int sigIndex = arrayCount+1; // index in sig + int startID = 0; // start of current ID in typeName + int index = 0; // index in typeName + while (index < length) { + char currentChar = typeName[index]; + switch (currentChar) { + case '.': + if (startID == -1) throw new IllegalArgumentException(new String(typeName)); + if (startID < index) { + sig = CharOperation.append(sig, sigIndex, typeName, startID, index); + sigIndex += index-startID; + } + sig[sigIndex++] = C_DOT; + index++; + startID = index; + break; + case '[': + if (startID != -1) { + if (startID < index) { + sig = CharOperation.append(sig, sigIndex, typeName, startID, index); + sigIndex += index-startID; + } + startID = -1; // no more id after [] + } + index++; + break; + default : + if (startID != -1 && CharOperation.isWhitespace(currentChar)) { + if (startID < index) { + sig = CharOperation.append(sig, sigIndex, typeName, startID, index); + sigIndex += index-startID; + } + startID = index+1; + } + index++; + break; + } + } + // last id + if (startID != -1 && startID < index) { + sig = CharOperation.append(sig, sigIndex, typeName, startID, index); + sigIndex += index-startID; + } + + // add L (or Q) at the beigininig and ; at the end + sig[arrayCount] = isResolved ? C_RESOLVED : C_UNRESOLVED; + sig[sigIndex++] = C_NAME_END; + + // resize if needed + if (sigLength > sigIndex) { + System.arraycopy(sig, 0, sig = new char[sigIndex], 0, sigIndex); + } + } + + // add array info + for (int i = 0; i < arrayCount; i++) { + sig[i] = C_ARRAY; + } + + return sig; +} +/** + * Creates a new type signature from the given type name. If the type name is qualified, + * then it is expected to be dot-based. The type name may contain primitive + * types or array types. However, parameterized types are not supported. + *

+ * For example: + *

+ * 
+ * createTypeSignature("int", hucairz) -> "I"
+ * createTypeSignature("java.lang.String", true) -> "Ljava.lang.String;"
+ * createTypeSignature("String", false) -> "QString;"
+ * createTypeSignature("java.lang.String", false) -> "Qjava.lang.String;"
+ * createTypeSignature("int []", false) -> "[I"
+ * 
+ * 
+ *

+ * + * @param typeName the possibly qualified type name + * @param isResolved true if the type name is to be considered + * resolved (for example, a type name from a binary class file), and + * false if the type name is to be considered unresolved + * (for example, a type name found in source code) + * @return the encoded type signature + */ +public static String createTypeSignature(String typeName, boolean isResolved) { + return createTypeSignature(typeName == null ? null : typeName.toCharArray(), isResolved); +} + +/** + * Returns the array count (array nesting depth) of the given type signature. + * + * @param typeSignature the type signature + * @return the array nesting depth, or 0 if not an array + * @exception IllegalArgumentException if the signature is not syntactically + * correct + * + * @since 2.0 + */ +public static int getArrayCount(char[] typeSignature) throws IllegalArgumentException { + try { + int count = 0; + while (typeSignature[count] == C_ARRAY) { + ++count; + } + return count; + } catch (ArrayIndexOutOfBoundsException e) { // signature is syntactically incorrect if last character is C_ARRAY + throw new IllegalArgumentException(); + } +} +/** + * Returns the array count (array nesting depth) of the given type signature. + * + * @param typeSignature the type signature + * @return the array nesting depth, or 0 if not an array + * @exception IllegalArgumentException if the signature is not syntactically + * correct + */ +public static int getArrayCount(String typeSignature) throws IllegalArgumentException { + return getArrayCount(typeSignature.toCharArray()); +} +/** + * Returns the type signature without any array nesting. + *

+ * For example: + *

+ * 
+ * getElementType({'[', '[', 'I'}) --> {'I'}.
+ * 
+ * 
+ *

+ * + * @param typeSignature the type signature + * @return the type signature without arrays + * @exception IllegalArgumentException if the signature is not syntactically + * correct + * + * @since 2.0 + */ +public static char[] getElementType(char[] typeSignature) throws IllegalArgumentException { + int count = getArrayCount(typeSignature); + if (count == 0) return typeSignature; + int length = typeSignature.length; + char[] result = new char[length-count]; + System.arraycopy(typeSignature, count, result, 0, length-count); + return result; +} +/** + * Returns the type signature without any array nesting. + *

+ * For example: + *

+ * 
+ * getElementType("[[I") --> "I".
+ * 
+ * 
+ *

+ * + * @param typeSignature the type signature + * @return the type signature without arrays + * @exception IllegalArgumentException if the signature is not syntactically + * correct + */ +public static String getElementType(String typeSignature) throws IllegalArgumentException { + return new String(getElementType(typeSignature.toCharArray())); +} +/** + * Returns the number of parameter types in the given method signature. + * + * @param methodSignature the method signature + * @return the number of parameters + * @exception IllegalArgumentException if the signature is not syntactically + * correct + * @since 2.0 + */ +public static int getParameterCount(char[] methodSignature) throws IllegalArgumentException { + try { + int count = 0; + int i = CharOperation.indexOf(C_PARAM_START, methodSignature); + if (i < 0) { + throw new IllegalArgumentException(); + } else { + i++; + } + for (;;) { + if (methodSignature[i] == C_PARAM_END) { + return count; + } + int e= scanTypeSignature(methodSignature, i); + if (e < 0) { + throw new IllegalArgumentException(); + } else { + i = e + 1; + } + count++; + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException(); + } +} + +/** + * Returns the kind of type signature encoded by the given string. + * + * @param typeSignature the type signature string + * @return the kind of type signature; one of the kind constants: + * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE}, + * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE} + * @exception IllegalArgumentException if this is not a type signature + * @since 3.0 + */ +public static int getTypeSignatureKind(char[] typeSignature) { + // need a minimum 1 char + if (typeSignature.length < 1) { + throw new IllegalArgumentException(); + } + char c = typeSignature[0]; + switch (c) { + case C_ARRAY : + return ARRAY_TYPE_SIGNATURE; + case C_RESOLVED : + case C_UNRESOLVED : + return CLASS_TYPE_SIGNATURE; + case C_TYPE_VARIABLE : + return TYPE_VARIABLE_SIGNATURE; + case C_BOOLEAN : + case C_BYTE : + case C_CHAR : + case C_DOUBLE : + case C_FLOAT : + case C_INT : + case C_LONG : + case C_SHORT : + case C_VOID : + return BASE_TYPE_SIGNATURE; + default : + throw new IllegalArgumentException(); + } +} + +/** + * Returns the kind of type signature encoded by the given string. + * + * @param typeSignature the type signature string + * @return the kind of type signature; one of the kind constants: + * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE}, + * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE} + * @exception IllegalArgumentException if this is not a type signature + * @since 3.0 + */ +public static int getTypeSignatureKind(String typeSignature) { + // need a minimum 1 char + if (typeSignature.length() < 1) { + throw new IllegalArgumentException(); + } + char c = typeSignature.charAt(0); + switch (c) { + case C_ARRAY : + return ARRAY_TYPE_SIGNATURE; + case C_RESOLVED : + case C_UNRESOLVED : + return CLASS_TYPE_SIGNATURE; + case C_TYPE_VARIABLE : + return TYPE_VARIABLE_SIGNATURE; + case C_BOOLEAN : + case C_BYTE : + case C_CHAR : + case C_DOUBLE : + case C_FLOAT : + case C_INT : + case C_LONG : + case C_SHORT : + case C_VOID : + return BASE_TYPE_SIGNATURE; + default : + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for a type signature starting at the given index + * and returns the index of the last character. + *
+ * TypeSignature:
+ *  |  BaseTypeSignature
+ *  |  ArrayTypeSignature
+ *  |  ClassTypeSignature
+ *  |  TypeVariableSignature
+ * 
+ * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type signature + * @see #appendTypeSignature(char[], int, boolean, StringBuffer) + */ +private static int scanTypeSignature(char[] string, int start) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + switch (c) { + case C_ARRAY : + return scanArrayTypeSignature(string, start); + case C_RESOLVED : + case C_UNRESOLVED : + return scanClassTypeSignature(string, start); + case C_TYPE_VARIABLE : + return scanTypeVariableSignature(string, start); + case C_BOOLEAN : + case C_BYTE : + case C_CHAR : + case C_DOUBLE : + case C_FLOAT : + case C_INT : + case C_LONG : + case C_SHORT : + case C_VOID : + return scanBaseTypeSignature(string, start); + default : + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for a base type signature starting at the given index + * and returns the index of the last character. + *
+ * BaseTypeSignature:
+ *     B | C | D | F | I
+ *   | J | S | V | Z
+ * 
+ * Note that although the base type "V" is only allowed in method return types, + * there is no syntactic ambiguity. This method will accept them anywhere + * without complaint. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a base type signature + */ +private static int scanBaseTypeSignature(char[] string, int start) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$ + return start; + } else { + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for an array type signature starting at the given + * index and returns the index of the last character. + *
+ * ArrayTypeSignature:
+ *     [ TypeSignature
+ * 
+ * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not an array type signature + * @see #appendArrayTypeSignature(char[], int, boolean, StringBuffer) + */ +private static int scanArrayTypeSignature(char[] string, int start) { + // need a minimum 2 char + if (start >= string.length - 1) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c != C_ARRAY) { //$NON-NLS-1$ + throw new IllegalArgumentException(); + } + return scanTypeSignature(string, start + 1); +} + +/** + * Scans the given string for a type variable signature starting at the given + * index and returns the index of the last character. + *
+ * TypeVariableSignature:
+ *     T Identifier ;
+ * 
+ * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type variable signature + */ +private static int scanTypeVariableSignature(char[] string, int start) { + // need a minimum 3 chars "Tx;" + if (start >= string.length - 2) { + throw new IllegalArgumentException(); + } + // must start in "T" + char c = string[start]; + if (c != C_TYPE_VARIABLE) { + throw new IllegalArgumentException(); + } + int id = scanIdentifier(string, start + 1); + c = string[id + 1]; + if (c == C_SEMICOLON) { + return id + 1; + } else { + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for an identifier starting at the given + * index and returns the index of the last character. + * Stop characters are: ";", ":", "<", ">", "/", ".". + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not an identifier + */ +private static int scanIdentifier(char[] string, int start) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + int p = start; + while (true) { + char c = string[p]; + if (c == '<' || c == '>' || c == ':' || c == ';' || c == '.' || c == '/') { + return p - 1; + } + p++; + if (p == string.length) { + return p - 1; + } + } +} + +/** + * Scans the given string for a class type signature starting at the given + * index and returns the index of the last character. + *
+ * ClassTypeSignature:
+ *     { L | Q } Identifier
+ *           { { / | . Identifier [ < TypeArgumentSignature* > ] }
+ *           ;
+ * 
+ * Note that although all "/"-identifiers most come before "."-identifiers, + * there is no syntactic ambiguity. This method will accept them without + * complaint. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a class type signature + * @see #appendClassTypeSignature(char[], int, boolean, StringBuffer) + */ +private static int scanClassTypeSignature(char[] string, int start) { + // need a minimum 3 chars "Lx;" + if (start >= string.length - 2) { + throw new IllegalArgumentException(); + } + // must start in "L" or "Q" + char c = string[start]; + if (c != C_RESOLVED && c != C_UNRESOLVED) { + return -1; + } + int p = start + 1; + while (true) { + if (p >= string.length) { + throw new IllegalArgumentException(); + } + c = string[p]; + if (c == C_SEMICOLON) { + // all done + return p; + } else if (c == C_GENERIC_START) { + int e = scanTypeArgumentSignatures(string, p); + p = e; + } else if (c == C_DOT || c == '/') { + int id = scanIdentifier(string, p + 1); + p = id; + } + p++; + } +} + +/** + * Scans the given string for a list of type argument signatures starting at + * the given index and returns the index of the last character. + *
+ * TypeArgumentSignatures:
+ *     < TypeArgumentSignature* >
+ * 
+ * Note that although there is supposed to be at least one type argument, there + * is no syntactic ambiguity if there are none. This method will accept zero + * type argument signatures without complaint. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a list of type arguments + * signatures + * @see #appendTypeArgumentSignatures(char[], int, boolean, StringBuffer) + */ +private static int scanTypeArgumentSignatures(char[] string, int start) { + // need a minimum 2 char "<>" + if (start >= string.length - 1) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c != C_GENERIC_START) { + throw new IllegalArgumentException(); + } + int p = start + 1; + while (true) { + if (p >= string.length) { + throw new IllegalArgumentException(); + } + c = string[p]; + if (c == C_GENERIC_END) { + return p; + } + int e = scanTypeArgumentSignature(string, p); + p = e + 1; + } +} + +/** + * Scans the given string for a type argument signature starting at the given + * index and returns the index of the last character. + *
+ * TypeArgumentSignature:
+ *     *
+ *  |  + TypeSignature
+ *  |  - TypeSignature
+ *  |  TypeSignature
+ * 
+ * Note that although base types are not allowed in type arguments, there is + * no syntactic ambiguity. This method will accept them without complaint. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type argument signature + * @see #appendTypeArgumentSignature(char[], int, boolean, StringBuffer) + */ +private static int scanTypeArgumentSignature(char[] string, int start) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c == C_STAR) { + return start; + } + if (c == '+' || c == '-') { + return scanTypeSignature(string, start + 1); + } else { + return scanTypeSignature(string, start); + } +} + +/** + * Returns the number of parameter types in the given method signature. + * + * @param methodSignature the method signature + * @return the number of parameters + * @exception IllegalArgumentException if the signature is not syntactically + * correct + */ +public static int getParameterCount(String methodSignature) throws IllegalArgumentException { + return getParameterCount(methodSignature.toCharArray()); +} +/** + * Extracts the parameter type signatures from the given method signature. + * The method signature is expected to be dot-based. + * + * @param methodSignature the method signature + * @return the list of parameter type signatures + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * + * @since 2.0 + */ +public static char[][] getParameterTypes(char[] methodSignature) throws IllegalArgumentException { + try { + int count = getParameterCount(methodSignature); + char[][] result = new char[count][]; + if (count == 0) { + return result; + } + int i = CharOperation.indexOf(C_PARAM_START, methodSignature); + if (i < 0) { + throw new IllegalArgumentException(); + } else { + i++; + } + int t = 0; + for (;;) { + if (methodSignature[i] == C_PARAM_END) { + return result; + } + int e = scanTypeSignature(methodSignature, i); + if (e < 0) { + throw new IllegalArgumentException(); + } + result[t] = CharOperation.subarray(methodSignature, i, e + 1); + t++; + i = e + 1; + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException(); + } +} +/** + * Extracts the parameter type signatures from the given method signature. + * The method signature is expected to be dot-based. + * + * @param methodSignature the method signature + * @return the list of parameter type signatures + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + */ +public static String[] getParameterTypes(String methodSignature) throws IllegalArgumentException { + char[][] parameterTypes = getParameterTypes(methodSignature.toCharArray()); + int length = parameterTypes.length; + String[] result = new String[length]; + for (int i = 0; i < length; i++) { + result[i] = new String(parameterTypes[i]); + } + return result; +} + +/** + * Extracts the type variable name from the given formal type parameter + * signature. The signature is expected to be dot-based. + * + * @param formalTypeParameterSignature the formal type parameter signature + * @return the name of the type variable + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * @since 3.0 + */ +public static String getTypeVariable(String formalTypeParameterSignature) throws IllegalArgumentException { + return new String(getTypeVariable(formalTypeParameterSignature.toCharArray())); +} + +/** + * Extracts the type variable name from the given formal type parameter + * signature. The signature is expected to be dot-based. + * + * @param formalTypeParameterSignature the formal type parameter signature + * @return the name of the type variable + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * @since 3.0 + */ +public static char[] getTypeVariable(char[] formalTypeParameterSignature) throws IllegalArgumentException { + int p = CharOperation.indexOf(C_COLON, formalTypeParameterSignature); + if (p < 0) { + // no ":" means can't be a formal type parameter signature + throw new IllegalArgumentException(); + } + return CharOperation.subarray(formalTypeParameterSignature, 0, p); +} + +/** + * Extracts the class and interface bounds from the given formal type + * parameter signature. The class bound, if present, is listed before + * the interface bounds. The signature is expected to be dot-based. + * + * @param formalTypeParameterSignature the formal type parameter signature + * @return the (possibly empty) list of type signatures for the bounds + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * @since 3.0 + */ +public static char[][] getTypeParameterBounds(char[] formalTypeParameterSignature) throws IllegalArgumentException { + int p1 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature); + if (p1 < 0) { + // no ":" means can't be a formal type parameter signature + throw new IllegalArgumentException(); + } + if (p1 == formalTypeParameterSignature.length - 1) { + // no class or interface bounds + return CharOperation.NO_CHAR_CHAR; + } + int p2 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature, p1 + 1); + char[] classBound; + if (p2 < 0) { + // no interface bounds + classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, formalTypeParameterSignature.length); + return new char[][] {classBound}; + } + if (p2 == p1 + 1) { + // no class bound, but 1 or more interface bounds + classBound = null; + } else { + classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, p2); + } + char[][] interfaceBounds = CharOperation.splitOn(C_COLON, formalTypeParameterSignature, p2 + 1, formalTypeParameterSignature.length); + if (classBound == null) { + return interfaceBounds; + } + int resultLength = interfaceBounds.length + 1; + char[][] result = new char[resultLength][]; + result[0] = classBound; + System.arraycopy(interfaceBounds, 0, result, 1, interfaceBounds.length); + return result; +} + +/** + * Extracts the class and interface bounds from the given formal type + * parameter signature. The class bound, if present, is listed before + * the interface bounds. The signature is expected to be dot-based. + * + * @param formalTypeParameterSignature the formal type parameter signature + * @return the (possibly empty) list of type signatures for the bounds + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * @since 3.0 + */ +public static String[] getTypeParameterBounds(String formalTypeParameterSignature) throws IllegalArgumentException { + char[][] bounds = getTypeParameterBounds(formalTypeParameterSignature.toCharArray()); + int length = bounds.length; + String[] result = new String[length]; + for (int i = 0; i < length; i++) { + result[i] = new String(bounds[i]); + } + return result; +} + +/** + * Returns a char array containing all but the last segment of the given + * dot-separated qualified name. Returns the empty char array if it is not qualified. + *

+ * For example: + *

+ * 
+ * getQualifier({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g'}
+ * getQualifier({'O', 'u', 't', 'e', 'r', '.', 'I', 'n', 'n', 'e', 'r'}) -> {'O', 'u', 't', 'e', 'r'}
+ * 
+ * 
+ *

+ * + * @param name the name + * @return the qualifier prefix, or the empty char array if the name contains no + * dots + * @exception NullPointerException if name is null + * @since 2.0 + */ +public static char[] getQualifier(char[] name) { + int lastDot = CharOperation.lastIndexOf(C_DOT, name); + if (lastDot == -1) { + return CharOperation.NO_CHAR; + } + return CharOperation.subarray(name, 0, lastDot); +} +/** + * Returns a string containing all but the last segment of the given + * dot-separated qualified name. Returns the empty string if it is not qualified. + *

+ * For example: + *

+ * 
+ * getQualifier("java.lang.Object") -> "java.lang"
+ * getQualifier("Outer.Inner") -> "Outer"
+ * 
+ * 
+ *

+ * + * @param name the name + * @return the qualifier prefix, or the empty string if the name contains no + * dots + * @exception NullPointerException if name is null + */ +public static String getQualifier(String name) { + int lastDot = name.lastIndexOf(C_DOT); + if (lastDot == -1) { + return EMPTY; + } + return name.substring(0, lastDot); +} +/** + * Extracts the return type from the given method signature. The method signature is + * expected to be dot-based. + * + * @param methodSignature the method signature + * @return the type signature of the return type + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + * + * @since 2.0 + */ +public static char[] getReturnType(char[] methodSignature) throws IllegalArgumentException { + // skip type parameters + int i = CharOperation.lastIndexOf(C_PARAM_END, methodSignature); + if (i == -1) { + throw new IllegalArgumentException(); + } + // ignore any thrown exceptions + int j = CharOperation.indexOf('^', methodSignature); + int last = (j == -1 ? methodSignature.length : j); + return CharOperation.subarray(methodSignature, i + 1, last); +} +/** + * Extracts the return type from the given method signature. The method signature is + * expected to be dot-based. + * + * @param methodSignature the method signature + * @return the type signature of the return type + * @exception IllegalArgumentException if the signature is syntactically + * incorrect + */ +public static String getReturnType(String methodSignature) throws IllegalArgumentException { + return new String(getReturnType(methodSignature.toCharArray())); +} +/** + * Returns the last segment of the given dot-separated qualified name. + * Returns the given name if it is not qualified. + *

+ * For example: + *

+ * 
+ * getSimpleName({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'O', 'b', 'j', 'e', 'c', 't'}
+ * 
+ * 
+ *

+ * + * @param name the name + * @return the last segment of the qualified name + * @exception NullPointerException if name is null + * @since 2.0 + */ +public static char[] getSimpleName(char[] name) { + int lastDot = CharOperation.lastIndexOf(C_DOT, name); + if (lastDot == -1) { + return name; + } + return CharOperation.subarray(name, lastDot + 1, name.length); +} +/** + * Returns the last segment of the given dot-separated qualified name. + * Returns the given name if it is not qualified. + *

+ * For example: + *

+ * 
+ * getSimpleName("java.lang.Object") -> "Object"
+ * 
+ * 
+ *

+ * + * @param name the name + * @return the last segment of the qualified name + * @exception NullPointerException if name is null + */ +public static String getSimpleName(String name) { + int lastDot = name.lastIndexOf(C_DOT); + if (lastDot == -1) { + return name; + } + return name.substring(lastDot + 1, name.length()); +} +/** + * Returns all segments of the given dot-separated qualified name. + * Returns an array with only the given name if it is not qualified. + * Returns an empty array if the name is empty. + *

+ * For example: + *

+ * 
+ * getSimpleNames({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}
+ * getSimpleNames({'O', 'b', 'j', 'e', 'c', 't'}) -> {{'O', 'b', 'j', 'e', 'c', 't'}}
+ * getSimpleNames("") -> {}
+ * 
+ * 
+ * + * @param name the name + * @return the list of simple names, possibly empty + * @exception NullPointerException if name is null + * @since 2.0 + */ +public static char[][] getSimpleNames(char[] name) { + if (name.length == 0) { + return CharOperation.NO_CHAR_CHAR; + } + int dot = CharOperation.indexOf(C_DOT, name); + if (dot == -1) { + return new char[][] {name}; + } + int n = 1; + while ((dot = CharOperation.indexOf(C_DOT, name, dot + 1)) != -1) { + ++n; + } + char[][] result = new char[n + 1][]; + int segStart = 0; + for (int i = 0; i < n; ++i) { + dot = CharOperation.indexOf(C_DOT, name, segStart); + result[i] = CharOperation.subarray(name, segStart, dot); + segStart = dot + 1; + } + result[n] = CharOperation.subarray(name, segStart, name.length); + return result; +} +/** + * Returns all segments of the given dot-separated qualified name. + * Returns an array with only the given name if it is not qualified. + * Returns an empty array if the name is empty. + *

+ * For example: + *

+ * 
+ * getSimpleNames("java.lang.Object") -> {"java", "lang", "Object"}
+ * getSimpleNames("Object") -> {"Object"}
+ * getSimpleNames("") -> {}
+ * 
+ * 
+ * + * @param name the name + * @return the list of simple names, possibly empty + * @exception NullPointerException if name is null + */ +public static String[] getSimpleNames(String name) { + char[][] simpleNames = getSimpleNames(name.toCharArray()); + int length = simpleNames.length; + String[] result = new String[length]; + for (int i = 0; i < length; i++) { + result[i] = new String(simpleNames[i]); + } + return result; +} +/** + * Converts the given method signature to a readable form. The method signature is expected to + * be dot-based. + *

+ * For example: + *

+ * 
+ * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
+ * 
+ * 
+ *

+ * + * @param methodSignature the method signature to convert + * @param methodName the name of the method to insert in the result, or + * null if no method name is to be included + * @param parameterNames the parameter names to insert in the result, or + * null if no parameter names are to be included; if supplied, + * the number of parameter names must match that of the method signature + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param includeReturnType true if the return type is to be + * included + * @return the char array representation of the method signature + * + * @since 2.0 + */ +public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) { + int firstParen = CharOperation.indexOf(C_PARAM_START, methodSignature); + if (firstParen == -1) { + throw new IllegalArgumentException(); + } + + StringBuffer buffer = new StringBuffer(methodSignature.length + 10); + + // return type + if (includeReturnType) { + char[] rts = getReturnType(methodSignature); + appendTypeSignature(rts, 0 , fullyQualifyTypeNames, buffer); + buffer.append(' '); + } + + // selector + if (methodName != null) { + buffer.append(methodName); + } + + // parameters + buffer.append('('); + char[][] pts = getParameterTypes(methodSignature); + for (int i = 0; i < pts.length; i++) { + appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer); + if (parameterNames != null) { + buffer.append(' '); + buffer.append(parameterNames[i]); + } + if (i != pts.length - 1) { + buffer.append(','); + buffer.append(' '); + } + } + buffer.append(')'); + char[] result = new char[buffer.length()]; + buffer.getChars(0, buffer.length(), result, 0); + return result; +} + +/** + * Converts the given type signature to a readable string. The signature is expected to + * be dot-based. + * + *

+ * For example: + *

+ * 
+ * toString({'[', 'L', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', ';'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '[', ']'}
+ * toString({'I'}) -> {'i', 'n', 't'}
+ * 
+ * 
+ *

+ *

+ * Note: This method assumes that a type signature containing a '$' + * is an inner type signature. While this is correct in most cases, someone could + * define a non-inner type name containing a '$'. Handling this + * correctly in all cases would have required resolving the signature, which + * generally not feasible. + *

+ * + * @param signature the type signature + * @return the string representation of the type + * @exception IllegalArgumentException if the signature is not syntactically + * correct + * + * @since 2.0 + */ +public static char[] toCharArray(char[] signature) throws IllegalArgumentException { + int sigLength = signature.length; + if (sigLength == 0 || signature[0] == C_PARAM_START || signature[0] == C_GENERIC_START) { + return toCharArray(signature, CharOperation.NO_CHAR, null, true, true); + } + + StringBuffer buffer = new StringBuffer(signature.length + 10); + appendTypeSignature(signature, 0, true, buffer); + char[] result = new char[buffer.length()]; + buffer.getChars(0, buffer.length(), result, 0); + return result; +} + +/** + * Scans the given string for a type signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type signature + * @see #scanTypeSignature(char[], int) + */ +private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + switch (c) { + case C_ARRAY : + return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer); + case C_RESOLVED : + case C_UNRESOLVED : + return appendClassTypeSignature(string, start, fullyQualifyTypeNames, buffer); + case C_TYPE_VARIABLE : + int e = scanTypeVariableSignature(string, start); + buffer.append(CharOperation.subarray(string, start + 1, e)); + return e; + case C_BOOLEAN : + buffer.append(BOOLEAN); + return start; + case C_BYTE : + buffer.append(BYTE); + return start; + case C_CHAR : + buffer.append(CHAR); + return start; + case C_DOUBLE : + buffer.append(DOUBLE); + return start; + case C_FLOAT : + buffer.append(FLOAT); + return start; + case C_INT : + buffer.append(INT); + return start; + case C_LONG : + buffer.append(LONG); + return start; + case C_SHORT : + buffer.append(SHORT); + return start; + case C_VOID : + buffer.append(VOID); + return start; + case C_CONST : + buffer.append(CONST); + return start; + default : + throw new IllegalArgumentException(); + } +} + +/** + * Scans the given string for an array type signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not an array type signature + * @see #scanArrayTypeSignature(char[], int) + */ +private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + // need a minimum 2 char + if (start >= string.length - 1) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c != C_ARRAY) { //$NON-NLS-1$ + throw new IllegalArgumentException(); + } + int e = appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer); + buffer.append('['); + buffer.append(']'); + return e; +} + +/** + * Scans the given string for a class type signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a class type signature + * @see #scanClassTypeSignature(char[], int) + */ +private static int appendClassTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + // need a minimum 3 chars "Lx;" + if (start >= string.length - 2) { + throw new IllegalArgumentException(); + } + // must start in "L" or "Q" + char c = string[start]; + if (c != C_RESOLVED && c != C_UNRESOLVED) { + throw new IllegalArgumentException(); + } + boolean resolved = (c == C_RESOLVED); + boolean removePackageQualifiers = !fullyQualifyTypeNames; + if (!resolved) { + // keep everything in an unresolved name + removePackageQualifiers = false; + } + int p = start + 1; + int checkpoint = buffer.length(); + while (true) { + if (p >= string.length) { + throw new IllegalArgumentException(); + } + c = string[p]; + switch(c) { + case C_SEMICOLON : + // all done + return p; + case C_GENERIC_START : + int e = appendTypeArgumentSignatures(string, p, fullyQualifyTypeNames, buffer); + // once we hit type arguments there are no more package prefixes + removePackageQualifiers = false; + p = e; + break; + case C_DOT : + if (removePackageQualifiers) { + // erase package prefix + buffer.setLength(checkpoint); + } else { + buffer.append('.'); + } + break; + case '/' : + if (removePackageQualifiers) { + // erase package prefix + buffer.setLength(checkpoint); + } else { + buffer.append('/'); + } + break; + case C_DOLLAR : + if (resolved) { + // once we hit "$" there are no more package prefixes + removePackageQualifiers = false; + /** + * Convert '$' in resolved type signatures into '.'. + * NOTE: This assumes that the type signature is an inner type + * signature. This is true in most cases, but someone can define a + * non-inner type name containing a '$'. + */ + buffer.append('.'); + } + break; + default : + buffer.append(c); + } + p++; + } +} + +/** + * Scans the given string for a list of type arguments signature starting at the + * given index and appends it to the given buffer, and returns the index of the + * last character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a list of type argument + * signatures + * @see #scanTypeArgumentSignatures(char[], int) + */ +private static int appendTypeArgumentSignatures(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + // need a minimum 2 char "<>" + if (start >= string.length - 1) { + throw new IllegalArgumentException(); + } + char c = string[start]; + if (c != C_GENERIC_START) { + throw new IllegalArgumentException(); + } + buffer.append('<'); + int p = start + 1; + int count = 0; + while (true) { + if (p >= string.length) { + throw new IllegalArgumentException(); + } + c = string[p]; + if (c == C_GENERIC_END) { + buffer.append('>'); + return p; + } + if (count != 0) { + buffer.append(','); + } + int e = appendTypeArgumentSignature(string, p, fullyQualifyTypeNames, buffer); + count++; + p = e + 1; + } +} + +/** + * Scans the given string for a type argument signature starting at the given + * index and appends it to the given buffer, and returns the index of the last + * character. + * + * @param string the signature string + * @param start the 0-based character index of the first character + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param buffer the string buffer to append to + * @return the 0-based character index of the last character + * @exception IllegalArgumentException if this is not a type argument signature + * @see #scanTypeArgumentSignature(char[], int) + */ +private static int appendTypeArgumentSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { + // need a minimum 1 char + if (start >= string.length) { + throw new IllegalArgumentException(); + } + char c = string[start]; + switch(c) { + case C_STAR : + buffer.append('?'); + return start; + case '+' : + buffer.append("? extends "); //$NON-NLS-1$ + return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer); + case '-' : + buffer.append("? super "); //$NON-NLS-1$ + return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer); + default : + return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer); + } +} + +/** + * Converts the given array of qualified name segments to a qualified name. + *

+ * For example: + *

+ * 
+ * toQualifiedName({{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}
+ * toQualifiedName({{'O', 'b', 'j', 'e', 'c', 't'}}) -> {'O', 'b', 'j', 'e', 'c', 't'}
+ * toQualifiedName({{}}) -> {}
+ * 
+ * 
+ *

+ * + * @param segments the list of name segments, possibly empty + * @return the dot-separated qualified name, or the empty string + * + * @since 2.0 + */ +public static char[] toQualifiedName(char[][] segments) { + int length = segments.length; + if (length == 0) return CharOperation.NO_CHAR; + if (length == 1) return segments[0]; + + int resultLength = 0; + for (int i = 0; i < length; i++) { + resultLength += segments[i].length+1; + } + resultLength--; + char[] result = new char[resultLength]; + int index = 0; + for (int i = 0; i < length; i++) { + char[] segment = segments[i]; + int segmentLength = segment.length; + System.arraycopy(segment, 0, result, index, segmentLength); + index += segmentLength; + if (i != length-1) { + result[index++] = C_DOT; + } + } + return result; +} +/** + * Converts the given array of qualified name segments to a qualified name. + *

+ * For example: + *

+ * 
+ * toQualifiedName(new String[] {"java", "lang", "Object"}) -> "java.lang.Object"
+ * toQualifiedName(new String[] {"Object"}) -> "Object"
+ * toQualifiedName(new String[0]) -> ""
+ * 
+ * 
+ *

+ * + * @param segments the list of name segments, possibly empty + * @return the dot-separated qualified name, or the empty string + */ +public static String toQualifiedName(String[] segments) { + int length = segments.length; + char[][] charArrays = new char[length][]; + for (int i = 0; i < length; i++) { + charArrays[i] = segments[i].toCharArray(); + } + return new String(toQualifiedName(charArrays)); +} +/** + * Converts the given type signature to a readable string. The signature is expected to + * be dot-based. + * + *

+ * For example: + *

+ * 
+ * toString("[Ljava.lang.String;") -> "java.lang.String[]"
+ * toString("I") -> "int"
+ * 
+ * 
+ *

+ *

+ * Note: This method assumes that a type signature containing a '$' + * is an inner type signature. While this is correct in most cases, someone could + * define a non-inner type name containing a '$'. Handling this + * correctly in all cases would have required resolving the signature, which + * generally not feasible. + *

+ * + * @param signature the type signature + * @return the string representation of the type + * @exception IllegalArgumentException if the signature is not syntactically + * correct + */ +public static String toString(String signature) throws IllegalArgumentException { + return new String(toCharArray(signature.toCharArray())); +} +/** + * Converts the given method signature to a readable string. The method signature is expected to + * be dot-based. + * + * @param methodSignature the method signature to convert + * @param methodName the name of the method to insert in the result, or + * null if no method name is to be included + * @param parameterNames the parameter names to insert in the result, or + * null if no parameter names are to be included; if supplied, + * the number of parameter names must match that of the method signature + * @param fullyQualifyTypeNames true if type names should be fully + * qualified, and false to use only simple names + * @param includeReturnType true if the return type is to be + * included + * @see #toCharArray(char[], char[], char[][], boolean, boolean) + * @return the string representation of the method signature + */ +public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) { + char[][] params; + if (parameterNames == null) { + params = null; + } else { + int paramLength = parameterNames.length; + params = new char[paramLength][]; + for (int i = 0; i < paramLength; i++) { + params[i] = parameterNames[i].toCharArray(); + } + } + return new String(toCharArray(methodSignature.toCharArray(), methodName == null ? null : methodName.toCharArray(), params, fullyQualifyTypeNames, includeReturnType)); +} + +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeInfo.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeInfo.java index ae233c3dcf7..7d9c412a32c 100644 --- a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeInfo.java +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeInfo.java @@ -10,7 +10,10 @@ *******************************************************************************/ package org.eclipse.cdt.core.browser; +import java.util.Iterator; + import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; import org.eclipse.cdt.internal.core.browser.cache.ITypeCache; import org.eclipse.cdt.internal.core.browser.util.ArrayUtil; import org.eclipse.core.resources.IProject; @@ -22,6 +25,8 @@ public class TypeInfo implements ITypeInfo protected IQualifiedTypeName fQualifiedName; protected ITypeReference[] fSourceRefs = null; protected int fSourceRefsCount = 0; + protected ITypeReference[] fDerivedSourceRefs = null; + protected int fDerivedSourceRefsCount = 0; protected final static int INITIAL_REFS_SIZE = 1; protected final static int REFS_GROW_BY = 2; @@ -54,26 +59,6 @@ public class TypeInfo implements ITypeInfo return null; } - public ICElement getCElement() { - ITypeReference ref = getResolvedReference(); - if (ref != null) { - ICElement[] elems = ref.getCElements(); - if (elems != null && elems.length > 0) { - if (elems.length == 1) - return elems[0]; - - for (int i = 0; i < elems.length; ++i) { - ICElement elem = elems[i]; - if (elem.getElementType() == fElementType && elem.getElementName().equals(getName())) { - //TODO should check fully qualified name - return elem; - } - } - } - } - return null; - } - public ITypeReference getResolvedReference() { for (int i = 0; i < fSourceRefsCount; ++i) { ITypeReference location = fSourceRefs[i]; @@ -85,9 +70,28 @@ public class TypeInfo implements ITypeInfo } public boolean isReferenced() { - return (fSourceRefs != null); + return (fSourceRefs != null || fDerivedSourceRefs != null); } + public boolean isReferenced(ITypeSearchScope scope) { + if (scope == null || scope.isWorkspaceScope()) + return true; + + // check if path is in scope + for (int i = 0; i < fSourceRefsCount; ++i) { + ITypeReference location = fSourceRefs[i]; + if (scope.encloses(location.getPath())) + return true; + } + for (int i = 0; i < fDerivedSourceRefsCount; ++i) { + ITypeReference location = fDerivedSourceRefs[i]; + if (scope.encloses(location.getPath())) + return true; + } + + return false; + } + public boolean isUndefinedType() { return fElementType == 0; } @@ -256,4 +260,64 @@ public class TypeInfo implements ITypeInfo public void setCache(ITypeCache typeCache) { fTypeCache = typeCache; } + + public void addDerivedReference(ITypeReference location) { + if (fDerivedSourceRefs == null) { + fDerivedSourceRefs = new ITypeReference[INITIAL_REFS_SIZE]; + fDerivedSourceRefsCount = 0; + } else if (fDerivedSourceRefsCount == fDerivedSourceRefs.length) { + ITypeReference[] refs = new ITypeReference[fDerivedSourceRefs.length + REFS_GROW_BY]; + System.arraycopy(fDerivedSourceRefs, 0, refs, 0, fDerivedSourceRefsCount); + fDerivedSourceRefs = refs; + } + fDerivedSourceRefs[fDerivedSourceRefsCount] = location; + ++fDerivedSourceRefsCount; + } + + public ITypeReference[] getDerivedReferences() { + if (fDerivedSourceRefs != null) { + ITypeReference[] refs = new ITypeReference[fDerivedSourceRefsCount]; + System.arraycopy(fDerivedSourceRefs, 0, refs, 0, fDerivedSourceRefsCount); + return refs; + } + return null; + } + + public boolean hasSubTypes() { + return (fDerivedSourceRefs != null); + } + + public ITypeInfo[] getSubTypes() { + if (fTypeCache != null) { + return fTypeCache.getSubtypes(this); + } + return null; + } + + public boolean hasSuperTypes() { + if (fTypeCache != null) { + return (fTypeCache.getSupertypes(this) != null); + } + return false; +// return true; //TODO can't know this until we parse + } + + public ITypeInfo[] getSuperTypes() { + if (fTypeCache != null) { + return fTypeCache.getSupertypes(this); + } + return null; + } + + public ASTAccessVisibility getSuperTypeAccess(ITypeInfo superType) { + if (fTypeCache != null) { + return fTypeCache.getSupertypeAccess(this, superType); + } + return null; + } + + public boolean isClass() { + return (fElementType == ICElement.C_CLASS + || fElementType == ICElement.C_STRUCT); + } } diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeUtil.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeUtil.java new file mode 100644 index 00000000000..9168ff6a686 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeUtil.java @@ -0,0 +1,282 @@ +/* + * Created on Jul 5, 2004 + * + * TODO To change the template for this generated file go to + * Window - Preferences - Java - Code Style - Code Templates + */ +package org.eclipse.cdt.core.browser; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.Flags; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICElementVisitor; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.IMember; +import org.eclipse.cdt.core.model.IMethodDeclaration; +import org.eclipse.cdt.core.model.IParent; +import org.eclipse.cdt.core.model.IStructure; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; +import org.eclipse.cdt.internal.core.browser.cache.ITypeCache; +import org.eclipse.cdt.internal.core.browser.cache.TypeCacheManager; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; + +/** + * @author CWiebe + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +public class TypeUtil { + + public static ICElement getDeclaringType(ICElement type) { + ICElement parentElement = type.getParent(); + if (parentElement != null && isClassOrStruct(parentElement)) { + return parentElement; + } + + if (isClassOrStruct(type)) { + while (parentElement != null) { + if (isClassOrStruct(parentElement)) { + return parentElement; + } else if (parentElement instanceof IMember) { + parentElement = parentElement.getParent(); + } else { + return null; + } + } + } + + return null; + } + + public static boolean isClassOrStruct(ICElement type) { + int kind = type.getElementType(); + // case ICElement.C_TEMPLATE_CLASS: + // case ICElement.C_TEMPLATE_STRUCT: + return (kind == ICElement.C_CLASS || kind == ICElement.C_STRUCT); + } + + public static boolean isClass(ICElement type) { + return (type.getElementType() == ICElement.C_CLASS); + } + + public static boolean isNamespace(ICElement type) { + return (type.getElementType() == ICElement.C_NAMESPACE); + } + + public static ICElement[] getTypes(ICElement elem) { + final List typeList = new ArrayList(3); + try { + elem.accept(new ICElementVisitor() { + public boolean visit(ICElement element) throws CoreException { + // TODO Auto-generated method stub + if (element instanceof IStructure) { + typeList.add(element); + } + return false; + }}); + } catch (CoreException e) { + } + return (ICElement[])typeList.toArray(new ICElement[typeList.size()]); + } + + public static IQualifiedTypeName getFullyQualifiedName(ICElement type) { + String name = type.getElementName(); + IQualifiedTypeName qualifiedName = new QualifiedTypeName(name); + ICElement parent = type.getParent(); + while (parent != null && (isNamespace(parent) || isClass(parent))) { + qualifiedName = new QualifiedTypeName(parent.getElementName()).append(qualifiedName); + parent = parent.getParent(); + } + return qualifiedName; + } + + public static ITypeInfo getTypeForElement(ICElement elem, IProgressMonitor monitor) { + if (elem != null) { + ICProject cProject = elem.getCProject(); + IQualifiedTypeName qualifiedName = getFullyQualifiedName(elem); + if (qualifiedName != null) { + final ITypeSearchScope fScope = new TypeSearchScope(true); + if (!AllTypesCache.isCacheUpToDate(fScope)) { + AllTypesCache.updateCache(fScope, monitor); + } + + ITypeCache cache = TypeCacheManager.getInstance().getCache(cProject.getProject()); + ITypeInfo info = cache.getType(elem.getElementType(), qualifiedName); + if (info != null) { + ITypeReference ref = info.getResolvedReference(); + if (ref == null) { + ref = AllTypesCache.resolveTypeLocation(info, monitor); + } + return info; + } + } + } + return null; + } + + public static ITypeInfo getTypeForElement(ICElement elem) { + return getTypeForElement(elem, new NullProgressMonitor()); + } + + public static ICElement getElementForType(ITypeInfo type, IProgressMonitor monitor) { + final ITypeSearchScope fScope = new TypeSearchScope(true); + if (!AllTypesCache.isCacheUpToDate(fScope)) { + AllTypesCache.updateCache(fScope, monitor); + } + ITypeReference ref = type.getResolvedReference(); + if (ref == null) { + ref = AllTypesCache.resolveTypeLocation(type, monitor); + } + if (ref != null) { + ICElement[] elems = ref.getCElements(); + if (elems != null && elems.length > 0) { + if (elems.length == 1) + return elems[0]; + + for (int i = 0; i < elems.length; ++i) { + ICElement elem = elems[i]; + if (elem.getElementType() == type.getCElementType() && elem.getElementName().equals(type.getName())) { + //TODO should check fully qualified name + return elem; + } + } + } + } + return null; + } + public static ICElement getElementForType(ITypeInfo type) { + return getElementForType(type, new NullProgressMonitor()); + } + + public static IMethodDeclaration[] getMethods(ICElement elem) { + if (elem instanceof IStructure) { + try { + List list = ((IParent)elem).getChildrenOfType(ICElement.C_METHOD_DECLARATION); + if (list != null && !list.isEmpty()) { + return (IMethodDeclaration[]) list.toArray(new IMethodDeclaration[list.size()]); + } + } catch (CModelException e) { + } + } + return null; + } + + public static ICElement[] getFields(ICElement elem) { + if (elem instanceof IStructure) { + try { + List list = ((IParent)elem).getChildrenOfType(ICElement.C_FIELD); + if (list != null && !list.isEmpty()) { + return (ICElement[]) list.toArray(new ICElement[list.size()]); + } + } catch (CModelException e) { + } + } + return null; + } + + + /** + * Finds a method by name. + * This searches for a method with a name and signature. Parameter types are only + * compared by the simple name, no resolving for the fully qualified type name is done. + * Constructors are only compared by parameters, not the name. + * @param name The name of the method to find + * @param paramTypes The type signatures of the parameters e.g. {"QString;","I"} + * @param isConstructor If the method is a constructor + * @param methods The methods to search in + * @return The found method or null, if nothing found + */ +// TODO move methods to CModelUtil + public static IMethodDeclaration findMethod(String name, String[] paramTypes, boolean isConstructor, boolean isDestructor, IMethodDeclaration[] methods) throws CModelException { + for (int i= methods.length - 1; i >= 0; i--) { + if (isSameMethodSignature(name, paramTypes, isConstructor, isDestructor, methods[i])) { + return methods[i]; + } + } + return null; + } + + /** + * Tests if a method equals to the given signature. + * Parameter types are only compared by the simple name, no resolving for + * the fully qualified type name is done. Constructors are only compared by + * parameters, not the name. + * @param name Name of the method + * @param paramTypes The type signatures of the parameters e.g. {"QString;","I"} + * @param isConstructor Specifies if the method is a constructor + * @return Returns true if the method has the given name and parameter types and constructor state. + */ +//TODO move methods to CModelUtil + public static boolean isSameMethodSignature(String name, String[] paramTypes, boolean isConstructor, boolean isDestructor, IMethodDeclaration curr) throws CModelException { + if (isConstructor || isDestructor || name.equals(curr.getElementName())) { + if ((isConstructor == curr.isConstructor()) && (isDestructor == curr.isDestructor())) { + String[] currParamTypes= curr.getParameterTypes(); + if (paramTypes.length == currParamTypes.length) { + for (int i= 0; i < paramTypes.length; i++) { + String t1= Signature.getSimpleName(Signature.toString(paramTypes[i])); + String t2= Signature.getSimpleName(Signature.toString(currParamTypes[i])); + if (!t1.equals(t2)) { + return false; + } + } + return true; + } + } + } + return false; + } + + /** + * Finds a method in a type. + * This searches for a method with the same name and signature. Parameter types are only + * compared by the simple name, no resolving for the fully qualified type name is done. + * Constructors are only compared by parameters, not the name. + * @param name The name of the method to find + * @param paramTypes The type signatures of the parameters e.g. {"QString;","I"} + * @param isConstructor If the method is a constructor + * @return The first found method or null, if nothing found + */ +// TODO move methods to CModelUtil + public static IMethodDeclaration findMethod(String name, String[] paramTypes, boolean isConstructor, boolean isDestructor, ICElement type) throws CModelException { + return findMethod(name, paramTypes, isConstructor, isDestructor, getMethods(type)); + } + + /** + * Finds a method declararion in a type's hierarchy. The search is top down, so this + * returns the first declaration of the method in the hierarchy. + * This searches for a method with a name and signature. Parameter types are only + * compared by the simple name, no resolving for the fully qualified type name is done. + * Constructors are only compared by parameters, not the name. + * @param type Searches in this type's supertypes. + * @param name The name of the method to find + * @param paramTypes The type signatures of the parameters e.g. {"QString;","I"} + * @param isConstructor If the method is a constructor + * @return The first method found or null, if nothing found + */ +// TODO move methods to CModelUtil + public static IMethodDeclaration findMethodDeclarationInHierarchy(ITypeHierarchy hierarchy, ICElement type, String name, String[] paramTypes, boolean isConstructor, boolean isDestructor) throws CModelException { + ICElement[] superTypes= hierarchy.getAllSupertypes(type); + for (int i= superTypes.length - 1; i >= 0; i--) { + IMethodDeclaration first= findMethod(name, paramTypes, isConstructor, isDestructor, superTypes[i]); + if (first != null && first.getVisibility() != ASTAccessVisibility.PRIVATE) { + // the order getAllSupertypes does make assumptions of the order of inner elements -> search recursivly + IMethodDeclaration res= findMethodDeclarationInHierarchy(hierarchy, TypeUtil.getDeclaringType(first), name, paramTypes, isConstructor, isDestructor); + if (res != null) { + return res; + } + return first; + } + } + return null; + } + +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ChangeCollector.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ChangeCollector.java new file mode 100644 index 00000000000..8dd7b729af0 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ChangeCollector.java @@ -0,0 +1,451 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.browser.typehierarchy; + +import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import org.eclipse.cdt.core.browser.TypeUtil; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICElementDelta; +import org.eclipse.cdt.core.model.IMember; +import org.eclipse.cdt.core.model.IParent; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.internal.core.model.CElement; + +/* + * Collects changes (reported through fine-grained deltas) that can affect a type hierarchy. + */ +public class ChangeCollector { + + /* + * A table from ICElements to TypeDeltas + */ + HashMap changes = new HashMap(); + + TypeHierarchy hierarchy; + + public ChangeCollector(TypeHierarchy hierarchy) { + this.hierarchy = hierarchy; + } + + /* + * Adds the children of the given delta to the list of changes. + */ + private void addAffectedChildren(ICElementDelta delta) throws CModelException { +// ICElementDelta[] children = delta.getAffectedChildren(); +// for (int i = 0, length = children.length; i < length; i++) { +// ICElementDelta child = children[i]; +// ICElement childElement = child.getElement(); +// switch (childElement.getElementType()) { +// case ICElement.IMPORT_CONTAINER: +// addChange((IImportContainer)childElement, child); +// break; +// case ICElement.IMPORT_DECLARATION: +// addChange((IImportDeclaration)childElement, child); +// break; +// case ICElement.TYPE: +// addChange((ICElement)childElement, child); +// break; +// case ICElement.INITIALIZER: +// case ICElement.FIELD: +// case ICElement.METHOD: +// addChange((IMember)childElement, child); +// break; +// } +// } + } + + /* + * Adds the given delta on a compilation unit to the list of changes. + */ + public void addChange(ITranslationUnit cu, ICElementDelta newDelta) throws CModelException { +// int newKind = newDelta.getKind(); +// switch (newKind) { +// case ICElementDelta.ADDED: +// ArrayList allTypes = new ArrayList(); +// getAllTypesFromElement(cu, allTypes); +// for (int i = 0, length = allTypes.size(); i < length; i++) { +// ICElement type = (ICElement)allTypes.get(i); +// addTypeAddition(type, (SimpleDelta)this.changes.get(type)); +// } +// break; +// case ICElementDelta.REMOVED: +// allTypes = new ArrayList(); +// getAllTypesFromHierarchy((JavaElement)cu, allTypes); +// for (int i = 0, length = allTypes.size(); i < length; i++) { +// ICElement type = (ICElement)allTypes.get(i); +// addTypeRemoval(type, (SimpleDelta)this.changes.get(type)); +// } +// break; +// case ICElementDelta.CHANGED: +// addAffectedChildren(newDelta); +// break; +// } + } + +/* private void addChange(IImportContainer importContainer, ICElementDelta newDelta) throws CModelException { + int newKind = newDelta.getKind(); + if (newKind == ICElementDelta.CHANGED) { + addAffectedChildren(newDelta); + return; + } + SimpleDelta existingDelta = (SimpleDelta)this.changes.get(importContainer); + if (existingDelta != null) { + switch (newKind) { + case ICElementDelta.ADDED: + if (existingDelta.getKind() == ICElementDelta.REMOVED) { + // REMOVED then ADDED + this.changes.remove(importContainer); + } + break; + case ICElementDelta.REMOVED: + if (existingDelta.getKind() == ICElementDelta.ADDED) { + // ADDED then REMOVED + this.changes.remove(importContainer); + } + break; + // CHANGED handled above + } + } else { + SimpleDelta delta = new SimpleDelta(); + switch (newKind) { + case ICElementDelta.ADDED: + delta.added(); + break; + case ICElementDelta.REMOVED: + delta.removed(); + break; + } + this.changes.put(importContainer, delta); + } + } + + private void addChange(IImportDeclaration importDecl, ICElementDelta newDelta) { + SimpleDelta existingDelta = (SimpleDelta)this.changes.get(importDecl); + int newKind = newDelta.getKind(); + if (existingDelta != null) { + switch (newKind) { + case ICElementDelta.ADDED: + if (existingDelta.getKind() == ICElementDelta.REMOVED) { + // REMOVED then ADDED + this.changes.remove(importDecl); + } + break; + case ICElementDelta.REMOVED: + if (existingDelta.getKind() == ICElementDelta.ADDED) { + // ADDED then REMOVED + this.changes.remove(importDecl); + } + break; + // CHANGED cannot happen for import declaration + } + } else { + SimpleDelta delta = new SimpleDelta(); + switch (newKind) { + case ICElementDelta.ADDED: + delta.added(); + break; + case ICElementDelta.REMOVED: + delta.removed(); + break; + } + this.changes.put(importDecl, delta); + } + } +*/ + + /* + * Adds a change for the given member (a method, a field or an initializer) and the types it defines. + */ + private void addChange(IMember member, ICElementDelta newDelta) throws CModelException { +// int newKind = newDelta.getKind(); +// switch (newKind) { +// case ICElementDelta.ADDED: +// ArrayList allTypes = new ArrayList(); +// getAllTypesFromElement(member, allTypes); +// for (int i = 0, length = allTypes.size(); i < length; i++) { +// ICElement innerType = (ICElement)allTypes.get(i); +// addTypeAddition(innerType, (SimpleDelta)this.changes.get(innerType)); +// } +// break; +// case ICElementDelta.REMOVED: +// allTypes = new ArrayList(); +// getAllTypesFromHierarchy((JavaElement)member, allTypes); +// for (int i = 0, length = allTypes.size(); i < length; i++) { +// ICElement type = (ICElement)allTypes.get(i); +// addTypeRemoval(type, (SimpleDelta)this.changes.get(type)); +// } +// break; +// case ICElementDelta.CHANGED: +// addAffectedChildren(newDelta); +// break; +// } + } + + /* + * Adds a change for the given type and the types it defines. + */ + private void addChange(ICElement type, ICElementDelta newDelta) throws CModelException { +// int newKind = newDelta.getKind(); +// SimpleDelta existingDelta = (SimpleDelta)this.changes.get(type); +// switch (newKind) { +// case ICElementDelta.ADDED: +// addTypeAddition(type, existingDelta); +// ArrayList allTypes = new ArrayList(); +// getAllTypesFromElement(type, allTypes); +// for (int i = 0, length = allTypes.size(); i < length; i++) { +// ICElement innerType = (ICElement)allTypes.get(i); +// addTypeAddition(innerType, (SimpleDelta)this.changes.get(innerType)); +// } +// break; +// case ICElementDelta.REMOVED: +// addTypeRemoval(type, existingDelta); +// allTypes = new ArrayList(); +// getAllTypesFromHierarchy((JavaElement)type, allTypes); +// for (int i = 0, length = allTypes.size(); i < length; i++) { +// ICElement innerType = (ICElement)allTypes.get(i); +// addTypeRemoval(innerType, (SimpleDelta)this.changes.get(innerType)); +// } +// break; +// case ICElementDelta.CHANGED: +// addTypeChange(type, newDelta.getFlags(), existingDelta); +// addAffectedChildren(newDelta); +// break; +// } + } + +/* private void addTypeAddition(ICElement type, SimpleDelta existingDelta) throws CModelException { + if (existingDelta != null) { + switch (existingDelta.getKind()) { + case ICElementDelta.REMOVED: + // REMOVED then ADDED + boolean hasChange = false; + if (hasSuperTypeChange(type)) { + existingDelta.superTypes(); + hasChange = true; + } + if (hasVisibilityChange(type)) { + existingDelta.modifiers(); + hasChange = true; + } + if (!hasChange) { + this.changes.remove(type); + } + break; + // CHANGED then ADDED + // or ADDED then ADDED: should not happen + } + } else { + // check whether the type addition affects the hierarchy + String typeName = type.getElementName(); + if (this.hierarchy.hasSupertype(typeName) + || this.hierarchy.subtypesIncludeSupertypeOf(type) + || this.hierarchy.missingTypes.contains(typeName)) { + SimpleDelta delta = new SimpleDelta(); + delta.added(); + this.changes.put(type, delta); + } + } + } +*/ +/* private void addTypeChange(ICElement type, int newFlags, SimpleDelta existingDelta) throws CModelException { + if (existingDelta != null) { + switch (existingDelta.getKind()) { + case ICElementDelta.CHANGED: + // CHANGED then CHANGED + int existingFlags = existingDelta.getFlags(); + boolean hasChange = false; + if ((existingFlags & ICElementDelta.F_SUPER_TYPES) != 0 + && hasSuperTypeChange(type)) { + existingDelta.superTypes(); + hasChange = true; + } + if ((existingFlags & ICElementDelta.F_MODIFIERS) != 0 + && hasVisibilityChange(type)) { + existingDelta.modifiers(); + hasChange = true; + } + if (!hasChange) { + // super types and visibility are back to the ones in the existing hierarchy + this.changes.remove(type); + } + break; + // ADDED then CHANGED: leave it as ADDED + // REMOVED then CHANGED: should not happen + } + } else { + // check whether the type change affects the hierarchy + SimpleDelta typeDelta = null; + if ((newFlags & ICElementDelta.F_SUPER_TYPES) != 0 + && this.hierarchy.includesTypeOrSupertype(type)) { + typeDelta = new SimpleDelta(); + typeDelta.superTypes(); + } + if ((newFlags & ICElementDelta.F_MODIFIERS) != 0 + && this.hierarchy.hasSupertype(type.getElementName())) { + if (typeDelta == null) { + typeDelta = new SimpleDelta(); + } + typeDelta.modifiers(); + } + if (typeDelta != null) { + this.changes.put(type, typeDelta); + } + } + } +*/ +/* private void addTypeRemoval(ICElement type, SimpleDelta existingDelta) { + if (existingDelta != null) { + switch (existingDelta.getKind()) { + case ICElementDelta.ADDED: + // ADDED then REMOVED + this.changes.remove(type); + break; + case ICElementDelta.CHANGED: + // CHANGED then REMOVED + existingDelta.removed(); + break; + // REMOVED then REMOVED: should not happen + } + } else { + // check whether the type removal affects the hierarchy + if (this.hierarchy.contains(type)) { + SimpleDelta typeDelta = new SimpleDelta(); + typeDelta.removed(); + this.changes.put(type, typeDelta); + } + } + } +*/ + /* + * Returns all types defined in the given element excluding the given element. + */ + private void getAllTypesFromElement(ICElement element, ArrayList allTypes) throws CModelException { + switch (element.getElementType()) { + case ICElement.C_UNIT: + ICElement[] types = TypeUtil.getTypes((ITranslationUnit)element); + for (int i = 0, length = types.length; i < length; i++) { + ICElement type = types[i]; + allTypes.add(type); + getAllTypesFromElement(type, allTypes); + } + break; + case ICElement.C_CLASS: + case ICElement.C_STRUCT: +// types = ((ICElement)element).getTypes(); + types = TypeUtil.getTypes((ICElement)element); + for (int i = 0, length = types.length; i < length; i++) { + ICElement type = types[i]; + allTypes.add(type); + getAllTypesFromElement(type, allTypes); + } + break; +// case ICElement.INITIALIZER: +// case ICElement.FIELD: + case ICElement.C_METHOD: + if (element instanceof IParent) { + ICElement[] children = ((IParent)element).getChildren(); + for (int i = 0, length = children.length; i < length; i++) { + ICElement type = (ICElement)children[i]; + allTypes.add(type); + getAllTypesFromElement(type, allTypes); + } + } + break; + } + } + + /* + * Returns all types in the existing hierarchy that have the given element as a parent. + */ + private void getAllTypesFromHierarchy(CElement element, ArrayList allTypes) { + switch (element.getElementType()) { + case ICElement.C_UNIT: + ArrayList types = (ArrayList)this.hierarchy.files.get(element); + if (types != null) { + allTypes.addAll(types); + } + break; + case ICElement.C_CLASS: + case ICElement.C_STRUCT: +// case ICElement.INITIALIZER: +// case ICElement.FIELD: + case ICElement.C_METHOD: + types = (ArrayList)this.hierarchy.files.get(((IMember)element).getTranslationUnit()); + if (types != null) { + for (int i = 0, length = types.size(); i < length; i++) { + ICElement type = (ICElement)types.get(i); + if (element.isAncestorOf(type)) { + allTypes.add(type); + } + } + } + break; + } + } + + private boolean hasSuperTypeChange(ICElement type) throws CModelException { +// // check super class +// ICElement superclass = this.hierarchy.getSuperclass(type); +// String existingSuperclassName = superclass == null ? null : superclass.getElementName(); +// String newSuperclassName = type.getSuperclassName(); +// if (existingSuperclassName != null && !existingSuperclassName.equals(newSuperclassName)) { +// return true; +// } +// +// // check super interfaces +// ICElement[] existingSuperInterfaces = this.hierarchy.getSuperInterfaces(type); +// String[] newSuperInterfaces = type.getSuperInterfaceNames(); +// if (existingSuperInterfaces.length != newSuperInterfaces.length) { +// return true; +// } +// for (int i = 0, length = newSuperInterfaces.length; i < length; i++) { +// String superInterfaceName = newSuperInterfaces[i]; +// if (!superInterfaceName.equals(newSuperInterfaces[i])) { +// return true; +// } +// } + + return false; + } + + private boolean hasVisibilityChange(ICElement type) throws CModelException { +// int existingFlags = this.hierarchy.getCachedFlags(type); +// int newFlags = type.getFlags(); +// return existingFlags != newFlags; + return false; + } + + /* + * Whether the hierarchy needs refresh according to the changes collected so far. + */ + public boolean needsRefresh() { + return changes.size() != 0; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + Iterator iterator = this.changes.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Map.Entry)iterator.next(); + buffer.append(((CElement)entry.getKey()).toDebugString()); + buffer.append(entry.getValue()); + if (iterator.hasNext()) { + buffer.append('\n'); + } + } + return buffer.toString(); + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ITypeHierarchy.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ITypeHierarchy.java new file mode 100644 index 00000000000..d6838a269d6 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ITypeHierarchy.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.browser.typehierarchy; + +import java.io.OutputStream; + +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * A type hierarchy provides navigations between a type and its resolved + * supertypes and subtypes for a specific type or for all types within a region. + * Supertypes may extend outside of the type hierarchy's region in which it was + * created such that the root of the hierarchy is always included. For example, if a type + * hierarchy is created for a java.io.File, and the region the hierarchy was + * created in is the package fragment java.io, the supertype + * java.lang.Object will still be included. + *

+ * A type hierarchy is static and can become stale. Although consistent when + * created, it does not automatically track changes in the model. + * As changes in the model potentially invalidate the hierarchy, change notifications + * are sent to registered ICElementHierarchyChangedListeners. Listeners should + * use the exists method to determine if the hierarchy has become completely + * invalid (for example, when the type or project the hierarchy was created on + * has been removed). To refresh a hierarchy, use the refresh method. + *

+ *

+ * The type hierarchy may contain cycles due to malformed supertype declarations. + * Most type hierarchy queries are oblivious to cycles; the getAll* + * methods are implemented such that they are unaffected by cycles. + *

+ *

+ * This interface is not intended to be implemented by clients. + *

+ */ +public interface ITypeHierarchy { +/** + * Adds the given listener for changes to this type hierarchy. Listeners are + * notified when this type hierarchy changes and needs to be refreshed. + * Has no effect if an identical listener is already registered. + * + * @param listener the listener + */ +void addTypeHierarchyChangedListener(ITypeHierarchyChangedListener listener); +/** + * Returns whether the given type is part of this hierarchy. + * + * @param type the given type + * @return true if the given type is part of this hierarchy, false otherwise + */ +boolean contains(ICElement type); +/** + * Returns whether the type and project this hierarchy was created on exist. + * @return true if the type and project this hierarchy was created on exist, false otherwise + */ +boolean exists(); + +/** + * Returns all resolved subtypes (direct and indirect) of the + * given type, in no particular order, limited to the + * types in this type hierarchy's graph. An empty array + * is returned if there are no resolved subtypes for the + * given type. + * + * @param type the given type + * @return all resolved subtypes (direct and indirect) of the given type + */ +ICElement[] getAllSubtypes(ICElement type); +/** + * Returns all resolved superclasses of the + * given class, in bottom-up order. An empty array + * is returned if there are no resolved superclasses for the + * given class. + * + *

NOTE: once a type hierarchy has been created, it is more efficient to + * query the hierarchy for superclasses than to query a class recursively up + * the superclass chain. Querying an element performs a dynamic resolution, + * whereas the hierarchy returns a pre-computed result. + * + * @param type the given type + * @return all resolved superclasses of the given class, in bottom-up order, an empty + * array if none. + */ +ICElement[] getAllSupertypes(ICElement type); + +/** + * Returns all classes in the graph which have no resolved superclass, + * in no particular order. + * + * @return all classes in the graph which have no resolved superclass + */ +ICElement[] getRootClasses(); + +/** + * Returns the direct resolved subtypes of the given type, + * in no particular order, limited to the types in this + * type hierarchy's graph. + * If the type is a class, this returns the resolved subclasses. + * If the type is an interface, this returns both the classes which implement + * the interface and the interfaces which extend it. + * + * @param type the given type + * @return the direct resolved subtypes of the given type limited to the types in this + * type hierarchy's graph + */ +ICElement[] getSubtypes(ICElement type); + +/** + * Returns the resolved supertypes of the given type, + * in no particular order, limited to the types in this + * type hierarchy's graph. + * For classes, this returns its superclass and the interfaces that the class implements. + * For interfaces, this returns the interfaces that the interface extends. As a consequence + * java.lang.Object is NOT considered to be a supertype of any interface + * type. + * + * @param type the given type + * @return the resolved supertypes of the given type limited to the types in this + * type hierarchy's graph + */ +ICElement[] getSupertypes(ICElement type); +/** + * Returns the type this hierarchy was computed for. + * Returns null if this hierarchy was computed for a region. + * + * @return the type this hierarchy was computed for + */ +ICElement getType(); +/** + * Re-computes the type hierarchy reporting progress. + * + * @param monitor the given progress monitor + * @exception JavaModelException if unable to refresh the hierarchy + */ +void refresh(IProgressMonitor monitor) throws CModelException; +/** + * Removes the given listener from this type hierarchy. + * Has no affect if an identical listener is not registered. + * + * @param listener the listener + */ +void removeTypeHierarchyChangedListener(ITypeHierarchyChangedListener listener); +/** + * Stores the type hierarchy in an output stream. This stored hierarchy can be load by + * ICElement#loadTypeHierachy(IJavaProject, InputStream, IProgressMonitor). + * Listeners of this hierarchy are not stored. + * + * Only hierarchies created by the following methods can be store: + *