001 /*
002 * Java Genetic Algorithm Library (jenetics-3.5.0).
003 * Copyright (c) 2007-2016 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
019 */
020 package org.jenetics.internal.util;
021
022 import static java.util.Objects.requireNonNull;
023
024 import java.lang.reflect.Field;
025 import java.util.HashSet;
026 import java.util.Set;
027 import java.util.stream.Stream;
028
029 import javax.xml.bind.DataBindingException;
030 import javax.xml.bind.JAXBContext;
031 import javax.xml.bind.JAXBException;
032
033 import org.jenetics.util.ISeq;
034
035 /**
036 * Caches the JAXB classes and lets you add additional one. You can either add
037 * a <em>JAXB</em> class directly, or the package where you have put in a
038 * {@code JAXBRegistry} class:
039 *
040 * <pre>{@code
041 * // Class may be package private
042 * final class JAXBRegistry {
043 * private JAXBRegistry() {require.noInstance();}
044 *
045 * // Must contain static final field 'CLASSES'.
046 * public static final ISeq<Class<?>> CLASSES = ISeq.of(
047 * BitGene.Model.class,
048 * EnumGene.Model.class,
049 * CharacterGene.Model.class,
050 * IntegerGene.Model.class,
051 * LongGene.Model.class,
052 * DoubleGene.Model.class
053 * )
054 * }
055 * }</pre>
056 *
057 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
058 * @version 3.5
059 * @since 3.5
060 */
061 public final class JAXBContextCache {
062 private JAXBContextCache() {require.noInstance();}
063
064 private static final Set<String> PACKAGES = new HashSet<>();
065
066 private static final Set<Class<?>> CLASSES = new HashSet<>();
067 static {
068 addPackage("org.jenetics");
069 addPackage("org.jenetics.engine");
070 addPackage("org.jenetics.internal.util");
071 }
072
073 private static JAXBContext _context;
074
075 /**
076 * Return a {@code JAXBContext} with the currently registered classes. This
077 * method is <em>synchronized</em>.
078 *
079 * @return the {@code JAXBContext} with the currently registered classes
080 */
081 public static synchronized JAXBContext context() {
082 if (_context == null) {
083 try {
084 _context = JAXBContext
085 .newInstance(CLASSES.toArray(new Class<?>[CLASSES.size()]));
086 } catch (JAXBException e) {
087 throw new DataBindingException(
088 "Something went wrong while creating JAXBContext.", e
089 );
090 }
091 }
092
093 return _context;
094 }
095
096 /**
097 * Return a {@code JAXBContext} with the currently registered classes plus
098 * the registered classes in the given packages. This method is
099 * <em>synchronized</em>.
100 *
101 * @param packages the additional packages of the return {@code JAXBContext}
102 * @return the {@code JAXBContext}
103 */
104 public static JAXBContext context(final String... packages) {
105 Stream.of(packages).forEach(JAXBContextCache::addPackage);
106 return context();
107 }
108
109 /**
110 * Return a {@code JAXBContext} with the currently registered classes plus
111 * the given classes. This method is <em>synchronized</em>.
112 *
113 * @param classes the additional classes of the return {@code JAXBContext}
114 * @return the {@code JAXBContext}
115 */
116 public static JAXBContext context(final Class<?>... classes) {
117 Stream.of(classes).forEach(JAXBContextCache::add);
118 return context();
119 }
120
121 /**
122 * Register the given source package.
123 *
124 * @param pkg the package to register
125 */
126 public static synchronized void addPackage(final String pkg) {
127 if (!PACKAGES.contains(pkg)) {
128 PACKAGES.add(pkg);
129
130 final ISeq<Class<?>> classes = jaxbClasses(pkg).stream()
131 .filter(cls -> !CLASSES.contains(cls))
132 .collect(ISeq.toISeq());
133
134 if (!classes.isEmpty()) {
135 _context = null;
136 CLASSES.addAll(classes.asList());
137 }
138 }
139 }
140
141 /**
142 * Register the given class.
143 *
144 * @param cls the class to register
145 */
146 public static synchronized void add(final Class<?> cls) {
147 requireNonNull(cls);
148
149 if (!CLASSES.contains(cls)) {
150 _context = null;
151 CLASSES.add(cls);
152 }
153 }
154
155 /**
156 * De-register the given class.
157 *
158 * @param cls the class to de-register
159 */
160 public static synchronized void remove(final Class<?> cls) {
161 requireNonNull(cls);
162
163 if (CLASSES.contains(cls)) {
164 _context = null;
165 CLASSES.remove(cls);
166 }
167 }
168
169 /**
170 * Check is the given class is already registered.
171 *
172 * @param cls the class to check
173 * @return {@code true} if the given class is already registered,
174 * {@code false} otherwise.
175 */
176 public static synchronized boolean contains(final Class<?> cls) {
177 requireNonNull(cls);
178 return CLASSES.contains(cls);
179 }
180
181 @SuppressWarnings("unchecked")
182 private static ISeq<Class<?>> jaxbClasses(final String pkg) {
183 requireNonNull(pkg);
184
185 try {
186 final Field field = Class
187 .forName(pkg + ".JAXBRegistry")
188 .getField("CLASSES");
189 field.setAccessible(true);
190
191 return (ISeq<Class<?>>)field.get(null);
192 } catch (ReflectiveOperationException e) {
193 return ISeq.empty();
194 }
195 }
196 }
|