/**
* Copyright (C) 2009 STMicroelectronics
*
* This file is part of "Mind Compiler" 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: mind@ow2.org
*
* Authors: Matthieu Leclercq
* Contributors:
*/
package org.ow2.mind;
import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.objectweb.fractal.adl.ADLException;
import org.objectweb.fractal.adl.CompilerError;
import org.objectweb.fractal.adl.error.Error;
import org.objectweb.fractal.adl.error.GenericErrors;
import org.objectweb.fractal.adl.util.FractalADLLogManager;
import org.ow2.mind.cli.CmdFlag;
import org.ow2.mind.cli.CmdOption;
import org.ow2.mind.cli.CmdOptionBooleanEvaluator;
import org.ow2.mind.cli.CommandLine;
import org.ow2.mind.cli.CommandLineOptionExtensionHelper;
import org.ow2.mind.cli.CommandOptionHandler;
import org.ow2.mind.cli.InvalidCommandLineException;
import org.ow2.mind.cli.Options;
import org.ow2.mind.cli.PrintStackTraceOptionHandler;
import org.ow2.mind.cli.StageOptionHandler;
import org.ow2.mind.error.ErrorManager;
import org.ow2.mind.inject.GuiceModuleExtensionHelper;
import org.ow2.mind.plugin.PluginLoaderModule;
import org.ow2.mind.plugin.PluginManager;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class Launcher {
protected static final String PROGRAM_NAME_PROPERTY_NAME = "mindc.launcher.name";
protected static final String ID_PREFIX = "org.ow2.mind.mindc.";
protected final CmdFlag helpOpt = new CmdFlag(
ID_PREFIX
+ "Help",
"h", "help",
"Print this help and exit");
protected final CmdFlag versionOpt = new CmdFlag(
ID_PREFIX
+ "Version",
"v", "version",
"Print version number and exit");
protected final CmdFlag extensionPointsListOpt = new CmdFlag(
ID_PREFIX
+ "PrintExtensionPoints",
null,
"extension-points",
"Print the list of available extension points and exit.");
protected final Options options = new Options();
protected static Logger logger = FractalADLLogManager
.getLogger("launcher");
protected Map<String, String> adlToExecName;
protected Map<Object, Object> compilerContext = new HashMap<Object, Object>();
// compiler components :
protected Injector injector;
protected ErrorManager errorManager;
protected ADLCompiler adlCompiler;
protected void init(final String... args) throws InvalidCommandLineException {
if (logger.isLoggable(Level.CONFIG)) {
for (final String arg : args) {
logger.config("[arg] " + arg);
}
}
/****** Initialization of the PluginManager Component *******/
final Injector bootStrapPluginManagerInjector = getBootstrapInjector();
final PluginManager pluginManager = bootStrapPluginManagerInjector
.getInstance(PluginManager.class);
addOptions(pluginManager);
// parse arguments to a CommandLine.
final CommandLine cmdLine = CommandLine.parseArgs(options, false, args);
checkExclusiveGroups(pluginManager, cmdLine);
compilerContext
.put(CmdOptionBooleanEvaluator.CMD_LINE_CONTEXT_KEY, cmdLine);
invokeOptionHandlers(pluginManager, cmdLine, compilerContext);
// If help is asked, print it and exit.
if (helpOpt.isPresent(cmdLine)) {
printHelp(System.out);
System.exit(0);
}
// If version is asked, print it and exit.
if (versionOpt.isPresent(cmdLine)) {
printVersion(System.out);
System.exit(0);
}
// If the extension points list is asked, print it and exit.
if (extensionPointsListOpt.isPresent(cmdLine)) {
printExtensionPoints(pluginManager, System.out);
System.exit(0);
}
// get list of ADL
adlToExecName = parserADLList(cmdLine.getArguments(), cmdLine);
// initialize compiler
initInjector(pluginManager, compilerContext);
initCompiler();
}
protected Injector getBootstrapInjector() {
return Guice.createInjector(new PluginLoaderModule());
}
protected void initCompiler() {
errorManager = injector.getInstance(ErrorManager.class);
adlCompiler = injector.getInstance(ADLCompiler.class);
}
protected void initInjector(final PluginManager pluginManager,
final Map<Object, Object> compilerContext) {
injector = Guice.createInjector(GuiceModuleExtensionHelper.getModules(
pluginManager, compilerContext));
}
protected Map<String, String> parserADLList(final List<String> adlList,
final CommandLine cmdLine) throws InvalidCommandLineException {
final Map<String, String> adlToExecName = new LinkedHashMap<String, String>();
// parse adlNames
for (final String adlName : adlList) {
final int i = adlName.indexOf(':');
if (i == 0) {
throw new InvalidCommandLineException(
"no adl name is specified for executable "
+ adlName.substring(i + 1) + ".", 1);
}
if (i == -1) {
adlToExecName.put(adlName, null);
} else {
final String adl = adlName.substring(0, i);
final String exec = adlName.substring(i + 1);
adlToExecName.put(adl, exec);
}
}
return adlToExecName;
}
public List<Object> compile(final List<Error> errors,
final List<Error> warnings) throws InvalidCommandLineException {
// Check if at least 1 adlName is specified
if (adlToExecName.size() == 0) {
throw new InvalidCommandLineException("no definition name is specified.",
1);
}
final List<Object> result = new ArrayList<Object>();
for (final Map.Entry<String, String> e : adlToExecName.entrySet()) {
try {
final HashMap<Object, Object> contextMap = new HashMap<Object, Object>(
compilerContext);
final String adlName = e.getKey();
final String execName = e.getValue();
final List<Object> l = adlCompiler.compile(adlName, execName,
StageOptionHandler.getCompilationStage(contextMap), contextMap);
if (!errorManager.getErrors().isEmpty()) {
break;
}
if (l != null) result.addAll(l);
} catch (final InterruptedException e1) {
throw new CompilerError(GenericErrors.INTERNAL_ERROR, e,
"Interrupted while executing compilation tasks");
} catch (final ADLException e1) {
if (!errorManager.getErrors().contains(e1.getError())) {
// the error has not been logged in the error manager, print it.
try {
errorManager.logError(e1.getError());
} catch (final ADLException e2) {
// ignore
}
}
}
}
if (errors != null) errors.addAll(errorManager.getErrors());
if (warnings != null) warnings.addAll(errorManager.getWarnings());
return result;
}
protected void addOptions(final PluginManager pluginManagerItf) {
options.addOptions(helpOpt, versionOpt, extensionPointsListOpt);
options.addOptions(CommandLineOptionExtensionHelper
.getCommandOptions(pluginManagerItf));
}
protected void checkExclusiveGroups(final PluginManager pluginManagerItf,
final CommandLine cmdLine) throws InvalidCommandLineException {
final Collection<Set<String>> exclusiveGroups = CommandLineOptionExtensionHelper
.getExclusiveGroups(pluginManagerItf);
for (final Set<String> exclusiveGroup : exclusiveGroups) {
CmdOption opt = null;
for (final String id : exclusiveGroup) {
final CmdOption opt1 = cmdLine.getOptions().getById(id);
if (opt1.isPresent(cmdLine)) {
if (opt != null) {
throw new InvalidCommandLineException("Options '"
+ opt.getPrototype() + "' and '" + opt1.getPrototype()
+ "' cannot be specified simultaneously on the command line.",
1);
}
opt = opt1;
}
}
}
}
protected void invokeOptionHandlers(final PluginManager pluginManagerItf,
final CommandLine cmdLine, final Map<Object, Object> context)
throws InvalidCommandLineException {
final List<CmdOption> toBeExecuted = new LinkedList<CmdOption>(cmdLine
.getOptions().getOptions());
final Set<String> executedId = new HashSet<String>(toBeExecuted.size());
while (!toBeExecuted.isEmpty()) {
final int toBeExecutedSize = toBeExecuted.size();
final Iterator<CmdOption> iter = toBeExecuted.iterator();
while (iter.hasNext()) {
final CmdOption option = iter.next();
final List<String> precedenceIds = CommandLineOptionExtensionHelper
.getPrecedenceIds(option, pluginManagerItf);
if (executedId.containsAll(precedenceIds)) {
// task ready to be executed
for (final CommandOptionHandler handler : CommandLineOptionExtensionHelper
.getHandler(option, pluginManagerItf)) {
handler.processCommandOption(option, cmdLine, context);
}
executedId.add(option.getId());
iter.remove();
}
}
if (toBeExecutedSize == toBeExecuted.size()) {
// nothing has been executed. there is a circular dependency
throw new CompilerError(GenericErrors.GENERIC_ERROR,
"Circular dependency in command line option handlers: "
+ toBeExecuted);
}
}
}
// ---------------------------------------------------------------------------
// Utility methods
// ---------------------------------------------------------------------------
private void printExtensionPoints(final PluginManager pluginManager,
final PrintStream out) {
final Iterable<String> extensionPoints = pluginManager
.getExtensionPointNames();
System.out.println("Supported extension points are : ");
for (final String extensionPoint : extensionPoints) {
System.out.println("\t'" + extensionPoint + "'");
}
}
protected static void checkDir(final File d)
throws InvalidCommandLineException {
if (d.exists() && !d.isDirectory())
throw new InvalidCommandLineException("Invalid build directory '"
+ d.getAbsolutePath() + "' not a directory", 6);
}
protected String getVersion() {
final String pkgVersion = this.getClass().getPackage()
.getImplementationVersion();
return (pkgVersion == null) ? "unknown" : pkgVersion;
}
protected String getProgramName() {
return System.getProperty(PROGRAM_NAME_PROPERTY_NAME, getClass().getName());
}
protected void printVersion(final PrintStream ps) {
ps.println(getProgramName() + " version " + getVersion());
}
protected void printHelp(final PrintStream ps) {
printUsage(ps);
ps.println();
ps.println("Available options are :");
int maxCol = 0;
for (final CmdOption opt : options.getOptions()) {
final int col = 2 + opt.getPrototype().length();
if (col > maxCol) maxCol = col;
}
for (final CmdOption opt : options.getOptions()) {
final StringBuffer sb = new StringBuffer(" ");
sb.append(opt.getPrototype());
while (sb.length() < maxCol)
sb.append(' ');
sb.append(" ").append(opt.getDescription());
ps.println(sb);
}
}
protected void printUsage(final PrintStream ps) {
ps.println("Usage: " + getProgramName()
+ " [OPTIONS] (<definition>[:<execname>])+");
ps.println(" where <definition> is the name of the component to"
+ " be compiled, ");
ps.println(" and <execname> is the name of the output file to be created.");
}
protected void handleException(final InvalidCommandLineException e) {
logger.log(Level.FINER, "Caught an InvalidCommandLineException", e);
if (PrintStackTraceOptionHandler.getPrintStackTrace(compilerContext)) {
e.printStackTrace();
} else {
System.err.println(e.getMessage());
printHelp(System.err);
System.exit(e.getExitValue());
}
}
/**
* Entry point.
*
* @param args
*/
public static void main(final String... args) {
final Launcher l = new Launcher();
try {
l.init(args);
l.compile(null, null);
} catch (final InvalidCommandLineException e) {
l.handleException(e);
}
if (!l.errorManager.getErrors().isEmpty()) System.exit(1);
}
public static void nonExitMain(final String... args)
throws InvalidCommandLineException, ADLException {
nonExitMain(null, null, args);
}
public static void nonExitMain(final List<Error> errors,
final List<Error> warnings, final String... args)
throws InvalidCommandLineException, ADLException {
final Launcher l = new Launcher();
l.init(args);
l.compile(errors, warnings);
}
}