/*******************************************************************************
* Copyright (c) 2006 Mountainminds GmbH & Co. KG
* This software is provided under the terms of the Eclipse Public License v1.0
* See http://www.eclipse.org/legal/epl-v10.html.
*
* $Id: StateFiles.java 88 2006-09-18 06:37:36Z mho $
******************************************************************************/
package com.mountainminds.eclemma.internal.core;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import com.mountainminds.eclemma.core.EclEmmaStatus;
/**
* Internal utility to manage files in the plugin's state locations.
*
* @author Marc R. Hoffmann
* @version $Revision: 88 $
*/
public class StateFiles {
private static final String LAUNCHDATA_FOLDER = ".launch/"; //$NON-NLS-1$
private static final String INSTRDATA_FOLDER = ".instr/"; //$NON-NLS-1$
private static final String IMPORTDATA_FOLDER = ".import/"; //$NON-NLS-1$
private static final String SOURCE_FOLDER = ".src/"; //$NON-NLS-1$
private static final ReferenceQueue CLEANUPQUEUE = new ReferenceQueue();
private static final Set CLEANUPFILES = new HashSet();
private final IPath stateLocation;
public StateFiles(IPath stateLocation) {
this.stateLocation = stateLocation;
this.stateLocation.toFile().mkdirs();
getLaunchDataFolder().toFile().mkdirs();
getInstrDataFolder().toFile().mkdirs();
getSourceDataFolder().toFile().mkdirs();
getImportDataFolder().toFile().mkdirs();
}
public void deleteTemporaryFiles() {
deleteFiles(getLaunchDataFolder().toFile(), false);
deleteFiles(getInstrDataFolder().toFile(), false);
deleteFiles(getSourceDataFolder().toFile(), false);
deleteFiles(getImportDataFolder().toFile(), false);
}
private static void deleteFiles(File file, boolean deleteparent) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (int i = 0; files != null && i < files.length; i++) {
deleteFiles(files[i], true);
}
}
if (deleteparent) file.delete();
}
public IPath getLaunchDataFolder() {
return stateLocation.append(LAUNCHDATA_FOLDER);
}
private IPath getInstrDataFolder() {
return stateLocation.append(INSTRDATA_FOLDER);
}
public IPath getInstrDataFolder(IPath location) throws CoreException {
return getInstrDataFolder().append(getInternalId(location, false));
}
private IPath getSourceDataFolder() {
return stateLocation.append(SOURCE_FOLDER);
}
public IPath getSourceFolder(IPath location) throws CoreException {
return getSourceDataFolder().append(getInternalId(location, true));
}
private IPath getImportDataFolder() {
return stateLocation.append(IMPORTDATA_FOLDER);
}
public IPath getImportSessionFile(IPath original) throws CoreException {
IPath p = getImportDataFolder().append(getInternalId(original, true));
registerForCleanup(p);
return p;
}
private static String getInternalId(IPath location, boolean withtimestamp) throws CoreException {
long timestamp = 0;
if (withtimestamp) {
File f = location.toFile();
if (f.exists()) {
timestamp = f.lastModified();
}
}
StringBuffer sb = new StringBuffer();
try {
MessageDigest md = MessageDigest.getInstance("MD5"); //$NON-NLS-1$
md.update(location.toString().getBytes("UTF8")); //$NON-NLS-1$
md.update(Long.toHexString(timestamp).getBytes("UTF8")); //$NON-NLS-1$
byte[] sig = md.digest();
for (int i = 0; i < sig.length; i++) {
sb.append(Character.forDigit((sig[i] >> 4) & 0xf, 0x10));
sb.append(Character.forDigit(sig[i] & 0xf, 0x10));
}
} catch (NoSuchAlgorithmException e) {
throw new CoreException(EclEmmaStatus.ID_CREATION_ERROR.getStatus(e));
} catch (UnsupportedEncodingException e) {
throw new CoreException(EclEmmaStatus.ID_CREATION_ERROR.getStatus(e));
}
return sb.toString();
}
/**
* Registers the file the given path points to for deletion as soon as the
* reference to the path objects gets garbage collected. The caller must
* ensure to hold a reference to the given path object as long as the file
* is required. The file is not required to (jet) actually exist.
*
* @param file path object that points to the file
*/
public void registerForCleanup(IPath file) {
cleanupObsoleteFiles();
CLEANUPFILES.add(new CleanupFile(file));
}
private void cleanupObsoleteFiles() {
while (true) {
CleanupFile f = (CleanupFile) CLEANUPQUEUE.poll();
if (f == null) {
break;
}
CLEANUPFILES.remove(f);
f.delete();
}
}
private static class CleanupFile extends PhantomReference {
private final File file;
public CleanupFile(IPath path) {
super(path, CLEANUPQUEUE);
this.file = path.toFile();
}
public void delete() {
deleteFiles(file, true);
clear();
}
}
}