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

com.helger.jaxb.IJAXBWriter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014-2024 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * 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 com.helger.jaxb;

import java.io.File;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Path;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.WillClose;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;

import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.helger.commons.io.EAppend;
import com.helger.commons.io.file.FileHelper;
import com.helger.commons.io.resource.IWritableResource;
import com.helger.commons.io.stream.ByteBufferOutputStream;
import com.helger.commons.io.stream.NonBlockingByteArrayInputStream;
import com.helger.commons.io.stream.NonBlockingStringWriter;
import com.helger.commons.io.stream.StreamHelper;
import com.helger.commons.state.ESuccess;
import com.helger.commons.string.StringHelper;
import com.helger.commons.system.ENewLineMode;
import com.helger.xml.XMLFactory;
import com.helger.xml.microdom.IMicroDocument;
import com.helger.xml.microdom.IMicroElement;
import com.helger.xml.microdom.serialize.MicroSAXHandler;
import com.helger.xml.serialize.write.EXMLIncorrectCharacterHandling;
import com.helger.xml.serialize.write.EXMLSerializeIndent;
import com.helger.xml.serialize.write.IXMLWriterSettings;
import com.helger.xml.serialize.write.SafeXMLStreamWriter;
import com.helger.xml.serialize.write.XMLWriterSettings;
import com.helger.xml.transform.TransformResultFactory;

/**
 * Interface for writing JAXB documents to various destinations.
 *
 * @author Philip Helger
 * @param 
 *        The JAXB type to be written
 */
