Powered by SmartDoc

Document-oriented application

Next, let us develop a document-oriented application program.

"SimpleDoc" is an XML document as follows:

SimpleDoc.xml
<?xml version='1.0' encoding='Shift_JIS' ?>

<!DOCTYPE sdoc SYSTEM "SimpleDoc.dtd">

<sdoc>

<section title="Paragraph">

This is a <i>test document for <b>SimpleDoc</b></i>.

</section>

<section title="List">

You can write lists.

<ul>
  <li>first item</li>
  <li>second item</li>
</ul>

You can <i>also</i> write numberd lists.

<ol>
  <li>first item</li>
  <li>second item</li>
</ol>

</section>

</sdoc>

We construct an application program which converts this XML document to plain text.

Structures of such XML documents can be modelled as list[SimpleDoc.rlx].

SimpleDoc.rlx
<?xml version="1.0"?>
<!DOCTYPE module SYSTEM "relaxCore.dtd">
<module>

  <interface>
    <export label="sdoc"/>
  </interface>

  <tag name="sdoc"/>

  <elementRule label="sdoc" role="sdoc">
    <mixed>
      <choice occurs="*">
        <ref label="section"/>
        <ref label="ul"/>
        <ref label="ol"/>
        <hedgeRef label="particle"/>
      </choice>
    </mixed>
  </elementRule>

  <tag name="section">
    <attribute name="title" required="true" type="string"/>
  </tag>

  <elementRule label="section" role="section">
    <mixed>
      <choice occurs="*">
        <ref label="ul"/>
        <ref label="ol"/>
        <hedgeRef label="particle"/>
      </choice>
    </mixed>
  </elementRule>

  <tag name="li"/>
  <elementRule label="li" role="li">
    <mixed>
      <hedgeRef label="particle" occurs="*"/>
    </mixed>
  </elementRule>

  <tag name="ul"/>
  <elementRule label="ul" role="ul">
    <ref label="li" occurs="*"/>
  </elementRule>

  <tag name="ol"/>
  <elementRule label="ol" role="ol">
    <ref label="li" occurs="*"/>
  </elementRule>

  <tag name="b"/>
  <elementRule label="b" role="b">
    <mixed>
      <hedgeRef label="particle" occurs="*"/>
    </mixed>
  </elementRule>

  <tag name="i"/>
  <elementRule label="i" role="i">
    <mixed>
      <hedgeRef label="particle" occurs="*"/>
    </mixed>
  </elementRule>

  <hedgeRule label="particle">
    <choice>
      <ref label="b"/>
      <ref label="i"/>
    </choice>
  </hedgeRule>

</module>

From this RELAX module, we generate Java classes containing the visitor mechanism. (figure[Class generation])

Class generation
C:\tmp> relaxer -visitor SimpleDoc.rlx

As a result, classes shown as figure[SimpleDoc class diagram] are generated.

Even for such a simple document-oriented application, Relaxer generates quite a few classes. Needless to say, it does not mean that Relaxer generates redundant classes. To the contrary, these classes are crutial for this application program. Therefore, development costs reduced by Relaxer are quite significant.

SimpleDoc class diagram

We create a visitor which traverses these classes. (list[TextMaker.java])

TextMaker.java
import java.util.*;

public class TextMaker extends RVisitorBase {
    private StringBuffer buffer = new StringBuffer();
    private StringBuffer paragraph = null;
    private Stack stack = new Stack();

    public void enter(Section section) {
	String title = section.getTitle();
	buffer.append("\n");
	buffer.append("[");
	buffer.append(title);
	buffer.append("]\n");
	buffer.append("\n");
    }

    public void enter(Ul ul) {
	flushParagraph();
	buffer.append("\n");
	stack.push(ul);
    }

    public void enter(Ol ol) {
	flushParagraph();
	buffer.append("\n");
	stack.push(ol);
    }

    public void enter(Li li) {
	Object parent = stack.peek();
	String mark = null;
	if (parent instanceof Ol) {
	    Li[] lis = ((Ol)parent).getLi();
	    for (int i = 0;i < lis.length;i++) {
		if (li == lis[i]) {
		    mark = Integer.toString(i + 1) + ".";
		    break;
		}
	    }
	    if (mark == null) {
		throw (new InternalError());
	    }
	} else if (parent instanceof Ul) {
	    mark = "-";
	} else {
	    throw (new InternalError());
	}
	flushParagraph();
	buffer.append(" ");
	buffer.append(mark);
	buffer.append(" ");
    }

    public void enter(I i) {
	setParagraph();
	paragraph.append("|");
    }

    public void enter(B b) {
	setParagraph();
	paragraph.append("*");
    }

    public void enter(RString rstring) {
	setParagraph();
	paragraph.append(rstring.getText());
    }

    public void leave(Section section) {
	flushParagraph();
    }

    public void leave(Ul ul) {
	buffer.append("\n");
	stack.pop();
    }

    public void leave(Ol ol) {
	buffer.append("\n");
	stack.pop();
    }

    public void leave(Li li) {
	flushParagraph();
    }

    public void leave(I i) {
	setParagraph();
	paragraph.append("|");
    }

    public void leave(B b) {
	paragraph.append("*");
    }

    public String getText() {
	flushParagraph();
	return (new String(buffer));
    }

    private void setParagraph() {
	if (paragraph == null) {
	    paragraph = new StringBuffer();
	}
    }

    private void flushParagraph() {
	if (paragraph != null) {
	    String text = new String(paragraph).trim();
	    if (text.length() > 0) {
		buffer.append(text);
		buffer.append("\n");
	    }
	    paragraph = null;
	}
    }	
}

We complete this application program by creating a class SimpleDoc, which is the main program for this visitor.

Now, we are ready to invoke this program.

Execution of the SimpleDoc
C:\tmp> java SimpleDoc SimpleDoc.xml

The result is shown in list[Result].

Result
[Paragraph]

This is a |test document for *SimpleDoc*|.

[List]

You can create lists.

 - first item
 - second item

You can |also| create numberd lists.

 1. first item
 2. second item

This application program is also included in the sample/visitor directory of the Relaxer distribution. Interested readers are encouraged to study it.