com.legstar.coxb.gen.CoxbBindingGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.legstar.coxb.gen.CoxbBindingGenerator.java

Source

/*******************************************************************************
 * Copyright (c) 2015 LegSem.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     LegSem - initial API and implementation
 ******************************************************************************/
package com.legstar.coxb.gen;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

import com.legstar.codegen.CodeGenUtil;
import com.legstar.coxb.CobolBindingException;
import com.legstar.coxb.host.HostException;
import com.legstar.coxb.impl.reflect.CComplexReflectBinding;
import com.legstar.coxb.util.BindingUtil;

/**
 * This class implements an ant task to generate COXB binding data from JAXB
 * cobol annotated instances. The generated binding code is faster than
 * alternative reflection methods also available in <code>legstar-coxbrt</code>.
 * Another advantage of binding classes is that they can bind to a different
 * object than the original JAXB object which served to generate it.
 * 
 * @author Fady Moussallam
 * 
 */
public class CoxbBindingGenerator extends Task {

    /** Container for all parameters to move around. */
    private CoxbGenModel _coxbGenModel;

    /** Folder where generated files are created. */
    private File _outputFolder;

    /**
     * List of jaxb root class names for which we need to generate binding
     * classes. Beware: this is not reflected in the model.
     */
    private List<JaxbRootClass> _jaxbRootClasses;

    /** Logger. */
    private final Log _log = LogFactory.getLog(getClass());

    /**
     * No args constructor.
     */
    public CoxbBindingGenerator() {
        _coxbGenModel = new CoxbGenModel();
    }

    /**
     * Constructor from a set of options.
     * 
     * @param context options set
     */
    public CoxbBindingGenerator(final CoxbGenModel context) {
        _coxbGenModel = context;
    }

    /**
     * The ant method. Generates COXB binding code.
     */
    public void execute() {
        checkInput();

        ClassLoader previousCl = Thread.currentThread().getContextClassLoader();

        try {
            /*
             * If we are provided with a folder containing JAXB classes, we
             * setup a context class loader that will be able to load them.
             * Otherwise, it is assumed JAXB classes are available from the
             * current class loader.
             */
            if (getJaxbBinDir() != null) {
                URL[] urlBinFiles = new URL[] { getJaxbBinDir().toURI().toURL() };
                URLClassLoader coxbCl = new URLClassLoader(urlBinFiles,
                        CoxbBindingGenerator.class.getClassLoader());
                Thread.currentThread().setContextClassLoader(coxbCl);
            }
            Object jaxbObjectFactory = BindingUtil.newJaxbObjectFactory(getJaxbPackageName());

            for (String jaxbRootClassName : getJaxbRootClassNames()) {

                /* Create an instance of the JAXB root object */
                Object jaxbRootObject = getRootObject(jaxbObjectFactory, jaxbRootClassName);

                /* Create a visitor */
                CoxbGenReflectVisitor visitor = new CoxbGenReflectVisitor(_coxbGenModel, _outputFolder);
                /* Bind the root object to a COXB type */
                CComplexReflectBinding ce = new CComplexReflectBinding(jaxbObjectFactory, jaxbRootObject);
                /* Visit COXB type and all subtypes recursively */
                ce.accept(visitor);

                /* For root objects, generate transformer classes */
                visitor.getWriter().writeHostToJavaTransformer(ce);
                visitor.getWriter().writeJavaToHostTransformer(ce);
                visitor.getWriter().writeTransformers(ce);
                if (isXmlTransformers()) {
                    visitor.getWriter().writeHostToXmlTransformer(ce);
                    visitor.getWriter().writeXmlToHostTransformer(ce);
                    visitor.getWriter().writeXmlTransformers(ce);
                }
                if (isJsonTransformers()) {
                    visitor.getWriter().writeHostToJsonTransformer(ce);
                    visitor.getWriter().writeJsonToHostTransformer(ce);
                    visitor.getWriter().writeJsonTransformers(ce);
                }
            }

        } catch (HostException e) {
            _log.error(CoxbGenWriter.BINDING_GENERATOR_NAME + " failure ", e);
            throw (new BuildException("HostException " + e.getMessage()));
        } catch (CoxbGenException e) {
            _log.error(CoxbGenWriter.BINDING_GENERATOR_NAME + " failure ", e);
            throw new BuildException(e);
        } catch (MalformedURLException e) {
            _log.error(CoxbGenWriter.BINDING_GENERATOR_NAME + " failure ", e);
            throw new BuildException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(previousCl);
        }

    }

