blob: 428a41974838853fd93e3359dc42209f41ca057f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2016 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import org.eclipse.cdt.core.IPositionConverter;
import org.eclipse.cdt.core.IPositionTrackerManager;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.IFileBuffer;
import org.eclipse.core.filebuffers.IFileBufferListener;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
public class PositionTrackerManager implements IPositionTrackerManager, IFileBufferListener {
private static final int HASHMAP_ENTRY_SIZE = 56;
private static final int MAX_MEMORY= 1024*512; // 512 kbytes
private static final int MAX_MEMORY_AFTER_CLEANUP= (MAX_MEMORY * 7) / 10; // 70% of MAX_MEMORY
private static PositionTrackerManager sManager= new PositionTrackerManager();
public static PositionTrackerManager getInstance() {
return sManager;
}
private int fMemoryCounter= 0;
private int fInstalled= 0;
/**
* as the key in the map we use:
* the full path for resources,
* the location as path for local non-workspace files,
* the location as URI for non-local non-workspace files.
*/
private HashMap<Object, PositionTrackerChain> fPositionTrackerMap;
private PositionTrackerManager() {
fPositionTrackerMap= new HashMap<Object, PositionTrackerChain>();
}
public synchronized void install() {
if (++fInstalled == 1) {
ITextFileBufferManager mgr= FileBuffers.getTextFileBufferManager();
mgr.addFileBufferListener(this);
}
}
public synchronized void uninstall() {
if (--fInstalled == 0) {
FileBuffers.getTextFileBufferManager().removeFileBufferListener(this);
fPositionTrackerMap.clear();
fMemoryCounter= 0;
}
}
@Override
public void bufferCreated(IFileBuffer buffer) {
if (buffer instanceof ITextFileBuffer) {
createCheckpoint((ITextFileBuffer) buffer);
}
}
@Override
public void bufferDisposed(IFileBuffer buffer) {
if (buffer instanceof ITextFileBuffer) {
resetToLastCheckpoint((ITextFileBuffer) buffer);
}
}
@Override
public void dirtyStateChanged(IFileBuffer buffer, boolean isDirty) {
if (!isDirty && buffer instanceof ITextFileBuffer) {
createCheckpoint((ITextFileBuffer) buffer);
}
}
@Override
public void stateValidationChanged(IFileBuffer buffer, boolean isStateValidated) {
if (isStateValidated && !buffer.isDirty()) {
bufferCreated(buffer);
}
}
@Override
public void bufferContentAboutToBeReplaced(IFileBuffer buffer) {}
@Override
public void bufferContentReplaced(IFileBuffer buffer) {}
@Override
public void underlyingFileMoved(IFileBuffer buffer, IPath path) {}
@Override
public void underlyingFileDeleted(IFileBuffer buffer) {}
@Override
public void stateChangeFailed(IFileBuffer buffer) {}
@Override
public void stateChanging(IFileBuffer buffer) {}
private synchronized void createCheckpoint(ITextFileBuffer buffer) {
final Object bufferKey= getKey(buffer);
PositionTrackerChain chain= fPositionTrackerMap.get(bufferKey);
if (chain == null) {
chain = new PositionTrackerChain(buffer.getModificationStamp());
fPositionTrackerMap.put(bufferKey, chain);
fMemoryCounter+= PositionTrackerChain.MEMORY_SIZE + HASHMAP_ENTRY_SIZE;
} else {
chain.stopTracking();
fMemoryCounter+= chain.createCheckpoint(buffer.getModificationStamp());
}
chain.startTracking(buffer.getDocument());
if (fMemoryCounter > MAX_MEMORY) {
runCleanup();
}
}
private Object getKey(ITextFileBuffer buffer) {
Object key= buffer.getLocation();
if (key == null) {
URI uri= buffer.getFileStore().toURI();
key= URIUtil.toPath(uri);
if (key == null) {
key= uri;
}
}
return key;
}
private synchronized void resetToLastCheckpoint(ITextFileBuffer buffer) {
final Object bufferKey= getKey(buffer);
PositionTrackerChain chain= fPositionTrackerMap.get(bufferKey);
if (chain != null) {
chain.stopTracking();
chain.getActiveTracker().clear();
if (!chain.isModified()) {
fPositionTrackerMap.remove(bufferKey);
chain.dispose();
}
}
}
private synchronized void runCleanup() {
fMemoryCounter= 0;
for (PositionTrackerChain chain : fPositionTrackerMap.values()) {
fMemoryCounter+= HASHMAP_ENTRY_SIZE;
fMemoryCounter+= chain.getMemorySize();
}
if (fMemoryCounter > MAX_MEMORY_AFTER_CLEANUP) {
SortedMap<Long, List<PositionTrackerChain>> map= new TreeMap<Long, List<PositionTrackerChain>>();
for (Iterator<PositionTrackerChain> iter = fPositionTrackerMap.values().iterator(); iter.hasNext();) {
PositionTrackerChain chain = iter.next();
addChain(map, chain);
}
while (!map.isEmpty()) {
Long key= map.firstKey();
List<PositionTrackerChain> list= map.remove(key);
for (Iterator<PositionTrackerChain> iter = list.iterator(); iter.hasNext();) {
PositionTrackerChain chain = iter.next();
fMemoryCounter+= chain.removeOldest();
addChain(map, chain);
}
if (fMemoryCounter <= MAX_MEMORY_AFTER_CLEANUP) {
break;
}
}
}
}
private synchronized void addChain(SortedMap<Long, List<PositionTrackerChain>> map, PositionTrackerChain chain) {
long or= chain.getOldestRetirement();
if (or != Long.MAX_VALUE) {
Long lor= Long.valueOf(or);
List<PositionTrackerChain> list= map.get(lor);
if (list == null) {
list= new LinkedList<PositionTrackerChain>();
map.put(lor, list);
}
list.add(chain);
}
}
@Override
public synchronized IPositionConverter findPositionConverter(IFile file, long timestamp) {
PositionTrackerChain chain= fPositionTrackerMap.get(file.getFullPath());
if (chain != null) {
return chain.findTrackerAt(timestamp);
}
return null;
}
@Override
public synchronized IPositionConverter findPositionConverter(IPath externalLocation, long timestamp) {
PositionTrackerChain chain= fPositionTrackerMap.get(externalLocation);
if (chain != null) {
return chain.findTrackerAt(timestamp);
}
return null;
}
@Override
public synchronized IPositionConverter findPositionConverter(ITranslationUnit tu, long timestamp) {
IFile file= (IFile) tu.getResource();
if (file != null) {
return findPositionConverter(file, timestamp);
}
IPath location= tu.getLocation();
if (location != null) {
return findPositionConverter(location, timestamp);
}
URI locationURI = tu.getLocationURI();
if (locationURI != null) {
return findPositionConverter(locationURI, timestamp);
}
return null;
}
@Override
public synchronized IPositionConverter findPositionConverter(URI locationURI, long timestamp) {
PositionTrackerChain chain= fPositionTrackerMap.get(locationURI);
if (chain == null) {
IPath path= URIUtil.toPath(locationURI);
if (path != null) {
chain= fPositionTrackerMap.get(path);
}
}
if (chain != null) {
return chain.findTrackerAt(timestamp);
}
return null;
}
}