/**
* 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.
*/
/*
* Created on 14/09/2005
*/
package org.python.pydev.builder.pycremover;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.IDocument;
import org.python.pydev.builder.PyDevBuilderPrefPage;
import org.python.pydev.builder.PyDevBuilderVisitor;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.codecompletion.revisited.PythonPathHelper;
import org.python.pydev.editorinput.PySourceLocatorBase;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.shared_core.callbacks.ICallback0;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.ui.filetypes.FileTypesPreferencesPage;
public class PycHandlerBuilderVisitor extends PyDevBuilderVisitor {
/**
* Job that actually deletes the files.
*/
private static final class PycDeleteJob extends WorkspaceJob {
public PycDeleteJob() {
super("Delete .pyc/$py.class files");
}
private final List<IFile> files = new ArrayList<IFile>();
private final Object lock = new Object();
private void addFilesToDelete(IFile[] files) {
synchronized (lock) {
for (IFile f : files) {
this.files.add(f);
}
}
}
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
IFile[] currentFilesToDelete = null;
synchronized (lock) {
currentFilesToDelete = files.toArray(new IFile[files.size()]);
files.clear();
}
monitor.beginTask("Delete .pyc/$py.class files", currentFilesToDelete.length);
try {
for (final IFile workspaceFile : currentFilesToDelete) {
if (workspaceFile != null && workspaceFile.exists()) {
try {
workspaceFile.delete(true, monitor);
} catch (CoreException e) {
Log.log(e);
}
}
monitor.worked(1);
}
} finally {
monitor.done();
}
return Status.OK_STATUS;
}
}
private static final PycDeleteJob pycDeleteJob = new PycDeleteJob();
private static final PySourceLocatorBase locator = new PySourceLocatorBase();
private int pycDeleteHandling;
@Override
public void visitingWillStart(IProgressMonitor monitor, boolean isFullBuild, IPythonNature nature) {
super.visitingWillStart(monitor, isFullBuild, nature);
pycDeleteHandling = PyDevBuilderPrefPage.getPycDeleteHandling();
}
@Override
public void visitChangedResource(IResource resource, ICallback0<IDocument> document, IProgressMonitor monitor) {
//Ignore: for pyc files we only care about their addition.
}
/**
* When a .pyc/$py.class file is found, we remove it if it doesn't have the correspondent .py or .pyw class.
*/
@Override
public void visitAddedResource(IResource resource, ICallback0<IDocument> document, IProgressMonitor monitor) {
switch (pycDeleteHandling) {
case PyDevBuilderPrefPage.PYC_NEVER_DELETE:
//See: never delete!
return;
case PyDevBuilderPrefPage.PYC_DELETE_WHEN_PY_IS_DELETED:
//We just found a pyc (not a remove from a .py), so, don't delete.
return;
case PyDevBuilderPrefPage.PYC_ALWAYS_DELETE:
//keep on going
}
final String loc = resource.getLocation().toOSString();
if (loc != null && (loc.endsWith(".pyc") || loc.endsWith("$py.class"))) {
String dotPyLoc = null;
final FastStringBuffer buf = new FastStringBuffer(StringUtils.stripExtension(loc), 8);
for (String ext : FileTypesPreferencesPage.getDottedValidSourceFiles()) {
buf.append(ext);
final String bufStr = buf.toString();
File file = new File(bufStr);
if (dotPyLoc == null) {
dotPyLoc = bufStr;
}
if (file.exists()) {
markAsDerived(resource);
return;
}
buf.deleteLastChars(ext.length());
}
//this is needed because this method might be called alone (not in the grouper that checks
//if it is in the pythonpath before)
//
//this happens only when a .pyc file is found... if it was a .py file, this would not be needed (as is the
//case in the visit removed resource)
IPythonNature nature = PythonNature.getPythonNature(resource);
if (nature == null) {
markAsDerived(resource);
return;
}
try {
if (!nature.isResourceInPythonpathProjectSources(dotPyLoc, false)) {
return; // we only analyze resources that are source folders (not external folders)
}
} catch (Exception e) {
Log.log(e);
return;
}
//if still did not return, let's remove it
deletePycFile(loc);
}
}
/**
* We must mark .pyc files as derived.
* @param resource the resource to be marked as derived.
*/
private void markAsDerived(IResource resource) {
try {
resource.setDerived(true);
} catch (CoreException e) {
Log.log(e);
}
}
/**
* When a .py file is removed (which is what we check for), we go on and remove the .pyc file too.
*/
@Override
public void visitRemovedResource(IResource resource, ICallback0<IDocument> document, IProgressMonitor monitor) {
switch (pycDeleteHandling) {
case PyDevBuilderPrefPage.PYC_NEVER_DELETE:
//See: never delete!
return;
}
String loc = resource.getLocation().toOSString();
if (PythonPathHelper.isValidSourceFile(loc)) {
String withoutExt = StringUtils.stripExtension(loc);
deletePycFile(withoutExt + ".pyc");
deletePycFile(withoutExt + "$py.class");
}
}
/**
* Deletes .pyc files
*/
private void deletePycFile(String loc) {
if (loc.endsWith(".pyc") || loc.endsWith("$py.class")) {
try {
File file = new File(loc);
//remove all: file and links
final IFile[] files = locator.getWorkspaceFiles(file);
if (files == null || files.length == 0) {
return;
}
pycDeleteJob.addFilesToDelete(files);
pycDeleteJob.schedule(200);
} catch (Exception e) {
Log.log(e);
}
}
}
}