| /******************************************************************************* |
| * Copyright (c) 2000, 2008 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.dltk.internal.core; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.dltk.core.IBuildpathEntry; |
| import org.eclipse.dltk.core.IModelElementDelta; |
| import org.eclipse.dltk.core.IProjectFragment; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.search.indexing.IndexManager; |
| import org.eclipse.dltk.internal.core.ModelManager.PerProjectInfo; |
| import org.eclipse.dltk.internal.core.search.ProjectIndexerManager; |
| |
| public class BuildpathChange { |
| public static int NO_DELTA = 0x00; |
| public static int HAS_DELTA = 0x01; |
| public static int HAS_PROJECT_CHANGE = 0x02; |
| public static int HAS_LIBRARY_CHANGE = 0x04; |
| |
| ScriptProject project; |
| IBuildpathEntry[] oldRawBuildpath; |
| IBuildpathEntry[] oldResolvedBuildpath; |
| |
| public BuildpathChange(ScriptProject project, |
| IBuildpathEntry[] oldRawBuildpath, |
| IBuildpathEntry[] oldResolvedBuildpath) { |
| this.project = project; |
| this.oldRawBuildpath = oldRawBuildpath; |
| this.oldResolvedBuildpath = oldResolvedBuildpath; |
| } |
| |
| private void addBuildpathDeltas(ModelElementDelta delta, |
| IProjectFragment[] roots, int flag) { |
| for (int i = 0; i < roots.length; i++) { |
| IProjectFragment root = roots[i]; |
| delta.changed(root, flag); |
| if ((flag & IModelElementDelta.F_REMOVED_FROM_BUILDPATH) != 0 |
| /* |
| * || (flag & IModelElementDelta.F_SOURCEATTACHED) != 0 || (flag & |
| * IModelElementDelta.F_SOURCEDETACHED) != 0 |
| */) { |
| try { |
| root.close(); |
| } catch (ModelException e) { |
| // ignore |
| } |
| } |
| } |
| } |
| |
| /* |
| * Returns the index of the item in the list if the given list contains the |
| * specified entry. If the list does not contain the entry, -1 is returned. |
| */ |
| private int buildpathContains(IBuildpathEntry[] list, IBuildpathEntry entry) { |
| IPath[] exclusionPatterns = entry.getExclusionPatterns(); |
| IPath[] inclusionPatterns = entry.getInclusionPatterns(); |
| int listLen = list == null ? 0 : list.length; |
| nextEntry: for (int i = 0; i < listLen; i++) { |
| IBuildpathEntry other = list[i]; |
| if (other.getContentKind() == entry.getContentKind() |
| && other.getEntryKind() == entry.getEntryKind() |
| && other.isExported() == entry.isExported() |
| && other.getPath().equals(entry.getPath())) { |
| |
| // check inclusion patterns |
| IPath[] otherIncludes = other.getInclusionPatterns(); |
| if (inclusionPatterns != otherIncludes) { |
| if (inclusionPatterns == null) |
| continue; |
| int includeLength = inclusionPatterns.length; |
| if (otherIncludes == null |
| || otherIncludes.length != includeLength) |
| continue; |
| for (int j = 0; j < includeLength; j++) { |
| // compare toStrings instead of IPaths |
| // since IPath.equals is specified to ignore trailing |
| // separators |
| if (!inclusionPatterns[j].toString().equals( |
| otherIncludes[j].toString())) |
| continue nextEntry; |
| } |
| } |
| // check exclusion patterns |
| IPath[] otherExcludes = other.getExclusionPatterns(); |
| if (exclusionPatterns != otherExcludes) { |
| if (exclusionPatterns == null) |
| continue; |
| int excludeLength = exclusionPatterns.length; |
| if (otherExcludes == null |
| || otherExcludes.length != excludeLength) |
| continue; |
| for (int j = 0; j < excludeLength; j++) { |
| // compare toStrings instead of IPaths |
| // since IPath.equals is specified to ignore trailing |
| // separators |
| if (!exclusionPatterns[j].toString().equals( |
| otherExcludes[j].toString())) |
| continue nextEntry; |
| } |
| } |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (!(obj instanceof BuildpathChange)) |
| return false; |
| return this.project.equals(((BuildpathChange) obj).project); |
| } |
| |
| /* |
| * Generates a buildpath change delta for this buildpath change. Returns |
| * whether a delta was generated, and whether project reference have |
| * changed. |
| */ |
| public int generateDelta(ModelElementDelta delta, boolean addBuildpathChange) { |
| ModelManager manager = ModelManager.getModelManager(); |
| DeltaProcessingState state = manager.deltaState; |
| if (state.findProject(this.project.getElementName()) == null) |
| // project doesn't exist yet (we're in an IWorkspaceRunnable) |
| // no need to create a delta here and no need to index (see |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=133334) |
| // the delta processor will create an ADDED project delta, and index |
| // the project |
| return NO_DELTA; |
| |
| DeltaProcessor deltaProcessor = state.getDeltaProcessor(); |
| IBuildpathEntry[] newResolvedBuildpath = null; |
| int result = NO_DELTA; |
| try { |
| PerProjectInfo perProjectInfo = this.project.getPerProjectInfo(); |
| |
| // get new info |
| this.project.resolveBuildpath(perProjectInfo, false/* |
| * don't use |
| * previous |
| * session |
| * values |
| */, |
| addBuildpathChange); |
| IBuildpathEntry[] newRawBuildpath; |
| |
| // use synchronized block to ensure consistency |
| synchronized (perProjectInfo) { |
| newRawBuildpath = perProjectInfo.rawBuildpath; |
| newResolvedBuildpath = perProjectInfo.getResolvedBuildpath(); |
| } |
| |
| if (newResolvedBuildpath == null) { |
| // another thread reset the resolved buildpath, use a temporary |
| // PerProjectInfo |
| PerProjectInfo temporaryInfo = this.project.newTemporaryInfo(); |
| this.project.resolveBuildpath(temporaryInfo, false/* |
| * don't use |
| * previous |
| * session |
| * values |
| */, |
| addBuildpathChange); |
| newRawBuildpath = temporaryInfo.rawBuildpath; |
| newResolvedBuildpath = temporaryInfo.getResolvedBuildpath(); |
| } |
| |
| // check if raw buildpath has changed |
| if (this.oldRawBuildpath != null |
| && !ScriptProject.areBuildpathsEqual(this.oldRawBuildpath, |
| newRawBuildpath)) { |
| delta.changed(this.project, |
| IModelElementDelta.F_BUILDPATH_CHANGED); |
| result |= HAS_DELTA; |
| |
| // reset containers that are no longer on the buildpath |
| // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=139446) |
| for (int i = 0, length = this.oldRawBuildpath.length; i < length; i++) { |
| IBuildpathEntry entry = this.oldRawBuildpath[i]; |
| if (entry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER) { |
| if (buildpathContains(newRawBuildpath, entry) == -1) |
| manager.containerPut(this.project, entry.getPath(), |
| null); |
| } |
| } |
| } |
| |
| // if no changes to resolved buildpath, nothing more to do |
| if (this.oldResolvedBuildpath != null |
| && ScriptProject.areBuildpathsEqual( |
| this.oldResolvedBuildpath, newResolvedBuildpath)) |
| return result; |
| |
| // close cached info |
| this.project.close(); |
| |
| // TODO: check is this is relevant here |
| // ensure caches of dependent projects are reset as well (see |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=207890) |
| // deltaProcessor.projectCachesToReset.add(this.project); |
| } catch (ModelException e) { |
| if (DeltaProcessor.VERBOSE) { |
| e.printStackTrace(); |
| } |
| // project no longer exist |
| return result; |
| } |
| |
| if (this.oldResolvedBuildpath == null) |
| return result; |
| |
| delta.changed(this.project, |
| IModelElementDelta.F_RESOLVED_BUILDPATH_CHANGED); |
| result |= HAS_DELTA; |
| |
| state.addForRefresh(this.project); |
| // ensure external jars are refreshed for this project (see |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=212769 ) |
| |
| Map<IPath, IProjectFragment> removedRoots = null; |
| IProjectFragment[] roots = null; |
| Map<IScriptProject, IProjectFragment[]> allOldRoots; |
| if ((allOldRoots = deltaProcessor.oldRoots) != null) { |
| roots = allOldRoots.get(this.project); |
| } |
| if (roots != null) { |
| removedRoots = new HashMap<IPath, IProjectFragment>(); |
| for (int i = 0; i < roots.length; i++) { |
| IProjectFragment root = roots[i]; |
| removedRoots.put(root.getPath(), root); |
| } |
| } |
| |
| int newLength = newResolvedBuildpath.length; |
| int oldLength = this.oldResolvedBuildpath.length; |
| for (int i = 0; i < oldLength; i++) { |
| int index = buildpathContains(newResolvedBuildpath, |
| this.oldResolvedBuildpath[i]); |
| if (index == -1) { |
| // remote project changes |
| int entryKind = this.oldResolvedBuildpath[i].getEntryKind(); |
| if (entryKind == IBuildpathEntry.BPE_PROJECT) { |
| result |= HAS_PROJECT_CHANGE; |
| continue; |
| } |
| if (entryKind == IBuildpathEntry.BPE_LIBRARY) { |
| result |= HAS_LIBRARY_CHANGE; |
| } |
| |
| IProjectFragment[] pkgFragmentRoots = null; |
| if (removedRoots != null) { |
| IProjectFragment oldRoot = removedRoots |
| .get(this.oldResolvedBuildpath[i].getPath()); |
| if (oldRoot != null) { // use old root if any (could be |
| // none |
| // if entry wasn't bound) |
| pkgFragmentRoots = new IProjectFragment[] { oldRoot }; |
| } |
| } |
| if (pkgFragmentRoots == null) { |
| try { |
| List<IProjectFragment> accumulatedRoots = new ArrayList<IProjectFragment>(); |
| HashSet<String> rootIDs = new HashSet<String>(5); |
| rootIDs.add(this.project.rootID()); |
| this.project.computeProjectFragments( |
| this.oldResolvedBuildpath[i], accumulatedRoots, |
| rootIDs, null, // inside original project |
| false, // don't check existence |
| false, // don't retrieve exported roots |
| null); /* no reverse map */ |
| pkgFragmentRoots = accumulatedRoots |
| .toArray(new IProjectFragment[accumulatedRoots |
| .size()]); |
| } catch (ModelException e) { |
| pkgFragmentRoots = new IProjectFragment[] {}; |
| } |
| } |
| addBuildpathDeltas(delta, pkgFragmentRoots, |
| IModelElementDelta.F_REMOVED_FROM_BUILDPATH); |
| } else { |
| // remote project changes |
| if (this.oldResolvedBuildpath[i].getEntryKind() == IBuildpathEntry.BPE_PROJECT) { |
| result |= HAS_PROJECT_CHANGE; |
| continue; |
| } |
| if (index != i) { // reordering of the buildpath |
| addBuildpathDeltas( |
| delta, |
| this.project |
| .computeProjectFragments(this.oldResolvedBuildpath[i]), |
| IModelElementDelta.F_REORDER); |
| } |
| // TODO (alex) check source attachment |
| } |
| } |
| |
| for (int i = 0; i < newLength; i++) { |
| int index = buildpathContains(this.oldResolvedBuildpath, |
| newResolvedBuildpath[i]); |
| if (index == -1) { |
| // remote project changes |
| int entryKind = newResolvedBuildpath[i].getEntryKind(); |
| if (entryKind == IBuildpathEntry.BPE_PROJECT) { |
| result |= HAS_PROJECT_CHANGE; |
| continue; |
| } |
| if (entryKind == IBuildpathEntry.BPE_LIBRARY) { |
| result |= HAS_LIBRARY_CHANGE; |
| } |
| addBuildpathDeltas( |
| delta, |
| this.project |
| .computeProjectFragments(newResolvedBuildpath[i]), |
| IModelElementDelta.F_ADDED_TO_BUILDPATH); |
| } // buildpath reordering has already been generated in previous |
| // loop |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public int hashCode() { |
| return this.project.hashCode(); |
| } |
| |
| /* |
| * Request the indexing of entries that have been added, and remove the |
| * index for removed entries. |
| */ |
| public void requestIndexing() { |
| IBuildpathEntry[] newResolvedBuildpath = null; |
| try { |
| newResolvedBuildpath = this.project.getResolvedBuildpath(); |
| } catch (ModelException e) { |
| // project doesn't exist |
| return; |
| } |
| |
| ModelManager manager = ModelManager.getModelManager(); |
| IndexManager indexManager = manager.indexManager; |
| if (indexManager == null) |
| return; |
| DeltaProcessingState state = manager.deltaState; |
| |
| int newLength = newResolvedBuildpath.length; |
| int oldLength = this.oldResolvedBuildpath == null ? 0 |
| : this.oldResolvedBuildpath.length; |
| for (int i = 0; i < oldLength; i++) { |
| int index = buildpathContains(newResolvedBuildpath, |
| this.oldResolvedBuildpath[i]); |
| if (index == -1) { |
| // remote projects are not indexed in this project |
| if (this.oldResolvedBuildpath[i].getEntryKind() == IBuildpathEntry.BPE_PROJECT) { |
| continue; |
| } |
| |
| // Remove the source files from the index for a source folder |
| // For a lib folder or a .zip file, remove the corresponding |
| // index if not shared. |
| IBuildpathEntry oldEntry = this.oldResolvedBuildpath[i]; |
| final IPath path = oldEntry.getPath(); |
| int changeKind = this.oldResolvedBuildpath[i].getEntryKind(); |
| switch (changeKind) { |
| case IBuildpathEntry.BPE_SOURCE: |
| char[][] inclusionPatterns = ((BuildpathEntry) oldEntry) |
| .fullInclusionPatternChars(); |
| char[][] exclusionPatterns = ((BuildpathEntry) oldEntry) |
| .fullExclusionPatternChars(); |
| indexManager.removeSourceFolderFromIndex(this.project, |
| path, inclusionPatterns, exclusionPatterns); |
| ProjectIndexerManager.removeProjectFragment(project, path); |
| break; |
| case IBuildpathEntry.BPE_LIBRARY: |
| if (state.otherRoots.get(path) == null) { // if root was |
| // not |
| // shared |
| indexManager.discardJobs(path.toString()); |
| indexManager.removeIndex(path); |
| ProjectIndexerManager.removeLibrary(project, path); |
| } |
| break; |
| } |
| } |
| } |
| |
| for (int i = 0; i < newLength; i++) { |
| int index = buildpathContains(this.oldResolvedBuildpath, |
| newResolvedBuildpath[i]); |
| if (index == -1 |
| || newResolvedBuildpath[i].getEntryKind() == IBuildpathEntry.BPE_LIBRARY) { |
| // remote projects are not indexed in this project |
| if (newResolvedBuildpath[i].getEntryKind() == IBuildpathEntry.BPE_PROJECT) { |
| continue; |
| } |
| |
| // Request indexing |
| int entryKind = newResolvedBuildpath[i].getEntryKind(); |
| switch (entryKind) { |
| case IBuildpathEntry.BPE_LIBRARY: |
| boolean pathHasChanged = true; |
| IPath newPath = newResolvedBuildpath[i].getPath(); |
| for (int j = 0; j < oldLength; j++) { |
| IBuildpathEntry oldEntry = this.oldResolvedBuildpath[j]; |
| if (oldEntry.getPath().equals(newPath)) { |
| pathHasChanged = false; |
| break; |
| } |
| } |
| if (pathHasChanged) { |
| // IBuildpathEntry entry = newResolvedBuildpath[i]; |
| // char[][] inclusionPatterns = ((BuildpathEntry) entry) |
| // .fullInclusionPatternChars(); |
| // char[][] exclusionPatterns = ((BuildpathEntry) entry) |
| // .fullExclusionPatternChars(); |
| ProjectIndexerManager.indexLibrary(project, newPath); |
| } |
| break; |
| case IBuildpathEntry.BPE_SOURCE: |
| IBuildpathEntry entry = newResolvedBuildpath[i]; |
| IPath path = entry.getPath(); |
| // char[][] inclusionPatterns = ((BuildpathEntry) entry) |
| // .fullInclusionPatternChars(); |
| // char[][] exclusionPatterns = ((BuildpathEntry) entry) |
| // .fullExclusionPatternChars(); |
| ProjectIndexerManager.indexProjectFragment(project, path); |
| break; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "BuildpathChange: " + this.project.getElementName(); //$NON-NLS-1$ |
| } |
| } |