/*
* $Id: TagList.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.sax.ValidatingParserFactory;
import ca.gorman.xml.parse.util.DtdUtilities;
import java.io.*;
import java.net.URI;
/**
* Example that lists start and end tags, using resequencing to place the
* character count for each element immediately after the start tag for that
* element.
*
* @author (ian) Ian E. Gorman.
*/
public class TagList
extends AbstractErrorListener>
{
/*
* A single method, with multiple invocations active at the same time, is
* used because all elements receive identical handling.
*
* At the start of each element, the start tag is printed, followed by a
* Mark for the character counts. Three new CharArrayWriters are pushed on
* on WriterStacks to collect PCDATA, CDATA, and Ignorable Whitespace. Then
* the element content is parsed, causing the element method to be invoked
* again for each child element, and causing the character content to go to
* one of the CharArrayWriters.
*
* At the end of an element, the three CharArrayWriters for that element
* are popped (to stop the accumulation of characters, and their sizes are
* written to the previously written Mark. This has the same effect as if
* we could reposition to a previous point in the output, insert more data,
* and then reposition to the end of the output to continue writing data.
*/
private final String INDENT = " ";
private final String lineSeparator
= System.getProperty("line.separator").intern();
private AbstractMessageListener> messageListener
= AbstractMessageListener.>newInstance();
private String [] argv = null;
private int anonymousKeyCount = 0;
private String indent = "";
private boolean isCdata = false;
// Writer stacks for temporary storage of character data
private WriterStack cdataWriterStack
= new WriterStack(new NullOutputWriter());
private WriterStack pcdataWriterStack
= new WriterStack(new NullOutputWriter());
private WriterStack ignorableWhitespaceWriterStack
= new WriterStack(new NullOutputWriter());
// Writer with pseudo-random access to the output stream
private ResequencingWriter resequencingWriter;
private Input input;
// private Writer out;
private PrintWriter err;
public TagList(Input input, Writer out, Writer err)
throws IOException, NoSuchMethodException, IllegalAccessException
{
super();
this.input = input;
this.err = new PrintWriter(err, true);
resequencingWriter = new ResequencingWriter(out);
}
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 TagList(input, out, err).run();
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* Prepare to write the document, including some of the initial preparation
* for writing before and after the current point in the output document,
* and start the parser.
*/
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(err);
} finally {
try {
resequencingWriter.close();
} catch(Exception e) { err.println(e.getMessage()); }
try {
err.flush();
} catch(Exception e) { System.err.println(e.getMessage()); }
}
}
////// character handlers /////////////////
public void characters(Parser parser, CharSequence charSequence)
throws ListenerException, IOException {
// copy input to the appropriate stack, CDATA or PCDATA
if (isCdata) {
cdataWriterStack.append(charSequence);
} else {
pcdataWriterStack.append(charSequence);
}
}
public void cdata(Parser parser, CurrentMarkedSection cdata)
throws ListenerException, IOException {
// change processing state while in CDATA marked section
isCdata = true;
cdata.parseContent();
isCdata = false;
}
public void ignorableWhitespace(Parser parser,
CharSequence charSequence)
throws ListenerException, IOException {
// copy input to the output
ignorableWhitespaceWriterStack.append(charSequence);
}
// every element is handled the same way
public void doElement(Parser parser, CurrentElement element)
throws ListenerException, IOException {
// output the start tag and increase the indent level
printStartTag(element);
String saveIndent = indent;
indent += INDENT;
// Get an anonymous Mark and write it as a placeholder for data that
// will be acquired later in the input
Resequencer.Mark mark = resequencingWriter.mark();
resequencingWriter.write(mark);
// set up to collect character content in several writers
CharArrayWriter pcdataArrayWriter = new CharArrayWriter();
pcdataWriterStack.push(pcdataArrayWriter);
CharArrayWriter cdataArrayWriter = new CharArrayWriter();
cdataWriterStack.push(cdataArrayWriter);
CharArrayWriter igwspArrayWriter = new CharArrayWriter();
ignorableWhitespaceWriterStack.push(igwspArrayWriter);
// process the element content, doing any output required for child
// elements. Pop the CharArrayWriters so that they will not collect
// any more output.
element.parseContent();
pcdataWriterStack.pop();
cdataWriterStack.pop();
ignorableWhitespaceWriterStack.pop();
// Now write the character counts to the Mark (placeholder) that was
// written immediately after the start tag
// This has the same effect as if we could rewind the output to a
// previous point, insert additional data at the previous point, and
// then return to the current end of output to append more data.
int pcdataSize = pcdataArrayWriter.size();
if (pcdataSize > 0) {
mark.write(indent + "PCDATA: " + Integer.toString(pcdataSize)
+ lineSeparator);
}
int cdataSize = cdataArrayWriter.size();
if (cdataSize > 0) {
mark.write(indent + "CDATA: " + Integer.toString(cdataSize)
+ lineSeparator);
}
int igwspSize = igwspArrayWriter.size();
if (igwspSize > 0) {
mark.write(indent + "IgnorableWhitespace: "
+ Integer.toString(igwspSize) + lineSeparator);
}
// restore the previous indent level and output the end tag
indent = saveIndent;
printEndTag(element);
}
private void printStartTag(Element element) throws IOException
{
Attribute [] attribute = element.getAttributes();
resequencingWriter.write(indent + "<"
+ DtdUtilities.getPrefixedName(element));
for (int i = 0 ; i < attribute.length ; ++i) {
resequencingWriter.write(" "
+ DtdUtilities.getPrefixedName(attribute[i])
+ "=\"" + attribute[i].getQuotableValue() + "\"");
}
resequencingWriter.write(">" + lineSeparator);
}
private void printEndTag(Element element) throws IOException
{
resequencingWriter.write(indent + ""
+ DtdUtilities.getPrefixedName(element)
+ ">" + lineSeparator);
}
}