/* * 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 3.5 * @since 3.5 */ 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::add); 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 add(final Class<?> cls) { requireNonNull(cls); if (!CLASSES.contains(cls)) { _context = null; CLASSES.add(cls); } } /** * De-register the given class. * * @param cls the class to de-register */ public static synchronized void remove(final Class<?> cls) { requireNonNull(cls); if (CLASSES.contains(cls)) { _context = null; CLASSES.remove(cls); } } /** * Check is the given class is already registered. * * @param cls the class to check * @return {@code true} if the given class is already registered, * {@code false} otherwise. */ public static synchronized boolean contains(final Class<?> cls) { requireNonNull(cls); return CLASSES.contains(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(); } } }