com.riversoforion.acheron.jaxb.JAXBContextBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.riversoforion.acheron.jaxb.JAXBContextBuilder.java

Source

/*******************************************************************************
 * Copyright 2012 Eric McIntyre
 * 
 * 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.
 ******************************************************************************/
package com.riversoforion.acheron.jaxb;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

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

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.riversoforion.acheron.patterns.Builder;
import com.riversoforion.acheron.patterns.CheckedExceptions;

/**
 * Uses a Builder Pattern to construct a JAXBContext. The builder is populated with classes, properties, and other
 * attributes that will be used to initialize the context when the {@link #build() build} method is called.
 * 
 * @author Eric McIntyre (<a href="mailto:mac@riversoforion.com">Mac</a>)
 */
public class JAXBContextBuilder implements Builder<JAXBContext> {

    private Class<?>[] boundClasses;
    private String contextPath;
    private ClassLoader classLoader;
    private Map<String, Object> props;
    private JAXBContext context;

    public JAXBContextBuilder() {

    }

    /**
     * Binds the classes with the specified names to the JAXBContext. Note that this method will have no effect if
     * {@link #forContextPath(String) contextPath} is not empty.
     * 
     * @param boundClasses
     *            A list of class names to bind. The class names will be resolved to class objects using the Thread's
     *            context ClassLoader, or the one {@linkplain #withClassLoader(ClassLoader) configured for this builder}
     *            .
     * @return This object
     */
    public JAXBContextBuilder bindClasses(List<String> boundClasses) {

        this.boundClasses = resolveBoundClasses(boundClasses);
        return this;
    }

    /**
     * Binds the specified classes to the JAXBContext. Note that this method will have not effect if
     * {@link #forContextPath(String) contextPath} is not empty.
     * 
     * @param boundClasses
     *            The classes to bind.
     * @return This object
     */
    public JAXBContextBuilder bindClasses(Class<?>... boundClasses) {

        this.boundClasses = boundClasses.clone();
        return this;
    }

    /**
     * Uses the specified context path to resolve JAXB-annotated classes.
     * 
     * @param contextPath
     *            The package name that contains JAXB classes
     * @return This object
     */
    public JAXBContextBuilder forContextPath(String contextPath) {

        this.contextPath = contextPath;
        return this;
    }

    /**
     * Uses the specified class loader to find and load classes that contain JAXB annotations. If this value is not set
     * the {@linkplain Thread#getContextClassLoader() thread's context ClassLoader} will be used.
     * 
     * @param classLoader
     *            The ClassLoader for resolving class references
     * @return This object
     */
    public JAXBContextBuilder withClassLoader(ClassLoader classLoader) {

        this.classLoader = classLoader;
        return this;
    }

    /**
     * Sets the properties to use for configuring the JAXBContext.
     * 
     * @param props
     *            The properties to set when building the JAXBContext
     * @return This object
     */
    public JAXBContextBuilder withProperties(Map<String, Object> props) {

        if (props != null) {
            this.props.putAll(props);
        }
        return this;
    }

    /**
     * Sets one property to use for configuring the JAXBContext.
     * 
     * @param name
     *            The name of the property to set. The recognized names are dependent on the JAXB implementation.
     * @param value
     *            The value of the property. The acceptable values are dependent on the JAXB implementation and the
     *            property being set.
     * @return This object
     */
    public JAXBContextBuilder withProperty(String name, Object value) {

        this.props.put(name, value);
        return this;
    }

    /**
     * Builds the JAXBContext from the current values of this builder. If there is a {@link #forContextPath(String)
     * contextPath} set, the JAXBContext will be configured with that; otherwise, the set of specified classes will be
     * used.
     */
    @Override
    public JAXBContext build() {

        if (!StringUtils.isEmpty(this.contextPath)) {
            buildWithContextPath();
        } else if (!ArrayUtils.isEmpty(this.boundClasses)) {
            buildWithBoundClasses();
        } else {
            throw new IllegalStateException("JAXB environment not configured properly");
        }
        return current();
    }

    @Override
    public JAXBContext current() {

        return this.context;
    }

    /**
     * Not implemented
     */
    @Override
    public JAXBContextBuilder reset() {

        throw new UnsupportedOperationException("reset");
    }

    private Class<?>[] resolveBoundClasses(List<String> classNames) {

        List<Class<?>> classObjects = new ArrayList<Class<?>>();
        for (String className : classNames) {
            ClassLoader cl = getClassLoader();
            try {
                Class<?> c = Class.forName(className, true, cl);
                classObjects.add(c);
            } catch (ClassNotFoundException e) {
                CheckedExceptions.throwAsUnchecked(e);
            }
        }
        return classObjects.toArray(new Class<?>[classObjects.size()]);
    }

    private void buildWithContextPath() {

        ClassLoader cl = getClassLoader();
        try {
            this.context = JAXBContext.newInstance(this.contextPath, cl, this.props);
        } catch (JAXBException e) {
            CheckedExceptions.throwAsUnchecked(e);
        }

    }

    private void buildWithBoundClasses() {

        try {
            this.context = JAXBContext.newInstance(this.boundClasses, this.props);
        } catch (JAXBException e) {
            CheckedExceptions.throwAsUnchecked(e);
        }

    }

    private ClassLoader getClassLoader() {

        return this.classLoader != null ? this.classLoader : Thread.currentThread().getContextClassLoader();
    }
}