I was feeling bored with nothing to do (well, sort of) and decided to write a program that runs a program in Java. What it pretty much does:
- Creates a new file with the name
classname + ".java" - Write the code into that file
- if it is not already compiled, run
javac filename.javaon the command line - run
java filenameon the command line
The code is below:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Represents some Java source code.
*
* <p>
* This source code then can be ran via the {@link #run()} method, or can be
* just used as a storage for source code.
* </p>
*/
public class JavaCode {
private static final String JAVA = ".java";
private static final String CLASS = ".class";
private static final String COMPILE = "javac ";
private static final String RUN = "java ";
private static final String IO_ERROR_MESSAGE = "An I/O error has occured.";
private final String code;
private final File codeFile;
private final String className;
private boolean isCompiled = false;
/**
* Creates a new <code>JavaCode</code> object.
*
* @param code
* The source code.
* @param className
* The name of the <code>public</code> class
*/
public JavaCode(String code, String className) {
this.code = code;
this.className = className;
this.codeFile = new File(className + JAVA);
try {
// Delete any previous file with same name
this.codeFile.delete();
this.codeFile.createNewFile();
writeCodeToFile();
} catch (IOException e) {
System.err.println(IO_ERROR_MESSAGE);
e.printStackTrace();
}
codeFile.deleteOnExit();
new File(className + CLASS).deleteOnExit();
}
private void writeCodeToFile() throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(
this.codeFile))) {
writer.write(this.code);
}
}
/**
* Gets the source code of the object.
*
* <p>
* Equivalent to {@link #toString()}.
* </p>
*
* @return the source code
*
* @see #toString()
*/
public String getCode() {
return code;
}
/**
* Gets the class name of the source code.
*
* @return the class name of the source code
*/
public String getClassName() {
return className;
}
/**
* {@inheritDoc}
*
* Equivalent to {@link #getCode()}.
*
* @return the source code
*
* @see #toString()
*/
@Override
public String toString() {
return getCode();
}
/**
* Runs the source code using the command line.
*
* <p>
* Runs the program by first creating a new file named
* <code>classname.java</code>, replacing <code>classname</code> with
* whatever the class is named, executing <code>javac classname.java</code>,
* then <code>java classname</code>.
* </p>
*
* <p>
* It will delete the <code>.java</code> and <code>.class</code> files on
* exit of program.
* </p>
*/
public void run() {
try {
if (!isCompiled) {
ProcessInfo info = runProcess(COMPILE + this.className + JAVA);
if (info.exitCode == 0) {
isCompiled = true;
} else {
// Compiler error
System.err.print(info.errorMessage);
return;
}
}
runProcess(RUN + this.className);
} catch (IOException e) {
System.err.println(IO_ERROR_MESSAGE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private ProcessInfo runProcess(String command) throws IOException,
InterruptedException {
Process process = Runtime.getRuntime().exec(command);
System.out.print(getLines(process.getInputStream()));
process.waitFor();
return new ProcessInfo(process.exitValue(),
getLines(process.getErrorStream()));
}
private String getLines(InputStream instream) throws IOException {
String line = null;
StringBuilder result = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(instream));
while ((line = in.readLine()) != null) {
result.append(line).append('\n');
}
return result.toString();
}
private class ProcessInfo {
private final int exitCode;
private final String errorMessage;
private ProcessInfo(int exitCode, String errorMessage) {
this.exitCode = exitCode;
this.errorMessage = errorMessage;
}
}
}
As an example, I ran a simple "Hello, World!" program and another (not-so-simple) Project Euler #1 that I wrote.
Code:
public static void main(String[] args) {
new JavaCode("public class Main{public static void main(String[]args){System.out.println(\"Hello, World!\");}}", "Main").run();
new JavaCode("public class MultipleFinder {private static final int MAX_NUMBER = 1000;private static final int[] MULTIPLES = new int[] { 3, 5 };public static void main(String[] args) {long time = System.nanoTime();int sum = 0;for(int multiple : MULTIPLES) {sum += triangle((MAX_NUMBER - 1) / multiple) * multiple;}sum -= triangle((MAX_NUMBER - 1) / (MULTIPLES[0] * MULTIPLES[1])) * (MULTIPLES[0] * MULTIPLES[1]);System.out.println(\"Result: \" + sum + \"\\nTime used for calculation in nanoseconds: \" + (System.nanoTime() - time));}private static int triangle(int i) {return (i + 1) * i / 2;}}", "MultipleFinder").run();
}
Note that I just put it on one line: I was too lazy to format it right.
The result makes sense (hover over to see the results; it is hidden for those that haven't solved Project Euler #1 yet):
Hello, World!
Result: 233168
Time used for calculation in nanoseconds: 34486
Concerns:
- Is there a simpler way to do what I did here?
- Anything in the
java.niopackage that can make my life easier? - Is JavaDoc okay?
- And as usual, anything else?
Some notes:
- The program may be ran multiple times. If this was the case, I would already have a compiled class file ready to be ran, therefore making it faster.