    /**
     * Checks that properties set are valid.
     */
    protected void checkInput() {

        if (_log.isDebugEnabled()) {
            _log.debug("checkInput started");
            _log.debug("coxbGenModel: " + _coxbGenModel.toString());
        }

        /*
         * If user did not provide a JAXB package name, we need to get it from
         * the XML schema annotations.
         */
        if (getJaxbPackageName() == null || getJaxbPackageName().length() == 0) {
            throw (new BuildException(
                    "You must specify either a JAXB package name or" + " an XML schema file name"));
        }

        if (getTargetDir() == null || !getTargetDir().exists()) {
            throw (new BuildException("You must specify a target directory"));
        }

        /* There must be at least one jaxb root class name to process */
        if (getJaxbRootClassNames() == null || getJaxbRootClassNames().size() == 0) {
            throw (new BuildException("You must specify at least one JAXB root class name"));
        }

        /* Check that we have a valid output folder */
        try {
            _outputFolder = createOutputFolder(_coxbGenModel);
        } catch (CoxbGenException e) {
            throw (new BuildException(e));
        }

    }

    /**
     * Create the folder which will receive the generated classes.
     * <p/>
     * Folder includes the COXB package path if any. When the COXB package path
     * is provided, the output is cleaned from any previous generation
     * artifacts.
     * 
     * @param coxbGenModel the generation model
     * 
     * @return the generation folder
     * @throws CoxbGenException if generation folder cannot be determined
     */
    public static File createOutputFolder(final CoxbGenModel coxbGenModel) throws CoxbGenException {
        try {
            File outputFolder = coxbGenModel.getCoxbSrcDir();
            if (coxbGenModel.getCoxbPackageName() != null) {
                outputFolder = new File(coxbGenModel.getCoxbSrcDir(),
                        CodeGenUtil.relativeLocation(coxbGenModel.getCoxbPackageName()));
            }
            CodeGenUtil.checkDirectory(outputFolder, true);
            if (coxbGenModel.getCoxbPackageName() != null) {
                FileUtils.cleanDirectory(outputFolder);
            }
            return outputFolder;
        } catch (IOException e) {
            throw new CoxbGenException(e);
        }
    }

    /**
     * Loads the object factory class using the current class loader assuming
     * the JAXB classes are available in the current classpath and returns a new
     * instance of it.
     * 
     * @param packageName the package containing a JAXB Object Factory
     * @return a JAXB Object factory
     */
    protected Object getObjectFactory(final String packageName) {

        Object jaxbObjectFactory = null;

        if (packageName == null || packageName.length() == 0) {
            throw (new BuildException("You must provide a JAXB package name."));
        }

        try {
            jaxbObjectFactory = BindingUtil.newJaxbObjectFactory(packageName);
        } catch (SecurityException e) {
            throw new BuildException(e);
        } catch (IllegalArgumentException e) {
            throw new BuildException(e);
        } catch (CobolBindingException e) {
            throw new BuildException(e);
        }

        return jaxbObjectFactory;
    }

    /**
     * Returns a new instance of the requested JAXB object.
     * 
     * @param jaxbObjectFactory an instance of a JAXB Object Factory
     * @param rootObjectName the JAXB root object name (non qualified)
     * @return an instance of the JAXB root object
     */
    protected Object getRootObject(final Object jaxbObjectFactory, final String rootObjectName) {

        Object jaxbRootObject = null;

        if (jaxbObjectFactory == null) {
            throw (new BuildException("You must provide a JAXB object factory."));
        }

        if (rootObjectName == null || rootObjectName.length() == 0) {
            throw (new BuildException("You must provide a JAXB object name."));
        }

        try {
            String createName = "create" + rootObjectName;
            Method creator = jaxbObjectFactory.getClass().getMethod(createName);
            jaxbRootObject = creator.invoke(jaxbObjectFactory);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw (new BuildException(
                    "IllegalAccessException " + e.getMessage() + " rootObjectName=" + rootObjectName));
        } catch (SecurityException e) {
            e.printStackTrace();
            throw (new BuildException("SecurityException " + e.getMessage() + " rootObjectName=" + rootObjectName));
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw (new BuildException(
                    "NoSuchMethodException " + e.getMessage() + " rootObjectName=" + rootObjectName));
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            throw (new BuildException(
                    "IllegalArgumentException " + e.getMessage() + " rootObjectName=" + rootObjectName));
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw (new BuildException(
                    "InvocationTargetException " + e.getMessage() + " rootObjectName=" + rootObjectName));
        }

        return jaxbRootObject;
    }

