blob: d468a35201d2e3a3710c0c542941aa88c438891c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 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
*
*******************************************************************************/
package org.eclipse.dltk.internal.core;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.ISaveContext;
import org.eclipse.core.resources.ISaveParticipant;
import org.eclipse.core.resources.ISavedState;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.AssertionFailedException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.dltk.compiler.problem.IProblem;
import org.eclipse.dltk.compiler.util.HashtableOfObjectToInt;
import org.eclipse.dltk.core.BuildpathContainerInitializer;
import org.eclipse.dltk.core.DLTKContentTypeManager;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IAccessRule;
import org.eclipse.dltk.core.IArchive;
import org.eclipse.dltk.core.IBuildpathAttribute;
import org.eclipse.dltk.core.IBuildpathContainer;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IModelStatus;
import org.eclipse.dltk.core.IModelStatusConstants;
import org.eclipse.dltk.core.IParent;
import org.eclipse.dltk.core.IProblemRequestor;
import org.eclipse.dltk.core.IProjectFragment;
import org.eclipse.dltk.core.IProjectFragmentTimestamp;
import org.eclipse.dltk.core.IScriptFolder;
import org.eclipse.dltk.core.IScriptModel;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ISourceModuleInfoCache;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.WorkingCopyOwner;
import org.eclipse.dltk.core.caching.IContentCache;
import org.eclipse.dltk.core.environment.EnvironmentPathUtils;
import org.eclipse.dltk.core.environment.IFileHandle;
import org.eclipse.dltk.core.search.indexing.IndexManager;
import org.eclipse.dltk.internal.core.builder.ScriptBuilder;
import org.eclipse.dltk.internal.core.caching.DLTKCoreCache;
import org.eclipse.dltk.internal.core.search.DLTKWorkspaceScope;
import org.eclipse.dltk.internal.core.search.ProjectIndexerManager;
import org.eclipse.dltk.internal.core.util.Messages;
import org.eclipse.dltk.internal.core.util.Util;
import org.eclipse.dltk.internal.core.util.WeakHashSet;
import org.eclipse.osgi.util.NLS;
import org.osgi.service.prefs.BackingStoreException;
public class ModelManager implements ISaveParticipant {
private final static int CONTAINERS_FILE_VERSION = 1;
public final static String BP_VARIABLE_PREFERENCES_PREFIX = DLTKCore.PLUGIN_ID
+ ".buildpathVariable."; //$NON-NLS-1$
public final static String BP_CONTAINER_PREFERENCES_PREFIX = DLTKCore.PLUGIN_ID
+ ".buildpathContainer."; //$NON-NLS-1$
public final static String BP_USERLIBRARY_PREFERENCES_PREFIX = DLTKCore.PLUGIN_ID
+ ".userLibrary."; //$NON-NLS-1$
public final static String BP_ENTRY_IGNORE = "##<cp entry ignore>##"; //$NON-NLS-1$
public final static IPath BP_ENTRY_IGNORE_PATH = new Path(BP_ENTRY_IGNORE);
public final static String TRUE = "true"; //$NON-NLS-1$
/**
* Unique handle onto the Model
*/
final Model model = new Model();
/**
* Buildpath variables pool
*/
public HashMap variables = new HashMap(5);
public HashSet variablesWithInitializer = new HashSet(5);
public HashSet readOnlyVariables = new HashSet(5);
public HashMap previousSessionVariables = new HashMap(5);
private ThreadLocal variableInitializationInProgress = new ThreadLocal();
/**
* Buildpath containers pool
*/
public HashMap containers = new HashMap(5);
public HashMap previousSessionContainers = new HashMap(5);
private ThreadLocal containerInitializationInProgress = new ThreadLocal();
public boolean batchContainerInitializations = false;
public final Hashtable<String, BuildpathContainerInitializer> containerInitializersCache = new Hashtable<>(
5);
/**
* Special value used for recognizing ongoing initialization and breaking
* initialization cycles
*/
public final static IPath VARIABLE_INITIALIZATION_IN_PROGRESS = new Path(
"Variable Initialization In Progress"); //$NON-NLS-1$
public final static IBuildpathContainer CONTAINER_INITIALIZATION_IN_PROGRESS = new IBuildpathContainer() {
@Override
public IBuildpathEntry[] getBuildpathEntries() {
return null;
}
@Override
public String getDescription() {
return "Container Initialization In Progress"; //$NON-NLS-1$
}
@Override
public int getKind() {
return 0;
}
@Override
public IPath getPath() {
return null;
}
@Override
public String toString() {
return getDescription();
}
};
/*
* A HashSet that contains the IScriptProject whose buildpath is being
* resolved.
*/
private ThreadLocal<HashSet<IScriptProject>> buildpathsBeingResolved = new ThreadLocal<>();
public static class PerProjectInfo {
static final IModelStatus NEED_RESOLUTION = new ModelStatus();
public IProject project;
public Object savedState;
public boolean triedRead;
public IBuildpathEntry[] rawBuildpath;
public IModelStatus rawBuildpathStatus;
public int rawTimeStamp = 0;
public boolean writtingRawClasspath = false;
public IBuildpathEntry[] resolvedBuildpath;
public IModelStatus unresolvedEntryStatus;
// reverse map from a package fragment root's path to the raw entry
public Map<IPath, IBuildpathEntry> rootPathToRawEntries;
// map from a package fragment root's path to the resolved entry
public Map<IPath, IBuildpathEntry> rootPathToResolvedEntries;
public IEclipsePreferences preferences;
public Hashtable options;
public PerProjectInfo(IProject project) {
this.triedRead = false;
this.savedState = null;
this.project = project;
}
public synchronized IBuildpathEntry[] getResolvedBuildpath() {
if (this.unresolvedEntryStatus == NEED_RESOLUTION)
return null;
return this.resolvedBuildpath;
}
public void rememberExternalLibTimestamps() {
IBuildpathEntry[] buildpath = this.resolvedBuildpath;
if (buildpath == null)
return;
Map<IPath, Long> externalTimeStamps = ModelManager
.getModelManager().deltaState.getExternalLibTimeStamps();
for (int i = 0, length = buildpath.length; i < length; i++) {
IBuildpathEntry entry = buildpath[i];
if (entry.getEntryKind() == IBuildpathEntry.BPE_LIBRARY) {
IPath path = entry.getPath();
if (externalTimeStamps.get(path) == null) {
Object target = Model.getTarget(
ResourcesPlugin.getWorkspace().getRoot(), path,
true);
if (target instanceof IFileHandle) {
long timestamp = DeltaProcessor
.getTimeStamp((IFileHandle) target);
externalTimeStamps.put(path, new Long(timestamp));
}
}
}
}
Map<IPath, Long> customTimeStamps = ModelManager
.getModelManager().deltaState.getCustomTimeStamps();
// Save custom project fragments timestamps.
try {
IScriptProject scriptProject = DLTKCore.create(project);
IProjectFragment[] fragments = scriptProject
.getAllProjectFragments();
for (int i = 0; i < fragments.length; i++) {
if (fragments[i] instanceof IProjectFragmentTimestamp) {
IProjectFragmentTimestamp stamp = (IProjectFragmentTimestamp) fragments[i];
long timeStamp = stamp.getTimeStamp();
customTimeStamps.put(fragments[i].getPath(),
new Long(timeStamp));
}
}
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
public synchronized BuildpathChange resetResolvedBuildpath() {
return setResolvedBuildpath(null, null, null, null, rawTimeStamp,
true);
}
protected BuildpathChange addBuildpathChange() {
// remember old info
ModelManager manager = ModelManager.getModelManager();
BuildpathChange buildpathChange = manager.deltaState
.addBuildpathChange(this.project, this.rawBuildpath,
this.resolvedBuildpath);
return buildpathChange;
}
public synchronized BuildpathChange setRawBuildpath(
IBuildpathEntry[] newRawBuildpath,
IModelStatus newRawBuildpathStatus) {
this.rawTimeStamp++;
return setBuildpath(newRawBuildpath, newRawBuildpathStatus,
null/* resolved classpath */,
null/* rootPathToRawEntries */,
null/* rootPathToResolvedEntries */,
null/* unresolved status */,
true/* add classpath change */);
}
public synchronized BuildpathChange setResolvedBuildpath(
IBuildpathEntry[] newResolvedClasspath,
Map<IPath, IBuildpathEntry> newRootPathToRawEntries,
Map<IPath, IBuildpathEntry> newRootPathToResolvedEntries,
IModelStatus newUnresolvedEntryStatus, int timeStamp,
boolean addBuildpathChange) {
if (this.rawTimeStamp != timeStamp)
return null;
return setBuildpath(this.rawBuildpath, this.rawBuildpathStatus,
newResolvedClasspath, newRootPathToRawEntries,
newRootPathToResolvedEntries, newUnresolvedEntryStatus,
addBuildpathChange);
}
private BuildpathChange setBuildpath(IBuildpathEntry[] newRawBuildpath,
IModelStatus newRawBuildpathStatus,
IBuildpathEntry[] newResolvedBuildpath,
Map<IPath, IBuildpathEntry> newRootPathToRawEntries,
Map<IPath, IBuildpathEntry> newRootPathToResolvedEntries,
IModelStatus newUnresolvedEntryStatus,
boolean addBuildpathChange) {
BuildpathChange classpathChange = addBuildpathChange
? addBuildpathChange()
: null;
this.rawBuildpath = newRawBuildpath;
this.rawBuildpathStatus = newRawBuildpathStatus;
this.resolvedBuildpath = newResolvedBuildpath;
this.rootPathToRawEntries = newRootPathToRawEntries;
this.rootPathToResolvedEntries = newRootPathToResolvedEntries;
this.unresolvedEntryStatus = newUnresolvedEntryStatus;
return classpathChange;
}
/**
* Reads the buildpath and caches the entries. Returns an array of raw
* buildpath entries.
*/
public synchronized IBuildpathEntry[] readAndCacheBuildpath(
ScriptProject javaProject) {
// read file entries and update status
IBuildpathEntry[] classpath;
IModelStatus status;
try {
classpath = javaProject.readFileEntriesWithException(
null/*
* not interested in unknown elements
*/);
status = ModelStatus.VERIFIED_OK;
} catch (CoreException e) {
classpath = ScriptProject.INVALID_BUILDPATH;
status = new ModelStatus(
IModelStatusConstants.INVALID_BUILDPATH_FILE_FORMAT,
Messages.bind(
Messages.buildpath_cannotReadBuildpathFile,
javaProject.getElementName()));
} catch (IOException e) {
classpath = ScriptProject.INVALID_BUILDPATH;
if (Messages.file_badFormat.equals(e.getMessage()))
status = new ModelStatus(
IModelStatusConstants.INVALID_BUILDPATH_FILE_FORMAT,
Messages.bind(Messages.buildpath_xmlFormatError,
javaProject.getElementName(),
Messages.file_badFormat));
else
status = new ModelStatus(
IModelStatusConstants.INVALID_BUILDPATH_FILE_FORMAT,
Messages.bind(
Messages.buildpath_cannotReadBuildpathFile,
javaProject.getElementName()));
} catch (/* BuildpathEntry. */AssertionFailedException e) {
classpath = ScriptProject.INVALID_BUILDPATH;
status = new ModelStatus(
IModelStatusConstants.INVALID_BUILDPATH_FILE_FORMAT,
Messages.bind(
Messages.buildpath_illegalEntryInBuildpathFile,
new String[] { javaProject.getElementName(),
e.getMessage() }));
}
// store new raw buildpath and new status, and null out
// resolved info
setRawBuildpath(classpath, status);
return classpath;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("Info for "); //$NON-NLS-1$
buffer.append(this.project.getFullPath());
buffer.append("\nRaw buildpath:\n"); //$NON-NLS-1$
if (this.rawBuildpath == null) {
buffer.append(" <null>\n"); //$NON-NLS-1$
} else {
for (int i = 0, length = this.rawBuildpath.length; i < length; i++) {
buffer.append(" "); //$NON-NLS-1$
buffer.append(this.rawBuildpath[i]);
buffer.append('\n');
}
}
buffer.append("Resolved buildpath:\n"); //$NON-NLS-1$
IBuildpathEntry[] resolvedCP = this.resolvedBuildpath;
if (resolvedCP == null) {
buffer.append(" <null>\n"); //$NON-NLS-1$
} else {
for (int i = 0, length = resolvedCP.length; i < length; i++) {
buffer.append(" "); //$NON-NLS-1$
buffer.append(resolvedCP[i]);
buffer.append('\n');
}
}
return buffer.toString();
}
public boolean writeAndCacheBuildpath(ScriptProject scriptProject,
final IBuildpathEntry[] newRawBuildpath) throws ModelException {
try {
this.writtingRawClasspath = true;
// write .classpath
if (!scriptProject.writeFileEntries(newRawBuildpath)) {
return false;
}
// store new raw classpath, new output and new status, and null
// out resolved info
setRawBuildpath(newRawBuildpath, ModelStatus.VERIFIED_OK);
} finally {
this.writtingRawClasspath = false;
}
return true;
}
}
public static class PerWorkingCopyInfo implements IProblemRequestor {
int useCount = 0;
IProblemRequestor problemRequestor;
ISourceModule workingCopy;
public PerWorkingCopyInfo(ISourceModule workingCopy,
IProblemRequestor problemRequestor) {
this.workingCopy = workingCopy;
this.problemRequestor = problemRequestor;
}
@Override
public void acceptProblem(IProblem problem) {
if (this.problemRequestor == null)
return;
this.problemRequestor.acceptProblem(problem);
}
@Override
public void beginReporting() {
if (this.problemRequestor == null)
return;
this.problemRequestor.beginReporting();
}
@Override
public void endReporting() {
if (this.problemRequestor == null)
return;
this.problemRequestor.endReporting();
}
public ISourceModule getWorkingCopy() {
return this.workingCopy;
}
@Override
public boolean isActive() {
return this.problemRequestor != null
&& this.problemRequestor.isActive();
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("Info for "); //$NON-NLS-1$
buffer.append(
((ModelElement) this.workingCopy).toStringWithAncestors());
buffer.append("\nUse count = "); //$NON-NLS-1$
buffer.append(this.useCount);
buffer.append("\nProblem requestor:\n "); //$NON-NLS-1$
buffer.append(this.problemRequestor);
return buffer.toString();
}
}
public static boolean VERBOSE = DLTKCore.VERBOSE_MODEL_MANAGER;
public static boolean BP_RESOLVE_VERBOSE = DLTKCore.VERBOSE_BP_RESOLVE;
public static final boolean BP_RESOLVE_VERBOSE_ADVANCED = false;
/**
* Name of the extension point for contributing buildpath variable
* initializers
*/
public static final String BPVARIABLE_INITIALIZER_EXTPOINT_ID = "buildpathVariableInitializer"; //$NON-NLS-1$
/**
* Name of the extension point for contributing buildpath container
* initializers
*/
public static final String BPCONTAINER_INITIALIZER_EXTPOINT_ID = "buildpathContainerInitializer"; //$NON-NLS-1$
public static boolean ZIP_ACCESS_VERBOSE = DLTKCore.VERBOSE_ZIP_ACCESS;
/**
* A cache of opened zip files per thread. (for a given thread, the object
* value is a HashMap from IPath to java.io.ZipFile)
*/
private ThreadLocal<Map<IPath, IArchive>> zipFiles = new ThreadLocal<>();
private UserLibraryManager userLibraryManager;
public final static ISourceModule[] NO_WORKING_COPY = new ISourceModule[0];
/**
* The singleton manager
*/
private static ModelManager MANAGER = new ModelManager();
/**
* Infos cache.
*/
public ModelCache cache;// = new ModelCache();
/*
* Temporary cache of newly opened elements
*/
private ThreadLocal temporaryCache = new ThreadLocal();
/**
* Set of elements which are out of sync with their buffers.
*/
protected HashSet<Openable> elementsOutOfSynchWithBuffers = new HashSet<>(
11);
/**
* Holds the state used for delta processing.
*/
public DeltaProcessingState deltaState = new DeltaProcessingState();
public IndexManager indexManager;
/**
* Table from IProject to PerProjectInfo. NOTE: this object itself is used
* as a lock to synchronize creation/removal of per project infos
*/
protected Map perProjectInfos = new HashMap(5);
/**
* Table from WorkingCopyOwner to a table of ISourceModule (working copy
* handle) to PerWorkingCopyInfo. NOTE: this object itself is used as a lock
* to synchronize creation/removal of per working copy infos
*/
protected Map perWorkingCopyInfos = new HashMap(5);
public final IEclipsePreferences[] preferencesLookup = new IEclipsePreferences[2];
static final int PREF_INSTANCE = 0;
static final int PREF_DEFAULT = 1;
// Preferences
HashSet<String> optionNames = new HashSet<>(20);
Hashtable<String, String> optionsCache;
/*
* Pools of symbols used in the model. Used as a replacement for
* String#intern() that could prevent garbage collection of strings on some
* VMs.
*/
private WeakHashSet stringSymbols = new WeakHashSet(5);
Map workspaceScope = null;
public static final String DELTA_LISTENER_PERF = DLTKCore.PLUGIN_ID
+ "/perf/deltalistener"; //$NON-NLS-1$
private ExternalFoldersManager externalFoldersManager = new ExternalFoldersManager();
private DLTKCoreCache coreCache = null;
final boolean resolveReferencedLibrariesForContainers = false;
public IContentCache getCoreCache() {
if (coreCache == null) {
coreCache = new DLTKCoreCache();
}
return coreCache;
}
/**
* Listener on properties changes.
*/
private IEclipsePreferences.IPreferenceChangeListener propertyListener;
private IEclipsePreferences.IPreferenceChangeListener resourcesPropertyListener;
/**
* Constructs a new ModelManager
*/
private ModelManager() {
// singleton: prevent others from creating a new instance
if (Platform.isRunning())
this.indexManager = new IndexManager();
}
/**
* Returns the handle to the active script model.
*/
public final Model getModel() {
return this.model;
}
/**
* Returns the singleton ModelManager
*/
public final static ModelManager getModelManager() {
return MANAGER;
}
public static DeltaProcessingState getDeltaState() {
return MANAGER.deltaState;
}
/**
* Returns the set of elements which are out of synch with their buffers.
*/
protected HashSet<Openable> getElementsOutOfSynchWithBuffers() {
return this.elementsOutOfSynchWithBuffers;
}
/**
* Returns the info for the element.
*/
public synchronized Object getInfo(IModelElement element) {
HashMap tempCache = (HashMap) this.temporaryCache.get();
if (tempCache != null) {
Object result = tempCache.get(element);
if (result != null) {
return result;
}
}
return this.cache.getInfo(element);
}
/**
* Returns the info for this element without disturbing the cache ordering.
*/
protected synchronized Object peekAtInfo(IModelElement element) {
HashMap tempCache = (HashMap) this.temporaryCache.get();
if (tempCache != null) {
Object result = tempCache.get(element);
if (result != null) {
return result;
}
}
return this.cache.peekAtInfo(element);
}
/*
* Removes all cached info for the given element (including all children)
* from the cache. Returns the info for the given element, or null if it was
* closed.
*/
public synchronized Object removeInfoAndChildren(ModelElement element)
throws ModelException {
Object info = this.cache.peekAtInfo(element);
if (info != null) {
boolean wasVerbose = false;
try {
if (VERBOSE) {
String elementType;
switch (element.getElementType()) {
case IModelElement.SCRIPT_PROJECT:
elementType = "project"; //$NON-NLS-1$
break;
case IModelElement.PROJECT_FRAGMENT:
elementType = "root"; //$NON-NLS-1$
break;
case IModelElement.SCRIPT_FOLDER:
elementType = "folder"; //$NON-NLS-1$
break;
case IModelElement.BINARY_MODULE:
elementType = "binary module"; //$NON-NLS-1$
break;
case IModelElement.SOURCE_MODULE:
elementType = "source module"; //$NON-NLS-1$
break;
default:
elementType = "element"; //$NON-NLS-1$
}
System.out.println(
Thread.currentThread() + " CLOSING " + elementType //$NON-NLS-1$
+ " " + element.toStringWithAncestors()); //$NON-NLS-1$
wasVerbose = true;
VERBOSE = false;
}
element.closing(info);
if (element instanceof IParent
&& info instanceof ModelElementInfo) {
IModelElement[] children = ((ModelElementInfo) info)
.getChildren();
for (int i = 0, size = children.length; i < size; ++i) {
ModelElement child = (ModelElement) children[i];
child.close();
}
}
this.cache.removeInfo(element);
if (wasVerbose) {
System.out.println(this.cache.toStringFillingRation("-> ")); //$NON-NLS-1$
}
} finally {
ModelManager.VERBOSE = wasVerbose;
}
return info;
}
return null;
}
/*
* Puts the infos in the given map (keys are IModelElements and values are
* ModelElementInfos) in the model cache in an atomic way. First checks that
* the info for the opened element (or one of its ancestors) has not been
* added to the cache. If it is the case, another thread has opened the
* element (or one of its ancestors). So returns without updating the cache.
*/
protected synchronized void putInfos(IModelElement openedElement,
Map newElements) {
// remove children
Object existingInfo = this.cache.peekAtInfo(openedElement);
if (openedElement instanceof IParent
&& existingInfo instanceof ModelElementInfo) {
IModelElement[] children = ((ModelElementInfo) existingInfo)
.getChildren();
for (int i = 0, size = children.length; i < size; ++i) {
ModelElement child = (ModelElement) children[i];
try {
child.close();
} catch (ModelException e) {
// ignore
}
}
}
// Need to put any ArchiveProjectFragment in first.
// This is due to the way the LRU cache flushes entries.
// When a BinaryFolder is flused from the LRU cache, the entire
// archive is flushed by removing the ArchiveProjectFragment and all of
// its
// children (see ElementCache.close()). If we flush the BinaryFolder
// when its ArchiveProjectFragment is not in the cache and the root is
// about to be
// added (during the 'while' loop), we will end up in an inconsist
// state.
// Subsequent resolution against package in the archive would fail as a
// result.
for (Iterator it = newElements.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
IModelElement element = (IModelElement) entry.getKey();
if (element instanceof ArchiveProjectFragment
|| element instanceof ExternalScriptFolder) {
Object info = entry.getValue();
it.remove();
this.cache.putInfo(element, info);
}
}
Iterator iterator = newElements.keySet().iterator();
while (iterator.hasNext()) {
IModelElement element = (IModelElement) iterator.next();
Object info = newElements.get(element);
this.cache.putInfo(element, info);
}
}
/**
* Returns the temporary cache for newly opened elements for the current
* thread. Creates it if not already created.
*/
public HashMap getTemporaryCache() {
HashMap result = (HashMap) this.temporaryCache.get();
if (result == null) {
result = new HashMap();
this.temporaryCache.set(result);
}
return result;
}
/*
* Resets the temporary cache for newly created elements to null.
*/
public void resetTemporaryCache() {
this.temporaryCache.set(null);
}
/*
* Returns whether there is a temporary cache for the current thread.
*/
public boolean hasTemporaryCache() {
return this.temporaryCache.get() != null;
}
/**
* Returns the model element corresponding to the given file, its project
* being the given project. Returns <code>null</code> if unable to associate
* the given file with a model element.
*
* Creating a model element has the side effect of creating and opening all
* of the element's parents if they are not yet open.
*/
public static IModelElement create(IFile file, IScriptProject project) {
if (file == null) {
return null;
}
if (project == null) {
project = DLTKCore.create(file.getProject());
}
// FIXME at the moment we can create source modules only
if (Util.isValidSourceModule(project, file)) {
return createSourceModuleFrom(file, project);
}
return null;
}
/**
* Creating an element has the side effect of creating and opening all of
* the element's parents if they are not yet open.
*/
public static IModelElement create(IResource resource,
IScriptProject project) {
if (resource == null) {
return null;
}
int type = resource.getType();
switch (type) {
case IResource.PROJECT:
return DLTKCore.create((IProject) resource);
case IResource.FILE:
return create((IFile) resource, project);
case IResource.FOLDER:
return create((IFolder) resource, project);
case IResource.ROOT:
return DLTKCore.create((IWorkspaceRoot) resource);
default:
return null;
}
}
public static IModelElement create(IFolder folder, IScriptProject project) {
if (folder == null) {
return null;
}
IModelElement element;
if (project == null) {
project = DLTKCore.create(folder.getProject());
element = determineIfOnBuildpath(folder, project);
if (element == null) {
// walk all projects and find one that have the given folder on
// its buildpath
IScriptProject[] projects;
try {
projects = ModelManager.getModelManager().getModel()
.getScriptProjects();
} catch (ModelException e) {
return null;
}
for (int i = 0, length = projects.length; i < length; i++) {
project = projects[i];
element = determineIfOnBuildpath(folder, project);
if (element != null)
break;
}
}
if (element == null) {
// not on buildpath - make the root its folder
IProjectFragment root = project.getProjectFragment(Path.EMPTY);
element = root.getScriptFolder(folder.getProjectRelativePath());
if (VERBOSE) {
System.out.println(
"WARNING : creating dir outside buildpath (" //$NON-NLS-1$
+ Thread.currentThread() + "): " //$NON-NLS-1$
+ folder.getFullPath());
}
}
} else {
element = determineIfOnBuildpath(folder, project);
}
return element;
}
/**
* Creates and returns a source module element for the given file, its
* project being the given project. Returns <code>null</code> if unable to
* recognize the source module.
*/
public static ISourceModule createSourceModuleFrom(IFile file,
IScriptProject project) {
if (file == null)
return null;
if (project == null) {
project = DLTKCore.create(file.getProject());
}
IScriptFolder folder = (IScriptFolder) determineIfOnBuildpath(file,
project);
if (folder == null) {
// not on buildpath - make the root its folder
IProjectFragment root = project
.getProjectFragment(file.getParent());
folder = root.getScriptFolder(Path.EMPTY);
if (VERBOSE) {
System.out.println(
"WARNING : creating module element outside buildpath (" //$NON-NLS-1$
+ Thread.currentThread() + "): " //$NON-NLS-1$
+ file.getFullPath());
}
}
return folder.getSourceModule(file.getName());
}
/**
* Returns the project fragment root represented by the resource, or the
* folder the given resource is located in, or <code>null</code> if the
* given resource is not on the buildpath of the given project.
*/
public static IModelElement determineIfOnBuildpath(IResource resource,
IScriptProject project) {
IPath resourcePath = resource.getFullPath();
try {
IBuildpathEntry[] entries = ((ScriptProject) project)
.getResourceOnlyResolvedBuildpath();
for (int i = 0; i < entries.length; i++) {
IBuildpathEntry entry = entries[i];
if (entry.getEntryKind() == IBuildpathEntry.BPE_PROJECT)
continue;
IPath rootPath = entry.getPath();
if (rootPath.equals(resourcePath)) {
return project.getProjectFragment(resource);
} else if (rootPath.isPrefixOf(resourcePath)) {
BuildpathEntry bpe = (BuildpathEntry) entry;
if (!Util.isExcluded(resource,
bpe.fullInclusionPatternChars(),
bpe.fullExclusionPatternChars())) {
/*
* given we have a resource child of the root, it cannot
* be a ZIP fragment
*/
IProjectFragment root = ((ScriptProject) project)
.getFolderProjectFragment(rootPath);
if (root == null)
return null;
IPath folderPath = resourcePath
.removeFirstSegments(rootPath.segmentCount());
if (resource.getType() == IResource.FILE) {
/*
* if the resource is a file, then remove the last
* segment which is the file name in the folder
*/
folderPath = folderPath.removeLastSegments(1);
}
return root.getScriptFolder(folderPath);
}
}
}
} catch (ModelException npe) {
return null;
}
return null;
}
/*
* Returns the per-project info for the given project. If specified, create
* the info if the info doesn't exist.
*/
public PerProjectInfo getPerProjectInfo(IProject project, boolean create) {
synchronized (this.perProjectInfos) { // use the perProjectInfo
// collection as its own lock
PerProjectInfo info = (PerProjectInfo) this.perProjectInfos
.get(project);
if (info == null && create) {
info = new PerProjectInfo(project);
this.perProjectInfos.put(project, info);
}
return info;
}
}
/*
* Returns the per-project info for the given project. If the info doesn't
* exist, check for the project existence and create the info. @throws
* ModelException if the project doesn't exist.
*/
public PerProjectInfo getPerProjectInfoCheckExistence(IProject project)
throws ModelException {
ModelManager.PerProjectInfo info = getPerProjectInfo(project,
false /*
* don't create info
*/);
if (info == null) {
if (!ScriptProject.hasScriptNature(project)) {
throw ((ScriptProject) DLTKCore.create(project))
.newNotPresentException();
}
info = getPerProjectInfo(project, true /* create info */);
}
return info;
}
/*
* Returns the per-working copy info for the given working copy at the given
* path. If it doesn't exist and if create, add a new per-working copy info
* with the given problem requestor. If recordUsage, increment the
* per-working copy info's use count. Returns null if it doesn't exist and
* not create.
*/
public PerWorkingCopyInfo getPerWorkingCopyInfo(SourceModule workingCopy,
boolean create, boolean recordUsage,
IProblemRequestor problemRequestor) {
synchronized (this.perWorkingCopyInfos) {
// use the perWorkingCopyInfo collection as its own lock
WorkingCopyOwner owner = workingCopy.getOwner();
Map workingCopyToInfos = (Map) this.perWorkingCopyInfos.get(owner);
if (workingCopyToInfos == null && create) {
workingCopyToInfos = new HashMap();
this.perWorkingCopyInfos.put(owner, workingCopyToInfos);
}
PerWorkingCopyInfo info = workingCopyToInfos == null ? null
: (PerWorkingCopyInfo) workingCopyToInfos.get(workingCopy);
if (info == null && create) {
info = new PerWorkingCopyInfo(workingCopy, problemRequestor);
workingCopyToInfos.put(workingCopy, info);
}
if (info != null && recordUsage)
info.useCount++;
return info;
}
}
/*
* Returns all the working copies which have the given owner. Adds the
* working copies of the primary owner if specified. Returns null if it has
* none.
*/
public ISourceModule[] getWorkingCopies(WorkingCopyOwner owner,
boolean addPrimary) {
synchronized (this.perWorkingCopyInfos) {
ISourceModule[] primaryWCs = addPrimary
&& owner != DefaultWorkingCopyOwner.PRIMARY
? getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY,
false)
: null;
Map workingCopyToInfos = (Map) this.perWorkingCopyInfos.get(owner);
if (workingCopyToInfos == null)
return primaryWCs;
int primaryLength = primaryWCs == null ? 0 : primaryWCs.length;
int size = workingCopyToInfos.size(); // note size is > 0
// otherwise
// pathToPerWorkingCopyInfos
// would be null
ISourceModule[] result = new ISourceModule[primaryLength + size];
int index = 0;
if (primaryWCs != null) {
for (int i = 0; i < primaryLength; i++) {
ISourceModule primaryWorkingCopy = primaryWCs[i];
boolean validSrcModule = false;
IResource res = primaryWorkingCopy.getResource();
if (res != null) {
validSrcModule = Util
.isValidSourceModule(primaryWorkingCopy, res);
} else {
validSrcModule = Util.isValidSourceModule(
primaryWorkingCopy,
primaryWorkingCopy.getPath());
}
if (validSrcModule) {
ISourceModule workingCopy = new SourceModule(
(ModelElement) primaryWorkingCopy.getParent(),
primaryWorkingCopy.getElementName(), owner);
if (!workingCopyToInfos.containsKey(workingCopy))
result[index++] = primaryWorkingCopy;
} else {
System.err.println("Not valid primary working copy:" //$NON-NLS-1$
+ primaryWorkingCopy.getElementName());
}
}
if (index != primaryLength)
System.arraycopy(result, 0,
result = new ISourceModule[index + size], 0, index);
}
Iterator iterator = workingCopyToInfos.values().iterator();
while (iterator.hasNext()) {
result[index++] = ((ModelManager.PerWorkingCopyInfo) iterator
.next()).getWorkingCopy();
}
return result;
}
}
public DeltaProcessor getDeltaProcessor() {
return this.deltaState.getDeltaProcessor();
}
public static ExternalFoldersManager getExternalManager() {
return MANAGER.externalFoldersManager;
}
public IndexManager getIndexManager() {
return this.indexManager;
}
public Object getLastBuiltState(IProject project,
IProgressMonitor monitor) {
if (!DLTKLanguageManager.hasScriptNature(project)) {
if (ScriptBuilder.DEBUG)
System.out.println(project + " is not a Java project"); //$NON-NLS-1$
return null; // should never be requested on non-Java projects
}
PerProjectInfo info = getPerProjectInfo(project,
true/*
* create if missing
*/);
if (!info.triedRead) {
info.triedRead = true;
try {
if (monitor != null)
monitor.subTask(
Messages.bind(Messages.build_readStateProgress,
project.getName()));
info.savedState = readState(project);
} catch (CoreException e) {
e.printStackTrace();
}
}
return info.savedState;
}
public String getOption(String optionName) {
if (DLTKCore.CORE_ENCODING.equals(optionName)) {
return DLTKCore.getEncoding();
}
String propertyName = optionName;
if (this.optionNames.contains(propertyName)) {
IPreferencesService service = Platform.getPreferencesService();
String value = service.get(optionName, null,
this.preferencesLookup);
return value == null ? null : value.trim();
}
return null;
}
/**
* Get workpsace eclipse preference for ScriptCore plugin.
*/
public IEclipsePreferences getInstancePreferences() {
return preferencesLookup[PREF_INSTANCE];
}
public void setOptions(Hashtable newOptions) {
try {
IEclipsePreferences defaultPreferences = getDefaultPreferences();
IEclipsePreferences instancePreferences = getInstancePreferences();
if (newOptions == null) {
instancePreferences.clear();
} else {
Enumeration keys = newOptions.keys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
if (!this.optionNames.contains(key))
continue; // unrecognized option
if (key.equals(DLTKCore.CORE_ENCODING))
continue; // skipped, contributed by resource prefs
String value = (String) newOptions.get(key);
String defaultValue = defaultPreferences.get(key, null);
if (defaultValue != null && defaultValue.equals(value)) {
instancePreferences.remove(key);
} else {
instancePreferences.put(key, value);
}
}
}
// persist options
instancePreferences.flush();
// update cache
this.optionsCache = newOptions == null ? null
: new Hashtable(newOptions);
} catch (BackingStoreException e) {
// ignore
}
}
public Hashtable<String, String> getOptions() {
// return cached options if already computed
if (this.optionsCache != null)
return new Hashtable<>(this.optionsCache);
if (!Platform.isRunning()) {
return this.optionsCache = getDefaultOptionsNoInitialization();
}
// init
Hashtable<String, String> options = new Hashtable<>(10);
IPreferencesService service = Platform.getPreferencesService();
// set options using preferences service lookup
for (String propertyName : optionNames) {
String propertyValue = service.get(propertyName, null,
this.preferencesLookup);
if (propertyValue != null) {
options.put(propertyName, propertyValue);
}
}
// get encoding through resource plugin
options.put(DLTKCore.CORE_ENCODING, DLTKCore.getEncoding());
// store built map in cache
this.optionsCache = new Hashtable<>(options);
// return built map
return options;
}
/*
* Reset project options stored in info cache.
*/
public void resetProjectOptions(ScriptProject scriptProject) {
synchronized (this.perProjectInfos) { // use the perProjectInfo
// collection as its own lock
IProject project = scriptProject.getProject();
PerProjectInfo info = (PerProjectInfo) this.perProjectInfos
.get(project);
if (info != null) {
info.options = null;
}
}
}
/*
* Reset project preferences stored in info cache.
*/
public void resetProjectPreferences(ScriptProject scriptProject) {
synchronized (this.perProjectInfos) { // use the perProjectInfo
// collection as its own lock
IProject project = scriptProject.getProject();
PerProjectInfo info = (PerProjectInfo) this.perProjectInfos
.get(project);
if (info != null) {
info.preferences = null;
}
}
}
public void setBuildpathBeingResolved(IScriptProject project,
boolean buildpathIsResolved) {
if (buildpathIsResolved) {
getBuildpathBeingResolved().add(project);
} else {
getBuildpathBeingResolved().remove(project);
}
}
private HashSet<IScriptProject> getBuildpathBeingResolved() {
HashSet<IScriptProject> result = this.buildpathsBeingResolved.get();
if (result == null) {
result = new HashSet<>();
this.buildpathsBeingResolved.set(result);
}
return result;
}
public boolean isBuildpathBeingResolved(IScriptProject project) {
return getBuildpathBeingResolved().contains(project);
}
/*
* Discards the per working copy info for the given working copy (making it
* a compilation unit) if its use count was 1. Otherwise, just decrement the
* use count. If the working copy is primary, computes the delta between its
* state and the original compilation unit and register it. Close the
* working copy, its buffer and remove it from the shared working copy
* table. Ignore if no per-working copy info existed. NOTE: it must NOT be
* synchronized as it may interact with the element info cache (if useCount
* is decremented to 0), see bug 50667. Returns the new use count (or -1 if
* it didn't exist).
*/
public int discardPerWorkingCopyInfo(SourceModule workingCopy)
throws ModelException {
// create the delta builder (this remembers the current content of the
// working copy)
// outside the perWorkingCopyInfos lock (see bug 50667)
ModelElementDeltaBuilder deltaBuilder = null;
if (workingCopy.isPrimary() && workingCopy.hasUnsavedChanges()) {
deltaBuilder = new ModelElementDeltaBuilder(workingCopy);
}
PerWorkingCopyInfo info = null;
synchronized (this.perWorkingCopyInfos) {
WorkingCopyOwner owner = workingCopy.getOwner();
Map workingCopyToInfos = (Map) this.perWorkingCopyInfos.get(owner);
if (workingCopyToInfos == null)
return -1;
info = (PerWorkingCopyInfo) workingCopyToInfos.get(workingCopy);
if (info == null)
return -1;
if (--info.useCount == 0) {
// remove per working copy info
workingCopyToInfos.remove(workingCopy);
if (workingCopyToInfos.isEmpty()) {
this.perWorkingCopyInfos.remove(owner);
}
}
}
if (info.useCount == 0) { // info cannot be null here (check was done
// above)
// remove infos + close buffer (since no longer working copy)
// outside the perWorkingCopyInfos lock (see bug 50667)
removeInfoAndChildren(workingCopy);
workingCopy.closeBuffer();
// compute the delta if needed and register it if there are changes
if (deltaBuilder != null) {
getSourceModuleInfoCache().remove(workingCopy);
deltaBuilder.buildDeltas();
if ((deltaBuilder.delta != null) && (deltaBuilder.delta
.getAffectedChildren().length > 0)) {
getDeltaProcessor().registerModelDelta(deltaBuilder.delta);
}
}
}
return info.useCount;
}
public synchronized String intern(String s) {
// make sure to copy the string (so that it doesn't hold on the
// underlying char[] that might be much bigger than necessary)
return (String) this.stringSymbols.add(s);
}
public void startup() throws CoreException {
try {
// initialize Model model cache
this.cache = new ModelCache();
// request state folder creation (workaround 19885)
DLTKCore.getPlugin().getStateLocation();
// Initialize eclipse preferences
initializePreferences();
// Listen to preference changes
this.propertyListener = event -> ModelManager.this.optionsCache = null;
installPreferenceChangeListener(DLTKCore.PLUGIN_ID,
this.propertyListener);
// listen for encoding changes (see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=255501 )
this.resourcesPropertyListener = event -> {
if (ResourcesPlugin.PREF_ENCODING.equals(event.getKey())) {
ModelManager.this.optionsCache = null;
}
};
installPreferenceChangeListener(
ResourcesPlugin.getPlugin().getBundle().getSymbolicName(),
this.resourcesPropertyListener);
long start = -1;
if (VERBOSE)
start = System.currentTimeMillis();
loadContainers();
if (VERBOSE)
traceContainers("Loaded", start); //$NON-NLS-1$
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.addResourceChangeListener(this.deltaState,
IResourceChangeEvent.PRE_BUILD
| IResourceChangeEvent.POST_BUILD
| IResourceChangeEvent.POST_CHANGE
| IResourceChangeEvent.PRE_DELETE
| IResourceChangeEvent.PRE_CLOSE);
DLTKContentTypeManager.installListener();
sourceModuleInfoCache = new SourceModuleInfoCache();
sourceModuleInfoCache.start();
startIndexing();
// process deltas since last activated in indexer thread so that
// indexes are up-to-date.
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38658
Job processSavedState = new Job(Messages.savedState_jobName) {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
// add save participant and process delta atomically
// see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=59937
workspace.run(progress -> {
ISavedState savedState = workspace
.addSaveParticipant(DLTKCore.getPlugin(),
ModelManager.this);
if (savedState != null) {
// the event type coming from the saved
// state is always POST_AUTO_BUILD
// force it to be POST_CHANGE so that the
// delta processor can handle it
ModelManager.this.deltaState
.getDeltaProcessor().overridenEventType = IResourceChangeEvent.POST_CHANGE;
savedState.processResourceChangeEvents(
ModelManager.this.deltaState);
}
}, monitor);
} catch (CoreException e) {
return e.getStatus();
}
return Status.OK_STATUS;
}
};
processSavedState.setSystem(true);
processSavedState.setPriority(Job.SHORT); // process asap
processSavedState.schedule();
} catch (RuntimeException e) {
shutdown();
throw e;
}
}
private static IEclipsePreferences getPluginPreferences(String pluginId) {
return InstanceScope.INSTANCE.getNode(pluginId);
}
private static void installPreferenceChangeListener(String pluginId,
IPreferenceChangeListener listener) {
getPluginPreferences(pluginId).addPreferenceChangeListener(listener);
}
private void startIndexing() {
if (indexManager != null) {
indexManager.reset();
// create contributed indexers in a job, so
// dltk.core initialization completes earlier.
final Job startIndexing = new Job("DLTK indexing initialization") {
@Override
protected IStatus run(IProgressMonitor monitor) {
ProjectIndexerManager.startIndexing();
return Status.OK_STATUS;
}
};
startIndexing.setSystem(true);
startIndexing.setPriority(Job.BUILD);
startIndexing.schedule();
}
}
/**
* Update the buildpath variable cache
*/
public static class EclipsePreferencesListener
implements IEclipsePreferences.IPreferenceChangeListener {
/**
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
*/
@Override
public void preferenceChange(
IEclipsePreferences.PreferenceChangeEvent event) {
String propertyName = event.getKey();
if (propertyName.startsWith(BP_CONTAINER_PREFERENCES_PREFIX)) {
recreatePersistedContainer(propertyName,
(String) event.getNewValue(), false);
} else if (propertyName
.startsWith(BP_USERLIBRARY_PREFERENCES_PREFIX)) {
String libName = propertyName
.substring(BP_USERLIBRARY_PREFERENCES_PREFIX.length());
UserLibraryManager manager = ModelManager
.getUserLibraryManager();
manager.updateUserLibrary(libName,
(String) event.getNewValue());
}
}
}
/**
* Reads the build state for the relevant project.
*/
protected Object readState(IProject project) throws CoreException {
File file = getSerializationFile(project);
if (file != null && file.exists()) {
try {
DataInputStream in = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)));
try {
String pluginID = in.readUTF();
if (!pluginID.equals(DLTKCore.PLUGIN_ID))
throw new IOException(Messages.build_wrongFileFormat);
String kind = in.readUTF();
if (!kind.equals("STATE")) //$NON-NLS-1$
throw new IOException(Messages.build_wrongFileFormat);
if (in.readBoolean())
return ScriptBuilder.readState(project, in);
if (ScriptBuilder.DEBUG)
System.out.println(
"Saved state thinks last build failed for " //$NON-NLS-1$
+ project.getName());
} finally {
in.close();
}
} catch (Exception e) {
e.printStackTrace();
throw new CoreException(new Status(IStatus.ERROR,
DLTKCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
"Error reading last build state for project " //$NON-NLS-1$
+ project.getName(),
e));
}
} else if (ScriptBuilder.DEBUG) {
if (file == null)
System.out.println("Project does not exist: " + project); //$NON-NLS-1$
else
System.out.println("Build state file " + file.getPath() //$NON-NLS-1$
+ " does not exist"); //$NON-NLS-1$
}
return null;
}
public static void recreatePersistedContainer(String propertyName,
String containerString, boolean addToContainerValues) {
int containerPrefixLength = BP_CONTAINER_PREFERENCES_PREFIX.length();
int index = propertyName.indexOf('|', containerPrefixLength);
if (containerString != null)
containerString = containerString.trim();
if (index > 0) {
String projectName = propertyName
.substring(containerPrefixLength, index).trim();
IScriptProject project = getModelManager().getModel()
.getScriptProject(projectName);
IPath containerPath = new Path(
propertyName.substring(index + 1).trim());
recreatePersistedContainer(project, containerPath, containerString,
addToContainerValues);
}
}
private static void recreatePersistedContainer(final IScriptProject project,
final IPath containerPath, String containerString,
boolean addToContainerValues) {
if (!project.getProject().isAccessible())
return; // avoid leaking deleted project's persisted container
if (containerString == null) {
getModelManager().containerPut(project, containerPath, null);
} else {
IBuildpathEntry[] entries;
try {
entries = ((ScriptProject) project).decodeBuildpath(
containerString,
null/*
* not interested in unknown elements
*/);
} catch (IOException e) {
Util.log(e, "Could not recreate persisted container: \n" //$NON-NLS-1$
+ containerString);
entries = ScriptProject.INVALID_BUILDPATH;
}
if (entries != ScriptProject.INVALID_BUILDPATH) {
final IBuildpathEntry[] containerEntries = entries;
IBuildpathContainer container = new IBuildpathContainer() {
@Override
public IBuildpathEntry[] getBuildpathEntries() {
return containerEntries;
}
@Override
public String getDescription() {
return "Persisted container [" + containerPath //$NON-NLS-1$
+ " for project [" + project.getElementName() //$NON-NLS-1$
+ "]"; //$NON-NLS-1$
}
@Override
public int getKind() {
return 0;
}
@Override
public IPath getPath() {
return containerPath;
}
@Override
public String toString() {
return getDescription();
}
};
if (addToContainerValues) {
getModelManager().containerPut(project, containerPath,
container);
}
Map projectContainers = (Map) getModelManager().previousSessionContainers
.get(project);
if (projectContainers == null) {
projectContainers = new HashMap(1);
getModelManager().previousSessionContainers.put(project,
projectContainers);
}
projectContainers.put(containerPath, container);
}
}
}
/**
* Initialize preferences lookups for DLTKCore plugin.
*/
public void initializePreferences() {
// Create lookups
preferencesLookup[PREF_INSTANCE] = InstanceScope.INSTANCE
.getNode(DLTKCore.PLUGIN_ID);
preferencesLookup[PREF_DEFAULT] = DefaultScope.INSTANCE
.getNode(DLTKCore.PLUGIN_ID);
// Listen to instance preferences node removal from parent in order to
// refresh stored one
IEclipsePreferences.INodeChangeListener listener = new IEclipsePreferences.INodeChangeListener() {
@Override
public void added(IEclipsePreferences.NodeChangeEvent event) {
// do nothing
}
@Override
public void removed(IEclipsePreferences.NodeChangeEvent event) {
if (event.getChild() == preferencesLookup[PREF_INSTANCE]) {
preferencesLookup[PREF_INSTANCE] = InstanceScope.INSTANCE
.getNode(DLTKCore.PLUGIN_ID);
preferencesLookup[PREF_INSTANCE]
.addPreferenceChangeListener(
new EclipsePreferencesListener());
}
}
};
((IEclipsePreferences) preferencesLookup[PREF_INSTANCE].parent())
.addNodeChangeListener(listener);
preferencesLookup[PREF_INSTANCE]
.addPreferenceChangeListener(new EclipsePreferencesListener());
// Listen to default preferences node removal from parent in order to
// refresh stored one
listener = new IEclipsePreferences.INodeChangeListener() {
@Override
public void added(IEclipsePreferences.NodeChangeEvent event) {
// do nothing
}
@Override
public void removed(IEclipsePreferences.NodeChangeEvent event) {
if (event.getChild() == preferencesLookup[PREF_DEFAULT]) {
preferencesLookup[PREF_DEFAULT] = DefaultScope.INSTANCE
.getNode(DLTKCore.PLUGIN_ID);
}
}
};
((IEclipsePreferences) preferencesLookup[PREF_DEFAULT].parent())
.addNodeChangeListener(listener);
}
public void shutdown() {
final IEclipsePreferences preferences = getPluginPreferences(
DLTKCore.PLUGIN_ID);
try {
preferences.flush();
} catch (BackingStoreException e) {
Util.log(e, "Could not save DLTKCore preferences"); //$NON-NLS-1$
}
// Stop listening to preferences changes
preferences.removePreferenceChangeListener(this.propertyListener);
getPluginPreferences(
ResourcesPlugin.getPlugin().getBundle().getSymbolicName())
.removePreferenceChangeListener(
this.resourcesPropertyListener);
if (coreCache != null) {
coreCache.stop();
}
IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.removeResourceChangeListener(this.deltaState);
DLTKContentTypeManager.uninstallListener();
workspace.removeSaveParticipant(DLTKCore.PLUGIN_ID);
if (sourceModuleInfoCache != null) {
sourceModuleInfoCache.stop();
}
if (this.indexManager != null) { // no more indexing
this.indexManager.shutdown();
}
// wait for the initialization job to finish
try {
Job.getJobManager().join(DLTKCore.PLUGIN_ID, null);
} catch (InterruptedException e) {
// ignore
}
}
public void removePerProjectInfo(ScriptProject scriptProject) {
synchronized (this.perProjectInfos) { // use the perProjectInfo
// collection as its own lock
IProject project = scriptProject.getProject();
PerProjectInfo info = (PerProjectInfo) this.perProjectInfos
.get(project);
if (info != null) {
this.perProjectInfos.remove(project);
}
}
}
public synchronized IPath variableGet(String variableName) {
// check initialization in progress first
HashSet initializations = variableInitializationInProgress();
if (initializations.contains(variableName)) {
return VARIABLE_INITIALIZATION_IN_PROGRESS;
}
return (IPath) this.variables.get(variableName);
}
private synchronized IPath variableGetDefaultToPreviousSession(
String variableName) {
IPath variablePath = (IPath) this.variables.get(variableName);
if (variablePath == null)
return getPreviousSessionVariable(variableName);
return variablePath;
}
/*
* Returns the set of variable names that are being initialized in the
* current thread.
*/
private HashSet variableInitializationInProgress() {
HashSet initializations = (HashSet) this.variableInitializationInProgress
.get();
if (initializations == null) {
initializations = new HashSet();
this.variableInitializationInProgress.set(initializations);
}
return initializations;
}
public synchronized String[] variableNames() {
int length = this.variables.size();
String[] result = new String[length];
Iterator vars = this.variables.keySet().iterator();
int index = 0;
while (vars.hasNext()) {
result[index++] = (String) vars.next();
}
return result;
}
public synchronized void variablePut(String variableName,
IPath variablePath) {
// set/unset the initialization in progress
HashSet initializations = variableInitializationInProgress();
if (variablePath == VARIABLE_INITIALIZATION_IN_PROGRESS) {
initializations.add(variableName);
// do not write out intermediate initialization value
return;
} else {
initializations.remove(variableName);
// update cache - do not only rely on listener refresh
if (variablePath == null) {
// if path is null, record that the variable was removed to
// avoid asking the initializer to initialize it again
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=112609
this.variables.put(variableName, BP_ENTRY_IGNORE_PATH);
// clean other variables caches
this.variablesWithInitializer.remove(variableName);
} else {
this.variables.put(variableName, variablePath);
}
// discard obsoleted information about previous session
this.previousSessionVariables.remove(variableName);
}
}
public void variablePreferencesPut(String variableName,
IPath variablePath) {
String variableKey = BP_VARIABLE_PREFERENCES_PREFIX + variableName;
if (variablePath == null) {
getInstancePreferences().remove(variableKey);
} else {
getInstancePreferences().put(variableKey, variablePath.toString());
}
try {
getInstancePreferences().flush();
} catch (BackingStoreException e) {
// ignore exception
}
}
/*
* Optimize startup case where 1 variable is initialized at a time with the
* same value as on shutdown.
*/
public boolean variablePutIfInitializingWithSameValue(
String[] variableNames, IPath[] variablePaths) {
if (variableNames.length != 1)
return false;
String variableName = variableNames[0];
IPath oldPath = variableGetDefaultToPreviousSession(variableName);
if (oldPath == null)
return false;
IPath newPath = variablePaths[0];
if (!oldPath.equals(newPath))
return false;
variablePut(variableName, newPath);
return true;
}
/**
* Sets the last built state for the given project, or null to reset it.
*/
public void setLastBuiltState(IProject project, Object state) {
if (DLTKLanguageManager.hasScriptNature(project)) {
// should never be requested on non-script projects
PerProjectInfo info = getPerProjectInfo(project,
true /*
* create if missing
*/);
info.triedRead = true; // no point trying to re-read once using
// setter
info.savedState = state;
}
if (state == null) { // delete state file to ensure a full build
// happens if the workspace crashes
try {
File file = getSerializationFile(project);
if (file != null && file.exists())
file.delete();
} catch (SecurityException se) {
// could not delete file: cannot do much more
}
}
}
/**
* Returns the File to use for saving and restoring the last built state for
* the given project.
*/
private File getSerializationFile(IProject project) {
if (!project.exists())
return null;
IPath workingLocation = project.getWorkingLocation(DLTKCore.PLUGIN_ID);
return workingLocation.append("state.dat").toFile(); //$NON-NLS-1$
}
// If modified, also modify the method getDefaultOptionsNoInitialization()
public Hashtable getDefaultOptions() {
Hashtable defaultOptions = new Hashtable(10);
// see
// DLTKCorePreferenceInitializer#initializeDefaultPluginPreferences()
// for changing default settings
// If modified, also modify the method
// getDefaultOptionsNoInitialization()
IEclipsePreferences defaultPreferences = getDefaultPreferences();
// initialize preferences to their default
Iterator iterator = this.optionNames.iterator();
while (iterator.hasNext()) {
String propertyName = (String) iterator.next();
String value = defaultPreferences.get(propertyName, null);
if (value != null)
defaultOptions.put(propertyName, value);
}
// get encoding through resource plugin
defaultOptions.put(DLTKCore.CORE_ENCODING, DLTKCore.getEncoding());
return defaultOptions;
}
/**
* Get default eclipse preference for DLTKCore plugin.
*/
public IEclipsePreferences getDefaultPreferences() {
return preferencesLookup[PREF_DEFAULT];
}
// Do not modify without modifying getDefaultOptions()
private Hashtable<String, String> getDefaultOptionsNoInitialization() {
System.err.println(
"Add language dependent compiler options. Or implement it in another whan in DLTK way..."); //$NON-NLS-1$
Map defaultOptionsMap = new HashMap(); // compiler defaults
return new Hashtable(defaultOptionsMap);
}
public IBuildpathContainer getBuildpathContainer(IPath containerPath,
IScriptProject project) throws ModelException {
IBuildpathContainer container = containerGet(project, containerPath);
if (container == null) {
if (this.batchContainerInitializations) {
// avoid deep recursion while initializaing container on
// workspace restart
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=60437)
this.batchContainerInitializations = false;
return initializeAllContainers(project, containerPath);
}
return initializeContainer(project, containerPath);
}
return container;
}
public synchronized IBuildpathContainer containerGet(IScriptProject project,
IPath containerPath) {
// check initialization in progress first
HashSet projectInitializations = containerInitializationInProgress(
project);
if (projectInitializations.contains(containerPath)) {
return CONTAINER_INITIALIZATION_IN_PROGRESS;
}
Map projectContainers = (Map) this.containers.get(project);
if (projectContainers == null) {
return null;
}
IBuildpathContainer container = (IBuildpathContainer) projectContainers
.get(containerPath);
return container;
}
/*
* Returns the set of container paths for the given project that are being
* initialized in the current thread.
*/
private HashSet containerInitializationInProgress(IScriptProject project) {
Map initializations = (Map) this.containerInitializationInProgress
.get();
if (initializations == null) {
initializations = new HashMap();
this.containerInitializationInProgress.set(initializations);
}
HashSet projectInitializations = (HashSet) initializations.get(project);
if (projectInitializations == null) {
projectInitializations = new HashSet();
initializations.put(project, projectInitializations);
}
return projectInitializations;
}
/*
* Initialize all container at the same time as the given container. Return
* the container for the given path and project.
*/
private IBuildpathContainer initializeAllContainers(
IScriptProject scriptProjectToInit, IPath containerToInit)
throws ModelException {
/*
* if (BP_RESOLVE_VERBOSE) { Util.verbose( "CPContainer INIT - batching
* containers initialization\n" + //$NON-NLS-1$ " project to init: " +
* scriptProjectToInit.getElementName() + '\n' + //$NON-NLS-1$ "
* container path to init: " + containerToInit); //$NON-NLS-1$ }
*/
// collect all container paths
final HashMap allContainerPaths = new HashMap();
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
.getProjects();
for (int i = 0, length = projects.length; i < length; i++) {
IProject project = projects[i];
if (!DLTKLanguageManager.hasScriptNature(project))
continue;
ScriptProject scriptProject = new ScriptProject(project,
getModel());
HashSet paths = null;
IBuildpathEntry[] rawBuildpath = scriptProject.getRawBuildpath();
for (int j = 0, length2 = rawBuildpath.length; j < length2; j++) {
IBuildpathEntry entry = rawBuildpath[j];
IPath path = entry.getPath();
if (entry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER
&& containerGet(scriptProject, path) == null) {
if (paths == null) {
paths = new HashSet();
allContainerPaths.put(scriptProject, paths);
}
paths.add(path);
}
}
}
HashSet containerPaths = (HashSet) allContainerPaths
.get(scriptProjectToInit);
if (containerPaths == null) {
containerPaths = new HashSet();
allContainerPaths.put(scriptProjectToInit, containerPaths);
}
containerPaths.add(containerToInit);
// end block
// mark all containers as being initialized
this.containerInitializationInProgress.set(allContainerPaths);
// initialize all containers
boolean ok = false;
try {
// if possible run inside an IWokspaceRunnable with AVOID_UPATE to
// avoid unwanted builds
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=118507)
IWorkspaceRunnable runnable = monitor -> {
Set keys = allContainerPaths.keySet();
int length = keys.size();
IScriptProject[] scriptProjects = new IScriptProject[length]; // clone
// as
// the
// following
// will
// have
// a
// side
// effect
keys.toArray(scriptProjects);
for (int i = 0; i < length; i++) {
IScriptProject scriptProject = scriptProjects[i];
HashSet pathSet = (HashSet) allContainerPaths
.get(scriptProject);
if (pathSet == null)
continue;
int length2 = pathSet.size();
IPath[] paths = new IPath[length2];
pathSet.toArray(paths); // clone as the following will
// have a side effect
for (int j = 0; j < length2; j++) {
IPath path = paths[j];
initializeContainer(scriptProject, path);
}
}
};
IWorkspace workspace = ResourcesPlugin.getWorkspace();
if (workspace.isTreeLocked())
runnable.run(null/* no progress available */);
else
workspace.run(runnable, null/* don't take any lock */,
IWorkspace.AVOID_UPDATE,
null/*
* no progress available here
*/);
ok = true;
} catch (CoreException e) {
// ignore
System.err.println("Exception while initializing all containers"); //$NON-NLS-1$
// Util.log(e, "Exception while initializing all containers");
// //$NON-NLS-1$
} finally {
if (!ok) {
// if we're being traversed by an exception, ensure that that
// containers are
// no longer marked as initialization in progress
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=66437)
this.containerInitializationInProgress.set(null);
}
}
return containerGet(scriptProjectToInit, containerToInit);
}
IBuildpathContainer initializeContainer(IScriptProject project,
IPath containerPath) throws ModelException {
IBuildpathContainer container = null;
final BuildpathContainerInitializer initializer = DLTKCore
.getBuildpathContainerInitializer(containerPath.segment(0));
if (initializer != null) {
if (BP_RESOLVE_VERBOSE) {
verbose_triggering_container_initialization(project,
containerPath, initializer);
}
// PerformanceStats stats = null;
containerPut(project, containerPath,
CONTAINER_INITIALIZATION_IN_PROGRESS); // avoid
// initialization
// cycles
boolean ok = false;
try {
// let OperationCanceledException go through
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59363)
initializer.initialize(containerPath, project);
// retrieve value (if initialization was successful)
container = containerGet(project, containerPath);
if (container == CONTAINER_INITIALIZATION_IN_PROGRESS)
return null; // break cycle
ok = true;
} catch (CoreException e) {
if (e instanceof ModelException) {
throw (ModelException) e;
} else {
throw new ModelException(e);
}
} catch (RuntimeException e) {
if (ModelManager.BP_RESOLVE_VERBOSE) {
e.printStackTrace();
}
throw e;
} catch (Error e) {
if (ModelManager.BP_RESOLVE_VERBOSE) {
e.printStackTrace();
}
throw e;
} finally {
if (!ok) {
// just remove initialization in progress and keep previous
// session container so as to avoid a full build
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=92588
containerRemoveInitializationInProgress(project,
containerPath);
if (BP_RESOLVE_VERBOSE) {
verbose_container_initialization_failed(project,
containerPath, container, initializer);
}
}
}
if (BP_RESOLVE_VERBOSE) {
verbose_container_value_after_initialization(project,
containerPath, container);
}
} else {
if (BP_RESOLVE_VERBOSE) {
Util.verbose("CPContainer INIT - no initializer found\n" + //$NON-NLS-1$
" project: " + project.getElementName() + '\n' + //$NON-NLS-1$
" container path: " + containerPath); //$NON-NLS-1$
}
}
return container;
}
private void verbose_triggering_container_initialization(
IScriptProject project, IPath containerPath,
final BuildpathContainerInitializer initializer) {
Util.verbose("BPContainer INIT - triggering initialization\n" + //$NON-NLS-1$
" project: " + project.getElementName() + '\n' + //$NON-NLS-1$
" container path: " + containerPath + '\n' + //$NON-NLS-1$
" initializer: " + initializer + '\n' + //$NON-NLS-1$
" invocation stack trace:"); //$NON-NLS-1$
new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
}
private void verbose_container_value_after_initialization(
IScriptProject project, IPath containerPath,
IBuildpathContainer container) {
StringBuffer buffer = new StringBuffer();
buffer.append("CPContainer INIT - after resolution\n"); //$NON-NLS-1$
buffer.append(" project: " + project.getElementName() + '\n'); //$NON-NLS-1$
buffer.append(" container path: " + containerPath + '\n'); //$NON-NLS-1$
if (container != null) {
buffer.append(" container: " + container.getDescription() + " {\n"); //$NON-NLS-2$//$NON-NLS-1$
IBuildpathEntry[] entries = container.getBuildpathEntries();
if (entries != null) {
for (int i = 0; i < entries.length; i++) {
buffer.append(" " + entries[i] + '\n'); //$NON-NLS-1$
}
}
buffer.append(" }");//$NON-NLS-1$
} else {
buffer.append(" container: {unbound}");//$NON-NLS-1$
}
Util.verbose(buffer.toString());
}
private void verbose_container_initialization_failed(IScriptProject project,
IPath containerPath, IBuildpathContainer container,
final BuildpathContainerInitializer initializer) {
if (container == CONTAINER_INITIALIZATION_IN_PROGRESS) {
Util.verbose(
"CPContainer INIT - FAILED (initializer did not initialize container)\n" //$NON-NLS-1$
+ " project: " + project.getElementName() + '\n'
+ " container path: " //$NON-NLS-1$
+ containerPath + '\n' + " initializer: " //$NON-NLS-1$
+ initializer);
} else {
Util.verbose("CPContainer INIT - FAILED (see exception above)\n" + //$NON-NLS-1$
" project: " //$NON-NLS-1$
+ project.getElementName() + '\n' + " container path: " //$NON-NLS-1$
+ containerPath + '\n' + " initializer: " + initializer); //$NON-NLS-1$
}
}
/**
* Returns a persisted container from previous session if any. Note that it
* is not the original container from previous session (i.e. it did not get
* serialized) but rather a summary of its entries recreated for CP
* initialization purpose. As such it should not be stored into container
* caches.
*/
public IBuildpathContainer getPreviousSessionContainer(IPath containerPath,
IScriptProject project) {
Map previousContainerValues = (Map) this.previousSessionContainers
.get(project);
if (previousContainerValues != null) {
IBuildpathContainer previousContainer = (IBuildpathContainer) previousContainerValues
.get(containerPath);
if (previousContainer != null) {
if (ModelManager.BP_RESOLVE_VERBOSE) {
StringBuffer buffer = new StringBuffer();
buffer.append(
"CPContainer INIT - reentering access to project container during its initialization, will see previous value\n"); //$NON-NLS-1$
buffer.append(
" project: " + project.getElementName() + '\n'); //$NON-NLS-1$
buffer.append(" container path: " + containerPath + '\n'); //$NON-NLS-1$
buffer.append(" previous value: "); //$NON-NLS-1$
buffer.append(previousContainer.getDescription());
buffer.append(" {\n"); //$NON-NLS-1$
IBuildpathEntry[] entries = previousContainer
.getBuildpathEntries();
if (entries != null) {
for (int j = 0; j < entries.length; j++) {
buffer.append(" "); //$NON-NLS-1$
buffer.append(entries[j]);
buffer.append('\n');
}
}
buffer.append(" }"); //$NON-NLS-1$
Util.verbose(buffer.toString());
new Exception("<Fake exception>") //$NON-NLS-1$
.printStackTrace(System.out);
}
return previousContainer;
}
}
return null; // break cycle if none found
}
/**
* Returns a persisted container from previous session if any
*/
public IPath getPreviousSessionVariable(String variableName) {
IPath previousPath = (IPath) this.previousSessionVariables
.get(variableName);
if (previousPath != null) {
// if (BP_RESOLVE_VERBOSE_ADVANCED)
// verbose_reentering_variable_access(variableName, previousPath);
return previousPath;
}
return null; // break cycle
}
public synchronized void containerPut(IScriptProject project,
IPath containerPath, IBuildpathContainer container) {
// set/unset the initialization in progress
if (container == CONTAINER_INITIALIZATION_IN_PROGRESS) {
HashSet projectInitializations = containerInitializationInProgress(
project);
projectInitializations.add(containerPath);
// do not write out intermediate initialization value
return;
} else {
containerRemoveInitializationInProgress(project, containerPath);
Map projectContainers = (Map) this.containers.get(project);
if (projectContainers == null) {
projectContainers = new HashMap(1);
this.containers.put(project, projectContainers);
}
if (container == null) {
projectContainers.remove(containerPath);
} else {
projectContainers.put(containerPath, container);
}
// discard obsoleted information about previous session
Map previousContainers = (Map) this.previousSessionContainers
.get(project);
if (previousContainers != null) {
previousContainers.remove(containerPath);
}
}
// container values are persisted in preferences during save operations,
// see #saving(ISaveContext)
}
/*
* The given project is being removed. Remove all containers for this
* project from the cache.
*/
public synchronized void containerRemove(IScriptProject project) {
Map initializations = (Map) this.containerInitializationInProgress
.get();
if (initializations != null) {
initializations.remove(project);
}
this.containers.remove(project);
}
void verbose_missbehaving_container(IScriptProject project,
IPath containerPath, IBuildpathEntry[] classpathEntries) {
Util.verbose(
"CPContainer GET - missbehaving container (returning null classpath entry)\n" //$NON-NLS-1$
+ " project: " + project.getElementName() + '\n'
+ " container path: " + containerPath + '\n'
+ " classpath entries: {\n"
+ Util.toString(classpathEntries, o -> {
StringBuffer buffer = new StringBuffer(" "); //$NON-NLS-1$
if (o == null) {
buffer.append("<null>"); //$NON-NLS-1$
return buffer.toString();
}
buffer.append(o);
return buffer.toString();
}) + "\n }" //$NON-NLS-1$
);
}
void verbose_missbehaving_container_null_entries(IScriptProject project,
IPath containerPath) {
Util.verbose(
"CPContainer GET - missbehaving container (returning null as classpath entries)\n" //$NON-NLS-1$
+ " project: " + project.getElementName() + '\n'
+ " container path: " + containerPath + '\n'
+ " classpath entries: <null>");
}
private void containerRemoveInitializationInProgress(IScriptProject project,
IPath containerPath) {
HashSet projectInitializations = containerInitializationInProgress(
project);
projectInitializations.remove(containerPath);
if (projectInitializations.size() == 0) {
Map initializations = (Map) this.containerInitializationInProgress
.get();
initializations.remove(project);
}
}
/*
* Optimize startup case where a container for 1 project is initialized at a
* time with the same entries as on shutdown.
*/
public boolean containerPutIfInitializingWithSameEntries(
IPath containerPath, IScriptProject[] projects,
IBuildpathContainer[] respectiveContainers) {
int projectLength = projects.length;
if (projectLength != 1)
return false;
final IBuildpathContainer container = respectiveContainers[0];
if (container == null)
return false;
final IScriptProject project = projects[0];
if (!containerInitializationInProgress(project).contains(containerPath))
return false;
IBuildpathContainer previousSessionContainer = getPreviousSessionContainer(
containerPath, project);
final IBuildpathEntry[] newEntries = container.getBuildpathEntries();
if (previousSessionContainer == null)
if (newEntries.length == 0) {
containerPut(project, containerPath, container);
return true;
} else {
return false;
}
final IBuildpathEntry[] oldEntries = previousSessionContainer
.getBuildpathEntries();
if (oldEntries.length != newEntries.length)
return false;
for (int i = 0, length = newEntries.length; i < length; i++) {
if (!newEntries[i].equals(oldEntries[i])) {
if (BP_RESOLVE_VERBOSE) {
Util.verbose("CPContainer SET - missbehaving container\n" + //$NON-NLS-1$
" container path: " //$NON-NLS-1$
+ containerPath + '\n' + " projects: {" //$NON-NLS-1$
+ Util.toString(projects,
o -> ((IScriptProject) o).getElementName())
+ "}\n values on previous session: {\n" + //$NON-NLS-1$
Util.toString(respectiveContainers, o -> {
StringBuffer buffer = new StringBuffer(
" "); //$NON-NLS-1$
if (o == null) {
buffer.append("<null>"); //$NON-NLS-1$
return buffer.toString();
}
buffer.append(container.getDescription());
buffer.append(" {\n"); //$NON-NLS-1$
for (int j = 0; j < oldEntries.length; j++) {
buffer.append(" "); //$NON-NLS-1$
buffer.append(oldEntries[j]);
buffer.append('\n');
}
buffer.append(" }"); //$NON-NLS-1$
return buffer.toString();
}) + "}\n new values: {\n" + //$NON-NLS-1$
Util.toString(respectiveContainers, o -> {
StringBuffer buffer = new StringBuffer(
" "); //$NON-NLS-1$
if (o == null) {
buffer.append("<null>"); //$NON-NLS-1$
return buffer.toString();
}
buffer.append(container.getDescription());
buffer.append(" {\n"); //$NON-NLS-1$
for (int j = 0; j < newEntries.length; j++) {
buffer.append(" "); //$NON-NLS-1$
buffer.append(newEntries[j]);
buffer.append('\n');
}
buffer.append(" }"); //$NON-NLS-1$
return buffer.toString();
}) + "\n }"); //$NON-NLS-1$
}
return false;
}
}
containerPut(project, containerPath, container);
return true;
}
/**
* Returns the open ZipFile at the given path. If the ZipFile does not yet
* exist, it is created, opened, and added to the cache of open ZipFiles.
*
* NOTE: closeZipFile() must be called for the resulting ZipFile, when the
* client is done using it.
*
* The path must be a file system path if representing an external zip, or
* it must be an absolute workspace relative path if representing a zip
* inside the workspace.
*
* @param archiveProjectFragment
*
* @exception CoreException
* If unable to create/open the ZipFile
*/
public IArchive getArchive(IPath path,
IProjectFragment archiveProjectFragment) throws CoreException {
Map<IPath, IArchive> map;
IArchive zipFile;
if ((map = this.zipFiles.get()) != null
&& (zipFile = map.get(path)) != null) {
return zipFile;
}
File localFile = null;
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IResource file = root.findMember(path);
if (file != null) {
// internal resource
URI location;
if (file.getType() != IResource.FILE
|| (location = file.getLocationURI()) == null) {
throw new CoreException(new Status(IStatus.ERROR,
DLTKCore.PLUGIN_ID, -1,
Messages.bind(Messages.file_notFound, path.toString()),
null));
}
localFile = Util.toLocalFile(location,
null/*
* no progress availaible
*/);
if (localFile == null)
throw new CoreException(new Status(IStatus.ERROR,
DLTKCore.PLUGIN_ID, -1,
Messages.bind(Messages.file_notFound, path.toString()),
null));
} else {
// external resource -> it is ok to use toFile()
if (EnvironmentPathUtils.isFull(path)) {
path = EnvironmentPathUtils.getLocalPath(path);
}
localFile = path.toFile();
}
try {
if (ZIP_ACCESS_VERBOSE) {
System.out.println("(" + Thread.currentThread() //$NON-NLS-1$
+ ") [ModelManager.getZipFile(IPath)] Creating ZipFile on " //$NON-NLS-1$
+ localFile);
}
zipFile = openArchive(archiveProjectFragment, localFile);
if (map != null) {
map.put(path, zipFile);
}
return zipFile;
} catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR,
DLTKCore.PLUGIN_ID, -1, Messages.status_IOException, e));
}
}
public IArchive openArchive(IProjectFragment archiveProjectFragment,
File localFile) throws IOException {
final IDLTKLanguageToolkit toolkit = DLTKLanguageManager
.getLanguageToolkit(archiveProjectFragment);
if (toolkit != null) {
return toolkit.openArchive(localFile);
} else {
return new ZipArchiveFile(localFile);
}
}
/**
* Starts caching ZipFiles. Ignores if there are already clients.
*/
public void cacheZipFiles() {
if (this.zipFiles.get() != null)
return;
this.zipFiles.set(new HashMap<IPath, IArchive>());
}
public void closeArchive(IArchive zipFile) {
if (zipFile == null)
return;
if (this.zipFiles.get() != null) {
return; // zip file will be closed by call to flushZipFiles
}
try {
if (ModelManager.ZIP_ACCESS_VERBOSE) {
System.out.println("(" + Thread.currentThread() //$NON-NLS-1$
+ ") [ModelManager.closeZipFile(ZipFile)] Closing ZipFile on " //$NON-NLS-1$
+ zipFile.getName());
}
zipFile.close();
} catch (IOException e) {
// problem occured closing zip file: cannot do much more
}
}
@Override
public void doneSaving(ISaveContext context) {
// nothing
}
@Override
public void prepareToSave(ISaveContext context) throws CoreException {
// TODO Auto-generated method stub
}
@Override
public void rollback(ISaveContext context) {
// TODO Auto-generated method stub
}
private void traceContainers(String action, long start) {
Long delta = System.currentTimeMillis() - start;
Long length = getContainersFile().length();
String pattern = "{0} {1} bytes in containers.dat in {2}ms"; //$NON-NLS-1$
String message = NLS.bind(pattern,
new Object[] { action, length, delta });
System.out.println(message);
}
@Override
public void saving(ISaveContext context) throws CoreException {
// save variable and container values on snapshot/full save
long start = -1;
if (VERBOSE)
start = System.currentTimeMillis();
savesContainers();
if (VERBOSE)
traceContainers("Saved", start); //$NON-NLS-1$
if (context.getKind() == ISaveContext.FULL_SAVE) {
// will need delta since this save (see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=38658)
context.needDelta();
// clean up indexes on workspace full save
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52347)
IndexManager manager = this.indexManager;
if (manager != null
// don't force initialization of workspace scope as we could
// be
// shutting down
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=93941)
&& this.workspaceScope != null) {
manager.cleanUpIndexes();
}
}
IProject savedProject = context.getProject();
if (savedProject != null) {
if (!ScriptProject.hasScriptNature(savedProject))
return; // ignore
PerProjectInfo info = getPerProjectInfo(savedProject,
true /*
* create info
*/);
saveState(info, context);
info.rememberExternalLibTimestamps();
return;
}
ArrayList vStats = null; // lazy initialized
ArrayList values = null;
synchronized (this.perProjectInfos) {
values = new ArrayList(this.perProjectInfos.values());
}
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
try {
PerProjectInfo info = (PerProjectInfo) iterator.next();
saveState(info, context);
info.rememberExternalLibTimestamps();
} catch (CoreException e) {
if (vStats == null)
vStats = new ArrayList();
vStats.add(e.getStatus());
}
}
if (vStats != null) {
IStatus[] stats = new IStatus[vStats.size()];
vStats.toArray(stats);
throw new CoreException(
new MultiStatus(DLTKCore.PLUGIN_ID, IStatus.ERROR, stats,
Messages.build_cannotSaveStates, null));
}
// save external libs timestamps
this.deltaState.saveExternalLibTimeStamps();
}
private File getContainersFile() {
return DLTKCore.getPlugin().getStateLocation().append("Containers.dat") //$NON-NLS-1$
.toFile();
}
private void saveState(PerProjectInfo info, ISaveContext context)
throws CoreException {
// passed this point, save actions are non trivial
if (context.getKind() == ISaveContext.SNAPSHOT)
return;
// save built state
if (info.triedRead)
saveBuiltState(info);
}
/**
* Saves the built state for the project.
*/
private void saveBuiltState(PerProjectInfo info) throws CoreException {
if (ScriptBuilder.DEBUG)
System.out.println(Messages.bind(Messages.build_saveStateProgress,
info.project.getName()));
File file = getSerializationFile(info.project);
if (file == null)
return;
long t = System.currentTimeMillis();
try {
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(file)));
try {
out.writeUTF(DLTKCore.PLUGIN_ID);
out.writeUTF("STATE"); //$NON-NLS-1$
if (info.savedState == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
ScriptBuilder.writeState(info.savedState, out);
}
} finally {
out.close();
}
} catch (RuntimeException e) {
try {
file.delete();
} catch (SecurityException se) {
// could not delete file: cannot do much more
}
throw new CoreException(new Status(IStatus.ERROR,
DLTKCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
Messages.bind(Messages.build_cannotSaveState,
info.project.getName()),
e));
} catch (IOException e) {
try {
file.delete();
} catch (SecurityException se) {
// could not delete file: cannot do much more
}
throw new CoreException(new Status(IStatus.ERROR,
DLTKCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
Messages.bind(Messages.build_cannotSaveState,
info.project.getName()),
e));
}
if (ScriptBuilder.DEBUG) {
t = System.currentTimeMillis() - t;
System.out.println(Messages.bind(Messages.build_saveStateComplete,
String.valueOf(t)));
}
}
private void savesContainers() throws CoreException {
File file = getContainersFile();
DataOutputStream out = null;
try {
out = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(file)));
out.writeInt(CONTAINERS_FILE_VERSION);
new ContainersSaveHelper(out).save();
// old code retained for performance comparisons
// containers
IScriptProject[] projects = getModel().getScriptProjects();
int length = projects.length;
out.writeInt(length);
for (int i = 0; i < length; i++) {
IScriptProject project = projects[i];
// clone while iterating (see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=59638)
Map projectContainers = containerClone(project);
out.writeUTF(project.getElementName());
if (projectContainers == null) {
out.writeInt(0);
continue;
}
HashMap containersToSave = new HashMap();
for (Iterator iterator = projectContainers.keySet()
.iterator(); iterator.hasNext();) {
IPath containerPath = (IPath) iterator.next();
IBuildpathContainer container = (IBuildpathContainer) projectContainers
.get(containerPath);
String containerString = null;
try {
if (container == null) {
// container has not been initialized yet, use
// previous session value
// (see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=73969
// )
container = getPreviousSessionContainer(
containerPath, project);
}
if (container != null) {
IBuildpathEntry[] entries = container
.getBuildpathEntries();
containerString = ((ScriptProject) project)
.encodeBuildpath(entries, false,
null/*
* not interested in unknown
* elements
*/);
}
} catch (ModelException e) {
// could not encode entry: will not persist
Util.log(e,
"Could not persist container " + containerPath //$NON-NLS-1$
+ " for project " //$NON-NLS-1$
+ project.getElementName());
}
if (containerString != null)
containersToSave.put(containerPath, containerString);
}
out.writeInt(containersToSave.size());
Iterator iterator = containersToSave.keySet().iterator();
while (iterator.hasNext()) {
IPath containerPath = (IPath) iterator.next();
out.writeUTF(containerPath.toPortableString());
String containerString = (String) containersToSave
.get(containerPath);
out.writeInt(containerString.length());
out.writeBytes(containerString);
}
}
} catch (IOException e) {
IStatus status = new Status(IStatus.ERROR, DLTKCore.PLUGIN_ID,
IStatus.ERROR,
"Problems while saving variables and containers", e); //$NON-NLS-1$
throw new CoreException(status);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// nothing we can do: ignore
}
}
}
}
protected synchronized void resetZIPTypeCache() {
this.cache.resetZIPTypeCache();
}
public DLTKWorkspaceScope getWorkspaceScope(IDLTKLanguageToolkit toolkit) {
if (this.workspaceScope == null) {
this.workspaceScope = new HashMap();
}
if (this.workspaceScope.containsKey(toolkit)) {
return (DLTKWorkspaceScope) this.workspaceScope.get(toolkit);
} else {
DLTKWorkspaceScope scope = new DLTKWorkspaceScope(toolkit);
this.workspaceScope.put(toolkit, scope);
return scope;
}
}
private final class ContainersSaveHelper {
private final HashtableOfObjectToInt buildpathEntryIds;
// -> int
private final DataOutputStream out;
private final HashtableOfObjectToInt stringIds; // Strings -> int
ContainersSaveHelper(DataOutputStream out) {
super();
this.buildpathEntryIds = new HashtableOfObjectToInt();
this.out = out;
this.stringIds = new HashtableOfObjectToInt();
}
void save() throws IOException, ModelException {
saveProjects(ModelManager.this.getModel().getScriptProjects());
}
private void saveAccessRule(BuildpathAccessRule rule)
throws IOException {
saveInt(rule.problemId);
savePath(rule.getPattern());
}
private void saveAccessRules(IAccessRule[] rules) throws IOException {
int count = rules == null ? 0 : rules.length;
saveInt(count);
for (int i = 0; i < count; ++i)
saveAccessRule((BuildpathAccessRule) rules[i]);
}
private void saveAttribute(IBuildpathAttribute attribute)
throws IOException {
saveString(attribute.getName());
saveString(attribute.getValue());
}
private void saveAttributes(IBuildpathAttribute[] attributes)
throws IOException {
int count = attributes == null ? 0 : attributes.length;
saveInt(count);
for (int i = 0; i < count; ++i)
saveAttribute(attributes[i]);
}
private void saveBuildpathEntries(IBuildpathEntry[] entries)
throws IOException {
int count = entries == null ? 0 : entries.length;
saveInt(count);
for (int i = 0; i < count; ++i)
saveBuildpathEntry(entries[i]);
}
private void saveBuildpathEntry(IBuildpathEntry entry)
throws IOException {
if (saveNewId(entry, this.buildpathEntryIds)) {
saveInt(entry.getContentKind());
saveInt(entry.getEntryKind());
savePath(entry.getPath());
savePaths(entry.getInclusionPatterns());
savePaths(entry.getExclusionPatterns());
this.out.writeBoolean(entry.isExported());
this.out.writeBoolean(entry.isExternal());
saveAccessRules(entry.getAccessRules());
this.out.writeBoolean(entry.combineAccessRules());
saveAttributes(entry.getExtraAttributes());
}
}
private void saveContainers(IScriptProject project, Map containerMap)
throws IOException {
saveInt(containerMap.size());
for (Iterator i = containerMap.entrySet().iterator(); i
.hasNext();) {
Entry entry = (Entry) i.next();
IPath path = (IPath) entry.getKey();
IBuildpathContainer container = (IBuildpathContainer) entry
.getValue();
IBuildpathEntry[] cpEntries = null;
if (container == null) {
// container has not been initialized yet, use previous
// session value
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=73969)
container = ModelManager.this
.getPreviousSessionContainer(path, project);
}
if (container != null)
cpEntries = container.getBuildpathEntries();
savePath(path);
saveBuildpathEntries(cpEntries);
}
}
private void saveInt(int value) throws IOException {
this.out.writeInt(value);
}
private boolean saveNewId(Object key, HashtableOfObjectToInt map)
throws IOException {
int id = map.get(key);
if (id == -1) {
int newId = map.size();
map.put(key, newId);
saveInt(newId);
return true;
} else {
saveInt(id);
return false;
}
}
private void savePath(IPath path) throws IOException {
if (path == null) {
this.out.writeBoolean(true);
} else {
this.out.writeBoolean(false);
saveString(path.toPortableString());
}
}
private void savePaths(IPath[] paths) throws IOException {
int count = paths == null ? 0 : paths.length;
saveInt(count);
for (int i = 0; i < count; ++i)
savePath(paths[i]);
}
private void saveProjects(IScriptProject[] projects)
throws IOException, ModelException {
int count = projects.length;
saveInt(count);
for (int i = 0; i < count; ++i) {
IScriptProject project = projects[i];
saveString(project.getElementName());
Map containerMap = (Map) ModelManager.this.containers
.get(project);
if (containerMap == null) {
containerMap = Collections.EMPTY_MAP;
} else {
// clone while iterating
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59638)
containerMap = new HashMap(containerMap);
}
saveContainers(project, containerMap);
}
}
private void saveString(String string) throws IOException {
if (saveNewId(string, this.stringIds))
this.out.writeUTF(string);
}
}
private synchronized Map containerClone(IScriptProject project) {
Map originalProjectContainers = (Map) this.containers.get(project);
if (originalProjectContainers == null)
return null;
Map projectContainers = new HashMap(originalProjectContainers.size());
projectContainers.putAll(originalProjectContainers);
return projectContainers;
}
public void loadContainers() throws CoreException {
// backward compatibility, load variables and containers from
// preferences into cache
loadVariablesAndContainers(getDefaultPreferences());
loadVariablesAndContainers(getInstancePreferences());
// load variables and containers from saved file into cache
File file = getContainersFile();
DataInputStream in = null;
try {
in = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)));
switch (in.readInt()) {
case 1:
new ContainersLoadHelper(in).load();
break;
}
} catch (IOException e) {
if (file.exists())
Util.log(e, "Unable to read variable and containers file"); //$NON-NLS-1$
} catch (RuntimeException e) {
if (file.exists())
Util.log(e,
"Unable to read variable and containers file (file is corrupt)"); //$NON-NLS-1$
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// nothing we can do: ignore
}
}
}
// override persisted values for containers which have a registered
// initializer
containersReset(getRegisteredContainerIDs());
}
private void loadVariablesAndContainers(IEclipsePreferences preferences) {
try {
// only get variable from preferences not set to their default
String[] propertyNames = preferences.keys();
int variablePrefixLength = BP_VARIABLE_PREFERENCES_PREFIX.length();
for (int i = 0; i < propertyNames.length; i++) {
String propertyName = propertyNames[i];
if (propertyName.startsWith(BP_VARIABLE_PREFERENCES_PREFIX)) {
String varName = propertyName
.substring(variablePrefixLength);
String propertyValue = preferences.get(propertyName, null);
if (propertyValue != null) {
String pathString = propertyValue.trim();
if (BP_ENTRY_IGNORE.equals(pathString)) {
// cleanup old preferences
preferences.remove(propertyName);
continue;
}
// add variable to table
IPath varPath = new Path(pathString);
this.variables.put(varName, varPath);
this.previousSessionVariables.put(varName, varPath);
}
} else if (propertyName
.startsWith(BP_CONTAINER_PREFERENCES_PREFIX)) {
String propertyValue = preferences.get(propertyName, null);
if (propertyValue != null) {
// cleanup old preferences
preferences.remove(propertyName);
// recreate container
recreatePersistedContainer(propertyName, propertyValue,
true/* add to container values */);
}
}
}
} catch (BackingStoreException e1) {
// TODO (frederic) see if it's necessary to report this failure...
}
}
private static final class PersistedBuildpathContainer
implements IBuildpathContainer {
private final IPath containerPath;
private final IBuildpathEntry[] entries;
private final IScriptProject project;
PersistedBuildpathContainer(IScriptProject project, IPath containerPath,
IBuildpathEntry[] entries) {
super();
this.containerPath = containerPath;
this.entries = entries;
this.project = project;
}
@Override
public IBuildpathEntry[] getBuildpathEntries() {
return entries;
}
@Override
public String getDescription() {
return "Persisted container [" + containerPath //$NON-NLS-1$
+ " for project [" + project.getElementName() //$NON-NLS-1$
+ "]]"; //$NON-NLS-1$
}
@Override
public int getKind() {
return 0;
}
@Override
public IPath getPath() {
return containerPath;
}
@Override
public String toString() {
return getDescription();
}
}
private final class ContainersLoadHelper {
private static final int ARRAY_INCREMENT = 200;
private IBuildpathEntry[] allBuildpathEntries;
private int allBuildpathEntryCount;
private final Map allPaths; // String -> IPath
private String[] allStrings;
private int allStringsCount;
private final DataInputStream in;
ContainersLoadHelper(DataInputStream in) {
super();
this.allBuildpathEntries = null;
this.allBuildpathEntryCount = 0;
this.allPaths = new HashMap();
this.allStrings = null;
this.allStringsCount = 0;
this.in = in;
}
void load() throws IOException {
loadProjects(ModelManager.this.getModel());
}
private IAccessRule loadAccessRule() throws IOException {
int problemId = loadInt();
IPath pattern = loadPath();
return new BuildpathAccessRule(pattern.toString().toCharArray(),
problemId);
}
private IAccessRule[] loadAccessRules() throws IOException {
int count = loadInt();
if (count == 0)
return BuildpathEntry.NO_ACCESS_RULES;
IAccessRule[] rules = new IAccessRule[count];
for (int i = 0; i < count; ++i)
rules[i] = loadAccessRule();
return rules;
}
private IBuildpathAttribute loadAttribute() throws IOException {
String name = loadString();
String value = loadString();
return new BuildpathAttribute(name, value);
}
private IBuildpathAttribute[] loadAttributes() throws IOException {
int count = loadInt();
if (count == 0)
return BuildpathEntry.NO_EXTRA_ATTRIBUTES;
IBuildpathAttribute[] attributes = new IBuildpathAttribute[count];
for (int i = 0; i < count; ++i)
attributes[i] = loadAttribute();
return attributes;
}
private boolean loadBoolean() throws IOException {
return this.in.readBoolean();
}
private IBuildpathEntry[] loadBuildpathEntries() throws IOException {
int count = loadInt();
IBuildpathEntry[] entries = new IBuildpathEntry[count];
for (int i = 0; i < count; ++i)
entries[i] = loadBuildpathEntry();
return entries;
}
private IBuildpathEntry loadBuildpathEntry() throws IOException {
int id = loadInt();
if (id < 0 || id > this.allBuildpathEntryCount)
throw new IOException("Unexpected buildpathentry id"); //$NON-NLS-1$
if (id < this.allBuildpathEntryCount)
return this.allBuildpathEntries[id];
int contentKind = loadInt();
int entryKind = loadInt();
IPath path = loadPath();
IPath[] inclusionPatterns = loadPaths();
IPath[] exclusionPatterns = loadPaths();
boolean isExported = loadBoolean();
boolean isExternal = loadBoolean();
IAccessRule[] accessRules = loadAccessRules();
boolean combineAccessRules = loadBoolean();
IBuildpathAttribute[] extraAttributes = loadAttributes();
IBuildpathEntry entry = new BuildpathEntry(contentKind, entryKind,
path, isExported, inclusionPatterns, exclusionPatterns,
accessRules, combineAccessRules, extraAttributes,
isExternal);
IBuildpathEntry[] array = this.allBuildpathEntries;
if (array == null || id == array.length) {
array = new IBuildpathEntry[id + ARRAY_INCREMENT];
if (id != 0)
System.arraycopy(this.allBuildpathEntries, 0, array, 0, id);
this.allBuildpathEntries = array;
}
array[id] = entry;
this.allBuildpathEntryCount = id + 1;
return entry;
}
private void loadContainers(IScriptProject project) throws IOException {
boolean projectIsAccessible = project.getProject().isAccessible();
int count = loadInt();
for (int i = 0; i < count; ++i) {
IPath path = loadPath();
IBuildpathEntry[] entries = loadBuildpathEntries();
if (!projectIsAccessible)
// avoid leaking deleted project's persisted container,
// but still read the container as it is is part of the file
// format
continue;
IBuildpathContainer container = new PersistedBuildpathContainer(
project, path, entries);
ModelManager.this.containerPut(project, path, container);
Map oldContainers = (Map) ModelManager.this.previousSessionContainers
.get(project);
if (oldContainers == null) {
oldContainers = new HashMap();
ModelManager.this.previousSessionContainers.put(project,
oldContainers);
}
oldContainers.put(path, container);
}
}
private int loadInt() throws IOException {
return this.in.readInt();
}
private IPath loadPath() throws IOException {
if (loadBoolean())
return null;
String portableString = loadString();
IPath path = (IPath) this.allPaths.get(portableString);
if (path == null) {
path = Path.fromPortableString(portableString);
this.allPaths.put(portableString, path);
}
return path;
}
private IPath[] loadPaths() throws IOException {
int count = loadInt();
IPath[] pathArray = new IPath[count];
for (int i = 0; i < count; ++i)
pathArray[i] = loadPath();
return pathArray;
}
private void loadProjects(IScriptModel model) throws IOException {
int count = loadInt();
for (int i = 0; i < count; ++i) {
String projectName = loadString();
loadContainers(model.getScriptProject(projectName));
}
}
private String loadString() throws IOException {
int id = loadInt();
if (id < 0 || id > this.allStringsCount)
throw new IOException("Unexpected string id"); //$NON-NLS-1$
if (id < this.allStringsCount)
return this.allStrings[id];
String string = this.in.readUTF();
String[] array = this.allStrings;
if (array == null || id == array.length) {
array = new String[id + ARRAY_INCREMENT];
if (id != 0)
System.arraycopy(this.allStrings, 0, array, 0, id);
this.allStrings = array;
}
array[id] = string;
this.allStringsCount = id + 1;
return string;
}
}
/**
* Returns the name of the container IDs for which an CP container
* initializer is registered through an extension point
*/
public static String[] getRegisteredContainerIDs() {
Plugin dltkCorePlugin = DLTKCore.getPlugin();
if (dltkCorePlugin == null)
return null;
ArrayList containerIDList = new ArrayList(5);
IExtensionPoint extension = Platform.getExtensionRegistry()
.getExtensionPoint(DLTKCore.PLUGIN_ID,
ModelManager.BPCONTAINER_INITIALIZER_EXTPOINT_ID);
if (extension != null) {
IExtension[] extensions = extension.getExtensions();
for (int i = 0; i < extensions.length; i++) {
IConfigurationElement[] configElements = extensions[i]
.getConfigurationElements();
for (int j = 0; j < configElements.length; j++) {
String idAttribute = configElements[j].getAttribute("id"); //$NON-NLS-1$
if (idAttribute != null)
containerIDList.add(idAttribute);
}
}
}
String[] containerIDs = new String[containerIDList.size()];
containerIDList.toArray(containerIDs);
return containerIDs;
}
private synchronized void containersReset(String[] containerIDs) {
for (int i = 0; i < containerIDs.length; i++) {
String containerID = containerIDs[i];
Iterator projectIterator = this.containers.keySet().iterator();
while (projectIterator.hasNext()) {
IScriptProject project = (IScriptProject) projectIterator
.next();
Map projectContainers = (Map) this.containers.get(project);
if (projectContainers != null) {
Iterator containerIterator = projectContainers.keySet()
.iterator();
while (containerIterator.hasNext()) {
IPath containerPath = (IPath) containerIterator.next();
if (containerPath.segment(0).equals(containerID)) { // registered
// container
projectContainers.put(containerPath, null); // reset
// container
// value,
// but
// leave
// entry
// in
// Map
}
}
}
}
}
}
/**
* Flushes ZipFiles cache if there are no more clients.
*/
public void flushZipFiles() {
Thread currentThread = Thread.currentThread();
Map<IPath, IArchive> map = this.zipFiles.get();
if (map == null)
return;
this.zipFiles.set(null);
for (IArchive zipFile : map.values()) {
try {
if (ModelManager.ZIP_ACCESS_VERBOSE) {
System.out.println("(" + currentThread //$NON-NLS-1$
+ ") [ModelManager.flushZipFiles()] Closing ZipFile on " //$NON-NLS-1$
+ zipFile.getName());
}
zipFile.close();
} catch (IOException e) {
// problem occured closing zip file: cannot do much more
}
}
}
private SourceModuleInfoCache sourceModuleInfoCache = null;
public ISourceModuleInfoCache getSourceModuleInfoCache() {
return sourceModuleInfoCache;
}
public static UserLibraryManager getUserLibraryManager() {
if (MANAGER.userLibraryManager == null) {
UserLibraryManager libraryManager = new UserLibraryManager();
synchronized (MANAGER) {
if (MANAGER.userLibraryManager == null) { // ensure another
// library manager
// was not set while
// creating the
// instance above
MANAGER.userLibraryManager = libraryManager;
}
}
}
return MANAGER.userLibraryManager;
}
}