org.dom4j.io.SAXEventRecorder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dom4j Show documentation
Show all versions of dom4j Show documentation
flexible XML framework for Java
/*
* Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
*
* This software is open source.
* See the bottom of this file for the licence.
*/
package org.dom4j.io;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
/**
*
* Records SAX events such that they may be "replayed" at a later time. Provides
* an alternative serialization approach when externalizing a DOM4J document.
* Rather than serializing a document as text and re-parsing, the sax events may
* be serialized instead.
*
* Example usage:
*
*
*
*
*
* SAXEventRecorder recorder = new SAXEventRecorder();
* SAXWriter saxWriter = new SAXWriter(recorder, recorder);
* saxWriter.write(document);
* out.writeObject(recorder);
* ...
* SAXEventRecorder recorder = (SAXEventRecorder)in.readObject();
* SAXContentHandler saxContentHandler = new SAXContentHandler();
* recorder.replay(saxContentHandler);
* Document document = saxContentHandler.getDocument();
*
*
*
*
*
* @author Todd Wolff (Bluestem Software)
*/
public class SAXEventRecorder extends DefaultHandler implements LexicalHandler,
DeclHandler, DTDHandler, Externalizable {
public static final long serialVersionUID = 1;
private static final byte STRING = 0;
private static final byte OBJECT = 1;
private static final byte NULL = 2;
private List events = new ArrayList();
private Map> prefixMappings = new HashMap>();
private static final String XMLNS = "xmlns";
private static final String EMPTY_STRING = "";
public SAXEventRecorder() {
}
public void replay(ContentHandler handler) throws SAXException {
for (SAXEvent saxEvent : events) {
switch (saxEvent.event) {
// replay to ContentHandler
case SAXEvent.PROCESSING_INSTRUCTION:
handler.processingInstruction((String) saxEvent.getParm(0),
(String) saxEvent.getParm(1));
break;
case SAXEvent.START_PREFIX_MAPPING:
handler.startPrefixMapping((String) saxEvent.getParm(0),
(String) saxEvent.getParm(1));
break;
case SAXEvent.END_PREFIX_MAPPING:
handler.endPrefixMapping((String) saxEvent.getParm(0));
break;
case SAXEvent.START_DOCUMENT:
handler.startDocument();
break;
case SAXEvent.END_DOCUMENT:
handler.endDocument();
break;
case SAXEvent.START_ELEMENT:
AttributesImpl attributes = new AttributesImpl();
List attParmList = (List) saxEvent.getParm(3);
if (attParmList != null) {
for (String[] attParms : attParmList) {
attributes.addAttribute(attParms[0], attParms[1],
attParms[2], attParms[3], attParms[4]);
}
}
handler.startElement((String) saxEvent.getParm(0),
(String) saxEvent.getParm(1), (String) saxEvent
.getParm(2), attributes);
break;
case SAXEvent.END_ELEMENT:
handler.endElement((String) saxEvent.getParm(0),
(String) saxEvent.getParm(1), (String) saxEvent
.getParm(2));
break;
case SAXEvent.CHARACTERS:
char[] chars = (char[]) saxEvent.getParm(0);
int start = (Integer) saxEvent.getParm(1);
int end = (Integer) saxEvent.getParm(2);
handler.characters(chars, start, end);
break;
// replay to LexicalHandler
case SAXEvent.START_DTD:
((LexicalHandler) handler).startDTD((String) saxEvent
.getParm(0), (String) saxEvent.getParm(1),
(String) saxEvent.getParm(2));
break;
case SAXEvent.END_DTD:
((LexicalHandler) handler).endDTD();
break;
case SAXEvent.START_ENTITY:
((LexicalHandler) handler).startEntity((String) saxEvent
.getParm(0));
break;
case SAXEvent.END_ENTITY:
((LexicalHandler) handler).endEntity((String) saxEvent
.getParm(0));
break;
case SAXEvent.START_CDATA:
((LexicalHandler) handler).startCDATA();
break;
case SAXEvent.END_CDATA:
((LexicalHandler) handler).endCDATA();
break;
case SAXEvent.COMMENT:
char[] cchars = (char[]) saxEvent.getParm(0);
int cstart = (Integer) saxEvent.getParm(1);
int cend = (Integer) saxEvent.getParm(2);
((LexicalHandler) handler).comment(cchars, cstart, cend);
break;
// replay to DeclHandler
case SAXEvent.ELEMENT_DECL:
((DeclHandler) handler).elementDecl((String) saxEvent
.getParm(0), (String) saxEvent.getParm(1));
break;
case SAXEvent.ATTRIBUTE_DECL:
((DeclHandler) handler).attributeDecl((String) saxEvent
.getParm(0), (String) saxEvent.getParm(1),
(String) saxEvent.getParm(2), (String) saxEvent
.getParm(3), (String) saxEvent.getParm(4));
break;
case SAXEvent.INTERNAL_ENTITY_DECL:
((DeclHandler) handler).internalEntityDecl(
(String) saxEvent.getParm(0), (String) saxEvent
.getParm(1));
break;
case SAXEvent.EXTERNAL_ENTITY_DECL:
((DeclHandler) handler).externalEntityDecl(
(String) saxEvent.getParm(0), (String) saxEvent
.getParm(1), (String) saxEvent.getParm(2));
break;
default:
throw new SAXException("Unrecognized event: "
+ saxEvent.event);
}
}
}
// ContentHandler interface
// -------------------------------------------------------------------------
public void processingInstruction(String target, String data)
throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.PROCESSING_INSTRUCTION);
saxEvent.addParm(target);
saxEvent.addParm(data);
events.add(saxEvent);
}
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.START_PREFIX_MAPPING);
saxEvent.addParm(prefix);
saxEvent.addParm(uri);
events.add(saxEvent);
}
public void endPrefixMapping(String prefix) throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.END_PREFIX_MAPPING);
saxEvent.addParm(prefix);
events.add(saxEvent);
}
public void startDocument() throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.START_DOCUMENT);
events.add(saxEvent);
}
public void endDocument() throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.END_DOCUMENT);
events.add(saxEvent);
}
public void startElement(String namespaceURI, String localName,
String qualifiedName, Attributes attributes) throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.START_ELEMENT);
saxEvent.addParm(namespaceURI);
saxEvent.addParm(localName);
saxEvent.addParm(qualifiedName);
QName qName;
if (namespaceURI != null) {
qName = new QName(localName, Namespace.get(namespaceURI));
} else {
qName = new QName(localName);
}
if ((attributes != null) && (attributes.getLength() > 0)) {
List attParmList = new ArrayList(attributes.getLength());
String[] attParms;
for (int i = 0; i < attributes.getLength(); i++) {
String attLocalName = attributes.getLocalName(i);
if (attLocalName.startsWith(XMLNS)) {
// if SAXWriter is writing a DOMDocument, namespace
// decls are treated as attributes. record a start
// prefix mapping event
String prefix;
if (attLocalName.length() > 5) {
prefix = attLocalName.substring(6);
} else {
prefix = EMPTY_STRING;
}
SAXEvent prefixEvent = new SAXEvent(
SAXEvent.START_PREFIX_MAPPING);
prefixEvent.addParm(prefix);
prefixEvent.addParm(attributes.getValue(i));
events.add(prefixEvent);
// 'register' the prefix so that we can generate
// an end prefix mapping event within endElement
List prefixes = prefixMappings.get(qName);
if (prefixes == null) {
prefixes = new ArrayList();
prefixMappings.put(qName, prefixes);
}
prefixes.add(prefix);
} else {
attParms = new String[5];
attParms[0] = attributes.getURI(i);
attParms[1] = attLocalName;
attParms[2] = attributes.getQName(i);
attParms[3] = attributes.getType(i);
attParms[4] = attributes.getValue(i);
attParmList.add(attParms);
}
}
saxEvent.addParm(attParmList);
}
events.add(saxEvent);
}
public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.END_ELEMENT);
saxEvent.addParm(namespaceURI);
saxEvent.addParm(localName);
saxEvent.addParm(qName);
events.add(saxEvent);
// check to see if a we issued a start prefix mapping event
// for DOMDocument namespace decls
QName elementName;
if (namespaceURI != null) {
elementName = new QName(localName, Namespace.get(namespaceURI));
} else {
elementName = new QName(localName);
}
List prefixes = prefixMappings.get(elementName);
if (prefixes != null) {
for (String prefixe : prefixes) {
SAXEvent prefixEvent =
new SAXEvent(SAXEvent.END_PREFIX_MAPPING);
prefixEvent.addParm(prefixe);
events.add(prefixEvent);
}
}
}
public void characters(char[] ch, int start, int end) throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.CHARACTERS);
saxEvent.addParm(ch);
saxEvent.addParm(start);
saxEvent.addParm(end);
events.add(saxEvent);
}
// LexicalHandler interface
// -------------------------------------------------------------------------
public void startDTD(String name, String publicId, String systemId)
throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.START_DTD);
saxEvent.addParm(name);
saxEvent.addParm(publicId);
saxEvent.addParm(systemId);
events.add(saxEvent);
}
public void endDTD() throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.END_DTD);
events.add(saxEvent);
}
public void startEntity(String name) throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.START_ENTITY);
saxEvent.addParm(name);
events.add(saxEvent);
}
public void endEntity(String name) throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.END_ENTITY);
saxEvent.addParm(name);
events.add(saxEvent);
}
public void startCDATA() throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.START_CDATA);
events.add(saxEvent);
}
public void endCDATA() throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.END_CDATA);
events.add(saxEvent);
}
public void comment(char[] ch, int start, int end) throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.COMMENT);
saxEvent.addParm(ch);
saxEvent.addParm(start);
saxEvent.addParm(end);
events.add(saxEvent);
}
// DeclHandler interface
// -------------------------------------------------------------------------
public void elementDecl(String name, String model) throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.ELEMENT_DECL);
saxEvent.addParm(name);
saxEvent.addParm(model);
events.add(saxEvent);
}
public void attributeDecl(String eName, String aName, String type,
String valueDefault, String value) throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.ATTRIBUTE_DECL);
saxEvent.addParm(eName);
saxEvent.addParm(aName);
saxEvent.addParm(type);
saxEvent.addParm(valueDefault);
saxEvent.addParm(value);
events.add(saxEvent);
}
public void internalEntityDecl(String name, String value)
throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.INTERNAL_ENTITY_DECL);
saxEvent.addParm(name);
saxEvent.addParm(value);
events.add(saxEvent);
}
public void externalEntityDecl(String name, String publicId, String sysId)
throws SAXException {
SAXEvent saxEvent = new SAXEvent(SAXEvent.EXTERNAL_ENTITY_DECL);
saxEvent.addParm(name);
saxEvent.addParm(publicId);
saxEvent.addParm(sysId);
events.add(saxEvent);
}
public void writeExternal(ObjectOutput out) throws IOException {
if (events == null) {
out.writeByte(NULL);
} else {
out.writeByte(OBJECT);
out.writeObject(events);
}
}
public void readExternal(ObjectInput in) throws ClassNotFoundException,
IOException {
if (in.readByte() != NULL) {
events = (List) in.readObject();
}
}
// SAXEvent inner class
// -------------------------------------------------------------------------
static class SAXEvent implements Externalizable {
public static final long serialVersionUID = 1;
static final byte PROCESSING_INSTRUCTION = 1;
static final byte START_PREFIX_MAPPING = 2;
static final byte END_PREFIX_MAPPING = 3;
static final byte START_DOCUMENT = 4;
static final byte END_DOCUMENT = 5;
static final byte START_ELEMENT = 6;
static final byte END_ELEMENT = 7;
static final byte CHARACTERS = 8;
static final byte START_DTD = 9;
static final byte END_DTD = 10;
static final byte START_ENTITY = 11;
static final byte END_ENTITY = 12;
static final byte START_CDATA = 13;
static final byte END_CDATA = 14;
static final byte COMMENT = 15;
static final byte ELEMENT_DECL = 16;
static final byte ATTRIBUTE_DECL = 17;
static final byte INTERNAL_ENTITY_DECL = 18;
static final byte EXTERNAL_ENTITY_DECL = 19;
protected byte event;
protected List