/*
* Copyright 2005-2015 by BerryWorks Software, LLC. All rights reserved.
*
* This file is part of EDIReader. You may obtain a license for its use directly from
* BerryWorks Software, and you may also choose to use this software under the terms of the
* GPL version 3. Other products in the EDIReader software suite are available only by licensing
* with BerryWorks. Only those files bearing the GPL statement below are available under the GPL.
*
* EDIReader is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* EDIReader 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with EDIReader. If not,
* see <http://www.gnu.org/licenses/>.
*/
package com.berryworks.edireader.util.sax;
import com.berryworks.edireader.EDIAttributes;
import com.berryworks.edireader.tokenizer.SourcePosition;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.LinkedList;
/**
* This implementation of a SAX ContentHandler passes SAX events it receives
* to a delegate ContentHandler with the added value of buffering these events
* in a queue so that items in the queue may be modified as needed
* before they are sent to the delegate.
*/
public class QueuedContentHandler extends DefaultHandler {
private final ContentHandler wrappedHandler;
private final LinkedList<QueuedItem> queue = new LinkedList<>();
private final int queueSizeLimit;
private final SourcePosition sourcePosition;
public QueuedContentHandler(ContentHandler handler, int queueSizeLimit, SourcePosition sourcePosition) {
wrappedHandler = handler;
this.queueSizeLimit = queueSizeLimit;
this.sourcePosition = sourcePosition;
}
@Override
public void startDocument() throws SAXException {
wrappedHandler.startDocument();
}
@Override
public void endDocument() throws SAXException {
drainQueue();
wrappedHandler.endDocument();
}
public void drainQueue() throws SAXException {
while (queue.size() > 0) {
queue.removeFirst().process(wrappedHandler);
}
if (wrappedHandler instanceof SourcePosition) {
((SourcePosition) wrappedHandler).setCharCounts(-1, -1);
}
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
limitSize();
final int charCount = sourcePosition == null ? 0 : sourcePosition.getCharCount();
final int segmentCharCount = sourcePosition == null ? 0 : sourcePosition.getSegmentCharCount();
queue.add(new QueuedStartItem(uri, localName, qName, attributes, charCount, segmentCharCount));
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
limitSize();
final int charCount = sourcePosition == null ? 0 : sourcePosition.getCharCount();
final int segmentCharCount = sourcePosition == null ? 0 : sourcePosition.getSegmentCharCount();
queue.add(new QueuedEndItem(uri, localName, qName, charCount, segmentCharCount));
}
private void limitSize() throws SAXException {
while (queue.size() >= queueSizeLimit) {
final QueuedItem queuedItem = queue.removeFirst();
queuedItem.process(wrappedHandler);
}
}
@Override
public void characters(char[] chars, int start, int length) throws SAXException {
characters(new String(chars, start, length));
}
public void characters(String data) {
queue.getLast().addData(data);
}
public String getAttribute(String tag, String attributeName) {
for (int i = queue.size() - 1; i >= 0; i--) {
QueuedItem queuedItem = queue.get(i);
if (tag.equals(queuedItem.getLocalName())) {
EDIAttributes attributes = queuedItem.getAttributes();
return attributes.getValue(attributeName);
}
}
return null;
}
public void putAttribute(String tag, String attributeName, String data) {
for (int i = queue.size() - 1; i >= 0; i--) {
QueuedItem queuedItem = queue.get(i);
if (tag.equals(queuedItem.getLocalName())) {
EDIAttributes attributes = queuedItem.getAttributes();
int index = attributes.getIndex(attributeName);
if (index >= 0)
attributes.removeAttribute(index);
attributes.addCDATA(attributeName, data);
return;
}
}
throw new RuntimeException("Could not find queued element " + tag + " for putAttribute()");
}
EDIAttributes getFirstAttributes() {
if (queue.size() == 0)
return null;
return queue.getFirst().getAttributes();
}
public ContentHandler getWrappedContentHandler() {
return wrappedHandler;
}
abstract class QueuedItem {
private final String uri;
private final String localName;
private final String qName;
private final int charCount;
private final int segmentCharCount;
QueuedItem(String uri, String localName, String qName, int charCount, int segmentCharCount) {
this.uri = uri;
this.localName = localName;
this.qName = qName;
this.charCount = charCount;
this.segmentCharCount = segmentCharCount;
}
public String getUri() {
return uri;
}
public String getLocalName() {
return localName;
}
public String getQName() {
return qName;
}
public void end(ContentHandler handler) throws SAXException {
handler.endElement(uri, localName, qName);
}
public abstract void process(ContentHandler handler) throws SAXException;
public abstract void addData(String data);
public abstract EDIAttributes getAttributes();
public int getCharCount() {
return charCount;
}
public int getSegmentCharCount() {
return segmentCharCount;
}
}
class QueuedStartItem extends QueuedItem {
private final EDIAttributes attributes;
private String data;
QueuedStartItem(String uri, String localName, String qName, Attributes attributes, int charCount, int segmentCharCount) {
super(uri, localName, qName, charCount, segmentCharCount);
this.attributes = new EDIAttributes(attributes);
}
@Override
public EDIAttributes getAttributes() {
return attributes;
}
public String getData() {
return data;
}
@Override
public void addData(String data) {
if (this.data == null)
this.data = data;
else
this.data += data;
}
@Override
public void process(ContentHandler handler) throws SAXException {
EDIAttributes attributes1 = getAttributes();
if (attributes1 == null)
throw new RuntimeException("null attributes");
String name = getLocalName();
if (name == null)
throw new RuntimeException("null name");
String qname = getQName();
if (qname == null)
throw new RuntimeException("null qname");
String uri = getUri();
if (uri == null)
throw new RuntimeException("null uri");
if (handler instanceof SourcePosition) {
((SourcePosition) handler).setCharCounts(getCharCount(), getSegmentCharCount());
}
handler.startElement(uri, name, qname, attributes1);
if (getData() != null) {
char[] ca = getData().toCharArray();
handler.characters(ca, 0, ca.length);
}
}
}
class QueuedEndItem extends QueuedItem {
QueuedEndItem(String uri, String localName, String qName, int charCount, int segmentCharCount) {
super(uri, localName, qName, charCount, segmentCharCount);
}
@Override
public void process(ContentHandler handler) throws SAXException {
if (handler instanceof SourcePosition) {
((SourcePosition) handler).setCharCounts(getCharCount(), getSegmentCharCount());
}
handler.endElement(getUri(), getLocalName(), getQName());
}
@Override
public void addData(String data) {
throw new RuntimeException("addData() should not be called on an end item");
}
@Override
public EDIAttributes getAttributes() {
throw new RuntimeException("getAttributes() should not be called on an end item");
}
}
}