    /**
     * This is an alternative to jaxbRootClassNames used when a single class is
     * to be bound.
     * 
     * @return Returns the JAXB root class name.
     */
    public String getJaxbRootClassName() {
        if (_coxbGenModel.getJaxbRootClassNames() != null && _coxbGenModel.getJaxbRootClassNames().size() > 0) {
            return _coxbGenModel.getJaxbRootClassNames().get(0);
        }
        return null;
    }

    /**
     * A JAXB root class name to set.
     * 
     * @param objectName The JAXB root class name to set.
     */
    public void setJaxbRootClassName(final String objectName) {
        addJaxbRootClass(objectName);
    }

    /**
     * The XML schema file.
     * 
     * @return the XML schema file
     */
    public File getXsdFile() {
        return _coxbGenModel.getXsdFile();
    }

    /**
     * The XML schema file.
     * 
     * @param xsdFile the XML schema file to set
     */
    public void setXsdFile(final File xsdFile) {
        _coxbGenModel.setXsdFile(xsdFile);
    }

    /**
     * The current target directory.
     * 
     * @return the current target directory
     */
    public File getTargetDir() {
        return _coxbGenModel.getCoxbSrcDir();
    }

    /**
     * The current target directory.
     * 
     * @param targetDir the target directory to set
     */
    public void setTargetDir(final File targetDir) {
        _coxbGenModel.setCoxbSrcDir(targetDir);
    }

    /**
     * @deprecated Use <code>getJaxbBinDir</code> instead
     * @return the location where JAXB classes live
     */
    public File getJaxbDir() {
        return _coxbGenModel.getJaxbBinDir();
    }

    /**
     * @deprecated Use <code>setJaxbBinDir</code> instead
     * @param jaxbDir the JAXB location to set
     */
    public void setJaxbDir(final File jaxbDir) {
        _coxbGenModel.setJaxbBinDir(jaxbDir);
    }

    /**
     * The location where JAXB classes live.
     * 
     * @return the location where JAXB classes live
     */
    public File getJaxbBinDir() {
        return _coxbGenModel.getJaxbBinDir();
    }

    /**
     * The location where JAXB classes live.
     * 
     * @param jaxbBinDir the JAXB location to set
     */
    public void setJaxbBinDir(final File jaxbBinDir) {
        _coxbGenModel.setJaxbBinDir(jaxbBinDir);
    }

    /**
     * The package name used for JAXB classes.
     * 
     * @return the package name used for JAXB classes
     */
    public String getJaxbPackageName() {
        try {
            return _coxbGenModel.getJaxbPackageName();
        } catch (CoxbGenException e) {
            throw (new BuildException(
                    "JAXB package name was not provided" + " and could not be recovered from XML schema file", e));
        }
    }

    /**
     * The package name used for JAXB classes.
     * 
     * @param jaxbPackageName the JAXB classes package name to set
     */
    public void setJaxbPackageName(final String jaxbPackageName) {
        _coxbGenModel.setJaxbPackageName(jaxbPackageName);
    }

    /**
     * The package name used for COXB classes.
     * 
     * @return the package name for generated binding classes
     */
    public String getCoxbPackageName() {
        try {
            return _coxbGenModel.getCoxbPackageName();
        } catch (CoxbGenException e) {
            throw (new BuildException(
                    "COXB package name was not provided" + " and could not be recovered from XML schema file", e));
        }
    }

    /**
     * The package name used for COXB classes.
     * 
     * @param coxbPackageName package name for generated binding classes to set
     */
    public void setCoxbPackageName(final String coxbPackageName) {
        _coxbGenModel.setCoxbPackageName(coxbPackageName);
    }

    /**
     * True if Host to XML transformers generation is turned on.
     * 
     * @return true if Host to XML transformers generation is turned on
     */
    public boolean isXmlTransformers() {
        return _coxbGenModel.isXmlTransformers();
    }

    /**
     * True if Host to XML transformers generation is turned on.
     * 
     * @param xmlTransformers true if Host to XML transformers generation is
     *            turned on
     */
    public void setXmlTransformers(final boolean xmlTransformers) {
        _coxbGenModel.setXmlTransformers(xmlTransformers);
    }

    /**
     * True if Host to JSON transformers generation is turned on.
     * 
     * @return true if Host to JSON transformers generation is turned on
     */
    public boolean isJsonTransformers() {
        return _coxbGenModel.isJsonTransformers();
    }

