edu.umd.cs.buildServer.builder.JavaSubmissionExtractor.java Source code

Java tutorial

Introduction

Here is the source code for edu.umd.cs.buildServer.builder.JavaSubmissionExtractor.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 Jan 21, 2005
 */
package edu.umd.cs.buildServer.builder;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import edu.umd.cs.buildServer.BuilderException;
import edu.umd.cs.marmoset.utilities.ZipExtractorException;

/**
 * SubmissionExtractor for Java submissions.
 * 
 * @author David Hovemeyer
 */
public class JavaSubmissionExtractor extends SubmissionExtractor {
    // Constants
    private static final String SRC_ENTRY_XPATH = "/classpath/classpathentry[@kind='src']";

    // Fields
    private String classpathFile; // Eclipse .classpath file
    private List<Pattern> excludedSourceFileList;

    /**
     * Constructor.
     * 
     * @param zipFile
     *            the submission zipfile
     * @param directory
     *            directory to extract the submission into
     * @param buildServerLog
     *            the buildserver's Log
     * @throws BuilderException
     */
    public JavaSubmissionExtractor(File zipFile, File directory, Logger buildServerLog)
            throws ZipExtractorException {
        super(zipFile, directory, buildServerLog);
        this.classpathFile = null;
        this.excludedSourceFileList = new LinkedList<Pattern>();
    }

    /**
     * Add a regex pattern specifying source file(s) to exclude.
     * 
     * @param pattern
     *            exclusion regex pattern
     */
    public void addExcludedSourceFilePattern(String pattern) {
        try {
            Pattern regex = Pattern.compile(pattern);
            excludedSourceFileList.add(regex);
        } catch (PatternSyntaxException e) {
            getLog().warn("Could not compile source file exclusion pattern " + pattern, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * edu.umd.cs.buildServer.ZipExtractor#successfulFileExtraction(java.lang
     * .String, java.lang.String)
     */
    @Override
    protected void successfulFileExtraction(String entryName, String fileName) {
        super.successfulFileExtraction(entryName, fileName);

        if (fileName.equals(".classpath")) {
            classpathFile = fileName;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * edu.umd.cs.buildServer.SubmissionExtractor#isSourceFile(java.lang.String)
     */
    @Override
    protected boolean isSourceFile(String fileName) {
        return fileName.endsWith(".java");
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * edu.umd.cs.buildServer.SubmissionExtractor#pruneSourceFileList(java.util
     * .List)
     */
    @Override
    protected void pruneSourceFileList(List<String> sourceFileList) {
        // Apply exclusion patterns, if any
        if (excludedSourceFileList.size() > 0) {
            outer: for (Iterator<String> i = sourceFileList.iterator(); i.hasNext();) {
                String sourceFile = i.next();
                for (Iterator<Pattern> j = excludedSourceFileList.iterator(); j.hasNext();) {
                    Pattern regex = j.next();
                    Matcher m = regex.matcher(sourceFile);
                    if (m.matches()) {
                        i.remove();
                        continue outer;
                    }
                }
            }
        }

        if (classpathFile == null)
            return;

        // The submission contained a .classpath file.
        // Try to read it so we can find out the sourcepath
        // and remove all entries that aren't on the sourcepath
        // from the source files list.

        try {
            List<String> sourcePath = new LinkedList<String>();
            InputStream in = null;

            try {
                in = new BufferedInputStream(new FileInputStream(new File(getDirectory(), classpathFile)));

                SAXReader reader = new SAXReader();
                Document document = reader.read(in);

                for (Iterator<?> i = document.selectNodes(SRC_ENTRY_XPATH).iterator(); i.hasNext();) {
                    Node node = (Node) i.next();
                    String srcPathEntry = node.valueOf("@path");
                    if (!srcPathEntry.equals("") && !srcPathEntry.endsWith("/"))
                        srcPathEntry += "/";
                    sourcePath.add(srcPathEntry);
                }

                if (!allSourcePathsExist(sourcePath))
                    return;

                // Remove all files that don't begin with a valid source path
                // element from the source file list
                for (Iterator<String> i = sourceFileList.iterator(); i.hasNext();) {
                    String sourceFile = i.next();
                    if (!isOnSourcePath(sourceFile, sourcePath)) {
                        // Source file wasn't on the source path
                        i.remove();
                    }
                }
            } finally {
                try {
                    if (in != null)
                        in.close();
                } catch (IOException ignore) {
                    // Ignore
                }
            }
        } catch (IOException e) {
            getLog().warn("Could not read .classpath file in submission zipfile", e);
        } catch (DocumentException e) {
            getLog().warn("Could not read .classpath file in submission zipfile", e);
        }

    }

    /**
     * Ensures that all of the source paths exist. Strives to handle web-based
     * submissions better, specifically when students submit their source
     * outside of a "src" directory but include an Eclipse .classpath file that
     * references the non-existent source file.
     * 
     * @param sourcePath
     *            list of the source paths
     * @return true if all of source paths in the given list exist; false if at
     *         least one of the source paths does not exist
     */
    private boolean allSourcePathsExist(List<String> sourcePath) {
        for (Iterator<String> ii = sourcePath.iterator(); ii.hasNext();) {
            String dirName = ii.next();
            File dir = new File(getDirectory().getAbsolutePath() + "/" + dirName);
            if (!dir.exists())
                return false;
        }
        return true;
    }

    /**
     * Determine whether given source file is on the source path.
     * 
     * @param sourceFile
     *            the source file
     * @param sourcePath
     *            the source path
     * @return true if source file is on the source path, false otherwise
     */
    private boolean isOnSourcePath(String sourceFile, List<String> sourcePath) {
        for (Iterator<String> j = sourcePath.iterator(); j.hasNext();) {
            String srcPathEntry = j.next();
            if (sourceFile.startsWith(srcPathEntry))
                return true;
        }
        return false;
    }

    public static void main(String[] args) throws Exception {
        if (args.length != 3) {
            System.err.println(
                    "Usage: " + JavaSubmissionExtractor.class.getName() + " <zipfile> <directory> <project root>");
            System.exit(1);
        }

        File zipFile = new File(args[0]).getAbsoluteFile();
        File directory = new File(args[1]).getAbsoluteFile();
        String projectRoot = args[2];
        Logger log = Logger.getLogger(SubmissionExtractor.class);

        SubmissionExtractor extractor = new JavaSubmissionExtractor(zipFile, directory, log);
        extractor.setProjectRoot(projectRoot);
        extractor.extract();

        List<String> sourceFileList = extractor.getSourceFileList();

        System.out.println("Source files:");
        for (Iterator<String> i = sourceFileList.iterator(); i.hasNext();) {
            System.out.println("\t" + i.next());
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * edu.umd.cs.buildServer.SubmissionExtractor#transformFileName(java.lang
     * .String)
     */
    @Override
    protected String transformFileName(String entryName) {
        String filename = super.transformFileName(entryName);
        // If a source file isn't in a src directory, put it into a src
        // directory!
        if (filename.endsWith(".java") && !filename.startsWith("src"))
            filename = "src/" + filename;
        return filename;
    }

    @Override
    protected boolean shouldExtract(String entryName) {
        if (entryName.contains(".clover"))
            return false;
        return super.shouldExtract(entryName);
    }
}