blob: 313368f5e69812c554bcaad676baf827b558027d [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.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.dltk.annotations.Internal;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ISourceModuleInfoCache;
/**
* Used to cache some source module information. All information related to
* source module are removed, then source module are changed.
*/
public class SourceModuleInfoCache implements ISourceModuleInfoCache,
IResourceChangeListener, IResourceDeltaVisitor {
@Internal
final int capacity = ModelCache.DEFAULT_ROOT_SIZE * 50;
private final ReferenceQueue<ISourceModuleInfo> queue = new ReferenceQueue<>();
@SuppressWarnings("serial")
private final Map<ISourceModule, CacheReference> map = new LinkedHashMap<ISourceModule, CacheReference>(
16, 0.9f, true) {
@Override
protected boolean removeEldestEntry(
Map.Entry<ISourceModule, CacheReference> eldest) {
return size() > capacity;
}
};
private static class CacheReference
extends SoftReference<ISourceModuleInfo> {
final long modificationStamp;
final ISourceModule module;
public CacheReference(ISourceModule module, ISourceModuleInfo referent,
ReferenceQueue<? super ISourceModuleInfo> q) {
super(referent, q);
this.module = module;
this.modificationStamp = getModificationStamp(module);
}
private static long getModificationStamp(ISourceModule module) {
final IResource resource = module.getResource();
return resource != null ? resource.getModificationStamp()
: IResource.NULL_STAMP;
}
public boolean isValid(ISourceModule module) {
final IResource resource = module.getResource();
return resource == null
|| resource.getModificationStamp() == modificationStamp;
}
}
public void start() {
DLTKCore.addPreProcessingResourceChangedListener(this,
IResourceChangeEvent.POST_CHANGE);
}
public void stop() {
DLTKCore.removePreProcessingResourceChangedListener(this);
}
private void expungeStaleEntries() {
for (CacheReference r; (r = (CacheReference) queue.poll()) != null;) {
if (DEBUG) {
System.out.println(
"[Cache] expunge " + r.module.getElementName());
}
map.remove(r.module);
}
}
@Override
public synchronized ISourceModuleInfo get(ISourceModule module) {
expungeStaleEntries();
final CacheReference ref = map.get(module);
if (ref != null) {
final ISourceModuleInfo info = ref.get();
if (info != null && ref.isValid(module)) {
return info;
}
}
final ISourceModuleInfo info = new SourceModuleInfo();
map.put(module, new CacheReference(module, info, queue));
return info;
}
@Override
public synchronized void resourceChanged(IResourceChangeEvent event) {
expungeStaleEntries();
final IResourceDelta delta = event.getDelta();
try {
delta.accept(this);
} catch (CoreException e) {
DLTKCore.error(e);
}
}
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
final int kind = delta.getKind();
if (kind == IResourceDelta.ADDED) {
return false;
}
final IResource resource = delta.getResource();
switch (kind) {
case IResourceDelta.CHANGED:
switch (resource.getType()) {
case IResource.PROJECT:
if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
final IProject project = (IProject) resource;
if (!project.isOpen()) {
removeByProject(project);
return false;
}
}
return true;
case IResource.FOLDER:
return true;
case IResource.FILE:
if ((delta.getFlags() & IResourceDelta.CONTENT) != 0) {
remove((IFile) resource);
}
break;
}
break;
case IResourceDelta.REMOVED:
switch (resource.getType()) {
case IResource.PROJECT:
removeByProject((IProject) resource);
return false;
case IResource.FOLDER:
return true;
case IResource.FILE:
remove((IFile) resource);
return false;
}
}
return true;
}
@Internal
static class SourceModuleInfo implements ISourceModuleInfo {
private Map<Object, Object> map;
@Override
public synchronized Object get(String key) {
if (map == null) {
return null;
}
return map.get(key);
}
@Override
public synchronized void put(String key, Object value) {
if (map == null) {
map = new HashMap<>();
}
map.put(key, value);
}
@Override
public synchronized void remove(String key) {
if (map != null) {
map.remove(key);
}
}
@Override
public synchronized boolean isEmpty() {
return this.map == null || this.map.isEmpty();
}
}
/**
* Not synchronized here, as it's called only from
* {@link #resourceChanged(IResourceChangeEvent)} which is already
* synchronized.
*/
private void removeByProject(IProject project) {
for (Iterator<ISourceModule> i = map.keySet().iterator(); i
.hasNext();) {
final ISourceModule module = i.next();
if (project.equals(module.getScriptProject().getProject())) {
i.remove();
}
}
}
public void remove(IFile file) {
remove(DLTKCore.createSourceModuleFrom(file));
}
@Override
public synchronized void remove(ISourceModule module) {
if (DEBUG) {
System.out.println("[Cache] remove " + module.getElementName()); //$NON-NLS-1$
}
map.remove(module);
}
private static final boolean DEBUG = false;
@Override
public synchronized void clear() {
// clear out reference queue.
while (queue.poll() != null)
;
map.clear();
}
@Override
public synchronized int size() {
return map.size();
}
@Override
public int capacity() {
return capacity;
}
}