diff options
33 files changed, 5991 insertions, 106 deletions
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 8be21ebedbd..89c047ae7d2 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,15 +14,23 @@ 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; import org.eclipse.cdt.core.model.ICElement; -import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IElementChangedListener; import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.internal.core.browser.cache.ITypeCache; +import org.eclipse.cdt.internal.core.browser.cache.TypeCacheManager; import org.eclipse.cdt.internal.core.browser.util.ArrayUtil; +import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Preferences; import org.eclipse.core.runtime.Preferences.IPropertyChangeListener; +import org.eclipse.core.runtime.Preferences.PropertyChangeEvent; +import org.eclipse.core.runtime.jobs.Job; /** * Manages a search cache for types in the workspace. Instead of returning @@ -39,13 +47,92 @@ import org.eclipse.core.runtime.Preferences.IPropertyChangeListener; */ public class AllTypesCache { + private static final int INITIAL_DELAY = 5000; + private static IWorkingCopyProvider fgWorkingCopyProvider; + private static TypeHierarchyBuilder fgTypeHierarchyBuilder; + private static IElementChangedListener fgElementChangedListener; + private static IPropertyChangeListener fgPropertyChangeListener; + static boolean fgEnableIndexing = true; + + /** Preference key for enabling background cache */ + public final static String ENABLE_BACKGROUND_TYPE_CACHE = "enableBackgroundTypeCache"; //$NON-NLS-1$ + + /** + * Initializes the AllTypesCache service. + * + * @param provider A working copy provider. + */ + public static void initialize(IWorkingCopyProvider workingCopyProvider) { + fgWorkingCopyProvider = workingCopyProvider; + TypeCacheManager.getInstance().setWorkingCopyProvider(fgWorkingCopyProvider); + fgTypeHierarchyBuilder = new TypeHierarchyBuilder(); + + // load prefs +// Preferences prefs = CCorePlugin.getDefault().getPluginPreferences(); +// if (prefs.contains(ENABLE_BACKGROUND_TYPE_CACHE)) { +// fgEnableIndexing = prefs.getBoolean(ENABLE_BACKGROUND_TYPE_CACHE); +// } else { +// prefs.setDefault(ENABLE_BACKGROUND_TYPE_CACHE, false); +// prefs.setValue(ENABLE_BACKGROUND_TYPE_CACHE, false); +// CCorePlugin.getDefault().savePluginPreferences(); +// fgEnableIndexing = true; +// } + fgEnableIndexing = false; + + // start jobs in background after INITIAL_DELAY + TypeCacheManager.getInstance().reconcile(fgEnableIndexing, Job.BUILD, INITIAL_DELAY); + + // add delta listener + fgElementChangedListener = new IElementChangedListener() { + public void elementChanged(ElementChangedEvent event) { + TypeCacheManager.getInstance().processElementChanged(event, fgEnableIndexing); + } + }; + CoreModel.getDefault().addElementChangedListener(fgElementChangedListener); + +// // add property change listener +// fgPropertyChangeListener = new IPropertyChangeListener() { +// public void propertyChange(PropertyChangeEvent event) { +// String property = event.getProperty(); +// if (property.equals(ENABLE_BACKGROUND_TYPE_CACHE)) { +// String value = (String) event.getNewValue(); +// fgEnableIndexing = Boolean.valueOf(value).booleanValue(); +// if (!fgEnableIndexing) { +// TypeCacheManager.getInstance().cancelJobs(); +// } else { +// TypeCacheManager.getInstance().reconcile(fgEnableIndexing, Job.BUILD, 0); +// } +// } +// } +// }; +// prefs.addPropertyChangeListener(fgPropertyChangeListener); + } + + /** + * Terminates the service provided by AllTypesCache. + */ + public static void terminate() { + // remove delta listener + if (fgElementChangedListener != null) + CoreModel.getDefault().removeElementChangedListener(fgElementChangedListener); + + // remove property change listener + if (fgPropertyChangeListener != null) + CCorePlugin.getDefault().getPluginPreferences().removePropertyChangeListener(fgPropertyChangeListener); + + // terminate all running jobs + if (TypeCacheManager.getInstance() != null) { + TypeCacheManager.getInstance().cancelJobs(); + } + } + /** * Returns all types in the workspace. */ public static ITypeInfo[] getAllTypes() { final Collection fAllTypes = new ArrayList(); TypeSearchScope workspaceScope = new TypeSearchScope(true); - ICProject[] projects = workspaceScope.getEnclosingProjects(); + IProject[] projects = workspaceScope.getEnclosingProjects(); ITypeInfoVisitor visitor = new ITypeInfoVisitor() { public boolean visit(ITypeInfo info) { fAllTypes.add(info); @@ -54,7 +141,7 @@ public class AllTypesCache { public boolean shouldContinue() { return true; } }; for (int i = 0; i < projects.length; ++i) { -// TypeCacheManager.getInstance().getCache(projects[i]).accept(visitor); + TypeCacheManager.getInstance().getCache(projects[i]).accept(visitor); } return (ITypeInfo[]) fAllTypes.toArray(new ITypeInfo[fAllTypes.size()]); } @@ -70,7 +157,7 @@ public class AllTypesCache { final Collection fTypesFound = new ArrayList(); final ITypeSearchScope fScope = scope; final int[] fKinds = kinds; - ICProject[] projects = scope.getEnclosingProjects(); + IProject[] projects = scope.getEnclosingProjects(); ITypeInfoVisitor visitor = new ITypeInfoVisitor() { public boolean visit(ITypeInfo info) { if (ArrayUtil.contains(fKinds, info.getCElementType()) @@ -82,12 +169,51 @@ public class AllTypesCache { public boolean shouldContinue() { return true; } }; for (int i = 0; i < projects.length; ++i) { -// TypeCacheManager.getInstance().getCache(projects[i]).accept(visitor); + TypeCacheManager.getInstance().getCache(projects[i]).accept(visitor); } return (ITypeInfo[]) fTypesFound.toArray(new ITypeInfo[fTypesFound.size()]); } /** + * Returns all types matching name in the given scope. + * + * @param scope The search scope + * @param qualifiedName The qualified type name + * @param kinds Array containing CElement types: C_NAMESPACE, C_CLASS, + * C_UNION, C_ENUMERATION, C_TYPEDEF + * @param matchEnclosed <code>true</code> if enclosed types count as matches (foo::bar == bar) + */ + public static ITypeInfo[] getTypes(ITypeSearchScope scope, IQualifiedTypeName qualifiedName, int[] kinds, boolean matchEnclosed) { + final Collection fTypesFound = new ArrayList(); + final ITypeSearchScope fScope = scope; + final int[] fKinds = kinds; + final IQualifiedTypeName fQualifiedName = qualifiedName; + final boolean fMatchEnclosed = matchEnclosed; + IProject[] projects = scope.getEnclosingProjects(); + ITypeInfoVisitor visitor = new ITypeInfoVisitor() { + public boolean visit(ITypeInfo info) { + if (ArrayUtil.contains(fKinds, info.getCElementType()) + && (fScope != null && info.isEnclosed(fScope))) { + IQualifiedTypeName currName = info.getQualifiedTypeName(); + if (fMatchEnclosed && currName.segmentCount() > fQualifiedName.segmentCount() + && currName.lastSegment().equals(fQualifiedName.lastSegment())) { + currName = currName.removeFirstSegments(currName.segmentCount() - fQualifiedName.segmentCount()); + } + if (currName.equals(fQualifiedName)) { + fTypesFound.add(info); + } + } + return true; + } + public boolean shouldContinue() { return true; } + }; + for (int i = 0; i < projects.length; ++i) { + TypeCacheManager.getInstance().getCache(projects[i]).accept(visitor); + } + return (ITypeInfo[]) fTypesFound.toArray(new ITypeInfo[fTypesFound.size()]); + } + + /** * Returns all namespaces in the given scope. * * @param scope The search scope @@ -96,7 +222,7 @@ public class AllTypesCache { public static ITypeInfo[] getNamespaces(ITypeSearchScope scope, boolean includeGlobalNamespace) { final Collection fTypesFound = new ArrayList(); final ITypeSearchScope fScope = scope; - ICProject[] projects = scope.getEnclosingProjects(); + IProject[] projects = scope.getEnclosingProjects(); ITypeInfoVisitor visitor = new ITypeInfoVisitor() { public boolean visit(ITypeInfo info) { if (info.getCElementType() == ICElement.C_NAMESPACE @@ -108,15 +234,76 @@ public class AllTypesCache { public boolean shouldContinue() { return true; } }; for (int i = 0; i < projects.length; ++i) { -// ITypeCache cache = TypeCacheManager.getInstance().getCache(projects[i]); -// cache.accept(visitor); -// if (includeGlobalNamespace) { -// fTypesFound.add(cache.getGlobalNamespace()); -// } + ITypeCache cache = TypeCacheManager.getInstance().getCache(projects[i]); + cache.accept(visitor); + if (includeGlobalNamespace) { + fTypesFound.add(cache.getGlobalNamespace()); + } } return (ITypeInfo[]) fTypesFound.toArray(new ITypeInfo[fTypesFound.size()]); } + /** + * Returns the global (default) namespace for the given project. + * + * @param project the project + */ + public static ITypeInfo getGlobalNamespace(IProject project) { + ITypeCache cache = TypeCacheManager.getInstance().getCache(project); + return cache.getGlobalNamespace(); + } + + /** + * Returns true if the type cache is up to date. + */ + public static boolean isCacheUpToDate(ITypeSearchScope scope) { + forceDeltaComplete(); + + IProject[] projects = scope.getEnclosingProjects(); + for (int i = 0; i < projects.length; ++i) { + IProject project = projects[i]; + if (project.exists() && project.isOpen()) { + if (!TypeCacheManager.getInstance().getCache(project).isUpToDate()) + return false; + } + } + return true; + } + + private static void forceDeltaComplete() { + if (fgWorkingCopyProvider != null) { + IWorkingCopy[] workingCopies = fgWorkingCopyProvider.getWorkingCopies(); + for (int i = 0; i < workingCopies.length; ++i) { + IWorkingCopy wc = workingCopies[i]; + try { + synchronized (wc) { + wc.reconcile(); + } + } catch (CModelException ex) { + } + } + } + } + + /** + * Updates the type cache. + * + * @param monitor the progress monitor + */ + public static void updateCache(ITypeSearchScope scope, IProgressMonitor monitor) { + TypeCacheManager.getInstance().updateCache(scope, monitor); + } + + /** + * Resolves a type location. + * + * @param info the type to search for + * @param monitor the progress monitor + */ + public static ITypeReference resolveTypeLocation(ITypeInfo info, IProgressMonitor monitor) { + return TypeCacheManager.getInstance().resolveTypeLocation(info, monitor, fgEnableIndexing); + } + /** Returns first type in the cache which matches the given * type and name. If no type is found, <code>null</code> * is returned. @@ -126,10 +313,9 @@ public class AllTypesCache { * @param qualifiedName the qualified type name to match * @return the matching type */ - public static ITypeInfo getType(ICProject project, int type, IQualifiedTypeName qualifiedName) { -// ITypeCache cache = TypeCacheManager.getInstance().getCache(project); -// return cache.getType(type, qualifiedName); - return null; + public static ITypeInfo getType(IProject project, int type, IQualifiedTypeName qualifiedName) { + ITypeCache cache = TypeCacheManager.getInstance().getCache(project); + return cache.getType(type, qualifiedName); } /** @@ -141,10 +327,39 @@ public class AllTypesCache { * @param ignoreCase <code>true</code> if case-insensitive * @return Array of types */ - public static ITypeInfo[] getTypes(ICProject project, IQualifiedTypeName qualifiedName, boolean matchEnclosed, boolean ignoreCase) { -// ITypeCache cache = TypeCacheManager.getInstance().getCache(project); -// return cache.getTypes(qualifiedName, matchEnclosed, ignoreCase); - return new ITypeInfo[0]; + public static ITypeInfo[] getTypes(IProject project, IQualifiedTypeName qualifiedName, boolean matchEnclosed, boolean ignoreCase) { + ITypeCache cache = TypeCacheManager.getInstance().getCache(project); + return cache.getTypes(qualifiedName, matchEnclosed, ignoreCase); } + /** + * 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 = TypeCacheManager.getInstance().getTypeForElement(type, true, true, fgEnableIndexing, monitor); + if (info != null) + return fgTypeHierarchyBuilder.createTypeHierarchy(info, fgEnableIndexing, monitor); + return null; + } + + public static void addTypeCacheChangedListener(ITypeCacheChangedListener listener) { + TypeCacheManager.getInstance().addTypeCacheChangedListener(listener); + } + + public static void removeTypeCacheChangedListener(ITypeCacheChangedListener listener) { + TypeCacheManager.getInstance().removeTypeCacheChangedListener(listener); + } + + public static ITypeInfo getTypeForElement(ICElement element, boolean forceUpdate, boolean forceResolve, IProgressMonitor monitor) { + return TypeCacheManager.getInstance().getTypeForElement(element, forceUpdate, forceResolve, fgEnableIndexing, monitor); + } + + public static ICElement getElementForType(ITypeInfo type, boolean forceUpdate, boolean forceResolve, IProgressMonitor monitor) { + return TypeCacheManager.getInstance().getElementForType(type, forceUpdate, forceResolve, fgEnableIndexing, monitor); + } } diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeCacheChangedListener.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeCacheChangedListener.java new file mode 100644 index 00000000000..a7e7bce5d19 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeCacheChangedListener.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.browser; + +import org.eclipse.core.resources.IProject; + + +/** + * A listener which gets notified when the type cache changes. + * <p> + * This interface may be implemented by clients. + * </p> + */ +public interface ITypeCacheChangedListener { + + /** + * Notifies that the type cache for the given project has changed in some way + * and should be refreshed at some point to make it consistent with the current + * state of the C model. + * + * @param project the given project + */ + void typeCacheChanged(IProject project); +} 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 5b10ac987a2..b985dbf27f6 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,8 +11,9 @@ package org.eclipse.cdt.core.browser; import org.eclipse.cdt.core.model.ICElement; -import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; +import org.eclipse.cdt.internal.core.browser.cache.ITypeCache; +import org.eclipse.core.resources.IProject; /** * Type information. @@ -126,7 +127,7 @@ public interface ITypeInfo extends Comparable { /** * Gets the enclosing project. */ - public ICProject getEnclosingProject(); + public IProject getEnclosingProject(); /** * Returns true if type is enclosed in the given scope. @@ -155,6 +156,9 @@ public interface ITypeInfo extends Comparable { */ public boolean canSubstituteFor(ITypeInfo info); + public ITypeCache getCache(); + public void setCache(ITypeCache typeCache); + /** * Returns true if other types extend this type. */ diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeSearchScope.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeSearchScope.java index 657bad5467d..269d3c9a8f5 100644 --- a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeSearchScope.java +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/ITypeSearchScope.java @@ -13,8 +13,8 @@ package org.eclipse.cdt.core.browser; import java.util.Collection; import org.eclipse.cdt.core.model.ICElement; -import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; public interface ITypeSearchScope { @@ -25,20 +25,20 @@ public interface ITypeSearchScope { public boolean isEmpty(); public boolean encloses(ITypeSearchScope scope); - public boolean encloses(ICProject project); + public boolean encloses(IProject project); public boolean encloses(IPath path); public boolean encloses(String path); public boolean encloses(ICElement element); public boolean encloses(IWorkingCopy workingCopy); public void add(IWorkingCopy workingCopy); - public void add(IPath path, boolean addSubfolders, ICProject enclosingProject); - public void add(ICProject project); + public void add(IPath path, boolean addSubfolders, IProject enclosingProject); + public void add(IProject project); public void add(ICElement elem); public void add(ITypeSearchScope scope); public void addWorkspace(); public void clear(); - public ICProject[] getEnclosingProjects(); + public IProject[] getEnclosingProjects(); public Collection pathSet(); public Collection containerSet(); diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/PathUtil.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/PathUtil.java index 38cafa24771..99750e02091 100644 --- a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/PathUtil.java +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/PathUtil.java @@ -14,8 +14,6 @@ import java.io.File; import java.io.IOException; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.model.CoreModel; -import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.core.parser.IScannerInfoProvider; import org.eclipse.core.resources.IProject; @@ -132,14 +130,14 @@ public class PathUtil { return relativePath; } - public static ICProject getEnclosingProject(IPath fullPath) { + public static IProject getEnclosingProject(IPath fullPath) { IWorkspaceRoot root = getWorkspaceRoot(); if (root != null) { IPath path = getWorkspaceRelativePath(fullPath); while (!path.isEmpty()) { IResource res = root.findMember(path); if (res != null) - return CoreModel.getDefault().create(res.getProject()); + return res.getProject(); path = path.removeLastSegments(1); } 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 5492c812bbd..9a10b78da9d 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 @@ -11,13 +11,14 @@ package org.eclipse.cdt.core.browser; import org.eclipse.cdt.core.model.ICElement; -import org.eclipse.cdt.core.model.ICProject; 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; public class TypeInfo implements ITypeInfo { -// protected ITypeCache fTypeCache; + protected ITypeCache fTypeCache; protected int fElementType; protected IQualifiedTypeName fQualifiedName; protected ITypeReference[] fSourceRefs = null; @@ -104,8 +105,8 @@ public class TypeInfo implements ITypeInfo return false; if (fElementType == info.getCElementType() && fQualifiedName.equals(info.getQualifiedTypeName())) { - ICProject project1 = getEnclosingProject(); - ICProject project2 = info.getEnclosingProject(); + IProject project1 = getEnclosingProject(); + IProject project2 = info.getEnclosingProject(); if (project1 == null && project2 == null) return true; if (project1 == null || project2 == null) @@ -116,8 +117,7 @@ public class TypeInfo implements ITypeInfo } public boolean exists() { -// return fTypeCache != null; - return true; + return fTypeCache != null; } public int getCElementType() { @@ -141,9 +141,9 @@ public class TypeInfo implements ITypeInfo } public ITypeInfo getEnclosingType(int kinds[]) { -// if (fTypeCache != null) { -// return fTypeCache.getEnclosingType(this, kinds); -// } + if (fTypeCache != null) { + return fTypeCache.getEnclosingType(this, kinds); + } return null; } @@ -152,16 +152,16 @@ public class TypeInfo implements ITypeInfo } public ITypeInfo getEnclosingNamespace(boolean includeGlobalNamespace) { -// if (fTypeCache != null) { -// return fTypeCache.getEnclosingNamespace(this, includeGlobalNamespace); -// } + if (fTypeCache != null) { + return fTypeCache.getEnclosingNamespace(this, includeGlobalNamespace); + } return null; } public ITypeInfo getRootNamespace(boolean includeGlobalNamespace) { -// if (fTypeCache != null) { -// return fTypeCache.getRootNamespace(this, includeGlobalNamespace); -// } + if (fTypeCache != null) { + return fTypeCache.getRootNamespace(this, includeGlobalNamespace); + } return null; } @@ -172,9 +172,9 @@ public class TypeInfo implements ITypeInfo } public boolean encloses(ITypeInfo info) { -// if (isEnclosingType() && fTypeCache == info.getCache()) { -// return fQualifiedName.isPrefixOf(info.getQualifiedTypeName()); -// } + if (isEnclosingType() && fTypeCache == info.getCache()) { + return fQualifiedName.isPrefixOf(info.getQualifiedTypeName()); + } return false; } @@ -183,9 +183,9 @@ public class TypeInfo implements ITypeInfo } public boolean hasEnclosedTypes() { -// if (isEnclosingType() && fTypeCache != null) { -// return fTypeCache.hasEnclosedTypes(this); -// } + if (isEnclosingType() && fTypeCache != null) { + return fTypeCache.hasEnclosedTypes(this); + } return false; } @@ -194,16 +194,16 @@ public class TypeInfo implements ITypeInfo } public ITypeInfo[] getEnclosedTypes(int kinds[]) { -// if (fTypeCache != null) { -// return fTypeCache.getEnclosedTypes(this, kinds); -// } + if (fTypeCache != null) { + return fTypeCache.getEnclosedTypes(this, kinds); + } return EMPTY_TYPES; } - public ICProject getEnclosingProject() { -// if (fTypeCache != null) { -// return fTypeCache.getProject(); -// } + public IProject getEnclosingProject() { + if (fTypeCache != null) { + return fTypeCache.getProject(); + } return null; } @@ -227,7 +227,7 @@ public class TypeInfo implements ITypeInfo public int hashCode() { int hashCode = fQualifiedName.hashCode() + fElementType; - ICProject project = getEnclosingProject(); + IProject project = getEnclosingProject(); if (project != null) hashCode += project.hashCode(); return hashCode; @@ -260,6 +260,14 @@ public class TypeInfo implements ITypeInfo return ArrayUtil.contains(KNOWN_TYPES, type); } + public ITypeCache getCache() { + return fTypeCache; + } + + public void setCache(ITypeCache typeCache) { + fTypeCache = typeCache; + } + public void addDerivedReference(ITypeReference location) { if (fDerivedSourceRefs == null) { fDerivedSourceRefs = new ITypeReference[INITIAL_REFS_SIZE]; @@ -287,31 +295,31 @@ public class TypeInfo implements ITypeInfo } public ITypeInfo[] getSubTypes() { -// if (fTypeCache != null) { -// return fTypeCache.getSubtypes(this); -// } + if (fTypeCache != null) { + return fTypeCache.getSubtypes(this); + } return null; } public boolean hasSuperTypes() { -// if (fTypeCache != null) { -// return (fTypeCache.getSupertypes(this) != null); -// } + 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); -// } + if (fTypeCache != null) { + return fTypeCache.getSupertypes(this); + } return null; } public ASTAccessVisibility getSuperTypeAccess(ITypeInfo superType) { -// if (fTypeCache != null) { -// return fTypeCache.getSupertypeAccess(this, superType); -// } + if (fTypeCache != null) { + return fTypeCache.getSupertypeAccess(this, superType); + } return null; } diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeSearchScope.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeSearchScope.java index bc162199f10..c29ea790f9a 100644 --- a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeSearchScope.java +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/TypeSearchScope.java @@ -16,13 +16,15 @@ import java.util.Iterator; import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.model.CModelException; -import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.CProjectNature; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IWorkingCopy; import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.core.parser.IScannerInfoProvider; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; @@ -35,8 +37,8 @@ public class TypeSearchScope implements ITypeSearchScope { private boolean fWorkspaceScope = false; // cached arrays - private ICProject[] fAllProjects = null; - private ICProject[] fProjects = null; + private IProject[] fAllProjects = null; + private IProject[] fProjects = null; private IPath[] fContainerPaths = null; public TypeSearchScope() { @@ -50,7 +52,7 @@ public class TypeSearchScope implements ITypeSearchScope { add(scope); } - public TypeSearchScope(ICProject project) { + public TypeSearchScope(IProject project) { add(project); } @@ -92,7 +94,7 @@ public class TypeSearchScope implements ITypeSearchScope { if (!scope.projectSet().isEmpty()) { // check if this scope encloses the other scope's projects for (Iterator i = scope.projectSet().iterator(); i.hasNext(); ) { - ICProject project = (ICProject) i.next(); + IProject project = (IProject) i.next(); if (!encloses(project)) return false; } @@ -101,7 +103,7 @@ public class TypeSearchScope implements ITypeSearchScope { return true; } - public boolean encloses(ICProject project) { + public boolean encloses(IProject project) { if (isWorkspaceScope()) return true; @@ -140,7 +142,7 @@ public class TypeSearchScope implements ITypeSearchScope { // check projects that were explicity added to scope if (fProjects == null) { - fProjects = (ICProject[]) fProjectSet.toArray(new ICProject[fProjectSet.size()]); + fProjects = (IProject[]) fProjectSet.toArray(new IProject[fProjectSet.size()]); } // check if one of the projects contains path for (int i = 0; i < fProjects.length; ++i) { @@ -164,15 +166,15 @@ public class TypeSearchScope implements ITypeSearchScope { return encloses(workingCopy.getOriginalElement().getPath()); } - public ICProject[] getEnclosingProjects() { + public IProject[] getEnclosingProjects() { if (isWorkspaceScope()) { return getAllProjects(); } - return (ICProject[]) fEnclosingProjectSet.toArray(new ICProject[fEnclosingProjectSet.size()]); + return (IProject[]) fEnclosingProjectSet.toArray(new IProject[fEnclosingProjectSet.size()]); } - private static boolean projectContainsPath(ICProject project, IPath path, boolean checkIncludePaths) { - IPath projectPath = project.getProject().getFullPath(); + private static boolean projectContainsPath(IProject project, IPath path, boolean checkIncludePaths) { + IPath projectPath = project.getFullPath(); if (projectPath.isPrefixOf(path)) { // ISourceRoot[] sourceRoots = null; // try { @@ -204,10 +206,10 @@ public class TypeSearchScope implements ITypeSearchScope { return false; } - private static IPath[] getIncludePaths(ICProject project) { - IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project.getProject()); + private static IPath[] getIncludePaths(IProject project) { + IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project); if (provider != null) { - IScannerInfo info = provider.getScannerInformation(project.getProject()); + IScannerInfo info = provider.getScannerInformation(project); if (info != null) { String[] includes = info.getIncludePaths(); if (includes != null && includes.length > 0) { @@ -223,22 +225,56 @@ public class TypeSearchScope implements ITypeSearchScope { return null; } - private static ICProject[] getAllProjects() { - ICProject[] projects = getCProjects(); + private static IProject[] getAllProjects() { + IProject[] projects = getCProjects(); if (projects == null) - projects = new ICProject[0]; + projects = new IProject[0]; return projects; } - private static ICProject[] getCProjects() { - try { - return CoreModel.getDefault().getCModel().getCProjects(); - } catch (CModelException e) { - CCorePlugin.log(e); - return new ICProject[0]; + private static IProject[] getCProjects() { + IProject[] allProjects = CCorePlugin.getWorkspace().getRoot().getProjects(); + if (allProjects != null) { + IProject[] cProjects = new IProject[allProjects.length]; + int count = 0; + for (int i = 0; i < allProjects.length; ++i) { + IProject project = allProjects[i]; + if (isCProject(project)) { + cProjects[count++] = project; + } + } + if (count > 0) { + if (count == allProjects.length) { + return cProjects; + } + IProject[] newProjects = new IProject[count]; + System.arraycopy(cProjects, 0, newProjects, 0, count); + return newProjects; + } } + return null; } + private static boolean isCProject(IProject project) { + IProjectDescription projDesc = null; + try { + projDesc = project.getDescription(); + if (projDesc == null) + return false; + } catch (CoreException e) { + return false; + } + String[] natures = projDesc.getNatureIds(); + if (natures != null) { + for (int i = 0; i < natures.length; ++i) { + if (natures[i].equals(CProjectNature.C_NATURE_ID)) { + return true; + } + } + } + return false; + } + public boolean isPathScope() { return !fPathSet.isEmpty(); } @@ -257,12 +293,16 @@ public class TypeSearchScope implements ITypeSearchScope { public void add(IWorkingCopy workingCopy) { IPath path = workingCopy.getOriginalElement().getPath(); + IProject enclosingProject = null; ICProject cProject = workingCopy.getCProject(); + if (cProject != null) + enclosingProject = cProject.getProject(); fPathSet.add(path); - addEnclosingProject(cProject); + if (enclosingProject != null) + addEnclosingProject(enclosingProject); } - public void add(IPath path, boolean addSubfolders, ICProject enclosingProject) { + public void add(IPath path, boolean addSubfolders, IProject enclosingProject) { if (addSubfolders) { fContainerSet.add(path); fContainerPaths = null; @@ -286,14 +326,14 @@ public class TypeSearchScope implements ITypeSearchScope { } } - public void add(ICProject project) { + public void add(IProject project) { fProjectSet.add(project); fProjects = null; fAllProjects = null; addEnclosingProject(project); } - private void addEnclosingProject(ICProject project) { + private void addEnclosingProject(IProject project) { fEnclosingProjectSet.add(project); } @@ -314,19 +354,25 @@ public class TypeSearchScope implements ITypeSearchScope { } case ICElement.C_PROJECT: { - ICProject project = ((ICProject)elem); + IProject project = ((ICProject)elem).getProject(); add(project); break; } case ICElement.C_CCONTAINER: { - ICProject project = elem.getCProject(); + IProject project = null; + ICProject cProject = elem.getCProject(); + if (cProject != null) + project = cProject.getProject(); add(elem.getPath(), true, project); break; } case ICElement.C_UNIT: { - ICProject project = elem.getCProject(); + IProject project = null; + ICProject cProject = elem.getCProject(); + if (cProject != null) + project = cProject.getProject(); add(elem.getPath(), false, project); break; } @@ -339,7 +385,10 @@ public class TypeSearchScope implements ITypeSearchScope { case ICElement.C_UNION: case ICElement.C_ENUMERATION: case ICElement.C_TYPEDEF: { - ICProject project = elem.getCProject(); + IProject project = null; + ICProject cProject = elem.getCProject(); + if (cProject != null) + project = cProject.getProject(); add(elem.getPath(), false, project); break; } 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 index 3c2e9ca898e..9c86849c845 100644 --- 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 @@ -13,6 +13,7 @@ package org.eclipse.cdt.core.browser; 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.ICElement; import org.eclipse.cdt.core.model.IMember; @@ -20,6 +21,7 @@ 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; public class TypeUtil { @@ -285,4 +287,33 @@ public class TypeUtil { 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. <code>{"QString;","I"}</code> + * @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.getDeclaringClass(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/UnknownTypeInfo.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/UnknownTypeInfo.java index a36c390e6a4..c31921f596c 100644 --- a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/UnknownTypeInfo.java +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/UnknownTypeInfo.java @@ -30,9 +30,11 @@ public class UnknownTypeInfo extends TypeInfo { } public boolean canSubstituteFor(ITypeInfo info) { - int compareType = info.getCElementType(); - if (fElementType == 0 || compareType == 0 || fElementType == compareType) { - return fQualifiedName.equals(info.getQualifiedTypeName()); + if (fTypeCache == info.getCache()) { + int compareType = info.getCElementType(); + if (fElementType == 0 || compareType == 0 || fElementType == compareType) { + return fQualifiedName.equals(info.getQualifiedTypeName()); + } } return false; } 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..71f9fe0f775 --- /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 Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package 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(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..befed906b67 --- /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 Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package 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 <code>java.io.File</code>, and the region the hierarchy was + * created in is the package fragment <code>java.io</code>, the supertype + * <code>java.lang.Object</code> will still be included. + * <p> + * 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 <code>ICElementHierarchyChangedListener</code>s. Listeners should + * use the <code>exists</code> 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 <code>refresh</code> method. + * </p> + * <p> + * The type hierarchy may contain cycles due to malformed supertype declarations. + * Most type hierarchy queries are oblivious to cycles; the <code>getAll* </code> + * methods are implemented such that they are unaffected by cycles. + * </p> + * <p> + * This interface is not intended to be implemented by clients. + * </p> + */ +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. + * + * <p>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 + * <code>java.lang.Object</code> 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 <code>null</code> 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: + * <ul> + * <li>ICElement#newSupertypeHierarchy(IProgressMonitor)</li> + * <li>ICElement#newTypeHierarchy(IJavaProject, IProgressMonitor)</li> + * <li>ICElement#newTypeHierarchy(IProgressMonitor)</li> + * </u> + * + * @param outputStream output stream where the hierarchy will be stored + * @param monitor the given progress monitor + * @exception JavaModelException if unable to store the hierarchy in the ouput stream + * @see ICElement#loadTypeHierachy(java.io.InputStream, IProgressMonitor) + * @since 2.1 + */ +void store(OutputStream outputStream, IProgressMonitor monitor) throws CModelException; +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ITypeHierarchyChangedListener.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ITypeHierarchyChangedListener.java new file mode 100644 index 00000000000..cc985600ab8 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/ITypeHierarchyChangedListener.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.browser.typehierarchy; + +/** + * A listener which gets notified when a particular type hierarchy object + * changes. + * <p> + * This interface may be implemented by clients. + * </p> + */ +public interface ITypeHierarchyChangedListener { + /** + * Notifies that the given type hierarchy has changed in some way and should + * be refreshed at some point to make it consistent with the current state of + * the Java model. + * + * @param typeHierarchy the given type hierarchy + */ + void typeHierarchyChanged(ITypeHierarchy typeHierarchy); +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchy.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchy.java new file mode 100644 index 00000000000..5288ff7e649 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchy.java @@ -0,0 +1,582 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.browser.typehierarchy; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.core.ICLogConstants; +import org.eclipse.cdt.core.browser.AllTypesCache; +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ElementChangedEvent; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICElementDelta; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.IElementChangedListener; +import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; +import org.eclipse.cdt.core.search.ICSearchScope; +import org.eclipse.cdt.internal.core.model.Util; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.Platform; + +public class TypeHierarchy implements ITypeHierarchy, IElementChangedListener { + + public static boolean DEBUG = false; + + private static final class TypeEntry { + ITypeInfo type; + ASTAccessVisibility access; + TypeEntry(ITypeInfo type, ASTAccessVisibility access) { + this.type = type; + this.access = access; + } + } + private static final int INITIAL_SUPER_TYPES = 1; + private static final int INITIAL_SUB_TYPES = 1; + private static final ITypeInfo[] NO_TYPES = new ITypeInfo[0]; + private ArrayList fRootTypes = new ArrayList(); + private Map fTypeToSuperTypes = new HashMap(); + private Map fTypeToSubTypes = new HashMap(); + + private ITypeInfo fFocusType; + + /** + * The progress monitor to report work completed too. + */ + protected IProgressMonitor fProgressMonitor = null; + /** + * Change listeners - null if no one is listening. + */ + protected ArrayList fChangeListeners = null; + + /* + * A map from Openables to ArrayLists of ITypes + */ + public Map files = null; + + /** + * Whether this hierarchy should contains subtypes. + */ + protected boolean fComputeSubtypes; + + /** + * The scope this hierarchy should restrain itsef in. + */ + ICSearchScope fScope; + + /* + * Whether this hierarchy needs refresh + */ + public boolean fNeedsRefresh = true; +// /* +// * Collects changes to types +// */ +// protected ChangeCollector fChangeCollector; + + + + /** + * Creates a TypeHierarchy on the given type. + */ + public TypeHierarchy(ITypeInfo type) { + fFocusType = type; + } + + /** + * Adds the type to the collection of root classes + * if the classes is not already present in the collection. + */ + public void addRootType(ITypeInfo type) { + if (!fRootTypes.contains(type)) { + fRootTypes.add(type); + } + } + + /** + * Adds the given supertype to the type. + */ + public void addSuperType(ITypeInfo type, ITypeInfo superType, ASTAccessVisibility access) { + Collection superEntries = (Collection) fTypeToSuperTypes.get(type); + if (superEntries == null) { + superEntries = new ArrayList(INITIAL_SUPER_TYPES); + fTypeToSuperTypes.put(type, superEntries); + } + Collection subTypes = (Collection) fTypeToSubTypes.get(superType); + if (subTypes == null) { + subTypes = new ArrayList(INITIAL_SUB_TYPES); + fTypeToSubTypes.put(superType, subTypes); + } + if (!subTypes.contains(type)) { + subTypes.add(type); + } + for (Iterator i = superEntries.iterator(); i.hasNext(); ) { + TypeEntry entry = (TypeEntry)i.next(); + if (entry.type.equals(superType)) { + // update the access + entry.access = access; + return; // don't add if already exists + } + } + TypeEntry typeEntry = new TypeEntry(superType, access); + superEntries.add(typeEntry); + } + + /** + * Adds the given subtype to the type. + */ + protected void addSubType(ITypeInfo type, ITypeInfo subType) { + Collection subTypes = (Collection) fTypeToSubTypes.get(type); + if (subTypes == null) { + subTypes = new ArrayList(INITIAL_SUB_TYPES); + fTypeToSubTypes.put(type, subTypes); + } + if (!subTypes.contains(subType)) { + subTypes.add(subType); + } + + Collection superEntries = (Collection) fTypeToSuperTypes.get(subType); + if (superEntries == null) { + superEntries = new ArrayList(INITIAL_SUPER_TYPES); + fTypeToSuperTypes.put(subType, superEntries); + } + for (Iterator i = superEntries.iterator(); i.hasNext(); ) { + TypeEntry entry = (TypeEntry)i.next(); + if (entry.type.equals(type)) + return; // don't add if already exists + } + // default to private access + TypeEntry typeEntry = new TypeEntry(type, ASTAccessVisibility.PRIVATE); + superEntries.add(typeEntry); + } + + /** + * Returns true if type already has the given supertype. + */ + public boolean hasSuperType(ITypeInfo type, ITypeInfo superType) { + Collection entries = (Collection) fTypeToSuperTypes.get(type); + if (entries != null) { + for (Iterator i = entries.iterator(); i.hasNext(); ) { + TypeEntry entry = (TypeEntry)i.next(); + if (entry.type.equals(superType)) + return true; + } + } + return false; + } + + /** + * Returns an array of supertypes for the given type - will never return null. + */ + public ITypeInfo[] getSuperTypes(ITypeInfo type) { + Collection entries = (Collection) fTypeToSuperTypes.get(type); + if (entries != null) { + ArrayList superTypes = new ArrayList(INITIAL_SUPER_TYPES); + for (Iterator i = entries.iterator(); i.hasNext(); ) { + TypeEntry entry = (TypeEntry)i.next(); + superTypes.add(entry.type); + } + return (ITypeInfo[])superTypes.toArray(new ITypeInfo[superTypes.size()]); + } + return NO_TYPES; + } + + /** + * Returns an array of subtypes for the given type - will never return null. + */ + public ITypeInfo[] getSubTypes(ITypeInfo type) { + Collection subTypes = (Collection) fTypeToSubTypes.get(type); + if (subTypes != null) { + return (ITypeInfo[])subTypes.toArray(new ITypeInfo[subTypes.size()]); + } + return NO_TYPES; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#addTypeHierarchyChangedListener(org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchyChangedListener) + */ + public void addTypeHierarchyChangedListener(ITypeHierarchyChangedListener listener) { + ArrayList listeners = fChangeListeners; + if (listeners == null) { + fChangeListeners = listeners = new ArrayList(); + } + + // register with JavaCore to get Java element delta on first listener added + if (listeners.size() == 0) { + CoreModel.getDefault().addElementChangedListener(this); + } + + // add listener only if it is not already present + if (listeners.indexOf(listener) == -1) { + listeners.add(listener); + } + } + + + /** + * @see ITypeHierarchy + */ + public synchronized void removeTypeHierarchyChangedListener(ITypeHierarchyChangedListener listener) { + ArrayList listeners = fChangeListeners; + if (listeners == null) { + return; + } + listeners.remove(listener); + + // deregister from JavaCore on last listener removed + if (listeners.isEmpty()) { + CoreModel.getDefault().removeElementChangedListener(this); + } + } + + /** + * Determines if the change effects this hierarchy, and fires + * change notification if required. + */ + public void elementChanged(ElementChangedEvent event) { + // type hierarchy change has already been fired + if (fNeedsRefresh) return; + + if (isAffected(event.getDelta())) { + fNeedsRefresh = true; + fireChange(); + } + } + + /** + * Returns true if the given delta could change this type hierarchy + */ + public synchronized boolean isAffected(ICElementDelta delta) { +// ICElement element= delta.getElement(); +// switch (element.getElementType()) { +// case ICElement.C_MODEL: +// return isAffectedByCModel(delta, element); +// case ICElement.C_PROJECT: +// return isAffectedByCProject(delta, element); +// case ICElement.C_UNIT: +// return isAffectedByOpenable(delta, element); +// } +// return false; + return true; + } + + /** + * Notifies listeners that this hierarchy has changed and needs + * refreshing. Note that listeners can be removed as we iterate + * through the list. + */ + public void fireChange() { + ArrayList listeners = fChangeListeners; + if (listeners == null) { + return; + } + if (DEBUG) { + System.out.println("FIRING hierarchy change ["+Thread.currentThread()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ + if (fFocusType != null) { + System.out.println(" for hierarchy focused on " + fFocusType.toString()); //$NON-NLS-1$ + } + } + // clone so that a listener cannot have a side-effect on this list when being notified + listeners = (ArrayList)listeners.clone(); + for (int i= 0; i < listeners.size(); i++) { + final ITypeHierarchyChangedListener listener= (ITypeHierarchyChangedListener)listeners.get(i); + Platform.run(new ISafeRunnable() { + public void handleException(Throwable exception) { + Util.log(exception, "Exception occurred in listener of Type hierarchy change notification", ICLogConstants.CDT); //$NON-NLS-1$ + } + public void run() throws Exception { + listener.typeHierarchyChanged(TypeHierarchy.this); + } + }); + } + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#contains(org.eclipse.cdt.core.model.ICElement) + */ + public boolean contains(ICElement type) { + // classes + ITypeInfo info = AllTypesCache.getTypeForElement(type, true, true, null); + + if (info == null) + return false; + + if (fTypeToSuperTypes.get(info) != null) { + return true; + } + + // root classes + if (fRootTypes.contains(type)) return true; + + return false; + } + + /** + * @see ITypeHierarchy + */ + public boolean exists() { + if (!fNeedsRefresh) return true; + + return (fFocusType == null || fFocusType.exists()) && cProject().exists(); + } + + /** + * Returns the C project this hierarchy was created in. + */ + public ICProject cProject() { + IProject project = fFocusType.getCache().getProject(); + return findCProject(project); + } + private ICProject findCProject(IProject project) { + try { + ICProject[] cProjects = CoreModel.getDefault().getCModel().getCProjects(); + if (cProjects != null) { + for (int i = 0; i < cProjects.length; ++i) { + ICProject cProject = cProjects[i]; + if (project.equals(cProjects[i].getProject())) + return cProject; + } + } + } catch (CModelException e) { + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#getAllClasses() + */ + public ICElement[] getAllClasses() { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#getRootClasses() + */ + public ICElement[] getRootClasses() { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#getSubtypes(org.eclipse.cdt.core.model.ICElement) + */ + public ICElement[] getSubtypes(ICElement type) { + List list = new ArrayList(); + ITypeInfo info = AllTypesCache.getTypeForElement(type, true, true, null); + Collection entries = (Collection) fTypeToSubTypes.get(info); + if (entries != null) { + for (Iterator i = entries.iterator(); i.hasNext(); ) { + ITypeInfo subType = (ITypeInfo)i.next(); + ICElement elem = AllTypesCache.getElementForType(subType, true, true, null); + if (elem != null) { + list.add(elem); + } + } + } + return (ICElement[])list.toArray(new ICElement[list.size()]); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#getAllSuperclasses(org.eclipse.cdt.core.model.ICElement) + */ + public ICElement[] getAllSubtypes(ICElement type) { + List list = new ArrayList(); + ITypeInfo info = AllTypesCache.getTypeForElement(type, true, true, null); + addSubs(info, list); + //convert list to ICElements + ICElement[] elems = new ICElement[list.size()]; + int count = 0; + for (Iterator i = list.iterator(); i.hasNext(); ) { + ITypeInfo subType = (ITypeInfo) i.next(); + elems[count++] = AllTypesCache.getElementForType(subType, true, true, null); + } + return elems; + } + + private void addSubs(ITypeInfo type, List list) { + Collection entries = (Collection) fTypeToSubTypes.get(type); + if (entries != null) { + for (Iterator i = entries.iterator(); i.hasNext(); ) { + ITypeInfo subType = (ITypeInfo)i.next(); + if (!list.contains(subType)) { + list.add(subType); + } + addSubs(subType, list); + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#getSupertypes(org.eclipse.cdt.core.model.ICElement) + */ + public ICElement[] getSupertypes(ICElement type) { + List list = new ArrayList(); + ITypeInfo info = AllTypesCache.getTypeForElement(type, true, true, null); + Collection entries = (Collection) fTypeToSuperTypes.get(info); + if (entries != null) { + for (Iterator i = entries.iterator(); i.hasNext(); ) { + TypeEntry entry = (TypeEntry)i.next(); + ITypeInfo superType = entry.type; + ICElement elem = AllTypesCache.getElementForType(superType, true, true, null); + if (elem != null) { + list.add(elem); + } + } + } + return (ICElement[])list.toArray(new ICElement[list.size()]); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#getAllSuperclasses(org.eclipse.cdt.core.model.ICElement) + */ + public ICElement[] getAllSupertypes(ICElement type) { + List list = new ArrayList(); + ITypeInfo info = AllTypesCache.getTypeForElement(type, true, true, null); + addSupers(info, list); + //convert list to ICElements + ICElement[] elems = new ICElement[list.size()]; + int count = 0; + for (Iterator i = list.iterator(); i.hasNext(); ) { + ITypeInfo superType = (ITypeInfo) i.next(); + elems[count++] = AllTypesCache.getElementForType(superType, true, true, null); + } + return elems; + } + + private void addSupers(ITypeInfo type, List list) { + Collection entries = (Collection) fTypeToSuperTypes.get(type); + if (entries != null) { + for (Iterator i = entries.iterator(); i.hasNext(); ) { + TypeEntry entry = (TypeEntry)i.next(); + ITypeInfo superType = entry.type; + if (!list.contains(superType)) { + list.add(superType); + } + addSupers(superType, list); + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#getType() + */ + public ICElement getType() { + if (fFocusType != null) + return AllTypesCache.getElementForType(fFocusType, true, true, null); + return null; + } + + /** + * @see ITypeHierarchy + * TODO (jerome) should use a PerThreadObject to build the hierarchy instead of synchronizing + * (see also isAffected(IJavaElementDelta)) + */ + public synchronized void refresh(IProgressMonitor monitor) throws CModelException { + try { + fProgressMonitor = monitor; + if (monitor != null) { + if (fFocusType != null) { + monitor.beginTask(TypeHierarchyMessages.getFormattedString("hierarchy.creatingOnType", fFocusType.getQualifiedTypeName().getFullyQualifiedName()), 100); //$NON-NLS-1$ + } else { + monitor.beginTask(TypeHierarchyMessages.getString("hierarchy.creating"), 100); //$NON-NLS-1$ + } + } + long start = -1; + if (DEBUG) { + start = System.currentTimeMillis(); + if (fComputeSubtypes) { + System.out.println("CREATING TYPE HIERARCHY [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + System.out.println("CREATING SUPER TYPE HIERARCHY [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + } + if (fFocusType != null) { + System.out.println(" on type " + fFocusType.toString()); //$NON-NLS-1$ + } + } + + compute(); +// initializeRegions(); + fNeedsRefresh = false; +// fChangeCollector = null; + + if (DEBUG) { + if (fComputeSubtypes) { + System.out.println("CREATED TYPE HIERARCHY in " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + System.out.println("CREATED SUPER TYPE HIERARCHY in " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ + } + System.out.println(this.toString()); + } + } finally { + if (monitor != null) { + monitor.done(); + } + fProgressMonitor = null; + } + } + + /** + * Compute this type hierarchy. + */ + protected void compute() { + if (fFocusType != null) { +// HierarchyBuilder builder = +// new IndexBasedHierarchyBuilder( +// this, +// this.scope); +// builder.build(this.computeSubtypes); + +// initialize(1); +// buildSupertypes(); + + } // else a RegionBasedTypeHierarchy should be used + } + + /** + * Initializes this hierarchy's internal tables with the given size. + */ + /* protected void initialize(int size) { + if (size < 10) { + size = 10; + } + int smallSize = (size / 2); + this.classToSuperclass = new HashMap(size); + this.interfaces = new ArrayList(smallSize); + this.missingTypes = new ArrayList(smallSize); + this.rootClasses = new TypeVector(); + this.typeToSubtypes = new HashMap(smallSize); + this.typeToSuperInterfaces = new HashMap(smallSize); + this.typeFlags = new HashMap(smallSize); + + this.projectRegion = new Region(); + this.packageRegion = new Region(); + this.files = new HashMap(5); + } +*/ + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.typehierarchy.ITypeHierarchy#store(java.io.OutputStream, org.eclipse.core.runtime.IProgressMonitor) + */ + public void store(OutputStream outputStream, IProgressMonitor monitor) throws CModelException { + // TODO Auto-generated method stub + + } + +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyBuilder.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyBuilder.java new file mode 100644 index 00000000000..cded12e9745 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyBuilder.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.browser.typehierarchy; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.ICModelStatusConstants; +import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; +import org.eclipse.cdt.internal.core.browser.cache.TypeCacheManager; +import org.eclipse.cdt.internal.core.model.CModelStatus; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; + +public class TypeHierarchyBuilder { + + public TypeHierarchyBuilder() { + } + + public ITypeHierarchy createTypeHierarchy(ITypeInfo info, boolean enableIndexing, IProgressMonitor monitor) throws CModelException { + TypeHierarchy typeHierarchy = new TypeHierarchy(info); + Set processedTypes = new HashSet(); + addSuperClasses(typeHierarchy, info, processedTypes, enableIndexing, monitor); + + typeHierarchy.addRootType(info); + processedTypes.clear(); + addSubClasses(typeHierarchy, info, processedTypes, enableIndexing, monitor); + + return typeHierarchy; + } + + private void addSuperClasses(TypeHierarchy typeHierarchy, ITypeInfo type, Set processedTypes, boolean enableIndexing, IProgressMonitor monitor) throws CModelException { + if (type.hasSuperTypes()) { + ITypeInfo[] superTypes = TypeCacheManager.getInstance().locateSuperTypesAndWait(type, enableIndexing, Job.SHORT, monitor); + if (superTypes == null) + throw new CModelException(new CModelStatus(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST)); + + for (int i = 0; i < superTypes.length; ++i) { + ITypeInfo superType = superTypes[i]; + + // recursively process sub sub classes + if (!processedTypes.contains(superType)) { + processedTypes.add(superType); + addSuperClasses(typeHierarchy, superType, processedTypes, enableIndexing, monitor); + } + + ASTAccessVisibility access = type.getSuperTypeAccess(superType); + + typeHierarchy.addSuperType(type, superType, access); + } + } else { + typeHierarchy.addRootType(type); + } + } + + private void addSubClasses(TypeHierarchy typeHierarchy, ITypeInfo type, Set processedTypes, boolean enableIndexing, IProgressMonitor monitor) throws CModelException { + if (type.hasSubTypes()) { + ITypeInfo[] subTypes = TypeCacheManager.getInstance().locateSubTypesAndWait(type, enableIndexing, Job.SHORT, monitor); + if (subTypes == null) + throw new CModelException(new CModelStatus(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST)); + + for (int i = 0; i < subTypes.length; ++i) { + ITypeInfo subType = subTypes[i]; + + // recursively process sub sub classes + if (!processedTypes.contains(subType)) { + processedTypes.add(subType); + addSubClasses(typeHierarchy, subType, processedTypes, enableIndexing, monitor); + } + + typeHierarchy.addSubType(type, subType); + } + } + } + +/* + private IStructure findCElementForType(ITypeInfo info, boolean enableIndexing, IProgressMonitor monitor) throws CModelException { + if (!info.exists()) + throw new CModelException(new CModelStatus(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST)); + + if (!info.isClass()) + throw new CModelException(new CModelStatus(ICModelStatusConstants.INVALID_ELEMENT_TYPES)); + + // first we need to resolve the type location + ITypeReference location = TypeCacheManager.getInstance().locateTypeAndWait(info, enableIndexing, Job.SHORT, monitor); + if (location == null) + throw new CModelException(new CModelStatus(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST)); + + ICElement cElem = location.getCElement(); + if (cElem == null) + throw new CModelException(new CModelStatus(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST)); + + if (!(cElem instanceof IStructure)) + throw new CModelException(new CModelStatus(ICModelStatusConstants.INVALID_ELEMENT_TYPES)); + + IStructure cClass = (IStructure)cElem; + + // check if type exists in cache + ITypeInfo type = TypeCacheManager.getInstance().getTypeForElement(cElem); + if (type == null || !type.equals(info)) + throw new CModelException(new CModelStatus(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST)); + + return cClass; + } + + private ITypeInfo findTypeInCache(ITypeCache cache, String name) { + IQualifiedTypeName qualName = new QualifiedTypeName(name); + ITypeInfo[] superTypes = cache.getTypes(qualName); + for (int i = 0; i < superTypes.length; ++i) { + ITypeInfo superType = superTypes[i]; + if (superType.isClass()) { + return superType; + } + } + return null; + } +*/ + +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyMessages.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyMessages.java new file mode 100644 index 00000000000..467841057cf --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyMessages.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.browser.typehierarchy; + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class TypeHierarchyMessages { + + private static final String RESOURCE_BUNDLE= TypeHierarchyMessages.class.getName(); + + private static ResourceBundle fgResourceBundle; + static { + try { + fgResourceBundle = ResourceBundle.getBundle(RESOURCE_BUNDLE); + } catch (MissingResourceException x) { + fgResourceBundle = null; + } + } + + private TypeHierarchyMessages() { + } + + public static String getString(String key) { + try { + return fgResourceBundle.getString(key); + } catch (MissingResourceException e) { + return '!' + key + '!'; + } catch (NullPointerException e) { + return "#" + key + "#"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + public static String getFormattedString(String key, String arg) { + return getFormattedString(key, new String[] { arg }); + } + + public static String getFormattedString(String key, String[] args) { + return MessageFormat.format(getString(key), args); + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyMessages.properties b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyMessages.properties new file mode 100644 index 00000000000..760292b333c --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/core/browser/typehierarchy/TypeHierarchyMessages.properties @@ -0,0 +1,17 @@ +############################################################################### +# Copyright (c) 2000, 2004 QNX Software Systems and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# QNX Software Systems - Initial API and implementation +############################################################################### + +### hierarchy +hierarchy.nullProject = Project argument cannot be null +hierarchy.nullRegion = Region cannot be null +hierarchy.nullFocusType = Type focus cannot be null +hierarchy.creating = Creating type hierarchy... +hierarchy.creatingOnType = Creating type hierarchy on {0}... diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/BasicJob.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/BasicJob.java new file mode 100644 index 00000000000..7bf631918b1 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/BasicJob.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import org.eclipse.cdt.internal.core.browser.util.DelegatedProgressMonitor; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; + +public abstract class BasicJob extends Job { + + private Object fFamily; + private DelegatedProgressMonitor fProgressMonitor= new DelegatedProgressMonitor(); + private Object fRunLock = new Object(); + private boolean fIsRunning = false; + private static boolean VERBOSE = false; + + public BasicJob(String name, Object family) { + super(name); + fFamily = family; + setPriority(BUILD); + setSystem(true); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#run(IProgressMonitor) + */ + protected abstract IStatus runWithDelegatedProgress(IProgressMonitor monitor) throws InterruptedException; + + /* (non-Javadoc) + * @see org.eclipse.core.internal.jobs.InternalJob#belongsTo(java.lang.Object) + */ + public boolean belongsTo(Object family) { + if (fFamily != null) { + return fFamily.equals(family); + } + return false; + } + + public boolean isRunning() { + synchronized(fRunLock) { + return fIsRunning; + } + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#run(IProgressMonitor) + */ + public IStatus run(IProgressMonitor monitor) { + synchronized(fRunLock) { + fIsRunning = true; + } + + fProgressMonitor.init(); + fProgressMonitor.addDelegate(monitor); + + IStatus result = Status.CANCEL_STATUS; + try { + if (monitor.isCanceled()) + throw new InterruptedException(); + + result = runWithDelegatedProgress(fProgressMonitor); + + if (monitor.isCanceled()) + throw new InterruptedException(); + } catch(InterruptedException ex) { + return Status.CANCEL_STATUS; + } catch (OperationCanceledException ex) { + return Status.CANCEL_STATUS; + } finally { + fProgressMonitor.done(); + fProgressMonitor.removeAllDelegates(); + fProgressMonitor.init(); + + synchronized(fRunLock) { + fIsRunning = false; + } + } + return result; + } + + /** + * Forwards progress info to the progress monitor and + * blocks until the job is finished. + * + * @param monitor the progress monitor. + * @throws InterruptedException + * + * @see Job#join + */ + public void join(IProgressMonitor monitor) throws InterruptedException { + if (monitor != null) { + fProgressMonitor.addDelegate(monitor); + } + super.join(); + } + + /** + * Outputs message to console. + */ + protected static void trace(String msg) { + if (VERBOSE) { + System.out.println("(" + Thread.currentThread() + ") " + msg); //$NON-NLS-1$ //$NON-NLS-2$ + } + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/ITypeCache.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/ITypeCache.java new file mode 100644 index 00000000000..d4e73fbdaef --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/ITypeCache.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import org.eclipse.cdt.core.browser.IQualifiedTypeName; +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.ITypeInfoVisitor; +import org.eclipse.cdt.core.browser.ITypeReference; +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; + +public interface ITypeCache extends ISchedulingRule { + + /** Returns whether cache contains any types. + * + * @return <code>true</code> if cache is empty + */ + public boolean isEmpty(); + + /** Returns whether cache is complete. + * + * @return <code>true</code> if cache is up to date. + */ + public boolean isUpToDate(); + + /** Inserts type into cache. + * + * @param info + */ + public void insert(ITypeInfo info); + + /** Adds subtype to type. + * + * @param type + * @param subtype + */ + public void addSubtype(ITypeInfo type, ITypeInfo subtype); + + /** Adds supertype to type. + * + * @param type the type + * @param supertype the supertype + * @param the access visibility (PUBLIC, PROTECTED, PRIVATE) + * @param isVirtual <code>true</code> if virtual base class + */ + public void addSupertype(ITypeInfo type, ITypeInfo supertype, ASTAccessVisibility access, boolean isVirtual); + + /** Removes type from cache. + * + * @param info + */ + public void remove(ITypeInfo info); + + /** Removes all types in the given scope. + * + * @param scope + */ + public void flush(ITypeSearchScope scope); + + /** Removes all types referenced by the given path. + * + * @param path + */ + public void flush(IPath path); + + /** Removes all types from the cache. + */ + public void flushAll(); + + /** Returns all paths in the cache which are enclosed by + * the given scope. If no paths are found, an empty + * array is returned. + * + * @param scope the scope to search, or <code>null</code> to + * search the entire cache. + * @return A collection of paths in the given scope. + */ + public IPath[] getPaths(ITypeSearchScope scope); + + /** Returns all types in the cache which are enclosed by + * the given scope. If no types are found, an empty array + * is returned. + * + * @param scope the scope to search, or <code>null</code> to + * search the entire cache. + * @return Array of types in the given scope + */ + public ITypeInfo[] getTypes(ITypeSearchScope scope); + + /** Returns all types in the cache which match the given + * name. If no types are found, an empty array is returned. + * + * @param qualifiedName the qualified type name to match + * @param matchEnclosed <code>true</code> if enclosed types count as matches (foo::bar == bar) + * @param ignoreCase <code>true</code> if case-insensitive + * @return Array of types + */ + public ITypeInfo[] getTypes(IQualifiedTypeName qualifiedName, boolean matchEnclosed, boolean ignoreCase); + + /** Returns first type in the cache which matches the given + * type and name. If no type is found, <code>null</code> + * is returned. + * + * @param type the ICElement type + * @param qualifiedName the qualified type name to match + * @return the matching type + */ + public ITypeInfo getType(int type, IQualifiedTypeName qualifiedName); + + /** Gets the first enclosing type which matches one of the given kinds. + * + * @param info the given type + * @param kinds Array containing CElement types: C_NAMESPACE, C_CLASS, C_STRUCT + * + * @return the enclosing type, or <code>null</code> if not found. + */ + public ITypeInfo getEnclosingType(ITypeInfo info, int[] kinds); + + /** Returns the enclosing namespace for the given type, or + * <code>null</code> if none exists. + * + * @param type the ICElement type + * @param includeGlobalNamespace <code>true</code> if the global (default) namespace should be returned + * @return the enclosing namespace, or <code>null</code> if not found. + */ + public ITypeInfo getEnclosingNamespace(ITypeInfo info, boolean includeGlobalNamespace); + + /** Gets the root namespace of which encloses the given type. + * + * @param info the given type + * @param includeGlobalNamespace <code>true</code> if the global (default) namespace should be returned + * @return the enclosing namespace, or <code>null</code> if not found. + */ + public ITypeInfo getRootNamespace(ITypeInfo info, boolean includeGlobalNamespace); + + /** Returns whether any types are enclosed by the given type. + * + * @param info the given type + * @return <code>true</code> if the given type encloses other types. + */ + public boolean hasEnclosedTypes(ITypeInfo info); + + /** Gets the types which are enclosed by the given type. + * + * @param info the given type + * @param kinds Array containing CElement types: C_NAMESPACE, C_CLASS, + * C_UNION, C_ENUMERATION, C_TYPEDEF + * @return the enclosed types, or an empty array if not found. + */ + public ITypeInfo[] getEnclosedTypes(ITypeInfo info, int kinds[]); + + /** Returns all types in the cache are supertypes of the given type. + * + * @param info the given type + * @return Array of supertypes, or <code>null</code> if none found. + */ + public ITypeInfo[] getSupertypes(ITypeInfo info); + + /** Returns the supertype access visiblity. + * + * @param type the given type + * @param supertype the supertype + * @return the visibility (PRIVATE, PROTECTED, PUBLIC) or <code>null</code> if none found. + */ + public ASTAccessVisibility getSupertypeAccess(ITypeInfo type, ITypeInfo superType); + + + /** Returns all types in the cache which extend the given type. + * + * @param info the given type + * @return Array of subtypes, or <code>null</code> if none found. + */ + public ITypeInfo[] getSubtypes(ITypeInfo info); + + /** Returns the project associated with this cache. + * + * @return the project + */ + public IProject getProject(); + + /** Accepts a visitor and iterates over all types in the cache. + * + * @param visitor + */ + public void accept(ITypeInfoVisitor visitor); + + public void addDelta(TypeCacheDelta delta); + public void reconcile(boolean enableIndexing, int priority, int delay); + public void reconcileAndWait(boolean enableIndexing, int priority, IProgressMonitor monitor); + public void cancelJobs(); + + public void locateType(ITypeInfo info, int priority, int delay); + public ITypeReference locateTypeAndWait(ITypeInfo info, int priority, IProgressMonitor monitor); + + public void locateSupertypes(ITypeInfo info, int priority, int delay); + public ITypeInfo[] locateSupertypesAndWait(ITypeInfo info, int priority, IProgressMonitor monitor); + + public void locateSubtypes(ITypeInfo info, int priority, int delay); + public ITypeInfo[] locateSubtypesAndWait(ITypeInfo info, int priority, IProgressMonitor monitor); + + public ITypeInfo getGlobalNamespace(); +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerDependenciesJob.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerDependenciesJob.java new file mode 100644 index 00000000000..e46b6711efe --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerDependenciesJob.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.io.IOException; + +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.PathUtil; +import org.eclipse.cdt.internal.core.index.IEntryResult; +import org.eclipse.cdt.internal.core.index.IIndex; +import org.eclipse.cdt.internal.core.index.cindexstorage.Index; +import org.eclipse.cdt.internal.core.index.cindexstorage.IndexedFileEntry; +import org.eclipse.cdt.internal.core.index.cindexstorage.io.BlocksIndexInput; +import org.eclipse.cdt.internal.core.index.cindexstorage.io.IndexInput; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; + +public class IndexerDependenciesJob extends IndexerJob { + + private ITypeCache fTypeCache; + private ITypeSearchScope fScope; + + public IndexerDependenciesJob(IndexManager indexManager, ITypeCache typeCache, ITypeSearchScope scope) { + super(indexManager, typeCache.getProject()); + fTypeCache = typeCache; + fScope = scope; + } + + protected boolean processIndex(IIndex index, IProject project, IProgressMonitor progressMonitor) throws InterruptedException { + IndexInput input = new BlocksIndexInput(index.getIndexFile()); + try { + input.open(); + flushDependencies(input, progressMonitor); + return true; + } catch (IOException e) { + return false; + } finally { + try { + input.close(); + } catch (IOException e) { + return false; + } + } + } + + private void flushDependencies(IndexInput input, IProgressMonitor progressMonitor) + throws InterruptedException, IOException { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult[] includeEntries = input.queryEntriesPrefixedBy(Index.encodeEntry(IIndex.INCLUDE, IIndex.ANY, IIndex.REFERENCE)); + if (includeEntries != null) { + //TODO subprogress monitor + for (int i = 0; i < includeEntries.length; ++i) { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult entry = includeEntries[i]; + IPath includePath = getIncludePath(entry); + + if (fScope != null && fScope.encloses(includePath)) { + int[] references = entry.getFileReferences(); + if (references != null) { + for (int j = 0; j < references.length; ++j) { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + IndexedFileEntry file = input.getIndexedFile(references[j]); + if (file != null && file.getPath() != null) { + IPath path = PathUtil.getWorkspaceRelativePath(file.getPath()); + fTypeCache.flush(path); + } + } + } + } + } + } + } + + private IPath getIncludePath(IEntryResult entry) { + return PathUtil.getWorkspaceRelativePath(entry.getName()); + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerDependenciesJob2.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerDependenciesJob2.java new file mode 100644 index 00000000000..2567bf32c6a --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerDependenciesJob2.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.io.IOException; + +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.PathUtil; +import org.eclipse.cdt.internal.core.index.IEntryResult; +import org.eclipse.cdt.internal.core.index.IIndex; +import org.eclipse.cdt.internal.core.index.cindexstorage.Index; +import org.eclipse.cdt.internal.core.index.cindexstorage.IndexedFileEntry; +import org.eclipse.cdt.internal.core.index.cindexstorage.io.BlocksIndexInput; +import org.eclipse.cdt.internal.core.index.cindexstorage.io.IndexInput; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; + +public class IndexerDependenciesJob2 extends IndexerJob2 { + + private ITypeCache fTypeCache; + private ITypeSearchScope fScope; + + public IndexerDependenciesJob2(IndexManager indexManager, ITypeCache typeCache, ITypeSearchScope scope) { + super(indexManager, typeCache.getProject()); + fTypeCache = typeCache; + fScope = scope; + } + + protected boolean processIndex(IProgressMonitor progressMonitor) throws InterruptedException { + IndexInput input = new BlocksIndexInput(fProjectIndex.getIndexFile()); + try { + input.open(); + flushDependencies(input, progressMonitor); + return true; + } catch (IOException e) { + return false; + } finally { + try { + input.close(); + } catch (IOException e) { + return false; + } + } + } + + private void flushDependencies(IndexInput input, IProgressMonitor progressMonitor) + throws InterruptedException, IOException { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult[] includeEntries = input.queryEntriesPrefixedBy(Index.encodeEntry(IIndex.INCLUDE, IIndex.ANY, IIndex.REFERENCE)); + if (includeEntries != null) { + //TODO subprogress monitor + for (int i = 0; i < includeEntries.length; ++i) { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult entry = includeEntries[i]; + IPath includePath = getIncludePath(entry); + + if (fScope != null && fScope.encloses(includePath)) { + int[] references = entry.getFileReferences(); + if (references != null) { + for (int j = 0; j < references.length; ++j) { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + IndexedFileEntry file = input.getIndexedFile(references[j]); + if (file != null && file.getPath() != null) { + IPath path = PathUtil.getWorkspaceRelativePath(file.getPath()); + fTypeCache.flush(path); + } + } + } + } + } + } + } + + private IPath getIncludePath(IEntryResult entry) { + return PathUtil.getWorkspaceRelativePath(entry.getName()); + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerJob.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerJob.java new file mode 100644 index 00000000000..4de9fba649a --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerJob.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial implementation + * QNX Software Systems - adapted for type cache + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.io.IOException; + +import org.eclipse.cdt.core.index.ICDTIndexer; +import org.eclipse.cdt.internal.core.index.IIndex; +import org.eclipse.cdt.internal.core.index.domsourceindexer.DOMSourceIndexer; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.cdt.internal.core.search.indexing.ReadWriteMonitor; +import org.eclipse.cdt.internal.core.search.processing.IIndexJob; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; + +public abstract class IndexerJob implements IIndexJob { + + private IndexManager fIndexManager; + private IProject fProject; + private IIndex fProjectIndex = null; + private DOMSourceIndexer fSourceIndexer = null; + + public static final String FAMILY= "BasicTypeIndexerJob"; //$NON-NLS-1$ + + public IndexerJob(IndexManager indexManager, IProject project) { + fIndexManager = indexManager; + fProject = project; + //Get the indexer assigned to this project; check to see if it's + //a Source Indexder + ICDTIndexer indexer = indexManager.getIndexerForProject(project); + if (indexer instanceof DOMSourceIndexer) + fSourceIndexer = (DOMSourceIndexer) indexer; + } + + public boolean belongsTo(String family) { + return family == FAMILY; + } + + public void cancel() { + } + + public boolean isReadyToRun() { + if (fProjectIndex == null) { // only check once. As long as this job is used, it will keep the same index picture + getIndexForProject(fProject); // will only cache answer if all indexes were available originally + } + return true; + } + + public String toString() { + return FAMILY; + } + + protected abstract boolean processIndex(IIndex index, IProject project, IProgressMonitor progressMonitor) throws InterruptedException; + + public boolean execute(IProgressMonitor progressMonitor) { + boolean success = false; + try { + fProjectIndex = getIndexForProject(fProject); + if (fProjectIndex == null) + return false; + + if (progressMonitor == null) { + progressMonitor = new NullProgressMonitor(); + } + if (progressMonitor.isCanceled()) + throw new OperationCanceledException(); + + progressMonitor.beginTask("", 1); //$NON-NLS-1$ + + success = prepareIndex(fProjectIndex, fProject, progressMonitor); + + if (progressMonitor.isCanceled()) { + throw new OperationCanceledException(); + } + progressMonitor.worked(1); + + return success; + } catch (InterruptedException e) { + throw new OperationCanceledException(); + } finally { + progressMonitor.done(); + } + } + + private boolean prepareIndex(IIndex index, IProject project, IProgressMonitor progressMonitor) throws InterruptedException { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + if (index == null) + return COMPLETE; + + if (fSourceIndexer == null) + return FAILED; + + ReadWriteMonitor monitor = fSourceIndexer.getMonitorFor(index); + + if (monitor == null) + return COMPLETE; // index got deleted since acquired + + try { + monitor.enterRead(); // ask permission to read + /* if index has changed, commit these before querying */ + if (index.hasChanged()) { + try { + monitor.exitRead(); // free read lock + monitor.enterWrite(); // ask permission to write + fSourceIndexer.saveIndex(index); + } catch (IOException e) { + return FAILED; + } finally { + monitor.exitWriteEnterRead(); // finished writing and reacquire read permission + } + } + + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + return processIndex(index, project, progressMonitor); + } finally { + monitor.exitRead(); // finished reading + } + } + + private IIndex getIndexForProject(IProject project) { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IPath path = project.getFullPath(); + IPath location; + if ((!root.getProject(path.lastSegment()).exists()) // if project does not exist + && path.segmentCount() > 1 + && ((location = root.getFile(path).getLocation()) == null + || !new java.io.File(location.toOSString()).exists()) // and internal jar file does not exist + && !new java.io.File(path.toOSString()).exists()) { // and external jar file does not exist + return null; + } + + // may trigger some index recreation work + if (fSourceIndexer != null) + return fSourceIndexer.getIndex(path, true /*reuse index file*/, false /*do not create if none*/); + + return null; + } +} + diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerJob2.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerJob2.java new file mode 100644 index 00000000000..51be068de3e --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerJob2.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2004,2005 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * QNX Software Systems - adapted for type cache + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.io.IOException; + +import org.eclipse.cdt.core.index.ICDTIndexer; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.internal.core.index.IIndex; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.cdt.internal.core.search.indexing.ReadWriteMonitor; +import org.eclipse.cdt.internal.core.search.processing.IIndexJob; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; + +public abstract class IndexerJob2 implements IIndexJob { + + protected IProject fProject; + protected IIndex fProjectIndex = null; + protected ICDTIndexer fSourceIndexer = null; + + public static final String FAMILY= "IndexerJob2"; //$NON-NLS-1$ + + public IndexerJob2(IndexManager indexManager, IProject project) { + fProject = project; + fSourceIndexer = indexManager.getIndexerForProject(project); + fProjectIndex = getIndexForProject(); + } + + public boolean belongsTo(String family) { + return family == FAMILY; + } + + public void cancel() { + } + + public boolean isReadyToRun() { + return true; + } + + public String toString() { + return FAMILY; + } + + protected abstract boolean processIndex(IProgressMonitor progressMonitor) throws InterruptedException; + + public boolean execute(IProgressMonitor progressMonitor) { + boolean success = false; + try { + if (fProjectIndex == null) + return false; + + if (progressMonitor == null) { + progressMonitor = new NullProgressMonitor(); + } + if (progressMonitor.isCanceled()) + throw new OperationCanceledException(); + + progressMonitor.beginTask("", 1); //$NON-NLS-1$ + + success = prepareIndex(progressMonitor); + + if (progressMonitor.isCanceled()) { + throw new OperationCanceledException(); + } + progressMonitor.worked(1); + + return success; + } catch (InterruptedException e) { + throw new OperationCanceledException(); + } finally { + progressMonitor.done(); + } + } + + private boolean prepareIndex(IProgressMonitor progressMonitor) throws InterruptedException { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + if (fProjectIndex == null) + return COMPLETE; + + if (fSourceIndexer == null) + return FAILED; + + ReadWriteMonitor monitor = fSourceIndexer.getMonitorFor(fProjectIndex); + + if (monitor == null) + return COMPLETE; // index got deleted since acquired + + try { + monitor.enterRead(); // ask permission to read + /* if index has changed, commit these before querying */ + if (fProjectIndex.hasChanged()) { + try { + monitor.exitRead(); // free read lock + monitor.enterWrite(); // ask permission to write + fSourceIndexer.saveIndex(fProjectIndex); + } catch (IOException e) { + return FAILED; + } finally { + monitor.exitWriteEnterRead(); // finished writing and reacquire read permission + } + } + + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + return processIndex(progressMonitor); + } finally { + monitor.exitRead(); // finished reading + } + } + + private IIndex getIndexForProject() { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IPath path = fProject.getFullPath(); + IPath location; + if ((!root.getProject(path.lastSegment()).exists()) // if project does not exist + && path.segmentCount() > 1 + && ((location = root.getFile(path).getLocation()) == null + || !new java.io.File(location.toOSString()).exists()) // and internal jar file does not exist + && !new java.io.File(path.toOSString()).exists()) { // and external jar file does not exist + return null; + } + + // may trigger some index recreation work + if (fSourceIndexer != null) + return fSourceIndexer.getIndex(path, true /*reuse index file*/, false /*do not create if none*/); + + return null; + } + + protected int index2ICElement( int kind ) + { + switch(kind) { + case IIndex.TYPE_CLASS: + return ICElement.C_CLASS; + case IIndex.TYPE_STRUCT: + return ICElement.C_STRUCT; + case IIndex.TYPE_ENUM: + return ICElement.C_ENUMERATION; + case IIndex.TYPE_UNION: + return ICElement.C_UNION; + case IIndex.TYPE_TYPEDEF: + return ICElement.C_TYPEDEF; + default: + return 0; + } + } +} + diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerTypesJob.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerTypesJob.java new file mode 100644 index 00000000000..78c7cee5cc8 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerTypesJob.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.io.IOException; + +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.PathUtil; +import org.eclipse.cdt.core.browser.QualifiedTypeName; +import org.eclipse.cdt.core.browser.TypeInfo; +import org.eclipse.cdt.core.browser.TypeReference; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.internal.core.index.IEntryResult; +import org.eclipse.cdt.internal.core.index.IIndex; +import org.eclipse.cdt.internal.core.index.cindexstorage.Index; +import org.eclipse.cdt.internal.core.index.cindexstorage.IndexedFileEntry; +import org.eclipse.cdt.internal.core.index.cindexstorage.io.BlocksIndexInput; +import org.eclipse.cdt.internal.core.index.cindexstorage.io.IndexInput; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; + +public class IndexerTypesJob extends IndexerJob { + + private ITypeCache fTypeCache; + + public IndexerTypesJob(IndexManager indexManager, ITypeCache typeCache, ITypeSearchScope scope) { + super(indexManager, typeCache.getProject()); + fTypeCache = typeCache; + } + + protected boolean processIndex(IIndex index, IProject project, IProgressMonitor progressMonitor) throws InterruptedException { + IndexInput input = new BlocksIndexInput(index.getIndexFile()); + try { + input.open(); + updateNamespaces(input, project, progressMonitor); + updateTypes(input, project, progressMonitor); + return true; + } catch (IOException e) { + return false; + } finally { + try { + input.close(); + } catch (IOException e) { + return false; + } + } + } + + private void updateNamespaces(IndexInput input, IProject project, IProgressMonitor monitor) + throws InterruptedException, IOException { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult[] namespaceEntries = input.queryEntriesPrefixedBy(Index.encodeEntry(IIndex.NAMESPACE, IIndex.ANY, IIndex.DECLARATION)); + if (namespaceEntries != null) { + //TODO subprogress monitor + for (int i = 0; i < namespaceEntries.length; ++i) { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult entry = namespaceEntries[i]; + String name = entry.getName(); + if (name.length() != 0) { + String[] enclosingNames = entry.getEnclosingNames(); + addType(input, project, entry, ICElement.C_NAMESPACE, name, enclosingNames, monitor); + } + } + } + } + + private void updateTypes(IndexInput input, IProject project, IProgressMonitor monitor) + throws InterruptedException, IOException { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult[] typeEntries = input.queryEntriesPrefixedBy(Index.encodeEntry(IIndex.TYPE, IIndex.ANY, IIndex.DECLARATION)); + if (typeEntries != null) { + //TODO subprogress monitor + for (int i = 0; i < typeEntries.length; ++i) { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult entry = typeEntries[i]; + + String name = entry.getName(); + switch (entry.getKind() ) { + case IIndex.TYPE_CLASS : + case IIndex.TYPE_STRUCT : + case IIndex.TYPE_TYPEDEF : + case IIndex.TYPE_ENUM : + case IIndex.TYPE_UNION : + if (name.length() != 0) { // skip anonymous structs + addType(input, project, entry, entry.getKind(), name, entry.getEnclosingNames(), monitor); + } + break; + case IIndex.TYPE_DERIVED : + if (name.length() != 0) { // skip anonymous structs + addSuperTypeReference(input, project, entry, name, entry.getEnclosingNames(), monitor); + } + break; + default: + break; + } + } + } + } + + private void addType(IndexInput input, IProject project, IEntryResult entry, int type, String name, String[] enclosingNames, IProgressMonitor monitor) + throws InterruptedException, IOException { + QualifiedTypeName qualifiedName = new QualifiedTypeName(name, enclosingNames); + ITypeInfo info = fTypeCache.getType(type, qualifiedName); + if (info == null || info.isUndefinedType()) { + int[] references = entry.getFileReferences(); + if (references != null && references.length > 0) { + // add new type to cache + if (info != null) { + info.setCElementType(type); + } else { + info = new TypeInfo(type, qualifiedName); + fTypeCache.insert(info); + } + +// for (int i = 0; i < references.length; ++i) { +// if (monitor.isCanceled()) +// throw new InterruptedException(); +// +// IndexedFile file = input.getIndexedFile(references[i]); +// if (file != null && file.getPath() != null) { +// IPath path = PathUtil.getWorkspaceRelativePath(file.getPath()); +// info.addReference(new TypeReference(path, project)); +// } +// } + // just grab the first reference + IndexedFileEntry file = input.getIndexedFile(references[0]); + if (file != null && file.getPath() != null) { + IPath path = PathUtil.getWorkspaceRelativePath(file.getPath()); + info.addReference(new TypeReference(path, project)); + } + } + } + } + + private void addSuperTypeReference(IndexInput input, IProject project, IEntryResult entry, String name, String[] enclosingNames, IProgressMonitor monitor) throws InterruptedException, IOException { + QualifiedTypeName qualifiedName = new QualifiedTypeName(name, enclosingNames); + ITypeInfo info = fTypeCache.getType(ICElement.C_CLASS, qualifiedName); + if (info == null) + info = fTypeCache.getType(ICElement.C_STRUCT, qualifiedName); + if (info == null) { + // add dummy type to cache + info = new TypeInfo(0, qualifiedName); + fTypeCache.insert(info); + } + int[] references = entry.getFileReferences(); + if (references != null && references.length > 0) { + for (int i = 0; i < references.length; ++i) { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IndexedFileEntry file = input.getIndexedFile(references[i]); + if (file != null && file.getPath() != null) { + IPath path = PathUtil.getWorkspaceRelativePath(file.getPath()); + info.addDerivedReference(new TypeReference(path, project)); +// +// // get absolute path +// IPath path = new Path(file.getPath()); +// IPath projectPath = project.getFullPath(); +// if (projectPath.isPrefixOf(path)) { +// path = project.getLocation().append(path.removeFirstSegments(projectPath.segmentCount())); +// } +// info.addDerivedReference(new TypeReference(path, project)); + } + } + } + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerTypesJob2.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerTypesJob2.java new file mode 100644 index 00000000000..206db83a5c3 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/IndexerTypesJob2.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.io.IOException; + +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.PathUtil; +import org.eclipse.cdt.core.browser.QualifiedTypeName; +import org.eclipse.cdt.core.browser.TypeInfo; +import org.eclipse.cdt.core.browser.TypeReference; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.internal.core.index.IEntryResult; +import org.eclipse.cdt.internal.core.index.IIndex; +import org.eclipse.cdt.internal.core.index.IQueryResult; +import org.eclipse.cdt.internal.core.index.cindexstorage.io.BlocksIndexInput; +import org.eclipse.cdt.internal.core.index.cindexstorage.io.IndexInput; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; + +public class IndexerTypesJob2 extends IndexerJob2 { + + private ITypeCache fTypeCache; + + public IndexerTypesJob2(IndexManager indexManager, ITypeCache typeCache, ITypeSearchScope scope) { + super(indexManager, typeCache.getProject()); + fTypeCache = typeCache; + } + + protected boolean processIndex(IProgressMonitor progressMonitor) throws InterruptedException { + IndexInput input = null; + try { + input = new BlocksIndexInput( fProjectIndex.getIndexFile() ); + input.open(); + updateNamespaces(input, progressMonitor); + updateTypes(input, progressMonitor); + return true; + } catch (IOException e) { + return false; + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + // do nothing + } + } + } + } + + private void updateNamespaces(IndexInput input, IProgressMonitor monitor) + throws InterruptedException, IOException { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult[] namespaceEntries = fProjectIndex.getEntries( IIndex.NAMESPACE, IIndex.ANY, IIndex.DEFINITION ); + IQueryResult[] namespacePaths = fProjectIndex.getPrefix(IIndex.NAMESPACE, IIndex.ANY, IIndex.DEFINITION ); +// input.queryEntriesPrefixedBy(Index.encodeEntry(IIndex.NAMESPACE, IIndex.ANY, IIndex.DECLARATION)); + if (namespaceEntries != null) { + //TODO subprogress monitor + for (int i = 0; i < namespaceEntries.length; ++i) { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult entry = namespaceEntries[i]; + String name = entry.getName(); + if (name.length() != 0) { + String[] enclosingNames = entry.getEnclosingNames(); + addType(input, entry, namespacePaths[i].getPath(), ICElement.C_NAMESPACE, name, enclosingNames, monitor); + } + } + } + } + + private void updateTypes(IndexInput input, IProgressMonitor monitor) + throws InterruptedException, IOException { + if (monitor.isCanceled()) + throw new InterruptedException(); + + for( int counter = 0; counter < 2; ++counter ) + { + IEntryResult[] typeEntries = fProjectIndex.getEntries( IIndex.TYPE, IIndex.ANY, ( counter == 0 ) ? IIndex.DECLARATION : IIndex.DEFINITION ); + + if (typeEntries != null) { + //TODO subprogress monitor + for (int i = 0; i < typeEntries.length; ++i) { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult entry = typeEntries[i]; + + String name = entry.extractSimpleName(); + switch (entry.getKind() ) { + case IIndex.TYPE_CLASS : + case IIndex.TYPE_STRUCT : + case IIndex.TYPE_TYPEDEF : + case IIndex.TYPE_ENUM : + case IIndex.TYPE_UNION : + if (counter != 0 && name.length() != 0) { // skip anonymous structs + addType(input, entry, null, index2ICElement( entry.getKind() ), name, entry.getEnclosingNames(), monitor); + } + break; + default: + break; + } + } + } + } + + IEntryResult[] typeEntries = fProjectIndex.getEntries( IIndex.TYPE, IIndex.TYPE_DERIVED, IIndex.ANY ); + if (typeEntries != null){ + for( int j = 0; j < typeEntries.length; ++j ) + { + if (monitor.isCanceled()) + throw new InterruptedException(); + + IEntryResult entry = typeEntries[j]; + String name = entry.extractSimpleName(); + switch( entry.getKind() ) + { + case IIndex.TYPE_DERIVED : + if (name.length() != 0) { // skip anonymous structs + addSuperTypeReference(input, entry, name, entry.getEnclosingNames(), monitor); + } + break; + default: + break; + } + } + } + } + + private void addType(IndexInput input, IEntryResult entry, String pth, int type, String name, String[] enclosingNames, IProgressMonitor monitor) + throws InterruptedException, IOException { + QualifiedTypeName qualifiedName = new QualifiedTypeName(name, enclosingNames); + ITypeInfo info = fTypeCache.getType(type, qualifiedName); + if (info == null || info.isUndefinedType()) { + int[] references = entry.getFileReferences(); + if (references != null && references.length > 0) { + // add new type to cache + if (info != null) { + info.setCElementType(type); + } else { + info = new TypeInfo(type, qualifiedName); + fTypeCache.insert(info); + } + +// for (int i = 0; i < references.length; ++i) { +// if (monitor.isCanceled()) +// throw new InterruptedException(); +// +// IndexedFile file = input.getIndexedFile(references[i]); +// if (file != null && file.getPath() != null) { +// IPath path = PathUtil.getWorkspaceRelativePath(file.getPath()); +// info.addReference(new TypeReference(path, project)); +// } +// } + if (pth == null) { + pth = input.getIndexedFile( references[0] ).getPath(); + } + + final IPath workspaceRelativePath = PathUtil.getWorkspaceRelativePath(pth); + int offset = entry.getOffsets()[0][0]; +// int offsetType = Integer.valueOf(String.valueOf(offsets[i][j]).substring(0,1)).intValue(); + int offsetType = offset; + int m = 1; + while (offsetType >= 10) { + offsetType = offsetType / 10; + m *= 10; + } + int value = offset - ( offsetType * m ); +// int value = Integer.valueOf(String.valueOf(offset).substring(1)).intValue(); + + TypeReference typeReference = null; + if (offsetType==IIndex.LINE){ + typeReference = new TypeReference(workspaceRelativePath, fProject, value, 0 ); + typeReference.offsetIsLineNumber = true; + }else if (offsetType==IIndex.OFFSET){ + typeReference = new TypeReference(workspaceRelativePath, fProject, value, entry.getOffsetLengths()[0][0] ); + } + if( typeReference != null ) + info.addReference(typeReference); + } + } + } + + private void addSuperTypeReference(IndexInput input, IEntryResult entry, String name, String[] enclosingNames, IProgressMonitor monitor) throws InterruptedException, IOException { + QualifiedTypeName qualifiedName = new QualifiedTypeName(name, enclosingNames); + ITypeInfo info = fTypeCache.getType(ICElement.C_CLASS, qualifiedName); + if (info == null) + info = fTypeCache.getType(ICElement.C_STRUCT, qualifiedName); + if (info == null) { + // add dummy type to cache + info = new TypeInfo(0, qualifiedName); + fTypeCache.insert(info); + } + int[] references = entry.getFileReferences(); + if (references != null && references.length > 0) { + for (int i = 0; i < references.length; ++i) { + if (monitor.isCanceled()) + throw new InterruptedException(); + + String pth = input.getIndexedFile( references[i] ).getPath(); + IPath path = PathUtil.getWorkspaceRelativePath(pth); + + info.addDerivedReference(new TypeReference(path, fProject)); +// +// // get absolute path +// IPath path = new Path(file.getPath()); +// IPath projectPath = project.getFullPath(); +// if (projectPath.isPrefixOf(path)) { +// path = project.getLocation().append(path.removeFirstSegments(projectPath.segmentCount())); +// } +// info.addDerivedReference(new TypeReference(path, project)); + + } + } + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/SubTypeLocatorJob.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/SubTypeLocatorJob.java new file mode 100644 index 00000000000..5fe508c83f7 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/SubTypeLocatorJob.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.IWorkingCopyProvider; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; + +public class SubTypeLocatorJob extends BasicJob { + + public static final Object FAMILY = new Object(); + private ITypeInfo fLocateType; + private ITypeCache fTypeCache; + private IWorkingCopyProvider fWorkingCopyProvider; + + public SubTypeLocatorJob(ITypeInfo info, ITypeCache typeCache, IWorkingCopyProvider workingCopyProvider) { + super(TypeCacheMessages.getString("SubTypeLocatorJob.jobName"), FAMILY); //$NON-NLS-1$ + fLocateType = info; + fTypeCache = typeCache; + fWorkingCopyProvider= workingCopyProvider; + } + + public ITypeInfo getType() { + return fLocateType; + } + + protected IStatus runWithDelegatedProgress(IProgressMonitor monitor) throws InterruptedException { + boolean success = false; + long startTime = System.currentTimeMillis(); + trace("SubTypeLocatorJob: started"); //$NON-NLS-1$ + + try { + monitor.beginTask(TypeCacheMessages.getString("SubTypeLocatorJob.taskName"), 100); //$NON-NLS-1$ + + if (monitor.isCanceled()) + throw new InterruptedException(); + + TypeParser parser = new TypeParser(fTypeCache, fWorkingCopyProvider); + success = parser.findSubTypes(fLocateType, new SubProgressMonitor(monitor, 100)); + + if (monitor.isCanceled()) + throw new InterruptedException(); + + } finally { + long executionTime = System.currentTimeMillis() - startTime; + if (success) + trace("SubTypeLocatorJob: completed ("+ executionTime + " ms)"); //$NON-NLS-1$ //$NON-NLS-2$ + else + trace("SubTypeLocatorJob: aborted ("+ executionTime + " ms)"); //$NON-NLS-1$ //$NON-NLS-2$ + + monitor.done(); + } + + return Status.OK_STATUS; + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCache.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCache.java new file mode 100644 index 00000000000..07f4fbc25a1 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCache.java @@ -0,0 +1,896 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.core.browser.IQualifiedTypeName; +import org.eclipse.cdt.core.browser.ITypeCacheChangedListener; +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.ITypeInfoVisitor; +import org.eclipse.cdt.core.browser.ITypeReference; +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.IWorkingCopyProvider; +import org.eclipse.cdt.core.browser.QualifiedTypeName; +import org.eclipse.cdt.core.browser.TypeInfo; +import org.eclipse.cdt.core.browser.TypeSearchScope; +import org.eclipse.cdt.core.index.ICDTIndexer; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; +import org.eclipse.cdt.internal.core.browser.util.ArrayUtil; +import org.eclipse.cdt.internal.core.model.CModelManager; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.IJobChangeListener; +import org.eclipse.core.runtime.jobs.IJobManager; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; + +public class TypeCache implements ITypeCache { + + private static final int INITIAL_TYPE_COUNT = 100; + private final Map fTypeKeyMap = new HashMap(INITIAL_TYPE_COUNT); + final IProject fProject; + private final IWorkingCopyProvider fWorkingCopyProvider; + final Collection fDeltas = new ArrayList(); + final ITypeInfo fGlobalNamespace; + private final Map fTypeToSubTypes = new HashMap(); + private final Map fTypeToSuperTypes = new HashMap(); + ITypeCacheChangedListener fChangeListener = null; + + private static final class SuperTypeEntry { + ITypeInfo superType; + ASTAccessVisibility access; + boolean isVirtual; + SuperTypeEntry(ITypeInfo superType, ASTAccessVisibility access, boolean isVirtual) { + this.superType = superType; + this.access = access; + this.isVirtual = isVirtual; + } + } + + private SuperTypeEntry findSuperTypeEntry(Collection entryCollection, ITypeInfo superType) { + for (Iterator i = entryCollection.iterator(); i.hasNext(); ) { + SuperTypeEntry e = (SuperTypeEntry) i.next(); + if (e.superType.equals(superType)) { + return e; + } + } + return null; + } + + private static final int[] ENCLOSING_TYPES = {ICElement.C_NAMESPACE, ICElement.C_CLASS, ICElement.C_STRUCT, 0}; + + private IJobChangeListener fJobChangeListener = new IJobChangeListener() { + public void aboutToRun(IJobChangeEvent event) { + } + + public void awake(IJobChangeEvent event) { + } + + public void done(IJobChangeEvent event) { + Job job = event.getJob(); + if (job instanceof TypeCacherJob) { + TypeCacherJob deltaJob = (TypeCacherJob)job; + IStatus status = event.getResult(); + if (status != null) { + boolean jobFinished = (status.equals(Status.OK_STATUS) + && !deltaJob.isIndexerBusy()); + // remove the completed deltas + synchronized(fDeltas) { + for (Iterator i = fDeltas.iterator(); i.hasNext(); ) { + TypeCacheDelta delta = (TypeCacheDelta) i.next(); + if (delta.getJob() != null && delta.getJob().equals(deltaJob)) { + if (jobFinished) { + i.remove(); + } else { + delta.assignToJob(null); + } + } + } + } + } + // TODO finer-grained change deltas + if (fChangeListener != null) + fChangeListener.typeCacheChanged(fProject); + } + } + + public void running(IJobChangeEvent event) { + } + + public void scheduled(IJobChangeEvent event) { + } + + public void sleeping(IJobChangeEvent event) { + } + }; + + private static class GlobalNamespace implements IQualifiedTypeName { + + private static final String GLOBAL_NAMESPACE = TypeCacheMessages.getString("TypeCache.globalNamespace"); //$NON-NLS-1$ + private static final String[] segments = new String[] { GLOBAL_NAMESPACE }; + + public GlobalNamespace() { + } + + public String getName() { + return GLOBAL_NAMESPACE; + } + + public String[] getEnclosingNames() { + return null; + } + + public String getFullyQualifiedName() { + return GLOBAL_NAMESPACE; + } + + public IQualifiedTypeName getEnclosingTypeName() { + return null; + } + + public boolean isEmpty() { + return false; + } + + public boolean isGlobal() { + return true; + } + + public boolean isQualified() { + return false; + } + + public boolean isValidSegment(String segment) { + return false; + } + + public int segmentCount() { + return 1; + } + + public String[] segments() { + return segments; + } + + public String segment(int index) { + if (index > 0) + return null; + return GLOBAL_NAMESPACE; + } + + public String lastSegment() { + return GLOBAL_NAMESPACE; + } + + public int matchingFirstSegments(IQualifiedTypeName typeName) { + return 1; + } + + public boolean isPrefixOf(IQualifiedTypeName typeName) { + return true; + } + + public IQualifiedTypeName append(String[] names) { + return new QualifiedTypeName(names); + } + + public IQualifiedTypeName append(IQualifiedTypeName typeName) { + return new QualifiedTypeName(typeName); + } + + public IQualifiedTypeName append(String qualifiedName) { + return new QualifiedTypeName(qualifiedName); + } + + public IQualifiedTypeName removeFirstSegments(int count) { + return this; + } + + public IQualifiedTypeName removeLastSegments(int count) { + return this; + } + + public boolean isLowLevel() { + return false; + } + + public boolean isValid() { + return true; + } + + public int hashCode() { + return GLOBAL_NAMESPACE.hashCode(); + } + + public String toString() { + return getFullyQualifiedName(); + } + + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof IQualifiedTypeName)) { + return false; + } + return equals((IQualifiedTypeName)obj); + } + + public int compareTo(Object obj) { + if (obj == this) { + return 0; + } + if (!(obj instanceof IQualifiedTypeName)) { + throw new ClassCastException(); + } + return compareTo((IQualifiedTypeName)obj); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.IQualifiedTypeName#equals(org.eclipse.cdt.core.browser.IQualifiedTypeName) + */ + public boolean equals(IQualifiedTypeName typeName) { + return (typeName instanceof GlobalNamespace); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.IQualifiedTypeName#equalsIgnoreCase(org.eclipse.cdt.core.browser.IQualifiedTypeName) + */ + public boolean equalsIgnoreCase(IQualifiedTypeName typeName) { + return (typeName instanceof GlobalNamespace); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.IQualifiedTypeName#compareTo(org.eclipse.cdt.core.browser.IQualifiedTypeName) + */ + public int compareTo(IQualifiedTypeName typeName) { + return getFullyQualifiedName().compareTo(typeName.getFullyQualifiedName()); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.IQualifiedTypeName#compareToIgnoreCase(org.eclipse.cdt.core.browser.IQualifiedTypeName) + */ + public int compareToIgnoreCase(IQualifiedTypeName typeName) { + return getFullyQualifiedName().compareToIgnoreCase(typeName.getFullyQualifiedName()); + } + } + + private static class HashKey { + private IQualifiedTypeName name; + private int type; + public HashKey(IQualifiedTypeName name, int type) { + this.name = name; + this.type = type; + } + public int hashCode() { + return (this.name.hashCode() + this.type); + } + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof HashKey)) { + return false; + } + HashKey otherKey = (HashKey)obj; + return (this.type == otherKey.type && this.name.equals(otherKey.name)); + } + } + + public TypeCache(IProject project, IWorkingCopyProvider workingCopyProvider) { + fProject = project; + fWorkingCopyProvider = workingCopyProvider; + fDeltas.add(new TypeCacheDelta(fProject)); + fGlobalNamespace = new TypeInfo(ICElement.C_NAMESPACE, new GlobalNamespace()); + fGlobalNamespace.setCache(this); + } + + public TypeCache(IProject project, IWorkingCopyProvider workingCopyProvider, ITypeCacheChangedListener listener) { + this(project, workingCopyProvider); + fChangeListener = listener; + } + + public boolean contains(ISchedulingRule rule) { + if (this == rule) + return true; + if (rule instanceof ITypeCache) { + ITypeCache typeCache = (ITypeCache) rule; + if (fProject.equals(typeCache.getProject())) + return true; + } + return false; + } + + public boolean isConflicting(ISchedulingRule rule) { + if (rule instanceof ITypeCache) { + ITypeCache typeCache = (ITypeCache) rule; + if (fProject.equals(typeCache.getProject())) + return true; + } + return false; + } + + public IProject getProject() { + return fProject; + } + + public synchronized boolean isEmpty() { + return fTypeKeyMap.isEmpty(); + } + + public synchronized void insert(ITypeInfo newType) { + // check if enclosing types are already in cache + IQualifiedTypeName enclosingName = newType.getQualifiedTypeName().getEnclosingTypeName(); + if (enclosingName != null) { + while (!enclosingName.isEmpty()) { + // try namespace, class, struct, then undefined + ITypeInfo enclosingType = null; + for (int i = 0; enclosingType == null && i < ENCLOSING_TYPES.length; ++i) { + enclosingType = (ITypeInfo) fTypeKeyMap.get(new HashKey(enclosingName, ENCLOSING_TYPES[i])); + } + if (enclosingType == null) { + // create a dummy type to take this place (type 0 == unknown) + ITypeInfo dummyType = new TypeInfo(0, enclosingName); + dummyType.setCache(this); + fTypeKeyMap.put(new HashKey(enclosingName, 0), dummyType); + } + enclosingName = enclosingName.removeLastSegments(1); + } + } + + fTypeKeyMap.put(new HashKey(newType.getQualifiedTypeName(), newType.getCElementType()), newType); + newType.setCache(this); + } + + public synchronized void remove(ITypeInfo info) { + fTypeKeyMap.remove(new HashKey(info.getQualifiedTypeName(), info.getCElementType())); + info.setCache(null); + } + + public synchronized void flush(ITypeSearchScope scope) { + if (scope.encloses(fProject)) { + flushAll(); + } else { + for (Iterator mapIter = fTypeKeyMap.entrySet().iterator(); mapIter.hasNext(); ) { + Map.Entry entry = (Map.Entry) mapIter.next(); + ITypeInfo info = (ITypeInfo) entry.getValue(); + if (info.isEnclosed(scope)) { + mapIter.remove(); + } + } + } + } + + public synchronized void flush(IPath path) { + ITypeSearchScope scope = new TypeSearchScope(); + scope.add(path, false, null); + flush(scope); + } + + public synchronized void flushAll() { + // flush the entire cache + accept(new ITypeInfoVisitor() { + public boolean visit(ITypeInfo info) { + info.setCache(null); + return true; + } + public boolean shouldContinue() { return true; } + }); + fTypeKeyMap.clear(); + } + + public synchronized void addSupertype(ITypeInfo type, ITypeInfo supertype, ASTAccessVisibility access, boolean isVirtual) { + Collection entryCollection = (Collection) fTypeToSuperTypes.get(type); + if (entryCollection == null) { + entryCollection = new ArrayList(); + fTypeToSuperTypes.put(type, entryCollection); + } + if (findSuperTypeEntry(entryCollection, supertype) == null) { + entryCollection.add(new SuperTypeEntry(supertype, access, isVirtual)); + supertype.setCache(this); + } + } + + public synchronized ITypeInfo[] getSupertypes(ITypeInfo type) { + Collection entryCollection = (Collection) fTypeToSuperTypes.get(type); + if (entryCollection != null && !entryCollection.isEmpty()) { + ITypeInfo[] superTypes = new ITypeInfo[entryCollection.size()]; + int count = 0; + for (Iterator i = entryCollection.iterator(); i.hasNext(); ) { + SuperTypeEntry e = (SuperTypeEntry) i.next(); + superTypes[count++] = e.superType; + } + return superTypes; + } + return null; + } + + public ASTAccessVisibility getSupertypeAccess(ITypeInfo type, ITypeInfo superType) { + Collection entryCollection = (Collection) fTypeToSuperTypes.get(type); + if (entryCollection != null && !entryCollection.isEmpty()) { + SuperTypeEntry e = findSuperTypeEntry(entryCollection, superType); + if (e != null) + return e.access; + } + return null; + } + + public synchronized void addSubtype(ITypeInfo type, ITypeInfo subtype) { + Collection typeCollection = (Collection) fTypeToSubTypes.get(type); + if (typeCollection == null) { + typeCollection = new ArrayList(); + fTypeToSubTypes.put(type, typeCollection); + } + if (!typeCollection.contains(subtype)) { + typeCollection.add(subtype); + subtype.setCache(this); + } + } + + public synchronized ITypeInfo[] getSubtypes(ITypeInfo type) { + Collection typeCollection = (Collection) fTypeToSubTypes.get(type); + if (typeCollection != null && !typeCollection.isEmpty()) { + return (ITypeInfo[]) typeCollection.toArray(new ITypeInfo[typeCollection.size()]); + } + return null; + } + + public synchronized void accept(ITypeInfoVisitor visitor) { + for (Iterator mapIter = fTypeKeyMap.entrySet().iterator(); mapIter.hasNext(); ) { + Map.Entry entry = (Map.Entry) mapIter.next(); + ITypeInfo info = (ITypeInfo) entry.getValue(); + if (!visitor.shouldContinue()) + return; // stop visiting + visitor.visit(info); + } + } + + public synchronized IPath[] getPaths(final ITypeSearchScope scope) { + final Set pathSet = new HashSet(); + accept(new ITypeInfoVisitor() { + public boolean visit(ITypeInfo info) { + if (scope == null || info.isEnclosed(scope)) { + ITypeReference[] refs = info.getReferences(); + if (refs != null) { + for (int i = 0; i < refs.length; ++i) { + IPath path = refs[i].getPath(); + if (scope == null || scope.encloses(path)) + pathSet.add(path); + } + } + } + return true; + } + public boolean shouldContinue() { return true; } + }); + return (IPath[]) pathSet.toArray(new IPath[pathSet.size()]); + } + + public synchronized ITypeInfo[] getTypes(final ITypeSearchScope scope) { + final Collection results = new ArrayList(); + accept(new ITypeInfoVisitor() { + public boolean visit(ITypeInfo info) { + if (scope == null || info.isEnclosed(scope)) { + results.add(info); + } + return true; + } + public boolean shouldContinue() { return true; } + }); + return (ITypeInfo[]) results.toArray(new ITypeInfo[results.size()]); + } + + public synchronized ITypeInfo[] getTypes(IQualifiedTypeName qualifiedName, boolean matchEnclosed, boolean ignoreCase) { + Collection results = new ArrayList(); + if (!ignoreCase && !matchEnclosed) { + for (int i = 0; i < ITypeInfo.KNOWN_TYPES.length; ++i) { + ITypeInfo info = (ITypeInfo) fTypeKeyMap.get(new HashKey(qualifiedName, ITypeInfo.KNOWN_TYPES[i])); + if (info != null) { + results.add(info); + } + } + ITypeInfo info = (ITypeInfo) fTypeKeyMap.get(new HashKey(qualifiedName, 0)); + if (info != null) { + results.add(info); + } + } else { + // TODO this should probably use a more efficient search algorithm + for (Iterator mapIter = fTypeKeyMap.entrySet().iterator(); mapIter.hasNext(); ) { + Map.Entry entry = (Map.Entry) mapIter.next(); + ITypeInfo info = (ITypeInfo) entry.getValue(); + IQualifiedTypeName currName = info.getQualifiedTypeName(); + + if (ignoreCase) { + if (matchEnclosed && currName.segmentCount() > qualifiedName.segmentCount() + && currName.lastSegment().equalsIgnoreCase(qualifiedName.lastSegment())) { + currName = currName.removeFirstSegments(currName.segmentCount() - qualifiedName.segmentCount()); + } + if (currName.equalsIgnoreCase(qualifiedName)) { + results.add(info); + } + } else { + if (matchEnclosed && currName.segmentCount() > qualifiedName.segmentCount() + && currName.lastSegment().equals(qualifiedName.lastSegment())) { + currName = currName.removeFirstSegments(currName.segmentCount() - qualifiedName.segmentCount()); + } + if (currName.equals(qualifiedName)) { + results.add(info); + } + } + } + } + return (ITypeInfo[]) results.toArray(new ITypeInfo[results.size()]); + } + + public synchronized ITypeInfo getType(int type, IQualifiedTypeName qualifiedName) { + ITypeInfo info = (ITypeInfo) fTypeKeyMap.get(new HashKey(qualifiedName, type)); + if (info == null && type != 0) { + info = (ITypeInfo) fTypeKeyMap.get(new HashKey(qualifiedName, 0)); + } + return info; + } + + public synchronized ITypeInfo getEnclosingType(ITypeInfo info, final int[] kinds) { + IQualifiedTypeName enclosingName = info.getQualifiedTypeName().getEnclosingTypeName(); + if (enclosingName != null) { + // try namespace, class, struct, then undefined + ITypeInfo enclosingType = null; + for (int i = 0; enclosingType == null && i < ENCLOSING_TYPES.length; ++i) { + if (ArrayUtil.contains(kinds, ENCLOSING_TYPES[i])) { + enclosingType = (ITypeInfo) fTypeKeyMap.get(new HashKey(enclosingName, ENCLOSING_TYPES[i])); + } + } + return enclosingType; + } + return null; + } + + public synchronized ITypeInfo getEnclosingNamespace(ITypeInfo info, boolean includeGlobalNamespace) { + IQualifiedTypeName enclosingName = info.getQualifiedTypeName().getEnclosingTypeName(); + if (enclosingName != null) { + // look for namespace + ITypeInfo enclosingType = (ITypeInfo) fTypeKeyMap.get(new HashKey(enclosingName, ICElement.C_NAMESPACE)); + if (enclosingType != null) { + return enclosingType; + } + // try class, struct, then undefined + final int[] kinds = {ICElement.C_CLASS, ICElement.C_STRUCT, 0}; + for (int i = 0; enclosingType == null && i < kinds.length; ++i) { + enclosingType = (ITypeInfo) fTypeKeyMap.get(new HashKey(enclosingName, kinds[i])); + } + if (enclosingType != null) { + return getEnclosingNamespace(enclosingType, includeGlobalNamespace); + } + } + if (includeGlobalNamespace) + return fGlobalNamespace; + return null; + } + + public synchronized ITypeInfo getRootNamespace(ITypeInfo info, boolean includeGlobalNamespace) { + IQualifiedTypeName qualifiedName = info.getQualifiedTypeName(); + if (qualifiedName.isGlobal()) { + if (info.getCElementType() == ICElement.C_NAMESPACE) + return info; + if (includeGlobalNamespace) + return fGlobalNamespace; + return null; + } + IQualifiedTypeName namespace = qualifiedName.removeLastSegments(qualifiedName.segmentCount()-1); + // try namespace, then undefined + ITypeInfo namespaceType = (ITypeInfo) fTypeKeyMap.get(new HashKey(namespace, ICElement.C_NAMESPACE)); + if (namespaceType == null) + namespaceType = (ITypeInfo) fTypeKeyMap.get(new HashKey(namespace, 0)); + return namespaceType; + } + + public synchronized boolean hasEnclosedTypes(final ITypeInfo info) { + final IQualifiedTypeName parentName = info.getQualifiedTypeName(); + final boolean[] foundTypes = { false }; + accept(new ITypeInfoVisitor() { + public boolean visit(ITypeInfo type) { + if (type != info && parentName.isPrefixOf(type.getQualifiedTypeName())) { + foundTypes[0] = true; + } + return true; + } + public boolean shouldContinue() { + return !foundTypes[0]; + } + }); + return foundTypes[0]; + } + + public synchronized ITypeInfo[] getEnclosedTypes(final ITypeInfo enclosedBy, final int kinds[]) { + final IQualifiedTypeName parentName = enclosedBy.getQualifiedTypeName(); + final Collection results = new ArrayList(); + accept(new ITypeInfoVisitor() { + public boolean visit(ITypeInfo type) { + if (ArrayUtil.contains(kinds, type.getCElementType())) { + IQualifiedTypeName enclosingName = type.getQualifiedTypeName().getEnclosingTypeName(); + if (enclosedBy == fGlobalNamespace) { + if (enclosingName == null) { + results.add(type); + } else { +// // check if enclosing parent is namespace +// getRootNamespace(type); + } + } else if (parentName.equals(enclosingName)) { + results.add(type); + } + } + return true; + } + public boolean shouldContinue() { return true; } + }); + + return (ITypeInfo[]) results.toArray(new ITypeInfo[results.size()]); + } + + public ITypeInfo getGlobalNamespace() { + return fGlobalNamespace; + } + + public boolean isUpToDate() { + synchronized(fDeltas) { + return fDeltas.isEmpty(); + } + } + + public void addDelta(TypeCacheDelta delta) { + synchronized(fDeltas) { + fDeltas.add(delta); + } + } + + public void reconcile(boolean enableIndexing, int priority, int delay) { + // check if anything needs to be done + if (deltasRemaining() == 0) + return; // nothing to do + + // cancel any scheduled or running jobs for this project + IJobManager jobManager = Platform.getJobManager(); + Job[] jobs = jobManager.find(TypeCacherJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + TypeCacherJob deltaJob = (TypeCacherJob) jobs[i]; + if (deltaJob.getCache().equals(this)) { + deltaJob.cancel(); + } + } + + // check again, in case some jobs finished in the meantime + if (deltasRemaining() == 0) + return; // nothing to do + + TypeCacherJob deltaJob = null; + IndexManager indexManager = CModelManager.getDefault().getIndexManager(); + ICDTIndexer indexer = indexManager.getIndexerForProject( fProject ); + boolean haveIndexer = (indexer != null && indexer.isIndexEnabled( fProject )); + synchronized(fDeltas) { + if( haveIndexer ){ + // grab all the remaining deltas + TypeCacheDelta[] jobDeltas = (TypeCacheDelta[]) fDeltas.toArray(new TypeCacheDelta[fDeltas.size()]); + + // assign deltas to job + if (jobDeltas != null) { + // create a new job + deltaJob = new TypeCacherJob(this, jobDeltas, enableIndexing); + for (int i = 0; i < jobDeltas.length; ++i) { + jobDeltas[i].assignToJob(deltaJob); + fDeltas.remove(jobDeltas[i]); + } + } + } else { + //we don't have an indexer, don't create a job to do these deltas, throw them away + fDeltas.clear(); + } + } + + if( deltaJob != null ){ + // schedule the new job + deltaJob.addJobChangeListener(fJobChangeListener); + deltaJob.setPriority(priority); + deltaJob.schedule(delay); + } + } + + public void reconcileAndWait(boolean enableIndexing, int priority, IProgressMonitor monitor) { + reconcile(enableIndexing, priority, 0); + + // wait for jobs to complete + IJobManager jobManager = Platform.getJobManager(); + Job[] jobs = jobManager.find(TypeCacherJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + TypeCacherJob deltaJob = (TypeCacherJob) jobs[i]; + if (deltaJob.getCache().equals(this)) { + try { + deltaJob.join(monitor); + } catch (InterruptedException e) { + } + } + } + } + + // returns the number of deltas either not assigned to a job, + // or assigned to a job which is not yet running + private int deltasRemaining() { + // count the left-over deltas + synchronized(fDeltas) { + int count = 0; + for (Iterator i = fDeltas.iterator(); i.hasNext(); ) { + TypeCacheDelta delta = (TypeCacheDelta) i.next(); + TypeCacherJob job = delta.getJob(); + if (job == null || !job.isRunning()) { + ++count; + } + } + return count; + } + } + + public void cancelJobs() { + IJobManager jobManager = Platform.getJobManager(); + Job[] jobs = jobManager.find(TypeCacherJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + TypeCacherJob deltaJob = (TypeCacherJob) jobs[i]; + if (deltaJob.getCache().equals(this)) { + deltaJob.cancel(); + } + } + jobs = jobManager.find(TypeLocatorJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + TypeLocatorJob locatorJob = (TypeLocatorJob) jobs[i]; + if (locatorJob.getType().getEnclosingProject().equals(fProject)) { + locatorJob.cancel(); + } + } + jobs = jobManager.find(SubTypeLocatorJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + SubTypeLocatorJob locatorJob = (SubTypeLocatorJob) jobs[i]; + if (locatorJob.getType().getEnclosingProject().equals(fProject)) { + locatorJob.cancel(); + } + } + } + + public void locateType(ITypeInfo info, int priority, int delay) { + ITypeReference location = info.getResolvedReference(); + if (location != null) + return; // nothing to do + + // cancel any scheduled or running jobs for this type + IJobManager jobManager = Platform.getJobManager(); + Job[] jobs = jobManager.find(TypeLocatorJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + TypeLocatorJob locatorJob = (TypeLocatorJob) jobs[i]; + if (locatorJob.getType().equals(info)) { + locatorJob.cancel(); + } + } + + // check again, in case some jobs finished in the meantime + location = info.getResolvedReference(); + if (location != null) + return; // nothing to do + + // create a new job + TypeLocatorJob locatorJob = new TypeLocatorJob(info, this, fWorkingCopyProvider); + // schedule the new job + locatorJob.setPriority(priority); + locatorJob.schedule(delay); + } + + public ITypeReference locateTypeAndWait(ITypeInfo info, int priority, IProgressMonitor monitor) { + locateType(info, priority, 0); + + // wait for jobs to complete + IJobManager jobManager = Platform.getJobManager(); + Job[] jobs = jobManager.find(TypeLocatorJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + TypeLocatorJob locatorJob = (TypeLocatorJob) jobs[i]; + if (locatorJob.getType().equals(info)) { + try { + locatorJob.join(monitor); + } catch (InterruptedException e) { + } + } + } + + return info.getResolvedReference(); + } + + public void locateSupertypes(ITypeInfo info, int priority, int delay) { + ITypeInfo[] superTypes = getSupertypes(info); + if (superTypes != null) + return; // nothing to do + + locateType(info, priority, delay); + } + + public ITypeInfo[] locateSupertypesAndWait(ITypeInfo info, int priority, IProgressMonitor monitor) { + locateSupertypes(info, priority, 0); + + // wait for jobs to complete + IJobManager jobManager = Platform.getJobManager(); + Job[] jobs = jobManager.find(SubTypeLocatorJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + SubTypeLocatorJob locatorJob = (SubTypeLocatorJob) jobs[i]; + if (locatorJob.getType().equals(info)) { + try { + locatorJob.join(monitor); + } catch (InterruptedException e) { + } + } + } + + return getSupertypes(info); + } + + public void locateSubtypes(ITypeInfo info, int priority, int delay) { + ITypeInfo[] subTypes = getSubtypes(info); + if (subTypes != null) + return; // nothing to do + + // cancel any scheduled or running jobs for this type + IJobManager jobManager = Platform.getJobManager(); + Job[] jobs = jobManager.find(SubTypeLocatorJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + SubTypeLocatorJob locatorJob = (SubTypeLocatorJob) jobs[i]; + if (locatorJob.getType().equals(info)) { + locatorJob.cancel(); + } + } + + // check again, in case some jobs finished in the meantime + subTypes = getSubtypes(info); + if (subTypes != null) + return; // nothing to do + + // create a new job + SubTypeLocatorJob locatorJob = new SubTypeLocatorJob(info, this, fWorkingCopyProvider); + // schedule the new job + locatorJob.setPriority(priority); + locatorJob.schedule(delay); + } + + public ITypeInfo[] locateSubtypesAndWait(ITypeInfo info, int priority, IProgressMonitor monitor) { + locateSubtypes(info, priority, 0); + + // wait for jobs to complete + IJobManager jobManager = Platform.getJobManager(); + Job[] jobs = jobManager.find(SubTypeLocatorJob.FAMILY); + for (int i = 0; i < jobs.length; ++i) { + SubTypeLocatorJob locatorJob = (SubTypeLocatorJob) jobs[i]; + if (locatorJob.getType().equals(info)) { + try { + locatorJob.join(monitor); + } catch (InterruptedException e) { + } + } + } + + return getSubtypes(info); + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheDelta.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheDelta.java new file mode 100644 index 00000000000..6b897bdb3ec --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheDelta.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.TypeSearchScope; +import org.eclipse.cdt.core.model.ICElementDelta; +import org.eclipse.core.resources.IProject; + + +public class TypeCacheDelta { + private IProject fProject = null; + private ICElementDelta fCElementDelta = null; + private ITypeSearchScope fScope = null; + private TypeCacherJob fJob = null; + + public TypeCacheDelta(IProject project, ICElementDelta delta) { + fProject = project; + fCElementDelta = delta; + } + + public TypeCacheDelta(IProject project, ITypeSearchScope scope) { + fProject = project; + fScope = scope; + } + + public TypeCacheDelta(IProject project) { + fProject = project; + fScope = new TypeSearchScope(); + fScope.add(project); + } + + public IProject getProject() { + return fProject; + } + + public ITypeSearchScope getScope() { + return fScope; + } + + public ICElementDelta getCElementDelta() { + return fCElementDelta; + } + + public void assignToJob(TypeCacherJob job) { + fJob = job; + } + + public TypeCacherJob getJob() { + return fJob; + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheManager.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheManager.java new file mode 100644 index 00000000000..623488a179a --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheManager.java @@ -0,0 +1,471 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.browser.IQualifiedTypeName; +import org.eclipse.cdt.core.browser.ITypeCacheChangedListener; +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.ITypeReference; +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.IWorkingCopyProvider; +import org.eclipse.cdt.core.browser.TypeSearchScope; +import org.eclipse.cdt.core.browser.TypeUtil; +import org.eclipse.cdt.core.model.ElementChangedEvent; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICElementDelta; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.internal.core.model.CModelManager; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.IJobManager; +import org.eclipse.core.runtime.jobs.Job; + +public class TypeCacheManager implements ITypeCacheChangedListener, IndexManager.IIndexerSelectionListener { + private static final TypeCacheManager fgInstance = new TypeCacheManager(); + private Map fCacheMap; + private IWorkingCopyProvider fWorkingCopyProvider; + private ArrayList fChangeListeners = new ArrayList(); + + private static final int INITIAL_TYPE_MAP_SIZE = 50; + //TODO make this a WeakHashMap or LRUCache + private Map fTypeToElementMap = new HashMap(INITIAL_TYPE_MAP_SIZE); + private Map fElementToTypeMap = new HashMap(INITIAL_TYPE_MAP_SIZE); + private boolean processTypeCacheEvents = true; + + private TypeCacheManager() { + fCacheMap = new HashMap(); + CModelManager.getDefault().getIndexManager().subscribeForIndexerChangeNotifications( this ); + } + + public static TypeCacheManager getInstance() { + return fgInstance; + } + + protected void finalize() throws Throwable { + CModelManager.getDefault().getIndexManager().unSubscribeForIndexerChangeNotifications( this ); + super.finalize(); + } + + public void setWorkingCopyProvider(IWorkingCopyProvider workingCopyProvider) { + fWorkingCopyProvider = workingCopyProvider; + } + + public synchronized void updateProject(IProject project) { + // TODO finer-grained flush needed, for now just flush the whole map + fTypeToElementMap.clear(); + fElementToTypeMap.clear(); + addCacheDelta(project, null); + } + + public synchronized void processElementChanged(ElementChangedEvent event, boolean enableIndexing) { + int deltaCount = processDelta(event.getDelta()); + if (deltaCount > 0) { + // TODO finer-grained flush needed, for now just flush the whole map + fTypeToElementMap.clear(); + fElementToTypeMap.clear(); + reconcile(enableIndexing, Job.BUILD, 0); + } + } + + private static final int PATH_ENTRY_FLAGS = ICElementDelta.F_ADDED_PATHENTRY_SOURCE + | ICElementDelta.F_REMOVED_PATHENTRY_SOURCE + | ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE + | ICElementDelta.F_CHANGED_PATHENTRY_MACRO + | ICElementDelta.F_PATHENTRY_REORDER; + + private int processDelta(ICElementDelta delta) { + ICElement elem = delta.getElement(); + boolean added = (delta.getKind() == ICElementDelta.ADDED); + boolean removed = (delta.getKind() == ICElementDelta.REMOVED); + boolean contentChanged = ((delta.getFlags() & ICElementDelta.F_CONTENT) != 0); + boolean pathEntryChanged = ((delta.getFlags() & PATH_ENTRY_FLAGS) != 0); + boolean openedOrClosed = (((delta.getFlags() & ICElementDelta.F_CLOSED) != 0) || ((delta.getFlags() & ICElementDelta.F_OPENED) != 0)); + boolean hasChildren = ((delta.getFlags() & ICElementDelta.F_CHILDREN) != 0); + int deltaCount = 0; + + + switch (elem.getElementType()) { + case ICElement.C_PROJECT: + case ICElement.C_CCONTAINER: { + ICProject cProject = elem.getCProject(); + IProject project = cProject.getProject(); + if (added || removed || pathEntryChanged || openedOrClosed) { + addCacheDelta(project, delta); + ++deltaCount; + } + } + break; + + case ICElement.C_UNIT: { + ICProject cProject = elem.getCProject(); + IProject project = cProject.getProject(); + ITranslationUnit unit = (ITranslationUnit) elem; + if (unit.isWorkingCopy()) { + deltaCount += processWorkingCopyDelta(delta); + return deltaCount; + } + if (added || removed || pathEntryChanged || contentChanged) { + addCacheDelta(project, delta); + ++deltaCount; + } + } + break; + + case ICElement.C_INCLUDE: + case ICElement.C_NAMESPACE: + case ICElement.C_TEMPLATE_CLASS: + case ICElement.C_CLASS: + case ICElement.C_STRUCT: + case ICElement.C_UNION: + case ICElement.C_ENUMERATION: + case ICElement.C_TYPEDEF: + { + ICProject cProject = elem.getCProject(); + IProject project = cProject.getProject(); + if (added || removed) { + addCacheDelta(project, delta); + ++deltaCount; + } + } + break; + } + + if (hasChildren) { + ICElementDelta[] children = delta.getAffectedChildren(); + if (children != null) { + for (int i = 0; i < children.length; ++i) { + deltaCount += processDelta(children[i]); + } + } + } + + return deltaCount; + } + + private void addCacheDelta(IProject project, ICElementDelta delta) { + if (delta == null) { + getCache(project).addDelta(new TypeCacheDelta(project)); + } else { + getCache(project).addDelta(new TypeCacheDelta(project, delta)); + } + } + + private int processWorkingCopyDelta(ICElementDelta delta) { + // ignore workies copies for now + return 0; +/* ICElement elem = delta.getElement(); + boolean added = (delta.getKind() == ICElementDelta.ADDED); + boolean removed = (delta.getKind() == ICElementDelta.REMOVED); + boolean contentChanged = ((delta.getFlags() & ICElementDelta.F_CONTENT) != 0); + boolean pathEntryChanged = ((delta.getFlags() & PATH_ENTRY_FLAGS) != 0); + boolean hasChildren = ((delta.getFlags() & ICElementDelta.F_CHILDREN) != 0); + + switch (elem.getElementType()) { + case ICElement.C_UNIT: { + ICProject cProject = elem.getCProject(); + IProject project = cProject.getProject(); + if (added || removed || pathEntryChanged || contentChanged) { + TypeCacheDelta cacheDelta = new TypeCacheDelta(project, delta); + getCache(project).addDelta(cacheDelta); + } + } + break; + + case ICElement.C_INCLUDE: + case ICElement.C_NAMESPACE: + case ICElement.C_TEMPLATE_CLASS: + case ICElement.C_CLASS: + case ICElement.C_STRUCT: + case ICElement.C_UNION: + case ICElement.C_ENUMERATION: + case ICElement.C_TYPEDEF: + { + ICProject cProject = elem.getCProject(); + IProject project = cProject.getProject(); + if (added || removed) { + TypeCacheDelta cacheDelta = new TypeCacheDelta(project, delta); + getCache(project).addDelta(cacheDelta); + } + } + break; + } + + if (hasChildren) { + ICElementDelta[] children = delta.getAffectedChildren(); + if (children != null) { + for (int i = 0; i < children.length; ++i) { + processWorkingCopyDelta(children[i]); + } + } + } +*/ + } + + public synchronized ITypeCache getCache(IProject project) { + synchronized(fCacheMap) { + ITypeCache cache = (ITypeCache) fCacheMap.get(project); + if (cache == null) { + cache = new TypeCache(project, fWorkingCopyProvider, this); + fCacheMap.put(project, cache); + } + return cache; + } + } + + public synchronized void reconcile(boolean enableIndexing, int priority, int delay) { + if (!(processTypeCacheEvents)) + return; + + TypeSearchScope workspaceScope = new TypeSearchScope(true); + IProject[] projects = workspaceScope.getEnclosingProjects(); + for (int i = 0; i < projects.length; ++i) { + ITypeCache cache = getCache(projects[i]); + cache.reconcile(enableIndexing, priority, delay); + } + } + + public synchronized void reconcileAndWait(boolean enableIndexing, int priority, IProgressMonitor monitor) { + if (!(processTypeCacheEvents)) + return; + + TypeSearchScope workspaceScope = new TypeSearchScope(true); + IProject[] projects = workspaceScope.getEnclosingProjects(); + for (int i = 0; i < projects.length; ++i) { + ITypeCache cache = getCache(projects[i]); + cache.reconcileAndWait(enableIndexing, priority, monitor); + } + } + + public void cancelJobs() { + IJobManager jobManager = Platform.getJobManager(); + jobManager.cancel(TypeCacherJob.FAMILY); + jobManager.cancel(TypeLocatorJob.FAMILY); + } + + public ITypeInfo[] locateSuperTypesAndWait(ITypeInfo info, boolean enableIndexing, int priority, IProgressMonitor monitor) { + ITypeInfo[] superTypes = info.getSuperTypes(); + if (superTypes == null) { + // cancel background jobs + IProject project = info.getEnclosingProject(); + getCache(project).cancelJobs(); + + // start the search job + getCache(project).locateSupertypesAndWait(info, priority, monitor); + + superTypes = info.getSuperTypes(); + + // resume background jobs + reconcile(enableIndexing, Job.BUILD, 0); + } + return superTypes; + } + + public ITypeInfo[] locateSubTypesAndWait(ITypeInfo info, boolean enableIndexing, int priority, IProgressMonitor monitor) { + ITypeInfo[] subTypes = info.getSubTypes(); + if (subTypes == null) { + // cancel background jobs + IProject project = info.getEnclosingProject(); + getCache(project).cancelJobs(); + + // start the search job + getCache(project).locateSubtypesAndWait(info, priority, monitor); + + subTypes = info.getSubTypes(); + + // resume background jobs + reconcile(enableIndexing, Job.BUILD, 0); + } + return subTypes; + } + + public void updateCache(ITypeSearchScope scope, IProgressMonitor monitor) { + // schedule jobs to update cache + IProject[] projects = scope.getEnclosingProjects(); + monitor.beginTask(TypeCacheMessages.getString("AllTypesCache.updateCache.taskName"), projects.length); //$NON-NLS-1$ + for (int i = 0; i < projects.length; ++i) { + IProject project = projects[i]; + // wait for any running jobs to finish + getCache(project).reconcileAndWait(true, Job.SHORT, new SubProgressMonitor(monitor, 1)); + } + monitor.done(); + } + + /** + * Resolves a type location. + * + * @param info the type to search for + * @param monitor the progress monitor + */ + public ITypeReference resolveTypeLocation(ITypeInfo info, IProgressMonitor monitor, boolean enableIndexing) { + ITypeReference location = info.getResolvedReference(); + if (location == null) { + // cancel background jobs + IProject project = info.getEnclosingProject(); + ITypeCache cache = getCache(project); + cache.cancelJobs(); + + // start the search job + cache.locateTypeAndWait(info, Job.SHORT, monitor); + + // get the newly parsed location + location = info.getResolvedReference(); + + // resume background jobs + reconcile(enableIndexing, Job.BUILD, 0); + } + return location; + } + + public void addTypeCacheChangedListener(ITypeCacheChangedListener listener) { + // add listener only if it is not already present + synchronized(fChangeListeners) { + if (!fChangeListeners.contains(listener)) { + fChangeListeners.add(listener); + } + } + } + + public void removeTypeCacheChangedListener(ITypeCacheChangedListener listener) { + synchronized(fChangeListeners) { + fChangeListeners.remove(listener); + } + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.browser.ITypeCacheChangedListener#typeCacheChanged(org.eclipse.core.resources.IProject) + */ + public synchronized void typeCacheChanged(final IProject project) { + // clone so that a listener cannot have a side-effect on this list when being notified + ArrayList listeners; + synchronized(fChangeListeners) { + listeners = (ArrayList) fChangeListeners.clone(); + } + for (Iterator i = listeners.iterator(); i.hasNext(); ) { + final ITypeCacheChangedListener listener = (ITypeCacheChangedListener) i.next(); + Platform.run(new ISafeRunnable() { + public void handleException(Throwable e) { + IStatus status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, IStatus.ERROR, "Exception occurred in listener of type cache change notification", e); //$NON-NLS-1$ + CCorePlugin.log(status); + } + public void run() throws Exception { + listener.typeCacheChanged(project); + } + }); + } + } + + public ITypeInfo getTypeForElement(ICElement element, boolean forceUpdate, boolean forceResolve, boolean enableIndexing, IProgressMonitor monitor) { + if (element.exists()) { + ITypeInfo cachedInfo = (ITypeInfo) fElementToTypeMap.get(element); + if (cachedInfo != null && cachedInfo.exists()) + return cachedInfo; + } + + IQualifiedTypeName qualifiedName = TypeUtil.getFullyQualifiedName(element); + if (qualifiedName != null) { + ICProject cProject = element.getCProject(); + IProject project = cProject.getProject(); + ITypeCache cache = getCache(project); + if (!cache.isUpToDate() && forceUpdate) { + if (monitor == null) + monitor = new NullProgressMonitor(); + // wait for any running jobs to finish + cache.reconcileAndWait(true, Job.SHORT, monitor); + } + + ITypeInfo info = cache.getType(element.getElementType(), qualifiedName); + if (info != null) { + ITypeReference ref = info.getResolvedReference(); + if (ref == null && forceResolve) { + if (monitor == null) + monitor = new NullProgressMonitor(); + ref = resolveTypeLocation(info, monitor, enableIndexing); + } + + // cache for later use + fElementToTypeMap.put(element, info); + return info; + } + } + return null; + } + + public ICElement getElementForType(ITypeInfo type, boolean forceUpdate, boolean forceResolve, boolean enableIndexing, IProgressMonitor monitor) { + if (type.exists()) { + ICElement cachedElem = (ICElement) fTypeToElementMap.get(type); + if (cachedElem != null && cachedElem.exists()) + return cachedElem; + } + + IProject project = type.getEnclosingProject(); + ITypeCache cache = getCache(project); + if (!cache.isUpToDate() && forceUpdate) { + if (monitor == null) + monitor = new NullProgressMonitor(); + // wait for any running jobs to finish + cache.reconcileAndWait(true, Job.SHORT, monitor); + + //TODO replace type with new type from cache??? + } + + ITypeReference ref = type.getResolvedReference(); + if (ref == null && forceResolve) { + ref = resolveTypeLocation(type, monitor, enableIndexing); + } + if (ref != null) { + ICElement[] elems = ref.getCElements(); + if (elems != null && elems.length > 0) { + ICElement foundElem = elems[0]; + if (elems.length > 1) { + 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 + foundElem = elem; + break; + } + } + } + + if (foundElem != null) { + // cache for later use + fTypeToElementMap.put(type, foundElem); + return foundElem; + } + } + } + return null; + } + public boolean getProcessTypeCacheEvents() { + return processTypeCacheEvents; + } + public void setProcessTypeCacheEvents(boolean processTypeCacheEvents) { + this.processTypeCacheEvents = processTypeCacheEvents; + } + + public void indexerSelectionChanged(IProject project) { + addCacheDelta(project, null ); + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheMessages.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheMessages.java new file mode 100644 index 00000000000..30e20e62572 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheMessages.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class TypeCacheMessages { + + private static final String RESOURCE_BUNDLE= TypeCacheMessages.class.getName(); + + private static ResourceBundle fgResourceBundle; + static { + try { + fgResourceBundle = ResourceBundle.getBundle(RESOURCE_BUNDLE); + } catch (MissingResourceException x) { + fgResourceBundle = null; + } + } + + private TypeCacheMessages() { + } + + public static String getString(String key) { + try { + return fgResourceBundle.getString(key); + } catch (MissingResourceException e) { + return '!' + key + '!'; + } catch (NullPointerException e) { + return "#" + key + "#"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + public static String getFormattedString(String key, String arg) { + return getFormattedString(key, new String[] { arg }); + } + + public static String getFormattedString(String key, String[] args) { + return MessageFormat.format(getString(key), args); + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheMessages.properties b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheMessages.properties new file mode 100644 index 00000000000..c4d26740d29 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacheMessages.properties @@ -0,0 +1,23 @@ +############################################################################### +# Copyright (c) 2000, 2004 QNX Software Systems and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# QNX Software Systems - Initial API and implementation +############################################################################### + +AllTypesCache.updateCache.taskName=Updating Type Cache... + +TypeCacherJob.defaultJobName=Type Cache +TypeCacherJob.jobName=Type Cache [{0}] +TypeCacherJob.taskName=Updating Type Cache... + +TypeLocatorJob.jobName=Type Locator +TypeLocatorJob.taskName=Searching for Type Declaration... +SubTypeLocatorJob.jobName=Subtype Locator +SubTypeLocatorJob.taskName=Searching for Subtypes... + +TypeCache.globalNamespace=(global) diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacherJob.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacherJob.java new file mode 100644 index 00000000000..3cb779f2e13 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeCacherJob.java @@ -0,0 +1,275 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.TypeSearchScope; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICElementDelta; +import org.eclipse.cdt.core.search.ICSearchConstants; +import org.eclipse.cdt.internal.core.index.IIndex; +import org.eclipse.cdt.internal.core.model.CModelManager; +import org.eclipse.cdt.internal.core.search.indexing.IndexManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; + + +/** + * Background job for filling the type cache. + * @see org.eclipse.core.runtime.jobs.Job + * @since 3.0 + */ +public class TypeCacherJob extends BasicJob { + + public static final Object FAMILY = new Object(); + private IndexManager fIndexManager; + private ITypeCache fTypeCache; + private TypeCacheDelta[] fDeltas; + private boolean fEnableIndexing; + private boolean fIndexerIsBusy; + + public TypeCacherJob(ITypeCache typeCache, TypeCacheDelta[] deltas, boolean enableIndexing) { + super(TypeCacheMessages.getString("TypeCacherJob.defaultJobName"), FAMILY); //$NON-NLS-1$ + fTypeCache = typeCache; + fDeltas = deltas; + fEnableIndexing = enableIndexing; + fIndexerIsBusy = false; + fIndexManager = CModelManager.getDefault().getIndexManager(); + setPriority(BUILD); + setSystem(true); + setRule(typeCache); + setName(TypeCacheMessages.getFormattedString("TypeCacherJob.jobName", fTypeCache.getProject().getName())); //$NON-NLS-1$ + } + + public ITypeCache getCache() { + return fTypeCache; + } + + public TypeCacheDelta[] getDeltas() { + return fDeltas; + } + + public boolean isIndexerBusy() { + return fIndexerIsBusy; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#run(IProgressMonitor) + */ + protected IStatus runWithDelegatedProgress(IProgressMonitor monitor) throws InterruptedException { + boolean success = false; + long startTime = System.currentTimeMillis(); + trace("TypeCacherJob: started"); //$NON-NLS-1$ + + try { + int totalWork = 100; + monitor.beginTask(TypeCacheMessages.getString("TypeCacherJob.taskName"), totalWork); //$NON-NLS-1$ + + // figure out what needs to be flushed + TypeSearchScope flushScope = new TypeSearchScope(); + if (fDeltas != null) { + for (int i = 0; i < fDeltas.length; ++i) { + TypeCacheDelta delta = fDeltas[i]; + prepareToFlush(delta, flushScope); + } + } + + if (monitor.isCanceled()) + throw new InterruptedException(); + + // flush the cache + int flushWork = 0; + if (!flushScope.isEmpty()) { + flushWork = totalWork / 4; + IProgressMonitor subMonitor = new SubProgressMonitor(monitor, flushWork); + flush(flushScope, subMonitor); + } + + // update the cache + IProgressMonitor subMonitor = new SubProgressMonitor(monitor, totalWork - flushWork); + update(flushScope, subMonitor); + + if (monitor.isCanceled()) + throw new InterruptedException(); + } finally { + long executionTime = System.currentTimeMillis() - startTime; + if (success) + trace("TypeCacherJob: completed ("+ executionTime + " ms)"); //$NON-NLS-1$ //$NON-NLS-2$ + else + trace("TypeCacherJob: aborted ("+ executionTime + " ms)"); //$NON-NLS-1$ //$NON-NLS-2$ + + monitor.done(); + } + + return Status.OK_STATUS; + } + + private void flush(ITypeSearchScope scope, IProgressMonitor monitor) throws InterruptedException { + // flush the cache + boolean success = true; + IProject project = fTypeCache.getProject(); + + monitor.beginTask("", 100); //$NON-NLS-1$ + + fTypeCache.flush(scope); + if (!scope.encloses(project)) { + if (project.exists() && project.isOpen()) { + success = doIndexerJob(new IndexerDependenciesJob(fIndexManager, fTypeCache, scope), monitor); + } + } + + if (!success || monitor.isCanceled()) { + throw new InterruptedException(); + } + + monitor.done(); + } + + private void update(ITypeSearchScope scope, IProgressMonitor monitor) throws InterruptedException { + boolean success = true; + IProject project = fTypeCache.getProject(); + + monitor.beginTask("", 100); //$NON-NLS-1$ + if (project.exists() && project.isOpen()) { + success = doIndexerJob(new IndexerTypesJob2(fIndexManager, fTypeCache, scope), monitor); + } + + if (!success || monitor.isCanceled()) { + throw new InterruptedException(); + } + + monitor.done(); + } + + private boolean doIndexerJob(IndexerJob job, IProgressMonitor monitor) { + if (!fEnableIndexing) { + return false; + } + + // check if indexer is busy + fIndexerIsBusy = false; + try { + fIndexManager.performConcurrentJob(new DummyIndexerJob(fIndexManager, fTypeCache.getProject()), + ICSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, new NullProgressMonitor(), null); + } catch (OperationCanceledException e) { + fIndexerIsBusy = true; + } + + // do an immediate (but possibly incomplete) search + // if fIndexerIsBusy the cache will stay dirty and we'll hit the indexer again next time + return fIndexManager.performConcurrentJob(job, + ICSearchConstants.FORCE_IMMEDIATE_SEARCH, monitor, null); + } + + private boolean doIndexerJob(IndexerJob2 job, IProgressMonitor monitor) { + if (!fEnableIndexing) { + return false; + } + + // check if indexer is busy + fIndexerIsBusy = false; + try { + fIndexManager.performConcurrentJob(new DummyIndexerJob(fIndexManager, fTypeCache.getProject()), + ICSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, new NullProgressMonitor(), null); + } catch (OperationCanceledException e) { + fIndexerIsBusy = true; + } + + // do an immediate (but possibly incomplete) search + // if fIndexerIsBusy the cache will stay dirty and we'll hit the indexer again next time + return fIndexManager.performConcurrentJob(job, + ICSearchConstants.FORCE_IMMEDIATE_SEARCH, monitor, null); + } + + + private static final int PATH_ENTRY_FLAGS = ICElementDelta.F_ADDED_PATHENTRY_SOURCE + | ICElementDelta.F_REMOVED_PATHENTRY_SOURCE + | ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE + | ICElementDelta.F_CHANGED_PATHENTRY_MACRO + | ICElementDelta.F_PATHENTRY_REORDER; + + private void prepareToFlush(TypeCacheDelta cacheDelta, ITypeSearchScope scope) { + ITypeSearchScope deltaScope = cacheDelta.getScope(); + if (deltaScope != null) + scope.add(deltaScope); + + ICElementDelta delta = cacheDelta.getCElementDelta(); + if (delta != null) { + ICElement elem = delta.getElement(); + boolean added = (delta.getKind() == ICElementDelta.ADDED); + boolean removed = (delta.getKind() == ICElementDelta.REMOVED); + boolean contentChanged = ((delta.getFlags() & ICElementDelta.F_CONTENT) != 0); + boolean pathEntryChanged = ((delta.getFlags() & PATH_ENTRY_FLAGS) != 0); + + switch (elem.getElementType()) { + case ICElement.C_MODEL: { + if (added || removed) { + scope.add(elem); + } + } + break; + + case ICElement.C_PROJECT: { + if (added || removed || pathEntryChanged) { + scope.add(elem); + } + } + break; + + case ICElement.C_CCONTAINER: { + if (added || removed || pathEntryChanged) { + scope.add(elem); + } + } + break; + + case ICElement.C_UNIT: { + if (added || removed || pathEntryChanged || contentChanged) { + scope.add(elem); + } + } + break; + + case ICElement.C_INCLUDE: + case ICElement.C_NAMESPACE: + case ICElement.C_TEMPLATE_CLASS: + case ICElement.C_CLASS: + case ICElement.C_STRUCT: + case ICElement.C_UNION: + case ICElement.C_ENUMERATION: + case ICElement.C_TYPEDEF: + { + //TODO handle working copies + if (added || removed) { + scope.add(elem); + } + } + break; + } + } + } + + private static final class DummyIndexerJob extends IndexerJob { + public DummyIndexerJob(IndexManager indexManager, IProject project) { + super(indexManager, project); + } + protected boolean processIndex(IIndex index, IProject project, IProgressMonitor progressMonitor) { + return false; + } + } + +} + diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeLocatorJob.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeLocatorJob.java new file mode 100644 index 00000000000..4bc931f10d3 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeLocatorJob.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2004 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.IWorkingCopyProvider; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; + +public class TypeLocatorJob extends BasicJob { + + public static final Object FAMILY = new Object(); + private ITypeInfo fLocateType; + private ITypeCache fTypeCache; + private IWorkingCopyProvider fWorkingCopyProvider; + + public TypeLocatorJob(ITypeInfo info, ITypeCache typeCache, IWorkingCopyProvider workingCopyProvider) { + super(TypeCacheMessages.getString("TypeLocatorJob.jobName"), FAMILY); //$NON-NLS-1$ + fLocateType = info; + fTypeCache = typeCache; + fWorkingCopyProvider= workingCopyProvider; + } + + public ITypeInfo getType() { + return fLocateType; + } + + protected IStatus runWithDelegatedProgress(IProgressMonitor monitor) throws InterruptedException { + boolean success = false; + long startTime = System.currentTimeMillis(); + trace("TypeLocatorJob: started"); //$NON-NLS-1$ + + try { + monitor.beginTask(TypeCacheMessages.getString("TypeLocatorJob.taskName"), 100); //$NON-NLS-1$ + + if (monitor.isCanceled()) + throw new InterruptedException(); + + TypeParser parser = new TypeParser(fTypeCache, fWorkingCopyProvider); + success = parser.findType(fLocateType, new SubProgressMonitor(monitor, 100)); + + if (monitor.isCanceled()) + throw new InterruptedException(); + + } finally { + long executionTime = System.currentTimeMillis() - startTime; + if (success) + trace("TypeLocatorJob: completed ("+ executionTime + " ms)"); //$NON-NLS-1$ //$NON-NLS-2$ + else + trace("TypeLocatorJob: aborted ("+ executionTime + " ms)"); //$NON-NLS-1$ //$NON-NLS-2$ + + monitor.done(); + } + + return Status.OK_STATUS; + } +} diff --git a/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeParser.java b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeParser.java new file mode 100644 index 00000000000..f40b8daca49 --- /dev/null +++ b/core/org.eclipse.cdt.core/browser/org/eclipse/cdt/internal/core/browser/cache/TypeParser.java @@ -0,0 +1,955 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial implementation + * QNX Software Systems - adapted for type search + *******************************************************************************/ +package org.eclipse.cdt.internal.core.browser.cache; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.ITypeReference; +import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.IWorkingCopyProvider; +import org.eclipse.cdt.core.browser.PathUtil; +import org.eclipse.cdt.core.browser.QualifiedTypeName; +import org.eclipse.cdt.core.browser.TypeInfo; +import org.eclipse.cdt.core.browser.TypeReference; +import org.eclipse.cdt.core.browser.TypeSearchScope; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.core.parser.CodeReader; +import org.eclipse.cdt.core.parser.DefaultProblemHandler; +import org.eclipse.cdt.core.parser.IParser; +import org.eclipse.cdt.core.parser.IProblem; +import org.eclipse.cdt.core.parser.IScanner; +import org.eclipse.cdt.core.parser.IScannerInfo; +import org.eclipse.cdt.core.parser.IScannerInfoProvider; +import org.eclipse.cdt.core.parser.ISourceElementCallbackDelegate; +import org.eclipse.cdt.core.parser.ISourceElementRequestor; +import org.eclipse.cdt.core.parser.ParseError; +import org.eclipse.cdt.core.parser.ParserFactory; +import org.eclipse.cdt.core.parser.ParserFactoryError; +import org.eclipse.cdt.core.parser.ParserLanguage; +import org.eclipse.cdt.core.parser.ParserMode; +import org.eclipse.cdt.core.parser.ParserTimeOut; +import org.eclipse.cdt.core.parser.ParserUtil; +import org.eclipse.cdt.core.parser.ScannerInfo; +import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; +import org.eclipse.cdt.core.parser.ast.ASTClassKind; +import org.eclipse.cdt.core.parser.ast.ASTNotImplementedException; +import org.eclipse.cdt.core.parser.ast.IASTASMDefinition; +import org.eclipse.cdt.core.parser.ast.IASTAbstractTypeSpecifierDeclaration; +import org.eclipse.cdt.core.parser.ast.IASTBaseSpecifier; +import org.eclipse.cdt.core.parser.ast.IASTClassReference; +import org.eclipse.cdt.core.parser.ast.IASTClassSpecifier; +import org.eclipse.cdt.core.parser.ast.IASTCodeScope; +import org.eclipse.cdt.core.parser.ast.IASTCompilationUnit; +import org.eclipse.cdt.core.parser.ast.IASTDeclaration; +import org.eclipse.cdt.core.parser.ast.IASTElaboratedTypeSpecifier; +import org.eclipse.cdt.core.parser.ast.IASTEnumerationReference; +import org.eclipse.cdt.core.parser.ast.IASTEnumerationSpecifier; +import org.eclipse.cdt.core.parser.ast.IASTEnumeratorReference; +import org.eclipse.cdt.core.parser.ast.IASTField; +import org.eclipse.cdt.core.parser.ast.IASTFieldReference; +import org.eclipse.cdt.core.parser.ast.IASTFunction; +import org.eclipse.cdt.core.parser.ast.IASTFunctionReference; +import org.eclipse.cdt.core.parser.ast.IASTInclusion; +import org.eclipse.cdt.core.parser.ast.IASTLinkageSpecification; +import org.eclipse.cdt.core.parser.ast.IASTMacro; +import org.eclipse.cdt.core.parser.ast.IASTMethod; +import org.eclipse.cdt.core.parser.ast.IASTMethodReference; +import org.eclipse.cdt.core.parser.ast.IASTNamespaceDefinition; +import org.eclipse.cdt.core.parser.ast.IASTNamespaceReference; +import org.eclipse.cdt.core.parser.ast.IASTOffsetableNamedElement; +import org.eclipse.cdt.core.parser.ast.IASTParameterReference; +import org.eclipse.cdt.core.parser.ast.IASTQualifiedNameElement; +import org.eclipse.cdt.core.parser.ast.IASTReference; +import org.eclipse.cdt.core.parser.ast.IASTScope; +import org.eclipse.cdt.core.parser.ast.IASTTemplateDeclaration; +import org.eclipse.cdt.core.parser.ast.IASTTemplateInstantiation; +import org.eclipse.cdt.core.parser.ast.IASTTemplateParameterReference; +import org.eclipse.cdt.core.parser.ast.IASTTemplateSpecialization; +import org.eclipse.cdt.core.parser.ast.IASTTypeSpecifier; +import org.eclipse.cdt.core.parser.ast.IASTTypedefDeclaration; +import org.eclipse.cdt.core.parser.ast.IASTTypedefReference; +import org.eclipse.cdt.core.parser.ast.IASTUsingDeclaration; +import org.eclipse.cdt.core.parser.ast.IASTUsingDirective; +import org.eclipse.cdt.core.parser.ast.IASTVariable; +import org.eclipse.cdt.core.parser.ast.IASTVariableReference; +import org.eclipse.cdt.internal.core.browser.util.SimpleStack; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubProgressMonitor; + +public class TypeParser implements ISourceElementRequestor { + + private ITypeCache fTypeCache; + private ITypeSearchScope fScope; + private IProject fProject; + private IWorkingCopyProvider fWorkingCopyProvider; + private IProgressMonitor fProgressMonitor; + ISourceElementCallbackDelegate fLastDeclaration; + private final SimpleStack fScopeStack = new SimpleStack(); + private final SimpleStack fResourceStack = new SimpleStack(); + private ITypeInfo fTypeToFind; + private ITypeInfo fSuperTypeToFind; + private Set fProcessedTypes = new HashSet(); + private boolean fFoundType; + IParser fParser = null; + ParserTimeOut fTimeoutThread = null; + + public TypeParser(ITypeCache typeCache, IWorkingCopyProvider provider) { + fTypeCache = typeCache; + fWorkingCopyProvider = provider; + + fTimeoutThread = new ParserTimeOut("TypeParser TimeOut Thread"); //$NON-NLS-1$ + fTimeoutThread.setThreadPriority(Thread.MAX_PRIORITY); + } + + public void parseTypes(TypeSearchScope scope, IProgressMonitor monitor) throws InterruptedException { + if (monitor == null) + monitor = new NullProgressMonitor(); + + if (monitor.isCanceled()) + throw new InterruptedException(); + + fScope = new TypeSearchScope(scope); + Map workingCopyMap = null; + if (fWorkingCopyProvider != null) { + IWorkingCopy[] workingCopies = fWorkingCopyProvider.getWorkingCopies(); + if (workingCopies != null && workingCopies.length > 0) { + workingCopyMap = new HashMap(workingCopies.length); + for (int i = 0; i < workingCopies.length; ++i) { + IWorkingCopy workingCopy = workingCopies[i]; + IPath wcPath = workingCopy.getOriginalElement().getPath(); + if (fScope.encloses(wcPath)) { +// // always flush working copies from cache? +// fTypeCache.flush(wcPath); + fScope.add(wcPath, false, null); + workingCopyMap.put(wcPath, workingCopy); + } + } + } + } + + fProject = fTypeCache.getProject(); + IPath[] searchPaths = fTypeCache.getPaths(fScope); + Collection workingCopyPaths = new HashSet(); + if (workingCopyMap != null) { + collectWorkingCopiesInProject(workingCopyMap, fProject, workingCopyPaths); + //TODO what about working copies outside the workspace? + } + + monitor.beginTask("", searchPaths.length + workingCopyPaths.size()); //$NON-NLS-1$ + try { + for (Iterator pathIter = workingCopyPaths.iterator(); pathIter.hasNext(); ) { + IPath path = (IPath) pathIter.next(); + parseSource(path, fProject, workingCopyMap, new SubProgressMonitor(monitor, 1)); + } + for (int i = 0; i < searchPaths.length; ++i) { + IPath path = searchPaths[i]; + if (!workingCopyPaths.contains(path)) { + parseSource(path, fProject, workingCopyMap, new SubProgressMonitor(monitor, 1)); + } else { + monitor.worked(1); + } + } + } finally { + monitor.done(); + } + } + + public boolean findType(ITypeInfo info, IProgressMonitor monitor) throws InterruptedException { + if (monitor == null) + monitor = new NullProgressMonitor(); + + if (monitor.isCanceled()) + throw new InterruptedException(); + + fScope = new TypeSearchScope(); + ITypeReference[] refs = info.getReferences(); + if (refs == null || refs.length == 0) + return false; // no source references + + fScope.add(refs[0].getPath(), false, null); +// for (int i = 0; i < refs.length; ++i) { +// ITypeReference location = refs[i]; +// IPath path = location.getPath(); +// fScope.add(path, false, null); +// } + + Map workingCopyMap = null; + if (fWorkingCopyProvider != null) { + IWorkingCopy[] workingCopies = fWorkingCopyProvider.getWorkingCopies(); + if (workingCopies != null && workingCopies.length > 0) { + workingCopyMap = new HashMap(workingCopies.length); + for (int i = 0; i < workingCopies.length; ++i) { + IWorkingCopy workingCopy = workingCopies[i]; + IPath wcPath = workingCopy.getOriginalElement().getPath(); + if (fScope.encloses(wcPath)) { +// // always flush working copies from cache? +// fTypeCache.flush(wcPath); + fScope.add(wcPath, false, null); + workingCopyMap.put(wcPath, workingCopy); + } + } + } + } + + fProject = fTypeCache.getProject(); + IPath[] searchPaths = fTypeCache.getPaths(fScope); + Collection workingCopyPaths = new HashSet(); + if (workingCopyMap != null) { + collectWorkingCopiesInProject(workingCopyMap, fProject, workingCopyPaths); + //TODO what about working copies outside the workspace? + } + + monitor.beginTask("", searchPaths.length + workingCopyPaths.size()); //$NON-NLS-1$ + try { + fTypeToFind = info; + fFoundType = false; + for (Iterator pathIter = workingCopyPaths.iterator(); pathIter.hasNext(); ) { + IPath path = (IPath) pathIter.next(); + parseSource(path, fProject, workingCopyMap, new SubProgressMonitor(monitor, 1)); + if (fFoundType) + return true; + } + for (int i = 0; i < searchPaths.length; ++i) { + IPath path = searchPaths[i]; + if (!workingCopyPaths.contains(path)) { + parseSource(path, fProject, workingCopyMap, new SubProgressMonitor(monitor, 1)); + } else { + monitor.worked(1); + } + + if (fFoundType) + return true; + } + } finally { + fTypeToFind = null; + fFoundType = false; + monitor.done(); + } + return false; + } + + public boolean findSubTypes(ITypeInfo info, IProgressMonitor monitor) throws InterruptedException { + if (monitor == null) + monitor = new NullProgressMonitor(); + + if (monitor.isCanceled()) + throw new InterruptedException(); + + fScope = new TypeSearchScope(); + ITypeReference[] refs = info.getDerivedReferences(); + if (refs == null || refs.length == 0) + return false; // no source references + + for (int i = 0; i < refs.length; ++i) { + ITypeReference location = refs[i]; + IPath path = location.getPath(); + fScope.add(path, false, null); + } + + Map workingCopyMap = null; + if (fWorkingCopyProvider != null) { + IWorkingCopy[] workingCopies = fWorkingCopyProvider.getWorkingCopies(); + if (workingCopies != null && workingCopies.length > 0) { + workingCopyMap = new HashMap(workingCopies.length); + for (int i = 0; i < workingCopies.length; ++i) { + IWorkingCopy workingCopy = workingCopies[i]; + IPath wcPath = workingCopy.getOriginalElement().getPath(); + if (fScope.encloses(wcPath)) { +// // always flush working copies from cache? +// fTypeCache.flush(wcPath); + fScope.add(wcPath, false, null); + workingCopyMap.put(wcPath, workingCopy); + } + } + } + } + + fProject = fTypeCache.getProject(); + IPath[] searchPaths = fTypeCache.getPaths(fScope); + Collection workingCopyPaths = new HashSet(); + if (workingCopyMap != null) { + collectWorkingCopiesInProject(workingCopyMap, fProject, workingCopyPaths); + //TODO what about working copies outside the workspace? + } + + monitor.beginTask("", searchPaths.length + workingCopyPaths.size()); //$NON-NLS-1$ + try { + fTypeToFind = null; + fSuperTypeToFind = info; + fFoundType = false; + for (Iterator pathIter = workingCopyPaths.iterator(); pathIter.hasNext(); ) { + IPath path = (IPath) pathIter.next(); + parseSource(path, fProject, workingCopyMap, new SubProgressMonitor(monitor, 1)); + } + for (int i = 0; i < searchPaths.length; ++i) { + IPath path = searchPaths[i]; + if (!workingCopyPaths.contains(path)) { + parseSource(path, fProject, workingCopyMap, new SubProgressMonitor(monitor, 1)); + } else { + monitor.worked(1); + } + } + } finally { + fTypeToFind = null; + fFoundType = false; + monitor.done(); + } + return false; + } + + private void collectWorkingCopiesInProject(Map workingCopyMap, IProject project, Collection workingCopyPaths) { + for (Iterator mapIter = workingCopyMap.entrySet().iterator(); mapIter.hasNext(); ) { + Map.Entry entry = (Map.Entry) mapIter.next(); + IPath path = (IPath) entry.getKey(); + IWorkingCopy copy = (IWorkingCopy) entry.getValue(); + + ICProject cProject = copy.getCProject(); + if (cProject != null && cProject.getProject().equals(project)) { + workingCopyPaths.add(path); + } + } + } + + private void parseSource(IPath path, IProject project, Map workingCopyMap, IProgressMonitor progressMonitor) throws InterruptedException { + if (progressMonitor.isCanceled()) + throw new InterruptedException(); + + // count how many types were indexed for this path + TypeSearchScope pathScope = new TypeSearchScope(); + pathScope.add(path, false, project); + int typeCount = fTypeCache.getTypes(pathScope).length; + + progressMonitor.beginTask("", typeCount); //$NON-NLS-1$ + try { + IWorkingCopy workingCopy = null; + if (workingCopyMap != null) { + workingCopy = (IWorkingCopy) workingCopyMap.get(path); + } + + ParserLanguage language = getLanguage(project, workingCopy); + if (language == null) { + return; // not C or C++ + } + + CodeReader reader = null; + Object stackObject = null; + + IResource resource = null; + if (workingCopy != null) { + reader = createWorkingCopyReader(workingCopy); + resource = workingCopy.getResource(); + if (resource != null) { + path = resource.getLocation(); + } + stackObject = workingCopy; + } else { + IWorkspace workspace = CCorePlugin.getWorkspace(); + if (workspace != null) { + IWorkspaceRoot wsRoot = workspace.getRoot(); + if (wsRoot != null) { + resource = wsRoot.findMember(path, true); + } + } + if (resource != null) { + reader = createResourceReader(resource); + path = resource.getLocation(); + stackObject = resource; + } else { + reader = createFileReader(path); + stackObject = path; + } + } + + if (reader != null) { + fResourceStack.clear(); + fScopeStack.clear(); + fResourceStack.push(stackObject); + parseContents(path, resource, project, reader, language, progressMonitor); + fResourceStack.pop(); + } + } finally { + progressMonitor.done(); + } + } + + private ParserLanguage getLanguage(IProject project, IWorkingCopy workingCopy) { + ParserLanguage projectLanguage = null; + if (project != null) { + if (CoreModel.hasCCNature(project)) { + projectLanguage = ParserLanguage.CPP; + } else if (CoreModel.hasCNature(project)) { + projectLanguage = ParserLanguage.C; + } + } + + if (workingCopy != null) { + ParserLanguage workingCopyLanguage = null; + ITranslationUnit unit = workingCopy.getTranslationUnit(); + if (unit != null) { + if (unit.isCLanguage()) { + workingCopyLanguage = ParserLanguage.C; + } else if (unit.isCXXLanguage()) { + workingCopyLanguage = ParserLanguage.CPP; + } + } + if (workingCopyLanguage != null) { + if (projectLanguage == null) { + return workingCopyLanguage; + } else if (projectLanguage.equals(ParserLanguage.CPP)) { + // if project is CPP then working copy must be CPP + return projectLanguage; + } else { + return workingCopyLanguage; + } + } + } + return projectLanguage; + } + + private CodeReader createWorkingCopyReader(IWorkingCopy workingCopy) { + CodeReader reader = null; + IResource resource = workingCopy.getResource(); + if (resource != null && resource.isAccessible()) { + char[] contents = workingCopy.getContents(); + if (contents != null) + reader = new CodeReader(resource.getLocation().toOSString(), contents); + } + return reader; + } + + private CodeReader createResourceReader(IResource resource) { + CodeReader reader = null; + if (resource.isAccessible() && resource instanceof IFile) { + IFile file = (IFile) resource; + InputStream contents = null; + try { + contents = file.getContents(); + if (contents != null) + reader = new CodeReader(resource.getLocation().toOSString(), file.getCharset(), contents); + } catch (CoreException ex) { + ex.printStackTrace(); + } catch (IOException e) { + } finally { + if (contents != null) { + try { + contents.close(); + } catch (IOException io) { + // ignore + } + } + } + } + return reader; + } + + private CodeReader createFileReader(IPath path) { + CodeReader reader = null; + try { + reader = new CodeReader(path.toOSString()); + } catch (IOException ex) { + ex.printStackTrace(); + } + return reader; + } + + private void parseContents(IPath realPath, IResource resource, IProject project, CodeReader reader, ParserLanguage language, IProgressMonitor progressMonitor) throws InterruptedException { + IScannerInfo scanInfo = null; + + if (project != null) { + //TODO temporary workaround to catch managed build exceptions + try { + IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project); + if (provider != null) { + IScannerInfo buildScanInfo = provider.getScannerInformation(resource != null ? resource : project); + if (buildScanInfo != null) + scanInfo = new ScannerInfo(buildScanInfo.getDefinedSymbols(), buildScanInfo.getIncludePaths()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + if (scanInfo == null) + scanInfo = new ScannerInfo(); + + try { + fProgressMonitor = progressMonitor; + IScanner scanner = ParserFactory.createScanner(reader, scanInfo, + ParserMode.STRUCTURAL_PARSE, language, this, ParserUtil.getScannerLogService(), null); + fParser = ParserFactory.createParser(scanner, this, ParserMode.STRUCTURAL_PARSE, language, ParserUtil.getParserLogService()); + + // start timer + int timeout = getParserTimeout(); + if (timeout > 0) { + fTimeoutThread.setTimeout(timeout); + fTimeoutThread.setParser(fParser); + while (!fTimeoutThread.isReadyToRun()){ + try { + Thread.sleep(20); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + fTimeoutThread.startTimer(); + } + + fParser.parse(); + } catch (ParserFactoryError e) { + CCorePlugin.log(e); + } catch (ParseError e) { + // no need to log + // CCorePlugin.log(e); + } catch (OperationCanceledException e) { + throw new InterruptedException(); + } catch (Exception e) { + CCorePlugin.log(e); + } finally { + // stop timer + fTimeoutThread.stopTimer(); + fTimeoutThread.setParser(null); + fProgressMonitor = null; + fParser = null; + } + } + + public boolean acceptProblem(IProblem problem) { + return DefaultProblemHandler.ruleOnProblem(problem, ParserMode.COMPLETE_PARSE); + } + public void acceptUsingDirective(IASTUsingDirective usageDirective) {} + public void acceptUsingDeclaration(IASTUsingDeclaration usageDeclaration) {} + public void acceptASMDefinition(IASTASMDefinition asmDefinition) {} + public void acceptAbstractTypeSpecDeclaration(IASTAbstractTypeSpecifierDeclaration abstractDeclaration) {} + public void enterTemplateDeclaration(IASTTemplateDeclaration declaration) {} + public void enterTemplateSpecialization(IASTTemplateSpecialization specialization) {} + public void enterTemplateInstantiation(IASTTemplateInstantiation instantiation) {} + public void exitTemplateDeclaration(IASTTemplateDeclaration declaration) {} + public void exitTemplateSpecialization(IASTTemplateSpecialization specialization) {} + public void exitTemplateExplicitInstantiation(IASTTemplateInstantiation instantiation) {} + public void acceptParameterReference(IASTParameterReference reference) {} + public void acceptTemplateParameterReference(IASTTemplateParameterReference reference) {} + public void acceptTypedefReference(IASTTypedefReference reference) {} + public void acceptEnumeratorReference(IASTEnumeratorReference reference) {} + public void acceptClassReference(IASTClassReference reference) {} + public void acceptNamespaceReference(IASTNamespaceReference reference) {} + public void acceptVariableReference(IASTVariableReference reference) {} + public void acceptFieldReference(IASTFieldReference reference) {} + public void acceptEnumerationReference(IASTEnumerationReference reference) {} + public void acceptFunctionReference(IASTFunctionReference reference) {} + public void acceptMethodReference(IASTMethodReference reference) {} + public void acceptField(IASTField field) {} + public void acceptMacro(IASTMacro macro) {} + public void acceptVariable(IASTVariable variable) {} + public void acceptFunctionDeclaration(IASTFunction function) {} + public void acceptMethodDeclaration(IASTMethod method) {} + public void enterCodeBlock(IASTCodeScope scope) {} + public void exitCodeBlock(IASTCodeScope scope) {} + public void acceptFriendDeclaration(IASTDeclaration declaration) {} + + public void enterLinkageSpecification(IASTLinkageSpecification linkageSpec) { + pushScope(linkageSpec); + } + + public void exitLinkageSpecification(IASTLinkageSpecification linkageSpec) { + popScope(); + } + + public void enterCompilationUnit(IASTCompilationUnit compilationUnit) { + pushScope(compilationUnit); + } + + public void exitCompilationUnit(IASTCompilationUnit compilationUnit) { + popScope(); + } + + public void enterFunctionBody(IASTFunction function) { + pushScope(function); + } + + public void exitFunctionBody(IASTFunction function) { + popScope(); + } + + public void enterMethodBody(IASTMethod method) { + pushScope(method); + } + + public void exitMethodBody(IASTMethod method) { + popScope(); + } + + public void enterNamespaceDefinition(IASTNamespaceDefinition namespaceDefinition) { + fLastDeclaration = namespaceDefinition; + acceptType(namespaceDefinition); + pushScope(namespaceDefinition); + } + + public void exitNamespaceDefinition(IASTNamespaceDefinition namespaceDefinition) { + popScope(); + } + + public void enterClassSpecifier(IASTClassSpecifier classSpecification) { + fLastDeclaration = classSpecification; + acceptType(classSpecification); + pushScope(classSpecification); + } + + public void exitClassSpecifier(IASTClassSpecifier classSpecification) { + popScope(); + } + + private void pushScope(IASTScope scope) { + if (fProgressMonitor.isCanceled()) + throw new OperationCanceledException(); + fScopeStack.push(scope); + } + + private IASTScope popScope() { + if (fProgressMonitor.isCanceled()) + throw new OperationCanceledException(); + return (IASTScope) fScopeStack.pop(); + } + + public void acceptTypedefDeclaration(IASTTypedefDeclaration typedef) { + fLastDeclaration = typedef; + acceptType(typedef); + } + + public void acceptEnumerationSpecifier(IASTEnumerationSpecifier enumeration) { + fLastDeclaration = enumeration; + acceptType(enumeration); + } + + public void acceptElaboratedForewardDeclaration(IASTElaboratedTypeSpecifier elaboratedType) { + // acceptType(elaboratedType); + } + + public void enterInclusion(IASTInclusion inclusion) { + if (fProgressMonitor.isCanceled()) + throw new OperationCanceledException(); + + String includePath = inclusion.getFullFileName(); + IPath path = new Path(includePath); + path = PathUtil.getWorkspaceRelativePath(path); + + IResource resource = null; + IWorkspace workspace = CCorePlugin.getWorkspace(); + if (workspace != null) { + IWorkspaceRoot wsRoot = workspace.getRoot(); + if (wsRoot != null) { + resource = wsRoot.findMember(path, true); + } + } + //TODO do inclusions get parsed as working copies? + + Object stackObject = path; + if (resource != null) + stackObject = resource; + + fResourceStack.push(stackObject); + } + + public void exitInclusion(IASTInclusion inclusion) { + if (fProgressMonitor.isCanceled()) + throw new OperationCanceledException(); + fResourceStack.pop(); + } + + private class NodeTypeInfo { + int type; + String name; + String[] enclosingNames; + int offset; + int end; + IASTOffsetableNamedElement offsetable; + + void init() { + type = 0; + name = null; + enclosingNames = null; + offset = 0; + end = 0; + offsetable = null; + } + + boolean parseNodeForTypeInfo(ISourceElementCallbackDelegate node) { + init(); + + if (node instanceof IASTReference) { + IASTReference reference = (IASTReference) node; + offset = reference.getOffset(); + end = offset + reference.getName().length(); + } else if (node instanceof IASTOffsetableNamedElement) { + offsetable = (IASTOffsetableNamedElement) node; + offset = offsetable.getNameOffset() != 0 ? offsetable.getNameOffset() : offsetable.getStartingOffset(); + end = offsetable.getNameEndOffset(); + if (end == 0) { + end = offset + offsetable.getName().length(); + } + } + + if (node instanceof IASTReference) + node = fLastDeclaration; + if (node instanceof IASTReference) { + offsetable = (IASTOffsetableNamedElement) ((IASTReference) node).getReferencedElement(); + name = ((IASTReference) node).getName(); + } else if (node instanceof IASTOffsetableNamedElement) { + offsetable = (IASTOffsetableNamedElement) node; + name = offsetable.getName(); + } else { + return false; + } + + // skip unnamed structs + if (name == null || name.length() == 0) + return false; + + // skip unused types + type = getElementType(offsetable); + if (type == 0) { + return false; + } + + // collect enclosing names + if (offsetable instanceof IASTQualifiedNameElement) { + String[] names = ((IASTQualifiedNameElement) offsetable).getFullyQualifiedName(); + if (names != null && names.length > 1) { + enclosingNames = new String[names.length - 1]; + System.arraycopy(names, 0, enclosingNames, 0, names.length - 1); + } + } + + return true; + } + + } + + private void acceptType(ISourceElementCallbackDelegate node) { + if (fProgressMonitor.isCanceled()) + throw new OperationCanceledException(); + + // skip local declarations + IASTScope currentScope = (IASTScope) fScopeStack.top(); + if (currentScope instanceof IASTFunction || currentScope instanceof IASTMethod) { + return; + } + + NodeTypeInfo nodeInfo = new NodeTypeInfo(); + if (nodeInfo.parseNodeForTypeInfo(node)) { + TypeReference originalLocation = null; + Object originalRef = fResourceStack.bottom(); + if (originalRef instanceof IWorkingCopy) { + IWorkingCopy workingCopy = (IWorkingCopy) originalRef; + originalLocation = new TypeReference(workingCopy, fProject); + } else if (originalRef instanceof IResource) { + IResource resource = (IResource) originalRef; + originalLocation = new TypeReference(resource, fProject); + } else if (originalRef instanceof IPath) { + IPath path = PathUtil.getProjectRelativePath((IPath)originalRef, fProject); + originalLocation = new TypeReference(path, fProject); + } + + TypeReference resolvedLocation = null; + Object resolvedRef = fResourceStack.top(); + if (resolvedRef instanceof IWorkingCopy) { + IWorkingCopy workingCopy = (IWorkingCopy) resolvedRef; + resolvedLocation = new TypeReference(workingCopy, fProject, nodeInfo.offset, nodeInfo.end - nodeInfo.offset); + } else if (resolvedRef instanceof IResource) { + IResource resource = (IResource) resolvedRef; + resolvedLocation = new TypeReference(resource, fProject, nodeInfo.offset, nodeInfo.end - nodeInfo.offset); + } else if (resolvedRef instanceof IPath) { + IPath path = PathUtil.getProjectRelativePath((IPath)resolvedRef, fProject); + resolvedLocation = new TypeReference(path, fProject, nodeInfo.offset, nodeInfo.end - nodeInfo.offset); + } + + if (fTypeToFind != null) { + if ((fTypeToFind.getCElementType() == nodeInfo.type || fTypeToFind.isUndefinedType()) && nodeInfo.name.equals(fTypeToFind.getName())) { + QualifiedTypeName qualifiedName = new QualifiedTypeName(nodeInfo.name, nodeInfo.enclosingNames); + if (qualifiedName.equals(fTypeToFind.getQualifiedTypeName())) { + // add types to cache + ITypeInfo newType = addType(nodeInfo.type, qualifiedName, originalLocation, resolvedLocation); + if (newType != null && node instanceof IASTClassSpecifier) { + addSuperClasses(newType, (IASTClassSpecifier)node, originalLocation, fProcessedTypes); + } + fProgressMonitor.worked(1); + + fFoundType = true; + // terminate the parser + fParser.cancel(); + } + } + } else { + // add types to cache + QualifiedTypeName qualifiedName = new QualifiedTypeName(nodeInfo.name, nodeInfo.enclosingNames); + ITypeInfo newType = addType(nodeInfo.type, qualifiedName, originalLocation, resolvedLocation); + if (newType != null && node instanceof IASTClassSpecifier) { + addSuperClasses(newType, (IASTClassSpecifier)node, originalLocation, fProcessedTypes); + } + fProgressMonitor.worked(1); + } + } + } + + int getElementType(IASTOffsetableNamedElement offsetable) { + if (offsetable instanceof IASTClassSpecifier || offsetable instanceof IASTElaboratedTypeSpecifier) { + ASTClassKind kind = null; + if (offsetable instanceof IASTClassSpecifier) { + kind = ((IASTClassSpecifier) offsetable).getClassKind(); + } else { + kind = ((IASTElaboratedTypeSpecifier) offsetable).getClassKind(); + } + if (kind == ASTClassKind.CLASS) { + return ICElement.C_CLASS; + } else if (kind == ASTClassKind.STRUCT) { + return ICElement.C_STRUCT; + } else if (kind == ASTClassKind.UNION) { + return ICElement.C_UNION; + } + } else if (offsetable instanceof IASTNamespaceDefinition) { + return ICElement.C_NAMESPACE; + } else if (offsetable instanceof IASTEnumerationSpecifier) { + return ICElement.C_ENUMERATION; + } else if (offsetable instanceof IASTTypedefDeclaration) { + return ICElement.C_TYPEDEF; + } + return 0; + } + + private void addSuperClasses(ITypeInfo type, IASTClassSpecifier classSpec, TypeReference location, Set processedClasses) { + Iterator baseIter = classSpec.getBaseClauses(); + if (baseIter != null) { + while (baseIter.hasNext()) { + IASTBaseSpecifier baseSpec = (IASTBaseSpecifier) baseIter.next(); + try { + ASTAccessVisibility baseAccess = baseSpec.getAccess(); + + IASTClassSpecifier parentClass = null; + IASTTypeSpecifier parentSpec = baseSpec.getParentClassSpecifier(); + if (parentSpec instanceof IASTClassSpecifier) + parentClass = (IASTClassSpecifier) parentSpec; + + if (parentClass != null) { + NodeTypeInfo nodeInfo = new NodeTypeInfo(); + if (nodeInfo.parseNodeForTypeInfo(parentClass)) { + // add type to cache + ITypeInfo superType = addSuperType(type, nodeInfo.type, nodeInfo.name, nodeInfo.enclosingNames, location, baseAccess, baseSpec.isVirtual()); + + // recursively process super super classes + if (!processedClasses.contains(parentClass)) { + processedClasses.add(parentClass); + addSuperClasses(superType, parentClass, location, processedClasses); + } + } + } + } catch (ASTNotImplementedException e) { + } + } + } + } + + private ITypeInfo addType(int type, QualifiedTypeName qualifiedName, TypeReference originalLocation, TypeReference resolvedLocation) { + ITypeInfo info = fTypeCache.getType(type, qualifiedName); + if (info == null || info.isUndefinedType()) { + // add new type to cache + if (info != null) { + info.setCElementType(type); + } else { + info = new TypeInfo(type, qualifiedName); + fTypeCache.insert(info); + } + + info.addReference(originalLocation); + } + + info.addReference(resolvedLocation); + + return info; + } + + private ITypeInfo addSuperType(ITypeInfo addToType, int type, String name, String[] enclosingNames, TypeReference location, ASTAccessVisibility access, boolean isVirtual) { + QualifiedTypeName qualifiedName = new QualifiedTypeName(name, enclosingNames); + ITypeInfo superType = fTypeCache.getType(type, qualifiedName); + if (superType == null || superType.isUndefinedType()) { + if (superType != null) { + // merge with existing type + superType.setCElementType(type); + } else { + // add new type to cache + superType = new TypeInfo(type, qualifiedName); + fTypeCache.insert(superType); + } + } + superType.addDerivedReference(location); + + if (fSuperTypeToFind != null && fSuperTypeToFind.equals(superType)) { + //TODO don't need to do anything here? + } + fTypeCache.addSupertype(addToType, superType, access, isVirtual); + fTypeCache.addSubtype(superType, addToType); + return superType; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.parser.ISourceElementRequestor#createReader(java.lang.String) + */ + public CodeReader createReader(String finalPath, Iterator workingCopies) { + return ParserUtil.createReader(finalPath, workingCopies); + } + + private static final int DEFAULT_PARSER_TIMEOUT = 30; + + public int getParserTimeout() { + //TODO we shouldn't be trying to access the indexer prefs + //TODO clean this up + int timeout = 0; + try { + // here we just reuse the indexer timeout + String str = CCorePlugin.getDefault().getPluginPreferences().getString("CDT_INDEXER_TIMEOUT"); //$NON-NLS-1$ + if (str != null && str.length() > 0) { + int val = Integer.valueOf(str).intValue(); + if (val > 0) { + timeout = val; + } + } + } catch (NumberFormatException e) { + // do nothing + } + + if (timeout == 0) { + timeout = DEFAULT_PARSER_TIMEOUT; + } + return timeout; + } + +} |