Question

I'm going to split this into two questions to be as concise as possible. Some time ago I began making a text editor for educational purposes and have been adding to it here and there. Along the way I decided I wanted it to compile and run java programs. I used the JavaCompiler class to compile and a simple exec() method to execute a command to run the program in the console. The source code had become a jumbled mess probably because it was written in unorganized blocks of random code so I recently decided to rewrite it. While rewriting the program I added a console window below my text area to display console output from the compiler as well as whatever exceptions are thrown at run time, etc.

While writing the code to either display the output from the JavaCompiler or the console output via a BufferedInputStream I realized I was doing these two things that I always thought of as similar in very different ways. My first question is mostly just asking your opinion, I'm not aware of a class in Java that will run a java program like a simple IDE would so executing a console command is the only way I know to do this, the same could be done to compile the code using the javac command and I'm curious if there are any advantages to using the JavaCompiler class?

Finally, I've been adding some neat functionality to this thing. I would like to be able to keep track of and organize java projects since I'm compiling and running java programs. I want to be able to tell what project any given file belongs to as well as be able to tell the difference between a package and a directory in the project so I can look inside the package folders to compile source files and execute the correct run command from the binary folder to run it, does Java provide a way to do this or am I going to have to do something like create custom classes that inherit from file but have additional fields to determine the type of file(directory or package)? Or is there a better way to implement this?

Thanks for your time.

Was it helpful?

Solution

The two questions you have kind of go hand-in-hand. Let's start with the meta data. The best way to figure out how to track project meta data for an IDE is to look at how the projects that are already established try to do it.

Eclipse and Netbeans both use project folders that are created inside the root of the project to hold meta data (.settings in Eclipse, .nbproject in Netbeans). Meta data like source folders, compiler target directories, source level, etc. are held in meta data files, mostly XML.

You'll probably want to follow something similar, then consider adding common project archetypes, like /src and /classes or /src/main/java and /target. This leads into the second part of your question, compiling.

Netbeans itself doesn't use the compiler directly. Eclipse does, but that's because Eclipse has its own internal compiler implementation to provide extra warnings and configuration that's not available in the default compiler. For your IDE, I'd take a look at how Netbeans manages its projects and compiles them. Instead of calling javac, Netbeans uses Ant and an internal build.xml file. If you aren't familiar with Ant, it's simply a tool that allows you to set up tasks in XML that get interpreted and run by Ant. For example, you can use Ant to compile Java source code, generate documentation, and create distribution JARs. It's a pretty powerful tool that allows you to automate your entire build process as a sort of script.

You could follow this same idea. New projects in your IDE would have a default build.xml file that will build their project using some default Ant targets. You can access property files from the build.xml as well, so you could store information about source directories in a properties file, along with any other customizable information that you want to use in your build script, and import them in the build script. Here's an example setup.

File structure:

| Project root/
|   .myideproject/
|     build.xml
|     project.properties
|     ui.properties
|   classes/
|     <compiled .class files>
|   doc/
|     <generated javadoc files>
|   lib/
|     <3rd-party .jar files needed for compilation>
|   src/
|     <user .java files>

The build.xml file is what's used by your IDE to compile things. You could either require the user to install Ant (a quick and painless process) or you could bundle it like Netbeans does (more work for you). In either case, if you nest your build.xml inside the .myideproject directory, your build file will look like this:

<project name="<User's project name>" default="compile" basedir="..">
    <!-- ... -->
</project>

Note 2 things:

  1. The "User's project name" is supplied by the user when they create the project.
  2. The "default" attribute will point to a target inside the build file.
  3. The "basedir" attribute will rebase the build process so that it points to directories in the project root, not in .myideproject.

Next you need to import your project.properties file (ui.properties is for you to save UI-related data and isn't for the build script, project.properties contains everything the build script needs to run properly). This is how you import properties into your Ant project:

<project ... >
    <import file=".myideproject/project.properties" />
    <!-- ... -->
</project>

This will make all the properties in project.properties available using the directive ${propname}. Now, src and classes are configurable directories in the project.properties file, and we can use them in our compile target, like so:

<target name="compile">
    <javac srcdir="${src}"
        destdir="${target}"
        classpath="${classpath}"
        source="${src.jdk}"
        target="${target.jdk}"
        debug="on"
        debugLevel="lines,vars,source" />
</target>

For the above target, the properties file could look something like this:

# delimits source directories containing .java files using ':'
src=src1:src2
# only 1 directory, it's where the .class files go
target=classes
# ':'-delimited list of jars or globs which are needed for compilation
classpath=lib/*
# Determines the -source value on the javac process
src.jdk=1.6
# Determines the -target value on the javac process
target.jdk=1.6

The comments are just there for clarity. This properties file would be built from your IDE's UI using user input, so if they add or remove source folders, the src value in the properties file is changed automatically, then they can rebuild the process.

This should be enough to get you started. Some things to consider:

  • You're going to need a run target that uses the java task along with properties set by the IDE to tell it which class to use for the main method.
  • You may want to consider support for Maven projects as well, which help users resolve dependencies automatically. This would be way more work for you, and you should probably wait til you get the Ant support done
  • You'll need to redirect standard-out/err/in from the Ant process to something the user can interact with. You don't have to support standard-in, but you should at least support standard-out/err

Last remark per your comment: you'll want to use a JTree with a custom node class that extends DefaultMutableTreeNode to create the view, but it doesn't have to be a view of the file-structure so much as just a view of the project, so maybe it could be something like:

+ Project 1 Name
| + Source Folder 1
|     <.java files>
| + Source Folder 2
|     <.java files>
| + Source Folder 3
|     <.java files>
+ Project 2 Name
| + Source Folder 1
|     <.java files>
| + Source Folder 2
|     <.java files>
| + Source Folder 3
|     <.java files>

The structure can be based on the file system, but only show information the user needs, like his source directories. You can expand on that idea from there.

Good luck, sounds like an interesting project. Hopefully my suggestions make some sense and can help you out. Let me know if anything needs clarification.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top