All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xmlobjects.util.xml.SAXBuffer Maven / Gradle / Ivy

There is a newer version: 1.1.2
Show newest version
/*
 * xml-objects - A simple and lightweight XML-to-object mapping library
 * https://github.com/xmlobjects
 *
 * Copyright 2019-2024 Claus Nagel 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.xmlobjects.util.xml;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlobjects.util.xml.ArrayBuffer.ArrayBufferIterator;

import javax.xml.XMLConstants;
import javax.xml.stream.XMLStreamReader;

public class SAXBuffer extends DefaultHandler {
    public static final int DEFAULT_BUFFER_SIZE = 512;

    static final byte UNDEFINED = 0;
    static final byte START_DOCUMENT = 1;
    static final byte END_DOCUMENT = 2;
    static final byte START_ELEMENT = 3;
    static final byte END_ELEMENT = 4;
    static final byte CHARACTERS = 5;
    static final byte NAMESPACE_PREFIX_MAPPING = 6;
    static final byte ATTRIBUTE = 7;

    private final String END_PREFIX_MAPPING = "END_PREFIX_MAPPING";
    private final ArrayBuffer events;
    private final ArrayBuffer strings;
    private final ArrayBuffer characters;

    private byte lastElement = UNDEFINED;
    private boolean useAsFragment;
    private boolean assumeMixedContent = true;

    public SAXBuffer(int bufferSize) {
        events = new ArrayBuffer<>(Byte.class, bufferSize);
        strings = new ArrayBuffer<>(String.class, bufferSize);
        characters = new ArrayBuffer<>(char[].class, bufferSize);
    }

    public SAXBuffer() {
        this(DEFAULT_BUFFER_SIZE);
    }

    public boolean isEmpty() {
        return events.isEmpty();
    }

    public boolean isUseAsFragment() {
        return useAsFragment;
    }

    public SAXBuffer useAsFragment(boolean useAsFragment) {
        this.useAsFragment = useAsFragment;
        return this;
    }

    public boolean isAssumeMixedContent() {
        return assumeMixedContent;
    }

    public SAXBuffer assumeMixedContent(boolean assumeMixedContent) {
        this.assumeMixedContent = assumeMixedContent;
        return this;
    }

    public void removeTrailingCharacters() {
        while (!events.isEmpty() && events.peek() == CHARACTERS) {
            events.pop();
            characters.pop();
        }
    }

    public void trimToSize() {
        events.trimToSize();
        strings.trimToSize();
        characters.trimToSize();
    }

    public void reset() {
        events.clear();
        strings.clear();
        characters.clear();
        lastElement = UNDEFINED;
    }

    public void addStartDocument() {
        if (!useAsFragment) {
            events.push(START_DOCUMENT);
        }
    }

    public void addEndDocument() {
        if (!useAsFragment) {
            events.push(END_DOCUMENT);
        }
    }

    public void addNamespacePrefixMapping(String prefix, String uri) {
        events.push(NAMESPACE_PREFIX_MAPPING);
        strings.push(prefix);
        strings.push(uri);
    }

    public void addStartElement(String uri, String localName, String qName) {
        if (!assumeMixedContent && lastElement == START_ELEMENT) {
            removeTrailingCharacters();
        }

        events.push(START_ELEMENT);
        strings.push(uri);
        strings.push(localName);
        strings.push(qName.length() != localName.length() ? qName : "");
        lastElement = START_ELEMENT;
    }

    public void addAttribute(String uri, String localName, String qName, String type, String value) {
        if (lastElement == START_ELEMENT) {
            events.push(ATTRIBUTE);
            strings.push(uri);
            strings.push(localName);
            strings.push(qName.length() != localName.length() ? qName : "");
            strings.push(type);
            strings.push(value);
        }
    }

    public void addEndElement() {
        events.push(END_ELEMENT);
        lastElement = END_ELEMENT;
    }

    public void addCharacters(char[] chars) {
        if (assumeMixedContent || lastElement == START_ELEMENT) {
            events.push(CHARACTERS);
            characters.push(chars);
        }
    }

    @Override
    public void startDocument() throws SAXException {
        addStartDocument();
    }

    @Override
    public void endDocument() throws SAXException {
        addEndDocument();
    }

    @Override
    public void startPrefixMapping(String prefix, String uri) throws SAXException {
        addNamespacePrefixMapping(prefix, uri);
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        addStartElement(uri, localName, qName);
        for (int i = 0; i < attributes.getLength(); i++) {
            addAttribute(attributes.getURI(i),
                    attributes.getLocalName(i),
                    attributes.getQName(i),
                    attributes.getType(i),
                    attributes.getValue(i));
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        addEndElement();
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        char[] chars = new char[length];
        System.arraycopy(ch, start, chars, 0, length);
        addCharacters(chars);
    }

    public XMLStreamReader toXMLStreamReader(boolean release) {
        XMLStreamReader reader = new SAXStreamReader(events.iterator(release),
                strings.iterator(release),
                characters.iterator(release));

        if (release) {
            lastElement = UNDEFINED;
        }

        return reader;
    }

    public void send(ContentHandler handler, boolean release) throws SAXException {
        ArrayBufferIterator events = this.events.iterator(release);
        ArrayBufferIterator strings = this.strings.iterator(release);
        ArrayBufferIterator characters = this.characters.iterator(release);

        AttributesImpl attributes = new AttributesImpl();
        ArrayBuffer util = new ArrayBuffer<>(String.class, DEFAULT_BUFFER_SIZE);

        if (release) {
            lastElement = UNDEFINED;
        }

        while (events.hasNext()) {
            switch (events.next()) {
                case START_ELEMENT:
                    sendStartElement(handler, events, strings, util, attributes);
                    break;
                case END_ELEMENT:
                    sendEndElement(handler, util);
                    break;
                case CHARACTERS:
                    char[] ch = characters.next();
                    handler.characters(ch, 0, ch.length);
                    break;
                case NAMESPACE_PREFIX_MAPPING:
                    sendStartPrefixMapping(handler, strings, util);
                    break;
                case START_DOCUMENT:
                    handler.startDocument();
                    break;
                case END_DOCUMENT:
                    handler.endDocument();
                    break;
            }
        }
    }

    private void sendStartElement(ContentHandler handler, ArrayBufferIterator events, ArrayBufferIterator strings, ArrayBuffer util, AttributesImpl attributes) throws SAXException {
        String uri = strings.next();
        String localName = strings.next();
        String qName = strings.next();
        if (qName.isEmpty()) {
            qName = localName;
        }

        while (events.hasNext() && events.peek() == ATTRIBUTE) {
            events.next();
            String attributeURI = strings.next();
            String attributeLocalName = strings.next();
            String attributeQName = strings.next();
            String attributeType = strings.next();
            String attributeValue = strings.next();

            attributes.addAttribute(attributeURI != null ? attributeURI : XMLConstants.NULL_NS_URI,
                    attributeLocalName,
                    !attributeQName.isEmpty() ? attributeQName : attributeLocalName,
                    attributeType,
                    attributeValue);
        }

        handler.startElement(uri, localName, qName, attributes);

        util.push(uri);
        util.push(localName);
        util.push(qName);
        attributes.clear();
    }

    private void sendEndElement(ContentHandler handler, ArrayBuffer util) throws SAXException {
        String qName = util.pop();
        String localName = util.pop();
        String uri = util.pop();

        handler.endElement(uri, localName, qName);

        while (END_PREFIX_MAPPING.equals(util.peek())) {
            util.pop();
            String prefix = util.pop();
            handler.endPrefixMapping(prefix);
        }
    }

    private void sendStartPrefixMapping(ContentHandler handler, ArrayBufferIterator strings, ArrayBuffer util) throws SAXException {
        String prefix = strings.next();
        String uri = strings.next();
        if (prefix == null) {
            prefix = XMLConstants.DEFAULT_NS_PREFIX;
        }

        handler.startPrefixMapping(prefix, uri);

        util.push(prefix);
        util.push(END_PREFIX_MAPPING);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy