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

org.eclipse.persistence.oxm.record.JSONWriterRecord Maven / Gradle / Ivy

There is a newer version: 5.0.0-B02
Show newest version
/*
 * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Blaise Doughan - 2.4 - initial implementation
package org.eclipse.persistence.oxm.record;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.List;

import javax.xml.namespace.QName;

import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.core.helper.CoreConversionManager;
import org.eclipse.persistence.internal.oxm.CharacterEscapeHandler;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.ConversionManager;
import org.eclipse.persistence.internal.oxm.NamespaceResolver;
import org.eclipse.persistence.internal.oxm.ObjectBuilder;
import org.eclipse.persistence.internal.oxm.Root;
import org.eclipse.persistence.internal.oxm.XMLBinaryDataHelper;
import org.eclipse.persistence.internal.oxm.XMLMarshaller;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.internal.oxm.record.ExtendedContentHandler;
import org.eclipse.persistence.internal.oxm.record.XMLFragmentReader;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;

/**
 * 

Use this type of MarshalRecord when the marshal target is a Writer and the * JSON should not be formatted with carriage returns or indenting.

*

* XMLContext xmlContext = new XMLContext("session-name");
* XMLMarshaller xmlMarshaller = xmlContext.createMarshaller();
* JSONRecord jsonWriterRecord = new JSONWriterRecord();
* jsonWriterRecord.setWriter(myWriter);
* xmlMarshaller.marshal(myObject, jsonWriterRecord);
*

*

If the marshal(Writer) and setMediaType(MediaType.APPLICATION_JSON) and * setFormattedOutput(false) method is called on XMLMarshaller, then the Writer * is automatically wrapped in a JSONWriterRecord.

*

* XMLContext xmlContext = new XMLContext("session-name");
* XMLMarshaller xmlMarshaller = xmlContext.createMarshaller();
* xmlMarshaller.setMediaType(MediaType.APPLICATION_JSON); * xmlMarshaller xmlMarshaller.setFormattedOutput(false);
* xmlMarshaller.marshal(myObject, myWriter);
*

* @see org.eclipse.persistence.oxm.XMLMarshaller */ public class JSONWriterRecord extends MarshalRecord { protected boolean isProcessingCData = false; protected static final String NULL="null"; protected String attributePrefix; protected boolean charactersAllowed = false; protected CharsetEncoder encoder; protected CharacterEscapeHandler characterEscapeHandler; protected String callbackName; protected Output writer; protected Level level; public JSONWriterRecord(){ super(); } public JSONWriterRecord(OutputStream outputStream) { this(); writer = new OutputStreamOutput(outputStream); } public JSONWriterRecord(OutputStream outputStream, String callbackName){ this(outputStream); setCallbackName(callbackName); } public JSONWriterRecord(Writer writer){ this(); setWriter(writer); } public JSONWriterRecord(Writer writer, String callbackName){ this(writer); setCallbackName(callbackName); } public void setCallbackName(String callbackName){ this.callbackName = callbackName; } /** * INTERNAL: */ @Override public void setMarshaller(XMLMarshaller marshaller) { super.setMarshaller(marshaller); attributePrefix = marshaller.getAttributePrefix(); encoder = Charset.forName(marshaller.getEncoding()).newEncoder(); if (marshaller.getValueWrapper() != null) { textWrapperFragment = new XPathFragment(); textWrapperFragment.setLocalName(marshaller.getValueWrapper()); } characterEscapeHandler = marshaller.getCharacterEscapeHandler(); writer.setMarshaller(marshaller); } /** * Handle marshal of an empty collection. * @param openGrouping if grouping elements should be marshalled for empty collections */ @Override public boolean emptyCollection(XPathFragment xPathFragment, NamespaceResolver namespaceResolver, boolean openGrouping) { if(marshaller.isMarshalEmptyCollections()){ super.emptyCollection(xPathFragment, namespaceResolver, true); if (null != xPathFragment) { startCollection(); if (!xPathFragment.isSelfFragment()) { openStartElement(xPathFragment, namespaceResolver); if (null != level) { level.setNeedToCloseComplex(false); level.setNeedToOpenComplex(false); } endElement(xPathFragment, namespaceResolver); } endEmptyCollection(); } return true; }else{ return super.emptyCollection(xPathFragment, namespaceResolver, openGrouping); } } @Override public void forceValueWrapper(){ charactersAllowed = false; } /** * Return the Writer that the object will be marshalled to. * @return The marshal target. */ public Writer getWriter() { return writer.getWriter(); } /** * Set the Writer that the object will be marshalled to. * @param writer The marshal target. */ public void setWriter(Writer writer) { this.writer = new WriterOutput(writer); } @Override public void namespaceDeclaration(String prefix, String namespaceURI){ } @Override public void defaultNamespaceDeclaration(String defaultNamespace){ } /** * INTERNAL: */ @Override public void startDocument(String encoding, String version) { try { if(null != level) { if(level.isFirst()) { level.setFirst(false); } else { writeListSeparator(); } }else if(callbackName != null){ startCallback(); } level = new Level(true, false, false, level); writer.write('{'); } catch (IOException e) { throw XMLMarshalException.marshalException(e); } } protected void writeListSeparator() throws IOException{ writer.write(','); } protected void writeSeparator() throws IOException{ writer.write(Constants.COLON); } /** * INTERNAL: */ protected void startCallback() throws IOException{ if(callbackName != null){ writer.write(callbackName); writer.write('('); } } /** * INTERNAL: */ @Override public void endDocument() { try { closeComplex(); if(null != level && null == level.getPreviousLevel()){ endCallback(); } level = level.getPreviousLevel(); } catch (IOException e) { throw XMLMarshalException.marshalException(e); } } /** * INTERNAL: */ @Override public void openStartElement(XPathFragment xPathFragment, NamespaceResolver namespaceResolver) { try { if(level.isFirst()) { level.setFirst(false); } else { writer.write(','); } if(xPathFragment.nameIsText()){ if(level.isCollection() && level.isEmptyCollection()) { writer.write('['); level.setEmptyCollection(false); level.setNeedToOpenComplex(false); charactersAllowed = true; level = new Level(true, true, false, level); return; } } if(level.needToOpenComplex){ if (!level.isNestedArray()) { writer.write('{'); } level.needToOpenComplex = false; level.needToCloseComplex = true; } //write the key unless this is a a non-empty collection if(!(level.isCollection() && !level.isEmptyCollection())){ if (!level.isNestedArray()) { writeKey(xPathFragment); } //if it is the first thing in the collection also add the [ if(level.isCollection() && level.isEmptyCollection()){ writer.write('['); level.setEmptyCollection(false); } } charactersAllowed = true; if (xPathFragment.getXMLField() != null && xPathFragment.getXMLField().isNestedArray() && this.marshaller.getJsonTypeConfiguration().isJsonDisableNestedArrayName()) { level = new Level(true, true, true, level); } else { level = new Level(true, true, false, level); } } catch (IOException e) { throw XMLMarshalException.marshalException(e); } } /** * INTERNAL: */ @Override public void element(XPathFragment frag) { } /** * INTERNAL: */ @Override public void attribute(String namespaceURI, String localName, String qName, String value) { XPathFragment xPathFragment = new XPathFragment(); xPathFragment.setNamespaceURI(namespaceURI); xPathFragment.setAttribute(true); xPathFragment.setLocalName(localName); openStartElement(xPathFragment, namespaceResolver); characters(null, value, null, false, true); endElement(xPathFragment, namespaceResolver); } /** * INTERNAL: */ @Override public void attribute(XPathFragment xPathFragment, NamespaceResolver namespaceResolver, String value) { attribute(xPathFragment, namespaceResolver, value, null); } /** * INTERNAL: * override so we don't iterate over namespaces when startPrefixMapping doesn't do anything */ @Override public void startPrefixMappings(NamespaceResolver namespaceResolver) { } /** * INTERNAL: * override so we don't iterate over namespaces when endPrefixMapping doesn't do anything */ @Override public void endPrefixMappings(NamespaceResolver namespaceResolver) { } /** * INTERNAL: */ @Override public void closeStartElement() {} /** * INTERNAL: */ @Override public void endElement(XPathFragment xPathFragment, NamespaceResolver namespaceResolver) { try{ if(null != level) { if(level.needToOpenComplex){ writer.write('{'); closeComplex(); } else if(level.needToCloseComplex && !level.nestedArray){ closeComplex(); } charactersAllowed = false; level = level.getPreviousLevel(); } } catch (IOException e) { throw XMLMarshalException.marshalException(e); } } protected void closeComplex() throws IOException { writer.write('}'); } @Override public void startCollection() { if(null == level) { try { startCallback(); writer.write('['); level = new Level(true, false, false, level); } catch(IOException e) { throw XMLMarshalException.marshalException(e); } } else { level.setCollection(true); level.setEmptyCollection(true); charactersAllowed = false; } } protected void endEmptyCollection(){ endCollection(); } protected void endCallback() throws IOException{ if(callbackName != null){ writer.write(')'); writer.write(';'); } } @Override public void endCollection() { try { if(level != null && null == level.getPreviousLevel()) { writer.write(']'); endCallback(); } else { if(level != null && level.isCollection() && !level.isEmptyCollection()) { writer.write(']'); } } level.setCollection(false); } catch (IOException e) { throw XMLMarshalException.marshalException(e); } } /** * INTERNAL: */ @Override public void characters(String value) { characters(value, true, false); } /** * INTERNAL: */ public void characters(String value, boolean isString, boolean isAttribute) { boolean textWrapperOpened = false; if(!charactersAllowed){ if(textWrapperFragment != null){ openStartElement(textWrapperFragment, namespaceResolver); textWrapperOpened = true; } } level.setNeedToOpenComplex(false); try { if(isString){ writer.write('"'); writeValue(value, isAttribute); writer.write('"'); }else{ writer.write(value); } } catch (IOException e) { throw XMLMarshalException.marshalException(e); } if(textWrapperOpened){ if(textWrapperFragment != null){ endElement(textWrapperFragment, namespaceResolver); } } } @Override public void attribute(XPathFragment xPathFragment, NamespaceResolver namespaceResolver, Object value, QName schemaType){ if(xPathFragment.getNamespaceURI() != null && xPathFragment.getNamespaceURI() == javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI){ return; } xPathFragment.setAttribute(true); openStartElement(xPathFragment, namespaceResolver); characters(schemaType, value, null, false, true); endElement(xPathFragment, namespaceResolver); } @Override public void characters(QName schemaType, Object value, String mimeType, boolean isCDATA){ characters(schemaType, value, mimeType, isCDATA, false); } public void characters(QName schemaType, Object value, String mimeType, boolean isCDATA, boolean isAttribute) { if(mimeType != null) { if(value instanceof List){ value = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesListForBinaryValues(// (List)value, marshaller, mimeType); }else{ value = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesForBinaryValue(// value, marshaller, mimeType).getData(); } } if(schemaType != null && Constants.QNAME_QNAME.equals(schemaType)){ String convertedValue = getStringForQName((QName)value); characters(convertedValue); } else if(value.getClass() == String.class){ //if schemaType is set and it's a numeric or boolean type don't treat as a string if(schemaType != null && isNumericOrBooleanType(schemaType)){ String convertedValue = ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType); characters(convertedValue, false, isAttribute); }else if(isCDATA){ cdata((String)value); }else{ characters((String)value); } }else{ ConversionManager conversionManager = getConversionManager(); String convertedValue = conversionManager.convertObject(value, CoreClassConstants.STRING, schemaType); Class theClass = conversionManager.javaType(schemaType); if(schemaType == null || theClass == null){ if(value.getClass() == CoreClassConstants.BOOLEAN || CoreClassConstants.NUMBER.isAssignableFrom(value.getClass())){ characters(convertedValue, false, isAttribute); }else{ characters(convertedValue); } }else if(schemaType != null && !isNumericOrBooleanType(schemaType)){ //if schemaType exists and is not boolean or number do write quotes characters(convertedValue); } else if(isCDATA){ cdata(convertedValue); }else{ characters(convertedValue, false, isAttribute); } } charactersAllowed = false; } private boolean isNumericOrBooleanType(QName schemaType){ if(schemaType == null){ return false; }else if(schemaType.equals(Constants.BOOLEAN_QNAME) || schemaType.equals(Constants.INTEGER_QNAME) || schemaType.equals(Constants.INT_QNAME) || schemaType.equals(Constants.BYTE_QNAME) || schemaType.equals(Constants.DECIMAL_QNAME) || schemaType.equals(Constants.FLOAT_QNAME) || schemaType.equals(Constants.DOUBLE_QNAME) || schemaType.equals(Constants.SHORT_QNAME) || schemaType.equals(Constants.LONG_QNAME) || schemaType.equals(Constants.NEGATIVE_INTEGER_QNAME) || schemaType.equals(Constants.NON_NEGATIVE_INTEGER_QNAME) || schemaType.equals(Constants.NON_POSITIVE_INTEGER_QNAME) || schemaType.equals(Constants.POSITIVE_INTEGER_QNAME) || schemaType.equals(Constants.UNSIGNED_BYTE_QNAME) || schemaType.equals(Constants.UNSIGNED_INT_QNAME) || schemaType.equals(Constants.UNSIGNED_LONG_QNAME) || schemaType.equals(Constants.UNSIGNED_SHORT_QNAME) ){ return true; } return false; } /** * INTERNAL: */ @Override public void namespaceDeclarations(NamespaceResolver namespaceResolver) { } /** * INTERNAL: */ @Override public void nilComplex(XPathFragment xPathFragment, NamespaceResolver namespaceResolver){ XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver); closeStartGroupingElements(groupingFragment); openStartElement(xPathFragment, namespaceResolver); characters(NULL, false, false); endElement(xPathFragment, namespaceResolver); } /** * INTERNAL: */ @Override public void nilSimple(NamespaceResolver namespaceResolver){ XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver); characters(NULL, false, false); closeStartGroupingElements(groupingFragment); } /** * Used when an empty simple value should be written * @since EclipseLink 2.4 */ @Override public void emptySimple(NamespaceResolver namespaceResolver){ nilSimple(namespaceResolver); } @Override public void emptyAttribute(XPathFragment xPathFragment,NamespaceResolver namespaceResolver){ XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver); openStartElement(xPathFragment, namespaceResolver); characters(NULL, false, false); endElement(xPathFragment, namespaceResolver); closeStartGroupingElements(groupingFragment); } /** * Used when an empty complex item should be written * @since EclipseLink 2.4 */ @Override public void emptyComplex(XPathFragment xPathFragment, NamespaceResolver namespaceResolver){ XPathFragment groupingFragment = openStartGroupingElements(namespaceResolver); closeStartGroupingElements(groupingFragment); openStartElement(xPathFragment, namespaceResolver); endElement(xPathFragment, namespaceResolver); } /** * INTERNAL: */ @Override public void marshalWithoutRootElement(ObjectBuilder treeObjectBuilder, Object object, Descriptor descriptor, Root root, boolean isXMLRoot){ if(treeObjectBuilder != null){ addXsiTypeAndClassIndicatorIfRequired(descriptor, null, descriptor.getDefaultRootElementField(), root, object, isXMLRoot, true); treeObjectBuilder.marshalAttributes(this, object, session); } } /** * INTERNAL: */ @Override public void cdata(String value) { characters(value); } /** * INTERNAL: * The character used to separate the prefix and uri portions when namespaces are present * @since 2.4 */ @Override public char getNamespaceSeparator(){ return marshaller.getNamespaceSeparator(); } /** * INTERNAL: * The optional fragment used to wrap the text() mappings * @since 2.4 */ @Override public XPathFragment getTextWrapperFragment() { return textWrapperFragment; } protected void writeKey(XPathFragment xPathFragment) throws IOException { if (xPathFragment.getLocalName() != null && !xPathFragment.getLocalName().equals(Constants.EMPTY_STRING)) { super.openStartElement(xPathFragment, namespaceResolver); writer.write('"'); if (xPathFragment.isAttribute() && attributePrefix != null) { writer.writeAttributePrefix(); } if (isNamespaceAware()) { if (xPathFragment.getNamespaceURI() != null) { String prefix = null; if (getNamespaceResolver() != null) { prefix = getNamespaceResolver().resolveNamespaceURI(xPathFragment.getNamespaceURI()); } else if (namespaceResolver != null) { prefix = namespaceResolver.resolveNamespaceURI(xPathFragment.getNamespaceURI()); } if (prefix != null && !prefix.equals(Constants.EMPTY_STRING)) { writer.write(prefix); writer.writeNamespaceSeparator(); } } } writer.writeLocalName(xPathFragment); writer.write('"'); writeSeparator(); } } /** * INTERNAL: */ protected void writeValue(String value, boolean isAttribute) { try { if (characterEscapeHandler != null) { writer.writeResultFromCharEscapeHandler(value, isAttribute); return; } char[] chars = value.toCharArray(); for (int x = 0, charsSize = chars.length; x < charsSize; x++) { char character = chars[x]; switch (character){ case '"' : { writer.write("\\\""); break; } case '\b': { writer.write("\\b"); break; } case '\f': { writer.write("\\f"); break; } case '\n': { writer.write("\\n"); break; } case '\r': { writer.write("\\r"); break; } case '\t': { writer.write("\\t"); break; } case '\\': { writer.write("\\\\"); break; } default: { if(Character.isISOControl(character) || !encoder.canEncode(character)){ writer.write("\\u"); String hex = Integer.toHexString(character).toUpperCase(); for(int i=hex.length(); i<4; i++){ writer.write("0"); } writer.write(hex); }else{ writer.write(character); } } } } } catch (IOException e) { throw XMLMarshalException.marshalException(e); } } @Override protected String getStringForQName(QName qName){ if(null == qName) { return null; } CoreConversionManager xmlConversionManager = getSession().getDatasourcePlatform().getConversionManager(); return xmlConversionManager.convertObject(qName, String.class); } /** * Receive notification of a node. * @param node The Node to be added to the document * @param namespaceResolver The NamespaceResolver can be used to resolve the * namespace URI/prefix of the node */ @Override public void node(Node node, NamespaceResolver namespaceResolver, String uri, String name) { if (node.getNodeType() == Node.ATTRIBUTE_NODE) { Attr attr = (Attr) node; String resolverPfx = null; if (getNamespaceResolver() != null) { resolverPfx = this.getNamespaceResolver().resolveNamespaceURI(attr.getNamespaceURI()); } String namespaceURI = attr.getNamespaceURI(); // If the namespace resolver contains a prefix for the attribute's URI, // use it instead of what is set on the attribute if (resolverPfx != null) { attribute(attr.getNamespaceURI(), Constants.EMPTY_STRING, resolverPfx+Constants.COLON+attr.getLocalName(), attr.getNodeValue()); } else { attribute(attr.getNamespaceURI(), Constants.EMPTY_STRING, attr.getName(), attr.getNodeValue()); // May need to declare the URI locally if (attr.getNamespaceURI() != null) { attribute(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, Constants.EMPTY_STRING, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + attr.getPrefix(), attr.getNamespaceURI()); this.getNamespaceResolver().put(attr.getPrefix(), attr.getNamespaceURI()); } } } else if (node.getNodeType() == Node.TEXT_NODE) { characters(node.getNodeValue(), false, false); } else { try { JSONWriterRecordContentHandler wrcHandler = new JSONWriterRecordContentHandler(); XMLFragmentReader xfragReader = new XMLFragmentReader(namespaceResolver); xfragReader.setContentHandler(wrcHandler); xfragReader.setProperty("http://xml.org/sax/properties/lexical-handler", wrcHandler); xfragReader.parse(node, uri, name); } catch (SAXException sex) { throw XMLMarshalException.marshalException(sex); } } } @Override public boolean isWrapperAsCollectionName() { return marshaller.isWrapperAsCollectionName(); } @Override public void flush() { try { writer.flush(); } catch(IOException e) { throw XMLMarshalException.marshalException(e); } } /** * This class will typically be used in conjunction with an XMLFragmentReader. * The XMLFragmentReader will walk a given XMLFragment node and report events * to this class - the event's data is then written to the enclosing class' * writer. * * @see org.eclipse.persistence.internal.oxm.record.XMLFragmentReader */ protected class JSONWriterRecordContentHandler implements ExtendedContentHandler, LexicalHandler { JSONWriterRecordContentHandler() { } // --------------------- CONTENTHANDLER METHODS --------------------- // @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { XPathFragment xPathFragment = new XPathFragment(localName); xPathFragment.setNamespaceURI(namespaceURI); openStartElement(xPathFragment, namespaceResolver); handleAttributes(atts); } @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { XPathFragment xPathFragment = new XPathFragment(localName); xPathFragment.setNamespaceURI(namespaceURI); JSONWriterRecord.this.endElement(xPathFragment, namespaceResolver); } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { } @Override public void characters(char[] ch, int start, int length) throws SAXException { String characters = new String (ch, start, length); characters(characters); } @Override public void characters(CharSequence characters) throws SAXException { JSONWriterRecord.this.characters(characters.toString()); } // --------------------- LEXICALHANDLER METHODS --------------------- // @Override public void comment(char[] ch, int start, int length) throws SAXException { } @Override public void startCDATA() throws SAXException { isProcessingCData = true; } @Override public void endCDATA() throws SAXException { isProcessingCData = false; } // --------------------- CONVENIENCE METHODS --------------------- // protected void handleAttributes(Attributes atts) { for (int i=0, attsLength = atts.getLength(); i= BUFFER_SIZE) { try { outputStream.write(buffer, 0, bufferIndex); bufferIndex = 0; if(bytesLength > BUFFER_SIZE) { outputStream.write(bytes); return; } } catch(IOException e) { throw XMLMarshalException.marshalException(e); } } System.arraycopy(bytes, 0, buffer, bufferIndex, bytes.length); bufferIndex += bytesLength; } @Override public void write(char character) throws IOException { if (character > 0x7F) { if(character > 0x7FF) { if((character >= Character.MIN_HIGH_SURROGATE) && (character <= Character.MAX_LOW_SURROGATE)) { int uc = ((character & 0x3ff) << 10); // 11110zzz write((byte)(0xF0 | ((uc >> 18)))); // 10zzyyyy write((byte)(0x80 | ((uc >> 12) & 0x3F))); // 10yyyyxx write((byte)(0x80 | ((uc >> 6) & 0x3F))); // 10xxxxxx write((byte)(0x80 + (uc & 0x3F))); return; } else { // 1110yyyy write((byte)(0xE0 + (character >> 12))); } // 10yyyyxx write((byte)(0x80 + ((character >> 6) & 0x3F))); } else { // 110yyyxx write((byte)(0xC0 + (character >> 6))); } write((byte)(0x80 + (character & 0x3F))); } else { write((byte) character); } } private void write(byte b) { if(bufferIndex == BUFFER_SIZE) { try { outputStream.write(buffer, 0, BUFFER_SIZE); bufferIndex = 0; } catch(IOException e) { throw XMLMarshalException.marshalException(e); } } buffer[bufferIndex++] = b; } @Override public void write(String text) throws IOException { write(text.getBytes(Constants.DEFAULT_CHARSET)); } @Override public void writeAttributePrefix() throws IOException { write(attributePrefix); } @Override public void writeCR() throws IOException { write(cr); } @Override public void writeLocalName(XPathFragment xPathFragment) throws IOException { write(xPathFragment.getLocalNameBytes()); } @Override public void writeNamespaceSeparator() throws IOException { write(namespaceSeparator); } @Override public void writeResultFromCharEscapeHandler(String value, boolean isAttribute) { try { CharArrayWriter out = new CharArrayWriter(); characterEscapeHandler.escape(value.toCharArray(), 0, value.length(), isAttribute, out); byte[] bytes = out.toString().getBytes(); write(bytes); out.close(); } catch (IOException e) { throw XMLMarshalException.marshalException(e); } } } private static class WriterOutput implements Output { private String attributePrefix; private CharacterEscapeHandler characterEscapeHandler; private String cr = Constants.cr(); private XMLMarshaller marshaller; private char namespaceSeparator; private Writer writer; @Override public void flush() throws IOException { writer.flush(); } protected WriterOutput(Writer writer) { this.writer = writer; } @Override public XMLMarshaller getMarshaller() { return marshaller; } @Override public OutputStream getOutputStream() { return null; } @Override public Writer getWriter() { return writer; } @Override public void setMarshaller(XMLMarshaller marshaller) { this.marshaller = marshaller; this.attributePrefix = marshaller.getAttributePrefix(); this.characterEscapeHandler = marshaller.getCharacterEscapeHandler(); this.namespaceSeparator = marshaller.getNamespaceSeparator(); } @Override public void writeAttributePrefix() throws IOException { writer.write(attributePrefix); } @Override public void write(char character) throws IOException { writer.write(character); } @Override public void write(String text) throws IOException { writer.write(text); } @Override public void writeCR() throws IOException { writer.write(cr); } @Override public void writeLocalName(XPathFragment xPathFragment) throws IOException { writer.write(xPathFragment.getLocalName()); } @Override public void writeNamespaceSeparator() throws IOException { writer.write(namespaceSeparator); } @Override public void writeResultFromCharEscapeHandler(String value, boolean isAttribute) { try { characterEscapeHandler.escape(value.toCharArray(), 0, value.length(), isAttribute, writer); } catch (IOException e) { throw XMLMarshalException.marshalException(e); } } } }