/** * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved. * Copyright (c) 2013 by Syapse, 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.ui.actions.container; import java.io.ByteArrayInputStream; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.IDocument; import org.python.pydev.core.FileUtilsFileBuffer; import org.python.pydev.core.docutils.SyntaxErrorException; import org.python.pydev.core.log.Log; import org.python.pydev.editor.actions.PyAction; import org.python.pydev.editor.codecompletion.revisited.PythonPathHelper; import org.python.pydev.parser.prettyprinterv2.IFormatter; import org.python.pydev.shared_core.string.StringUtils; import org.python.pydev.shared_ui.utils.RunInUiThread; /** * Abstraction that allos either formatting or organize of imports. * @author Fabio, Jeremy J. Carroll * */ abstract class PyContainerFormatterAction extends PyContainerAction { private final String verbPassed; private final String verbPresent; private final String verbPresentMsg; /** * This class is customized by implementing the {@link #createFormatter()} * message and providing a few strings for message in the constructor. * @param present The activity in present tense (e.g. 'format' 'organize imports') * @param presentMsg The activity in present tense with an object e.g. 'a file' after (e.g. 'format' 'organize imports in') * @param passed The activity in passed tense (e.g. 'formatted' 'organized') */ PyContainerFormatterAction(String present, String presentMsg, String passed) { this.verbPassed = passed; this.verbPresent = present; this.verbPresentMsg = presentMsg; } /** * This is used for doing the source formatting * (only valid after beforeRun() and before afterRun()). */ protected IFormatter formatter; /** * Set with the open files (only valid after beforeRun() and before afterRun()) */ protected Set<IFile> openFiles; abstract IFormatter createFormatter(); /** * Initialize the open files and the formatter to be used. */ @Override protected void beforeRun() { openFiles = PyAction.getOpenFiles(); formatter = createFormatter(); } @Override protected void afterRun(int formatted) { openFiles = null; formatter = null; MessageDialog.openInformation(null, "Files " + verbPassed, StringUtils.format("%s %s files.", StringUtils.getWithFirstUpper(verbPassed), formatted)); } @Override protected boolean confirmRun() { return MessageDialog .openConfirm( null, "Confirm " + verbPresent, "Are you sure that you want to recursively " + verbPresentMsg + " the python files from the selected folder(s)?\n" + "\n" + "It'll be applied to all the file-types specified in the preferences: PyDev > Editor > Code Style > File types.\n" + "\n" + "This action cannot be undone."); } /** * Applies source code formatting to the files... * Recursively pass the folders and delete the files (and sum them so that we know how many * files were formatted). * * @param container the folder from where we want to remove the files * @return the number of files formatted */ @Override protected int doActionOnContainer(IContainer container, IProgressMonitor monitor) { int formatted = 0; try { IResource[] members = container.members(); for (final IResource c : members) { if (monitor.isCanceled()) { break; } monitor.worked(1); if (c instanceof IContainer) { formatted += this.doActionOnContainer((IContainer) c, monitor); } else if (c instanceof IFile) { final String name = c.getName(); if (name != null) { monitor.setTaskName("Formatting: " + name); if (PythonPathHelper.isValidSourceFile(name)) { IFile file = (IFile) c; final IDocument doc = FileUtilsFileBuffer.getDocFromResource(c); final boolean isOpenedFile = openFiles.contains(file); try { if (isOpenedFile) { RunInUiThread.async(new Runnable() { @Override public void run() { try { formatter.formatAll(doc, null, (IFile) c, isOpenedFile, true); } catch (SyntaxErrorException e) { Log.log(IStatus.ERROR, "Could not " + verbPresentMsg + " file: " + name + " (invalid syntax).", e); } } }); } else { formatter.formatAll(doc, null, (IFile) c, isOpenedFile, true); } } catch (SyntaxErrorException e) { Log.log(IStatus.ERROR, "Could not " + verbPresentMsg + " file: " + name + " (invalid syntax).", e); } formatted += 1; if (isOpenedFile) { //This means that it's an open buffer (let the user save it when he wants). continue; } file.setContents(new ByteArrayInputStream(doc.get().getBytes()), true, true, monitor); } } } } } catch (CoreException e) { Log.log(e); } return formatted; } }