package org.eclipse.jdt.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.util.*;
import java.util.Map.Entry;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.internal.core.DeltaProcessor.RootInfo;
import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
import org.eclipse.jdt.internal.core.nd.indexer.Indexer;
import org.eclipse.jdt.internal.core.nd.indexer.IndexerEvent;
import org.eclipse.jdt.internal.core.nd.java.JavaIndex;
import org.eclipse.jdt.internal.core.util.Util;
public class DeltaProcessingState implements IResourceChangeListener, Indexer.Listener {
public IElementChangedListener[] elementChangedListeners = new IElementChangedListener[5];
public int[] elementChangedListenerMasks = new int[5];
public int elementChangedListenerCount = 0;
public IResourceChangeListener[] preResourceChangeListeners = new IResourceChangeListener[1];
public int[] preResourceChangeEventMasks = new int[1];
public int preResourceChangeListenerCount = 0;
private ThreadLocal<DeltaProcessor> deltaProcessors = new ThreadLocal<>();
public void doNotUse() {
this.deltaProcessors.set(null);
}
public Map<IPath, RootInfo> roots = new LinkedHashMap<>();
public Map<IPath, List<RootInfo>> otherRoots = new HashMap<>();
public Map<IPath, RootInfo> oldRoots = new LinkedHashMap<>();
public Map<IPath, List<RootInfo>> oldOtherRoots = new HashMap<>();
public Map<IPath, IPath> sourceAttachments = new HashMap<>();
public Map<IJavaProject, IJavaProject[]> projectDependencies = new HashMap<>();
public boolean rootsAreStale = true;
private Set<Thread> initializingThreads = Collections.synchronizedSet(new HashSet<>());
public Hashtable<IPath, Long> externalTimeStamps;
private Map<IProject, ClasspathChange> classpathChanges = new LinkedHashMap<>();
private Map<JavaProject, ClasspathValidation> classpathValidations = new LinkedHashMap<>();
private Set<IJavaProject> projectReferenceChanges = new LinkedHashSet<>();
private Map<JavaProject, ExternalFolderChange> externalFolderChanges = new LinkedHashMap<>();
private Set<String> javaProjectNamesCache;
private Set<IJavaElement> externalElementsToRefresh;
public synchronized void addElementChangedListener(IElementChangedListener listener, int eventMask) {
for (int i = 0; i < this.elementChangedListenerCount; i++){
if (this.elementChangedListeners[i] == listener){
int cloneLength = this.elementChangedListenerMasks.length;
System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks = new int[cloneLength], 0, cloneLength);
this.elementChangedListenerMasks[i] |= eventMask;
return;
}
}
int length;
if ((length = this.elementChangedListeners.length) == this.elementChangedListenerCount){
System.arraycopy(this.elementChangedListeners, 0, this.elementChangedListeners = new IElementChangedListener[length*2], 0, length);
System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks = new int[length*2], 0, length);
}
this.elementChangedListeners[this.elementChangedListenerCount] = listener;
this.elementChangedListenerMasks[this.elementChangedListenerCount] = eventMask;
this.elementChangedListenerCount++;
}
public synchronized void addForRefresh(IJavaElement externalElement) {
if (this.externalElementsToRefresh == null) {
this.externalElementsToRefresh = new LinkedHashSet<>();
}
this.externalElementsToRefresh.add(externalElement);
}
public synchronized void addPreResourceChangedListener(IResourceChangeListener listener, int eventMask) {
for (int i = 0; i < this.preResourceChangeListenerCount; i++){
if (this.preResourceChangeListeners[i] == listener) {
this.preResourceChangeEventMasks[i] |= eventMask;
return;
}
}
int length;
if ((length = this.preResourceChangeListeners.length) == this.preResourceChangeListenerCount) {
System.arraycopy(this.preResourceChangeListeners, 0, this.preResourceChangeListeners = new IResourceChangeListener[length*2], 0, length);
System.arraycopy(this.preResourceChangeEventMasks, 0, this.preResourceChangeEventMasks = new int[length*2], 0, length);
}
this.preResourceChangeListeners[this.preResourceChangeListenerCount] = listener;
this.preResourceChangeEventMasks[this.preResourceChangeListenerCount] = eventMask;
this.preResourceChangeListenerCount++;
}
public DeltaProcessor getDeltaProcessor() {
DeltaProcessor deltaProcessor = this.deltaProcessors.get();
if (deltaProcessor != null) return deltaProcessor;
deltaProcessor = new DeltaProcessor(this, JavaModelManager.getJavaModelManager());
this.deltaProcessors.set(deltaProcessor);
return deltaProcessor;
}
public ClasspathChange addClasspathChange(IProject project, IClasspathEntry[] oldRawClasspath, IPath oldOutputLocation, IClasspathEntry[] oldResolvedClasspath) {
synchronized (this.classpathChanges) {
ClasspathChange change = this.classpathChanges.get(project);
if (change == null) {
change = new ClasspathChange((JavaProject) JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(project), oldRawClasspath, oldOutputLocation, oldResolvedClasspath);
this.classpathChanges.put(project, change);
} else {
if (change.oldRawClasspath == null)
change.oldRawClasspath = oldRawClasspath;
if (change.oldOutputLocation == null)
change.oldOutputLocation = oldOutputLocation;
if (change.oldResolvedClasspath == null)
change.oldResolvedClasspath = oldResolvedClasspath;
}
return change;
}
}
public ClasspathChange getClasspathChange(IProject project) {
synchronized (this.classpathChanges) {
return this.classpathChanges.get(project);
}
}
public Map<IProject, ClasspathChange> removeAllClasspathChanges() {
synchronized (this.classpathChanges) {
Map<IProject, ClasspathChange> result = this.classpathChanges;
this.classpathChanges = new LinkedHashMap<>(result.size());
return result;
}
}
public synchronized ClasspathValidation addClasspathValidation(JavaProject project) {
ClasspathValidation validation = this.classpathValidations.get(project);
if (validation == null) {
validation = new ClasspathValidation(project);
this.classpathValidations.put(project, validation);
}
return validation;
}
public synchronized void addExternalFolderChange(JavaProject project, IClasspathEntry[] oldResolvedClasspath) {
ExternalFolderChange change = this.externalFolderChanges.get(project);
if (change == null) {
change = new ExternalFolderChange(project, oldResolvedClasspath);
this.externalFolderChanges.put(project, change);
}
}
public synchronized void addProjectReferenceChange(IJavaProject project) {
this.projectReferenceChanges.add(project);
}
public void initializeRoots(boolean initAfterLoad) {
RootInfos rootInfos = null;
if (this.rootsAreStale) {
Thread currentThread = Thread.currentThread();
boolean addedCurrentThread = false;
try {
if (!this.initializingThreads.add(currentThread)) return;
addedCurrentThread = true;
JavaModelManager.getJavaModelManager().forceBatchInitializations(initAfterLoad);
rootInfos = getRootInfos(false);
} finally {
if (addedCurrentThread) {
this.initializingThreads.remove(currentThread);
}
}
}
synchronized(this) {
this.oldRoots = this.roots;
this.oldOtherRoots = this.otherRoots;
if (this.rootsAreStale && rootInfos != null) {
this.roots = rootInfos.roots;
this.otherRoots = rootInfos.otherRoots;
this.sourceAttachments = rootInfos.sourceAttachments;
this.projectDependencies = rootInfos.projectDependencies;
this.rootsAreStale = false;
}
}
}
synchronized void initializeRootsWithPreviousSession() {
RootInfos rootInfos = getRootInfos(true);
if (rootInfos != null) {
this.roots = rootInfos.roots;
this.otherRoots = rootInfos.otherRoots;
this.sourceAttachments = rootInfos.sourceAttachments;
this.projectDependencies = rootInfos.projectDependencies;
this.rootsAreStale = false;
}
}
private RootInfos getRootInfos(boolean usePreviousSession) {
RootInfos ri = new RootInfos();
IJavaModel model = JavaModelManager.getJavaModelManager().getJavaModel();
IJavaProject[] projects;
try {
projects = model.getJavaProjects();
} catch (JavaModelException e) {
return null;
}
for (int i = 0, length = projects.length; i < length; i++) {
JavaProject project = (JavaProject) projects[i];
IClasspathEntry[] classpath;
try {
if (usePreviousSession) {
PerProjectInfo perProjectInfo = project.getPerProjectInfo();
project.resolveClasspath(perProjectInfo, true, false);
classpath = perProjectInfo.resolvedClasspath;
} else {
classpath = project.getResolvedClasspath();
}
} catch (JavaModelException e) {
continue;
}
for (int j= 0, classpathLength = classpath.length; j < classpathLength; j++) {
IClasspathEntry entry = classpath[j];
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
IJavaProject key = model.getJavaProject(entry.getPath().segment(0));
IJavaProject[] dependents = ri.projectDependencies.get(key);
if (dependents == null) {
dependents = new IJavaProject[] {project};
} else {
int dependentsLength = dependents.length;
System.arraycopy(dependents, 0, dependents = new IJavaProject[dependentsLength+1], 0, dependentsLength);
dependents[dependentsLength] = project;
}
ri.projectDependencies.put(key, dependents);
continue;
}
IPath path = entry.getPath();
if (ri.roots.get(path) == null) {
ri.roots.put(path, new DeltaProcessor.RootInfo(project, path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), entry));
} else {
List<RootInfo> rootList = ri.otherRoots.get(path);
if (rootList == null) {
rootList = new ArrayList<>();
ri.otherRoots.put(path, rootList);
}
rootList.add(new DeltaProcessor.RootInfo(project, path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), entry));
}
if (entry.getEntryKind() != IClasspathEntry.CPE_LIBRARY) continue;
String propertyString = null;
try {
propertyString = Util.getSourceAttachmentProperty(path);
} catch (JavaModelException e) {
e.printStackTrace();
}
IPath sourceAttachmentPath;
if (propertyString != null) {
int index= propertyString.lastIndexOf(PackageFragmentRoot.ATTACHMENT_PROPERTY_DELIMITER);
sourceAttachmentPath = (index < 0) ? new Path(propertyString) : new Path(propertyString.substring(0, index));
} else {
sourceAttachmentPath = entry.getSourceAttachmentPath();
}
if (sourceAttachmentPath != null) {
ri.sourceAttachments.put(sourceAttachmentPath, path);
}
}
}
return ri;
}
public synchronized ClasspathValidation[] removeClasspathValidations() {
int length = this.classpathValidations.size();
if (length == 0) return null;
ClasspathValidation[] validations = new ClasspathValidation[length];
this.classpathValidations.values().toArray(validations);
this.classpathValidations.clear();
return validations;
}
public synchronized ExternalFolderChange[] removeExternalFolderChanges() {
int length = this.externalFolderChanges.size();
if (length == 0) return null;
ExternalFolderChange[] updates = new ExternalFolderChange[length];
this.externalFolderChanges.values().toArray(updates);
this.externalFolderChanges.clear();
return updates;
}
public synchronized Set<IJavaProject> removeProjectReferenceChanges() {
Set<IJavaProject> result = this.projectReferenceChanges;
this.projectReferenceChanges = new HashSet<>();
return result;
}
public synchronized Set<IJavaElement> removeExternalElementsToRefresh() {
Set<IJavaElement> result = this.externalElementsToRefresh;
this.externalElementsToRefresh = null;
return result;
}
public synchronized void removeElementChangedListener(IElementChangedListener listener) {
for (int i = 0; i < this.elementChangedListenerCount; i++){
if (this.elementChangedListeners[i] == listener){
int length = this.elementChangedListeners.length;
IElementChangedListener[] newListeners = new IElementChangedListener[length];
System.arraycopy(this.elementChangedListeners, 0, newListeners, 0, i);
int[] newMasks = new int[length];
System.arraycopy(this.elementChangedListenerMasks, 0, newMasks, 0, i);
int trailingLength = this.elementChangedListenerCount - i - 1;
if (trailingLength > 0){
System.arraycopy(this.elementChangedListeners, i+1, newListeners, i, trailingLength);
System.arraycopy(this.elementChangedListenerMasks, i+1, newMasks, i, trailingLength);
}
this.elementChangedListeners = newListeners;
this.elementChangedListenerMasks = newMasks;
this.elementChangedListenerCount--;
return;
}
}
}
public synchronized void removePreResourceChangedListener(IResourceChangeListener listener) {
for (int i = 0; i < this.preResourceChangeListenerCount; i++){
if (this.preResourceChangeListeners[i] == listener){
int length = this.preResourceChangeListeners.length;
IResourceChangeListener[] newListeners = new IResourceChangeListener[length];
int[] newEventMasks = new int[length];
System.arraycopy(this.preResourceChangeListeners, 0, newListeners, 0, i);
System.arraycopy(this.preResourceChangeEventMasks, 0, newEventMasks, 0, i);
int trailingLength = this.preResourceChangeListenerCount - i - 1;
if (trailingLength > 0) {
System.arraycopy(this.preResourceChangeListeners, i+1, newListeners, i, trailingLength);
System.arraycopy(this.preResourceChangeEventMasks, i+1, newEventMasks, i, trailingLength);
}
this.preResourceChangeListeners = newListeners;
this.preResourceChangeEventMasks = newEventMasks;
this.preResourceChangeListenerCount--;
return;
}
}
}
@Override
public void resourceChanged(final IResourceChangeEvent event) {
for (int i = 0; i < this.preResourceChangeListenerCount; i++) {
final IResourceChangeListener listener = this.preResourceChangeListeners[i];
if ((this.preResourceChangeEventMasks[i] & event.getType()) != 0)
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
Util.log(exception, "Exception occurred in listener of pre Java resource change notification");
}
@Override
public void run() throws Exception {
listener.resourceChanged(event);
}
});
}
try {
getDeltaProcessor().resourceChanged(event);
} finally {
if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
this.deltaProcessors.set(null);
} else {
getDeltaProcessor().overridenEventType = -1;
}
}
}
public Hashtable<IPath, Long> getExternalLibTimeStamps() {
if (this.externalTimeStamps == null) {
Hashtable<IPath, Long> timeStamps = new Hashtable<>();
File timestampsFile = getTimeStampsFile();
DataInputStream in = null;
try {
in = new DataInputStream(new BufferedInputStream(new FileInputStream(timestampsFile)));
int size = in.readInt();
while (size-- > 0) {
String key = in.readUTF();
long timestamp = in.readLong();
timeStamps.put(Path.fromPortableString(key), Long.valueOf(timestamp));
}
} catch (IOException e) {
if (timestampsFile.exists())
Util.log(e, "Unable to read external time stamps");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
this.externalTimeStamps = timeStamps;
}
return this.externalTimeStamps;
}
public IJavaProject findJavaProject(String name) {
if (getOldJavaProjecNames().contains(name))
return JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(name);
return null;
}
public synchronized Set<String> getOldJavaProjecNames() {
if (this.javaProjectNamesCache == null) {
IJavaProject[] projects;
try {
projects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
} catch (JavaModelException e) {
return this.javaProjectNamesCache;
}
HashSet<String> result = new LinkedHashSet<>();
for (int i = 0, length = projects.length; i < length; i++) {
IJavaProject project = projects[i];
result.add(project.getElementName());
}
return this.javaProjectNamesCache = result;
}
return this.javaProjectNamesCache;
}
public synchronized void resetOldJavaProjectNames() {
this.javaProjectNamesCache = null;
}
private File getTimeStampsFile() {
return JavaCore.getPlugin().getStateLocation().append("externalLibsTimeStamps").toFile();
}
public void saveExternalLibTimeStamps() throws CoreException {
if (this.externalTimeStamps == null) return;
HashSet<IPath> toRemove = new HashSet<>();
if (this.roots != null) {
Enumeration<IPath> keys = this.externalTimeStamps.keys();
while (keys.hasMoreElements()) {
IPath key = keys.nextElement();
if (this.roots.get(key) == null) {
toRemove.add(key);
}
}
}
File timestamps = getTimeStampsFile();
DataOutputStream out = null;
try {
out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(timestamps)));
out.writeInt(this.externalTimeStamps.size() - toRemove.size());
Iterator<Entry<IPath, Long>> entries = this.externalTimeStamps.entrySet().iterator();
while (entries.hasNext()) {
Entry<IPath, Long> entry = entries.next();
IPath key = entry.getKey();
if (!toRemove.contains(key)) {
out.writeUTF(key.toPortableString());
Long timestamp = entry.getValue();
out.writeLong(timestamp.longValue());
}
}
} catch (IOException e) {
IStatus status = new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, "Problems while saving timestamps", e);
throw new CoreException(status);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
}
public synchronized void updateRoots(IPath containerPath, IResourceDelta containerDelta, DeltaProcessor deltaProcessor) {
Map<IPath, RootInfo> updatedRoots;
Map<IPath, List<RootInfo>> otherUpdatedRoots;
if (containerDelta.getKind() == IResourceDelta.REMOVED) {
updatedRoots = this.oldRoots;
otherUpdatedRoots = this.oldOtherRoots;
} else {
updatedRoots = this.roots;
otherUpdatedRoots = this.otherRoots;
}
int containerSegmentCount = containerPath.segmentCount();
boolean containerIsProject = containerSegmentCount == 1;
Iterator<Entry<IPath, RootInfo>> iterator = updatedRoots.entrySet().iterator();
while (iterator.hasNext()) {
Entry<IPath, RootInfo> entry = iterator.next();
IPath path = entry.getKey();
if (containerPath.isPrefixOf(path) && !containerPath.equals(path)) {
IResourceDelta rootDelta = containerDelta.findMember(path.removeFirstSegments(containerSegmentCount));
if (rootDelta == null) continue;
DeltaProcessor.RootInfo rootInfo = entry.getValue();
if (!containerIsProject
|| !rootInfo.project.getPath().isPrefixOf(path)) {
deltaProcessor.updateCurrentDeltaAndIndex(rootDelta, IJavaElement.PACKAGE_FRAGMENT_ROOT, rootInfo);
}
List<RootInfo> rootList = otherUpdatedRoots.get(path);
if (rootList != null) {
Iterator<RootInfo> otherProjects = rootList.iterator();
while (otherProjects.hasNext()) {
rootInfo = otherProjects.next();
if (!containerIsProject
|| !rootInfo.project.getPath().isPrefixOf(path)) {
deltaProcessor.updateCurrentDeltaAndIndex(rootDelta, IJavaElement.PACKAGE_FRAGMENT_ROOT, rootInfo);
}
}
}
}
}
}
@Override
public void consume(IndexerEvent event) {
if (JavaIndex.isEnabled()) {
DeltaProcessor processor = getDeltaProcessor();
JavaElementDelta delta = (JavaElementDelta) event.getDelta();
delta.ignoreFromTests = true;
processor.notifyAndFire(delta);
this.deltaProcessors.set(null);
}
}
private static final class RootInfos {
final Map<IPath, RootInfo> roots;
final Map<IPath, List<RootInfo>> otherRoots;
final Map<IPath, IPath> sourceAttachments;
final Map<IJavaProject, IJavaProject[]> projectDependencies;
public RootInfos() {
this.roots = new LinkedHashMap<>();
this.otherRoots = new HashMap<>();
this.sourceAttachments = new HashMap<>();
this.projectDependencies = new HashMap<>();
}
}
}