public interface IJAXBWriter 
{
  /**
   * Use the {@link SafeXMLStreamWriter} where applicable to ensure valid XML is
   * created? This is a work around for
   * https://github.com/eclipse-ee4j/jaxb-ri/issues/614 and
   * https://github.com/eclipse-ee4j/jaxb-ri/issues/960
* Note: these bugs are still open for JAXB 4.0.0 */ boolean USE_JAXB_CHARSET_FIX = true; /** * @return The special JAXB namespace context to be used. May be * null. * @since 8.5.3 in this interface */ @Nullable NamespaceContext getNamespaceContext (); /** * @return true if the JAXB output should be formatted. Default * is false. * @since 8.5.3 in this interface */ boolean isFormattedOutput (); /** * @return The special JAXB Charset to be used for writing. null * by default. * @since 8.5.3 in this interface */ @Nullable Charset getCharset (); default boolean hasCharset () { return getCharset () != null; } /** * @return The JAXB indentation string to be used for writing. * null by default. Only used when formatted output is * used. * @since 8.5.3 in this interface */ @Nullable String getIndentString (); default boolean hasIndentString () { return StringHelper.hasText (getIndentString ()); } /** * @return true if an eventually configured XML Schema should be * used, false to explicitly disable the usage of XML * Schema. * @since 11.0.3 */ boolean isUseSchema (); /** * @return The schema location to be used for writing. null by * default. * @since 8.6.0 */ @Nullable String getSchemaLocation (); default boolean hasSchemaLocation () { return StringHelper.hasText (getSchemaLocation ()); } /** * @return The no namespace schema location to be used for writing. * null by default. * @since 9.0.0 */ @Nullable String getNoNamespaceSchemaLocation (); default boolean hasNoNamespaceSchemaLocation () { return StringHelper.hasText (getNoNamespaceSchemaLocation ()); } /** * @return The XML writer settings to be used based on this writer settings. * Never null. */ @Nonnull default IXMLWriterSettings getXMLWriterSettings () { final XMLWriterSettings ret = new XMLWriterSettings ().setNamespaceContext (getNamespaceContext ()) .setIndent (isFormattedOutput () ? EXMLSerializeIndent.INDENT_AND_ALIGN : EXMLSerializeIndent.NONE); if (hasIndentString ()) ret.setIndentationString (getIndentString ()); if (hasCharset ()) ret.setCharset (getCharset ()); return ret.setNewLineMode (ENewLineMode.DEFAULT) .setIncorrectCharacterHandling (EXMLIncorrectCharacterHandling.DO_NOT_WRITE_LOG_WARNING); } /** * Write the passed object to a {@link File}. * * @param aObject * The object to be written. May not be null. * @param aResultFile * The result file to be written to. May not be null. * @return {@link ESuccess} */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull final File aResultFile) { if (USE_JAXB_CHARSET_FIX) { final OutputStream aOS = FileHelper.getBufferedOutputStream (aResultFile); if (aOS == null) return ESuccess.FAILURE; return write (aObject, aOS); } return write (aObject, TransformResultFactory.create (aResultFile)); } /** * Write the passed object to a {@link Path}. * * @param aObject * The object to be written. May not be null. * @param aResultPath * The result path to be written to. May not be null. * @return {@link ESuccess} */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull final Path aResultPath) { if (USE_JAXB_CHARSET_FIX) return write (aObject, aResultPath.toFile ()); return write (aObject, TransformResultFactory.create (aResultPath)); } /** * Write the passed object to an {@link OutputStream}. * * @param aObject * The object to be written. May not be null. * @param aOS * The output stream to write to. Will always be closed. May not be * null. * @return {@link ESuccess} */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull @WillClose final OutputStream aOS) { try { if (USE_JAXB_CHARSET_FIX) { return write (aObject, SafeXMLStreamWriter.create (aOS, getXMLWriterSettings ())); } return write (aObject, TransformResultFactory.create (aOS)); } finally { // Needs to be manually closed StreamHelper.close (aOS); } } /** * Write the passed object to a {@link Writer}. * * @param aObject * The object to be written. May not be null. * @param aWriter * The writer to write to. Will always be closed. May not be * null. * @return {@link ESuccess} */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull @WillClose final Writer aWriter) { try { if (USE_JAXB_CHARSET_FIX) { return write (aObject, SafeXMLStreamWriter.create (aWriter, getXMLWriterSettings ())); } return write (aObject, TransformResultFactory.create (aWriter)); } finally { // Needs to be manually closed StreamHelper.close (aWriter); } } /** * Write the passed object to a {@link ByteBuffer}. * * @param aObject * The object to be written. May not be null. * @param aBuffer * The byte buffer to write to. If the buffer is too small, it is * automatically extended. May not be null. * @return {@link ESuccess} * @throws BufferOverflowException * If the ByteBuffer is too small */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull final ByteBuffer aBuffer) { return write (aObject, new ByteBufferOutputStream (aBuffer, false)); } /** * Write the passed object to an {@link IWritableResource}. * * @param aObject * The object to be written. May not be null. * @param aResource * The result resource to be written to. May not be null. * @return {@link ESuccess} */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull final IWritableResource aResource) { if (USE_JAXB_CHARSET_FIX) { final OutputStream aOS = aResource.getOutputStream (EAppend.TRUNCATE); if (aOS == null) return ESuccess.FAILURE; return write (aObject, aOS); } return write (aObject, TransformResultFactory.create (aResource)); } /** * Convert the passed object to XML. * * @param aObject * The object to be converted. May not be null. * @param aMarshallerFunc * The marshalling function. May not be null. * @return {@link ESuccess} */ @Nonnull ESuccess write (@Nonnull JAXBTYPE aObject, @Nonnull IJAXBMarshaller aMarshallerFunc); /** * Convert the passed object to XML. This method is potentially dangerous, * when using StreamResult because it may create invalid XML. Only when using * the {@link SafeXMLStreamWriter} it is ensured that only valid XML is * created! * * @param aObject * The object to be converted. May not be null. * @param aResult * The result object holder. May not be null. Usually * SAXResult, DOMResult and StreamResult are supported. * @return {@link ESuccess} */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull final Result aResult) { if (USE_JAXB_CHARSET_FIX && aResult instanceof StreamResult) { LoggerFactory.getLogger (IJAXBWriter.class) .warn ("Potentially invalid XML is created by using StreamResult object: {}", aResult); } return write (aObject, (m, e) -> m.marshal (e, aResult)); } /** * Convert the passed object to XML. * * @param aObject * The object to be converted. May not be null. * @param aHandler * XML will be sent to this handler as SAX2 events. May not be * null. * @return {@link ESuccess} */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull final org.xml.sax.ContentHandler aHandler) { // No need for charset fix, because it is up to the ContentHandler, if it is // converting to a byte[] or not. return write (aObject, (m, e) -> m.marshal (e, aHandler)); } /** * Convert the passed object to XML. * * @param aObject * The object to be converted. May not be null. * @param aWriter * XML will be sent to this writer. May not be null. * @return {@link ESuccess} */ @Nonnull default ESuccess write (@Nonnull final JAXBTYPE aObject, @Nonnull @WillClose final javax.xml.stream.XMLStreamWriter aWriter) { // No need for charset fix, because it is up to the XMLStreamWriter, if it // is converting to a byte[] or not. final ESuccess ret = write (aObject, (m, e) -> m.marshal (e, aWriter)); // Needs to be manually flushed and closed try { aWriter.flush (); } catch (final XMLStreamException ex) { throw new IllegalStateException ("Failed to flush XMLStreamWriter", ex); } try { aWriter.close (); } catch (final XMLStreamException ex) { throw new IllegalStateException ("Failed to close XMLStreamWriter", ex); } return ret; } /** * Convert the passed object to a new DOM document (write). * * @param aObject * The object to be converted. May not be null. * @return null if converting the document failed. */ @Nullable default Document getAsDocument (@Nonnull final JAXBTYPE aObject) { // No need for charset fix, because the document is returned in an internal // representation with String content final Document aDoc = XMLFactory.newDocument (); return write (aObject, TransformResultFactory.create (aDoc)).isSuccess () ? aDoc : null; } /** * Convert the passed object to a new DOM document and return the document * element (write). * * @param aObject * The object to be converted. May not be null. * @return null if converting the document failed. * @since 11.0.2 */ @Nullable default Element getAsElement (@Nonnull final JAXBTYPE aObject) { final Document aDoc = getAsDocument (aObject); return aDoc == null ? null : aDoc.getDocumentElement (); } /** * Convert the passed object to a new micro document (write). * * @param aObject * The object to be converted. May not be null. * @return null if converting the document failed. */ @Nullable default IMicroDocument getAsMicroDocument (@Nonnull final JAXBTYPE aObject) { // No need for charset fix, because the document is returned in an internal // representation with String content final MicroSAXHandler aHandler = new MicroSAXHandler (false, null, true); return write (aObject, aHandler).isSuccess () ? aHandler.getDocument () : null; } /** * Convert the passed object to a new micro document and return only the root * element (write). * * @param aObject * The object to be converted. May not be null. * @return null if converting the document failed. */ @Nullable default IMicroElement getAsMicroElement (@Nonnull final JAXBTYPE aObject) { final IMicroDocument aDoc = getAsMicroDocument (aObject); if (aDoc == null) return null; final IMicroElement ret = aDoc.getDocumentElement (); // Important to detach from document - otherwise the element cannot be // re-added somewhere else ret.detachFromParent (); return ret; } /** * Utility method to directly convert the passed domain object to an XML * string (write). * * @param aObject * The domain object to be converted. May not be null. * @return null if the passed domain object could not be * converted because of validation errors. */ @Nullable default String getAsString (@Nonnull final JAXBTYPE aObject) { try (final NonBlockingStringWriter aSW = new NonBlockingStringWriter ()) { if (USE_JAXB_CHARSET_FIX) { return write (aObject, SafeXMLStreamWriter.create (aSW, getXMLWriterSettings ())).isSuccess () ? aSW.getAsString () : null; } return write (aObject, TransformResultFactory.create (aSW)).isSuccess () ? aSW.getAsString () : null; } } /** * Write the passed object to a {@link ByteBuffer} and return it (write). * * @param aObject * The object to be written. May not be null. * @return null if the passed domain object could not be * converted because of validation errors. */ @Nullable default ByteBuffer getAsByteBuffer (@Nonnull final JAXBTYPE aObject) { try (final ByteBufferOutputStream aBBOS = new ByteBufferOutputStream ()) { if (USE_JAXB_CHARSET_FIX) { return write (aObject, SafeXMLStreamWriter.create (aBBOS, getXMLWriterSettings ())).isFailure () ? null : aBBOS.getBuffer (); } return write (aObject, aBBOS).isFailure () ? null : aBBOS.getBuffer (); } } /** * Write the passed object to a byte array and return the created byte array * (write). * * @param aObject * The object to be written. May not be null. * @return null if the passed domain object could not be * converted because of validation errors. */ @Nullable default byte [] getAsBytes (@Nonnull final JAXBTYPE aObject) { try (final ByteBufferOutputStream aBBOS = new ByteBufferOutputStream ()) { if (USE_JAXB_CHARSET_FIX) { return write (aObject, SafeXMLStreamWriter.create (aBBOS, getXMLWriterSettings ())).isFailure () ? null : aBBOS.getAsByteArray (); } return write (aObject, aBBOS).isFailure () ? null : aBBOS.getAsByteArray (); } } /** * Write the passed object to a byte array and return the input stream on that * array. * * @param aObject * The object to be written. May not be null. * @return null if the passed domain object could not be * converted because of validation errors. * @since 9.1.8 */ @Nullable default NonBlockingByteArrayInputStream getAsInputStream (@Nonnull final JAXBTYPE aObject) { final byte [] aBytes = getAsBytes (aObject); return aBytes == null ? null : new NonBlockingByteArrayInputStream (aBytes); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy