MergeModletsCommand.java

/*
 * Copyright (C) 2009 Christian Schulte <cs@schulte.it>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   o Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   o Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $JOMC: MergeModletsCommand.java 5263 2016-05-01 23:44:12Z schulte $
 *
 */
package org.jomc.cli.commands;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.util.JAXBResult;
import javax.xml.bind.util.JAXBSource;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.jomc.modlet.DefaultModletProvider;
import org.jomc.modlet.ModelContext;
import org.jomc.modlet.ModelException;
import org.jomc.modlet.ModelValidationReport;
import org.jomc.modlet.Modlet;
import org.jomc.modlet.ModletObject;
import org.jomc.modlet.Modlets;
import org.jomc.modlet.ObjectFactory;

/**
 * {@code merge-modlets} command implementation.
 *
 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
 */
public final class MergeModletsCommand extends AbstractModletCommand
{

    /**
     * Creates a new {@code MergeModletsCommand} instance.
     */
    public MergeModletsCommand()
    {
        super();
    }

    @Override
    public org.apache.commons.cli.Options getOptions()
    {
        final org.apache.commons.cli.Options options = super.getOptions();
        Option option = (Option) Options.DOCUMENT_OPTION.clone();
        option.setRequired( true );
        options.addOption( option );

        options.addOption( Options.DOCUMENT_ENCODING_OPTION );
        options.addOption( Options.STYLESHEET_OPTION );

        option = (Option) Options.MODLET_OPTION.clone();
        option.setRequired( true );
        options.addOption( option );

        options.addOption( Options.MODLET_VERSION_OPTION );
        options.addOption( Options.MODLET_VENDOR_OPTION );
        options.addOption( Options.MODLET_INCLUDES_OPTION );
        options.addOption( Options.MODLET_EXCLUDES_OPTION );
        options.addOption( Options.RESOURCES_OPTION );
        return options;
    }

    public String getName()
    {
        return "merge-modlets";
    }

    public String getAbbreviatedName()
    {
        return "mmd";
    }

    public String getShortDescription( final Locale locale )
    {
        return Messages.getMessage( "mergeModletsShortDescription" );
    }

    public String getLongDescription( final Locale locale )
    {
        return null;
    }

