/**
* Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.core.cache;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.python.pydev.core.FastBufferedReader;
import org.python.pydev.core.ModulesKey;
import org.python.pydev.core.ModulesKeyForZip;
import org.python.pydev.core.ObjectsInternPool;
import org.python.pydev.core.ObjectsInternPool.ObjectsPoolMap;
import org.python.pydev.shared_core.io.FileUtils;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;
/**
* All this cache does is keep a map with the names of the modules we know and its last modification time.
* Afterwards it's used to check if our indexes are consistent by comparing with the actual filesystem data.
*/
public final class DiskCache {
private static final boolean DEBUG = false;
public static final int VERSION = 2;
private final Object lock = new Object();
/**
* This is the folder that the cache can use to persist its values (TODO: use lucene to index).
*/
private String folderToPersist;
/**
* The keys will be in memory all the time... only the values will come and go to the disk.
*/
private Map<CompleteIndexKey, CompleteIndexKey> keys = new HashMap<CompleteIndexKey, CompleteIndexKey>();
/**
* Writes this cache in a format that may later be restored with loadFrom.
*/
public void writeTo(FastStringBuffer tempBuf) {
tempBuf.append("-- START DISKCACHE_" + DiskCache.VERSION + "\n");
tempBuf.append(folderToPersist);
tempBuf.append('\n');
for (CompleteIndexKey key : keys.values()) {
ModulesKey modKey = key.key;
tempBuf.append(modKey.name);
tempBuf.append('|');
tempBuf.append(key.lastModified);
tempBuf.append('|');
if (modKey.file != null) {
tempBuf.append(modKey.file.toString());
} else {
//could be null!
}
if (modKey instanceof ModulesKeyForZip) {
ModulesKeyForZip modulesKeyForZip = (ModulesKeyForZip) modKey;
tempBuf.append('|');
tempBuf.append(modulesKeyForZip.zipModulePath);
tempBuf.append('|');
tempBuf.append(modulesKeyForZip.isFile ? '0' : '1');
}
tempBuf.append('\n');
}
tempBuf.append("-- END DISKCACHE\n");
}
/**
* Loads from a reader a string that was acquired from writeTo.
* @param objectsPoolMap
*/
public static DiskCache loadFrom(FastBufferedReader reader, ObjectsPoolMap objectsPoolMap) throws IOException {
DiskCache diskCache = new DiskCache();
FastStringBuffer line = reader.readLine();
if (line.startsWith("-- ")) {
throw new RuntimeException("Unexpected line: " + line);
}
diskCache.folderToPersist = line.toString();
FastStringBuffer buf = new FastStringBuffer();
CompleteIndexKey key = null;
char[] internalCharsArray = line.getInternalCharsArray();
while (true) {
line = reader.readLine();
key = null;
if (line == null || line.startsWith("-- ")) {
if (line != null && line.startsWith("-- END DISKCACHE")) {
return diskCache;
}
throw new RuntimeException("Unexpected line: " + line);
} else {
int length = line.length();
int part = 0;
for (int i = 0; i < length; i++) {
char c = internalCharsArray[i];
if (c == '|') {
switch (part) {
case 0:
key = new CompleteIndexKey(
ObjectsInternPool.internLocal(objectsPoolMap, buf.toString()));
diskCache.add(key);
break;
case 1:
key.lastModified = org.python.pydev.shared_core.string.StringUtils
.parsePositiveLong(buf);
break;
case 2:
if (buf.length() > 0) {
key.key.file = new File(
ObjectsInternPool.internLocal(objectsPoolMap, buf.toString()));
}
break;
case 3:
//path in zip
key.key = new ModulesKeyForZip(key.key.name, key.key.file,
ObjectsInternPool.internLocal(objectsPoolMap, buf.toString()), true);
break;
case 4:
//isfile in zip
if (buf.toString().equals(0)) {
((ModulesKeyForZip) key.key).isFile = true;
}
break;
default:
throw new RuntimeException("Unexpected part in line: " + line);
}
part++;
buf.clear();
} else {
buf.append(c);
}
}
// Found end of line... this is the last part and depends on where we stopped previously.
if (buf.length() > 0) {
switch (part) {
case 1:
key.lastModified = StringUtils.parsePositiveLong(buf);
break;
case 2:
//File also written.
key.key.file = new File(ObjectsInternPool.internLocal(objectsPoolMap, buf.toString()));
break;
case 3:
//path in zip
key.key = new ModulesKeyForZip(key.key.name, key.key.file,
ObjectsInternPool.internLocal(objectsPoolMap, buf.toString()), true);
break;
case 4:
//isfile in zip
if (buf.toString().equals(0)) {
((ModulesKeyForZip) key.key).isFile = true;
}
break;
}
buf.clear();
}
}
}
}
private DiskCache() {
//private constructor (only used for internal restore of data).
}
public DiskCache(File folderToPersist, String suffix) {
this();
this.folderToPersist = FileUtils.getFileAbsolutePath(folderToPersist);
}
/**
* Removes both: from the memory and from the disk
*/
public void remove(CompleteIndexKey key) {
synchronized (lock) {
if (DEBUG) {
System.out.println("Disk cache - Removing: " + key);
}
keys.remove(key);
}
}
/**
* Adds to both: the memory and the disk
*/
public void add(CompleteIndexKey key) {
synchronized (lock) {
if (DEBUG) {
System.out.println("Disk cache - Adding: " + key);
}
keys.put(key, key);
}
}
/**
* Clear the whole cache.
*/
public void clear() {
synchronized (lock) {
if (DEBUG) {
System.out.println("Disk cache - clear");
}
keys.clear();
}
}
/**
* @return a copy of the keys available
*/
public Map<CompleteIndexKey, CompleteIndexKey> keys() {
synchronized (lock) {
return new HashMap<CompleteIndexKey, CompleteIndexKey>(keys);
}
}
public void setFolderToPersist(String folderToPersist) {
synchronized (lock) {
File file = new File(folderToPersist);
if (!file.exists()) {
file.mkdirs();
}
if (DEBUG) {
System.out.println("Disk cache - persist :" + folderToPersist);
}
this.folderToPersist = folderToPersist;
}
}
public String getFolderToPersist() {
synchronized (lock) {
return folderToPersist;
}
}
}