package com.peterphi.std.util.jaxb;

import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * A factory for JAXBSerialiser instances that can optionally be forced to use EclipseLink MOXy (or use the default JAXBContext
 * implementation acquisition rules).<br />
 * Caches JAXBSerialiser instances created using {@link java.lang.ref.SoftReference}s.
 */
public class JAXBSerialiserFactory
{
	private final ConcurrentHashMap<String, SoftReference<JAXBSerialiser>> cache = new ConcurrentHashMap<>();
	private final boolean useMoxy;


	public JAXBSerialiserFactory(boolean useMoxy)
	{
		this.useMoxy = useMoxy;
	}


	protected JAXBSerialiser getInstance(final String key, final Supplier<JAXBSerialiser> provider)
	{
		final SoftReference<JAXBSerialiser> ref = cache.get(key);
		JAXBSerialiser instance = (ref != null) ? ref.get() : null;

		if (instance == null)
		{
			instance = provider.get();
			cache.put(key, new SoftReference<>(instance));

			// We just took the penalty to create a JAXBContext, do some maintenance on the map while we're at it
			prune();
		}

		return instance;
	}


	/**
	 * Finds stale entries in the map
	 */
	private void prune()
	{
		Iterator<Map.Entry<String, SoftReference<JAXBSerialiser>>> it = cache.entrySet().iterator();

		while (it.hasNext())
		{
			final Map.Entry<String, SoftReference<JAXBSerialiser>> entry = it.next();

			if (entry.getValue() == null || entry.getValue().get() == null)
				it.remove();
		}
	}


	public JAXBSerialiser getInstance(final Class<?> clazz)
	{
		return getInstance(clazz.toString(), () -> construct(clazz));
	}


	public JAXBSerialiser getInstance(final String contextPath)
	{
		return getInstance(contextPath, () -> construct(contextPath));
	}


	JAXBSerialiser construct(String contextPath)
	{
		if (useMoxy)
			return JAXBSerialiser.getMoxy(contextPath);
		else
			return JAXBSerialiser.getInstance(contextPath);
	}


	JAXBSerialiser construct(Class<?> clazz)
	{
		if (useMoxy)
			return JAXBSerialiser.getMoxy(clazz);
		else
			return JAXBSerialiser.getInstance(clazz);
	}
}

@@ -30,16 +30,48 @@
 .jaxb;%0A%0A
+import java.lang.ref.Reference;%0A
 import j
@@ -492,17 +492,48 @@
 erence%7Ds
-.
+ (see %7B@link #reference(Object)%7D
 %0A */%0Apub
@@ -599,36 +599,32 @@
 HashMap%3CString, 
-Soft
 Reference%3CJAXBSe
@@ -783,16 +783,122 @@
 xy;%0A%09%7D%0A%0A
+%09private static %3CT%3E Reference%3CT%3E reference(final T instance) %7B%0A%09%09return new SoftReference%3C%3E(instance);%0A%09%7D%0A
 %0A%09protec
@@ -998,20 +998,16 @@
 %09%09final 
-Soft
 Referenc
@@ -1185,33 +1185,25 @@
 ut(key, 
-new SoftR
+r
 eference
 %3C%3E(insta
@@ -1186,34 +1186,32 @@
 t(key, reference
-%3C%3E
 (instance));%0A%0A%09%09
@@ -1353,17 +1353,16 @@
 ce;%0A%09%7D%0A%0A
-%0A
 %09/**%0A%09 *
@@ -1444,36 +1444,32 @@
 p.Entry%3CString, 
-Soft
 Reference%3CJAXBSe
@@ -1569,20 +1569,16 @@
 String, 
-Soft
 Referenc

Modify JAXBSerialiserFactory to abstract which type of reference is used by the cache