/* * $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(); } }