/*
 * Java Genetic Algorithm Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * 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.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
 */
package org.jenetics.internal.util;

import static java.util.Objects.requireNonNull;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;

import javax.xml.bind.DataBindingException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

import org.jenetics.util.ISeq;

/**
 * Caches the JAXB classes and lets you add additional one. You can either add
 * a <em>JAXB</em> class directly, or the package where you have put in a
 * {@code JAXBRegistry} class:
 *
 * <pre>{@code
 * // Class may be package private
 * final class JAXBRegistry {
 *     private JAXBRegistry() {require.noInstance();}
 *
 *     // Must contain static final field 'CLASSES'.
 *     public static final ISeq<Class<?>> CLASSES = ISeq.of(
 *         BitGene.Model.class,
 *         EnumGene.Model.class,
 *         CharacterGene.Model.class,
 *         IntegerGene.Model.class,
 *         LongGene.Model.class,
 *         DoubleGene.Model.class
 *     )
 * }
 * }</pre>
 *
 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
 * @version !__version__!
 * @since !__version__!
 */
public final class JAXBContextCache {
	private JAXBContextCache() {require.noInstance();}

	private static final Set<String> PACKAGES = new HashSet<>();

	private static final Set<Class<?>> CLASSES = new HashSet<>();
	static {
		addPackage("org.jenetics");
		addPackage("org.jenetics.engine");
		addPackage("org.jenetics.internal.util");
	}

	private static JAXBContext _context;

	/**
	 * Return a {@code JAXBContext} with the currently registered classes. This
	 * method is <em>synchronized</em>.
	 *
	 * @return the {@code JAXBContext} with the currently registered classes
	 */
	public static synchronized JAXBContext context() {
		if (_context == null) {
			try {
				_context = JAXBContext
					.newInstance(CLASSES.toArray(new Class<?>[CLASSES.size()]));
			} catch (JAXBException e) {
				throw new DataBindingException(
					"Something went wrong while creating JAXBContext.", e
				);
			}
		}

		return _context;
	}

	/**
	 * Return a {@code JAXBContext} with the currently registered classes plus
	 * the registered classes in the given packages. This method is
	 * <em>synchronized</em>.
	 *
	 * @param packages the additional packages of the return {@code JAXBContext}
	 * @return the {@code JAXBContext}
	 */
	public static JAXBContext context(final String... packages) {
		Stream.of(packages).forEach(JAXBContextCache::addPackage);
		return context();
	}

	/**
	 * Return a {@code JAXBContext} with the currently registered classes plus
	 * the given classes. This method is <em>synchronized</em>.
	 *
	 * @param classes the additional classes of the return {@code JAXBContext}
	 * @return the {@code JAXBContext}
	 */
	public static JAXBContext context(final Class<?>... classes) {
		Stream.of(classes).forEach(JAXBContextCache::addClass);
		return context();
	}

	/**
	 * Register the given source package.
	 *
	 * @param pkg the package to register
	 */
	public static synchronized void addPackage(final String pkg) {
		if (!PACKAGES.contains(pkg)) {
			PACKAGES.add(pkg);

			final ISeq<Class<?>> classes = jaxbClasses(pkg).stream()
				.filter(cls -> !CLASSES.contains(cls))
				.collect(ISeq.toISeq());

			if (!classes.isEmpty()) {
				_context = null;
				CLASSES.addAll(classes.asList());
			}
		}
	}

	/**
	 * Register the given class.
	 *
	 * @param cls the class to register
	 */
	public static synchronized void addClass(final Class<?> cls) {
		requireNonNull(cls);

		if (!CLASSES.contains(cls)) {
			_context = null;
			CLASSES.add(cls);
		}
	}

	@SuppressWarnings("unchecked")
	private static ISeq<Class<?>> jaxbClasses(final String pkg) {
		requireNonNull(pkg);

		try {
			final Field field = Class
				.forName(pkg + ".JAXBRegistry")
				.getField("CLASSES");
			field.setAccessible(true);

			return (ISeq<Class<?>>)field.get(null);
		} catch (ReflectiveOperationException e) {
			return ISeq.empty();
		}
	}
}

@@ -4372,16 +4372,607 @@
 %09%09%7D%0A%09%7D%0A%0A
+%09/**%0A%09 * De-register the given class.%0A%09 *%0A%09 * @param cls the class to de-register%0A%09 */%0A%09public static synchronized void removeClass(final Class%3C?%3E cls) %7B%0A%09%09requireNonNull(cls);%0A%0A%09%09if (CLASSES.contains(cls)) %7B%0A%09%09%09_context = null;%0A%09%09%09CLASSES.remove(cls);%0A%09%09%7D%0A%09%7D%0A%0A%09/**%0A%09 * Check is the given class is already registered.%0A%09 *%0A%09 * @param cls the class to check%0A%09 * @return %7B@code true%7D if the given class is already registered,%0A%09 *         %7B@code false%7D otherwise.%0A%09 */%0A%09public static synchronized boolean contains(final Class%3C?%3E cls) %7B%0A%09%09requireNonNull(cls);%0A%09%09return CLASSES.contains(cls);%0A%09%7D%0A%0A
 %09@Suppre

Add additional register methods.