<commit_before>/**
 *  Copyright (C) 2004 Orbeon, Inc.
 *
 *  This program 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 program 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.
 *
 *  The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */
package org.orbeon.oxf.util;

import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.orbeon.oxf.common.OXFException;
import org.orbeon.oxf.xml.XMLConstants;

import java.io.IOException;
import java.io.OutputStream;

/**
 * An OutputStream that converts the bytes written into it into Base64-encoded characters written to
 * a ContentHandler.
 */
public class ContentHandlerOutputStream extends OutputStream {

    private static final String DEFAULT_BINARY_DOCUMENT_ELEMENT = "document";

    private ContentHandler contentHandler;

    private byte[] byteBuffer = new byte[76 * 3 / 4]; // maximum bytes that, once decoded, can fit in a line of 76 characters
    private int currentBufferSize = 0;
    private char[] resultingLine = new char[76 + 1];

    private byte[] singleByte = new byte[1];

    public ContentHandlerOutputStream(ContentHandler contentHandler) {
        this.contentHandler = contentHandler;
    }

    public void startDocument(String contentType) throws SAXException {
        // Start document
        AttributesImpl attributes = new AttributesImpl();
        contentHandler.startPrefixMapping(XMLConstants.XSI_PREFIX, XMLConstants.XSI_URI);
        contentHandler.startPrefixMapping(XMLConstants.XSD_PREFIX, XMLConstants.XSD_URI);
        attributes.addAttribute(XMLConstants.XSI_URI, "type", "xsi:type", "CDATA", XMLConstants.XS_BASE64BINARY_QNAME.getQualifiedName());
        if (contentType != null)
            attributes.addAttribute("", "content-type", "content-type", "CDATA", contentType);

        contentHandler.startDocument();
        contentHandler.startElement("", DEFAULT_BINARY_DOCUMENT_ELEMENT, DEFAULT_BINARY_DOCUMENT_ELEMENT, attributes);
    }

    public void endDocument() throws SAXException {
        // End document
        contentHandler.endElement("", DEFAULT_BINARY_DOCUMENT_ELEMENT, DEFAULT_BINARY_DOCUMENT_ELEMENT);
        contentHandler.endDocument();
    }

    public void close() throws IOException {
        flushBuffer();
    }

    public void flush() throws IOException {
        try {
            // NOTE: This will only flush on Base64 line boundaries. Is that what we want? Or
            // should we just ignore? We can't output an incomplete encoded line unless we have a
            // number of bytes in the buffer multiple of 3. Otherwise, we would have to output '='
            // characters, which do signal an end of transmission.
            contentHandler.processingInstruction("oxf-serializer", "flush");
        } catch (SAXException e) {
            throw new OXFException(e);
        }
    }
<commit_msg>Fixed "[ #307608 ] oxf:pdf-template processor outputs malformed XML document".<commit_after>/**
 *  Copyright (C) 2004 Orbeon, Inc.
 *
 *  This program 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 program 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.
 *
 *  The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */
package org.orbeon.oxf.util;

import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.orbeon.oxf.common.OXFException;
import org.orbeon.oxf.xml.XMLConstants;

import java.io.IOException;
import java.io.OutputStream;

/**
 * An OutputStream that converts the bytes written into it into Base64-encoded characters written to
 * a ContentHandler.
 */
public class ContentHandlerOutputStream extends OutputStream {

    private static final String DEFAULT_BINARY_DOCUMENT_ELEMENT = "document";

    private ContentHandler contentHandler;

    private byte[] byteBuffer = new byte[76 * 3 / 4]; // maximum bytes that, once decoded, can fit in a line of 76 characters
    private int currentBufferSize = 0;
    private char[] resultingLine = new char[76 + 1];

    private byte[] singleByte = new byte[1];

    public ContentHandlerOutputStream(ContentHandler contentHandler) {
        this.contentHandler = contentHandler;
    }

    public void startDocument(String contentType) throws SAXException {
        // Start document
        AttributesImpl attributes = new AttributesImpl();
        attributes.addAttribute(XMLConstants.XSI_URI, "type", "xsi:type", "CDATA", XMLConstants.XS_BASE64BINARY_QNAME.getQualifiedName());
        if (contentType != null)
            attributes.addAttribute("", "content-type", "content-type", "CDATA", contentType);

        contentHandler.startDocument();
        contentHandler.startPrefixMapping(XMLConstants.XSI_PREFIX, XMLConstants.XSI_URI);
        contentHandler.startPrefixMapping(XMLConstants.XSD_PREFIX, XMLConstants.XSD_URI);
        contentHandler.startElement("", DEFAULT_BINARY_DOCUMENT_ELEMENT, DEFAULT_BINARY_DOCUMENT_ELEMENT, attributes);
    }

    public void endDocument() throws SAXException {
        // End document
        contentHandler.endElement("", DEFAULT_BINARY_DOCUMENT_ELEMENT, DEFAULT_BINARY_DOCUMENT_ELEMENT);
        contentHandler.endPrefixMapping(XMLConstants.XSI_PREFIX);
        contentHandler.endPrefixMapping(XMLConstants.XSD_PREFIX);
        contentHandler.endDocument();
    }

    public void close() throws IOException {
        flushBuffer();
    }

    public void flush() throws IOException {
        try {
            // NOTE: This will only flush on Base64 line boundaries. Is that what we want? Or
            // should we just ignore? We can't output an incomplete encoded line unless we have a
            // number of bytes in the buffer multiple of 3. Otherwise, we would have to output '='
            // characters, which do signal an end of transmission.
            contentHandler.processingInstruction("oxf-serializer", "flush");
        } catch (SAXException e) {
            throw new OXFException(e);
        }
    }

{'fraction_non_alphanumeric': 0.08205577456563001, 'fraction_numerical': 0.00788436268068331, 'mean_word_length': 3.494750656167979, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '530290', 'n_tokens_mistral': 1851, 'n_tokens_neox': 1768, 'n_words': 611}