Mercurial > hg > release > thermostat-2.0
changeset 2309:7cb8987aa6bc
Replace CommandInfo.needsFileTabCompletion with CompleterService
Reviewed-by: jerboaa
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-April/018639.html
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-May/018762.html
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/FileNameArgument.java Tue May 24 09:17:54 2016 -0400 @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2016 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.cli; + +import com.redhat.thermostat.client.cli.internal.LocaleResources; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.shared.locale.Translate; + +public class FileNameArgument { + + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + public static final String ARGUMENT_NAME = "filename"; + + private final String fileName; + + private FileNameArgument(Arguments args, boolean isRequired) throws CommandException { + this.fileName = args.getArgument(ARGUMENT_NAME); + if (isRequired && fileName == null) { + throw new CommandException(translator.localize(LocaleResources.FILENAME_REQUIRED)); + } + } + + public static FileNameArgument required(Arguments args) throws CommandException { + return new FileNameArgument(args, true); + } + + public static FileNameArgument optional(Arguments args) throws CommandException { + return new FileNameArgument(args, false); + } + + /** + * @return The value of filename stored in this object, which may be null. + */ + public String getFileName() { + return fileName; + } + + public boolean isPresent() { + return fileName != null; + } + +}
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java Tue May 24 11:09:57 2016 +0200 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java Tue May 24 09:17:54 2016 -0400 @@ -102,6 +102,7 @@ AGENTID_REQUIRED, ONE_ID_REQUIRED, AGENT_INFO_NOT_FOUND, + FILENAME_REQUIRED, PURGING_AGENT_DATA, STORAGE_UNAVAILABLE,
--- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties Tue May 24 11:09:57 2016 +0200 +++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties Tue May 24 09:17:54 2016 -0400 @@ -60,6 +60,7 @@ VMID_REQUIRED = A vmId is required AGENTID_REQUIRED = An agentId is required ONE_ID_REQUIRED = An agentId or vmId is required +FILENAME_REQUIRED = A filename is required AGENT_INFO_NOT_FOUND = Agent Information could not be retrieved for the {0} {1} PURGING_AGENT_DATA = Purging data for agent:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/FileNameArgumentTest.java Tue May 24 09:17:54 2016 -0400 @@ -0,0 +1,95 @@ +/* + * Copyright 2012-2016 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 + * <http://www.gnu.org/licenses/>. + * + * 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.client.cli; + +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandException; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class FileNameArgumentTest { + + private static final String FILENAME = "filename"; + private static final String PATH = "path/to/file"; + private Arguments args; + + @Before + public void setup() { + args = mock(Arguments.class); + } + + @Test(expected = CommandException.class) + public void testRequiredArgumentNotPresent() throws CommandException { + when(args.hasArgument(FILENAME)).thenReturn(false); + when(args.getArgument(FILENAME)).thenReturn(null); + FileNameArgument.required(args); + } + + @Test + public void testRequiredArgumentPresent() throws CommandException { + when(args.hasArgument(FILENAME)).thenReturn(true); + when(args.getArgument(FILENAME)).thenReturn(PATH); + FileNameArgument argument = FileNameArgument.required(args); + assertThat(argument.isPresent(), is(true)); + assertThat(argument.getFileName(), is(PATH)); + } + + @Test + public void testOptionalArgumentNotPresent() throws CommandException { + when(args.hasArgument(FILENAME)).thenReturn(false); + when(args.getArgument(FILENAME)).thenReturn(null); + FileNameArgument argument = FileNameArgument.optional(args); + assertThat(argument.isPresent(), is(false)); + assertThat(argument.getFileName(), is(equalTo(null))); + } + + @Test + public void testOptionalArgumentPresent() throws CommandException { + when(args.hasArgument(FILENAME)).thenReturn(true); + when(args.getArgument(FILENAME)).thenReturn(PATH); + FileNameArgument argument = FileNameArgument.optional(args); + assertThat(argument.isPresent(), is(true)); + assertThat(argument.getFileName(), is(PATH)); + } + +}
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/CompleterService.java Tue May 24 11:09:57 2016 +0200 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/CompleterService.java Tue May 24 09:17:54 2016 -0400 @@ -47,6 +47,11 @@ * implementation so that "-f TAB" can produce a list of possible fooIds. This implementation would return a singleton * Set of "foo-command" for {@link CompleterService#getCommands()}, and a Map from a CliCommandOption with short-opt "-f"/ * long-opt "--fooId" to the relevant {@link TabCompleter} instance for {@link CompleterService#getOptionCompleters()}. + * + * There are several built-in completions which Thermostat provides. If your command uses a vmId, then declaring a + * -v/--vmId option in your thermostat-plugin.xml will give you automagic vmId completions in Thermostat shell. + * Likewise, -a/--agentId, -d/--dbUrl, and -f/--filename completions are provided if you simply include these options + * in your XML. */ @ExtensionPoint public interface CompleterService {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/FileNameTabCompleter.java Tue May 24 09:17:54 2016 -0400 @@ -0,0 +1,55 @@ +/* + * Copyright 2012-2016 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 + * <http://www.gnu.org/licenses/>. + * + * 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.common.cli; + +import com.redhat.thermostat.annotations.Service; + +/** + * A stub interface for marking a TabCompleter which specifically provides file name completions. + * This is intended to be exported as an OSGi service. If you are writing a {@link CompleterService} + * which provides file name completion, you may use this interface as a dependency and use the + * Thermostat-provided file name completions this way. + * + * However, if it is applicable, you may alternatively simply use the convention of naming your + * filename option argument -f/--filename (ex. foo-command -f /path/to/file, or + * foo-command --filename /path/to/file). If you define your file name completion argument to match + * this naming convention exactly in your thermostat-plugin.xml, then the filename completions will + * be taken care of automatically by Thermostat. + */ +@Service +public interface FileNameTabCompleter extends TabCompleter { +}
--- a/distribution/docs/thermostat-plugin.xsd Tue May 24 11:09:57 2016 +0200 +++ b/distribution/docs/thermostat-plugin.xsd Tue May 24 09:17:54 2016 -0400 @@ -36,13 +36,6 @@ <xs:element name="required" type="xs:boolean"/> -<xs:element name="add-file-completion" type="xs:boolean"> - <xs:annotation> - <xs:documentation>Determines whether a command gets tab completion added for files. - That is, in addition to command option completion.</xs:documentation> - </xs:annotation> -</xs:element> - <xs:element name="id" type="xs:string"/> <xs:element name="configuration" type="xs:string"/> @@ -104,7 +97,6 @@ <xs:element ref="options" minOccurs="0" maxOccurs="1"/> <xs:element ref="environments"/> <xs:element ref="bundles"/> - <xs:element ref="add-file-completion" minOccurs="0" maxOccurs="1"/> </xs:sequence> </xs:complexType> </xs:element>
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/Activator.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/Activator.java Tue May 24 09:17:54 2016 -0400 @@ -47,6 +47,7 @@ import com.redhat.thermostat.common.cli.CommandRegistry; import com.redhat.thermostat.common.cli.CommandRegistryImpl; import com.redhat.thermostat.common.cli.CompleterService; +import com.redhat.thermostat.common.cli.FileNameTabCompleter; import com.redhat.thermostat.common.config.ClientPreferences; import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource; import com.redhat.thermostat.launcher.BundleManager; @@ -339,11 +340,15 @@ LogLevelCompleterService logLevelCompleterService = new LogLevelCompleterService(); context.registerService(CompleterService.class.getName(), logLevelCompleterService, null); + FileNameCompleterService fileNameCompleterService = new FileNameCompleterService(); + context.registerService(CompleterService.class.getName(), fileNameCompleterService, null); context.registerService(CompleterService.class.getName(), vmIdCompleterService, null); context.registerService(CompleterService.class.getName(), agentIdCompleterService, null); context.registerService(CompleterService.class.getName(), pingCommandCompleterService, null); context.registerService(CompleterService.class.getName(), dbUrlCompleterService, null); + + context.registerService(FileNameTabCompleter.class.getName(), new JLineFileNameCompleter(), null); } @Override
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BasicCommandInfo.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BasicCommandInfo.java Tue May 24 09:17:54 2016 -0400 @@ -52,9 +52,8 @@ private final Options options; private final Set<Environment> environments; private final List<BundleInformation> bundles; - private final boolean fileTabCompletionNeeded; - public BasicCommandInfo(String name, String summary, String description, String usage, Options options, Set<Environment> environments, List<BundleInformation> bundles, boolean fileTabCompletionNeeded) { + public BasicCommandInfo(String name, String summary, String description, String usage, Options options, Set<Environment> environments, List<BundleInformation> bundles) { this.name = name; this.summary = summary; this.description = description; @@ -62,7 +61,6 @@ this.options = options; this.environments = environments; this.bundles = bundles; - this.fileTabCompletionNeeded = fileTabCompletionNeeded; } @Override @@ -91,11 +89,6 @@ } @Override - public boolean needsFileTabCompletions() { - return fileTabCompletionNeeded; - } - - @Override public Set<Environment> getEnvironments() { return environments; }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BuiltInCommandInfo.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BuiltInCommandInfo.java Tue May 24 09:17:54 2016 -0400 @@ -63,7 +63,6 @@ private static final String PROPERTY_USAGE = "usage"; private static final String PROPERTY_OPTIONS = "options"; private static final String PROPERTY_ENVIRONMENTS = "environments"; - private static final String PROPERTY_FILE_COMPLETION = "add-file-completion"; private static final String PROP_SHORTOPT = ".short"; private static final String PROP_LONGOPT = ".long"; @@ -75,7 +74,6 @@ private Options options; private EnumSet<Environment> environment; private List<BundleInformation> dependencies; - private boolean fileTabCompletionNeeded; BuiltInCommandInfo(String commandName, Properties properties) { options = new Options(); @@ -94,8 +92,6 @@ learnOptions((String) entry.getValue(), properties); } else if (key.equals(PROPERTY_ENVIRONMENTS)) { environment = parseEnvironment(properties.getProperty(key)); - } else if (key.equals(PROPERTY_FILE_COMPLETION)) { - fileTabCompletionNeeded = Boolean.parseBoolean(properties.getProperty(key)); } } } @@ -400,9 +396,5 @@ return dependencies; } - @Override - public boolean needsFileTabCompletions() { - return fileTabCompletionNeeded; - } }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CommandInfo.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CommandInfo.java Tue May 24 09:17:54 2016 -0400 @@ -80,11 +80,6 @@ */ public Options getOptions(); - /** - * Returns whether the command will get tab completions for files - */ - public boolean needsFileTabCompletions(); - List<BundleInformation> getBundles(); }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSource.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSource.java Tue May 24 09:17:54 2016 -0400 @@ -138,10 +138,8 @@ List<BundleInformation> bundles = new ArrayList<>(); bundles.addAll(info1.getBundles()); bundles.addAll(info2.getBundles()); - boolean fileTabCompletionNeeded = info1.needsFileTabCompletions() || info2.needsFileTabCompletions(); - - return new BasicCommandInfo(name, summary, description, usage, options, environment, bundles, fileTabCompletionNeeded); + return new BasicCommandInfo(name, summary, description, usage, options, environment, bundles); } private <T> T selectBest(T first, T second) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/FileNameCompleterService.java Tue May 24 09:17:54 2016 -0400 @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2016 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 + * <http://www.gnu.org/licenses/>. + * + * 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 com.redhat.thermostat.common.cli.AbstractCompleterService; +import com.redhat.thermostat.common.cli.CliCommandOption; +import com.redhat.thermostat.common.cli.TabCompleter; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +public class FileNameCompleterService extends AbstractCompleterService { + + public static final CliCommandOption FILENAME_OPTION = new CliCommandOption("f", "filename", true, "path/to/local/file", false); + + @Override + public Set<String> getCommands() { + return TabCompletion.ALL_COMMANDS_COMPLETER; + } + + @Override + public Map<CliCommandOption, ? extends TabCompleter> getOptionCompleters() { + CliCommandOption option = FILENAME_OPTION; + TabCompleter completer = new JLineFileNameCompleter(); + + return Collections.singletonMap(option, completer); + } +}
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/JLineFileNameCompleter.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/JLineFileNameCompleter.java Tue May 24 09:17:54 2016 -0400 @@ -36,9 +36,10 @@ package com.redhat.thermostat.launcher.internal; +import com.redhat.thermostat.common.cli.FileNameTabCompleter; import jline.console.completer.FileNameCompleter; -public class JLineFileNameCompleter extends JLineCompleterWrapper { +public class JLineFileNameCompleter extends JLineCompleterWrapper implements FileNameTabCompleter { public JLineFileNameCompleter() { super(new FileNameCompleter());
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java Tue May 24 09:17:54 2016 -0400 @@ -120,13 +120,11 @@ private final Options options; private final Set<Environment> environment; private final List<BundleInformation> bundles; - private final boolean fileTabCompletionNeeded; public NewCommand(String name, String usage, String summary, String description, List<String> positionalArguments, Options options, Set<Environment> environment, - List<BundleInformation> bundles, - boolean fileTabCompletionNeeded) { + List<BundleInformation> bundles) { this.commandName = name; this.usage = usage; this.summary = summary; @@ -135,7 +133,6 @@ this.options = options; this.environment = environment; this.bundles = bundles; - this.fileTabCompletionNeeded = fileTabCompletionNeeded; } public String getCommandName() { @@ -178,9 +175,6 @@ return Collections.unmodifiableList(bundles); } - public boolean needsFileTabCompletions() { - return fileTabCompletionNeeded; - } } public static class PluginID {
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Tue May 24 09:17:54 2016 -0400 @@ -359,7 +359,6 @@ Options options = new Options(); Set<Environment> availableInEnvironments = EnumSet.noneOf(Environment.class); List<BundleInformation> bundles = new ArrayList<>(); - boolean fileTabCompletionNeeded = false; NodeList nodes = commandNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { @@ -380,8 +379,6 @@ availableInEnvironments = parseEnvironment(pluginName, name, node); } else if (node.getNodeName().equals("bundles")) { bundles.addAll(parseBundles(pluginName, name, node)); - } else if (node.getNodeName().equals("add-file-completion")) { - fileTabCompletionNeeded = Boolean.parseBoolean(node.getTextContent().trim()); } } @@ -394,7 +391,7 @@ "name='" + name + "', summary='" + summary + ", description='" + description + "', options='" + options + "'"); return null; } else { - return new NewCommand(name, usage, summary, description, arguments, options, availableInEnvironments, bundles, fileTabCompletionNeeded); + return new NewCommand(name, usage, summary, description, arguments, options, availableInEnvironments, bundles); } }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginInfoSource.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginInfoSource.java Tue May 24 09:17:54 2016 -0400 @@ -178,8 +178,8 @@ usage, command.getOptions(), command.getEnvironments(), - command.getBundles(), - command.needsFileTabCompletions()); + command.getBundles() + ); allNewCommands.put(commandName, info); } @@ -201,8 +201,8 @@ old.getUsage(), old.getOptions(), old.getEnvironments(), - updatedBundles, - old.needsFileTabCompletions()); + updatedBundles + ); allNewCommands.put(entry.getKey(), updated); iter.remove(); } @@ -232,7 +232,7 @@ } private BasicCommandInfo createCommandInfo(String name, List<BundleInformation> bundles) { - return new BasicCommandInfo(name, null, null, null, null, null, bundles, false); + return new BasicCommandInfo(name, null, null, null, null, null, bundles); } public Map<String, String> getConfiguration(String pluginID, String fileName) throws IOException {
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/TabCompletion.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/TabCompletion.java Tue May 24 09:17:54 2016 -0400 @@ -36,7 +36,6 @@ package com.redhat.thermostat.launcher.internal; -import com.redhat.thermostat.common.cli.Arguments; import com.redhat.thermostat.common.cli.CliCommandOption; import com.redhat.thermostat.common.cli.CompleterService; import com.redhat.thermostat.common.cli.TabCompleter; @@ -115,9 +114,6 @@ if (ALL_COMMANDS_COMPLETER == service.getCommands()) { globalCompleterServices.remove(service); } - if (commandMap.isEmpty()) { - return; - } for (String commandName : service.getCommands()) { TreeCompleter.Node command = commandMap.get(commandName); if (command != null) { @@ -157,11 +153,6 @@ setupDefaultCompletion(command, option); } - if (info.needsFileTabCompletions()) { - TreeCompleter.Node files = new TreeCompleter.Node("fileName", new JLineFileNameCompleter()); - files.setRestartNode(command); - command.addBranch(files); - } treeCompleter.addBranch(command); for (CompleterService service : globalCompleterServices) {
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BasicCommandInfoTest.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BasicCommandInfoTest.java Tue May 24 09:17:54 2016 -0400 @@ -59,9 +59,8 @@ final Options OPTIONS = new Options(); final Set<Environment> ENVIRONMENT = EnumSet.noneOf(Environment.class); final List<BundleInformation> BUNDLES = Collections.emptyList(); - final boolean ADD_FILE_COMPLETION = true; - BasicCommandInfo info = new BasicCommandInfo(NAME, SUMMARY, DESCRIPTION, USAGE, OPTIONS, ENVIRONMENT, BUNDLES, ADD_FILE_COMPLETION); + BasicCommandInfo info = new BasicCommandInfo(NAME, SUMMARY, DESCRIPTION, USAGE, OPTIONS, ENVIRONMENT, BUNDLES); assertEquals(NAME, info.getName()); assertEquals(SUMMARY, info.getSummary()); @@ -69,7 +68,6 @@ assertEquals(USAGE, info.getUsage()); assertEquals(OPTIONS, info.getOptions()); assertEquals(BUNDLES, info.getBundles()); - assertEquals(ADD_FILE_COMPLETION, info.needsFileTabCompletions()); assertEquals(String.format("%s (summary='%s', description='%s', dependencies='%s')", NAME, SUMMARY, DESCRIPTION, BUNDLES.toString()), info.toString());
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BuiltInCommandInfoTest.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BuiltInCommandInfoTest.java Tue May 24 09:17:54 2016 -0400 @@ -312,15 +312,5 @@ assertTrue(commandEnv.contains(Environment.SHELL)); } - @Test - public void verifyFileUsage() { - Properties props = new Properties(); - String name = "name"; - String usage = "true"; - props.put("add-file-completion", usage); - BuiltInCommandInfo info = new BuiltInCommandInfo(name, props); - - assertTrue(info.needsFileTabCompletions()); - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/FileNameCompleterServiceTest.java Tue May 24 09:17:54 2016 -0400 @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2016 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 + * <http://www.gnu.org/licenses/>. + * + * 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 com.redhat.thermostat.common.cli.CliCommandOption; +import com.redhat.thermostat.common.cli.TabCompleter; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class FileNameCompleterServiceTest { + + private FileNameCompleterService completerService; + + @Before + public void setup() { + completerService = new FileNameCompleterService(); + } + + @Test + public void testProvidesAllCommandsCompletion() { + assertThat(completerService.getCommands(), is(equalTo(TabCompletion.ALL_COMMANDS_COMPLETER))); + } + + @Test + public void testProvidesOnlyOneCompletion() { + Map<CliCommandOption, ? extends TabCompleter> map = completerService.getOptionCompleters(); + assertThat(map.size(), is(1)); + } + + @Test + public void testProvidesCompletionForFileNameArguments() { + Map<CliCommandOption, ? extends TabCompleter> map = completerService.getOptionCompleters(); + assertThat(map.keySet(), is(equalTo(Collections.singleton(FileNameCompleterService.FILENAME_OPTION)))); + } + + @Test + public void testFileNameCompleterIsNotNull() { + Map<CliCommandOption, ? extends TabCompleter> map = completerService.getOptionCompleters(); + assertThat(map.get(FileNameCompleterService.FILENAME_OPTION), is(not(equalTo(null)))); + } + + @Test + public void testFileNameOptionArgumentName() { + assertThat(FileNameCompleterService.FILENAME_OPTION.getLongOpt(), is("filename")); + assertThat(FileNameCompleterService.FILENAME_OPTION.getOpt(), is("f")); + } + +}
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java Tue May 24 09:17:54 2016 -0400 @@ -586,7 +586,6 @@ " <environment>shell</environment>" + " <environment>cli</environment>" + " </environments>" + - " <add-file-completion>true</add-file-completion>" + " </command>\n" + " </commands>\n" + "</plugin>"; @@ -601,7 +600,6 @@ NewCommand command = newCommands.get(0); assertEquals("test", command.getCommandName()); - assertTrue(command.needsFileTabCompletions()); } @Test @@ -617,7 +615,6 @@ " <environment>shell</environment>" + " <environment>cli</environment>" + " </environments>" + - " <add-file-completion>false</add-file-completion>" + " </command>\n" + " </commands>\n" + "</plugin>"; @@ -632,7 +629,6 @@ NewCommand command = newCommands.get(0); assertEquals("test", command.getCommandName()); - assertFalse(command.needsFileTabCompletions()); } @Test @@ -648,7 +644,6 @@ " <environment>shell</environment>" + " <environment>cli</environment>" + " </environments>" + - " <add-file-completion>invalid</add-file-completion>" + " </command>\n" + " </commands>\n" + "</plugin>"; @@ -663,7 +658,6 @@ NewCommand command = newCommands.get(0); assertEquals("test", command.getCommandName()); - assertFalse(command.needsFileTabCompletions()); } }
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/ShellCommandTest.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/ShellCommandTest.java Tue May 24 09:17:54 2016 -0400 @@ -43,7 +43,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -92,7 +91,6 @@ private Version version; private ConfigurationInfoSource config; private CommandInfoSource infos; - private File dir; private ClientPreferences prefs; private TabCompletion tabCompletion; @@ -111,10 +109,6 @@ cmd = new ShellCommand(bundleContext, version, historyProvider, config, prefs); cmd.setTabCompletion(tabCompletion); setupCommandInfoSource(); - - dir = new File(System.getProperty("java.io.tmpdir") + File.separator + "shellcommand"); - dir.deleteOnExit(); - dir.mkdirs(); } @After @@ -534,166 +528,6 @@ assertEquals("", ctxFactory.getError()); } - @Test - public void testFilesTabComplete() throws CommandException, IOException { - ServiceReference ref = mock(ServiceReference.class); - when(bundleContext.getServiceReference(Launcher.class.getName())).thenReturn(ref); - Launcher launcher = mock(Launcher.class); - when(bundleContext.getService(ref)).thenReturn(launcher); - - String filename = "testFilesTabComplete"; - createTempFile(filename); - createTempFile(filename+"12345678"); - - TestCommandContextFactory ctxFactory = new TestCommandContextFactory(bundleContext); - ctxFactory.setInput("validate " + dir.getAbsolutePath() + File.separator + "testFil\t\nexit\n"); - Arguments args = new SimpleArguments(); - CommandContext ctx = ctxFactory.createContext(args); - cmd.run(ctx); - - String tabOutput = getTabOutput(getOutputWithoutIntro(ctxFactory)); - assertTrue(tabOutput.contains(filename)); - assertEquals("", ctxFactory.getError()); - } - - @Test - public void testFilesTabCompleteAfterOptions() throws CommandException, IOException { - ServiceReference ref = mock(ServiceReference.class); - when(bundleContext.getServiceReference(Launcher.class.getName())).thenReturn(ref); - Launcher launcher = mock(Launcher.class); - when(bundleContext.getService(ref)).thenReturn(launcher); - - String filename = "testFilesTabCompleteAfterOptions"; - createTempFile(filename); - createTempFile(filename + "12345678"); - - TestCommandContextFactory ctxFactory = new TestCommandContextFactory(bundleContext); - ctxFactory.setInput("validate --fake-option -f " + dir.getAbsolutePath() + File.separator + "testFil\t\nexit\n"); - Arguments args = new SimpleArguments(); - CommandContext ctx = ctxFactory.createContext(args); - cmd.run(ctx); - - String tabOutput = getTabOutput(getOutputWithoutIntro(ctxFactory)); - assertTrue(tabOutput.contains(filename)); - assertEquals("", ctxFactory.getError()); - } - - @Test - public void testFilesDoNotTabCompleteWithoutCommand() throws CommandException, IOException { - ServiceReference ref = mock(ServiceReference.class); - when(bundleContext.getServiceReference(Launcher.class.getName())).thenReturn(ref); - Launcher launcher = mock(Launcher.class); - when(bundleContext.getService(ref)).thenReturn(launcher); - - String filename = "testFilesDoNotTabCompleteWithoutCommand"; - createTempFile(filename); - createTempFile(filename + "12345678"); - - TestCommandContextFactory ctxFactory = new TestCommandContextFactory(bundleContext); - ctxFactory.setInput(dir.getAbsolutePath() + File.separator + "testFil\t\nexit\n"); - Arguments args = new SimpleArguments(); - CommandContext ctx = ctxFactory.createContext(args); - cmd.run(ctx); - - String tabOutput = getTabOutput(getOutputWithoutIntro(ctxFactory)); - assertFalse(tabOutput.contains(filename)); - assertEquals("", tabOutput); - assertEquals("", ctxFactory.getError()); - } - - @Test - public void testFileTabCompletesInline() throws CommandException, IOException { - ServiceReference ref = mock(ServiceReference.class); - when(bundleContext.getServiceReference(Launcher.class.getName())).thenReturn(ref); - Launcher launcher = mock(Launcher.class); - when(bundleContext.getService(ref)).thenReturn(launcher); - - File tempDir = makeTempDir("testFileTabCompletesInline"); - - String filename = "testFileTabCompletesInline"; - createTempFile(filename, tempDir); - - TestCommandContextFactory ctxFactory = new TestCommandContextFactory(bundleContext); - String input = "validate " + tempDir.getAbsolutePath() + File.separator + "testFil"; - ctxFactory.setInput(input + "\t\nexit\n"); - Arguments args = new SimpleArguments(); - CommandContext ctx = ctxFactory.createContext(args); - cmd.run(ctx); - - String inline = getTabbedInline(ctxFactory, input); - String tabOutput = getTabOutput(getOutputWithoutIntro(ctxFactory)); - - setupCommandInfoSource(); - assertEquals(0, tabOutput.length()); - assertTrue(inline.endsWith(filename)); - assertEquals("", ctxFactory.getError()); - } - - @Test - public void testFilesDoNotTabCompleteInlineFullCommand() throws CommandException, IOException { - ServiceReference ref = mock(ServiceReference.class); - when(bundleContext.getServiceReference(Launcher.class.getName())).thenReturn(ref); - Launcher launcher = mock(Launcher.class); - when(bundleContext.getService(ref)).thenReturn(launcher); - - File tempDir = makeTempDir("testNotFullCommand"); - - String filename = "testFileTabCompletesInline"; - String similarName = "testFileTabNumber"; - createTempFile(filename, tempDir); - createTempFile(similarName + "1234567", tempDir); - createTempFile(similarName + "987654321", tempDir); - createTempFile(similarName + "456123", tempDir); - - TestCommandContextFactory ctxFactory = new TestCommandContextFactory(bundleContext); - String input = "validate " + tempDir.getAbsolutePath() + File.separator + "testFil"; - ctxFactory.setInput(input + "\t\nexit\n"); - Arguments args = new SimpleArguments(); - CommandContext ctx = ctxFactory.createContext(args); - cmd.run(ctx); - - String inline = getTabbedInline(ctxFactory, input); - String tabOutput = getTabOutput(getOutputWithoutIntro(ctxFactory)); - - assertTrue(tabOutput.contains(filename)); - assertFalse(inline.contains(filename)); - assertFalse(inline.contains(similarName)); - assertTrue(inline.endsWith("testFileTab")); - assertEquals("", ctxFactory.getError()); - } - - @Test - public void testFilesTabCompleteCommonPortionInline() throws CommandException, IOException { - ServiceReference ref = mock(ServiceReference.class); - when(bundleContext.getServiceReference(Launcher.class.getName())).thenReturn(ref); - Launcher launcher = mock(Launcher.class); - when(bundleContext.getService(ref)).thenReturn(launcher); - - File tempDir = makeTempDir("testFilesTabCompleteCommonPortionInline"); - - String filename = "testFileTabCompletesInline"; - String commonPortion = "testFileTab"; - createTempFile(filename,tempDir); - createTempFile(commonPortion + "1234567", tempDir); - createTempFile(commonPortion + "987654321", tempDir); - createTempFile(commonPortion + "456123", tempDir); - - TestCommandContextFactory ctxFactory = new TestCommandContextFactory(bundleContext); - String input = "validate " + tempDir.getAbsolutePath() + File.separator + "testFil"; - ctxFactory.setInput(input + "\t\nexit\n"); - Arguments args = new SimpleArguments(); - CommandContext ctx = ctxFactory.createContext(args); - cmd.run(ctx); - - String inline = getTabbedInline(ctxFactory, input); - String tabOutput = getTabOutput(getOutputWithoutIntro(ctxFactory)); - - assertTrue(tabOutput.contains(filename)); - assertFalse(inline.contains(filename)); - assertTrue(inline.endsWith(commonPortion)); - assertEquals("", ctxFactory.getError()); - } - private String getOutputWithoutIntro(final TestCommandContextFactory ctxFactory) { String[] allOutput = makeNewlinesConsistent(ctxFactory.getOutput()).split("\n"); String[] outputWithoutIntro = Arrays.copyOfRange(allOutput, 2, allOutput.length); @@ -728,34 +562,13 @@ return input.replace("\r\n", "\n"); } - private void createTempFile(String name) throws IOException { - File file = new File(dir, name); - file.deleteOnExit(); - file.createNewFile(); - } - - private void createTempFile(String name, File dir) throws IOException { - File file = new File(dir, name); - file.deleteOnExit(); - file.createNewFile(); - } - - private File makeTempDir(String name) { - File tempDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "sc-" + name); - tempDir.deleteOnExit(); - tempDir.mkdirs(); - return tempDir; - } - private void setupCommandInfoSource() { - Collection<CommandInfo> infoList = new ArrayList<>(); CommandInfo info1 = mock(CommandInfo.class); when(info1.getName()).thenReturn("test1"); when(info1.getDescription()).thenReturn("test command 1"); when(info1.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL)); - when(info1.needsFileTabCompletions()).thenReturn(false); ArrayList<Option> optionsList1 = new ArrayList<>(); Option option1 = mock(Option.class); @@ -782,7 +595,6 @@ when(info2.getName()).thenReturn("test2longname"); when(info2.getDescription()).thenReturn("test command 2"); when(info2.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL)); - when(info2.needsFileTabCompletions()).thenReturn(false); ArrayList<Option> optionsList2 = new ArrayList<>(); Option option4 = mock(Option.class); @@ -809,7 +621,6 @@ when(info3.getName()).thenReturn("validate"); when(info3.getDescription()).thenReturn("mock validate command"); when(info3.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL)); - when(info3.needsFileTabCompletions()).thenReturn(true); ArrayList<Option> optionsList3 = new ArrayList<>(); Option option7 = mock(Option.class);
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/TestCommandInfo.java Tue May 24 11:09:57 2016 +0200 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/TestCommandInfo.java Tue May 24 09:17:54 2016 -0400 @@ -54,7 +54,6 @@ private Options options = new Options(); private Set<Environment> environments; - private boolean fileTabCompletionNeeded; public TestCommandInfo(String name) { this.name = name; @@ -97,15 +96,6 @@ return options; } - @Override - public boolean needsFileTabCompletions() { - return fileTabCompletionNeeded; - } - - public void setFileTabCompletionNeeded(boolean fileTabCompletionNeeded) { - this.fileTabCompletionNeeded = fileTabCompletionNeeded; - } - public void addOptions(Option... options) { for (Option option : options) { this.options.addOption(option);
--- a/validate-command/command/pom.xml Tue May 24 11:09:57 2016 +0200 +++ b/validate-command/command/pom.xml Tue May 24 09:17:54 2016 -0400 @@ -94,6 +94,11 @@ <artifactId>thermostat-common-core</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-client-cli</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>com.redhat.thermostat</groupId>
--- a/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/internal/Activator.java Tue May 24 11:09:57 2016 +0200 +++ b/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/internal/Activator.java Tue May 24 09:17:54 2016 -0400 @@ -38,26 +38,66 @@ import java.util.Hashtable; +import com.redhat.thermostat.common.cli.CompleterService; +import com.redhat.thermostat.common.cli.FileNameTabCompleter; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import com.redhat.thermostat.common.cli.Command; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; public class Activator implements BundleActivator { - private ServiceRegistration registration; + private ServiceRegistration commandRegistration; + private ValidateCommandCompleterService completerService; + private ServiceTracker fileNameCompleterTracker; + private ServiceRegistration completerRegistration; @Override + @SuppressWarnings("unchecked") public void start(final BundleContext context) throws Exception { - Hashtable<String,String> properties = new Hashtable<>(); + Hashtable<String, String> properties = new Hashtable<>(); properties.put(Command.NAME, "validate"); - registration = context.registerService(Command.class.getName(), new ValidateCommand(), properties); + + commandRegistration = context.registerService(Command.class.getName(), new ValidateCommand(), properties); + + completerService = new ValidateCommandCompleterService(); + + fileNameCompleterTracker = new ServiceTracker(context, FileNameTabCompleter.class, new ServiceTrackerCustomizer() { + @Override + public Object addingService(ServiceReference serviceReference) { + FileNameTabCompleter tabCompleter = (FileNameTabCompleter) context.getService(serviceReference); + completerService.setFileNameTabCompleter(tabCompleter); + completerRegistration = context.registerService(CompleterService.class.getName(), completerService, null); + return context.getService(serviceReference); + } + + @Override + public void modifiedService(ServiceReference serviceReference, Object o) { + } + + @Override + public void removedService(ServiceReference serviceReference, Object o) { + completerService.setFileNameTabCompleter(null); + } + }); + fileNameCompleterTracker.open(); } @Override public void stop(BundleContext context) throws Exception { - registration.unregister(); + if (commandRegistration != null) { + commandRegistration.unregister(); + } + if (completerRegistration != null) { + completerRegistration.unregister(); + } + if (fileNameCompleterTracker != null) { + fileNameCompleterTracker.close(); + } } }
--- a/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/internal/LocaleResources.java Tue May 24 11:09:57 2016 +0200 +++ b/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/internal/LocaleResources.java Tue May 24 09:17:54 2016 -0400 @@ -43,9 +43,9 @@ VALIDATION_SUCCESSFUL, VALIDATION_FAILED, - - FILE_REQUIRED, - FILE_NOT_FOUND, + + ONE_ARGUMENT_EXPECTED, + FILE_NOT_FOUND, ; static final String RESOURCE_BUNDLE =
--- a/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/internal/ValidateCommand.java Tue May 24 11:09:57 2016 +0200 +++ b/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/internal/ValidateCommand.java Tue May 24 09:17:54 2016 -0400 @@ -39,7 +39,6 @@ import java.io.File; import java.io.FileNotFoundException; -import com.redhat.thermostat.common.cli.Arguments; import com.redhat.thermostat.common.cli.Command; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; @@ -51,40 +50,39 @@ public class ValidateCommand implements Command { + public static final String COMMAND_NAME = "validate"; + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); - private PluginValidator validator; + private final PluginValidator validator; + + public ValidateCommand() { + this.validator = new PluginValidator(); + } public void run(CommandContext ctx) throws CommandException { - Arguments args = ctx.getArguments(); - validator = new PluginValidator(); + if (ctx.getArguments().getNonOptionArguments().size() != 1) { + throw new CommandException(translator.localize(LocaleResources.ONE_ARGUMENT_EXPECTED)); + } + String filename = ctx.getArguments().getNonOptionArguments().get(0); File pluginFile = null; - String argString = null; - - try { - argString = args.getNonOptionArguments().get(0); - pluginFile = new File(argString); - validator.validate(pluginFile); - ctx.getConsole().getOutput().println(translator.localize( - LocaleResources.VALIDATION_SUCCESSFUL, pluginFile.getAbsolutePath()) - .getContents()); - - } catch (PluginConfigurationValidatorException e) { - ValidationErrorsFormatter formatter = new ValidationErrorsFormatter(); - ctx.getConsole().getError().println(formatter.format(e.getAllErrors())); - ctx.getConsole().getError().println(translator.localize( - LocaleResources.VALIDATION_FAILED, pluginFile.getAbsolutePath()) - .getContents()); - - } catch (IndexOutOfBoundsException | NullPointerException e) { - throw new CommandLineArgumentParseException - (translator.localize( - LocaleResources.FILE_REQUIRED)); - - } catch (FileNotFoundException fnfe) { - throw new CommandLineArgumentParseException - (translator.localize( - LocaleResources.FILE_NOT_FOUND, pluginFile.getAbsolutePath())); - } + try { + pluginFile = new File(filename); + validator.validate(pluginFile); + ctx.getConsole().getOutput().println(translator.localize( + LocaleResources.VALIDATION_SUCCESSFUL, pluginFile.getAbsolutePath()) + .getContents()); + + } catch (PluginConfigurationValidatorException e) { + ValidationErrorsFormatter formatter = new ValidationErrorsFormatter(); + ctx.getConsole().getError().println(formatter.format(e.getAllErrors())); + ctx.getConsole().getError().println(translator.localize( + LocaleResources.VALIDATION_FAILED, pluginFile.getAbsolutePath()) + .getContents()); + + } catch (FileNotFoundException fnfe) { + throw new CommandLineArgumentParseException(translator.localize( + LocaleResources.FILE_NOT_FOUND, pluginFile.getAbsolutePath())); + } } public boolean isStorageRequired() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/internal/ValidateCommandCompleterService.java Tue May 24 09:17:54 2016 -0400 @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2016 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 + * <http://www.gnu.org/licenses/>. + * + * 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.validate.command.internal; + +import com.redhat.thermostat.common.cli.AbstractCompleterService; +import com.redhat.thermostat.common.cli.CliCommandOption; +import com.redhat.thermostat.common.cli.FileNameTabCompleter; +import com.redhat.thermostat.common.cli.TabCompleter; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +public class ValidateCommandCompleterService extends AbstractCompleterService { + + private FileNameTabCompleter tabCompleter; + + @Override + public Set<String> getCommands() { + return Collections.singleton(ValidateCommand.COMMAND_NAME); + } + + @Override + public Map<CliCommandOption, ? extends TabCompleter> getOptionCompleters() { + if (tabCompleter == null) { + return Collections.emptyMap(); + } + CliCommandOption option = CliCommandOption.POSITIONAL_ARG_COMPLETION; + + return Collections.singletonMap(option, tabCompleter); + } + + public void setFileNameTabCompleter(FileNameTabCompleter tabCompleter) { + this.tabCompleter = tabCompleter; + } +}
--- a/validate-command/command/src/main/resources/com/redhat/thermostat/validate/locale/strings.properties Tue May 24 11:09:57 2016 +0200 +++ b/validate-command/command/src/main/resources/com/redhat/thermostat/validate/locale/strings.properties Tue May 24 09:17:54 2016 -0400 @@ -1,5 +1,5 @@ VALIDATION_SUCCESSFUL = Validation successful for file {0}\n VALIDATION_FAILED = Validation failed for file {0}\n -FILE_REQUIRED = Could not parse argument: missing file name FILE_NOT_FOUND = File {0} not found. Please check the file name and/or the path +ONE_ARGUMENT_EXPECTED = One non-option argument expected \ No newline at end of file
--- a/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/internal/ActivatorTest.java Tue May 24 11:09:57 2016 +0200 +++ b/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/internal/ActivatorTest.java Tue May 24 09:17:54 2016 -0400 @@ -37,12 +37,18 @@ package com.redhat.thermostat.validate.command.internal; import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered; +import static com.redhat.thermostat.testutils.Asserts.assertServiceIsRegistered; import static org.junit.Assert.*; +import com.redhat.thermostat.common.cli.Command; +import com.redhat.thermostat.common.cli.CompleterService; +import com.redhat.thermostat.common.cli.FileNameTabCompleter; import org.junit.Test; import com.redhat.thermostat.testutils.StubBundleContext; +import java.util.List; + public class ActivatorTest { @Test @@ -60,5 +66,24 @@ assertEquals(0, ctx.getAllServices().size()); } + @Test + public void testValidateCompleterBecomesAvailableWhenFileNameCompleterAppears() throws Exception { + StubBundleContext ctx = new StubBundleContext(); + ctx.registerService(FileNameTabCompleter.class, new StubFileNameTabCompleter(), null); + Activator activator = new Activator(); + activator.start(ctx); + assertEquals(3, ctx.getAllServices().size()); + assertServiceIsRegistered(ctx, FileNameTabCompleter.class, StubFileNameTabCompleter.class); + assertServiceIsRegistered(ctx, CompleterService.class, ValidateCommandCompleterService.class); + assertServiceIsRegistered(ctx, Command.class, ValidateCommand.class); + } + + private static class StubFileNameTabCompleter implements FileNameTabCompleter { + @Override + public int complete(String buffer, int cursor, List<CharSequence> candidates) { + return 0; + } + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/internal/ValidateCommandCompleterServiceTest.java Tue May 24 09:17:54 2016 -0400 @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2016 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 + * <http://www.gnu.org/licenses/>. + * + * 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.validate.command.internal; + +import com.redhat.thermostat.common.cli.CliCommandOption; +import com.redhat.thermostat.common.cli.FileNameTabCompleter; +import com.redhat.thermostat.common.cli.TabCompleter; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +public class ValidateCommandCompleterServiceTest { + + private ValidateCommandCompleterService completerService; + + @Before + public void setup() { + completerService = new ValidateCommandCompleterService(); + completerService.setFileNameTabCompleter(mock(FileNameTabCompleter.class)); + } + + @Test + public void testOnlyProvidesCompletionForValidateCommand() { + assertThat(completerService.getCommands(), is(equalTo(Collections.singleton(ValidateCommand.COMMAND_NAME)))); + } + + @Test + public void testProvidesOnlyOneCompleter() { + assertThat(completerService.getOptionCompleters().size(), is(1)); + } + + @Test + public void testProvidesCompletionForPositionalArgument() { + Map<CliCommandOption, ? extends TabCompleter> map = completerService.getOptionCompleters(); + assertThat(map.keySet(), is(equalTo(Collections.singleton(CliCommandOption.POSITIONAL_ARG_COMPLETION)))); + } + + @Test + public void testCompleterIsNotNull() { + Map<CliCommandOption, ? extends TabCompleter> map = completerService.getOptionCompleters(); + assertThat(map.get(CliCommandOption.POSITIONAL_ARG_COMPLETION), is(not(equalTo(null)))); + } + + @Test + public void testProvidesNoCompletionWhenFileNameTabCompleterNotAvailable() { + completerService.setFileNameTabCompleter(null); + Map<CliCommandOption, ? extends TabCompleter> map = completerService.getOptionCompleters(); + assertThat(map.isEmpty(), is(true)); + } + +}
--- a/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/internal/ValidateCommandTest.java Tue May 24 11:09:57 2016 +0200 +++ b/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/internal/ValidateCommandTest.java Tue May 24 09:17:54 2016 -0400 @@ -38,12 +38,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.commons.cli.MissingArgumentException; @@ -64,13 +66,11 @@ private Arguments mockArgs; private Console console; private String fileName; - private List<String> list = new ArrayList<>(); private ByteArrayOutputStream outputBaos, errorBaos; private PrintStream output, error; @Before public void setUp() { - cmd = new ValidateCommand(); ctxt = mock(CommandContext.class); mockArgs = mock(Arguments.class); @@ -86,15 +86,15 @@ when(ctxt.getConsole()).thenReturn(console); when(console.getError()).thenReturn(error); when(console.getOutput()).thenReturn(output); - when(mockArgs.getNonOptionArguments()).thenReturn(list); - + when(mockArgs.hasArgument("filename")).thenReturn(true); } @Test public void validateIncorrectFile() throws CommandException, MissingArgumentException { fileName = PluginValidator.class.getResource("/incorrectPlugin.xml").getPath().toString(); - list.add(fileName); - + + when(mockArgs.getNonOptionArguments()).thenReturn(Collections.singletonList(fileName)); + cmd.run(ctxt); String errorOutput = new String(errorBaos.toByteArray()); String validateOutput = buildErrorMessage(); @@ -106,8 +106,9 @@ @Test public void validateCorrectFile() throws CommandException, MissingArgumentException { fileName = PluginValidator.class.getResource("/correctPlugin.xml").getPath().toString(); - list.add(fileName); - + + when(mockArgs.getNonOptionArguments()).thenReturn(Collections.singletonList(fileName)); + cmd.run(ctxt); String expected = "Validation successful for file " + fileName + "\n\n"; @@ -120,8 +121,8 @@ @Test public void validateNonExistingFile() throws CommandException, MissingArgumentException { fileName = "/nonExistingFile.xml"; - list.add(fileName); - + when(mockArgs.getNonOptionArguments()).thenReturn(Collections.singletonList(fileName)); + try { cmd.run(ctxt); } catch(CommandLineArgumentParseException clpae) { @@ -131,10 +132,11 @@ @Test public void missingFileAsArgument() throws CommandException, MissingArgumentException { - + when(mockArgs.getNonOptionArguments()).thenReturn(Collections.<String>emptyList()); try { - cmd.run(ctxt); - } catch(CommandLineArgumentParseException clpae) { + cmd.run(ctxt); + fail(); + } catch(CommandException clpae) { // pass } }
--- a/validate-command/distribution/thermostat-plugin.xml Tue May 24 11:09:57 2016 +0200 +++ b/validate-command/distribution/thermostat-plugin.xml Tue May 24 09:17:54 2016 -0400 @@ -44,20 +44,17 @@ <name>validate</name> <summary>validate a thermostat plug-in XML file</summary> <description>Validate a thermostat plug-in XML file against the standard schema and report any issues.</description> - <arguments> - <argument>file</argument> - </arguments> <environments> <environment>cli</environment> <environment>shell</environment> </environments> <bundles> + <bundle><symbolic-name>com.redhat.thermostat.common.core</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.client.cli</symbolic-name><version>${project.version}</version></bundle> <bundle><symbolic-name>com.redhat.thermostat.validate.command</symbolic-name><version>${project.version}</version></bundle> - <bundle><symbolic-name>com.redhat.thermostat.common.core</symbolic-name><version>${project.version}</version></bundle> <bundle><symbolic-name>com.redhat.thermostat.configuration</symbolic-name><version>${project.version}</version></bundle> <bundle><symbolic-name>com.redhat.thermostat.plugin.validator</symbolic-name><version>${project.version}</version></bundle> </bundles> - <add-file-completion>true</add-file-completion> </command> </commands> </plugin>
--- a/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/internal/SaveHeapDumpToFileCommand.java Tue May 24 11:09:57 2016 +0200 +++ b/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/internal/SaveHeapDumpToFileCommand.java Tue May 24 09:17:54 2016 -0400 @@ -44,6 +44,7 @@ import java.io.InputStream; import java.io.OutputStream; +import com.redhat.thermostat.client.cli.FileNameArgument; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; @@ -66,7 +67,6 @@ private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); private static final String HEAP_ID_ARGUMENT = "heapId"; - private static final String FILE_NAME_ARGUMENT = "file"; private final FileStreamCreator creator; private final BundleContext context; @@ -89,7 +89,6 @@ run(ctx, heapDAO); } finally { context.ungetService(ref); - heapDAO = null; } } @@ -99,10 +98,8 @@ if (heapId == null) { throw new CommandLineArgumentParseException(translator.localize(LocaleResources.HEAP_ID_REQUIRED)); } - String filename = args.getArgument(FILE_NAME_ARGUMENT); - if (filename == null) { - throw new CommandLineArgumentParseException(translator.localize(LocaleResources.FILE_REQUIRED)); - } + FileNameArgument fileNameArgument = FileNameArgument.required(args); + String filename = fileNameArgument.getFileName(); HeapInfo heapInfo = heapDAO.getHeapInfo(heapId); try (InputStream heapStream = heapDAO.getHeapDumpData(heapInfo)) { @@ -121,7 +118,7 @@ } } - private void saveHeapDump(InputStream heapStream, String filename) throws FileNotFoundException, IOException { + private void saveHeapDump(InputStream heapStream, String filename) throws IOException { try (BufferedInputStream bis = new BufferedInputStream(heapStream); BufferedOutputStream bout = new BufferedOutputStream(creator.createOutputStream(filename))) { StreamUtils.copyStream(bis, bout);
--- a/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/locale/LocaleResources.java Tue May 24 11:09:57 2016 +0200 +++ b/vm-heap-analysis/command/src/main/java/com/redhat/thermostat/vm/heap/analysis/command/locale/LocaleResources.java Tue May 24 09:17:54 2016 -0400 @@ -52,7 +52,6 @@ HEADER_OBJECT_ID, HEADER_OBJECT_TYPE, - FILE_REQUIRED, INVALID_LIMIT, HEAP_ID_NOT_FOUND, HEAP_ID_REQUIRED,
--- a/vm-heap-analysis/command/src/main/resources/com/redhat/thermostat/vm/heap/analysis/command/locale/strings.properties Tue May 24 11:09:57 2016 +0200 +++ b/vm-heap-analysis/command/src/main/resources/com/redhat/thermostat/vm/heap/analysis/command/locale/strings.properties Tue May 24 09:17:54 2016 -0400 @@ -11,7 +11,6 @@ HEADER_OBJECT_ID = ID HEADER_OBJECT_TYPE = TYPE -FILE_REQUIRED = A file name is required INVALID_LIMIT = Invalid limit {0} HEAP_ID_NOT_FOUND = Heap ID not found: {0} HEAP_ID_REQUIRED = Heap ID required
--- a/vm-heap-analysis/command/src/test/java/com/redhat/thermostat/vm/heap/analysis/command/internal/SaveHeapDumpToFileCommandTest.java Tue May 24 11:09:57 2016 +0200 +++ b/vm-heap-analysis/command/src/test/java/com/redhat/thermostat/vm/heap/analysis/command/internal/SaveHeapDumpToFileCommandTest.java Tue May 24 09:17:54 2016 -0400 @@ -81,7 +81,7 @@ SimpleArguments args = new SimpleArguments(); args.addArgument(Arguments.HOST_ID_ARGUMENT, "host-id"); args.addArgument(VmArgument.ARGUMENT_NAME, "1"); - args.addArgument("file", "heap-id-1"); + args.addArgument("filename", "heap-id-1"); HeapDAO heapDAO = mock(HeapDAO.class); context.registerService(HeapDAO.class, heapDAO, null); @@ -125,7 +125,7 @@ SimpleArguments args = new SimpleArguments(); args.addArgument("heapId", HEAP_ID); - args.addArgument("file", FILE_NAME); + args.addArgument("filename", FILE_NAME); FileStreamCreator creator = mock(FileStreamCreator.class); when(creator.createOutputStream(FILE_NAME)).thenReturn(heapDumpStream); @@ -144,7 +144,7 @@ args.addArgument(Arguments.HOST_ID_ARGUMENT, "host-id"); args.addArgument(VmArgument.ARGUMENT_NAME, "1"); args.addArgument("heapId", "heap-id-1"); - args.addArgument("file", "heap-id-1"); + args.addArgument("filename", "heap-id-1"); Command command = new SaveHeapDumpToFileCommand(context, mock(FileStreamCreator.class));
--- a/vm-heap-analysis/distribution/thermostat-plugin.xml Tue May 24 11:09:57 2016 +0200 +++ b/vm-heap-analysis/distribution/thermostat-plugin.xml Tue May 24 09:17:54 2016 -0400 @@ -350,7 +350,7 @@ <description>the ID of the heapdump to analyze</description> </option> <option> - <long>file</long> + <long>filename</long> <short>f</short> <argument>filename</argument> <required>true</required> @@ -395,7 +395,6 @@ <bundle><symbolic-name>${lucene-core.bundle.symbolic-name}</symbolic-name><version>${lucene.osgi-version}</version></bundle> <bundle><symbolic-name>${lucene-analysis.bundle.symbolic-name}</symbolic-name><version>${lucene.osgi-version}</version></bundle> </bundles> - <add-file-completion>true</add-file-completion> </command> <command> <name>show-heap-histogram</name>