/*
* $Id: Idrefs.java,v 1.3 2005/06/16 00:57:05 ian Exp $
* $Name: gxparse-sf-alpha-2_0 $
*
* This file is part of GXPARSE, a general XML parser API.
* Copyright (C) 2003-2005 Ian E. Gorman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* or go to http://www.gnu.org/
*
* Ian E. Gorman http://gorman.ca/
*/
package xml;
import ca.gorman.io.*;
import ca.gorman.xml.parse.*;
import ca.gorman.xml.parse.util.SimpleElementMapper;
import ca.gorman.xml.parse.sax.ValidatingParserFactory;
import java.io.*;
import java.net.URI;
import javax.xml.namespace.QName;
/**
* Example showing how to handle forward and backward references (IDREFS) in
* a stream processing paradigm. ID attributes and IDREF attributes can be
* completely processed at the time they are encountered, whether the IDREF
* attribute is encountered before or after its corresponding ID attribute.
*/
public class Idrefs
extends AbstractListener>
implements Runnable
{
/*
* Title elements require special handling because of formatting, and
* because they are associated with the ID attribute of their parent
* element. Paragraph elements require special handling because of
* formatting. Ref elements require special handling because they have
* IDREF elements. All other elements can simply pass through.
*
* The special handling of ID and IDREF attributes requries either a
* ResequencingWriter or a ResequencingWriterStack to allow the writing
* of forward and backward references in the output stream.
* A ResequencingWriterStack is used to simplify the handling of titles by
* diverting the title to a String variable so that the formatting and the
* handling of the (parent) ID attribute can be done separately, and
* without requiring the character handler to know which element is being
* processed.
*
* A SimpleElementMapper is used to automatically find and use the
* appropriate element method, instead of requiring a test to identify and
* dispatch the element from within the program.
*/
private String lineSeparator = System.getProperty("line.separator");
private String blankLine = lineSeparator + lineSeparator;
// element handler to send elements to appropriate methods in this class
private ElementMapper> elementMapper;
private static QName ATT_IDREF = new QName("idref");
private static QName ATT_ID = new QName("id");
private ResequencingWriterStack writerStack;
private Input input;
private Writer out;
private PrintWriter err;
public Idrefs(Input input, Writer out, Writer err)
throws IOException, NoSuchMethodException, IllegalAccessException
{
this.input = input;
this.err = new PrintWriter(err, true);
this.out = out;
writerStack = new ResequencingWriterStack(
new ResequencingWriter(this.out));
elementMapper = new SimpleElementMapper>(
"element_", this, "defaultElement");
}
public static void main(String [] argv)
{
Input input = new Input(new InputStreamReader(System.in));
Writer out = new OutputStreamWriter(System.out);
Writer err = new OutputStreamWriter(System.err);
try {
if (argv.length > 0) {
input = new Input(new URI(argv[0]));
}
new Idrefs(input, out, err).run();
} catch(Exception e) {
e.printStackTrace();
}
}
public void run()
{
try {
// get parser
ParserFactory parserFactory
= ValidatingParserFactory.newInstance();
Parser parser = parserFactory.newParser();
parser.setListener(this);
parser.parse(input);
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch(Exception e) { err.println(e.getMessage()); }
try {
err.flush();
} catch(Exception e) { System.err.println(e.getMessage()); }
}
}
public void doDocument(Parser parser, CurrentDocument document)
throws ListenerException, IOException {
document.parseContent();
writerStack.write(lineSeparator);
writerStack.close();
}
public void characters(Parser parser, CharSequence charSequence)
throws ListenerException, IOException {
// copy input to the output
writerStack.append(charSequence);
}
/**
* Element dispatcher to select and invoke the appropriate element method.
* @see ElementMapper
*/
public void doElement(Parser parser, CurrentElement element)
throws ListenerException, IOException {
// find and invoke one of the element methods in this class
assert (elementMapper != null);
elementMapper.doElement(parser, element);
}
public void defaultElement (Parser parser, CurrentElement element)
throws ListenerException, IOException {
// let the parser handle the content of unspecified elements
element.parseContent();
}
public void element_title (Parser parser, CurrentElement element)
throws ListenerException, IOException {
// capture title in a String so that it can be used twice
writerStack.push(new CharArrayWriter());
element.parseContent();
String title = ((CharArrayWriter) writerStack.pop()).toString();
// output title to document
writerStack.write(blankLine + "=== " + title + " ===");
// if parent has an ID attribute, associate the title with the ID value
Attribute id = element.getParent().getAttribute(ATT_ID);
if (id != null) {
// associate title with a mark, creating mark if it does not exist
writerStack.mark(id.getValue()).write(title);
}
}
public void element_para (Parser parser, CurrentElement element)
throws ListenerException, IOException {
// start every para on a new line
writerStack.write(blankLine);
element.parseContent();
}
public void element_ref (Parser parser, CurrentElement element)
throws ListenerException, IOException {
Attribute idref = element.getAttribute(ATT_IDREF);
if (idref != null) {
// insert a marker for the IDREF, content will be supplied at
// another time (before or after this event!)
writerStack.write(lineSeparator + "See \"");
writerStack.writeMark(idref.getValue());
writerStack.write("\"");
} else {
writerStack.write(lineSeparator + "null idref");
}
element.parseContent();
}
}