package de.retest.recheck.persistence.xml;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.helpers.DefaultValidationEventHandler;

import org.eclipse.persistence.internal.oxm.record.namespaces.MapNamespacePrefixMapper;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.logging.AbstractSessionLog;

import com.google.common.collect.ImmutableMap;

import de.retest.recheck.persistence.xml.util.SessionLogDelegate;
import de.retest.recheck.persistence.xml.util.StdXmlClassesProvider;
import de.retest.recheck.persistence.xml.util.XmlUtil;

public class XmlTransformer {

	private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger( XmlTransformer.class );

	private static final ImmutableMap<String, String> NAMESPACE_MAPPINGS = ImmutableMap
			.of( "http://www.w3.org/2001/XMLSchema", "xsd", "http://www.w3.org/2001/XMLSchema-instance", "xsi" );

	private final XmlTransformerConfiguration config;
	private final Class<?>[] additionalClazzes;

	public XmlTransformer( final XmlTransformerConfiguration config, final Class<?>... additionalClazzes ) {
		this.config = config;
		this.additionalClazzes = additionalClazzes;
	}

	public XmlTransformer( final Class<?>... additionalClazzes ) {
		this( XmlTransformerConfiguration.builder().build(), additionalClazzes );
	}

	public XmlTransformer( final Set<Class<?>> xmlDataClasses ) {
		this( xmlDataClasses.toArray( new Class<?>[xmlDataClasses.size()] ) );
	}

	public <T> T fromXML( final InputStream in ) {
		return fromXML( in, null );
	}

	public <T> T fromXML( final InputStream in, final Unmarshaller.Listener listener ) {
		try {
			final JAXBContext jc = createJAXBContext( additionalClazzes );
			final Unmarshaller unmarshaller = jc.createUnmarshaller();
			unmarshaller.setEventHandler( new DefaultValidationEventHandler() );
			unmarshaller.setListener( listener );

			@SuppressWarnings( "unchecked" )
			final T result = (T) unmarshaller.unmarshal( in );
			return result;

		} catch ( final JAXBException e ) {
			throw new RuntimeException( e );
		}
	}

	// TODO Try if this is possible with a marshaller delegate instead?
	private static final Set<Marshaller> lightweightMarshallerSet = new HashSet<>();

	public static boolean isLightweightMarshaller( final Marshaller m ) {
		return lightweightMarshallerSet.contains( m );
	}

	public void toXML( final Object obj, final OutputStream out, final Marshaller.Listener listener ) {
		Marshaller marshaller = null;
		try {
			final JAXBContext jc = createJAXBContext( additionalClazzes );
			marshaller = jc.createMarshaller();
			marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
			marshaller.setProperty( MarshallerProperties.NAMESPACE_PREFIX_MAPPER,
					new MapNamespacePrefixMapper( NAMESPACE_MAPPINGS ) );
			marshaller.setProperty( MarshallerProperties.INDENT_STRING, "\t" );
			marshaller.setEventHandler( new DefaultValidationEventHandler() );
			marshaller.setListener( listener );
			final SessionLogDelegate sessionLog = new SessionLogDelegate( AbstractSessionLog.getLog() );
			AbstractSessionLog.setLog( sessionLog );

			if ( config.isOnlyFragment() ) {
				logger.info( "Create only fragment for '{}'.", obj );
				marshaller.setProperty( Marshaller.JAXB_FRAGMENT, true );
			}

			if ( config.isLightweightXml() ) {
				logger.info( "Use lightweight xml for '{}'.", obj );
				lightweightMarshallerSet.add( marshaller );
				XmlUtil.addLightWeightAdapter( marshaller );
			}

			marshaller.marshal( obj, out );

			if ( sessionLog.containsMessages() ) {
				throw new RuntimeException( "Error persisting xml: " + sessionLog.getLog() );
			}
		} catch ( final JAXBException e ) {
			throw new RuntimeException( e );
		} finally {
			if ( config.isLightweightXml() && marshaller != null ) {
				lightweightMarshallerSet.remove( marshaller );
			}
		}
	}

	public void toXML( final Object obj, final OutputStream out ) {
		toXML( obj, out, null );
	}

	public String toXML( final Object obj ) {
		final ByteArrayOutputStream baos = new ByteArrayOutputStream();
		final PrintStream ps = new PrintStream( baos );

		toXML( obj, ps );

		return baos.toString();
	}

	/**
	 * Use a well-defined JAXB class context, so we don't interfere with some SUT-configured context. Although a context
	 * <a href=https://stackoverflow.com/a/7400735>"should only be created once and reused"</a>, this fails the
	 * corresponding test for this class.
	 *
	 * @param additionalClazzes
	 *            Classes to be bound.
	 * @return A sparkling fresh {@code JAXBContext}.
	 */
	private JAXBContext createJAXBContext( final Class<?>... additionalClazzes ) {
		try {
			final Class<?>[] contextClasses = StdXmlClassesProvider.getXmlDataClasses( additionalClazzes );
			return JAXBContextFactory.createContext( contextClasses, Collections.emptyMap() );
		} catch ( final JAXBException e ) {
			throw new RuntimeException( e );
		}
	}
}

@@ -947,122 +947,62 @@
 il;%0A
-%0Apublic class XmlTransformer %7B%0A%0A%09private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(
+import lombok.extern.slf4j.Slf4j;%0A%0A@Slf4j%0Apublic class
  Xml
@@ -1016,17 +1016,10 @@
 rmer
-.class );
+ %7B
 %0A%0A%09p
@@ -1207,16 +1207,292 @@
 si%22 );%0A%0A
+%09// TODO Try if this is possible with a marshaller delegate instead?%0A%09private static final Set%3CMarshaller%3E lightweightMarshallerSet = new HashSet%3C%3E();%0A%0A%09public static boolean isLightweightMarshaller( final Marshaller m ) %7B%0A%09%09return lightweightMarshallerSet.contains( m );%0A%09%7D%0A%0A
 %09private
@@ -2657,284 +2657,8 @@
 %09%7D%0A%0A
-%09// TODO Try if this is possible with a marshaller delegate instead?%0A%09private static final Set%3CMarshaller%3E lightweightMarshallerSet = new HashSet%3C%3E();%0A%0A%09public static boolean isLightweightMarshaller( final Marshaller m ) %7B%0A%09%09return lightweightMarshallerSet.contains( m );%0A%09%7D%0A%0A
 %09pub
@@ -3456,35 +3456,32 @@
 nt() ) %7B%0A%09%09%09%09log
-ger
 .info( %22Create o
@@ -3629,11 +3629,8 @@
 %09log
-ger
 .inf
@@ -3649,19 +3649,19 @@
 tweight 
-xml
+XML
  for '%7B%7D
@@ -3903,19 +3903,19 @@
 sisting 
-xml
+XML
 : %22 + se

Refactor transformer