# HG changeset patch # User Omair Majid # Date 1367009156 14400 # Node ID dead5cab5a9ad1eac1d2615d2263bf4c6cf3ce41 # Parent 3ffe0afe5cdb0e8f0b620527e851ea3f244752ed Compute usage strings automatically Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-April/006405.html diff -r 3ffe0afe5cdb -r dead5cab5a9a distribution/docs/plugin.xsd --- a/distribution/docs/plugin.xsd Fri Apr 26 11:08:23 2013 -0600 +++ b/distribution/docs/plugin.xsd Fri Apr 26 16:45:56 2013 -0400 @@ -9,6 +9,7 @@ + @@ -62,7 +63,9 @@ + + @@ -71,6 +74,14 @@ + + + + + + + + diff -r 3ffe0afe5cdb -r dead5cab5a9a launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java Fri Apr 26 11:08:23 2013 -0600 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java Fri Apr 26 16:45:56 2013 -0400 @@ -69,14 +69,18 @@ private static final Logger logger = LoggingUtils.getLogger(PluginCommandInfoSource.class); + private final UsageStringBuilder usageBuilder; + private Map allNewCommands = new HashMap<>(); private Map> additionalBundlesForExistingCommands = new HashMap<>(); public PluginCommandInfoSource(String internalJarRoot, String pluginRootDir) { - this(new File(internalJarRoot), new File(pluginRootDir), new PluginConfigurationParser()); + this(new File(internalJarRoot), new File(pluginRootDir), new PluginConfigurationParser(), new UsageStringBuilder()); } - PluginCommandInfoSource(File internalJarRoot, File pluginRootDir, PluginConfigurationParser parser) { + PluginCommandInfoSource(File internalJarRoot, File pluginRootDir, PluginConfigurationParser parser, UsageStringBuilder usageBuilder) { + this.usageBuilder = usageBuilder; + File[] pluginDirs = pluginRootDir.listFiles(); if (pluginDirs == null) { logger.log(Level.SEVERE, "plugin root dir " + pluginRootDir + " does not exist"); @@ -132,9 +136,13 @@ addIfValidPath(bundlePaths, coreJarRoot, command.getDepenedencyBundles()); + String usage = command.getUsage(); + if (usage == null) { + usage = usageBuilder.getUsage(commandName, command.getOptions(), command.getPositionalArguments().toArray(new String[0])); + } BasicCommandInfo info = new BasicCommandInfo(commandName, command.getDescription(), - command.getUsage(), + usage, command.getOptions(), bundlePaths); diff -r 3ffe0afe5cdb -r dead5cab5a9a launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java Fri Apr 26 11:08:23 2013 -0600 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java Fri Apr 26 16:45:56 2013 -0400 @@ -87,16 +87,20 @@ public static class NewCommand { private final String commandName; + private final String usage; private final String description; - private String usage; + private final List positionalArguments; private final Options options; private final List additionalResources; private final List coreDeps; - public NewCommand(String name, String description, - Options options, List additionalResources, List coreDeps) { + public NewCommand(String name, String usage, String description, + List positionalArguments, Options options, + List additionalResources, List coreDeps) { this.commandName = name; + this.usage = usage; this.description = description; + this.positionalArguments = positionalArguments; this.options = options; this.additionalResources = additionalResources; this.coreDeps = coreDeps; @@ -106,14 +110,25 @@ return commandName; } + /** + * The usage string may be null if no usage string was explicitly + * provided. In that case, usage should be "computed" using options and + * arguments + */ + public String getUsage() { + return usage; + } + public String getDescription() { return description; } - public String getUsage() { - return usage; + /** Returns a list of strings indicating positional arguments */ + public List getPositionalArguments() { + return positionalArguments; } + /** Returns options (both optional and required) */ public Options getOptions() { return options; } diff -r 3ffe0afe5cdb -r dead5cab5a9a launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Fri Apr 26 11:08:23 2013 -0600 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Fri Apr 26 16:45:56 2013 -0400 @@ -285,7 +285,7 @@ } if (bundles.isEmpty()) { - logger.warning("plugin " + pluginName + " extends the command " + name + " but supplies no bundles"); + logger.warning("plugin " + pluginName + " extends the command " + name + " but supplies no bundles"); } if (name == null) { @@ -297,7 +297,9 @@ private NewCommand parseNewCommand(String pluginName, Node commandNode) { String name = null; + String usage = null; String description = null; + List arguments = new ArrayList<>(); Options options = new Options(); List bundles = new ArrayList<>(); List dependencies = new ArrayList<>(); @@ -307,8 +309,12 @@ Node node = nodes.item(i); if (node.getNodeName().equals("name")) { name = node.getTextContent().trim(); + } else if (node.getNodeName().equals("usage")) { + usage = node.getTextContent().trim(); } else if (node.getNodeName().equals("description")) { description = node.getTextContent().trim(); + } else if (node.getNodeName().equals("arguments")) { + arguments = parseArguments(pluginName, name, node); } else if (node.getNodeName().equals("options")) { options = parseOptions(node); } else if (node.getNodeName().equals("bundles")) { @@ -330,44 +336,39 @@ "name='" + name + "', description='" + description + "', options='" + options + "'"); return null; } else { - return new NewCommand(name, description, options, bundles, dependencies); + return new NewCommand(name, usage, description, arguments, options, bundles, dependencies); } } private Collection parseBundles(String pluginName, String commandName, Node bundlesNode) { - List bundles = new ArrayList<>(); - NodeList nodes = bundlesNode.getChildNodes(); + return parseNodeAsList(pluginName, commandName, bundlesNode, "bundle"); + } + + private Collection parseDependencies(String pluginName, String commandName, Node dependenciesNode) { + return parseNodeAsList(pluginName, commandName, dependenciesNode, "dependency"); + } + + private List parseArguments(String pluginName, String commandName, Node argumentsNode) { + return parseNodeAsList(pluginName, commandName, argumentsNode, "argument"); + } + + private List parseNodeAsList(String pluginName, String commandName, Node parentNode, String childElementName) { + List result = new ArrayList<>(); + NodeList nodes = parentNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); - if (node.getNodeName().equals("bundle")) { - String bundleName = node.getTextContent().trim(); - bundles.add(bundleName); + if (node.getNodeName().equals(childElementName)) { + String data = node.getTextContent().trim(); + result.add(data); } } - if (bundles.isEmpty()) { - logger.warning("plugin " + pluginName + " has an empty bundles element for command " + commandName); + if (result.isEmpty()) { + logger.warning("plugin " + pluginName + " has an empty " + parentNode.getNodeName() + + " element for command " + commandName); } - return bundles; - } - - private Collection parseDependencies(String pluginName, String commandName, Node dependenciesNode) { - List dependencies = new ArrayList<>(); - NodeList nodes = dependenciesNode.getChildNodes(); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - if (node.getNodeName().equals("dependency")) { - String bundleName = node.getTextContent().trim(); - dependencies.add(bundleName); - } - } - - if (dependencies.isEmpty()) { - logger.warning("plugin " + pluginName + " has an empty dependencies element for command " + commandName); - } - - return dependencies; + return result; } private Options parseOptions(Node optionsNode) { @@ -479,8 +480,10 @@ } } - Option opt = new Option(shortName, longName, Boolean.parseBoolean(argument), description); - opt.setArgName(longName != null? longName : shortName); + Option opt = new Option(shortName, longName, (argument != null), description); + if (argument != null) { + opt.setArgName(argument); + } opt.setRequired(required); return opt; } diff -r 3ffe0afe5cdb -r dead5cab5a9a launcher/src/main/java/com/redhat/thermostat/launcher/internal/UsageStringBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/UsageStringBuilder.java Fri Apr 26 16:45:56 2013 -0400 @@ -0,0 +1,106 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.launcher.internal; + +import java.util.Collection; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +public class UsageStringBuilder { + + /** + * Generates a 'usage' string based on the name (of a program) and the + * options that program will accept on the command line. + * + * @param name the program name + * @param options the options accepted by this program + * @return a String representing the usage. + */ + public String getUsage(String name, Options options, String... positionalArguments) { + StringBuilder result = new StringBuilder(); + result.append(name); + // commons-cli has no support for generics, so suppress this warning. + @SuppressWarnings("unchecked") + Collection