XML-a-thon and Machinima


XML-a-thon and Machinima

Ever since I started building modules for Neverwinter Nights I have thought about automatically generating a live Shakespeare play from the XML plays on the web.

The idea is this. Given a Shakespearean play in XML format, generate the NWN scripts to have all the characters in the play give their lines and respond to stage direction. You just provide the actors (in the form of in-game characters) and the code for the stage direction. It will call out to the stage direction at the right time. Pauses will be determined based on how many words the character is reading so that it flows as you would expect.

What do we need to do this then? Well, you need to parse the play and generate the scripts. The best way I have found to manipulate XML in my language of choice (Java) is XMLBeans from the Apache group. It’s not yet JAXB compliant yet but its not really clear to me that makes any difference for something that is code generated and it is really easy to use compared to the esoteric binding files and limited schema support that JAXB currently gives you. The only problem is that there isn’t really a DTD or XML Schema for the Shakespeare files that you find online and XMLBeans requires an XML Schema to understand the format of the files. Enter oXygen.

oXygen is a program for manipulating XML documents and schemas. Integrated with it is a program called Trang by James Clark that will take an XML instance document(s) and produce a loose DTD or schema for that document(s). For instance, I give it hamlet.xml and it gives play.xsd. That schema file is enough for XMLBeans to generate a set of Java classes for it:

/Users/sam/Projects/PlayCompiler:> jaxbc play.xsd
Time to build schema type system: 1.72 seconds
Time to generate code: 0.295 seconds
Time to compile code: 2.138 seconds
Compiled types to: xmltypes.jar

Then you get to manipulate the XML data using XQuery or XPath or DOM or a typed interface or whatever you want. For example:

 PLAYDocument playDoc = PLAYDocument.Factory.parse(new File(args[0]));
 PLAYDocument.PLAY play = playDoc.getPLAY();
 String title = play.getMAINTITLE();
 System.out.println(“Processing: “ + title);
 
 PERSONAEDocument.PERSONAE people = play.getPERSONAE();
 System.out.println(“Starring:”);
 for (int i = 0; i < people.sizeOfPERSONAArray(); i++) {
 String persona = people.getPERSONAArray(i);
 System.out.println(“Person: “ + persona);
 }
 
 XmlObject[] result = play.selectPath(“$this/ACT/SCENE/SPEECH”);
 for (int i = 0; i < result.length; i++) {
 SPEECHDocument.SPEECH speech = (SPEECHDocument.SPEECH) result[i];
 if (speech.getSPEAKERArray()[0].equals(“HAMLET”)) {
 System.out.println(speech);
 }
 }

In this example, if you give it hamlet.xml it will give you back the title, all the regular players in the play, and all the lines that Hamlet should read. Similarly, you can also use the generated classes to create new plays that will be in the right format. As you can see, this gives a pretty good basis for processing the plays. Now all I have to do is to figure out what I want to code generate for NWN so I can get all the characters to act our their roles.

For the code generator I think I am going to use Velocity. Looks to be easy to use and has the features that I need. I do wish that it had a callback type API though like the code generator we use at WebLogic. Have to look into that to see if I am just missing that mode. I don’t like having to populate the whole thing and then generate. Very inefficient.

P.S. Figured out how to do this but now I’m not so sure that the authors of this software really know what they are doing. Here is some example code from their docs:

context.put( “name”, new String(“Velocity”) );

Very amateurish.