/*
* Copyright (C) 2011 Laurent Caillette
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.configuration.parse;
import java.io.File;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.List;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.novelang.logger.Logger;
import org.novelang.logger.LoggerFactory;
/**
* Base class for command-line parameters parsing.
*
* @author Laurent Caillette
*/
public abstract class GenericParameters {
private static final Logger LOGGER = LoggerFactory.getLogger( GenericParameters.class ) ;
protected final Options options ;
protected final CommandLine line ;
protected final HelpPrinter helpPrinter;
private final File baseDirectory ;
private final File contentRoot ;
private final Iterable< File > fontDirectories ;
private final Iterable< File > styleDirectories ;
private final File hyphenationDirectory ;
private final Charset defaultSourceCharset ;
private final Charset defaultRenderingCharset ;
private final File logDirectory ;
private final File temporaryDirectory ;
public GenericParameters(
final File baseDirectory,
final String[] parameters
)
throws ArgumentException
{
LOGGER.debug( "Base directory: '", baseDirectory.getAbsolutePath(), "'" ) ;
LOGGER.debug( "Parameters: '", Lists.newArrayList( parameters ), "'" ) ;
this.baseDirectory = Preconditions.checkNotNull( baseDirectory ) ;
options = new Options() ;
options.addOption( GenericParametersConstants.OPTION_HELP ) ;
options.addOption( GenericParametersConstants.OPTION_CONTENT_ROOT ) ;
options.addOption( GenericParametersConstants.OPTION_FONT_DIRECTORIES ) ;
options.addOption( GenericParametersConstants.OPTION_EMPTY ) ;
options.addOption( GenericParametersConstants.OPTION_STYLE_DIRECTORIES ) ;
options.addOption( GenericParametersConstants.OPTION_LOG_DIRECTORY ) ;
options.addOption( GenericParametersConstants.OPTION_TEMPORARY_DIRECTORY ) ;
options.addOption( GenericParametersConstants.OPTION_HYPHENATION_DIRECTORY ) ;
options.addOption( GenericParametersConstants.OPTION_DEFAULT_SOURCE_CHARSET ) ;
options.addOption( GenericParametersConstants.OPTION_DEFAULT_RENDERING_CHARSET ) ;
enrich( options ) ;
helpPrinter = new HelpPrinter( options ) ;
if( containsHelpTrigger( parameters ) ) {
LOGGER.debug( "Help trigger detected" ) ;
throw new ArgumentException( helpPrinter ) ;
}
final CommandLineParser parser = new PosixParser() ;
try {
line = parser.parse( options, parameters ) ;
logDirectory = extractDirectory(
baseDirectory, GenericParametersConstants.OPTION_LOG_DIRECTORY, line, false ) ;
temporaryDirectory = extractDirectory(
baseDirectory, GenericParametersConstants.OPTION_TEMPORARY_DIRECTORY, line, false ) ;
if( line.hasOption( GenericParametersConstants.OPTION_CONTENT_ROOT.getLongOpt() ) ) {
contentRoot = extractDirectory( baseDirectory, GenericParametersConstants.OPTION_CONTENT_ROOT, line ) ;
} else {
contentRoot = null ;
}
if( line.hasOption( GenericParametersConstants.OPTION_DEFAULT_SOURCE_CHARSET.getLongOpt() ) ) {
defaultSourceCharset = Charset.forName(
line.getOptionValue( GenericParametersConstants.OPTION_DEFAULT_SOURCE_CHARSET.getLongOpt() ) ) ;
} else {
defaultSourceCharset = null ;
}
if( line.hasOption( GenericParametersConstants.OPTION_DEFAULT_RENDERING_CHARSET.getLongOpt() ) ) {
defaultRenderingCharset = Charset.forName(
line.getOptionValue( GenericParametersConstants.OPTION_DEFAULT_RENDERING_CHARSET.getLongOpt() ) ) ;
} else {
defaultRenderingCharset = null ;
}
if( line.hasOption( GenericParametersConstants.OPTION_STYLE_DIRECTORIES.getLongOpt() ) ) {
final String[] styleDirectoriesNames =
line.getOptionValues( GenericParametersConstants.OPTION_STYLE_DIRECTORIES.getLongOpt() ) ;
LOGGER.debug( "Argument for Style directories = '",
Lists.newArrayList( styleDirectoriesNames ), "'" ) ;
styleDirectories = extractDirectories( baseDirectory, styleDirectoriesNames ) ;
} else {
styleDirectories = ImmutableList.of() ;
}
hyphenationDirectory = extractDirectory( baseDirectory, GenericParametersConstants.OPTION_HYPHENATION_DIRECTORY, line ) ;
if( line.hasOption( GenericParametersConstants.OPTION_FONT_DIRECTORIES.getLongOpt() ) ) {
final String[] fontDirectoriesNames =
line.getOptionValues( GenericParametersConstants.OPTION_FONT_DIRECTORIES.getLongOpt() ) ;
LOGGER.debug( "Argument for Font directories = '",
Lists.newArrayList( fontDirectoriesNames ), "'" ) ;
fontDirectories = extractDirectories( baseDirectory, fontDirectoriesNames ) ;
} else {
fontDirectories = ImmutableList.of() ;
}
} catch( ParseException e ) {
throw new ArgumentException( e, helpPrinter ) ;
}
}
protected abstract void enrich( Options options ) ;
private void throwArgumentException( final String message ) throws ArgumentException {
throw new ArgumentException( message, helpPrinter ) ;
}
// =======
// Getters
// =======
/**
* Return the directory used to evaluate all relative directories from.
* @return a non-null object.
*/
public File getBaseDirectory() {
return baseDirectory ;
}
/**
* Returns the content root directory to get content from.
* @return a possibly-null object.
*/
public File getContentRoot() {
return contentRoot ;
}
/**
* Returns the directories containing embeddable font files.
* @return a non-null object iterating over no nulls.
*/
public Iterable< File > getFontDirectories() {
return fontDirectories;
}
/**
* Returns the directory containing style files.
* @return a non-null object iterating over no nulls.
*/
public Iterable< File > getStyleDirectories() {
return styleDirectories;
}
/**
* Returns the directory containing hyphenation files.
* @return a null object if undefined, a reference to an existing directory otherwise.
*/
public File getHyphenationDirectory() {
return hyphenationDirectory;
}
/**
* Returns the directory to spit log files into.
* @return a null object if undefined, a reference to an existing directory otherwise.
*/
public File getLogDirectory() {
return logDirectory;
}
/**
* Returns the directory to put temporary files into.
* @return a null object if undefined, a reference to an existing directory otherwise.
*/
public File getTemporaryDirectory() {
return temporaryDirectory;
}
/**
* Returns the default charset for source documents.
* @return a null object if undefined, a valid {@code Charset} otherwise.
*/
public Charset getDefaultSourceCharset() {
return defaultSourceCharset ;
}
/**
* Returns the default charset for rendering documents.
* @return a null object if undefined, a valid {@code Charset} otherwise.
*/
public Charset getDefaultRenderingCharset() {
return defaultRenderingCharset;
}
// ==========
// Extractors
// ==========
protected File extractDirectory(
final File baseDirectory,
final Option option,
final CommandLine line
) throws ArgumentException {
return extractDirectory( baseDirectory, option, line, true ) ;
}
protected File extractDirectory(
final File baseDirectory,
final Option option,
final CommandLine line,
final boolean failOnNonExistingDirectory
)
throws ArgumentException
{
final File directory ;
if( line.hasOption( option.getLongOpt() ) ) {
final String directoryName =
line.getOptionValue( option.getLongOpt() ) ;
LOGGER.debug( "Argument for ", option.getDescription(), " = '", directoryName, "'" ) ;
directory = extractDirectory( baseDirectory, directoryName, failOnNonExistingDirectory ) ;
} else {
directory = null ;
}
return directory ;
}
protected final Iterable< File > extractDirectories(
final File parent,
final String[] directoryNames
)
throws ArgumentException
{
final List< File > directories = Lists.newArrayList() ;
for( final String directoryName : directoryNames ) {
directories.add( extractDirectory( parent, directoryName, true ) ) ;
}
return ImmutableList.copyOf( directories ) ;
}
protected final File extractDirectory(
final File parent,
final String directoryName,
final boolean failOnNonExistingDirectory
)
throws ArgumentException
{
final File directory ;
final File maybeAbsoluteDirectory = new File( directoryName ) ;
if( maybeAbsoluteDirectory.isAbsolute() ) {
directory = maybeAbsoluteDirectory ;
} else {
directory = new File( parent, directoryName ) ;
}
if( failOnNonExistingDirectory && ! ( directory.exists() && directory.isDirectory() ) ) {
throwArgumentException( "Not a directory: '" + directoryName + "'" ) ;
}
return directory ;
}
// =================
// Commons-CLI stuff
// =================
// ====
// Help
// ====
public static class HelpPrinter {
private final Options options ;
public HelpPrinter( final Options options ) {
this.options = options;
}
public void print(
final PrintStream printStream,
final String commandName,
final int columns
) {
print( new PrintWriter( printStream ), commandName, columns ) ;
}
public void print(
final PrintWriter printWriter,
final String commandName,
final int columns
) {
final HelpFormatter helpFormatter = new HelpFormatter() ;
helpFormatter.printHelp(
printWriter,
columns,
commandName,
"",
options,
2,
2,
""
) ;
printWriter.flush() ;
}
public String asString( final String commandName, final int columns ) {
final StringWriter stringWriter = new StringWriter() ;
print( new PrintWriter( stringWriter ), commandName, columns ) ;
return stringWriter.toString() ;
}
}
private boolean containsHelpTrigger( final String[] parameters ) {
for( int i = 0 ; i < parameters.length ; i++ ) {
final String parameter = parameters[ i ] ;
if( GenericParametersConstants.HELP_TRIGGER.equals( parameter ) ) {
return true ;
}
}
return false ;
}
}