Property files to xml

Skip to end of metadata
Go to start of metadata

This example shows how to convert a series of properties like:

machine=antlr
port="8080"
description = "The research server for antlr"

into the following XML for those that like to type.

<properties>
<property id="machine">antlr</property>
<property id="port">8080</property>
<property id="description">The research server for antlr</property>
</properties>

Property file grammar

The first step is to get an input grammar that recognizes a series of property assignments:

grammar Prop;

file : prop+ ;

prop : ID '=' value ;

value : ID | STRING ;

ID  :   (LETTER|'_') (LETTER|'0'..'9'|'_')* ;
fragment
LETTER : 'a'..'z'|'A'..'Z' ;
STRING : '"' ~'"'* '"' ;
WS  :   (' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;} ;

Morph script

Now let's write a morph script that rewrites the input to XML using a few templates.

morph Prop;

options {conforms=false;} // output is xml; not same language

@members {
public String strip(String s) {
    if ( s.charAt(0)=='"' ) return s.substring(1, s.length()-1);
    return s;
}
}

file:   (p+=prop)+ -> file(props={$p}) ;

prop: ID '=' value -> prop(ID={$ID.text}, v={strip($value.text)}) ;

Templates file and prop live in xml.stg:

group XML;

file(props) ::= <<
\<properties>
<props; separator="\n">
\</properties>
>>

prop(ID,v) ::= "\<property id=\"<ID>\"><v>\</property>"

The props attribute of file is set by the file(props={$p}) rewrite action. The ID,v attributes of the prop rule are set by the rewrite action in rule prop.

Before running ANTLRMorph on the script, we have to make sure that we run ANTLR and compile the property parser:

$ java org.antlr.Tool Prop.g
$ javac PropParser.java PropLexer.java
$

The ANTLRMorph tool needs to parse inside the patterns as it reads your morph script; it does so with reflection, which means you must compile the parser and lexer before running ANTLRMorph.

Ok, now we can run ANTLRMorph and process the generated grammar file and Java files:

$ java org.antlr.morph.Tool xml.morph
$ java org.antlr.Tool -debug Prop_morph.g    # make sure to use -debug mode
$ javac *.java
$

Here is the test rig:

import java.io.*;
import java.util.*;
import org.antlr.runtime.*;
import org.antlr.stringtemplate.*;
import org.antlr.stringtemplate.language.*;
import org.antlr.morph.runtime.*;

public class Test {
    public static void main(String[] args) throws Exception {
        CharStream input = null;
        if ( args.length>0 ) input = new ANTLRFileStream(args[0]);
        else input = new ANTLRInputStream(System.in);
        PropLexer lexer = new PropLexer(input);
        TokenRewriteStream tokens = new TokenRewriteStream(lexer);
        MorphParseTreeBuilder builder = new MorphParseTreeBuilder("Prop");
        Prop_morph parser = new Prop_morph(tokens, builder);
        parser.file();
        if ( builder.getErrors()!=null ) {
            builder.displaySyntaxErrors(parser);
            System.err.println("Aborting rewrite due to syntax errors");
            System.exit(1);
        }
        // Otherwise, no errors so proceed to rewriting

        MorphParseTree pt = builder.getTree();
        //System.out.println(pt.toStringTree());

        PropMorpher morphExecutor = new PropMorpher(pt,tokens);
        //morphExecutor.trace = true;

        StringTemplateGroup templates =
            new StringTemplateGroup(new FileReader("xml.stg"));
        morphExecutor.setTemplateLib(templates);

        morphExecutor.morph();
        String output = morphExecutor.getOutputString();
        System.out.print(output);
        //System.out.println(morphExecutor.getListener());
    }
}

Compile that and run:

$ javac Test.java
$ java Test < input
machine=antlr
port="8080"
description = "The research server for antlr"
$

Variations

Alternatively, we could put all the templates in line like this:

morph Prop;

options {conforms=false;}

@members {
public String strip(String s) {
    if ( s.charAt(0)=='"' ) return s.substring(1, s.length()-1);
    return s;
}
}

file:   (p+=prop)+ ->
<<
\<properties>
<p; separator="\n">
\</properties>
>>
    ;

prop: ID '=' value
    -> template(v={strip($value.text)}, ID={$ID.text})
    "\<property id=\"<ID>\"><v>\</property>"
    ;

Naturally, this approach is much less flexible. For example, we can change the template library and therefore change with the output looks like without having to recompile or change any of the translation stuff.

For example, let's generate pascal assignments. All we need is to load pascal.stg not xml.stg. Here is pascal.stg:

group XML;

file(props) ::= "<props; separator=\"\n\">"

prop(ID,v) ::= "<ID> := <v>;"

Change the test rig to load that file not xml.stg and rerun:

machine := antlr;
port := 8080;
description := The research server for antlr;
Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.