New External Process Properties

May 9th, 2008, 11:00 pm by Mathias

I just finished adding a bunch of new properties for external processes:

  • ${gt;op1="5";op2="7"} — greater than
  • ${lt;op1="5";op2="7"} — less than
  • ${gte;op1="5";op2="7"} — greater than or equal
  • ${lte;op1="5";op2="7"} — less than or equal
  • ${eq;op1="5";op2="7"} — equal
  • ${add;op1="5";op2="7"} — addition
  • ${sub;op1="5";op2="7"} — subtraction
  • ${mul;op1="5";op2="7"} — multiplication
  • ${div;op1="5";op2="7"} — division
  • ${strlen;op="abcde"} — string length
  • ${count;op="abc;def;ghi";sep=";"} — count items in list string

I also moved a lot of the code out of MainFrame.java; that file is already big enough.

PS: I’m tremendously upset that Rice started the graduation fireworks early, and I missed the presidential reception. That’s what I get for working at 10 PM the night before graduation :(

Print This Print This   Email This Email This

“Open Java API Javadoc” Uses Compiler’s Version

May 9th, 2008, 2:43 pm by Mathias

I just committed a few changes that improve the way “Open Java API Javadoc” works. In the “Preferences” dialog, you can set the Javadoc version to use to “use compiler version”. When this option is selected, DrJava will open the Javadoc page of the same version as the compiler currently selected in the “Compiler Output” tab on the bottom right of the screen.

If JDK 5.0_13 is used as compiler, putting the cursor on File and selecting “Open Java API Javadoc for Word Under Cursor” will open http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html. But if JDK 1.4.2 is the compiler that DrJava uses, it will open http://java.sun.com/j2se/1.4.2/docs/api/java/io/File.html.

It’s these small details that help a lot and avoid confusion. Thank you, Dr. Wong, for this suggestion.

Print This Print This   Email This Email This

The Dumbest DrJava Bug Report Yet

April 22nd, 2008, 1:06 am by Mathias

Looking at DrJava bug reports often makes me upset: Either because there is a bug in DrJava, or because there might be a bug in DrJava, but I can’t know for sure because the user neglected to provide crucial information about the system DrJava was running on and the source code that was edited.

Then there is a completely different category: Bug reports filed by users who are so unbelievably ignorant that they report a bug in a "Hello World"-level program of theirs as a bug in DrJava. That really makes me lose my cool.

In order to vent a little, I herewith present you the dumbest DrJava bug report yet: [ 1914390 ] Unhandled exception: java.lang.NullPointerException.

Check out this masterpiece of software engineering:

  1. import java.io.*;
  2. import java.util.Scanner;
  3.  
  4. public class ScanFajlli {
  5. public static void main(String arg[]) throws IOException {
  6. Scanner s = null;
  7. // String f = null;
  8. try {
  9. s.useDelimiter(\t);
  10. s = new Scanner(new BufferedReader(new
  11. FileReader(“test.txt”)));
  12. while (s.hasNext()) {
  13. System.out.println(s.next());
  14. }
  15. } finally {
  16. if (s != null) {
  17. s.close();
  18. }
  19. }
  20. }
  21. }

It’s difficult to put the null value and the method call any closer together. Truly stunning.

Print This Print This   Email This Email This

The Builder Pattern in DrJava

April 16th, 2008, 5:57 pm by Mathias

I’m currently in the process of dealing with a confusing error message that one of our users got when he tried to save his files in DrJava. The problem is actually pretty simple: The files were read-only, and he got a “Permission denied” message that, and I agree, was too broad to nail down the exact nature of the problem.

What I’m planning to do now is display a better error message, and ask the user if he wants to overwrite the files even though they are read-only. Generally, this transfers to failed attempts to save. Here, I want to present the user with a list of files that could not be saved and ask the user if he wants to retry saving them. For read-only files, there is a clear solution available: make the files writable (even though Sun, in their inexplicable enterprise to keep Java development challenging and interesting has provided the File.setReadOnly() method, but no method to make a file writable). For writes that failed for other reasons, there is no clear panacea, but a “Retry” option seems useful.

There already was a dialog that does much of what I need, ScrollableListDialog. It has been used to display a list of files that could not be found and have therefore been removed from the project. Now I’m going to extend that dialog so it supports more than just one button (”OK”), lets the programmer know which button was pressed, and let the user select files in the list. So it’s getting a bit more complicated.

When I looked at the constructor, I decided that it was already way too complicated. It was badly designed and as a result error-prone and time-consuming to deal with. Here is the main constructor of the class:

  1. public ScrollableListDialog(Frame owner,
  2. String dialogTitle,
  3. String leaderText,
  4. Collection<?> listItems,
  5. int messageType,
  6. int width,
  7. int height,
  8. Icon icon,
  9. boolean fitToScreen) { /* … */ }

There were additional constructors that supplied default values so that not all values had to be set manually. Still, extending this structure even further meant adding even more constructors. I decided this was the wrong path. Instead, I chose the Builder design pattern.

The constructor for ScrollableListDialog is private, so instances cannot be created directly. Instead, there is a static class ScrollableListDialog.Builder that starts out with the default values in its fields. Builder has setters to change these values, and a factory method ScrollableListDialog build() that creates the ScrollableListDialog so configured. All of the setters return this, so setting different values can be chained together. Consider this explicit constructor call (using one of the other constructors that I haven’t shown):

  1. ScrollableListDialog dialog = new ScrollableListDialog(MainFrame.this,
  2. “Files Not Found”,
  3. “The following files could not be found and have been removed from the project.”,
  4. filePaths,
  5. JOptionPane.ERROR_MESSAGE);

This isn’t even the full-blown constructor shown above, but it already isn’t clear which of the strings is the title and which the text for the dialog body. If we add width and height, it isn’t clear which comes first. Type systems do not help here. Using the Builder pattern, the above becomes a bit more verbose, but much clearer:

  1. ScrollableListDialog dialog = new ScrollableListDialog.Builder()
  2. .setOwner(MainFrame.this)
  3. .setTitle(“Files Not Found”)
  4. .setText(“The following files could not be found and have been removed from the project.”)
  5. .setItems(filePaths)
  6. .setMessageType(JOptionPane.ERROR_MESSAGE)
  7. .build();

Not only is this type-safe, but it also pairs up the value being set with its name. Using the Builder pattern, Java can achieve something similar to named variables, where the name of the variable has to be stated when passing an argument. The order in which the values are set is arbitrary, and only those values need to be bothered with that are actually important in the specific situation.

I should mention two little issues: First, some people may ask why everything in the Builder class can’t be static. Then I would not have to new the builder class and create another object. In single-threaded code, that would be fine, but if the Builder class is accessed from two different threads simultaneously, their settings would interfere with each other. Second, the builder pattern makes it slightly harder to subclass ScrollableListDialog: In addition to subclassing that class, the Builder class has to be subclassed as well so that it actually builds an instance of the new subclass.

Now that the Builder is in place, I can easily extend ScrollableListDialog without having to worry that other users do not know what to pass in for the canSelect flag, for example.

Print This Print This   Email This Email This

The Last Two Weeks in Review

April 13th, 2008, 8:56 pm by Mathias

After finishing the change of the XML project file format in a series of all-nighters leading up to Thursday, April 3, with almost no sleep between Saturday and that Thursday, I unfortunately got sick again. It’s a shame: I get sick, I get better, I miss programming so much that I throw myself into endless hours of work, then I get sick again, probably from overdoing it and not eating properly. Before we made a new development release, drjava-20080411-1951-r4436, I managed to make one commit with several small bugfixes for the new XML project file format on Windows machines, but that was it. I was so sick, I couldn’t make it from my bed to the fridge. I’m afraid I’m not healthy yet, I’m still just as sick, but I’ve lost so much weight that it’s become easier to transport my body…

Now I added a small thing that I wish had gone into the release on Friday: An automatic update checker for DrJava. By default, it runs every seven days on start-up of DrJava and checks if there is a newer stable or beta release. If there is, then a dialog is displayed with a “Download” button that takes the user to SourceForge.

The settings can also be changed so that it

  1. checks for stable releases only,
  2. checks for stable and beta releases,
  3. checks for all releases, including development releases, or
  4. doesn’t check automatically.

The number of days between automatic checks can also be changed (all in the “Miscellaneous” pane of the “Preferences”). The dialog can be manually invoked by using the “Check for New Version of DrJava” menu item in the “Help” menu.

It works by checking the LATEST_VERSION*.TXT files on drjava.org:

http://www.drjava.org/LATEST_VERSION.TXT
http://www.drjava.org/LATEST_BETA_VERSION.TXT
http://www.drjava.org/LATEST_DEV_VERSION.TXT

For that, of course, DrJava requires internet access, but DrJava fails gracefully if it cannot access the internet. I chose “check for stable and beta releases” as default. It would have been nice if all the users of the development release were automatically notified of the next beta release.

I’m pretty sure we have to make a new release soon, though, because the Mac application doesn’t work for me and at least one other person.

Update

Corky and Dan figured out what the problem with the Mac application is: The DrJava file inside the application doesn’t have the execute permission set, because zip/unzip do not store permissions. We’ll revert to using gzip/gunzip, and then it should work again.

Print This Print This   Email This Email This

The Awful Project File Subsystem of DrJava Will Be Gone!

March 31st, 2008, 11:29 pm by Mathias

Hurray, we’re finally getting rid of the awfully over-engineered, needlessly complex, brittle and confusing project file subsystem of DrJava. We’ll keep the files in the current state and retain the ability to read old project files, but we’ll never ever touch them again, and we will always save in the new format.

The subsystem is too big to completely show here on the Code Pranger, but I found instructions on what must be done to add a property to the project file:

  1. /** This parser uses the s-expression parser defined
  2. * in the util pacakge. The SExp tree given by the parser is
  3. * interpreted into a ProjectFileIR that is given to the user.
  4. * This class must also deal with different versions of the project file.
  5. *
  6. * If at some point new information is to be stored in the project
  7. * file, the following places in the code that need to changed:
  8. * — If the new information pertains to a document, the
  9. * DocFile class should be augmented to store the new info.
  10. * — The interface for the DocumentInfoGetter should be
  11. * expanded to allow for the new data to be retrieved.
  12. * — Add a new clause to the else-if ladder in the FilePropertyVisitor.
  13. * — Add the new information to the DocFile from the
  14. * DocumentInfoGetter in the ProjectFileBuilder’s
  15. * addSourceDocument method.
  16. * — If the change is at the top level, you must modify the
  17. * evaluateExpression method in this parser and add the
  18. * corresponding methods to the ProjectFileIR, ProjectFileIRImpl,
  19. * and ProjectFileBuilder.
  20. */

So let’s see: If you are only adding some more data that describes a document, you just have to change three classes, and one method. That’s not true at all. You have to change the OpenDefinitionsDocument class, the inner classes ConcreteOpenDefDoc in both AbstractGlobalModel and DefaultGlobalModel, and perhaps one or more of the interfaces it implements as well, for instance INavigatorItem.

And if the data you are storing is of some new form, in a format for which we have no visitors (sub-parsers), then you have to write your own visitors. For example, for a list of breakpoints, I had to write this very short snippet, just to parse1:

  1. /* Parses out a list of breakpoint nodes. */
  2. private class BreakpointListVisitor implements SEListVisitor<
  3. List<DebugBreakpointData>> {
  4. public List<DebugBreakpointData> forEmpty(Empty e) {
  5. return new ArrayList<DebugBreakpointData>(); }
  6. public List<DebugBreakpointData> forCons(Cons c) {
  7. List<DebugBreakpointData> list = c.getRest().accept(this);
  8. DebugBreakpointData tmp = ProjectFileParser.ONLY
  9. .parseBreakpoint(c.getFirst(), _srcFileBase);
  10. list.add(0, tmp); // add to the end
  11. return list;
  12. }
  13. };
  14. /* Parses out the labeled node (a non-empty list) into
  15. * a breakpoint. The node must have the "breakpoint" label on it.
  16. * @param s the non-empty list expression
  17. * @return the breakpoint described by this s-expression
  18. /
  19. DebugBreakpointData parseBreakpoint(SExp s, String pathRoot) {
  20. String name = s.accept(NameVisitor.ONLY);
  21. if (name.compareToIgnoreCase(“breakpoint”) != 0)
  22. throw new PrivateProjectException(“Expected a breakpoint tag, “+
  23. “found: “ + name);
  24. if (! (s instanceof Cons))
  25. throw new PrivateProjectException(“Expected a labeled node, “+
  26. “found a label: “ + name);
  27. SEList c = ((Cons)s).getRest(); // get parameter list
  28. BreakpointPropertyVisitor v = new BreakpointPropertyVisitor(pathRoot);
  29. return c.accept(v);
  30. }
  31.  
  32. /* Traverses the list of expressions found after "breakpoint"
  33. * tag and returns the Breakpoint described by those properties. */
  34. private static class BreakpointPropertyVisitor implements
  35. SEListVisitor<DebugBreakpointData> {
  36. private String fname = null;
  37. private Integer offset = null;
  38. private Integer lineNumber = null;
  39. private boolean isEnabled = false;
  40. private String pathRoot;
  41. public BreakpointPropertyVisitor(String pr) { pathRoot = pr; }
  42. public DebugBreakpointData forCons(Cons c) {
  43. String name = c.getFirst().accept(NameVisitor.ONLY);
  44. if (name.compareToIgnoreCase(“name”) == 0) {
  45. fname = ProjectFileParser.ONLY.parseFileName(c.getFirst()); }
  46. else if (name.compareToIgnoreCase(“offset”) == 0) {
  47. offset = ProjectFileParser.ONLY.parseInt(c.getFirst()); }
  48. else if (name.compareToIgnoreCase(“line”) == 0) {
  49. lineNumber = ProjectFileParser.ONLY.parseInt(c.getFirst()); }
  50. else if (name.compareToIgnoreCase(“enabled”) == 0) {
  51. isEnabled = true; }
  52. return c.getRest().accept(this);
  53. }
  54. public DebugBreakpointData forEmpty(Empty c) {
  55. if ((fname == null) || (offset == null) || (lineNumber == null)) {
  56. throw new PrivateProjectException(“Breakpoint information “+
  57. “incomplete, need name, offset and line tags”);
  58. }
  59. if (pathRoot == null || new File(fname).isAbsolute()) {
  60. final File f = new File(fname);
  61. return new DebugBreakpointData() {
  62. public File getFile() { return f; }
  63. public int getOffset() { return offset; }
  64. public int getLineNumber() { return lineNumber; }
  65. public boolean isEnabled() { return isEnabled; }
  66. };
  67. }
  68. else {
  69. final File f = new File(pathRoot, fname);
  70. return new DebugBreakpointData() {
  71. public File getFile() { return f; }
  72. public int getOffset() { return offset; }
  73. public int getLineNumber() { return lineNumber; }
  74. public boolean isEnabled() { return isEnabled; }
  75. };
  76. }
  77. }
  78. }

And when the data that you want to add does not pertain to one particular document, but is rather a property of the entire project, then you need to change ProjectFileIR, ProjectFileIRImpl and ProjectFileBuilder. At least that’s what the description says. But you’ll also have to change the AbstractGlobalModel, its subclass DefaultGlobalModel, perhaps one or more of its superclasses or implemented interfaces, and definitely also the inner class ProjectFileGroupingState in AbstractGlobalModel. And since we want to be able to treat both project mode and flat-file mode the same, you then modify the interface ProjectFileGroupingState is implementing, and that forces you to change the inner class FlatFileGroupingState in AbstractGlobalModel as well. Oh, and of course you may have the parsing problem again and have to write that lil’ bit of code up there again.

Once you’ve done all that, you finally get to the easy part of adding GUI components to the “Project Properties” dialog. I hate GUI work, but after the ordeal described above, it felt like a vacation.

I’m the only developer that has added2 anything to the project file format during the last three years and eight months. I’m the only one who had to go through this pain since July 2004. In fact, I have suffered more than the person who initially created this complex construct, because after it was in place, only three small pieces of data were added, and then the project file format remained unchanged for a year and eight months, until I came along.

I have since added persistent breakpoints and watches from the debugger to the project file, persistent bookmarks and information about where the project’s jar file should be created, and using which options. I can’t exactly tell who came up with this code that has tortured me for hours because the SourceForge repository history doesn’t reach far enough back, and I wouldn’t mention the name here anyway, but it must have been written by someone who completely misunderstood fundamental principles of object-oriented design. The code is generally rather well written, assuming that nothing changes: It is correct and robust. However, the developer paid no attention at all to extensibility and flexibility, and made it a nightmare to extend the system.

This is a clear example of over-engineering, of thinking of doing it “the right way”, of creating a large object-oriented design that incorporates nearly all design patterns the developer partially understands. This is an example of code that made the original developer feel good about himself, because he came up with something he considered “pretty”, even though something much simpler and much less pretty would have sufficed, and in the long term prevented a lot of silent anger and premature aging.

Of course my XMLConfig class has about 600 lines of code and 500 lines of unit tests, and it relies on Java’s classes for dealing with XML files, but there’s no reason to reinvent the wheel. In fact, writing an S-expression parser for the project files is another mark of a bad developer who overestimates himself: Instead of looking around and using code that already exists and that has been tested, the macho programmer decides he can do better and without any reason writes the code himself all over again. And because this programmer isn’t as good as he thinks he is, and because he is human, he makes mistakes. The repository history shows several commits that had to fix bugs in the project file code.

XMLConfig is actually very simple code. It just takes a string, which describes a path, follows that path into the AST defined by the XML file, and then returns the value found at the end of the path. And once I got past writing and testing XMLConfig, which did not take long, I have been using it in almost every project that needs to save data. Instead of requiring code like the 80-line extract above, my code using XMLConfig can load a list of file names in a single line.

Soon, two of our current DrJava developers taking the COMP 312 “Production Programming” course will add a complex data structure to the project file, and I don’t want them to go through what I had to go through. I don’t want them to write something like the 80 lines above. And I want to get to a point where adding information to the project file is really easy, because right now the current project file subsystem acts as a huge deterrent against any kind of change.


Footnotes:
  1. Please don’t criticize the coding style. I wrote this code, but I mimicked the style the project file subsystem was written in. [back]
  2. One developer changed the format slightly by changing the name of a property. [back]

Print This Print This   Email This Email This

Creating a Branch for Your Project

March 24th, 2008, 10:41 pm by Mathias

Today I was asked how to create a branch for someone’s project. Branches are good for projects that take a little longer and involve a bit of trial and error, because you can commit your changes to the branch without disturbing the trunk. Committing often, with only small changes makes everyone’s life easier, because you can revert to a certain point, and of course the repository also serves as a backup of your code.

On the other hand, branches are separate from the trunk, so changes other developers make to the trunk do not automatically appear in the branch. If you use a branch, you have to do some work to keep up with the changes.

To create the branch, cd into the directory you’re working in (the topmost directory that has a .svn directory inside), e.g. if you’re working in /home/mgricken/drjava/drjava.autorefresh, and /home/mgricken/drjava/drjava.autorefresh is the topmost directory with a .svn subdirectory, then cd into /home/mgricken/drjava/drjava.autorefresh.

Copy the files into a branch by using svn copy:

Note: I’m using a backslash (\) at the end of a line and then indent the next line to format the command lines so that they display well on this blog. The backslash indicates that the entire command line should be typed as one line, without the backslashes at the end of the line.

svn copy . https://drjava.svn.sourceforge.net/svnroot/drjava/branches/<branch name>   \
    -m "Created <branch name> branch"

As branch name, I suggest something like autorefresh or drjava-autorefresh. For example:

svn copy . https://drjava.svn.sourceforge.net/svnroot/drjava/branches/drjava-autorefresh   \
    -m "Created drjava-autorefresh branch"

Whether you should name the branch drjava-autorefresh or just autorefresh depends on whether you have checked out the entire trunk with all the projects in it, like docs, platform, dynamicjava, etc. (entire trunk) or just the DrJava project (just DrJava). If you have checked out the entire trunk, then name the branch just autorefresh. If you have checked out only the DrJava project, name it drjava-autorefresh.

Subversion will automatically commit this change. When its done, you can go to http://drjava.svn.sourceforge.net/viewvc/drjava/branches/ and you should see a directory with the branch name you chose.

Now you need to do a fresh check out from that branch. You cannot continue to work with the files you have, because they use the trunk. So create a new directory, e.g. drjava.autorefresh.branch and check out from the branch:

cd ..
mkdir drjava.autorefresh.branch
cd drjava.autorefresh.branch
svn co https://drjava.svn.sourceforge.net/svnroot/drjava/branches/drjava-autorefresh

Subversion will now check out the files from the branch. Now you’re free to commit as often as you like, because you are just working with the branch, not with the trunk. Your changes won’t be visible to other developers working with the trunk. Just remember to always check out from your branch. To see whether you’re working with a branch or the trunk, you can run svn info. If you’re working with the trunk, among other things, it will output

URL: https://drjava.svn.sourceforge.net/svnroot/drjava/trunk...

If you’re working with a branch, it will output

URL: https://drjava.svn.sourceforge.net/svnroot/drjava/branches/drjava-autorefresh...

More information about creating a branch can be found in the Subversion manual.


Every once in a while, you will probably want to merge changes to the trunk into your branch, just so that when you’re finished with the project, your DrJava won’t look completely different from the others’ DrJava. To find out how your branch differs from the trunk, use svn diff:

svn diff https://drjava.svn.sourceforge.net/svnroot/drjava/branches/drjava-autorefresh   \
    https://drjava.svn.sourceforge.net/svnroot/drjava/trunk/drjava

If you want to compare your branch to a certain revision, you can add an @<revision number> after the trunk URL:

svn diff https://drjava.svn.sourceforge.net/svnroot/drjava/branches/drjava-autorefresh   \
    https://drjava.svn.sourceforge.net/svnroot/drjava/trunk/drjava@4412

To actually merge the changes to the trunk into your branch, use svn merge:

svn merge https://drjava.svn.sourceforge.net/svnroot/drjava/trunk/drjava .

That merges the differences between your branch and the most recent revision of the trunk into your working copy on your hard drive. If you want to a certain revision of the trunk, you can add @<revision number> after the trunk URL again. Since the changes are only merged into your working copy, not into the repository, you still have to do a commit.

When you run svn merge, it will output the files that were changed:

U src/edu/rice/cs/drjava/DrJava.java
U src/edu/rice/cs/drjava/DrJavaRoot.java

In some cases, Subversion isn’t able to merge the changes automatically. In that case, it will show that there was a conflict:

C src/edu/rice/cs/drjava/ui/MainFrame.java

In case of a conflict, Subversion will create three files in addition to the original file that has a conflict. In the above example, you will get:

src/edu/rice/cs/drjava/ui/MainFrame.java
src/edu/rice/cs/drjava/ui/MainFrame.java.mine
src/edu/rice/cs/drjava/ui/MainFrame.java.r<oldrev> (e.g. MainFrame.java.r4415)
src/edu/rice/cs/drjava/ui/MainFrame.java.r<newrev> (e.g. MainFrame.java.r4417)

The actual file in conflict, MainFrame.java, will contain all versions, separated by lines filled with <<<< and >>>> and ====. You can be pretty sure that it won’t compile. MainFrame.java.mine is the file you had in your working copy before you ran svn merge. MainFrame.java.r<oldrev> corresponds to the file in your branch before you changed it in your working copy. MainFrame.java.r<newrev> is the file from the trunk.

You’ll have to manually compare the three files and edit the file in conflict to produce a file that makes sense. This might involve talking to other developers to figure out what their changes do and how they work. After you have edited the file, MainFrame.java in this example, so that it compiles and is consistent with both your work and other developers’ work (don’t just copy MainFrame.java.mine over to MainFrame.java; that would dismiss the changes by other developers and probably make them angry ;-), you need to run svn resolved:

svn resolved src/edu/rice/cs/drjava/ui/MainFrame.java

Again, since merging happens in your local working copy, if you have any conflicts and you resolved them, you still have to make a commit (which will go into your branch) to move the changes into the repository.


When you have finished your project and you want to merge your branch into the trunk, you pretty much do the same as described above. This time, you check out the trunk, and then specify the URL of your branch for the merge:

svn co https://drjava.svn.sourceforge.net/svnroot/drjava/trunk/drjava
cd drjava
svn merge https://drjava.svn.sourceforge.net/svnroot/drjava/branches/drjava-autorefresh .
svn commit

More information about diff’ing and merging can be found in the Subversion manual.


This was a pretty long post, and some svn commands are a little complicated. I pretty much always forget how to do things and have to look them up again. Sometimes I accidentally create a branch or merge and then have to delete or revert. But don’t worry, you cannot permanently screw things up, and we’re here to help.

In general, we as the DrJava team probably do not use branches as often as we should. We all know how good it feels to make a commit: It gives you a sense of security, the ability to roll back to that point, and the repository serves as a backup of your code. Branches allow these kinds of “micro-commits” that we should be making. Instead of embarking on a long journey with many changes and no stops in between, we can commit to our branch many times, every time we have reached a nice place to stop, not the top of the mountain, the end of our project, but a plateau, if you will.

Print This Print This   Email This Email This

New Ideas for Properties

March 12th, 2008, 1:48 pm by Mathias

I have already implemented a few of the properties mentioned below but then had to take a detour because of attributes for the properties. However, here are a few new properties that I came up with:

  • ${tmpfile;content="..."} (done) — this creates a temporary file and puts the value of the content attribute into the file.
  • ${tmpfile;name="..."; content="..."} (done)
  • ${tmpfile;dir="..."; name="..."; content="..."} (done)
  • ${tmpfile;keep="true"; dir="..."; name="..."; content="..."} (done)
  • ${input}– asks the user for an input string
  • ${input; message="..."; title="..."}

Again, can anyone come up with other properties that might be useful? Please post a comment.

Thank you!

Print This Print This   Email This Email This

Performance Improvement and Feature Request Done

March 10th, 2008, 10:28 pm by Mathias

After finishing the separation of the maximum heap size argument from the JVM arguments, I implemented the code to detect out of memory situations in the master and slave JVM. If one of the JVMs runs out of memory, a dialog is displayed that suggests that the user increases the respective maximum heap size. This finishes up feature request [ 1908470 ] Separate Memory Preferences, Better Out of Memory Errors.

I then optimized the creation of the “Auto-Complete” list. It is no longer necessary to create wrappers around JavaAPIListEntry instances because both that class and GotoFileListEntry now share a common superclass. Toggling the “Java API” switch is a lot faster and uses less memory now. Even with “Scan Class Files After Every Compile”, which allows auto-completion of inner classes, the function is fast, at least on local drives.

I also changed three keyboard shortcuts. I added “Shift-Mask-O” for “Open Folder”, and I changed “Revert to Saved” from “Mask-R” to “Shift-Mask-R” because I accidentally hit “Mask-R” and lost a whole lot of changes. I also changed the “Are you sure?” dialog for “Revert” to have “No” as default selection. “Mask-R” is now “Rename”.

I also moved all the “Save Window Position” and “Reset Window Position” into their own pane, “Window Positions” under “Display”. The “Display” pane in the “Preferences” was getting a bit cluttered.

Print This Print This   Email This Email This

Fortnight-and-Changely Log 02/24 to 03/10

March 10th, 2008, 3:41 pm by Mathias

On February 25, I got attributes working; they modify the output of variables. For instance, you can tell a variable that returns a file to make that file relative to some directory.

I spent the remaining days fixing small things that had gone wrong, like the escaping behavior for the quote-balancing tokenizer. I changed the escape character from backslash () to dollar ($), and then reversed the behavior of quotes and keywords that start with the escape character: Some text is considered a quote or a keyword unless it is prefixed with the escape character (e.g. ${foo}). Before, the first character was considered an escape character, so to get the quote ${foo}, you had to write $${foo}, and that’s just dumb. This problem is similar to the grep vs. egrep situation.

I also changed the way makeRelativeTo works on Windows: If the file is on a different drive than the directory it should be made relative to, then the absolute path of the file on its drive is returned. That’s not a relative path, but it’s as relative as it can get. The “Execute External Process” variables now work correctly on Windows, too.

Over Spring break, unfortunately I got sick, so I didn’t get as much done as I had wanted. On Tuesday, March 4, I think I finally got the behavior for properties and attributes right.

I then embarked on a small side project: I separated the “maximum heap size” settings for the Main and the Interactions VM from the JVM_ARGS settings and created settings for the sizes of their own. This also involves “sanitizing” the JVM_ARGS lines and removing or copying the size into the correct property. I just committed this.

Now we can catch exceptions like com.sun.jdi.VMOutOfMemoryException and java.lang.OutOfMemoryError and directly ask the user to increase the memory size.

Print This Print This   Email This Email This

A Whole New Beethoven Experience

March 10th, 2008, 5:20 am by Mathias

On Saturday, March 8, I had to get up early and only after a couple hours of sleep, but the anticipation of the event about to happen made any tiredness fade away. It was like Christmas. Today was all about Beethoven, my favorite composer. Today, the Houston Symphony offered a program of Beethoven material spanning from 10 AM to 11:30 PM. It was truly like Christmas.

I had heard about the “Beethoven Experience” a few months ago, in the program of another concert I had attended, the one featuring the also amazing Dame Glennie, and I was so happy to find out that the Houston Symphony offered a student rate for just $25. Twenty-five dollars for a day of music and information about my favorite composer… This caused months of tingling suspense.

There was also a Wavelength offer for the 7:30 PM “Beethoven’s Egmont” concert and a following after-party for $20, and I think again the organizers of Wavelenth, a program for music enthusiasts aged 18 to 29, was incredible. I had taken advantage of their offers many times before, but this time $25 for the whole day was the better deal.

I still feel indebted to Carey Kirkpatrick, who seems to be the organizer of Wavelength and whom I have finally met in person, because she let my friend and me have a Wavelength gift bag each, and she let us attend the Wavelength after-party even though this time we weren’t Wavelength participants at all. I like having taken advantage of her, because when I asked her if she had been able to enjoy the day’s program, she said she spent the day making the gift bags. Carey, the gifts were truly appreciated, and I’m sure with a doctorate degree in music, you knew everything I learned about Beethoven already anyway.

The day started at 10 AM with “Beethoven’s Missing Notes”, a program targeted at children and starring a lederhosen-wearing, strong German-accent wielding Beethoven actor. It was aimed at children, but it was just as educational for adults. It condensed the biographical information given on the first page of the program brochure and interspersed in the following pages for those adults with a short attention span. To make the story short, Beethoven had lost the first page of the sheets for his 5th symphony, and through asking him questions and having him tell us about his live, the audience was supposed to help him recreate the beginning. The final clue was that he wrote about what he heard. I’ve been told the 5th symphony is about fate knocking on one’s door; in this case, it was the landlord knocking on Beethoven’s door, duh-duh-duh-dah, reminding him that the rent was due. I was thoroughly entertained, perhaps more so than the kids.

The pieces played, mostly in part, were:

  • Beethoven, Overture to Coriolan, Opus 62
  • Beethoven, Bagatelle in A minor, WoO 59 (Für Elise)
  • Haydn, Symphony No. 60 in C major (II Distratto), VI Finale: Prestissimi
  • Haydn, Symphony No. 94 in G major (Surprise), III Menuet and Trio: Allegro molto
  • Beethoven, Symphony No. 1 in C major, Opus 21, IV Adagio: Allegro molto e vivace
  • Beethoven, Symphony No. 6 in F major, Opus 68 (Pastoral), III Merry assembly of country folk: Allegro
  • Beethoven, Symphony No. 5 in C major, Opus 67, I Allegro con brio
  • Beethoven, Symphony No. 5 in D minor, Opus 125 (Choral), II Molto vivace

The second part of the program, starting at 11:15 AM, featured the incredibly gifted pianist Carl R. Cunningham, who also contributed much to the program brochure. He played and commented on Beethoven’s Sonata for Piano No. 24 in F-sharp major, Opus 78. He also played one of only four polonaises composed by Beethoven, the Polonaise in C major, Opus 89, and finally the 14th Sonata for Piano in C-sharp minor, Opus 27, No. 2, to everyone better known as the “Moonlight Sonata”. His playing was simply amazing.

After a lunch break, the discussion “Beethoven’s Triumph Over Deafness”, lead by host Hans Graf, and the performance of several pieces composed by Beethoven followed. Dr, Richard Stasney, otolaryngologist and director of The Texas Voice Center, provided commentary on the progression of Beethoven’s hearing loss and his other illnesses. The pieces performed were, in order:

  • Violin Sonata No. 5 in F major, Opus 24 (Spring), I Allegro
  • Violin Sonata No. 9 in A major, Opus 47 (Kreutzer), U Adagio sostuneto-Presto-Adagio
  • Violin Sonata No. 10 in G major, Opus 96, I Allegro moderato
  • Quartet for Strings No. 15 in A minor, Opus 132 (Heiliger Dankgesang), III Canzona di ringraziamiento, molto adagio

The interesting thing here is that, of course, none of the pieces were played in full. So, rather than playing them linearly, full piece by full piece, they were played laterally, sideways. This exposed the effect Beethoven’s maturing and his deafness had on his music.

While the first piece, Opus 24, was still quite conservative and represented a learning Beethoven, the second piece, Opus 47, contains completely different, more muscular, more exhausting piano music; it was much less conservative, but much more Beethoven. Still, he had begun to struggle with hearing loss.

By the time Beethoven composed the piece Opus 96, his hearing loss was near complete. He had stopped performing the piano parts himself, and since there was no one that could replace him with his physical style at the piano found in Opus 47, Beethoven’s compositions had become quieter. At the same time, his deafness perhaps forced him to perform a deeper, inner analysis, because Opus 96 sounds quieter, yet very finely woven.

Opus 132, Beethoven’s holy chant of thanking, was composed during a period of complete deafness and horrible suffering due to other diseases. The heaviness of the molto adagio almost made my eyes water, yet towards the end of the piece, there was the distinct determination present that Beethoven would keep composing until the end of his life.

A part of Beethoven’s “Heiligenstadt Testament”, a last will written on October 6, 1802, to his brothers, Carl Casper and Nicolaus Johann, puts this force of will into writing, and it touched me personally very much:

Such circumstances brought me to the brink of despair, and had well nigh made me put an end to my life: nothing but my art was holding my hand. Ah! it seemed to me impossible to quit the world before I had produced all that I felt myself called to accomplish.

It was very important that Hans Graf decided to place this lateral, analytical session early in the afternoon, because it allowed the audience to put every piece played later into the context established by the four pieces played in this part, and to Beethoven’s style, his deafness, and his suffering.

I enjoyed this part of the day very much. I have been a Beethoven fan for pretty much my entire life as I remember it, but the pieces I liked, and thus had listend to the most, were Beethoven’s Symphony No. 9 and his Missa Solemnis, Opus 125 and Opus 123, respectively. I also liked the Romance for Violin No. 2 in F major, Opus 50, very much, but the Beethoven I knew was the one in the Ninth and Missa Solemnis. I knew about his by then complete deafness, I knew that when the Ninth premiered, he was conducting, but only as a secondary conductor, and he was off by a few bars at the end, but I didn’t know much about the course of Beethoven’s illnesses before and after that. This section very much broadened my horizon and my appreciation of Beethoven’s music. I was truly a student in this section, and in the following one, so I think the student tickets were very much justified.

After another break, Hans Graf returned with the entire symphony orchestra, for “Inside the Fifth”. In this session, Music Director Graf was conducting the first movement of Beethoven’s Symphony No. 5, I Allegro con brio, probably one of the most famous pieces of music ever written. But it was more like a rehearsal than a concert, and this was reinforced by most of the orchestra wearing every day clothes. I think this was a great decision. Because of this, and because of the chamber music character of the previous session, I had the feeling of spending an afternoon with the musicians and the conductor, rather than attending a series of concerts.

I didn’t have pen and paper with me, and I wanted to enjoy the performance anyway, and recording devices are, as far as I know, forbidden in the Houston Symphony, so unfortunately I have forgotten most of what Mr. Graf was able to teach us about the composition, literally, of the Fifth. He let the orchestra play certain only parts, sometimes asked one part of the orchestra to remain quiet, play louder, or improvise in another key, perhaps a key that wasn’t available on the instruments in Beethoven’s time. This allowed the audience to feel the genius behind the Fifth, why it was written the way it was, and why it still probably would have been better than it would have been had it been written 100 years later by someone else.

Mr. Graf explained why the trumpets stayed on G, even though with modern instruments, they would have played a different note, but why staying on G was overall better. He numerously showed where the “stem cell” of the Fifth, the “duh-duh-duh-dah”, shows up, and how Beethoven treated the entire orchestra as one instrument: In one bar, it is played by the violins, in another by the cellos, and in another one on the drums, but it is always present. Mr. Graf explained how another related idea was coined; how often in the orchestra was divided juxtaposition; and how this was in the end resolved by the most concise ending possible. I feel I understand the Fifth a little bit more; I feel terrible for already having forgotten so much; and I know for sure that some of the recordings of Symphony No. 5 I have are garbage. Or perhaps there just is no replacement for hearing something live, and this session, more rehearsal than concert, was more alive than live.

The next two sessions were, unfortunately, for me the low points of the day. At 4 PM, a fantastic string quartet played Beethoven’s Quartet for Strings No. 7 in F major, Opus 59, No 1. (Rasumovsky No. 1), which also happens to be one of the longest string quartets ever. The musicians were very good, the interplay between the instruments was amazing, how sometimes they would all play the same theme, sometimes echo each other, other times form groups of rivaling instruments. The quartet played movements III Adagio molto e mesto and and movement IV Allegro without pause in between, as with the other movements. Maybe because of that, or perhaps because of the general length, what appeared to me as movement III, was the “movement without end”. There were three, four opportunities to bring the movement to a graceful halt, but no, the musicians kept on going. Of course, it wasn’t their fault at all, they were admirably skillful, but this quartet just seemed longer than it had to be written by Beethoven, and it also appeared to be a gap filler as there was no additional commentary on the relevance of the piece in Beethoven’s life. According to the Opus number and by the style, I can only assume that it came between the more athletic, masculine Beethoven of the Kreutzer Sonata and the time when his hearing loss was taking a strong impact. A little more context here would have helped.

The session at 5 PM had the skillful pianist Cunningham return and play Six Variations on an Original Theme for Piano in D major, Opus 76 (often called the Turkish March), and the Sonata for Piano No. 15 in D major, Opus 28 (Pastoral), which is often overlooked because it is overshadowed by the preceding Spring and Moonlight sonatas and the following piano sonatas (Opus 31), the piano variations “Prometheus” or “Eroica” (Opus 35) and Symphony No. 2 (Opus No. 6). The six variations of Opus 76 were normal, running notes in the 1st; accented leaps in the 2nd; double-notes in the 3rd; rocking chords in the 4th; arpeggios in the 5th; and a 6th variation, played very fast, summarizing the elements heard in the previous variations. While listening to those variations was entertaining and a simple exercise for the listener, it lacked the context in Beethoven’s life, and the same is true for Opus 28. From what I have gathered in the “Beethoven’s Triumph over Deafness”, I can only conclude that Opus 76 was during the already quieter time of Beethoven, when deafness had struck, and he had to work out melodies and harmonies in his head rather than hammering them out on the piano himself; while Opus 28 was still a learning Beethoven.

At 7 PM, there was a lecture that summarized and interpreted the “Egmont” play by Johann Wolfgang von Goethe (another one of my favorites) and the incidental music composed by Beethoven (Opus 84). Egmont is a Flamish warrior and refuses to run from or submit to the Spanish rulers, and is thus sentenced to death; attempts to free him fail, and his mistress takes her life. The play ends tragically with the death of Egmont, issuing a last call to independence and resistance. The comparison was made that in the end, the hero of Goethe’s play “Egmont” and Beethoven’s incidental music was Beethoven himself.

The concert of Beethoven’s “Egmont” was the main event of the day, and a separate concert in itself. It was interesting to see some of the musicians in the third change of wardrobe. The concert hall filled up considerably. This was also the event sponsored by Wavelength, at $8 for the concert, or $20 for the concert and an after-party. Again, I think we got the better deal, and the folks at Wavelength were very generous to give us Wavelength gift bags and access to the after-party; I hope that this generous gesture will pay off by this article and my word of mouth.

I have to comment that the choir’s accentuation of the German lyrics was quite bad. Having been a member of a choir myself, I remember that maintaining the breaks between words is almost the most important thing. A native-speaker, I could not understand what the choir was singing without glancing at the English translation on the screens at the side, which brings me to my second comment: Sometimes the written translation was way off. Perhaps it only seems to a native speaker like that, because a change in sentence order already reveals a difference, even though the meaning of the sentence was preserved; but even keeping that in mind, I thought the translation could have maintained the meaning of the sentence and been more faithful to the original.

I just wish Wavelength had considered advertising the whole day at $25 or perhaps a little more for non-students: I think the turnout for the whole day would have been much greater, and many of the Wavelength members are still students, and the whole day was very much a learning experience. The friend accompanying me afterwards mentioned how tired she was, and I immediately likened it to a day full of lectures. Unless you allowed yourself to nap, to ignore what was being said, and to not look for the connections between the pieces played, this day was very much an intellectual exercise, especially for those not particularly musically trained, like me.

The last event of the day was a screening of the movie “Immortal Beloved”, about two letters written by Beethoven but never sent, to the unknown love of Beethoven’s life. Since I had already seen this movie and the event overlapped with the Wavelength after-party, we decided to instead head one and a half blocks east to Hunan. The snacks were suave, the drinks that could be obtained with the drink coupons were extravagant, and both my friend and I were able to make some interesting connections there. I wish we could have stayed longer, but the whole Beethoven Experience was a long day, for my friend, who had to go to work for a few hours beforehand, and for me on two hours of sleep.

I can only thank the Houston Symphony who created this event, for Mr. Graf’s education, Dr. Stasney’s education, the soloist performances of Mr. Cunningham, Ms. Joh, Ms. Dulgarska, Kurt Johnson, Mr. Holshouser accompanying on the piano; the quartet of Ms. Fuller, Mr. Halen, Mr, Brooks, and Mr. Brinton; the quarted of AMs. Silivos, Ms. Owen, Ms. Porfiris, and Mr. Dvorak; the soloists for “Egmont”, and of course the entire Houston Symphony and the Houston Symphony Chorus. A special thanks goes to Carey Kirkpatrick and the organizers of Wavelength.

Thank you all so much. Beethoven has always been my favorite composer, and now even more so I know why.

Much of this information comes from the program notes by Carl R. Cunningham, Hans Graf’s oral remarks, and from “The Beethoven Compendium - A Guide to Beethoven’s Life and Music”, edited by Barry Cooper.

Print This Print This   Email This