    protected void executeCommand( final CommandLine commandLine ) throws CommandExecutionException
    {
        if ( commandLine == null )
        {
            throw new NullPointerException( "commandLine" );
        }

        CommandLineClassLoader classLoader = null;

        try
        {
            classLoader = new CommandLineClassLoader( commandLine );
            final Modlets modlets = new Modlets();
            final ModelContext context = this.createModelContext( commandLine, classLoader );
            final Marshaller marshaller = context.createMarshaller( ModletObject.MODEL_PUBLIC_ID );
            final Unmarshaller unmarshaller = context.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID );

            if ( !commandLine.hasOption( Options.NO_MODLET_RESOURCE_VALIDATION_OPTION.getOpt() ) )
            {
                unmarshaller.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
            }

            File stylesheetFile = null;
            if ( commandLine.hasOption( Options.STYLESHEET_OPTION.getOpt() ) )
            {
                stylesheetFile = new File( commandLine.getOptionValue( Options.STYLESHEET_OPTION.getOpt() ) );
            }

            String modletVersion = null;
            if ( commandLine.hasOption( Options.MODLET_VERSION_OPTION.getOpt() ) )
            {
                modletVersion = commandLine.getOptionValue( Options.MODLET_VERSION_OPTION.getOpt() );
            }

            String modletVendor = null;
            if ( commandLine.hasOption( Options.MODLET_VENDOR_OPTION.getOpt() ) )
            {
                modletVendor = commandLine.getOptionValue( Options.MODLET_VENDOR_OPTION.getOpt() );
            }

            if ( commandLine.hasOption( Options.DOCUMENTS_OPTION.getOpt() ) )
            {
                for ( final File f : this.getDocumentFiles( commandLine ) )
                {
                    if ( this.isLoggable( Level.FINEST ) )
                    {
                        this.log( Level.FINEST, Messages.getMessage( "readingResource", f.getAbsolutePath() ), null );
                    }

                    Object o = unmarshaller.unmarshal( f );
                    if ( o instanceof JAXBElement<?> )
                    {
                        o = ( (JAXBElement<?>) o ).getValue();
                    }

                    if ( o instanceof Modlet )
                    {
                        modlets.getModlet().add( (Modlet) o );
                    }
                    else if ( o instanceof Modlets )
                    {
                        modlets.getModlet().addAll( ( (Modlets) o ).getModlet() );
                    }
                    else if ( this.isLoggable( Level.WARNING ) )
                    {
                        this.log( Level.WARNING,
                                  Messages.getMessage( "failureProcessing", f.getAbsolutePath(), o.toString() ),
                                  null );

                    }
                }
            }

            if ( commandLine.hasOption( Options.CLASSPATH_OPTION.getOpt() ) )
            {
                String[] resourceNames = null;

                if ( commandLine.hasOption( Options.RESOURCES_OPTION.getOpt() ) )
                {
                    resourceNames = commandLine.getOptionValues( Options.RESOURCES_OPTION.getOpt() );
                }

                if ( resourceNames == null )
                {
                    resourceNames = new String[]
                    {
                        DefaultModletProvider.getDefaultModletLocation()
                    };
                }

                for ( final String resource : resourceNames )
                {
                    for ( final Enumeration<URL> e = classLoader.getResources( resource ); e.hasMoreElements(); )
                    {
                        final URL url = e.nextElement();

                        if ( this.isLoggable( Level.FINEST ) )
                        {
                            this.log( Level.FINEST,
                                      Messages.getMessage( "readingResource", url.toExternalForm() ),
                                      null );

                        }

                        Object o = unmarshaller.unmarshal( url );
                        if ( o instanceof JAXBElement<?> )
                        {
                            o = ( (JAXBElement<?>) o ).getValue();
                        }

                        if ( o instanceof Modlet )
                        {
                            modlets.getModlet().add( (Modlet) o );
                        }
                        else if ( o instanceof Modlets )
                        {
                            modlets.getModlet().addAll( ( (Modlets) o ).getModlet() );
                        }
                        else if ( this.isLoggable( Level.WARNING ) )
                        {
                            this.log( Level.WARNING,
                                      Messages.getMessage( "failureProcessing", url.toExternalForm(), o.toString() ),
                                      null );

                        }
                    }
                }
            }

            if ( commandLine.hasOption( Options.MODLET_INCLUDES_OPTION.getOpt() ) )
            {
                final String[] values = commandLine.getOptionValues( Options.MODLET_INCLUDES_OPTION.getOpt() );

                if ( values != null )
                {
                    final List<String> includes = Arrays.asList( values );

                    for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); )
                    {
                        final Modlet m = it.next();

                        if ( !includes.contains( m.getName() ) )
                        {
                            this.log( Level.INFO,
                                      Messages.getMessage( "modletNameExclusionInfo", m.getName() ),
                                      null );

                            it.remove();
                        }
                        else
                        {
                            this.log( Level.INFO,
                                      Messages.getMessage( "modletNameInclusionInfo", m.getName() ),
                                      null );

                        }
                    }
                }
            }

            if ( commandLine.hasOption( Options.MODLET_EXCLUDES_OPTION.getOpt() ) )
            {
                final String[] values = commandLine.getOptionValues( Options.MODLET_EXCLUDES_OPTION.getOpt() );

                if ( values != null )
                {
                    for ( final String exclude : values )
                    {
                        final Modlet m = modlets.getModlet( exclude );

                        if ( m != null )
                        {
                            this.log( Level.INFO,
                                      Messages.getMessage( "modletNameExclusionInfo", m.getName() ),
                                      null );

                            modlets.getModlet().remove( m );
                        }
                    }
                }
            }

            final ModelValidationReport validationReport =
                context.validateModel( ModletObject.MODEL_PUBLIC_ID,
                                       new JAXBSource( marshaller, new ObjectFactory().createModlets( modlets ) ) );

            this.log( validationReport, marshaller );

            if ( !validationReport.isModelValid() )
            {
                throw new CommandExecutionException( Messages.getMessage( "invalidModel",
                                                                          ModletObject.MODEL_PUBLIC_ID ) );

            }

            Modlet mergedModlet = modlets.getMergedModlet(
                commandLine.getOptionValue( Options.MODLET_OPTION.getOpt() ), this.getModel( commandLine ) );

            mergedModlet.setVersion( modletVersion );
            mergedModlet.setVendor( modletVendor );

            final File modletFile = new File( commandLine.getOptionValue( Options.DOCUMENT_OPTION.getOpt() ) );

            if ( stylesheetFile != null )
            {
                final Transformer transformer = this.createTransformer( new StreamSource( stylesheetFile ) );
                final JAXBSource source =
                    new JAXBSource( marshaller, new ObjectFactory().createModlet( mergedModlet ) );

                final JAXBResult result = new JAXBResult( unmarshaller );
                unmarshaller.setSchema( null );
                transformer.transform( source, result );

                if ( result.getResult() instanceof JAXBElement<?>
                         && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Modlet )
                {
                    mergedModlet = (Modlet) ( (JAXBElement<?>) result.getResult() ).getValue();
                }
                else
                {
                    throw new CommandExecutionException( Messages.getMessage( "illegalTransformationResultError",
                                                                              stylesheetFile.getAbsolutePath() ) );

                }
            }

            marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );

            if ( commandLine.hasOption( Options.DOCUMENT_ENCODING_OPTION.getOpt() ) )
            {
                marshaller.setProperty( Marshaller.JAXB_ENCODING,
                                        commandLine.getOptionValue( Options.DOCUMENT_ENCODING_OPTION.getOpt() ) );

            }

            marshaller.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
            marshaller.marshal( new ObjectFactory().createModlet( mergedModlet ), modletFile );

            if ( this.isLoggable( Level.INFO ) )
            {
                this.log( Level.INFO, Messages.getMessage( "writingResource", modletFile.getAbsolutePath() ), null );
            }

            classLoader.close();
            classLoader = null;
        }
        catch ( final IOException e )
        {
            throw new CommandExecutionException( Messages.getMessage( e ), e );
        }
        catch ( final TransformerException e )
        {
            String message = Messages.getMessage( e );
            if ( message == null )
            {
                message = Messages.getMessage( e.getException() );
            }

            throw new CommandExecutionException( message, e );
        }
        catch ( final JAXBException e )
        {
            String message = Messages.getMessage( e );
            if ( message == null )
            {
                message = Messages.getMessage( e.getLinkedException() );
            }

            throw new CommandExecutionException( message, e );
        }
        catch ( final ModelException e )
        {
            throw new CommandExecutionException( Messages.getMessage( e ), e );
        }
        finally
        {
            try
            {
                if ( classLoader != null )
                {
                    classLoader.close();
                }
            }
            catch ( final IOException e )
            {
                this.log( Level.SEVERE, Messages.getMessage( e ), e );
            }
        }
    }

}