edu.umd.cs.eclipse.courseProjectManager.TurninProjectAction.java Source code

Java tutorial

Introduction

Here is the source code for edu.umd.cs.eclipse.courseProjectManager.TurninProjectAction.java

Source

/**
 * Marmoset: a student project snapshot, submission, testing and code review
 * system developed by the Univ. of Maryland, College Park
 * 
 * Developed as part of Jaime Spacco's Ph.D. thesis work, continuing effort led
 * by William Pugh. See http://marmoset.cs.umd.edu/
 * 
 * Copyright 2005 - 2011, Univ. of Maryland
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 */

/*
 * Created on Aug 12, 2004
 *
 */
package edu.umd.cs.eclipse.courseProjectManager;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
import org.apache.commons.httpclient.methods.MultipartPostMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.protocol.Protocol;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.PluginVersionIdentifier;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSTeamProvider;
import org.eclipse.team.internal.ccvs.core.ICVSFile;
import org.eclipse.team.internal.ccvs.core.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.ICVSResourceVisitor;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWebBrowser;

/**
 * @author jspacco
 * 
 */
@SuppressWarnings("restriction")
public class TurninProjectAction implements IObjectActionDelegate {
    static {
        Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
        Protocol.registerProtocol("easyhttps", easyhttps);
    }

    // Fields
    private ISelection selection;

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.
     * action.IAction, org.eclipse.ui.IWorkbenchPart)
     */
    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
    }

    /**
     * Get all resources in given project.
     * 
     * @param project
     *            the project
     * @return Set containing all resources in the project
     */
    private Set<IResource> getProjectResources(IProject project) throws CoreException {
        final Set<IResource> projectResources = new HashSet<IResource>();
        project.accept(new IResourceVisitor() {
            public boolean visit(IResource resource) {
                projectResources.add(resource);
                return true;
            }
        });
        return projectResources;
    }

    /**
     * Get all dirty editors which are editing resources belonging to a project.
     * 
     * @param workbench
     *            the workbench
     * @param project
     *            the project
     * @return the list of dirty editors for the project
     */
    private List<IEditorPart> getDirtyEditorsForProject(IWorkbench workbench, IProject project)
            throws CoreException {
        Set<IResource> projectResources = getProjectResources(project);
        List<IEditorPart> dirtyEditors = new LinkedList<IEditorPart>();

        // This code is based on Workbench.saveAllEditors().
        // You'd think there would be a much easier way
        // to find dirty editors.
        IWorkbenchWindow[] wwinList = workbench.getWorkbenchWindows();
        for (int i = 0; i < wwinList.length; i++) {
            IWorkbenchWindow wwin = wwinList[i];
            IWorkbenchPage[] pageList = wwin.getPages();
            for (int j = 0; j < pageList.length; j++) {
                IWorkbenchPage page = pageList[j];
                IEditorReference[] editorReferenceList = page.getEditorReferences();
                for (int k = 0; k < editorReferenceList.length; k++) {
                    IEditorReference editorRef = editorReferenceList[k];
                    IEditorPart editor = editorRef.getEditor(true);
                    if (editor != null && editor.isDirty()) {
                        IEditorInput input = editor.getEditorInput();
                        IResource resource = (IResource) input.getAdapter(IResource.class);
                        if (resource != null) {
                            Debug.print("Got a resource from a dirty editor: " + resource.getName());
                            if (projectResources.contains(resource))
                                dirtyEditors.add(editor);
                        }
                    }
                }
            }
        }

        return dirtyEditors;
    }

    /**
     * Save all dirty editors in given project.
     * 
     * @param project
     *            the project
     * @param workbench
     *            the workbench
     * @return true if no editors are dirty, or if all dirty editors were
     *         successfully saved
     * @throws CoreException
     */
    private boolean saveDirtyEditors(IProject project, IWorkbench workbench) throws CoreException {

        List<IEditorPart> dirtyEditors = getDirtyEditorsForProject(workbench, project);
        boolean noDirt;
        if (dirtyEditors.isEmpty()) {
            noDirt = true;
        } else {
            if (!workbench.saveAllEditors(true))
                noDirt = false;
            else
                noDirt = getDirtyEditorsForProject(workbench, project).isEmpty();
        }
        if (noDirt)
            return true;

        AutoCVSPlugin.getPlugin().getEventLog()
                .logMessage("Submission of project " + project.getName() + " cancelled");
        return false;
    }

    static Properties getUserProperties(IResource submitUserResource) throws IOException {
        Properties userProperties = new Properties();
        if (submitUserResource != null) {
            // load .submitUser properties (classAccount and oneTimePassword)
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(submitUserResource.getRawLocation().toString());
                userProperties.load(fileInputStream);
            } catch (FileNotFoundException e) {
            } finally {
                if (fileInputStream != null)
                    fileInputStream.close();
            }
        }
        return userProperties;
    }

    private static Properties getSubmitUserProperties(IProject project) throws IOException {
        // Somehow, the resource might exist but the file doesn't exist?
        IResource submitUserResource = project.findMember(AutoCVSPlugin.SUBMITUSER);
        return getUserProperties(submitUserResource);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
     */
    public void run(IAction action) {
        // TODO Refactor: Should the places where we raise a dialog and return
        // could throw an exception instead?
        String timeOfSubmission = "t" + System.currentTimeMillis();

        // Make sure we can get the workbench...
        IWorkbench workbench = PlatformUI.getWorkbench();
        if (workbench == null) {
            Dialogs.errorDialog(null, "Warning: project submission failed", "Could not submit project",
                    "Internal error: Can't get workbench", IStatus.ERROR);
            return;
        }
        // ...and the workbenchWindow
        IWorkbenchWindow wwin = workbench.getActiveWorkbenchWindow();
        if (wwin == null) {
            Dialogs.errorDialog(null, "Error submitting project", "Could not submit project",
                    "Internal error: Can't get workbench window", IStatus.ERROR);
            return;
        }

        // Shell to use as parent of dialogs.
        Shell parent = wwin.getShell();
        // Sanity check.
        if (!(selection instanceof IStructuredSelection)) {
            Dialogs.errorDialog(parent, "Warning: Selection is Invalid",
                    "Invalid turnin action: You have selected an object that is not a Project. Please select a Project and try again.",
                    "Object selected is not a Project", IStatus.WARNING);
            return;
        }
        IStructuredSelection structured = (IStructuredSelection) selection;
        Object obj = structured.getFirstElement();
        Debug.print("Selection object is a " + obj.getClass().getName() + " @" + System.identityHashCode(obj));
        IProject project;
        if (obj instanceof IProject) {
            project = (IProject) obj;
        } else if (obj instanceof IProjectNature) {
            project = ((IProjectNature) obj).getProject();
        } else {
            Dialogs.errorDialog(null, "Warning: Selection is Invalid",
                    "Invalid turnin action: You have selected an object that is not a Project. Please select a Project and try again.",
                    "Object selected is not a Project", IStatus.WARNING);
            return;
        }
        Debug.print("Got the IProject for the turnin action @" + System.identityHashCode(project));

        // ================================= save dirty editors
        // ========================================
        // save dirty editors
        try {
            if (!saveDirtyEditors(project, workbench)) {
                Dialogs.errorDialog(parent, "Submit not performed",
                        "Projects cannot be submitted unless all open files are saved",
                        "Unsaved files prevent submission", IStatus.WARNING);
                return;
            }
        } catch (CoreException e) {
            Dialogs.errorDialog(parent, "Submit not performed",
                    "Could not turn on cvs management for all project files", e);
            return;
        }

        // ========================= Add all non-ignored files in the project
        // =========================
        IResource[] files;
        try {
            Set<IResource> resourceSet = getProjectResources(project);
            ArrayList<IFile> addedFiles = new ArrayList<IFile>();
            for (Iterator<IResource> iter = resourceSet.iterator(); iter.hasNext();) {
                IResource resource = iter.next();
                if (resource instanceof IFile) {
                    IFile file = (IFile) resource;
                    if (!AutoCVSPlugin.isCVSIgnored(file) && !AutoCVSPlugin.isCVSManaged(file)) {
                        addedFiles.add(file);
                    }
                }
            }
            files = (IResource[]) addedFiles.toArray(new IResource[addedFiles.size()]);
        } catch (CoreException e) {
            Dialogs.errorDialog(parent, "Submit not performed",
                    "Could not perform submit; unable to find non-ignored resources", e);
            return;
            // TODO what to do here?
        }

        // ================================= perform CVS commit
        // ========================================
        // TODO Somehow move this into the previous try block
        // This forces add/commit operations when AutoSync was shut off
        // Would it just be easier to enable autoSync and then trigger
        // a resource changed delta, since this method appears to enable
        // autoSync anyway?
        //
        String cvsStatus = "Not performed";
        try {
            cvsStatus = forceCommit(project, files);
        } catch (Exception e) {
            Dialogs.errorDialog(parent,
                    "CVS commit not performed as part of submission due to unexpected exception",
                    e.getClass().getName() + " " + e.getMessage(), e);
        }

        // ================================= perform CVS tag
        // ========================================
        try {
            CVSOperations.tagProject(project, timeOfSubmission, CVSOperations.SYNC);
        } catch (Exception e) {
            AutoCVSPlugin.getPlugin().getEventLog()
                    .logError("Error tagging submission; submission via the web unlikely to work", e);
        }

        // ================================= find properties
        // ========================================
        // find the .submitProject file
        IResource submitProjectFile = project.findMember(AutoCVSPlugin.SUBMITPROJECT);
        if (submitProjectFile == null) {
            Dialogs.errorDialog(parent, "Warning: Project submission not enabled", "Submission is not enabled",
                    "There is no " + AutoCVSPlugin.SUBMITPROJECT + " file for the project", IStatus.ERROR);
            return;
        }
        // Get the properties from the .submit file, and the .submitUser file,
        // if it exists
        // or can be fetched from the server
        Properties allSubmissionProps = null;
        try {
            allSubmissionProps = getAllProperties(timeOfSubmission, parent, project, submitProjectFile);
        } catch (IOException e) {
            String message = "IOException finding " + AutoCVSPlugin.SUBMITPROJECT + " and "
                    + AutoCVSPlugin.SUBMITUSER + " files; " + cvsStatus;
            AutoCVSPlugin.getPlugin().getEventLog().logError(message, e);
            Dialogs.errorDialog(parent, "Submission failed", message, e.getMessage(), IStatus.ERROR);
            Debug.print("IOException: " + e);
            return;
        } catch (CoreException e) {
            String message = "IOException finding " + AutoCVSPlugin.SUBMITPROJECT + " and "
                    + AutoCVSPlugin.SUBMITUSER + " files; " + cvsStatus;
            AutoCVSPlugin.getPlugin().getEventLog().logError(message, e);
            Dialogs.errorDialog(parent, "Submission failed", message, e.getMessage(), IStatus.ERROR);
            Debug.print("CoreException: " + e);
            return;
        }

        //
        // THE ACTUAL SUBMIT HAPPENS HERE
        //
        try {
            // ============================== find files to submit
            // ====================================
            Collection<IFile> cvsFiles = findFilesForSubmission(project);

            // ========================== assemble zip file in byte array
            // ==============================

            ByteArrayOutputStream bytes = new ByteArrayOutputStream(4096);
            ZipOutputStream zipfile = new ZipOutputStream(bytes);
            zipfile.setComment("zipfile for submission created by CourseProjectManager version "
                    + AutoCVSPlugin.getPlugin().getVersion());

            try {
                byte[] buf = new byte[4096];
                for (IFile file : cvsFiles) {
                    if (!file.exists()) {
                        Debug.print("Resource " + file.getName() + " being ignored because it doesn't exist");
                        continue;
                    }

                    ZipEntry entry = new ZipEntry(file.getProjectRelativePath().toString());
                    entry.setTime(file.getModificationStamp());

                    zipfile.putNextEntry(entry);
                    // Copy file data to zip file
                    InputStream in = file.getContents();

                    try {
                        while (true) {
                            int n = in.read(buf);
                            if (n < 0)
                                break;
                            zipfile.write(buf, 0, n);
                        }
                    } finally {
                        in.close();
                    }
                    zipfile.closeEntry();
                }
            } catch (IOException e1) {
                Dialogs.errorDialog(parent, "Warning: Project submission failed",
                        "Unable to zip files for submission\n" + cvsStatus, e1);
                return;
            } finally {
                if (zipfile != null)
                    zipfile.close();
            }

            // ============================== Post to submit server
            // ====================================
            String version = System.getProperties().getProperty("java.runtime.version");
            boolean useEasyHttps = version.startsWith("1.3") || version.startsWith("1.2")
                    || version.startsWith("1.4.0") || version.startsWith("1.4.1")
                    || version.startsWith("1.4.2_0") && version.charAt(7) < '5';
            if (useEasyHttps) {
                String submitURL = allSubmissionProps.getProperty("submitURL");
                if (submitURL.startsWith("https"))
                    submitURL = "easy" + submitURL;
                allSubmissionProps.setProperty("submitURL", submitURL);
            }
            // prepare multipart post method
            MultipartPostMethod filePost = new MultipartPostMethod(allSubmissionProps.getProperty("submitURL"));

            // add properties
            addAllPropertiesButSubmitURL(allSubmissionProps, filePost);

            // add filepart
            byte[] allInput = bytes.toByteArray();
            filePost.addPart(new FilePart("submittedFiles", new ByteArrayPartSource("submit.zip", allInput)));

            // prepare httpclient
            HttpClient client = new HttpClient();
            client.setConnectionTimeout(5000);
            int status = client.executeMethod(filePost);

            // Piggy-back uploading the launch events onto submitting.
            EclipseLaunchEventLog.postEventLogToServer(project);

            if (status == HttpStatus.SC_OK) {
                Dialogs.okDialog(parent, "Project submission successful",
                        "Project " + allSubmissionProps.getProperty("projectNumber")
                                + " was submitted successfully\n" + filePost.getResponseBodyAsString());

            } else {
                Dialogs.errorDialog(parent, "Warning: Project submission failed", "Project submission failed",
                        filePost.getStatusText() + "\n " + cvsStatus, IStatus.CANCEL);
                AutoCVSPlugin.getPlugin().getEventLog().logMessage(filePost.getResponseBodyAsString());
            }

        } catch (CoreException e) {
            Dialogs.errorDialog(parent, "Warning: Project submission failed",
                    "Project submissions via https failed\n" + cvsStatus, e);
        } catch (HttpConnection.ConnectionTimeoutException e) {
            Dialogs.errorDialog(parent, "Warning: Project submission failed", "Project submissions failed",
                    "Connection timeout while trying to connect to submit server\n " + cvsStatus, IStatus.ERROR);
        } catch (IOException e) {
            Dialogs.errorDialog(parent, "Warning: Project submission failed",
                    "Project submissions failed\n " + cvsStatus, e);
        }
    }

    /**
     * @param allSubmissionProps
     * @param filePost
     */
    static void addAllPropertiesButSubmitURL(Properties allSubmissionProps, MultipartPostMethod filePost) {
        for (Map.Entry<?, ?> e : allSubmissionProps.entrySet()) {
            String key = (String) e.getKey();
            String value = (String) e.getValue();
            if (!key.equals("submitURL"))
                filePost.addParameter(key, value);
        }
    }

    /**
     * @param project
     * @param submitProjectFile
     * @return list of files that should be submitted
     * @throws TeamException
     */
    static Collection<IFile> findFilesForSubmission(IProject project) throws TeamException {
        // TODO Do I really need to use ICVSFile? Can't I just go ahead and use
        // a regular
        // visitor for IResources?

        // look for .submitIgnore file
        // .submitIgnore functions like a .cvsignore file and allows
        // fine-grained filtering of what gets submitted
        SubmitIgnoreFilter ignoreFilter = null;
        IResource submitignoreFile = project.findMember(AutoCVSPlugin.SUBMITIGNORE);
        if (submitignoreFile != null) {
            String filename = submitignoreFile.getRawLocation().toString();
            try {
                ignoreFilter = SubmitIgnoreFilter.createSubmitIgnoreFilterFromFile(filename);
            } catch (IOException ignore) {
                Debug.print("Unable to create new ignore SubmitFilter: " + filename);
                // ignore and use default null value
            }
        }
        List<ICVSFile> cvsFiles = new LinkedList<ICVSFile>();
        SubmitResourceVisitor visitor = new SubmitResourceVisitor(ignoreFilter, cvsFiles);

        // Guaranteed to have a valid .submit file.
        IResource submitProjectFile = project.findMember(AutoCVSPlugin.SUBMITPROJECT);
        if (submitProjectFile != null) {
            CVSTeamProvider provider = CVSOperations.getProvider(submitProjectFile);
            CVSWorkspaceRoot root = provider.getCVSWorkspaceRoot();
            ICVSFolder rootFolder = root.getLocalRoot();
            rootFolder.acceptChildren(visitor);
        }
        Collection<IFile> result = new LinkedHashSet<IFile>();
        for (ICVSFile cvsFile : cvsFiles) {
            IResource r = cvsFile.getIResource();
            if (r instanceof IFile)
                result.add((IFile) r);
            else
                Debug.print("Not a file: " + r);

        }
        IResource submitIncludeFile = project.findMember(AutoCVSPlugin.SUBMITINCLUDE);
        if (submitIncludeFile instanceof IFile) {
            try {
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(((IFile) submitIncludeFile).getContents()));
                while (true) {
                    String filePath = reader.readLine();
                    if (filePath == null)
                        break;
                    IResource fileResource = project.findMember(filePath);
                    if (fileResource instanceof IFile)
                        result.add((IFile) fileResource);

                }

            } catch (IOException e) {
                Debug.print("Error handling " + AutoCVSPlugin.SUBMITINCLUDE + " file", e);

            } catch (CoreException e) {
                Debug.print("Error handling " + AutoCVSPlugin.SUBMITINCLUDE + " file", e);

            }
        }

        return result;
    }

    /**
     * @param timeOfSubmission
     * @param parent
     * @param project
     * @param submitProjectFile
     * @return
     * @throws IOException
     * @throws FileNotFoundException
     * @throws HttpException
     * @throws CoreException
     */
    private Properties getAllProperties(String timeOfSubmission, Shell parent, IProject project,
            IResource submitProjectFile) throws IOException, FileNotFoundException, HttpException, CoreException {
        Properties allSubmissionProps;
        //
        // Load all properties contained in .submitproject file
        // TODO validate .submitproject file
        allSubmissionProps = new Properties();
        FileInputStream fileInputStream = new FileInputStream(submitProjectFile.getRawLocation().toString());
        allSubmissionProps.load(fileInputStream);
        fileInputStream.close();
        // p.list(System.out);
        allSubmissionProps.setProperty("cvstagTimestamp", timeOfSubmission);
        allSubmissionProps.setProperty("submitClientTool", "EclipsePlugin");
        allSubmissionProps.setProperty("submitClientVersion", AutoCVSPlugin.getPlugin().getVersion());
        allSubmissionProps.setProperty("hasFailedCVSOperation",
                Boolean.toString(AutoCVSPlugin.getPlugin().hasFailedOperation()));
        allSubmissionProps.setProperty("isAutoCVSSync",
                Boolean.toString(AutoCVSPlugin.getPlugin().getAutoSync(project)));

        Properties userProperties = getSubmitUserProperties(project);

        String authentication = allSubmissionProps.getProperty("authentication.type");
        Debug.print("properties: " + userProperties);

        // classAccount will be null when we don't have a .submitUser file and
        // need
        // to fetch one from the server
        if (invalidSubmitUser(userProperties)) {
            InputStream submitUser = null;
            if (!authentication.equals("ldap")) {

                submitUser = getSubmitUserForOpenId(parent, allSubmissionProps);
            } else {
                PasswordDialog passwordDialog = new PasswordDialog(parent);
                int passwordStatus = passwordDialog.open();
                if (passwordStatus != PasswordDialog.OK) {
                    // TODO fail here in some useful way
                    Debug.print("PasswordDialog failed");
                }
                String username = passwordDialog.getUsername();
                String password = passwordDialog.getPassword();

                submitUser = TurninProjectAction.getSubmitUserFileFromServer(username, password,
                        allSubmissionProps);
            }
            Debug.print("I have input stream from the server");

            // create .submituser file
            IFile submitUserFile = project.getFile(AutoCVSPlugin.SUBMITUSER);
            IResource submitUserResource = project.findMember(AutoCVSPlugin.SUBMITUSER);
            Debug.print("\nsubmitUserResource = " + submitUserResource + "\n");
            if (submitUserResource == null)
                submitUserFile.create(submitUser, true, null);
            else
                submitUserFile.setContents(submitUser, true, false, null);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }

            Debug.print("created .submituser file");
            userProperties = getSubmitUserProperties(project);
        }

        // open .submituser file, load properties, and add those properties
        // the current set of properties
        if (invalidSubmitUser(userProperties)) {
            throw new IOException(
                    "Cannot find classAccount in user properties even after negotiating with the SubmitServer for a one-time password for this project");
        }

        // combine the two sets of properties
        addPropertiesNotAlreadyDefined(allSubmissionProps, userProperties);
        if (invalidSubmitUser(allSubmissionProps)) {
            throw new IOException(
                    "Cannot find classAccount in all properties even after negotiating with the SubmitServer for a one-time password for this project");
        }
        return allSubmissionProps;
    }

    private boolean invalidSubmitUser(Properties userProperties) {
        return userProperties.getProperty("cvsAccount") == null
                && userProperties.getProperty("classAccount") == null;
    }

    public static boolean openURL(String u) {
        try {
            URL url = new URL(u);

            if (false) {
                IWebBrowser browser = PlatformUI.getWorkbench().getBrowserSupport()
                        .createBrowser("courseProjectManager");
                browser.openURL(url);
            } else {
                PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(url);
            }
            return true;
        } catch (PartInitException e) {
            return false;
        } catch (MalformedURLException e) {
            return false;
        }

    }

    public static InputStream getSubmitUserForOpenId(Shell parent, Properties properties) throws IOException {
        String courseKey = properties.getProperty("courseKey");
        String projectNumber = properties.getProperty("projectNumber");
        String baseURL = properties.getProperty("baseURL");

        String encodedProjectNumber = URLEncoder.encode(projectNumber, "UTF-8");
        String u = baseURL + "/view/submitStatus.jsp?courseKey=" + courseKey + "&projectNumber="
                + encodedProjectNumber;
        SubmitUserDialog submitUserDialog = new SubmitUserDialog(parent);
        System.out.println(u);
        openURL(u);
        int status = submitUserDialog.open();

        if (status != SubmitUserDialog.OK) {
            // TODO fail here in some useful way
            Debug.print("SubmitUserDialog failed");
        }

        String classAccount = submitUserDialog.getClassAccount();
        String oneTimePassword = submitUserDialog.getOneTimePassString();
        String results = String.format("classAccount=%s%noneTimePassword=%s%n", classAccount, oneTimePassword);
        return new ByteArrayInputStream(results.getBytes());

    }

    private static String forceCommit(IProject project, IResource[] addedFiles) {
        String cvsStatus = "(already submitted backup copy via CVS)";
        try {
            // Special case: if AutoSync has been disabled because of a failed
            // cvs update or because it was explicitly disabled,
            // but the user chooses to submit anyway, we need
            // to add/commit all non-ignored resources in order to bring the
            // project in sync with the repository. If we didn't, unmanaged
            // files
            // wouldn't get tagged, and some tags would correspond to stale
            // file versions.
            if (addedFiles.length > 0 || AutoCVSPlugin.getPlugin().hasFailedOperation()
                    || !AutoCVSPlugin.getPlugin().getAutoSync(project)) {
                if (AutoCVSPlugin.getPlugin().hasFailedOperation()
                        || !AutoCVSPlugin.getPlugin().getAutoSync(project)) {
                    AutoCVSPlugin.getPlugin().getEventLog().logMessage(
                            "Syncing project for disconnected submit operation for project " + project.getName());
                }
                cvsStatus = "(CVS submission unsucessful as well)";
                // Completion callback to record the success or failure of the
                // add and submit actions.
                CVSOperations.SimpleContext completion = new CVSOperations.SimpleContext();

                // XXX It's not clear that SYNC mode actually works
                // CVSOperations.cvsCommand(files, CVSOperations.SYNC,
                // completion, Command.ADD);
                if (addedFiles.length > 0)
                    CVSOperations.add(addedFiles, CVSOperations.SYNC, completion);
                if (addedFiles.length == 0 || completion.success) {

                    // Commit the project to ensure all files are in sync with
                    // the repository.
                    // CVSOperations.cvsCommand(new IResource[]{project},
                    // CVSOperations.SYNC, completion, Command.COMMIT);
                    CVSOperations.commit(new IResource[] { project }, CVSOperations.SYNC, completion);
                    if (completion.success)
                        cvsStatus = "(However, CVS submission successful)";

                }
            }
        } catch (CoreException e) {
            AutoCVSPlugin.getPlugin().getEventLog().logError("Error performing CVS commit during submission", e);
        }
        return cvsStatus;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action
     * .IAction, org.eclipse.jface.viewers.ISelection)
     */
    public void selectionChanged(IAction action, ISelection selection) {
        this.selection = selection;
    }

    public static void addPropertiesNotAlreadyDefined(Properties dst, Properties src) {
        for (Map.Entry<?, ?> entry : src.entrySet()) {
            if (!dst.containsKey(entry.getKey()))
                dst.setProperty((String) entry.getKey(), (String) entry.getValue());
        }
    }

    private static class SubmitResourceVisitor implements ICVSResourceVisitor {
        private SubmitIgnoreFilter filter;

        private List<ICVSFile> cvsFiles;

        /**
         *
         */
        public SubmitResourceVisitor(SubmitIgnoreFilter filter, List<ICVSFile> cvsFiles) {
            this.filter = filter;
            this.cvsFiles = cvsFiles;
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.eclipse.team.internal.ccvs.core.ICVSResourceVisitor#visitFile
         * (org.eclipse.team.internal.ccvs.core.ICVSFile)
         */
        public void visitFile(ICVSFile file) throws CVSException {
            // TODO More elegant way to skip these files than hardcoding things
            // here?
            // Maybe make a default SubmitIgnore object that always ignores
            // these?
            if (file.getName().equals(EclipseLaunchEventLog.LOG_NAME)) {
                Debug.print("\n\nSkipping " + file.getName() + "\n\n");
                return;
            } else if (file.getName().equals(".submitIgnore")) {
                Debug.print("Skipping submitIgnore file = " + file.getName());
                return;
            }
            // Debug.print("file: " + file.getName());
            // Debug.print("filter: " + filter);
            if (filter != null)
                Debug.print(
                        "filtering these patterns: " + filter.toString() + " against this file: " + file.getName());
            if (filter == null || !filter.matches(file.getName())) {
                // only add the file if the filter exists and doesn't match this
                // string
                cvsFiles.add(file);
            }
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.eclipse.team.internal.ccvs.core.ICVSResourceVisitor#visitFolder
         * (org.eclipse.team.internal.ccvs.core.ICVSFolder)
         */
        public void visitFolder(ICVSFolder folder) throws CVSException {
            if (filter == null || !filter.matches(folder.getName())) {
                // only traverse this directory if the .submitignore file
                // doesn't match this directory
                Debug.print("vising folder: " + folder.getName());
                SubmitIgnoreFilter newFilter = null;
                ICVSFile submitIgnoreFile = folder.getFile(AutoCVSPlugin.SUBMITIGNORE);
                if (submitIgnoreFile != null && submitIgnoreFile.exists()) {
                    Debug.print("valid submitIgnoreFile that exists on the file system: " + submitIgnoreFile);

                    String filename = getRawFilesystemLocation(submitIgnoreFile);
                    try {
                        Debug.print("Trying to open submitignore file: " + filename);
                        newFilter = SubmitIgnoreFilter.createSubmitIgnoreFilterFromFile(filename);

                    } catch (IOException e) {
                        // keep default null value, there's no useful way to
                        // recover from this error
                        Debug.print("Unable to create submitignore filter from file: " + filename);
                    }

                }
                SubmitResourceVisitor newVisitor = new SubmitResourceVisitor(newFilter, cvsFiles);
                folder.acceptChildren(newVisitor);
            }
        }

        /**
         * @param iCVSFile
         * @return
         * @throws CVSException
         */
        private String getRawFilesystemLocation(ICVSFile iCVSFile) throws CVSException {
            String filename = iCVSFile.getIResource().getRawLocation().toString();
            return filename;
        }
    }

    private static class SubmitIgnoreFilter {
        private HashSet<String> filters = new HashSet<String>();

        /**
         * Adds a new filter represented by the given string.
         * 
         * @param filterString
         *            the String representing the types of files to filter
         */
        void addFilter(String filterString) {
            filterString = "^" + filterString;
            // using replaceAll() since replace() is only available in Java 1.5
            // filterString = filterString.replaceAll("\\$", "\\\\\\$");
            filterString = filterString.replaceAll("\\.", "\\\\.");
            filterString = filterString.replaceAll("\\*", "\\.\\*");
            filters.add(filterString);
        }

        /**
         * Checks if a filename should be filtered out because it matches one of
         * the rules in the SubmitIgnore object (which was created from a
         * .submitignore file).
         * 
         * @param filename
         *            the filename to try to match
         * @return true if the file should be filtered, false otherwise
         */
        boolean matches(String filename) {
            for (String regexp : filters) {
                if (filename.matches(regexp))
                    return true;
            }
            return false;
        }

        public String toString() {
            StringBuffer result = new StringBuffer();
            for (Iterator<String> ii = filters.iterator(); ii.hasNext();) {
                result.append(ii.next() + "\n");
            }
            return result.toString();
        }

        /**
         * Gets an iterator over the filter strings.
         * 
         * @return an iterator over the filter strings
         */
        Iterator<String> iterator() {
            return filters.iterator();
        }

        /**
         * Static factory method that creates a SubmitIgnoreFilter from a file.
         * 
         * @param filename
         *            the name of the file to use to create the
         *            SubmitIgnoreFilter.
         * @return a submitIgnoreFilter
         * @throws IOException
         *             if there is an error reading the file
         */
        static SubmitIgnoreFilter createSubmitIgnoreFilterFromFile(String filename) throws IOException {
            SubmitIgnoreFilter submitIgnoreFilter = new SubmitIgnoreFilter();

            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new FileReader(filename));

                String filterString;
                while ((filterString = readLine(reader)) != null) {
                    submitIgnoreFilter.addFilter(filterString);
                }
                return submitIgnoreFilter;
            } finally {
                if (reader != null)
                    reader.close();
            }
        }
    }

    /**
     * Utility method that reads the next non-comment, non-empty line from a
     * BufferedReader. Treats the data coming out of the BufferedReader as if it
     * were a unix-style configuration file, using '#' to denote a comment.
     * 
     * @param reader
     *            the BufferedReader
     * @return the next non-comment, non-empty line, or null if we're at EOF
     * @throws IOException
     */
    public static String readLine(BufferedReader reader) throws IOException {
        String line;
        do {
            line = reader.readLine();
            // return null if the BufferedReader returns null (meaning we'er at
            // EOF)
            if (line == null)
                return null;

            // System.out.println("line before: " + line);

            // else try to strip out comments
            int startComment = line.indexOf('#');
            // System.out.println("startComment: " +startComment);
            if (startComment != -1) {
                line = line.substring(0, startComment);
            }
            // System.out.println("line after: " +line);

            // replace all leading whitespace
            line = line.replaceAll("\\s+", "");
        } while (line.equals(""));

        // at this point we know that we have a valid non-empty string
        return line;
    }

    public static void main(String[] args) throws Exception {
        String HOME = System.getenv("HOME");
        TurninProjectAction.SubmitIgnoreFilter submitIgnoreFilter = TurninProjectAction.SubmitIgnoreFilter
                .createSubmitIgnoreFilterFromFile(HOME + "/submitignore");

        for (Iterator<String> ii = submitIgnoreFilter.iterator(); ii.hasNext();) {
            System.out.println(ii.next());
        }

    }

    private static InputStream getSubmitUserFileFromServer(String loginName, String password,
            Properties allProperties) throws IOException, HttpException {
        String url = allProperties.getProperty("baseURL");
        url += "/eclipse/NegotiateOneTimePassword";
        // System.out.println(url);
        // Debug.print("url: " +url);
        PostMethod post = new PostMethod(url);
        post.addParameter("loginName", loginName);
        post.addParameter("password", password);

        addParameter(post, "courseKey", allProperties);
        addParameter(post, "projectNumber", allProperties);
        post.addParameter("submitClientVersion", AutoCVSPlugin.getPlugin().getVersion());

        HttpClient client = new HttpClient();
        client.setConnectionTimeout(5000);

        // System.out.println("Preparing to execute method");
        int status = client.executeMethod(post);
        // System.out.println("Post finished with status: " +status);

        if (status != HttpStatus.SC_OK) {
            throw new HttpException(
                    "Unable to negotiate one-time password with the server: " + post.getResponseBodyAsString());
        }

        return post.getResponseBodyAsStream();
    }

    static void addParameter(PostMethod post, String name, Properties properties) {
        String property = properties.getProperty(name);
        if (property != null)
            post.addParameter(name, property);
    }

}