001/*
002 * Copyright (C) 2009 Christian Schulte <cs@schulte.it>
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without
006 * modification, are permitted provided that the following conditions
007 * are met:
008 *
009 *   o Redistributions of source code must retain the above copyright
010 *     notice, this list of conditions and the following disclaimer.
011 *
012 *   o Redistributions in binary form must reproduce the above copyright
013 *     notice, this list of conditions and the following disclaimer in
014 *     the documentation and/or other materials provided with the
015 *     distribution.
016 *
017 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 * $JOMC: AbstractJomcToolCommand.java 5299 2016-08-30 01:50:13Z schulte $
029 *
030 */
031package org.jomc.cli.commands;
032
033import java.io.File;
034import java.net.MalformedURLException;
035import java.net.URL;
036import java.util.Locale;
037import java.util.logging.Level;
038import org.apache.commons.cli.CommandLine;
039import org.apache.commons.lang.StringEscapeUtils;
040import org.apache.commons.lang.StringUtils;
041import org.jomc.model.Implementation;
042import org.jomc.model.Module;
043import org.jomc.model.Modules;
044import org.jomc.model.Specification;
045import org.jomc.model.modlet.ModelHelper;
046import org.jomc.modlet.Model;
047import org.jomc.tools.JomcTool;
048
049/**
050 * {@code JomcTool} based command implementation.
051 *
052 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
053 */
054public abstract class AbstractJomcToolCommand extends AbstractModelCommand
055{
056
057    /**
058     * Creates a new {@code AbstractJomcToolCommand} instance.
059     */
060    public AbstractJomcToolCommand()
061    {
062        super();
063    }
064
065    @Override
066    public org.apache.commons.cli.Options getOptions()
067    {
068        final org.apache.commons.cli.Options options = super.getOptions();
069        options.addOption( Options.TEMPLATE_PROFILE_OPTION );
070        options.addOption( Options.DEFAULT_TEMPLATE_PROFILE_OPTION );
071        options.addOption( Options.DEFAULT_TEMPLATE_ENCODING_OPTION );
072        options.addOption( Options.TEMPLATE_LOCATION_OPTION );
073        options.addOption( Options.OUTPUT_ENCODING_OPTION );
074        options.addOption( Options.INPUT_ENCODING_OPTION );
075        options.addOption( Options.INDENTATION_STRING_OPTION );
076        options.addOption( Options.LINE_SEPARATOR_OPTION );
077        options.addOption( Options.LANGUAGE_OPTION );
078        options.addOption( Options.COUNTRY_OPTION );
079        options.addOption( Options.LOCALE_VARIANT_OPTION );
080        options.addOption( Options.IMPLEMENTATION_OPTION );
081        options.addOption( Options.MODULE_OPTION );
082        options.addOption( Options.SPECIFICATION_OPTION );
083        return options;
084    }
085
086    /**
087     * Creates a new object for a given class name and type.
088     *
089     * @param className The name of the class to create an object of.
090     * @param type The class of the type of object to create.
091     * @param <T> The type of the object to create.
092     *
093     * @return A new instance of the class with name {@code className}.
094     *
095     * @throws NullPointerException if {@code className} or {@code type} is {@code null}.
096     * @throws CommandExecutionException if creating a new object fails.
097     */
098    protected <T> T createObject( final String className, final Class<T> type ) throws CommandExecutionException
099    {
100        if ( className == null )
101        {
102            throw new NullPointerException( "className" );
103        }
104        if ( type == null )
105        {
106            throw new NullPointerException( "type" );
107        }
108
109        try
110        {
111            return Class.forName( className ).asSubclass( type ).newInstance();
112        }
113        catch ( final InstantiationException e )
114        {
115            throw new CommandExecutionException( Messages.getMessage( "objectCreationFailure", className ), e );
116        }
117        catch ( final IllegalAccessException e )
118        {
119            throw new CommandExecutionException( Messages.getMessage( "objectCreationFailure", className ), e );
120        }
121        catch ( final ClassNotFoundException e )
122        {
123            throw new CommandExecutionException( Messages.getMessage( "objectCreationFailure", className ), e );
124        }
125        catch ( final ClassCastException e )
126        {
127            throw new CommandExecutionException( Messages.getMessage( "objectCreationFailure", className ), e );
128        }
129    }
130
131    /**
132     * Creates a new {@code JomcTool} object for a given class name and type.
133     *
134     * @param commandLine The {@code CommandLine} to configure the new {@code JomcTool} object with.
135     * @param className The name of the class to create an object of.
136     * @param type The class of the type of object to create.
137     * @param <T> The type of the object to create.
138     *
139     * @return A new instance of the class with name {@code className} configured using {@code commandLine}.
140     *
141     * @throws NullPointerException if {@code commandLine}, {@code className} or {@code type} is {@code null}.
142     * @throws CommandExecutionException if creating a new object fails.
143     *
144     * @see #createObject(java.lang.String, java.lang.Class)
145     */
146    protected <T extends JomcTool> T createJomcTool( final String className, final Class<T> type,
147                                                     final CommandLine commandLine ) throws CommandExecutionException
148    {
149        if ( commandLine == null )
150        {
151            throw new NullPointerException( "commandLine" );
152        }
153        if ( className == null )
154        {
155            throw new NullPointerException( "className" );
156        }
157        if ( type == null )
158        {
159            throw new NullPointerException( "type" );
160        }
161
162        final T tool = this.createObject( className, type );
163        tool.setLogLevel( this.getLogLevel() );
164        tool.setExecutorService( this.getExecutorService( commandLine ) );
165        tool.setLocale( this.getLocale( commandLine ) );
166        tool.getListeners().add( new JomcTool.Listener()
167        {
168
169            @Override
170            public void onLog( final Level level, final String message, final Throwable throwable )
171            {
172                super.onLog( level, message, throwable );
173                log( level, message, throwable );
174            }
175
176        } );
177
178        if ( commandLine.hasOption( Options.TEMPLATE_ENCODING_OPTION.getOpt() ) )
179        {
180            this.log( Level.WARNING, Messages.getMessage( "deprecatedOptionMessage",
181                                                          Options.TEMPLATE_ENCODING_OPTION.getLongOpt(),
182                                                          Options.DEFAULT_TEMPLATE_ENCODING_OPTION.getLongOpt() ),
183                      null );
184
185            tool.setDefaultTemplateEncoding( commandLine.getOptionValue( Options.TEMPLATE_ENCODING_OPTION.getOpt() ) );
186        }
187        else if ( commandLine.hasOption( Options.DEFAULT_TEMPLATE_ENCODING_OPTION.getOpt() ) )
188        {
189            tool.setDefaultTemplateEncoding(
190                commandLine.getOptionValue( Options.DEFAULT_TEMPLATE_ENCODING_OPTION.getOpt() ) );
191
192        }
193        if ( commandLine.hasOption( Options.DEFAULT_TEMPLATE_PROFILE_OPTION.getOpt() ) )
194        {
195            tool.setDefaultTemplateProfile(
196                commandLine.getOptionValue( Options.DEFAULT_TEMPLATE_PROFILE_OPTION.getOpt() ) );
197
198        }
199        if ( commandLine.hasOption( Options.TEMPLATE_PROFILE_OPTION.getOpt() ) )
200        {
201            tool.setTemplateProfile( commandLine.getOptionValue( Options.TEMPLATE_PROFILE_OPTION.getOpt() ) );
202        }
203        if ( commandLine.hasOption( Options.TEMPLATE_LOCATION_OPTION.getOpt() ) )
204        {
205            try
206            {
207                tool.setTemplateLocation( new URL(
208                    commandLine.getOptionValue( Options.TEMPLATE_LOCATION_OPTION.getOpt() ) ) );
209
210            }
211            catch ( final MalformedURLException e )
212            {
213                this.log( Level.FINER, null, e );
214
215                try
216                {
217                    tool.setTemplateLocation( new File(
218                        commandLine.getOptionValue( Options.TEMPLATE_LOCATION_OPTION.getOpt() ) ).toURI().toURL() );
219
220                }
221                catch ( final MalformedURLException e2 )
222                {
223                    throw new CommandExecutionException( Messages.getMessage( e2 ), e2 );
224                }
225            }
226        }
227        if ( commandLine.hasOption( Options.INPUT_ENCODING_OPTION.getOpt() ) )
228        {
229            tool.setInputEncoding( commandLine.getOptionValue( Options.INPUT_ENCODING_OPTION.getOpt() ) );
230        }
231        if ( commandLine.hasOption( Options.OUTPUT_ENCODING_OPTION.getOpt() ) )
232        {
233            tool.setOutputEncoding( commandLine.getOptionValue( Options.OUTPUT_ENCODING_OPTION.getOpt() ) );
234        }
235        if ( commandLine.hasOption( Options.INDENTATION_STRING_OPTION.getOpt() ) )
236        {
237            tool.setIndentation( StringEscapeUtils.unescapeJava(
238                commandLine.getOptionValue( Options.INDENTATION_STRING_OPTION.getOpt() ) ) );
239
240        }
241        if ( commandLine.hasOption( Options.LINE_SEPARATOR_OPTION.getOpt() ) )
242        {
243            tool.setLineSeparator( StringEscapeUtils.unescapeJava(
244                commandLine.getOptionValue( Options.LINE_SEPARATOR_OPTION.getOpt() ) ) );
245
246        }
247
248        return tool;
249    }
250
251    /**
252     * Gets the specification to process from a given model.
253     *
254     * @param commandLine The command line specifying the specification to process.
255     * @param model The model to get the specification to process from.
256     *
257     * @return The specification to process or {@code null}.
258     *
259     * @throws NullPointerException if {@code commandLine} or {@code model} is {@code null}.
260     */
261    protected final Specification getSpecification( final CommandLine commandLine, final Model model )
262    {
263        if ( commandLine == null )
264        {
265            throw new NullPointerException( "commandLine" );
266        }
267        if ( model == null )
268        {
269            throw new NullPointerException( "model" );
270        }
271
272        Specification s = null;
273
274        if ( commandLine.hasOption( Options.SPECIFICATION_OPTION.getOpt() ) )
275        {
276            final String identifier = commandLine.getOptionValue( Options.SPECIFICATION_OPTION.getOpt() );
277            final Modules modules = ModelHelper.getModules( model );
278
279            if ( modules != null )
280            {
281                s = modules.getSpecification( identifier );
282            }
283
284            if ( s == null )
285            {
286                this.log( Level.WARNING, Messages.getMessage( "specificationNotFoundWarning", identifier ), null );
287            }
288        }
289
290        return s;
291    }
292
293    /**
294     * Gets the implementation to process from a given model.
295     *
296     * @param commandLine The command line specifying the implementation to process.
297     * @param model The model to get the implementation to process from.
298     *
299     * @return The implementation to process or {@code null}.
300     *
301     * @throws NullPointerException if {@code commandLine} or {@code model} is {@code null}.
302     */
303    protected final Implementation getImplementation( final CommandLine commandLine, final Model model )
304    {
305        if ( commandLine == null )
306        {
307            throw new NullPointerException( "commandLine" );
308        }
309        if ( model == null )
310        {
311            throw new NullPointerException( "model" );
312        }
313
314        Implementation i = null;
315
316        if ( commandLine.hasOption( Options.IMPLEMENTATION_OPTION.getOpt() ) )
317        {
318            final String identifier = commandLine.getOptionValue( Options.IMPLEMENTATION_OPTION.getOpt() );
319            final Modules modules = ModelHelper.getModules( model );
320
321            if ( modules != null )
322            {
323                i = modules.getImplementation( identifier );
324            }
325
326            if ( i == null )
327            {
328                this.log( Level.WARNING, Messages.getMessage( "implementationNotFoundWarning", identifier ), null );
329            }
330        }
331
332        return i;
333    }
334
335    /**
336     * Gets the module to process from a given model.
337     *
338     * @param commandLine The command line specifying the implementation to process.
339     * @param model The model to get the module to process from.
340     *
341     * @return The module to process or {@code null}.
342     *
343     * @throws NullPointerException if {@code model} is {@code null}.
344     */
345    protected final Module getModule( final CommandLine commandLine, final Model model )
346    {
347        if ( commandLine == null )
348        {
349            throw new NullPointerException( "commandLine" );
350        }
351        if ( model == null )
352        {
353            throw new NullPointerException( "model" );
354        }
355
356        Module m = null;
357
358        if ( commandLine.hasOption( Options.MODULE_OPTION.getOpt() ) )
359        {
360            final String name = commandLine.getOptionValue( Options.MODULE_OPTION.getOpt() );
361            final Modules modules = ModelHelper.getModules( model );
362
363            if ( modules != null )
364            {
365                m = modules.getModule( name );
366            }
367
368            if ( m == null )
369            {
370                this.log( Level.WARNING, Messages.getMessage( "moduleNotFoundWarning", name ), null );
371            }
372        }
373
374        return m;
375    }
376
377    /**
378     * Gets a flag indicating that all modules are requested to be processed.
379     *
380     * @param commandLine The command line to process.
381     *
382     * @return {@code true}, if processing of all modules is requested; {@code false}, else.
383     *
384     * @throws NullPointerException if {@code commandLine} is {@code null}.
385     *
386     * @see #getSpecification(org.apache.commons.cli.CommandLine, org.jomc.modlet.Model)
387     * @see #getImplementation(org.apache.commons.cli.CommandLine, org.jomc.modlet.Model)
388     * @see #getModule(org.apache.commons.cli.CommandLine, org.jomc.modlet.Model)
389     */
390    protected final boolean isModulesProcessingRequested( final CommandLine commandLine )
391    {
392        if ( commandLine == null )
393        {
394            throw new NullPointerException( "commandLine" );
395        }
396
397        return !( commandLine.hasOption( Options.SPECIFICATION_OPTION.getOpt() )
398                  || commandLine.hasOption( Options.IMPLEMENTATION_OPTION.getOpt() )
399                  || commandLine.hasOption( Options.MODULE_OPTION.getOpt() ) );
400
401    }
402
403    /**
404     * Gets a locale from a command line.
405     *
406     * @param commandLine The command line to get a locale from.
407     *
408     * @return The locale from {@code commandLine} or {@code null}, if {@code commandLine} does not hold options
409     * specifying a locale.
410     */
411    protected final Locale getLocale( final CommandLine commandLine )
412    {
413        if ( commandLine == null )
414        {
415            throw new NullPointerException( "commandLine" );
416        }
417
418        Locale locale = null;
419
420        final String lc = commandLine.hasOption( Options.LANGUAGE_OPTION.getOpt() )
421                              ? commandLine.getOptionValue( Options.LANGUAGE_OPTION.getOpt() )
422                              : null;
423
424        final String cc = commandLine.hasOption( Options.COUNTRY_OPTION.getOpt() )
425                              ? commandLine.getOptionValue( Options.COUNTRY_OPTION.getOpt() )
426                              : null;
427
428        final String lv = commandLine.hasOption( Options.LOCALE_VARIANT_OPTION.getOpt() )
429                              ? commandLine.getOptionValue( Options.LOCALE_VARIANT_OPTION.getOpt() )
430                              : null;
431
432        if ( lc != null || cc != null || lv != null )
433        {
434            locale = new Locale( StringUtils.defaultString( lc ),
435                                 StringUtils.defaultString( cc ),
436                                 StringUtils.defaultString( lv ) );
437
438        }
439
440        return locale;
441    }
442
443}