    /**
     * True if Host to JSON transformers generation is turned on.
     * 
     * @param jsonTransformers true if Host to JSON transformers generation is
     *            turned on
     */
    public void setJsonTransformers(final boolean jsonTransformers) {
        _coxbGenModel.setJsonTransformers(jsonTransformers);
    }

    /**
     * Optional runtime alternative to the Jaxb package name used at generation
     * time.
     * 
     * @return the optional runtime alternative to the Jaxb package name used at
     *         generation time
     */
    public String getAlternativePackageName() {
        return _coxbGenModel.getAlternativePackageName();
    }

    /**
     * Optional runtime alternative to the Jaxb package name used at generation
     * time.
     * 
     * @param alternativePackageName the optional runtime alternative to the
     *            Jaxb package name used at generation time
     */
    public void setAlternativePackageName(final String alternativePackageName) {
        _coxbGenModel.setAlternativePackageName(alternativePackageName);
    }

    /**
     * Alternate factory to used rather than the JAXB one.
     * 
     * @return the alternate factory to used rather than the JAXB one.
     */
    public String getAlternativeFactoryName() {
        return _coxbGenModel.getAlternativeFactoryName();
    }

    /**
     * Alternate factory to used rather than the JAXB one.
     * 
     * @param targetFactoryName the alternate factory to used rather than the
     *            JAXB one
     */
    public void setAlternativeFactoryName(final String targetFactoryName) {
        _coxbGenModel.setAlternativeFactoryName(targetFactoryName);
    }

    /**
     * Add a JAXB root class name holder object.
     * 
     * @param className the class name to add
     */
    public void addJaxbRootClass(final String className) {
        JaxbRootClass jaxbRootClass = createJaxbRootClass();
        jaxbRootClass.setName(className);
        _coxbGenModel.addJaxbRootClassName(className);
    }

    /**
     * Creates a JAXB root class name holder object.
     * 
     * @return a JAXB root class name holder object
     */
    public JaxbRootClass createJaxbRootClass() {
        if (_jaxbRootClasses == null) {
            _jaxbRootClasses = new ArrayList<JaxbRootClass>();
        }
        JaxbRootClass jaxbRootClass = new JaxbRootClass();
        _jaxbRootClasses.add(jaxbRootClass);
        return jaxbRootClass;
    }

    /**
     * List of JAXB root class names to process.
     * 
     * @return the list of jaxb root class names to process
     */
    public List<String> getJaxbRootClassNames() {
        List<String> classNames = new ArrayList<String>();
        if (_jaxbRootClasses != null) {
            for (JaxbRootClass className : _jaxbRootClasses) {
                classNames.add(className.getName());
            }
        }
        return classNames;
    }

    /**
     * Represent a simple inner element for the ant task. This element holds a
     * jaxb root class name. These elements are useful when there are more than
     * one jaxb class name to process.
     */
    public static class JaxbRootClass {

        /** Name of the inner class. */
        private String mName;

        /** Needs to be a public constructor. */
        public JaxbRootClass() {
        }

        /**
         * @param name the class name
         */
        public void setName(final String name) {
            mName = name;
        }

        /**
         * @return the current text value
         */
        public String getName() {
            return mName;
        }
    }

    /**
     * Unmarshal choice strategies to inject in generated bindings.
     * 
     * @return the unmarshal choice strategies to inject in generated bindings
     */
    public List<UnmarshalChoiceStrategy> getUnmarshalChoiceStrategies() {
        return _coxbGenModel.getUnmarshalChoiceStrategies();
    }

    /**
     * Unmarshal choice strategies to inject in generated bindings.
     * 
     * @param _unmarshalChoiceStrategies the unmarshal choice strategies to
     *            inject in generated bindings to set
     */
    public void setUnmarshalChoiceStrategies(List<UnmarshalChoiceStrategy> _unmarshalChoiceStrategies) {
        _coxbGenModel.setUnmarshalChoiceStrategies(_unmarshalChoiceStrategies);
    }

    /**
     * Add a choice strategy to inject in generated bindings.
     * 
     * @param unmarshalChoiceStrategy a choice strategy to inject in generated
     *            bindings
     */
    public void addUnmarshalChoiceStrategy(final UnmarshalChoiceStrategy unmarshalChoiceStrategy) {
        _coxbGenModel.addUnmarshalChoiceStrategy(unmarshalChoiceStrategy);
    }
}