AutumnProject.java
package autumn.lang.compiler;
import autumn.lang.compiler.errors.IErrorReporter;
import autumn.util.FileIO;
import autumn.util.test.TestResults;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
/**
* <b>(Under Active Development)</b> - An instance of this class represents an Autumn project.
*
* @author Mackenzie High
*/
public final class AutumnProject
{
/**
* This is the path to the directory containing the Autumn project.
*
* <p>
* The directory has several important subdirectories, including "src", "test", and "lib".
* </p>
*/
private final File project;
/**
* This is the path to the JAR-file representation of the project, if any.
*/
private final File program;
/**
* This object is used to report compilation errors.
*/
private final IErrorReporter reporter;
/**
* Sole Constructor.
*
* @param project is the directory that contains the Autumn-based project.
* @param reporter is used to report compilation errors.
* @throws NullPointerException if project is null.
* @throws NullPointerException if reporter is null.
* @throws IllegalArgumentException if project does not refer to a directory.
*/
public AutumnProject(final File project,
final IErrorReporter reporter)
{
Preconditions.checkNotNull(project);
Preconditions.checkNotNull(reporter);
Preconditions.checkArgument(project.isDirectory());
this.project = project;
this.program = new File(project, "program.jar");
this.reporter = reporter;
}
/**
* This method enumerates all the source-code files in the project.
*
* @return the aforedescribed immutable list.
*/
public List<File> srcFiles()
{
return filesOf(new File(project, "src/"), ".leaf");
}
/**
* This method enumerates all the test-code files in the project.
*
* @return the aforedescribed immutable list.
*/
public List<File> testFiles()
{
return filesOf(new File(project, "test/"), ".leaf");
}
/**
* This method enumerates all the library jar-files in the project.
*
* @return the aforedescribed immutable list.
*/
public List<File> libFiles()
{
return filesOf(new File(project, "lib/"), ".jar");
}
/**
* This method creates a list of all the files with a certain extension in a directory.
*
* <p>
* The list will include files in subdirectories.
* </p>
*
* <p>
* Hidden files are simply ignored.
* </p>
*
* @param folder is the root directory.
* @param extension is the file extension of each file.
* @return the aforedescribed immutable list.
*/
private List<File> filesOf(final File folder,
final String extension)
{
final List<File> files = Lists.newLinkedList();
for (File file : FileIO.filesOf(folder, true))
{
if (!file.isHidden() && file.isFile() && file.getPath().endsWith(extension))
{
files.add(file);
}
}
return Collections.unmodifiableList(files);
}
/**
* This method compiles the project and outputs a jar-file.
*
* <p>
* The name of the jar-file is always "program.jar".
* The jar-file will be placed in the root directory of the project.
* </p>
*
* @throws IOException if an IO error occurs.
*/
public void compile()
throws IOException
{
/**
* Load the project.
*/
final Autumn autumn = load();
/**
* Compile the project and then create the resulting JAR file.
*/
autumn.compile(program);
}
/**
* This method dynamically compiles and then runs a project.
*
* @param args are the command-line arguments to pass to the program.
* @throws IOException if an IO error occurs.
* @throws ClassNotFoundException if the main module of the project cannot be found.
* @throws NoSuchMethodException if the project does not contain a valid entry-point.
* @throws InvocationTargetException if the entry-point function throws an exception.
* @throws IllegalAccessException
*/
public void run(final String[] args)
throws IOException,
ClassNotFoundException,
NoSuchMethodException,
InvocationTargetException,
IllegalAccessException
{
/**
* Load the project.
*/
final Autumn autumn = load();
/**
* Execute the program.
*/
autumn.run(args);
}
/**
* This method dynamically compiles and then runs the unit-tests of a project.
*
* @return the results of running the unit-tests.
* @throws IOException if an IO error occurs.
*/
public TestResults test()
throws IOException
{
/**
* Load the project.
*/
final Autumn autumn = load();
/**
* Test the program.
*/
final TestResults results = autumn.test();
/**
* Return the results of the unit-testing.
*/
return results;
}
public void execute(final String[] args)
throws MalformedURLException,
IOException,
ClassNotFoundException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException,
NoSuchMethodException
{
final List<File> jars = Lists.newArrayList();
jars.add(program);
jars.addAll(libFiles());
execute(jars, args);
}
public static void execute(final List<File> program,
final String[] args)
throws MalformedURLException,
IOException,
ClassNotFoundException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException,
NoSuchMethodException
{
/**
* Step 1. Load all the jar files.
*/
final URL[] jars = new URL[program.size()];
int i = 0;
for (File jar : program)
{
jars[i++] = jar.toURI().toURL();
}
final URLClassLoader loader = new URLClassLoader(jars);
/**
* Step 2. Find the main class.
*/
String name = null;
for (File jar : program)
{
name = new JarFile(jar).getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
}
final Class klass = Class.forName(name, true, loader);
/**
* Reflectively find the main function.
*/
final Method main = klass.getMethod("main", String[].class);
/**
* Invoke the main function.
*/
main.invoke(null, (Object) args);
}
public Autumn load()
throws IOException
{
/**
* Create the object that will actually perform the execution.
*/
final Autumn autumn = new Autumn();
autumn.setErrorReporter(reporter);
/**
* Load all of the library files.
*/
for (File library : libFiles())
{
autumn.loadFile(library);
}
/**
* Read all of the source-code files.
*/
for (File src : srcFiles())
{
autumn.srcFile(src);
}
/**
* Read all of the test-code files.
*/
for (File test : testFiles())
{
autumn.srcFile(test);
}
return autumn;
}
public static void create(final File project)
throws IOException
{
Preconditions.checkNotNull(project);
URL url;
String code;
/**
* Create the project folder itself.
*/
project.mkdirs();
/**
* Create the project/src directory.
*/
final File src = new File(project, "src");
src.mkdirs();
/**
* Create the project/test directory.
*/
final File test = new File(project, "test");
test.mkdirs();
/**
* Create the project/lib directory.
*/
final File lib = new File(project, "lib");
lib.mkdirs();
/**
* Create the project/src/Main.leaf file.
*/
url = Resources.getResource(Autumn.class, "/com/mackenziehigh/autumn/resources/default-src-main.leaf");
code = Resources.toString(url, Charset.defaultCharset());
final File src_main = new File(src, "Main.leaf");
Files.write(code, src_main, Charset.defaultCharset());
/**
* Create the project/test/MainTest.leaf file.
*/
url = Resources.getResource(Autumn.class, "/com/mackenziehigh/autumn/resources/default-test-main.leaf");
code = Resources.toString(url, Charset.defaultCharset());
final File test_main = new File(test, "MainTest.leaf");
Files.write(code, test_main, Charset.defaultCharset());
}
}