I'm pleased to report that XSLTXT recently got mentioned in an article on IBM's developerWorks: XML zone that talks about alternative syntaxes for XML.
Coincidentally I'm using XSLTXT at work at the moment as part of a system for editing Java UIs with NetBeans. Rather than using the code generation facilities NetBeans provides I've developed our own that takes the xml description of the screen layout produced by NetBeans and transforms it with XSLTXT to produce a dialog class. This does actually have some advantages for us over using NetBeans directly.
- The generated classes are designed for extending by subclassing instead of by editing directly. This makes it much easier to manage our customizations of the generated code.
- We can generate additional code to link each UI screen to the persistent object that it is editing.
- We use a number of custom wrapper classes round standard widgets like JButton and have a few larger composite widgets as well. Having our own generation system is easier than setting these things up as beans so that they can be directly used in NetBeans.
More file renaming, this time in Java. Demonstrates why you wouldn't really want to do it this way. Sorry about the wacky formatting, I was trying to reduce the line length so it fits on the page better.
import java.io.*;
public class Rename {
public static void main(String[] args) {
File directory =
new File(System.getProperty("user.dir"));
String[] textFileNames =
directory.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".txt");
}
});
for (int i = 0; i < textFileNames.length; ++i) {
File oldFile =
new File(textFileNames[i]);
File newFile =
new File(textFileNames[i].
substring(0,
textFileNames[i].length() - 4) +
".xml");
oldFile.renameTo(newFile);
}
}
}
Another round of renaming scripts. This time I've tried to provide "functional" and "imperative" styles for Python and Perl. For this sort of problem I personally think of it as a problem of processing a list of names so I prefer the functional approach. If the end result were to be a list of some sort then I think the functional approaches win hands down. The presence of the rename "side effect" means that they're not pure but The Python imperative style is courtesy of Jarno Virtanen with a downgrade by me so it matches the functionality of the others.
As a Python script, functional style:
import glob
import os
map(lambda f: os.rename(f + '.txt', f + '.xml'),
[os.path.splitext(f)[0] for f in glob.glob('*.txt')])
As a Python script, imperative style:
import glob
import os
for filename in glob.glob('*.txt'):
base = os.path.splitext(filename)[0]
newname = '%s.%s' % (base, 'xml')
os.rename(base + '.txt', newname)
As a Perl script, functional style:
map(rename($_ . '.txt', $_ . '.xml'),
map(s/\.txt$//, glob('*.txt')))
As a Perl script, imperative style:
foreach $f (glob('*.txt')) {
$f =~ s/\.txt$//;
rename($f . '.txt', $f . '.xml');
}
Finally the shortest solution, provided by John Masson. As a Bash shell script from the command line:
for i in *.txt; do mv $i ${i%%.txt}.xml; done
Jarno Virtanen has a much nicer pythonization of the elisp bulk file rename script here. As he says, his looks much more Pythonic. Mine was a literal translation from an elisp semi functional approach using the same constructs.
I'll try rewriting the perl in a more perlish style later. Probably involve using foreach $f (readdir(DIR)) as an outer loop rather than trying a functional approach. Of course it's also possible to write the elisp as an imperative loop rather than in a semi functional style.
It's interesting that once I had a solution it was easy to produce a literal translation, but it didn't occur to me to recast the problem using a different approach. If I'd have started with a Python script would I have written the elsip using dolist or while?
I wanted to rename all of the files in a directory that ended in .txt so that they ended in .xml . Clearly some sort of script was needed, but what language to use? I chose elisp, because I was working in emacs and the interactive help made it easy to find the functions I wanted. Thinking about it later I wondered how I'd do this in other languages so here also are python and perl versions with the same functionality. None are heavily tested but all work if run in the directory containing the files to rename. My final impression? The elisp one was the quickest to write and I think is the easiest to manage for this job.
In elisp:
(defun rename-some-files (dir) "Rename some files." (interactive "DDirectory name ") (mapc (lambda (f) (rename-file (concat f ".txt") (concat f ".xml"))) (mapcar 'file-name-sans-extension (directory-files dir nil "\.txt$")))
In python:
import os
import re
pat = re.compile('\.txt$')
map(lambda f: os.rename(f + '.txt', f + '.xml'),
[os.path.splitext(f)[0] for f in os.listdir(os.getcwd()) \
if pat.search(f)])
In perl:
use Cwd;
use File::Basename;
opendir(DIR, cwd());
map(rename($_ . '.txt', $_ . '.xml'),
map(basename($_, '.txt'),
grep(/\.txt$/, readdir(DIR))));
closedir(DIR);
Simon Brunning pointed me to this resource for web programming in python and recommended Quixote or PHP from the Webware project. Of course, in the typical response of programmers everywhere I decided to hack something together myself instead.
In my defense I can only say that my requirements are very, very simple, and I wanted an opportunity to expand my python "skills". The system I came up with is a basic version of Java Server Pages (very basic). A template is just an HTML page with <#= and #> around any python expressions. The values of the expressions are substituted into the page at runtime. All variables used by the template are provided as members of a dictionary called v. For example:
<h1>Written by <#= v["author"] #></h1>
The actual template is created from the html page by converting the page into the source code for a python function, compiling the function, and then executing it passing in the dictionary that provides the variable values. The generated function is cached and reused if the source file hasn't changed since the function was generated. Templates are of course represented as objects so creating and using one looks like:
t = Template("test.html")
t.execute({"author":"Alex", "date":"October 2002"})
Extending this would probably involve adding a TemplateFactory that cached templates. Adding support for compound python statements while, for etc. is compilcated by python's use of indentation to mark scope. If I ever need this I'll probably have to go with some sort of END_BLOCK or DEDENT marker to end up with:
<ul> <# for item in v["lines"]: #> <li><#= item #></li> <# END_BLOCK #> </ul>
Anyway, here's the source code, and here it is syntax highlighted for viewing.
I've just had to deal with an error caused by improper java serialization and deserialization. I thought I'd document it so that I don't forget.
An instance of a non-static inner class in Java has a hidden reference to the instance of the outer class that contains it. This reference is used when accessing members of the outer class. By getting the serialization wrong I was able to create an instance of an inner class that didn't know what it's outer class instance was. This leads to a NullPointerException when accessing members of the outer class. If the method called is private the message at least gives a hint of the problem, if it's not then the whole thing is pretty confusing.
To start with here's a very basic Java program that creates an instance of a class Outer1 that includes a reference to an instance of an inner class Inner1. This is then serialized and deserialized. If you compile and execute the program you get.
bash-2.05b$ java -cp . Outer1 Before printString(outerField) printString(innerField) After printString(outerField) printString(innerField)
Say I want to implement my own serialization and deserialization for the inner class. All I need to do is add writeObject and readObject methods like this.
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.writeObject(innerField);
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
innerField = (String)ois.readObject();
}
Notice that I don't call the defaultWriteObject and defaultReadObject methods on the ObjectOutputStream and ObjectInputStream. After all, my class is a direct subclass of Object and I'm handling all of its fields myself. Here's the modified program but when I run it I get a NullPointerException. Here are the messages
bash-2.05b$ java -cp . Outer2 Before printString(outerField) printString(innerField) After printString(outerField) java.lang.NullPointerException at Outer2.access$100(Outer2.java:9) at Outer2$Inner2.output(Outer2.java:30) at Outer2.output(Outer2.java:17) at Outer2.main(Outer2.java:66)
If printString(String s) is made public the messages are even more cryptic.
bash-2.05b$ java -cp . Outer2 Before printString(outerField) printString(innerField) After printString(outerField) java.lang.NullPointerException at Outer2$Inner2.output(Outer2.java:30) at Outer2.output(Outer2.java:17) at Outer2.main(Outer2.java:66)
Note that Outer2.java:30 is printString(innerField); and Outer2.java:9 is public class Outer2. The problem is that while on line Outer2.java:17 the instance of Outer2 is able to call the output() method on the Inner2 instance (inner.output();) that inner code instance is unable to call the printString(String s) on the Outer2 instance.
And why is this? It's because I didn't call defaultWriteObject() and defaultReadObject(). As well as the user defined fields in a class these methods also take care of, for an inner class, the reference to the enclosing outer class instance. Modifying the writeObject and readObject methods as below cures the problem.
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
oos.writeObject(innerField);
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
innerField = (String)ois.readObject();
}
This version of the program runs correctly and produces:
bash-2.05b$ java -cp . Outer3 Before printString(outerField) printString(innerField) After printString(outerField) printString(innerField)
From the Independent newspaper in the UK, found via plasticbag.org comes this wacky court transcript. On a similar note my brother, who's a chartered surveyor, has actually heard a barrister utter the famous phrase I put it to you that your testimony is a tissue of lies.
Log4j is an excellent logging package for Java. I've been using it since version 0.7.4 in early 2000 and we use it at work for all our logging. Presenting the information below may seem to many log4j users like teaching your grandmother to suck eggs but I think it's useful so I though I'd write it down.
Log4j's free documentation is good for describing the various configuration and logging choices you can make but doesn't present any task based or how-to information. This note describes a simple log4j setup that uses four files to record debug, informational, warning and error messages. The debug file will contain all messages, the informational file only informational, warning and error messages, the warning file only warnings, and errors and the error file only errors.
In each Java source file I define a private static final Logger instance and use that for logging. Using the class the logger is in as the value for the getLogger method means each class gets it's own Logger instance, and the logger hierarchy parallels the class hierarchy. This gives you good information about where messages are coming from and good control over which ones are logged. So in a class called Test I'd use:
private static final Logger log =
Logger.getLogger(Test.class);
When logging a debug or info message it's good practice to preceed each with a test using the isDebugEnabled or isInfoEnabled method. This will save you cycles later when debug or info messages are disabled, especially if the message being logged includes objects that are expesive to compute. For example:
if (log.isDebugEnabled())
log.debug("A Debug Message");
In the log4j configuration file the first stage is to define four appenders, one for each output file. If this file is named log4j.xml and is on the classpath then it is found automatically by log4j when it is needed. Use the threshold parameter to limit the messages that are written to the file. As threshold is a property of the AppenderSkeleton class the same technique should work for any appender. Here's the definition of the DEBUG appender.
<appender name="DEBUG" class="org.apache.log4j.FileAppender">
<param name="File" value="logs/debug.log" />
<param name="Threshold" value="DEBUG" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{ISO8601} %-5p %c - %m%n"/>
</layout>
</appender>
After all of the appenders the lowest level logger to be controlled is defined. It is associated with all four appenders so that all messages sent to the logger are presented to all appenders. As explained in the log4j manual appenders are additive Each enabled logging request for a given logger will be forwarded to all the appenders in that logger as well as the appenders higher in the hierarchy. So the single logger definition below will see messages logged by any loggers in the com.zanthan hierarchy.
<logger name="com.zanthan"> <level value="debug"/> <appender-ref ref="DEBUG"/> <appender-ref ref="INFO"/> <appender-ref ref="WARN"/> <appender-ref ref="ERROR"/> </logger>
You can turn on or off logging of various levels by class or package by adding extra logger entries. For example the entry below will meant that classes whose names start with com.zanthan.xsltxt will only output warning and error messages.
<logger name="com.zanthan.xsltxt"> <level value="warn"/> </logger>
Interesting interview with David Gelernter, Rethinking the GUI for the Big Picture. At one point at work we were considering structuring heavily human centric workflow using a narrative model. The possible paths through a process are alternative choices in a sort of "make your own story". The final result of someone following the process is a narrative that can be understood as a story. This might also help during the analysis phase as people could find it easier to describe what they wanted in terms of possible stories with variations about the process instead of having to deal with the thing as one complex whole.
By bob mcwhirter of The Werken Company. Very interesting stuff. I've used jaxen and saxpath and been very impressed.
I work on a commercial Business Process Management tool so was interested to read about werkflow and drools. Not quite the same target though.
Spotted on Slashdot. A link to this Microsoft ad purporting to be a freelance writer who switched from Mac to Pc (apparently the page has been removed, here's the Google cache copy). Of course the text rings pretty false but even more damming is the fact discovered by this Slashdot poster that the image of the "switcher" is a stock photograph. Oh boy, not a grass roots switch but an astroturf campaign like others Microsoft have tried.
Another good Slashdot post. In a thread about underwater gliders, of all things, someone wrote a typical ra ra America post including the phrase Pax Americana. It drew the following funny reply... Pax Americana*
Yesterday I got a cold call voice mail at home from the Domain Support Group saying that there had "recently been changes in the internet" and I should contact them. As I suspected, it's a scam. As Paul Graham suggests in his note, Domain Support Group, I'm linking to it in the hope that it improves its Google ranking.
For the past week and a half at work we've been putting together a point release of our software. This involves taking stuff that was going to be in the next release and stuffing it into the current one so that it can be delivered to a customer. Ok, not the best plan for how to go about things but that's the way it is.
The fun part has been fixing lots of little bugs and tweaking the UI. It's very satisfying to be able to knock off five or six or more issues in a day and see an improvement in the software. Having a close deadline and having to work under time pressure can be motivating. Everyone pitches in even more than normal and things really move forward. It's only when it drags on for weeks or months and becomes the company's normal way of operating that it's a problem. After a couple of weeks you need to slow down, step back and refactor, and make sure the code's still in a good state. I've certainly seen some stuff in the UI code that could benefit from some cleanup when we have the time.
In the past few days I've started to mess around with Python (2.2.1) a bit and I'm increasingly impressed. Perhaps it's not as elegant, I think, as scheme but it's a whole lot better than perl. Good library/module support for easily doing the sorts of things, like parsing xml and sending/serving HTTP, that are needed to build some classes of interesting apps these days. Very similar to Java but faster to write. My only quible so far would be with the GUI. I've used tk pretty extensively in the past, both from tcl and scheme, and I don't think it's as good as Swing. On the other hand it's certainly my choice for the P in LAMP.
From Gamasutra comes The Analyst Primer. Though directed specifically at the games industry I think it's a pretty good overview of the whole industry analyst scene and how it works.
For the few people around who haven't seen it, which included me till Friday, here's Things My Girlfriend and I Have Argued About. Most amusing.
From this entry on Interconnected I found 14 Principles of Polite Apps.
Some of the ideas are interesting, though difficult to implement, but I think that the article is marred by remarks like Call a nerd Mike when he calls himself Michael and he likely won't answer, because you couldn't possibly be referring to him. and a general dismissal of the ability of programmers to design and implement usable software. The people I work with try very hard to make usable/polite software, but that's only one of the things our software has to be. Sometimes it comes down to having to trade off functionality against usability/politeness, especially when a function is first introduced.
Finally, one principle in particular. Polite Software Has Common Sense. I'm sure the author can't really mean common sense. That's one of the most difficult things for a program to exhibit. You can write programs that are experts in deciding where to drill oil wells but "common sense" is much harder than that.
From Joe Conason's journal on Salon. As this story on yahoo news says It is not radioactive, it is not chemical and it is not explosive. Surprisingly, or not, we've not heard much about this in the media. According to Debkafile, the uranium seizure resulted from a joint operation by the [Russian] Foreign Intelligence Service and the CIA which began at the start of August. I would not be at all surprised, the dog appears to be wagging more each day.
My comments on this O'Reilly weblog entry. O'Reilly Network: Has Java passed its prime? [October 01, 2002]
Increasing complexity, whether real or just perceived, does present a problem for Java as it may prevent some people from starting to use it. In some areas it's likely that Java will be replaced by simpler languages that offer other benefits to their users in those niches. In time one of these may emerge from its niche to challenge Java in the mainstream, but I don't think that's going to happen very soon.
In Java the perceived complexity comes from more packages and APIs being added to the standard set. The language itself has changed very little though, the complexity is in the number of packages.
The complexity arises for a reason though. Java is used to try to solve an increasingly diverse range of increasingly complex problems. Any language used in this way requires more complex functionality, look at C#, perl, python etc. If a language can solve a wide range of problems then it's likely to be seen be some people as "complex".
Complexity has its benefits too. There is now so much Java code available, in the standard packages and from open source projects, that many programming problems can be solved by assembling a solution from already working code. This benefit, often claimed for perl with its huge CPAN repository, really is quite substantial. However, it only occurs when a language reaches some critical mass, which is very close to the point at which people start to declare it's become too complex!
Finally, Java really should have no existence outside of the benefits it provides to its users, if it is replaced by a better solution then that's good. It's like the lamp in the new IKEA adverts, why feel sorry for Java, it has no feelings.